define_method
define_method(...)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>
4Notes
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