2011-07-13 12 views
5

Esiste un modo per chiamare implicitamente metodi sull'oggetto di un'istruzione case? IE:Ruby: caso tramite oggetto

class Foo 

    def bar 
    1 
    end 

    def baz 
    ... 
    end 

end 

Quello che mi piacerebbe essere in grado di fare è qualcosa di simile ...

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

... dove il "quando" dichiarazioni stanno valutando il ritorno di metodi su il caso oggetto. È possibile una struttura come questa? Non sono stato in grado di capire la sintassi se è così ...

risposta

9

FWIW, non è necessario passare un oggetto a un'istruzione case in 1.8.7.

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

Sono stato sorpreso come hegg.

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

+0

Penso che questa sia la cosa più vicina a ciò che voglio.Grazie :) – Andrew

+1

oh, pulito! Non lo sapevo. Detto questo, potresti combinarlo con instance_exec (vedi la mia risposta) per ottenere quasi esattamente ciò che hai delineato nella domanda. – Ian

+0

Andrew, non c'è sudore. Ian, è vero. – hellodally

6

Che case .. when fa è che chiama il metodo === sui vostri when valori, passando l'oggetto foo come argomento al metodo ===. Quindi, in questo codice:

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

Si cercherà 1 === foo, quindi 2 === foo, poi 3 === foo, finché uno di loro restituisce un valore truthy.

Un modo per rendere case .. when più potente è l'utilizzo di Procs come valori when. Non sono sicuro delle versioni precedenti di Ruby, ma nel 1.9, proc === x equivale a proc.call(x). Così si può scrivere codice come questo:

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 

Nota che non hanno nemmeno bisogno di passare foo nelle Procs, dal momento che abbiamo già accesso ad esso. Non credo che questa è una scelta molto buona di struttura di controllo per questo esempio, una semplice catena di IFS avrebbe più senso:

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

Il '' === comportamento per Proc è una cosa 1.9, non c'è alcuna menzione di esso nel [1.8.7 documenti] (http://ruby-doc.org/core-1.8.7/classes/Proc.html) ma c'è in [1.9 documenti] (http://ruby-doc.org/core /classes/Proc.html#M000550). –

+0

+1 per suggerimento 'if'. Per cominciare, questo non sembra il posto giusto per "caso", IMO. –

+0

Grazie per la risposta dettagliata, questa è stata una lettura utile. Ero davvero combattuto tra la tua risposta e quella di Hellodally - la tua è una domanda più informativa, ma il suo è quello che finirò per usare. Dato che era fondamentalmente un disguido, gli ho dato la risposta dal momento che il suo rappresentante è di circa 25.000 in meno dei tuoi :) Grazie per la spiegazione completa però! – Andrew

4

Sembra che hai intenzione di cambiare il destinatario predefinito. Questo è hacky, ma si potrebbe fare qualcosa di simile:

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 

Questo è un grande, terribile odore di codice, però, se si sta solo facendo perché siete pigri. Se lo fai perché stai creando una DSL, è un'altra cosa.

0

Per diverso scenario in cui si desidera verificare il valore metodo truthy di un oggetto

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" 
Problemi correlati