ฉันจะใช้ define_method เพื่อสร้างเมธอดคลาสได้อย่างไร


108

สิ่งนี้มีประโยชน์หากคุณพยายามสร้าง class method metaprogramatically:

def self.create_methods(method_name)
    # To create instance methods:
    define_method method_name do
      ...
    end

    # To create class methods that refer to the args on create_methods:
    ???
end

คำตอบของฉันที่จะติดตาม ...

คำตอบ:


196

ฉันคิดว่าใน Ruby 1.9 คุณสามารถทำได้:

class A
  define_singleton_method :loudly do |message|
    puts message.upcase
  end
end

A.loudly "my message"

# >> MY MESSAGE

4
ด้วยsingleton_class.define_method
Pyro

@Pyro แค่ชี้แจงคุณจะไปsingleton_class.define_method :loudly do |message|ฯลฯ ?
Joshua Pinter

25

ฉันชอบใช้ send to call define_method และฉันก็ต้องการสร้างเมธอด metaclass เพื่อเข้าถึง metaclass:

class Object
  def metaclass
    class << self
      self
    end
  end
end

class MyClass
  # Defines MyClass.my_method
  self.metaclass.send(:define_method, :my_method) do
    ...
  end
end

2
ขอบคุณ! มีวิธีทำให้ดีกว่านี้ด้วยตัวคุณเองอย่างแน่นอน แต่ถ้าคุณกำลังทำงานกับปลั๊กอินโอเพนซอร์สฉันคิดว่ามันจะดีกว่าที่จะไม่อุดตันเนมสเปซด้วยmetaclassดังนั้นจึงเป็นการดีที่จะรู้ชวเลขแบบสแตนด์อโลนที่ง่ายและสะดวก
Chinasaur

ฉันตัดสินใจไปกับคำตอบเดิมของฉัน ความเข้าใจของฉันคือการใช้ send () เพื่อเข้าถึงเมธอดส่วนตัวหากไปใน Ruby 1.9 ซึ่งดูเหมือนจะไม่ใช่สิ่งที่ดีที่จะใช้ นอกจากนี้หากคุณกำลังกำหนดวิธีการมากกว่าหนึ่งวิธีการบล็อกจะสะอาดกว่า
Chinasaur

@ Vincent Robert ลิงก์ใดที่จะอธิบายถึงความมหัศจรรย์ของวิธีการ metaclass?
Amol Pujari

ชั้น << ตนเอง; ตนเอง; จบ; เพียงแค่เปิดคลาสของตนเองอีกครั้ง (คลาส << ตนเอง) จากนั้นส่งคืนคลาสนั้น (ตนเอง) ดังนั้นจึงส่งคืนคลาสของตัวเอง
Vincent Robert

10

นี่เป็นวิธีที่ง่ายที่สุดใน Ruby 1.8+:

class A
  class << self
    def method_name
      ...
    end
  end
end

1
ฉันชอบอันนี้มาก เล็กเรียบร้อยอ่านได้ดีและพกพาได้ แน่นอนคุณสามารถถามว่าฉันใช้ Ruby 1.8 ในปี 2013 ...
A Fader Darkly

8

มาจาก: Jay and Whyซึ่งเป็นผู้จัดหาวิธีทำให้สวยขึ้น

self.create_class_method(method_name)
  (class << self; self; end).instance_eval do
    define_method method_name do
      ...
    end
  end
end

อัปเดต : จากการมีส่วนร่วมของ VR ด้านล่าง; วิธีการที่รัดกุมมากขึ้น (ตราบเท่าที่คุณกำหนดวิธีการเดียวด้วยวิธีนี้) ที่ยังคงเป็นแบบสแตนด์อโลน:

self.create_class_method(method_name)
  (class << self; self; end).send(:define_method, method_name) do
    ...
  end
end

แต่โปรดทราบว่าการใช้ send () เพื่อเข้าถึงเมธอดส่วนตัวเช่น define_method () ไม่จำเป็นต้องเป็นความคิดที่ดีเสมอไป (ความเข้าใจของฉันคือมันกำลังจะหายไปใน Ruby 1.9)


ทางเลือกที่ดีกว่า (?) อาจเป็นการใส่สิ่งต่างๆลงในโมดูลจากนั้นให้ create_class_method ของคุณขยายโมดูลไปยังคลาส ??? ดู: blog.jayfields.com/2008/07/ruby-underuse-of-modules.html
Chinasaur

6

เพื่อใช้ใน Rails หากคุณต้องการกำหนด class method แบบไดนามิกจากข้อกังวล:

module Concerns::Testable
  extend ActiveSupport::Concern

  included do 
    singleton_class.instance_eval do
      define_method(:test) do
        puts 'test'
      end
    end
  end
end

-1

คุณยังสามารถทำสิ่งนี้ได้โดยไม่ต้องอาศัย define_method:

A.class_eval do
  def self.class_method_name(param)
    puts param
  end
end

A.class_method_name("hello") # outputs "hello" and returns nil
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.