Spacing around parentheses in Ruby
I've recently had some problems getting a path working properly. The solution turned out to be simple but I had an unexpected problem that stopped me getting to it. The following line, included in an erb template, works perfectly:
<%= button_to "Confirm delivery", delivery_confirm_path( @delivery ) %>
This one doesn't:
<%= button_t开发者_如何学编程o "Confirm delivery", delivery_confirm_path ( @delivery ) %>
It seems spacing before the opening parenthesis is not allowed in this case. The error I get is this:
/app/views/deliveries/_delivery_buttons.html.erb:22: syntax error,
unexpected tLPAREN_ARG, expecting keyword_do or '{' or '('
...ivery", delivery_confirm_path ( @delivery ) );@output_buffer...
... ^
Can anybody explain why this causes an error?
EDIT: for info, this is Ruby 1.9.2 and Rails 3.0.9, on Windows 7 64-bit
Anyone that puts a space before a parameter-list parentheses gets what they deserve, I say!
The problem is that it's closing out the call to button_to
in the second example (space before the parentheses) and doesn't know what to do next.
I'm not sure if this is how ruby's parser actually works, but I think of it this way: The comma before delivery_confirm_path
has higher precedence than the parentheses, unless you get rid of the space.
The parser sees the method call as this:
button_to "Confirm delivery", delivery_confirm_path
In other words, delivery_confirm_path
is parsed as a method call without arguments. But then the parser sees the dangling ( @delivery )
and it's not valid syntax because it follows the button_to
method call. It's as though you had this invalid syntax:
button_to("Confirm delivery", delivery_confirm_path) ( @delivery )
You can avoid the comma-precedence by doing this instead:
button_to "Confirm delivery", (delivery_confirm_path ( @delivery ))
But it's usually easier to just remove the space.
The principle to remember is that if there's a space before parentheses with a method call, the parentheses are used as grouping, and not as method-call parentheses.
Here are some examples to help. I use the following method in my examples:
def foo(*args); puts args.inspect; true; end
If you're on ruby 1.9, I suggest turning on warnings when running the examples: $-w = true
. This will display warning: (...) interpreted as grouped expression
if you have space before parentheses.
These two lines are syntactically equivalent:
foo (1)
foo 1
That's because (1)
as a grouped expression is just 1
.
What good is grouping?
One reason is just for more readability. You might consider it easier to understand with parens in this expression versus without:
foo (2 + 3)
foo 2 + 3
Another reasons is precedence. Let's say I have a low-precedence operation, like the and
operator. Because the method call has higher precedence, the and
is evaluated after the call. This prints [2]
and returns 3
:
foo 2 and 3 # same as foo(2) and 3, i.e. true and 3
But this prints [3]
and returns true
:
foo (2 and 3) # the grouped expr returns 3, which is passed to foo
Note, however that the and
example is somewhat contrived, because ruby doesn't allow removal of the preceding space. (I'm not sure why, since &&
is allowed instead of and
.) But you get the idea.
foo(2 and 3) # syntax error - but why?? I still don't understand.
foo(2 && 3) # works fine. This is strangely inconsistent.
This demonstrates that removing the space before a method call will elevate the method-call precedence above a comma:
foo 1, foo 2 # syntax error; the 2 is dangling
foo 1, foo(2) # ok
Another gotcha is argument lists.
foo 2, 3 # both are treated as args to the method call
foo (2, 3) # syntax error, because "2, 3" is grouped as an expression, but is not a valid one
You can't use a space between a function and its arguments. In fact, in rails the parser engine wants the parenthesis of the function just after its call. That's the rails' life.
Hope to help you.
Thank you
精彩评论