¿Cómo uso define_method para crear métodos de clase?


108

Esto es útil si está intentando crear métodos de clase metaprogramáticamente:

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

Mi respuesta a seguir ...

Respuestas:


196

Creo que en Ruby 1.9 puedes hacer esto:

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

A.loudly "my message"

# >> MY MESSAGE

4
tambiénsingleton_class.define_method
Pyro

@Pyro Solo para aclarar, ¿irías, singleton_class.define_method :loudly do |message|etc.?
Joshua Pinter

25

Prefiero usar enviar para llamar a define_method, y también me gusta crear un método de metaclase para acceder a la metaclase:

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
¡Gracias! Definitivamente hay formas de hacer esto mejor para ti. Pero si está trabajando en un complemento de código abierto, por ejemplo, creo que es mejor no obstruir el espacio de nombres metaclass, por lo que es bueno saber la abreviatura fácil e independiente.
Chinasaur

Decidí seguir mi respuesta original. Tengo entendido que usar send () para acceder a métodos privados si desaparece en Ruby 1.9, por lo que no parecía una buena opción. Además, si está definiendo más de un método, instancia_evaluar un bloque es más limpio.
Chinasaur

@ Vincent Robert ¿Algún enlace que explique la magia del método de metaclase?
Amol Pujari

class << self; yo; final; simplemente reabre la clase de self (clase << self) y luego devuelve esa clase (self), por lo que en realidad devuelve la metaclase de self.
Vincent Robert

10

Esta es la forma más sencilla en Ruby 1.8+:

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

1
Realmente me gusta esta. Pequeño, ordenado, se lee bien y es portátil. Por supuesto, podría preguntar qué estoy haciendo usando ruby ​​1.8 en 2013 ...
A Fader Darkly

8

Derivado de: Jay and Why , quienes también brindan formas de hacer esto más bonito.

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

Actualización : de la contribución de VR a continuación; un método más conciso (siempre que solo esté definiendo un método de esta manera) que aún es independiente:

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

pero tenga en cuenta que usar send () para acceder a métodos privados como define_method () no es necesariamente una buena idea (tengo entendido que desaparecerá en Ruby 1.9).


¿Mejor (?) Alternativa puede ser poner cosas en un módulo y luego hacer que su create_class_method extienda el módulo a la clase ??? Ver: blog.jayfields.com/2008/07/ruby-underuse-of-modules.html
Chinasaur

6

Para ser utilizado en Rails si desea definir métodos de clase dinámicamente por preocupación:

module Concerns::Testable
  extend ActiveSupport::Concern

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

-1

También puede hacer algo como esto sin depender de 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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.