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


108

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 ...

Réponses:


196

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

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

A.loudly "my message"

# >> MY MESSAGE

4
aussisingleton_class.define_method
Pyro

@Pyro Juste pour clarifier, voudriez-vous simplement aller, singleton_class.define_method :loudly do |message|etc.?
Joshua Pinter

25

Je préfère utiliser send to call 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

2
Merci! Il existe certainement des moyens de rendre cela plus agréable pour vous-même. Mais si vous travaillez sur un plugin open source, par exemple, je pense qu'il est plus agréable de ne pas obstruer l'espace de noms metaclass, donc c'est bien de connaître le raccourci simple et autonome.
Chinasaur

J'ai décidé d'aller avec ma réponse originale. Je crois comprendre que l'utilisation de send () pour accéder aux méthodes privées si vous partez dans Ruby 1.9, donc cela ne semble pas être une bonne chose à utiliser. De plus, si vous définissez plus d'une méthode, l'instance_evaling d'un bloc est plus propre.
Chinasaur

@Vincent Robert un lien qui expliquerait la magie de la méthode métaclasse?
Amol Pujari

classe << soi; soi; fin; rouvre simplement la classe de self (class << self) et retourne ensuite cette classe (self) pour renvoyer en fait la métaclasse de self.
Vincent Robert

10

C'est le moyen le plus simple dans Ruby 1.8+:

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

1
J'aime vraiment celle-ci. Petit, soigné, se lit bien et il est portable. Bien sûr, vous pouvez me demander ce que je fais avec ruby ​​1.8 en 2013 ...
A Fader Darkly

8

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

Mise à jour : à partir de la contribution de VR ci-dessous; une méthode plus concise (tant que vous ne définissez qu'une seule 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 nécessairement une bonne idée (je crois comprendre que cela disparaîtra dans Ruby 1.9).


La meilleure alternative (?) Peut être de mettre des choses dans un module et ensuite de faire étendre le module create_class_method à la classe ??? Voir: blog.jayfields.com/2008/07/ruby-underuse-of-modules.html
Chinasaur

6

A utiliser dans Rails si vous souhaitez définir dynamiquement des méthodes de classe 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

-1

Vous pouvez également faire quelque chose comme ça sans vous fier à 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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.