define_method is one of the many powerful features of Ruby that makes possible the creation of awesome gems like Rails. Understanding its usage and how to wield it will make you awesome.

In this post, we’ll see all the different ways we can use define_method to define class and instance methods in a Ruby class.

But before diving in, make sure you read Yehuda Katz’s excellent post about self (a pun here would be to ‘Know Thyself!’).

We’ll need a class and an object for experimenting with.

class Dummy
end

dummy_object = Dummy.new

In order to make each examples stand on its own to enhance readability, we’ll re-open our Dummy class as and when needed. We’ll also use the same dummy_object object where necessary.

Simplest Usecase

Scenario 1) To define an instance method named instance_hi that takes a name argument and greets the name:

class Dummy
  define_method :instance_hi do |name|
    "hi '#{name}'"
  end
end

dummy_object = Dummy.new
puts dummy_object.instance_hi "instance method"
# => hi 'instance method'

Scenario 2) To define a class method named class_hi:

class Dummy
  class << self
    # we are in Dummy's metaclass environment here
    define_method :class_hi do |name|
      "hi '#{name}'"
    end
  end
end

puts Dummy.class_hi "class method"
# => hi 'class method'

If you find class &lt;&lt; self tedious, and if your ruby version is > 1.9.2, you can use define_singleton_method like this:

class Dummy
  define_singleton_method :class_hi do |name|
    "hi '#{name}'"
  end
end

puts Dummy.class_hi "class method"
# => hi 'class method'

Note: The above examples define methods that only take arguments. How would you define methods that also use a block? Using scenario 1, here’s how:

class Dummy
  define_method :instance_hi do |name, &block|
    "hi '#{name}'#{block.call if block}"
  end
end

dummy_object = Dummy.new
puts dummy_object.instance_hi("instance method")
# => hi 'instance method'
puts dummy_object.instance_hi("instance method") { '!' }
# => hi 'instance method!

Note that you can’t use yield to replace block.call. It would result in LocalJumpError.

Real-world Usecase

This is great to understand define_method. But it wasn’t created to define a method name we already know! The real application for define_method is when we use it to define methods on the fly, dynamically.

We’ll see how to create class and instance methods dynamically using define_method from existing class and instance methods.

Scenario 3) Defining an instance method from an existing instance method

class Dummy
  def create_instance_method_from_instance_method method_name, &block
    self.class.send :define_method, method_name do |name, &block|
      "hi '#{name}'#{block.call if block}"
    end
  end
end

dummy_object.create_instance_method_from_instance_method :instance_hi_from_instance_method
puts dummy_object.instance_hi_from_instance_method("dynamic instance method from instance method") { '!' }
# => hi 'dynamic instance method from instance method!'

Since define_method is a private method, it needs to be called via send.

Scenario 4) Defining a class method from an existing instance method

For all ruby versions:

class Dummy
  def create_class_method_from_instance_method method_name, &block
    metaclass_of_dummy_class = (class << self.class; self; end)
    metaclass_of_dummy_class.instance_eval do
      define_method method_name do |name, &block|
        "hi '#{name}'#{block.call if block}"
      end
    end
  end
end

dummy_object.create_class_method_from_instance_method :class_hi_from_instance_method
puts Dummy.class_hi_from_instance_method('dynamic class method from instance method') { '!' }
# => hi 'dynamic class method from instance method!'

For ruby versions > 1.9.2, using define_singleton_method:

class Dummy
  def create_class_method_from_instance_method method_name, &block
    self.class.define_singleton_method method_name do |name, &block|
      "hi '#{name}'#{block.call if block}"
    end
  end
end

dummy_object.create_class_method_from_instance_method :class_hi_from_instance_method
puts Dummy.class_hi_from_instance_method('dynamic class method from instance method') { '!' }
# => hi 'dynamic class method from instance method'!

Scenario 5) Defining an instance method from an existing class method

class Dummy
  def self.create_instance_method_from_class_method method_name, &block
    define_method method_name do |name, &block|
      "hi '#{name}'#{block.call if block}"
    end
  end
end

Dummy.create_instance_method_from_class_method :instance_hi_from_class_method
puts dummy_object.instance_hi_from_class_method('dynamic instance method from class method') { '!' }
# => hi 'dynamic instance method from class method'!

Scenario 6) Defining a class method from an existing class method

For all ruby versions:

class Dummy
  def self.create_class_method_from_class_method method_name, &block
    metaclass_of_dummy_class = (class << self; self; end)
    metaclass_of_dummy_class.instance_eval do
      define_method method_name do |name, &block|
        "hi '#{name}'#{block.call if block}"
      end
    end
  end
end

Dummy.create_class_method_from_class_method :class_hi_from_class_method
puts Dummy.class_hi_from_class_method('dynamic class method from class method') { '!' }
# => hi 'dynamic class method from class method'!

For ruby versions > 1.9.2, using define_singleton_method:

class Dummy
  def self.create_class_method_from_class_method method_name, &block
    define_singleton_method method_name do |name, &block|
      "hi '#{name}'#{block.call if block}"
    end
  end
end

Dummy.create_class_method_from_class_method :class_hi_from_class_method
puts Dummy.class_hi_from_class_method('dynamic class method from class method') { '!' }
# => hi 'dynamic class method from class method'!

Conclusion

As you can see, understanding the value self holds in different environment (context) is crucial to understanding define_method.

Dynamic methods can also be defined using ‘class_eval and instance_eval too. But according to Aaron Patterson's research, it is slower compared to using define_method. You can read about the research, about how class_eval was used in rails, and one gotcha of using define_method` in this elaborate post from him.