Ruby: Case using object
Is there a way to implicitly call methods on the object of a case statement? IE:
class Foo
def bar
1
end
def baz
...
end
end
What I'd like to be able to do is something like this...
foo = Foo.new
case foo
when .bar==1 t开发者_Go百科hen "something"
when .bar==2 then "something else"
when .baz==3 then "another thing"
end
... where the "when" statements are evaluating the return of methods on the case object. Is some structure like this possible? I haven't been able to figure out the syntax if so...
FWIW, you don't need to pass an object to a case statement in 1.8.7 at all.
foo = Foo.new()
case
when foo.bar == this then that
when foo.baz == this then that
end
I was surprised as hegg.
http://www.skorks.com/2009/08/how-a-ruby-case-statement-works-and-what-you-can-do-with-it/
What case .. when
does is it calls the method ===
on your when
values, passing your foo
object as the argument to the ===
method. So in this code:
case foo
when 1 then "something"
when 2 then "something else"
when 3 then "another thing"
end
It will try 1 === foo
, then 2 === foo
, then 3 === foo
, until one of them returns a truthy value.
One way of making case .. when
more powerful is using Procs as the when
values. I'm not sure about earlier versions of Ruby, but in 1.9, proc === x
is equivalent to proc.call(x)
. So you can write code like this:
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
Note that we don't even have to pass foo
into the Procs, since we already have access to it. I don't think this is a very good choice of control structure for this example, a simple chain of ifs would make more sense:
if foo.bar == 1
"something"
elsif foo.bar == 2
"something else"
elsif foo.baz == 3
"another thing"
end
For different scenario where you want to test truthy method value of an object
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"
It looks like you're wanting to change the default receiver. This is hacky, but you could do something like:
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
That's a big, terrible code smell, though, if you're just doing it because you're lazy. If you're doing it because you're creating a DSL, that's something else.
精彩评论