readonly
readonly

Reputation: 355484

Is it possible to define a block with optional arguments in Ruby?

I'm trying to dynamically define functions that call through to another function that takes an options parameter:

class MyClass
  ["hour", "minute", "second"].each do |interval|
    define_method "get_#{interval}" do |args|
      some_helper(interval, args)
    end
  end
  def some_helper(interval, options={})
    # Do something, with arguments
  end
end

I'd like to be able to call the different methods on MyClass in these two ways (with and without optional arguments):

mc = MyClass.new
mc.get_minute( :first_option => "foo", :second_option => "bar")
mc.get_minute  # This fails with: warning: multiple values for a block parameter (0 for 1)

On the second call to minute, I see this warning:

warning: multiple values for a block parameter (0 for 1)

  1. Is there a way to write the block for the "get_*" method so that this warning won't come up?
  2. Am I abusing define_method?

Upvotes: 7

Views: 6251

Answers (3)

knut
knut

Reputation: 27845

Two years later... I don't know if is is a new feature with ruby 1.9.2, or if it was also possible in the past, but this works:

class MyClass
    ["hour", "minute", "second"].each do |interval|
        define_method "get_#{interval}" do |args = {:first_option => "default foo", :second_option => "default  bar"}|
           some_helper(interval, args)
        end
    end
    def some_helper(interval, options={})
        # Do something, with arguments
        p options
    end
end

mc = MyClass.new
mc.get_minute( :first_option => "foo", :second_option => "bar")
mc.get_minute  

Result is:

{:first_option=>"foo", :second_option=>"bar"}
{:first_option=>"default foo", :second_option=>"default  bar"}

Upvotes: 5

stjernstrom
stjernstrom

Reputation: 119

I agree with Gordon adding * to your args will make it go away.

Another way of doing this is to use method_missing()

Something like this:

class MyClass

  def method_missing(m, *args)  
    if /get_(.+)/.match(m.to_s)
      some_helper($1, args) 
    else
      raise 'Method not found...'
    end
  end  

  def some_helper(interval, *args)
    puts interval + ' -> ' + args.inspect
  end

end

m = MyClass.new
m.get_minute( :first_option => "foo", :second_option => "bar" )

Upvotes: 0

Gordon Wilson
Gordon Wilson

Reputation: 26384

The only change you need to make is to change args to *args. The * indicates that args will contain an array of optional arguments to the block.

Upvotes: 16

Related Questions