3

It is easy to have a condition at the head of a chain and share the rest:

if condition
    get_this
else
    get_that
end
.foo.bar.baz

But often, I want a condition in the middle or at the tail of a chain. The best I can think of is using instance_eval:

foo.bar
.instance_eval{
    if condition
        get_this
    else
        get_that
    end
}
.baz

But I have concern that calling instance_eval is heavy, so I actually end up not doing it. Is it worth doing so? Is there a better way, or should I just simply write:

if condition
    foo.bar.get_this
else
    foo.bar.get_that
end
.baz
sawa
  • 165,429
  • 45
  • 277
  • 381
  • +1 for the syntax in the first snippet. – Andrew Grimm Jan 02 '12 at 20:34
  • @AndrewGrimm Thanks for the comment, but what do you mean by "first snippet"? – sawa Jan 02 '12 at 20:41
  • A snippet is a small piece of code. I'm referring to the `if condition ... .foo.bar.baz` one. Snippet a diminutive of "snip", according to [wiktionary](http://en.wiktionary.org/wiki/snippet). "Snip" itself is probably onomatopoeia, which in Japanese is apparently チョキチョキ. – Andrew Grimm Jan 02 '12 at 21:02
  • @AndrewGrimm I know the word. I didn't understand what you meant in the context. So, just directly continuing the chain after the condition is already good enough of a syntax? – sawa Jan 02 '12 at 21:27
  • Yes. I've seen `foo = (condition)`, but not `(condition).foo.bar.baz` before. – Andrew Grimm Jan 02 '12 at 21:35
  • Related: http://stackoverflow.com/q/1797189/498594 – Kelvin Apr 01 '13 at 16:39
  • See [my answer](http://stackoverflow.com/a/10110969/498594) for a general purpose solution. – Kelvin Apr 01 '13 at 16:40

3 Answers3

3

How about using Object#send to conditionally call a method:

foo.bar.send(condition? ? :get_this : :get_that).baz

In a more complicated case, with multiple/different parameters one could use the splat operator to unwrap an array into parameters:

foo.bar.send(
  * case conditional
    when qux
      [:get_this, param1]
    when quux
      [:get_that, param1, param2]
    else
      [:get_other, param1, param2]
    end
).baz
Community
  • 1
  • 1
clyfe
  • 23,695
  • 8
  • 85
  • 109
  • I see. That partially solves the problem, i.e., when `get_this` and `get_that` are simplistic method (possibly with arguments). Can you also think of a way when they are more complicated? Your answer is a nice idea, though. – sawa Dec 30 '11 at 10:05
  • Yeah. That's what I mentioned as a simplistic (single) method. But sometimes, there are sequences of things to do separately under different conditions. But, your answer is nice. – sawa Dec 30 '11 at 10:36
  • Thanks. I think I would either need to do it like your answer or Iwe's answer. – sawa Dec 30 '11 at 17:29
  • Well, I answered from the control-flow POV in sync with the question tone. Of course, otherwise, when code gets heavy it's refactored into thin methods. – clyfe Dec 30 '11 at 17:34
3

Why not provide a method on bar which accepts a boolean value and then acts accordingly? This encapsulates the logic and can even shield complex logic or multiple conditions, without getting too messy.

class Bar
  def get_what(condition)
    case condition
      when qux; get_this(param1)
      when quux; get_that(param1, param2)
      else get_other(param1)
    end
  end
end

# assuming `bar` is an instance of `Bar`
foo.bar.get_what(cond).baz

PS: Depending on the use case I often try to avoid stuff like overly long message chains (because according to the Law of Demeter (Wikipedia) this might be considered bad practice :)), though when working with hashes and arrays in ruby it's kind of useful. So I suppose you have a valid use case for using a long message chain.

lwe
  • 2,625
  • 20
  • 19
2

Sometimes a low-tech solution is the best:

my_bar = foo.bar
if condition
  my_thing = my_bar.get_this
else
  my_thing = my_bar.get_that
end
my_thing.baz

Seriously, I consider it far more important that the meaning of source code is 100% clear to everybody (including yourself some time in the future) than it being shortened by a line or two.

Lindydancer
  • 25,428
  • 4
  • 49
  • 68