Define_method, Define_singleton_method vs Method_missing

Lakhveer Singh Rajput
3 min readDec 5, 2023

--

In the previous article, we saw many aspects of metaprogramming like class_eval, instance_eval, and send method. Now, this is the last article on the basic use of metaprogramming in Ruby. It will cover the define_method, define_singleton_method, and method_missing, how they work, and how they can be used to modify the class dynamically. We will be going through the method definition first and then will continue with an example to use them in real-time.

Define_method and Define_singleton_method — Adding behaviours dynamically to class

The define_method as the name suggests helps us to define methods on the go whenever we need to create them dynamically. The same is true with the define_singleton_method, it just creates the class method instead of the instance method. Let's see their syntax and the way to create them.

class Product
# Define an instance method using define_method
define_method :product_name do |param|
puts "This is an instance method with param: #{param}"
end
end

# Create an instance of Product
obj = Product.new

# Call the instance method
obj.product_name("Hello, Product Class!")
#This is an instance method with param: Hello, Product Class!

class Product
# Define a class method using define_singleton_method
define_singleton_method :class_name do |param|
puts "This is a class method with param: #{param}"
end
end

# Call the class method
Product.class_name("Hello, class method for Product Class!")
#This is a class method with param: Hello, class method for Product Class!

Method Missing — When no Method Exists

We have many a time seen that when we call a method that doesn’t exist it throws an error for the method that had been missing for the Class. The method_missing is the part of the BasicObject that had been called whenever an unknown method is called on an Object that had not been defined in its class. By using method_missing we simply use the OOPs concept of method overriding(Method Overloading is not supported by Ruby). Let’s see their syntax and the way to override this.

class Example
def method_missing(name, *args, &block)
if name.to_s.start_with?("test")
puts "We are testing"
else
super
end
end
end

# Example usage:
obj = Example.new
obj.test_method # Outputs: We are testing
obj.another_method # Raises NoMethodError

Real Time Usage

For the time let's suppose we have a Person class in which we will automatically create a method for the person's name and return his details in the formatted way. We will also create a class method for which it will define how to use this method. We will also use the send method here so that we can again get through it.

class Person
def initialize(name, address, date_of_birth)
@name = name
@address = address
@date_of_birth = date_of_birth
end

def method_missing(name, *args, &block)
super unless @name.downcase.gsub(' ', '_') == name.to_s
self.class.send(:define_method, name) do
puts "Name: #{@name}"
puts "Address: #{@address}"
puts "Date Of Birth: #{@date_of_birth}"
end
self.send name
end

define_singleton_method :person_name do
puts "Whenever you want to get the details of the Person \n(Let's suppose his name is John Doe) : \nperson.john_doe (here person is the object of the Person class)"
end
end

# Person usage:
Person.person_name
#Whenever you want to get the details of the Person
#(Let's suppose his name is John Doe) :
#person.john_doe (here person is the object of the Person class)
person = Person.new("William James", "154, Bevery Hills", "12-07-1980")
person.william_james
#Name: William James
#Address: 154, Bevery Hills
#Date Of Birth: 12-07-1980
person.another_method
#Raise NoMethodError

Here we created the Person class and overridden the BasicObject method method_missing. The method_missing now checks that if the method is called with the person’s name then it defines a method in the class that would give the details for the person otherwise it simply returns the NoMethodError. We had again called the person_name method so that it could output the details for the person, as we had created the method not called it.

To be continued

We will have many more interesting topics to get through the Ruby and Ruby On Rails in the coming time. So, we forget everything if we just read and use it once. But if we try to implement them in everyday code, then it would be far more interesting.

--

--

Lakhveer Singh Rajput

Ruby on Rails enthusiast, book lover and DevOps explorer. Follow me for insights on coding, book recommendations, and bridging development with operations.🚀📚