Ruby는 스크립트가 자체 소스 파일을 데이터 소스로 사용할 수 있는 방법을 제공한다는 사실을 알고 계셨습니까? 일회성 스크립트와 개념 증명을 작성할 때 시간을 절약할 수 있는 멋진 트릭입니다. 확인해 봅시다!
DATA 및 END
아래 예에서는 __END__
라는 재미있는 키워드를 사용하고 있습니다. . __END__
아래의 모든 항목 Ruby 인터프리터는 무시합니다. 하지만 더 흥미롭게도, ruby는 DATA
라는 IO 객체를 제공합니다. , __END__
아래의 모든 내용을 읽을 수 있습니다. 다른 파일에서 읽을 수 있는 것처럼
다음 예에서는 각 줄을 반복하고 인쇄합니다.
DATA.each_line do |line|
puts line
end
__END__
Doom
Quake
Diablo
이 기술의 내가 가장 좋아하는 실용적인 예는 DATA
를 사용합니다. ERB 템플릿을 포함합니다. 또한 YAML, CSV 등과 함께 작동합니다. 음
require 'erb'
time = Time.now
renderer = ERB.new(DATA.read)
puts renderer.result()
__END__
The current time is <%= time %>.
실제로 DATA
를 사용할 수 있습니다. __END__
위의 내용을 읽으려면 예어. DATA
때문입니다. 실제로 전체 소스 파일에 대한 포인터이며 __END__
로 빨리 감기됩니다. 예어. IO 개체를 인쇄하기 전에 되감으면 이것을 볼 수 있습니다. 아래 예는 전체 소스 파일을 출력합니다.
DATA.rewind
puts DATA.read # prints the entire source file
__END__
meh
여러 파일 구분
이 기술의 큰 단점 중 하나는 스크립트가 단일 소스 파일에 적합하고 해당 파일을 포함하지 않고 직접 실행하는 경우에만 실제로 작동한다는 것입니다.
아래 예에는 각각 고유한 __END__
가 있는 두 개의 파일이 있습니다. 부분. 그러나 DATA
는 하나만 있을 수 있습니다. 글로벌. 따라서 __END__
두 번째 파일의 섹션에 액세스할 수 없습니다.
# first.rb
require "./second"
puts "First file\n----------------------"
puts DATA.read
print_second_data()
__END__
First end clause
# second.rb
def print_second_data
puts "Second file\n----------------------"
puts DATA.read # Won't output anything, since first.rb read the entire file
end
__END__
Second end clause
snhorne ~/tmp $ ruby first.rb
First file
----------------------
First end clause
Second file
----------------------
여러 파일에 대한 해결 방법
Sinatra에는 __END__
뒤에 삽입하여 여러 인라인 템플릿을 앱에 추가할 수 있는 멋진 기능이 있습니다. 성명. 다음과 같습니다.
# This code is from the Sinatra docs at https://www.sinatrarb.com/intro.html
require 'sinatra'
get '/' do
haml :index
end
__END__
@@ layout
%html
= yield
@@ index
%div.title Hello world.
그러나 시나트라는 정확히 어떻게 이것을 할 수 있습니까? 결국 앱은 아마도 랙에 의해 로드될 것입니다. ruby myapp.rb
를 실행하지 않을 것입니다. 생산 중! DATA
를 사용하는 방법을 알아냈을 것입니다. 여러 파일로.
그러나 Sinatra 소스를 조금 파헤쳐 보면 그들이 일종의 속임수임을 알 수 있습니다. DATA
를 사용하지 않습니다. 조금도. 대신 아래 코드와 유사한 작업을 수행합니다.
# I'm paraphrasing. See the original at https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1284
app, data = File.read(__FILE__).split(/^__END__$/, 2)
__FILE__
을(를) 읽고 싶지 않기 때문에 실제로는 조금 더 복잡합니다. . 그것은 바로 sinatra/base.rb 파일일 것입니다. 대신 그들은 함수를 호출한 파일의 내용을 얻기를 원합니다. 그들은 호출자의 결과를 파싱하여 이것을 얻습니다.
호출자 함수는 현재 실행 중인 함수가 호출된 위치를 알려줍니다. 다음은 간단한 예입니다.
def some_method
puts caller
end
some_method # => caller.rb:5:in `<main>'
이제 거기에서 파일 이름을 가져오고 해당 파일의 DATA에 해당하는 것을 추출하는 것은 매우 간단한 문제입니다.
def get_caller_data
puts File.read(caller.first.split(":").first).split("__END__", 2).last
end
악이 아닌 선을 위해 사용
이러한 트릭이 매일 사용하고 싶은 것이 아니라는 것이 분명하기를 바랍니다. 깨끗하고 유지 관리 가능한 큰 코드 베이스를 만들지 못합니다.
그러나 때때로 일회성 유틸리티 스크립트나 개념 증명을 위해 빠르고 더러운 것이 필요합니다. 이 경우 DATA
및 __END__
꽤 유용할 수 있습니다.