简体   繁体   中英

parenthetical parameters in Ruby / Rails

Learning Ruby / Rails, and encountered this with ERB templating:

Works :

<%= link_to image_tag(link.screenshot.url(:thumb)), link.screenshot.url(:original) %>

Doesn't work :

<%= link_to image_tag link.screenshot.url(:thumb), link.screenshot.url(:original) %>

In the first one, the parameters for image_tag are in parentheses. For the other, they are not.

I've come to understand in Ruby that methods don't always need their parameters in parentheses, but the convention of skipping them seems to be problematic in a situation like this.

Just deal with it? Pecularity of ERB templates? Or am I missing something bigger?

As always, thanks.

Lack of parentheses in this example indicates the problem of parsing such an expression. Generally, in Ruby you can omit them when it is clear "how to pass arguments".

Let's consider the following situation - I have add and multi methods defined:

def add(*args)
  args.inject(:+)
end

def multi(*args)
  args.inject(:*)
end

So the following is clear:

add 1, 2, 3, 4
# => 10

multi 1, 2, 3, 4
# => 24

but in case:

add 1, multi 2, 3, 4

what would be expected output? You can consider couple scenarios:

add(1, multi(2, 3), 4)
# => 11

add(1, multi(2, 3, 4))
# => 25

add(1, multi(2), 3, 4)
# => 10

When parentheses used - you can calculate the output, because you know what parameters are used for which method. When used without - you can't, so Ruby is unable to do so as well.

Let's get back to the method you've mentioned. Ruby doesn't know if method:

<%= link_to image_tag link.screenshot.url(:thumb), link.screenshot.url(:original) %>

should be treated like:

<%= link_to image_tag(link.screenshot.url(:thumb)), link.screenshot.url(:original) %>

or:

<%= link_to image_tag(link.screenshot.url(:thumb), link.screenshot.url(:original)) %>

Hope it sheds some light on the problem!

Good luck!

When determining how to interpret nested function calls with omitted parentheses, ruby doesn't look at the method signatures and how many arguments they allow. Instead, it just follows a fixed set of precedence rules, which in your case leads to a different result with and without the parentheses:

def a(*arguments)
  puts "a called with #{arguments.inspect}"
  return :a_result
end

def b(*arguments)
  puts "b called with #{arguments.inspect}"
  return :b_result
end

def c(*arguments)
  puts "c called with #{arguments.inspect}"
  return :c_result
end

def d(*arguments)
  puts "d called with #{arguments.inspect}"
  return :d_result
end

puts (a b(c(:foo)), d(:bar)).inspect
# c called with [:foo]
# b called with [:c_result]
# d called with [:bar]
# a called with [:b_result, :d_result]
# :a_result

puts (a b c(:foo), d(:bar) ).inspect
# c called with [:foo]
# d called with [:bar]
# b called with [:c_result, :d_result]
# a called with [:b_result]
# :a_result

Thus, with b s parens ommitted, the call is interpreted equivalently to

a(b(c(:foo), d(:bar)))

If one of the functions doesn't allow for these numbers of arguments, the line will fail. More dangerously, when the functions do allow for a variable number of arguments, as my example functions here do, you'll perform a different operation than you might have thought and might only notice the mistake much, much later.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM