2011-07-28 22 views
5

[rubí 1,8]¿Cómo crear dinámicamente métodos de instancia en tiempo de ejecución?

Supongamos que tengo:

dummy "string" do 
    puts "thing" 
end 

Ahora, esto es una llamada a un método que tiene como argumentos de entrada una cadena y un bloque. Bonito.

Ahora supongo que puedo tener muchas llamadas similares (diferentes nombres de métodos, mismos argumentos). Ejemplo:

otherdummy "string" do 
    puts "thing" 
end 

Ahora porque no hacen lo mismo, y pueden ser cientos, no quiero crear un método de instancia para cada uno en la clase deseada. Me gustaría encontrar una forma inteligente de definir el método dinámicamente en tiempo de ejecución en función de una regla general.

¿Es esto posible? ¿Qué técnicas se usan comúnmente?

Gracias

Respuesta

8

soy especialmente aficionado de utilizar method_missing, sobre todo cuando el código que desea utilizar es muy similar en las distintas llamadas a métodos. He aquí un ejemplo de este site - cada vez que alguien llama x.boo y boo no existe, method_missing se llama con boo, los argumentos a boo, y (opcionalmente) un bloque:

class ActiveRecord::Base 
    def method_missing(meth, *args, &block) 
    if meth.to_s =~ /^find_by_(.+)$/ 
     run_find_by_method($1, *args, &block) 
    else 
     super # You *must* call super if you don't handle the 
      # method, otherwise you'll mess up Ruby's method 
      # lookup. 
    end 
    end 

    def run_find_by_method(attrs, *args, &block) 
    # Make an array of attribute names 
    attrs = attrs.split('_and_') 

    # #transpose will zip the two arrays together like so: 
    # [[:a, :b, :c], [1, 2, 3]].transpose 
    # # => [[:a, 1], [:b, 2], [:c, 3]] 
    attrs_with_args = [attrs, args].transpose 

    # Hash[] will take the passed associative array and turn it 
    # into a hash like so: 
    # Hash[[[:a, 2], [:b, 4]]] # => { :a => 2, :b => 4 } 
    conditions = Hash[attrs_with_args] 

    # #where and #all are new AREL goodness that will find all 
    # records matching our conditions 
    where(conditions).all 
    end 
end 

define_method se ve también como funcionaría para usted, pero tengo menos experiencia con él que method_missing. Aquí está el ejemplo del mismo enlace:

%w(user email food).each do |meth| 
    define_method(meth) { @data[meth.to_sym] } 
end 
+0

Gracias por ejemplo útil (necesito también para gestionar nombres de los métodos de esa forma). Definitivamente iré con 'method_missing' siendo métodos que no conozco a priori. –

6

Sí, hay algunas opciones.

El primero es method_missing. Su primer argumento es un símbolo que es el método que se llamó, y los argumentos restantes son los argumentos que se usaron.

class MyClass 
    def method_missing(meth, *args, &block) 
    # handle the method dispatch as you want; 
    # call super if you cannot resolve it 
    end 
end 

La otra opción es crear dinámicamente los métodos de instancia en tiempo de ejecución, si sabe de antemano qué métodos serán necesarios. Esto debe hacerse en la clase, y un ejemplo es la siguiente:

class MyClass 
    1.upto(1000) do |n| 
    define_method :"method_#{n}" do 
     puts "I am method #{n}!" 
    end 
    end 
end 

Es un patrón común tener define_method llamada en un método de clase que necesita para crear nuevos métodos de instancia en tiempo de ejecución.

+0

Supongo que sería 'puts '¡Soy el método # {n}!" '. De todos modos, buena respuesta! –

+0

¡Reparado, gracias! –

+0

Gracias. Muy apreciado. –

3

uso define_method:

class Bar 
end 

bar_obj = Bar.new 

class << bar_obj 
define_method :new_dynamic_method do 
    puts "content goes here" 
end 
end 

bar_obj.new_dynamic_method 

de salida:

content goes here 
Cuestiones relacionadas