คุณอาจเคยอ่านเกี่ยวกับ Ruby metaprogramming มาก่อน
แต่…
อาจสร้างความสับสนเล็กน้อยหากคุณไม่มีตัวอย่างที่เฉพาะเจาะจง
นั่นเป็นเหตุผลในบทความนี้ :
เราจะดูโปรเจ็กต์โอเพ่นซอร์สยอดนิยมบางโปรเจ็กต์โดยใช้ Ruby metaprogramming
โครงการที่ชอบ :
- ราง
- ซินาตรา
- อัญมณีคลิปหนีบกระดาษ
ทั้งหมดใช้เมตาโปรแกรมมิง .
มาดูโค้ดกันและค้นหาว่าพวกเขากำลังทำอะไรอยู่!
ตัวอย่างราง
Rails ใช้งาน metaprogramming อย่างหนัก ดังนั้นจึงเป็นจุดเริ่มต้นที่ดีในการเริ่มต้นค้นหา
ตัวอย่างเช่น :
เมื่อคุณต้องการตรวจสอบสภาพแวดล้อมปัจจุบันในแอป Rails คุณทำสิ่งต่อไปนี้
Rails.env.production?
แต่อะไรคือ env ? และมันทำงานอย่างไร?
คำตอบอยู่ใน StringInquirer class ซึ่งเป็นคลาสย่อยของ String . คลาสนี้ใช้ method_missing เพื่อเรียก env.production? แทน env == production .
นี่คือลักษณะของโค้ด :
def method_missing(method_name, *arguments)
if method_name[-1] == '?'
self == method_name[0..-2]
else
super
end
end
นี่คือคำพูด :
“ถ้าชื่อเมธอดลงท้ายด้วยเครื่องหมายคำถาม ให้ทำการเปรียบเทียบ มิฉะนั้น ให้ขึ้นไปบนสายบรรพบุรุษ”
รหัสต้นฉบับสามารถพบได้ที่นี่
คณะผู้แทนซินาตรา
หากคุณเคยใช้ซินาตรามาก่อน คุณอาจรู้ว่ามีสองวิธีในการกำหนดเส้นทางของคุณ:
- โดยใช้
get/postเมธอดโดยตรง นอกคลาสใดๆ - โดยการกำหนดคลาสที่สืบทอดมาจาก
Sinatra::Application.
เมธอด Sinatra DSL (ภาษาเฉพาะโดเมน) ถูกกำหนดไว้ใน Sinatra::Application แล้วคุณจะใช้มันนอกชั้นเรียนได้อย่างไร
มีสองสิ่งที่เกิดขึ้นที่นี่ :
- เมตาโปรแกรมมิ่ง
- ส่วนขยายโมดูล
Sinatra กำหนด Sinatra::Delegator โมดูลภายใน base.rb ซึ่งใช้ในการมอบหมายเมธอดการเรียกไปยัง target .
เป้าหมายถูกตั้งค่าเป็น Sinatra::Application โดยค่าเริ่มต้น
นี่เป็นเวอร์ชันที่เรียบง่ายของ delegate วิธีการเรียนที่กำหนดไว้ใน Sinatra::Delegator :
def self.delegate(*methods)
methods.each do |method_name|
define_method(method_name) do |*args, &block|
Delegator.target.send(method_name, *args, &block)
end
end
end
delegate :get, :patch, :put, :post, :delete
รหัสนี้สร้างชุดวิธีการ (ด้วย define_method ) ที่จะส่งต่อวิธีการเรียกไปยัง Sinatra::Application (ค่าเริ่มต้นสำหรับ Delegator.target )
จากนั้น main วัตถุถูกขยายด้วยวิธีการที่กำหนดไว้ใน Sinatra::Delegator ซึ่งทำให้ Sinatra DSL ใช้งานได้นอก Sinatra::Application ชั้นเรียน
เป็นที่น่าสังเกตว่า Ruby มีคลาสในตัวสองคลาสสำหรับการมอบหมายเมธอด ในกรณีที่คุณต้องการ:Delegator &Forwardable
อัญมณีคลิปหนีบกระดาษ
คลิปหนีบกระดาษเป็นอัญมณีที่ช่วยให้แอปพลิเคชันของคุณจัดการการอัปโหลดไฟล์ ไฟล์เชื่อมโยงกับ ActiveRecord โมเดล
คุณสามารถกำหนดไฟล์แนบในโมเดลโดยใช้ has_attached_file วิธีการ
ถูกใจสิ่งนี้ :
class User
has_attached_file :avatar, :styles => { :normal => "100x100#" }
end
เมธอดนี้กำหนดไว้ใน paperclip.rb และใช้ metaprogramming เพื่อกำหนดวิธีการในโมเดล
วิธีนี้สามารถใช้เพื่อเข้าถึงไฟล์แนบได้ (ซึ่งเป็นตัวอย่างของ Paperclip::Attachment คลาส)
เช่น ถ้าไฟล์แนบคือ :avatar , Paperclip จะกำหนด avatar วิธีการของโมเดล
นี่คือ define_instance_getter วิธีการซึ่งมีหน้าที่:
# @name => The name of the attachment
# @klass => The ActiveRecord model where this method is being defined
def define_instance_getter
name = @name
options = @options
@klass.send :define_method, @name do |*args|
ivar = "@attachment_#{name}"
attachment = instance_variable_get(ivar)
if attachment.nil?
attachment = Attachment.new(name, self, options)
instance_variable_set(ivar, attachment)
end
end
end
จากรหัสนี้ เราจะเห็นว่าวัตถุที่แนบถูกเก็บไว้ภายใต้ @attachment_#{name} ตัวแปรอินสแตนซ์
ในตัวอย่างของเราคือ @attachment_avatar .
จากนั้นจะตรวจสอบว่าไฟล์แนบนี้มีอยู่แล้วหรือไม่ และหากไม่มี ระบบจะสร้าง Attachment ใหม่ วัตถุและตั้งค่าตัวแปรอินสแตนซ์
นี่คือซอร์สโค้ดต้นฉบับ
บทสรุป
ดังที่คุณได้เห็นแล้วว่า Metaprogramming เป็นเทคนิคที่ทรงพลังและยืดหยุ่น แต่เมื่อใดก็ตามที่คุณต้องการบรรลุเป้าหมาย โปรดจำคำพูดที่ว่า:“พลังอันยิ่งใหญ่มาพร้อมความรับผิดชอบที่ยิ่งใหญ่”
หากคุณชอบโพสต์นี้อย่าลืมสมัครรับจดหมายข่าวของฉัน 🙂