Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

ใช้ Ruby's DATA และ __END__: Embed Code และ Data Seamlessly

รู้ไหมว่า Ruby มีวิธีให้ script ใช้ source file ของตัวเองเป็น data source? Trick น่าสนใจประหยัดเวลา one-off scripts และ proofs of concept มาดู!

DATA และ END

ตัวอย่างใช้ keyword แปลก __END__ ทุกอย่างใต้ __END__ จะถูก ignore โดย Ruby interpreter แต่น่าสนใจกว่านั้น Ruby ให้ IO object ชื่อ DATA ให้อ่านทุกอย่างใต้ __END__ เหมือนอ่าน file อื่นๆ

ตัวอย่าง iterate แต่ละบรรทัด print:

DATA.each_line do |line| puts line end __END__ Doom Quake Diablo 

Practical example โปรดของผม ใช้ DATA เก็บ ERB template ทำงานกับ YAML, CSV ได้ด้วย

require 'erb' time = Time.now renderer = ERB.new(DATA.read) puts renderer.result() __END__ The current time is <%= time %>. 

อ่าน content เหนือ __END__ keyword ได้ด้วย เพราะ DATA เป็น pointer ไป entire source file fast-forwarded ไป __END__ keyword เห็นได้ถ้า rewind IO object ก่อน print ตัวอย่าง print entire source file

DATA.rewind puts DATA.read # prints the entire source file __END__ meh 

The Multiple-File Delimma

Downside ใหญ่ technique นี้ใช้ได้ก็ต่อเมื่อ script fit single source file และรัน file นั้นโดยตรง ไม่ใช่ include

ตัวอย่าง 2 files แต่ละอันมี __END__ section แต่มีแค่หนึ่ง DATA global __END__ section ของ file ที่ 2 inaccessible

# 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 ---------------------- 

A Work-around for Multiple Files

Sinatra มี feature cool ให้เพิ่ม multiple inline templates ใน apps โดยวางหลัง __END__ statement 长这样:

# 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. 

แต Sinatra ทำยังไง? App อาจ load by rack ไม่รัน ruby myapp.rb ใน production! ต้องมีวิธีใช้ DATA กับ multiple files

ขุด Sinatra source จะเห็นพวกเขา kind of cheating ไม่ได้用 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) 

ซับซ้อนนิดหน่อยเพราะไม่อยาก read __FILE__ นั่นคือ sinatra/base.rb file อยากได้ content ของ file ที่ invoked function ได้จาก parsing caller result

caller function บอกที่ currently running function ถูก invoked ตัวอย่าง:

def some_method puts caller end some_method # => caller.rb:5:in `<main>' 

Pull filename ออกมา extract DATA equivalent สำหรับ file นั้นง่าย:

def get_caller_data puts File.read(caller.first.split(":").first).split("__END__", 2).last end 

Use It for Good, Not Evil

หวังว่าจะชัดเจน tricks เหล่านี้ไม่ใช่ของใช้ทุกวัน ไม่ทำให้ clean, maintainable large code bases

แต่บางครั้งต้องการ quick and dirty สำหรับ one-off utility script หรือ proof of concept ในกรณีนั้น DATA และ __END__ useful ค่อนข้าง