Tuesday, March 1, 2011

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?
From stackoverflow
  • 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.

    Readonly : Thanks for pointing that out!
  • 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" )
    
    Priit : I'd rather call 'super' in method missing instead of 'raise' so it will perform a default Ruby action when method is really missing.

0 comments:

Post a Comment