Ruby 1.9 has some cool new features. One of them is the ability to define default values for the arguments passed to code blocks, like in the following example:
pow = proc { |a, b = 2| a**b }
pow.call 3, 3
# 27
pow.call 3
# 9
This is very useful, for instance, when we dynamically create new methods using metaprogramming and want some of the arguments for these methods to be optional.
class MyMath
class < < self
define_method :pow do |base, exponent = 2|
base**exponent
end
end
end
MyMath.pow 3 # 9
MyMath.pow 2, 3 # 8
But in Ruby 1.8.x we can't do that, we can't define default values the arguments of a code block. So what if your application runs on 1.8.x and you need to dynamically create methods with optional arguments? There is a solution, not so elegant as the Ruby 1.9 approach but still very functional and simple:
class MyMath
class < < self
define_method :pow do |*args|
base, exponent = args[0], args[1] || 2
base**exponent
end
end
end
MyMath.pow 3 # 9
MyMath.pow 2, 3 # 8
When we pass a splat (that thing with an asterisk on the left) as the argument to the block, whatever gets there will be treated as an array. This way, we can simulate the existence of default values for those arguments. We can actually pass any number of arguments and define the default values in any way we want.
What is lost, unfortunately, is the ability to validate the number of arguments that the method should receive when called. But we can do that validating the elements inside the array that the block receives.
I usually use this technique to create methods that can optionally receive an options hash, what is very useful when writing plugins or gems, for instance:
module MyModule
module ClassMethods
def do_the_magic
class_eval do
define_method :magic_method do |*args|
options = args.first || {}
# ...
end
end
end
end
def self.included(base)
base.extend ClassMethods
end
end
class MyClass
include MyModule
do_the_magic
end
obj = MyClass.new
obj.magic_method # works
obj.magic_method :abra => "cadabra" # also works!