Question

If I have the following method in Ruby:

def foo(arg1, arg2 = "bar")
  puts arg1
  puts arg2
end

Is there a way of determining if a user passed a value in for arg2 within the method? Obviously I could just add if arg2 == "bar" to the method, but this doesn't catch the case where the user manually passed in "bar" herself. Of course, I could set the default to be something that no user would ever pass in, but then that gets pretty ugly pretty quickly.

Is there anything elegant out there?

Was it helpful?

Solution 5

I am not sure this is possible. Consider the following:

class Test
  def method(a,b=1)
    local_variables.each do |var|
          puts "Setting #{var} to #{eval var.to_s}"
    end
  end
end

And then try to call method on an instance of Test:

?> t.method(1)
Setting a to 1
Setting b to 1
=> [:a, :b]

?> t.method(1,2)
Setting a to 1
Setting b to 2
=> [:a, :b]

?> t.method(1,1)
Setting a to 1
Setting b to 1
=> [:a, :b]

Those do not distinguish how the method was called.

So I would suggest @sawa's approach mixed with the following:

raise ArgumentError, "Too many arguments" if args.length > 2

since you seem to want to limit the parameters to 2 and @sawa's approach allows for an undefined number.

This is an unorthodox approach to an unorthodox requirement, so just make sure the clients of your method are aware of how the API works.

OTHER TIPS

def foo(arg1, arg2 = (arg2_not_passed = true; "bar"))
  puts arg1
  puts arg2
  puts 'arg2 was passed' unless arg2_not_passed
end
def foo(*args)
  case args.length
  when 1
    # arg2 was not given
    arg1, arg2 = args.first, "bar"
    ...
  when 2
    # arg2 was given
    arg1, arg2 = args
    ...
  else
    raise ArgumentError
  end
end

One way (not elegant but fun) is to make the default value such that it has a unique object_id. It wil not work if default value is an immutable object like integer.

def c
  @c ||= "bar"
end

def fun(a,b = c)   
     puts  b.object_id == c.object_id ? "default" : "user" 
end

fun "foo"
#=> "default"

fun "foo", "bar"
#=> "user"

I have a solution! Suppose we want:

def foo(arg1, arg2 = 42)

Then we would instead write:

def foo(arg1, arg2 = [42,KEY])

If agr2 == [42,KEY], we conclude that the calling method did not pass a second argument, in which case the default of 42 applies. If arg2 is anything else, arg2 was passed. Simple!

I know what you're thinking: what if the calling program were to actually pass [42,KEY]? I have that figured out too: KEY = rand(100000000000000).

I would like to add one more thing. I am in principal opposed to doing so, but I know if I don't I will receive so many downvotes that my reputation will go negative. So here it is: :-)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top