In general, there are three ways. One way is to use the default value to set another variable indicating whether the default value has been evaluated:
def f(p1 = (no_argument_passed = true; nil)) 'no arguments passed' if no_argument_passed end f
The second way is to use some kind of object that is known only inside the method as the default value, so the outsider cannot pass this object to:
-> { undefined = BasicObject.new define_method(:f) do |p1 = undefined| 'no arguments passed' if undefined.equal?(p1) end }.() f
Of these two, the first of them is more idiomatic. The second (in fact, its variant) is used inside Rubinius, but I have never met it anywhere.
A third solution would be to take a variable number of arguments using splat:
def f(*ps) num_args = ps.size raise ArgumentError, "wrong number of arguments (#{num_args} for 0..1)" if num_args > 1 'no arguments passed' if num_args.zero? end f
Note that this requires a re-implementation of Ruby authentication manually. (And we still do not understand, because it raises an exception inside the method, while Ruby raises it at the call site.) It also requires that you manually document your method signature, because automatic documentation generators like RDoc or YARD will cause an arbitrary number of parameters instead of one optional.
source share