2010-11-04 26 views
25

Tiene que haber una forma integrada de hacerlo, ¿no?Ruby: Cómo encadenar varias llamadas a métodos junto con "enviar"

class Object 
    def send_chain(arr) 
    o=self 
    arr.each{|a| o=o.send(a) } 
    return o 
    end 
end 
+1

No estoy seguro, pero he seguido caminos como este antes, y generalmente descubro que conducen a una mala implementación de algo. ¿Qué es lo que realmente estás intentando lograr? Tal vez haya un patrón de programación existente que te beneficie más. –

+2

Eh, es solo una tarea de rake generar algunas columnas de algunos modelos de ActiveRecord. Acepta una matriz de cadenas, donde cada cadena representa el atributo/método de un modelo o el atributo/método de un modelo asociado. Entonces, quiero darle algo como ["fecha", "título", "color", "autor.nombre"] para el modelo "Libro" y hacer que imprima los atributos y el atributo del nombre del autor asociado. Supongo que podría escribir métodos para Book que devolverían self.author.name, pero solo quería algo rápido para esta tarea de salida de rake única. – Zando

Respuesta

48

Acabo de funcionar a través de este y lo que realmente plantea para inyectar: ​​

def send_chain(arr) 
    arr.inject(self) {|o, a| o.send(a) } 
end 
+1

Agradable. Mejor respuesta. –

+23

Más corto: 'arr.inject (self,: send)' – Ajedi32

+11

Más seguro: 'arr.inject (self,: try)' :) – Archonic

6

No, no no es construido en la manera de hacer esto. Lo que hizo es simple y conciso, sin mencionar peligroso. Tenga cuidado al usarlo.

En otro pensamiento, esto se puede extender a aceptar los argumentos así:

class Object 
    def send_chain(*args) 
    o=self 
    args.each do |arg| 
     case arg 
     when Symbol, String 
     o = o.send arg # send single symbol or string without arguments 
     when Array 
     o = o.send *arg # smash the inner array into method name + arguments 
     else 
     raise ArgumentError 
     end 
    end 
    return o 
    end 
end 

esto dejará pasar un nombre de método con sus argumentos en una matriz, como;

test = MyObject.new 
test.send_chain :a_method, [:a_method_with_args, an_argument, another_argument], :another_method 
+2

Simplemente siendo quisquilloso aquí, pero manteniéndome en línea con las expresiones idiomáticas de Ruby, esto realmente debería ser tipado. –

+0

@edgerunner. No funcionó para mí sa.grade_accesses.send_chain (: except_grade_k,: except_grade_math_fact) NoMethodError: método indefinido 'except_grade_k 'for #

+0

@MohitJain Todos los métodos deberían regresar a self. – thisismydesign

3

Sobre la base de las respuestas anteriores, en caso de que necesite para pasar argumentos a cada método, puede utilizar esto:

def send_chain(arr) 
    Array(arr).inject(self) { |o, a| o.send(*a) } 
end 

continuación, puede utilizar el método de la siguiente manera:

arr = [:to_i, [:+, 4], :to_s, [:*, 3]] 
'1'.send_chain(arr) # => "555" 

Este método también acepta argumentos únicos.

Cuestiones relacionadas