How to pass optional named arguments in Ruby 2.0

It's not entirely clear why you want to know if the argument was provided or not. The most useful reason to know, is because you want to require one of your optional arguments. If that's the reason, then starting with Ruby 2.1, you can get what you want.

def foo(a:,b:nil,c:3)
 [a, b, c]
end

foo a: 1, b: 2, c: 3
#=> [1, 2, 3]
foo a: 1, b: 2
#=> [1, 2, 3]
foo a: 1
#=> [1, nil, 3]
foo
#ArgumentError: missing keyword: a

I would assume from you example that you are not using name in foo but want to pass it along.

You should use the catch all for named arguments instead:

def foo(**named)
  # …
  bar(**named)
end

def bar(name: 'unknown')
  # …
  puts name
end

foo # => 'unknown'
foo(name: 'baz') # => 'baz'

Note that this not interfere with other arguments (named or not), so you if you had other arguments for foo:

def foo(what, other: 42, **named)

You can use an "undefined" value as the default to tell it apart from a nil:

Undefined = Object.new.freeze

def foo(a: Undefined)
  puts a
end

foo(a: nil) # => "nil"
foo # => "#<Object:0x000056004b29a058>"

If you use dry-rb, then you can use Dry::Core::Constants::Undefined (source), which provides a few extras:

Undefined = Dry::Core::Constants::Undefined

def foo(a: Undefined)
  puts a
end

foo(a: nil) # => "nil"
foo # => "Undefined"

Probably the best way to go would be:

def bar(name: nil)
  name ||= 'unknown'
  puts name
end

Tags:

Ruby