Ruby on Rails 3, syntax error while creating a new object - ruby

I am still learning Ruby, and still copy pasting from my manual. But I run on a problem, that I dont know how to explain and what am I doing wrong. So here it is:
I want to create a new object with this:
second_page = Page.new ( :name=>"Second page", :position=>1, :permalink => "second" )
and I got a error:
Loading development environment (Rails 3.0.10)
ruby-1.9.2-p290 :001 > second_page = Page.new ( :name=>"Second page", :position=>1, :permalink => "second" )
SyntaxError: (irb):1: syntax error, unexpected tASSOC, expecting ')'
...econd_page = Page.new ( :name=>"Second page", :position=>1, ...
... ^
(irb):1: syntax error, unexpected ',', expecting $end
...age.new ( :name=>"Second page", :position=>1, :permalink => ...
... ^
from /usr/local/rvm/gems/ruby-1.9.2-p290/gems/railties-3.0.10/lib/rails/commands/console.rb:44:in `start'
from /usr/local/rvm/gems/ruby-1.9.2-p290/gems/railties-3.0.10/lib/rails/commands/console.rb:8:in `start'
from /usr/local/rvm/gems/ruby-1.9.2-p290/gems/railties-3.0.10/lib/rails/commands.rb:23:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
but, if I do this:
ruby-1.9.2-p290 :002 > second_page = Page.new :name=>"Second page", :position=>1, :permalink => "second"
=> #<Page id: nil, subject_id: nil, name: "Second page", permalink: "second", position: 1, visible: false, created_at: nil, updated_at: nil>
that seems to work.
I got example from manual, and I am wondering what is going on?
And without () I dont know how can I do stuff with that object?
Thank you

Ruby doesn't allow you to put spaces before round brackets if you choose to place them. This should work:
second_page = Page.new( :name=>"Second page", :position=>1, :permalink => "second" )

What you have here is the parser trying to resolve syntax ambiguities. Page.new accepts a single argument: a hash containing attributes which should be set on the newly created active record object.
If you now call the method without any parentheses, it is not initially clear what the arguments are. Thus the parser is smart enough to figure out it should be a hash in this case.
If you actually write the parentheses, you have to be a bit more specific and have to actually write down the hash braces too. Thus the following statements are equivalent:
first_page = Page.new :foo => "Bar"
second_page = Page.new({:foo => "Bar"})
third_page = Page.new ({:foo => "Bar"})
In most cases, parentheses are optional in method calls in Ruby. But only if there aren't any ambiguities. If in doubt, always specify the parentheses. Note that Ruby 1.9 changed the syntax here and is thus a bit more strict.

In ruby parenthesis to the method arguments are not necessary. So,
object.method()
# is same as
object.method
object.method(param1, param2)
# is same as
object.method param1, param2
There is another popular syntax for passing arbitrary number of parameters:
def print_a(*params)
puts params.inspect
end
print_a "a"
#prints: ["a"]
print_a "a", "b"
#prints: ["a", "b"]
print_a "a", "b", 2, :four => 4
#prints: ["a", "b", 2, {:four=>4}]
print_a "a", "b", 3, :four => 4, :five => 5
#prints: ["a", "b", 3, {:four=>4, :five=>5}]
As you may have noticed in the last example ruby is smart enough to detect hashes and aggregate the key value pairs in a single hash argument. But it only works if the hash is last argument.
print_a("a", "b", :four => 4, :five => 5, 3)
# gives error: syntax error, unexpected '\n', expecting tASSOC
# converting the hash to an explicit hash works again
print_a "a", "b", {:four => 4, :five => 5}, 3
# ["a", "b", {:four=>4, :five=>5}, 3]

Related

Does declaring a hash with curly braces require assignment?

I thought I could declare a hash using either {} or Hash[], but curly braces doesn't seem to work unless I assign it to a variable (see below). I'm guessing irb gets confused about whether it's a hash or a block. Is that correct, or am I declaring the hash with curly braces incorrectly?
>> puts h = { :a=>1, :b=>2 }
{:a=>1, :b=>2}
>> puts { :a=>1, :b=>2 }
SyntaxError: (irb):58: syntax error, unexpected =>, expecting '}'
puts { :a=>1, :b=>2 }
^
from /usr/bin/irb:12:in `<main>'
>> puts Hash[ :a=>1, :b=>2 ]
{:a=>1, :b=>2}
>>
You can fix this by adding parentheses:
puts({ :a => 1, :b => 2 })
# {:a=>1, :b=>2}
If you leave off the parentheses, Ruby will interpret the curly braces as a block:
puts { :a => 1, :b => 2 }
# SyntaxError: unexpected ':', expecting '}'
# semantically the same as
puts do
:a => 1, :b => 2
end
# SyntaxError: unexpected =>, expecting keyword_end
But you can also omit both parentheses and Ruby will treat it as a Hash:
puts :a => 1, :b => 2
# {:a=>1, :b=>2}
# same as
puts(:a => 1, :b => 2)
# {:a=>1, :b=>2}
# same as
puts({:a => 1, :b => 2})
# {:a=>1, :b=>2}
And you can even use short notation for symbol keys ;-)
puts a: 1, b: 2
# {:a=>1, :b=>2}
It just requires unambiguous syntax, e.g.,
irb> puts({ :a => 1, :b => 2 })
{:a=>1, :b=>2}
Your puts { ... } looks to ruby as though you are passing a block of code. Since puts is a method that can take arguments and/or a block you need to specify that the stuff inside the curly brackets is intended to be an argument and not a block. Try:
puts( { a: 1, b: 2 })

Hash declaration syntax error in irb

1. { :a => 10 } #=> no error
2. { a: 10 } #=> no error
3. { :"str" => 10 } #=> no error
4. { "str": 10 } #=> syntax error, unexpected ':', expecting =>
Isn't 4. same as 2? Why 2 is working and 4 throws syntax error?
My understanding is that {"key": value} is not a valid syntax as it is not clear whether it means {:"key" => value} or {"key" => value}
There is a discussion on this here. Quote from Matz in the discussion
| Iff {'key': 'value'} means {:key => 'value'} I have no objection.
| Won't that be misleading? I think the OP wants {'key': 'value'} to mean {'key' => 'value}
But considering the fact that {key: "value"}
is a shorthand for {:key => "value"}, {"key": "value"} should be a
shorthand for {:"key" => "value"}. Besides that, since it reminds me
JSON so much, making a: and "a": different could cause more confusion
than the above misleading.
matz.
Hash: Hashes allow an alternate syntax form when your keys are always symbols.
options = { :font_size => 10, :font_family => "Arial" }
You could write it as:
options = { font_size: 10, font_family: "Arial" }
In your first 3 cases all are symbols in key position,but the fourth is a string instance,not the symbol instance as key.That's the reason 4th case is invalid Ruby syntax.
{ :a => 10 }.keys[0].class # => Symbol
{ a: 10 }.keys[0].class # => Symbol
{ :"str" => 10 }.keys[0].class # => Symbol
No. (1) is standard symbol, (2) is shorthand 1.9 syntax for symbol-key hashes, (3) is shorthand for "str".to_sym, (4) does not exist and you should use the hashrocket.

Why is pushing a hash into an array in Ruby had to use parenthesis?

I would like to add a hash into an array using Ruby version 1.8.7:
items = Array.new
items.push {:a => "b", :c => "d"}
Statements above will return an error something like:
SyntaxError: compile error
(irb):35: syntax error, unexpected tASSOC, expecting '}'
items.push {:a => "b", :c => "d"}
^
(irb):35: syntax error, unexpected ',', expecting '}'
items.push {:a => "b", :b => "c"}
^
Well, I found that the solution is to wrap the push arguments within parenthesis ( ) or I can use the << operator. I also know that push accept one or more argument and << only accept a single argument from this answer, but what's bothering me is that why do I need to use the parenthesis, while as we all know parenthesis in Ruby are optional?
My guess is that this is because ruby is trying to parse the hash as a block, expecting code and not hash keys and values. this is similar to:
items.push() do
:a => "b", :b => "c"
end
which is not valid syntax.

How to pass the second parameter to Mongo::Collection#find?

This is a newbie question. I find the method definition in the YARD Rdoc:
(Object) find(selector = {}, opts = {})
Options Hash (opts):
:fields (Array, Hash)
then I try this coll.find('English' => 'fulcrum',{English:1,Chinese:1}), want the result 'English' field is fulcrum ,and only return English and Chinese field, but Ruby punished me with the this
irb(main):018:0> coll.find('English' => 'fulcrum',{English:1,Chinese:1})
SyntaxError: (irb):18: syntax error, unexpected ')', expecting tASSOC
from /usr/local/bin/irb:12:in `<main>'
irb(main):019:0>
I want to know why, thanks
after correct the syntax problem by the suggestion by #mu, I got Unknown options error:
irb(main):013:0> coll.find({English:'fulcrum'},{English:1, :Chinese => 1})RuntimeError: Unknown options [{:English=>1, :Chinese=>1}]
from /usr/local/lib/ruby/gems/1.9.1/gems/mongo-1.5.2/lib/mongo/collection.rb:234:in `find'
from (irb):13
from /usr/local/bin/irb:12:in `<main>'
irb(main):014:0>
When Ruby sees an unwrapped Hash in argument list:
o.m(k => v, ...)
it assumes that you really mean this:
o.m({ k => v, ... })
So, when you say this:
coll.find('English' => 'fulcrum', {English: 1, Chinese: 1})
Ruby sees this:
coll.find({ 'English' => 'fulcrum', {English: 1, Chinese: 1} })
A Hash is a perfectly valid key so Ruby expects it to be followed by a => value:
coll.find('English' => 'fulcrum', {English: 1, Chinese: 1} => some_value)
and that's where the error message comes from:
syntax error, unexpected ')', expecting tASSOC
If you want to pass two hashes, you need to wrap the first one in braces:
coll.find({'English' => 'fulcrum'}, {English: 1, Chinese: 1})
The second argument to [find](
http://api.mongodb.org/ruby/current/Mongo/Collection.html#find-instance_method) should be an options Hash and it looks like you want the :fields option and you can give that an array of names instead of a noisy Hash:
coll.find({'English' => 'fulcrum'}, :fields => %w[English Chinese])

Ruby - method parameters

My method:
def my_method=(attributes, some_option = true, another_option = true)
puts hello
end
When i try to call this, i get such error:
my_method=({:one => 'one', :two => 'two'}, 1, 1)
#you_code.rb:4: syntax error, unexpected ',', expecting ')'
#my_method=({:one => 'one', :two => 'two'}, 1, 1)
^
What's the problem?
Method with suffix punctuation = can have only one argument.
Otherwise, you must use send to invoke with multiple parameters.
send :'my_method=', {:a => 1}, 1, 1
Don't use parenthesis when invoking a method using the = syntactic sugar.
Invoke it like this:
mymethod= {:one => 'one', :two => 'two'}, 1, 1

Resources