define_method
define_method(...)
public
Defines an instance method in the receiver. The method parameter can be a Proc or Method object. If a block is specified, it is used as the method body. This block is evaluated using instance_eval, a point that is tricky to demonstrate because define_method is private. (This is why we resort to the send hack in this example.)
class A def fred puts "In Fred" end def create_method(name, &block) self.class.send(:define_method, name, &block) end define_method(:wilma) { puts "Charge it!" } end class B < A define_method(:barney, instance_method(:fred)) end a = B.new a.barney a.wilma a.create_method(:betty) { p self } a.betty
produces:
In Fred Charge it! #<B:0x401b39e8>
define_method with parameters
Just to be clear, you can do this:
define_method(:my_method) do |foo, bar| # or even |*args| # do something end
This means same as:
def my_method(foo, bar) # do something end
If you want to define method with parameters that have default values, you need to get a bit more creative and do something like this:
define_method(:my_method) do |foo, bar| bar ||= {} # do something end
define_method with default parameters
To define a method with a default parameter the usual notation can be used:
define_method("example") do |fixed, default = {}| # something end
Avoiding the "multiple values for a block parameter" warning
As pointed out below, you can also have optional parameters. But you will get something like “warning: multiple values for a block parameter (0 for 1)” if you omit them.
You can avoid those warnings by passing *args and picking the parameters yourself:
define_method :that_method do |*args|
foo = args[0] || 'my default' # ... end
Now the warning will be gone. Just make sure you fetch your parameters from *args and assign a default value (unless you want them to default to nil).
define_method with blocks works differently
As it is already stated that block is evaluated using instance_exec/instance_eval, so let me give you an example.
module Service module ClassMethods def endpoint_instance_exec(name, &block) define_method name do instance_exec(&block) end end def endpoint_block_call(name, &block) define_method name, &block end def endpoint_block_improper_call(name, &block) define_method name do # In this case, we called the block without "instance_eval" that # means block was called in the context of class MyService. block.call end end end def self.included(klass) klass.extend ClassMethods end private def hello puts 'world' end end class MyService include Service endpoint_instance_exec :foo do hello end endpoint_block_call :bar do hello end endpoint_block_improper_call :foobar do hello end end
Now, understand how can we execute the code and understand the working of define_method and instance_exec.
MyService.new.foo # => "hello" MyService.new.bar # => "hello" MyService.new.foobar # => undefined local variable or method `hello' for MyService:Class