开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜