One thing I like most about Ruby is the flexibility it gives you to do the same thing in many different ways, so you can choose the way that suits you better. Deciding how you want your methods to receive arguments and therefore how those arguments are going to be passed is one of the things that you can perform in many different ways. This may sound simple, but as Ruby evolves the way arguments are passed to methods also do, so lets take a closer look to the most common approaches out there.
###Positional arguments
The most common way of passing them is by using positional arguments. Every argument has it's own position, they must be passed in the same order as they are defined and all of them are required:
def foo(a, b, c)
puts [a, b, c]
end
foo(1, 2, 3)
#=> [1, 2, 3]
But what happens if any of them are missing?
def foo(a, b, c)
puts [a, b, c]
end
foo(1, 2)
#=> ArgumentError: wrong number of arguments (2 for 3)
This can be easily prevented by assigning some default values:
def foo(a = 1, b = 2, c = 3)
puts [a, b, c]
end
foo(1, 2)
#=> [1, 2, 3]
###Array arguments
There might be situations where you need to accept an undetermined number of arguments, or just some optional ones. This can be done using this simple syntax:
def foo(*args)
puts args
end
foo(1, 2, 3)
#=> [1, 2, 3]
Using the *
will tell your method to create an array with the received arguments. You can also mix array arguments with ordinary ones, and Ruby will be smart enough to assign them:
def foo(a, b, c, *d)
puts [a, b, c]
puts d
end
foo(1, 2, 3, 4, 5, 6)
#=> [1, 2, 3]
#=> [4, 5, 6]
But what about default values? Yes, you can also use default values, just remember that you have to place default value parameters always before ordinary ones, otherwise you will get a syntax error:
def foo(a, b, c = 'default', *d, e)
puts [a, b, c]
puts d
puts e
end
foo(1, 2, 3)
#=> [1, 2, 'default']
#=> []
#=> 3
###Hash arguments Array arguments look useful, but retrieving their values by their index may not be that useful. That's why Ruby offers us hash arguments, so you can access their values by their key, making your code more readable:
def foo(args)
puts args[:a]
puts args[:b]
end
foo(a: 1, b: 2)
#=> 1
#=> 2
As we saw previously, you can also mix ordinary arguments with hashes:
def foo(a, args)
puts a
puts args
end
foo(1, b: 2, c: 3)
#=> 1
#=> {b: 2, c: 3}
Cool, right? But what about missing arguments and default values? Well, here comes the tricky part. To set default values into hash arguments you will have to use the following technique which sets an empty hash as the default value and afterwards merge it with your desired default values:
def foo(a, args = {})
defaults = {b: 2, c: 3}
args = defaults.merge(args)
puts a
puts args
end
foo(1)
#=> 1
#=> {b: 2, c: 3}
This adds some more typing, but without doing so, missing argument's default values will be ignored, and you don't want that.
###Keyword arguments Keyword arguments is one of the most awaited features of Ruby 2.0. Thanks to them you have even more freedom and flexibility while defining your arguments. Lets take a look at how to use them:
def foo(a: 1, b: 2)
puts a
puts b
end
foo(a: 1)
#=> 1
#=> 2
As you can see it's very similar to hash arguments but without the ugly merging part. Now you can set default values right in their definition and you can also pass them in any order you like, just remember to set nil values to missing (or optional) parameters to avoid any missing arguments errors:
def foo(a: 1, b: nil)
puts a
puts b
end
foo(a: 1)
#=> 1
Combining them with positional arguments is just the same as explained before:
def foo(a, b: 2, c: 3)
puts a
puts b
puts c
end
foo(1, b: 2, c: 3)
#=> 1
#=> 2
#=> 3
As with array arguments, optional arguments may be defined using the new **args
syntax. It's like the old single asterisk *args
we saw before, but now instead of using an array, it will keep those arguments into a hash:
def foo(b: 2, c: 3, **args)
puts b
puts c
puts args
end
foo(a: 1, b: 2, c: 3, d: 4)
#=> 2
#=> 3
#=> {a: 1, d: 4}
As you can see, it's all about flexibility and freedom of choice.
Happy coding!