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

Ruby Metaprogramming:ตัวอย่างในโลกแห่งความจริง

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

หากคุณชอบโพสต์นี้อย่าลืมสมัครรับจดหมายข่าวของฉัน 🙂