2011-07-13 16 views
5

¿Hay alguna manera de invocar implícitamente métodos en el objeto de una declaración de caso? IE:Ruby: caso que usa el objeto

class Foo 

    def bar 
    1 
    end 

    def baz 
    ... 
    end 

end 

Lo que me gustaría ser capaz de hacer algo como esto ...

foo = Foo.new 
case foo 
when .bar==1 then "something" 
when .bar==2 then "something else" 
when .baz==3 then "another thing" 
end 

... donde el "cuándo" declaraciones están evaluando el rendimiento de los métodos de el objeto del caso ¿Es posible alguna estructura como esta? No he sido capaz de averiguar la sintaxis de ser así ...

Respuesta

9

FWIW, no necesita pasar un objeto a una declaración de caso en 1.8.7 en absoluto.

foo = Foo.new() 
case 
when foo.bar == this then that 
when foo.baz == this then that 
end 

Me sorprendió como hegg.

http://www.skorks.com/2009/08/how-a-ruby-case-statement-works-and-what-you-can-do-with-it/

+0

Creo que esto es lo más cercano a lo que quiero.Gracias :) – Andrew

+1

¡oh, limpio! No lo sabía Dado eso, podrías combinarlo con instance_exec (ver mi respuesta) para obtener casi exactamente lo que delineaste en la pregunta. – Ian

+0

Andrew, sin sudar. Ian, cierto eso. – hellodally

6

Lo case .. when hace es que llama al método === en sus when valores, pasando el objeto foo como argumento al método ===. Así que en este código:

case foo 
when 1 then "something" 
when 2 then "something else" 
when 3 then "another thing" 
end 

Se tratará 1 === foo, entonces 2 === foo, entonces 3 === foo, hasta que uno de ellos devuelva un valor Truthy.

Una forma de hacer case .. when más poderosa es utilizar Procs como los valores when. No estoy seguro acerca de las versiones anteriores de Ruby, pero en 1.9, proc === x es equivalente a proc.call(x). Para que pueda escribir código como este:

case foo 
when Proc.new { foo.bar == 1 } then "something" 
when Proc.new { foo.bar == 2 } then "something else" 
when Proc.new { foo.baz == 3 } then "another thing" 
end 

en cuenta que no tienen ni siquiera para pasar foo en los procs, puesto que ya tenemos acceso a ella. No creo que esta es una muy buena elección de la estructura de control para este ejemplo, una simple cadena de IFS tendría más sentido:

if foo.bar == 1 
    "something" 
elsif foo.bar == 2 
    "something else" 
elsif foo.baz == 3 
    "another thing" 
end 
+1

El '' === comportamiento para Proc es una cosa 1.9, no hay mención de ella en el [1.8.7 documentos] (http://ruby-doc.org/core-1.8.7/classes/Proc.html) pero hay en [1.9 documentos] (http://ruby-doc.org/core /classes/Proc.html#M000550). –

+0

+1 para la sugerencia 'if'. Esto no parece ser un lugar apropiado para 'case' para comenzar, IMO. –

+0

Gracias por la respuesta detallada, esta fue una lectura útil. Estaba realmente dividido entre tu respuesta y la de hellodally: tu es más informativa en general, pero la suya es la que realmente terminaré usando. Teniendo en cuenta que básicamente era un desastre, le di la respuesta, ya que su representante es aproximadamente 25,000 menos que el tuyo :) ¡Gracias por la completa explicación! – Andrew

4

Parece que usted está queriendo cambiar el receptor predeterminado. Esta es hacky, pero se podría hacer algo como:

string = Foo.new.instance_eval do 
    if bar==1 then "something" 
    elsif bar==2 then "something else" 
    elsif baz==3 then "another thing" 
    end 
end 

eso es un gran olor código terribles, sin embargo, si sólo está haciendo porque usted es perezoso. Si lo haces porque estás creando un DSL, eso es otra cosa.

0

Para diferente escenario en el que desea probar valor del método de Truthy de un objeto

class Foo 
    def approved? 
    false 
    end 

    def pending? 
    true 
    end 
end 

foo = Foo.new 
case foo 
when :approved?.to_proc 
    puts 'Green' 
when :pending?.to_proc 
    puts 'Amber' 
else 
    puts 'Grey' 
end 

# This will output: "Amber" 
Cuestiones relacionadas