Sınıf yöntemleri oluşturmak için define_method'u nasıl kullanırım?


108

Metaprogramatik olarak sınıf yöntemleri oluşturmaya çalışıyorsanız bu yararlıdır:

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

Cevabım takip edilecek ...

Yanıtlar:


196

Ruby 1.9'da bunu yapabileceğinizi düşünüyorum:

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

A.loudly "my message"

# >> MY MESSAGE

4
ayrıcasingleton_class.define_method
Pyro

@Pyro Sadece açıklığa kavuşturmak için, sadece gider miydin singleton_class.define_method :loudly do |message|vs.?
Joshua Pinter

25

Tanımlama yöntemini çağırmak için gönder kullanmayı tercih ediyorum ve ayrıca meta sınıfa erişmek için bir meta sınıf yöntemi oluşturmayı seviyorum:

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
Teşekkürler! Kesinlikle bunu kendiniz için daha güzel yapmanın yolları var. Ancak, örneğin açık kaynaklı bir eklenti üzerinde çalışıyorsanız, ad alanını tıkamamanın daha iyi olduğunu düşünüyorum metaclass, bu yüzden kolay, bağımsız kısaltmayı bilmek güzel.
Chinasaur

Orijinal cevabımla gitmeye karar verdim. Anladığım kadarıyla, Ruby 1.9'da uzaklaşıyorsanız, özel yöntemlere erişmek için send () kullanmanın kullanılması iyi bir şey gibi görünmüyordu. Ayrıca, birden fazla yöntem tanımlıyorsanız, bir bloğun instance_evaling'i daha temizdir.
Chinasaur

@Vincent Robert, metasınıf yönteminin büyüsünü açıklayabilecek herhangi bir bağlantı?
Amol Pujari

sınıf << öz; öz; son; basitçe benlik sınıfını (sınıf << öz) yeniden açar ve sonra o sınıfı (öz) döndürür, böylece aslında öz metasınıfını döndürür.
Vincent Robert

10

Ruby 1.8+ sürümünün en basit yolu budur:

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

1
Bunu gerçekten beğendim. Küçük, temiz, iyi okur ve taşınabilir. Elbette 2013'te Ruby 1.8 kullanarak ne yaptığımı sorabilirsiniz ...
A Fader Darkly

8

: Elde Edilen Jay ve Why de bu güzel hale getirmek için yöntemler sağlar.

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

Güncelleme : Aşağıdaki VR'nin katkısından; hala bağımsız olan daha kısa bir yöntem (bu şekilde yalnızca bir yöntem tanımladığınız sürece):

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

ancak define_method () gibi özel yöntemlere erişmek için send () kullanmanın mutlaka iyi bir fikir olmadığına dikkat edin (anladığım kadarıyla Ruby 1.9'da ortadan kalkıyor).


Daha iyi (?) Bir alternatif, şeyleri bir modüle koymak ve sonra create_class_method'un modülü sınıfa genişletmesini sağlamak olabilir ??? Bakınız: blog.jayfields.com/2008/07/ruby-underuse-of-modules.html
Chinasaur

6

Sınıf yöntemlerini kaygıdan dinamik olarak tanımlamak istiyorsanız Rails'de kullanılmak üzere:

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 güvenmeden de böyle bir şey yapabilirsiniz:

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

A.class_method_name("hello") # outputs "hello" and returns nil
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.