I’ve previously given an overview of basic method arguments in Ruby (at least in Ruby 1.9). There is quite a lot you can do with just the basic method arguments, so I purposely left out the more advanced topics from that post (which many people were quick to point out :)). However, if you want to have an in-depth knowledge of Ruby you will need to know how to use hashes as method arguments as well as where blocks fit into the picture and this is what I am going to cover here.
Using Hashes As Arguments
A Hash is just a regular object in Ruby, so normally, using it as an argument is no different from using any other object as an argument e.g.:
```ruby def some_method(a, my_hash, b) p a p my_hash p b end
some_method “Hello”, {:first=>“abc”, :second=>“123”},“World”```
This would produce the following output:
"Hello" {:first=>"abc", :second=>"123"} "World"
The interesting things about hashes as arguments is that depending on their location in the argument list you can get some interesting benefits (and sometimes interesting detriments).
Hashes As The Last Argument
When you use a hash as the last argument in the list Ruby allows you to forego the use of the curly braces which surprisingly can make argument lists look a lot nicer, consider this:
```ruby def print_name_and_age(age, name_hash) p “Name: #{name_hash[:first]} #{name_hash[:middle]} #{name_hash[:last]}” p “Age: #{age}” end
print_name_and_age 25, :first=>‘John’, :middle=>’M.‘, :last=>‘Smith’```
This produces the following output:
"Name: John M. Smith" "Age: 25"
We don’t need to use curly braces as the hash is the last argument which makes our method call look a little neater. This also has a very neat side-effect of making the method arguments somewhat self-documenting (the hash key tells you what the argument value relates to). If we weren’t using a hash, then we would have to know the exact order of the method arguments to pass them in (e.g. does last name come first in the argument list, or is it first name) e.g.:
```ruby def print_name_and_age(age, last, middle, first) p “Name: #{first} #{middle} #{last}” p “Age: #{age}” end
print_name_and_age 25, ‘John’, ’M.‘, ‘Smith’```
This looks correct, but we no longer know for sure if we accidentally confused the order of the parameters (which we did in this case) unless we look at the method definition, so our output is not what we expect:
"Name: Smith M. John" "Age: 25"
This feature of not having to use curly braces also works when we use a hash as the only argument to our method. Because of this and the self-documenting properties of passing in a hash as an argument, a case can be made for exclusively using a hash to pass all arguments to a method rather than having an argument list. Not only do we get the benefits of self-documentation and neater syntax, but we can also pass arguments in any order since the hash doesn’t care due to the key/value association.
So Why Not Do This All The Time
When you use a hash as an argument you always have the extra overhead of using a hash, i.e. you always have to pull values from the hash, inside the method, using the [] operator. It is probably not such a big deal, but it is there nonetheless, so it may not be worth doing this for methods that have one or two simple arguments.
There is one more caveat to be aware of with hashes. Just like you get some niceness when you use the hash as a last (or only) argument in an argument list, you get some nastiness when you use it as the first. If you use a hash as the first argument, you can’t leave out the curly braces, not only that, because there are other arguments in the list you can’t leave out the parentheses from the method call like you usually would with Ruby e.g.:
```ruby def print_name_and_age(name_hash, age) p “Name: #{name_hash[:first]} #{name_hash[:middle]} #{name_hash[:last]}” p “Age: #{age}” end
print_name_and_age({:first=>‘John’, :middle=>’M.‘, :last=>‘Smith’}, 25)```
You can probably guess that if you did try to leave off the parentheses, then Ruby would try to interpret your hash as a block (as curly braces are also used for block syntax) and so we must use the them to disambiguate our method call. This leads me neatly into discussing block and exactly what they are (and aren’t).
Blocks Are NOT Method Arguments
Despite what some people might believe, blocks are not method arguments. Blocks and arguments are two separate constructs. Infact if you have even a basic understanding of blocks you know that blocks can have arguments of their own. Does it really make sense for arguments to have arguments?
But to get back to block basics. There are many methods in ruby that iterate over a range of values. Most of these iterators are written in such a way as to be able to take a code block as part of their calling syntax. The method can then yield control to the code block (i.e. execute the block) during execution as many times as is necessary for the iteration to complete (e.g. if we are iterating over array values, we can execute the block as many times as there are array values etc.).
There are two types of block syntax, curly brace and do..end. If we want to supply a one-liner block to a method we normally us the curly brace syntax, otherwise we use the do..end syntax. For example:
ruby
[1,2,3,4].each {p 'hello'}
or
[1,2,3,4].each do print 'i am printing ' puts 'hello' end
The two versions of block syntax are not exactly alike, but I will cover that aspect of blocks in a later post (which I plan to devote completely to blocks).
The other side of the coin when it comes to blocks is the yield keyword. Any method that wants to take a block as a parameter can use the yield keyword to execute the block at any time. It’s as simple as that. This one is another thing that I would like to explore further in a later post.
One final thing to remember about block basics is the fact that blocks can also take parameters e.g.:
ruby
[1,2,3,4].each do |x|
print "i am printing #{x} "
puts "hello"
end
The above will produce the following output:
i am printing 1 hello i am printing 2 hello i am printing 3 hello i am printing 4 hello
Depending on what you’re iterating over the parameters that get passed to the blocks can have different values (and there can even be a different number of parameters). In the above case, because we are iterating over an array, each time the block is executed the parameter contains the value of the current element of the array that we are iterating over. As you can see you don’t pass parameters to blocks using parentheses like you would to methods, instead you pass them in between two pipes (i.e. |x|). There is once again much more to be said about blocks and block arguments, but since we are only covering block basics here, I’ll leave that for later.
The main things to take away from this are as follows:
- blocks are not method arguments but are infact a separate construct
- blocks are used to support iterator-type methods
- there are two types of block syntax
- blocks can take parameters of their own
I will explore all of those in more depth when I dig into the how and why of blocks in Ruby. I hope you found at least some of this helpful/interesting and as always if you have something to add (or if you just want to say hello :)), feel free to leave a comment below.
Image by Aislinn Ritchie