166

I would like to pass an argument(s) to a method being defined using define_method, how would I do that?

4 Answers 4

212

The block that you pass to define_method can include some parameters. That's how your defined method accepts arguments. When you define a method you're really just nicknaming the block and keeping a reference to it in the class. The parameters come with the block. So:

define_method(:say_hi) { |other| puts "Hi, " + other } 
Sign up to request clarification or add additional context in comments.

Comments

106

... and if you want optional parameters

 class Bar define_method(:foo) do |arg=nil| arg end end a = Bar.new a.foo #=> nil a.foo 1 # => 1 

... as many arguments as you want

 class Bar define_method(:foo) do |*arg| arg end end a = Bar.new a.foo #=> [] a.foo 1 # => [1] a.foo 1, 2 , 'AAA' # => [1, 2, 'AAA'] 

...combination of

 class Bar define_method(:foo) do |bubla,*arg| p bubla p arg end end a = Bar.new a.foo #=> wrong number of arguments (0 for 1) a.foo 1 # 1 # [] a.foo 1, 2 ,3 ,4 # 1 # [2,3,4] 

... all of them

 class Bar define_method(:foo) do |variable1, variable2,*arg, &block| p variable1 p variable2 p arg p block.inspect end end a = Bar.new a.foo :one, 'two', :three, 4, 5 do 'six' end 

Update

Ruby 2.0 introduced double splat ** (two stars) which (I quote) does:

Ruby 2.0 introduced keyword arguments, and ** acts like *, but for keyword arguments. It returns a Hash with key / value pairs.

...and of course you can use it in define method too :)

 class Bar define_method(:foo) do |variable1, variable2,*arg,**options, &block| p variable1 p variable2 p arg p options p block.inspect end end a = Bar.new a.foo :one, 'two', :three, 4, 5, ruby: 'is awesome', foo: :bar do 'six' end # :one # "two" # [:three, 4, 5] # {:ruby=>"is awesome", :foo=>:bar} 

Named attributes example:

 class Bar define_method(:foo) do |variable1, color: 'blue', **other_options, &block| p variable1 p color p other_options p block.inspect end end a = Bar.new a.foo :one, color: 'red', ruby: 'is awesome', foo: :bar do 'six' end # :one # "red" # {:ruby=>"is awesome", :foo=>:bar} 

I was trying to create example with keyword argument, splat and double splat all in one:

 define_method(:foo) do |variable1, variable2,*arg, i_will_not: 'work', **options, &block| # ... 

or

 define_method(:foo) do |variable1, variable2, i_will_not: 'work', *arg, **options, &block| # ... 

... but this will not work, it looks like there is a limitation. When you think about it makes sense as splat operator is "capturing all remaining arguments" and double splat is "capturing all remaining keyword arguments" therefore mixing them would break expected logic. (I don't have any reference to prove this point doh! )

update 2018 August:

Summary article: https://blog.eq8.eu/til/metaprogramming-ruby-examples.html

3 Comments

Interesting - specialy the 4th block: it did work on 1.8.7! First block didn't work in 1.8.7, and second block has a typo (should be a.foo 1 instead of foo 1). Thank's!
thanks for feedback, typo was fixed, ...On ruby 1.9.3 and 1.9.2 all of examples works and I'm positive that on 1.9.1 too (but didn't try)
I combined this answer with the accepted answer at stackoverflow.com/questions/4470108/… to figure how to overwrite (not override) a method at runtime that takes optional args and a block and still be able to call the original method with the args and block. Ah, ruby. Specifically, I needed to overwrite Savon::Client.request in my dev env for a single API call to a host I can access only in production. Cheers!
60

In addition to Kevin Conner's answer: block arguments do not support the same semantics as method arguments. You cannot define default arguments or block arguments.

This is only fixed in Ruby 1.9 with the new alternative "stabby lambda" syntax which supports full method argument semantics.

Example:

# Works def meth(default = :foo, *splat, &block) puts 'Bar'; end # Doesn't work define_method :meth { |default = :foo, *splat, &block| puts 'Bar' } # This works in Ruby 1.9 (modulo typos, I don't actually have it installed) define_method :meth, ->(default = :foo, *splat, &block) { puts 'Bar' } 

1 Comment

Actually, I believe block arguments on define_method do support splat, which can provides a round-a-bout way to define default arguments too.
9

With 2.2 you can now use keyword arguments: https://robots.thoughtbot.com/ruby-2-keyword-arguments

define_method(:method) do |refresh: false| .......... end 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.