Rewbie Newbie

Documenting my path on becoming a Rails Developer.

Hashes as Method Arguments

After trudging through rails for a couple of weeks now, one of the most confusing patterns for me is how hashes are passed as arguments without being bookended with curly braces. For example:

1
2
3
4
5
# What is going on here.
# Are there 2 arguments consisting of a string and a hash? 
# Or are there 3 arguments consisting of a string and 2 hashes?

link_to "home", :controller => "page", :action => "index"

I thought the answer would lie in some weird rails convention, but to my surprise, the practice is rooted in Ruby.

It turns out that in Ruby if you call a method and pass in a hash as the last argument, it is acceptable to write the hash without the curly braces. So, if you follow this rule, and you sprinkle in a few key-value pairs, Ruby will interpret all of the key-value pairs as one hash.

1
2
3
4
5
6
7
8
9
10
#Method with 1 argument

def some_method(a)
  puts a.class
  puts a
end

some_method a:1, b:2, c:3
#=> Hash
#=> {:a=>1, :b=>2, :c=>3}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#Method with 2 argument

def some_method(a,b)
  puts a.class
  puts a
  puts b.class
  puts b
end

some_method "words", a:1, b:2, c:3
#=> String
#=> words
#=> Hash
#=> {:a=>1, :b=>2, :c=>3}
1
2
3
4
5
6
7
8
9
10
11
#Method with 2 argument

def some_method(a,b)
  puts a.class
  puts a
  puts b.class
  puts b
end

some_method a:1, b:2, c:3, "words"
#=> syntax error, unexpected '\n', expecting tASSOC

As you can see in the last example, Ruby will throw a syntax error if you pass in a hash without curly braces when the hash is not the last argument.

But as I was eager to address the error of the missing curly braces, I encounted another error that I was not expecting:

1
2
3
4
5
6
7
8
9
10
11
#Method with 2 argument

def some_method(a,b)
  puts a.class
  puts a
  puts b.class
  puts b
end

some_method {a:1, b:2, c:3}, "words"
#=> syntax error, unexpected tINTEGER, expecting tSTRING_CONTENT or tSTRING_DBEG or tSTRING_DVAR or tSTRING_END

By adding curly braces, Ruby interpreted the hash as a block and complained when the syntax inside the braces did not match that of a block. This error can be easily solved by enclosing all of the arguments inside parentheses and leads to the conclusion that when you are passing a hash as the first of many arguments inside of a method, you must enclose the whole argument list within parentheses.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#Method with 2 argument

def some_method(a,b)
  puts a.class
  puts a
  puts b.class
  puts b
end

some_method({a:1, b:2, c:3}, "words")
#=> Hash
#=> {:a=>1, :b=>2, :c=>3}
#=> String
#=> words