Consulting. Training. Development.

Keyword arguments in Ruby 2.0

In computer programming, named parameters or keyword arguments refer to a computer language’s support for function calls that clearly state the name of each parameter within the function call itself. © Wikipedia

Right now (I mean in Ruby <= 1.9.3) you can ‘emulate’ it with passing a hash as an argument to a method:

1
2
3
4
5
def foo(options = {})
  puts "#{options[:bar]} #{options[:buz]}"
end

foo(bar: 'bar', buz: 'buz') # => 'bar buz'

I bet you saw this a lot. But the main problem with this technique is you can’t easily set default value for arguments. For sure you can solve it like this:

1
2
3
4
5
6
def foo(options = {})
  options = {bar: 'bar'}.merge(options)
  puts "#{options[:bar]} #{options[:buz]}"
end

foo(buz: 'buz') # => 'bar buz'

but it doesn’t look like a clean solution. So what will be changed with Ruby 2.0? Let’s look at this CHANGELOG entry:

Implement keyword arguments. The feature is promised to be included in 2.0, but the detail spec is still under discussion; this commit is a springboard for further discussion. Please try it and give us feedback.

Sounds promising! As you can see there is a chance that keyword arguments will be a part of ruby syntax. So how to use it? There is a couple of tests here. Let’s take a brief look at some of them:

  • only keyword parameters
1
2
3
4
5
6
7
8
def foo(str: "foo", num: 424242)
  [str, num]
end

foo(str: 'buz', num: 9) #=> ['buz', 9]
foo(str: 'bar') # => ['bar', 424242]
foo # => ['foo', 424242]
foo(bar: 'buz') # => ArgumentError

So as you can see you don’t need to do merge or something to achieve default values and if you pass an argument that doesn’t match arguments from the method’s definition you’ll get ArgumentError. To make something similar Rails extends Hash class with assert_valid_keys method.

  • ordinary argument with keyword arguments
1
2
3
4
5
6
def foo(x, str: "foo", num: 424242)
  [x, str, num]
end

foo(13) #=> [13, 'foo', 424242]
foo(13, str: 'bar') #=> [13, 'bar', 424242]
  • keyword arguments and options
1
2
3
4
5
6
def foo(str: "foo", num: 424242, **options)
  [str, num, options]
end

foo #=> ['foo', 424242, {}]
foo(check: true) # => ['foo', 424242, {check: true}]

As you can see there is a new thing like **. It allows you to pass a hash to this method because you can’t write

1
2
3
def foo(str: "foo", num: 424242, options = {})
  [str, num, options]
end

It won’t work. You’ll get something like syntax error, unexpected ')', expecting end-of-input.

  • it works with blocks too
1
define_method(:foo) {|str: "foo", num: 424242| [str, num] }

So let’s keep fingers crossed for adding this feature to ruby 2.0.

Comments