2

The following code results in an error

Example 1

if params[:id] == '2' || params.has_key? :id
  abort('params id = 2 or nothing')
end


syntax error, unexpected tSYMBEG, expecting keyword_then or ';' or '\n'
if params[:id] == '2' || params.has_key? :id

However, switching the conditional statements || adding parentheses works 100%.

Example 2

if params.has_key? :id || params[:id] == '2' 
  abort('params id = 2 or nothing')
end

Example 3

if (params[:id] == '2') || (params.has_key? :id)
  abort('params id = 2 or nothing')
end

Can anyone explain to me why Example 1 will result in an error ?

Thanks

4 Answers4

4

Your problem is happening at:

params[:id] == '2' || params.has_key? :id

which can be simplified to:

:foo || some_method :bar

which causes the same error. This expression is in principle, ambiguous between

(:foo || some_method) :bar         (1)

and

:foo || (some_method :bar)         (2)

When an expression is ambiguous, it is resolved by other factors. One factor, operator precedence tells nothing here about disambiguating between (1) and (2). The next factor is linear order. Since || appears before argument application () (omitted) in the expression in question, the former applies before the latter. Therefore, the expression is interpreted as (1). Since (:foo || some_method) would then be parsed as an expression, there would be two expressions next to each other. That is ungrammatical, just as:

:baz :bar

is ungrammatical.

In fact, if you switch the order as:

some_method :bar || :foo

then, it will be interpreted as

(some_method :bar) || :foo

for the same reason, and syntax error will disappear.

Also when you resolve ambiguity by explicitly using parentheses to indicate argument application:

:foo || some_method(:bar)

then there is no ambiguity needed to be resolved, and the syntax error disappears.

sawa
  • 165,429
  • 45
  • 277
  • 381
  • Thanks for the great explanation. This is a helpful link : http://www.techotopia.com/index.php/Ruby_Operator_Precedence – Deon Heunis Jul 18 '13 at 22:56
1

Your :id is a symbol in Ruby.

a = {'id' => 'a', 'value' => 'value'}

a.has_key? 'id'
=> true

a.has_key? :id
=> false

So you need to change your code:

if params[:id] == '2' or params.has_key? 'id'
  abort('params id = 2 or nothing')
end

Note: If you are going to use this code checking for key before checking for value makes more sense.

Note #2: Tested with :

params = {'id' => 'a', 'value' => 'value'}

if params[:id] == '2' or params.has_key? 'id'
  abort('params id = 2 or nothing')
end

and Ruby 2.0.0

Also check this question for more information about Ruby Symbols.

Community
  • 1
  • 1
Paulo Fidalgo
  • 21,709
  • 7
  • 99
  • 115
0

It has to do with how Ruby evaluates that if statement.

Example 1 is like if (params[:id] == '2' || params.has_key?) :id

which resolves in an unexpected symbol error as you can see syntax error, unexpected tSYMBEG.

gmaliar
  • 5,294
  • 1
  • 28
  • 36
0

As guy says... the parser doesn't assume that the symbol is a parameter for the has_key? method

You can bypass the error by explicitly coding the parentheses

if params[:id] == '2' || params.has_key?(:id)
SteveTurczyn
  • 36,057
  • 6
  • 41
  • 53
  • IMHO, that's a stupid assumption to make, considering that has_key ALWAYS needs a parameter ? 100% Agree that adding the parentheses will work, it's just confusing when you need to (best practices) parentheses and when you shouldn't. – Deon Heunis Jul 18 '13 at 10:21
  • ouch, for that I got downgraded? :) The parser doesn't necessarily know that has_key? needs a parameter.. it might be a user method. Off-hand I can't think of a legitimate construction of ` if condition_a || condition_b :symbol` so what the hey, it might be a 'bug' in the Ruby parser... I don't know if it's been reported as so to the ruby core team via redmine – SteveTurczyn Jul 18 '13 at 10:41
  • Wasn't me that downgraded you ;) I don't have enough reputation points. @sawa answer seems legit, although I don't agree with how ruby parses that code. – Deon Heunis Jul 18 '13 at 11:35