คุณอาจเคยอ่านเกี่ยวกับ 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 เป็นเทคนิคที่ทรงพลังและยืดหยุ่น แต่เมื่อใดก็ตามที่คุณต้องการบรรลุเป้าหมาย โปรดจำคำพูดที่ว่า:“พลังอันยิ่งใหญ่มาพร้อมความรับผิดชอบที่ยิ่งใหญ่”
หากคุณชอบโพสต์นี้อย่าลืมสมัครรับจดหมายข่าวของฉัน 🙂