web-dev-qa-db-fra.com

Comment utiliser define_method pour créer des méthodes de classe?

Ceci est utile si vous essayez de créer des méthodes de classe de manière métaprogrammée:

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

Ma réponse à suivre ...

98
Chinasaur

Je pense que dans Ruby 1.9, vous pouvez le faire:

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

A.loudly "my message"

# >> MY MESSAGE
181
fguillen

Je préfère utiliser send pour appeler define_method, et j'aime aussi créer une méthode de métaclasse pour accéder à la métaclasse:

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
25
Vincent Robert

C'est la manière la plus simple de Ruby 1.8+:

class A
  class << self
    def method_name
      ...
    end
  end
end
9
Joseph Jaber

Dérivé de: Jay et Why , qui fournissent également des moyens de rendre cela plus joli.

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

Update: extrait de la contribution de VR ci-dessous; une méthode plus concise (tant que vous ne définissez qu'une méthode de cette façon) qui est toujours autonome:

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

mais notez que l'utilisation de send () pour accéder à des méthodes privées comme define_method () n'est pas forcément une bonne idée (si j'ai bien compris, cela va disparaître dans Ruby 1.9).

8
Chinasaur

À utiliser dans Rails si vous souhaitez définir des méthodes de classe de manière dynamique, par souci:

module Concerns::Testable
  extend ActiveSupport::Concern

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

Vous pouvez aussi faire quelque chose comme ça sans faire appel à 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
0
Bijan