Metaprogramming in Ruby on Rails
Metaprogramming is the technique of writing code that can generate or modify other code at runtime.
Ruby has powerful built-in support for metaprogramming. This allows developers to:
- Define methods dynamically
- Modify existing classes
- Change object behavior at runtime
Ruby on Rails heavily uses metaprogramming internally. Features like dynamic finders, associations, validations, and scopes are all powered by it.
Some commonly used metaprogramming techniques in Ruby are:
define_methodmethod_missingsendclass_evalinstance_eval
define_method
As the name suggests, define_method is used to define methods dynamically at runtime.
Without Metaprogramming:
class Order
def pending?
status == "pending"
end
def processing?
status == "processing"
end
def shipped?
status == "shipped"
end
endThis works, but becomes repetitive when there are many statuses.
With define_method:
class Order
STATUSES = %w[pending processing shipped delivered cancelled]
attr_accessor :status
STATUSES.each do |status|
define_method("#{status}?") do
self.status == status
end
end
end
order = Order.new
order.status = "pending"
order.pending? # => true
order.shipped? # => falseHere, methods are generated dynamically for each status, making the code cleaner and easier to maintain.
method_missing
method_missing is called automatically when Ruby cannot find a method. It allows us to handle undefined methods dynamically.
class DynamicFinder
def method_missing(method_name, *args)
if method_name.to_s.start_with?("find_by_")
attribute = method_name.to_s.delete_prefix("find_by_")
"Finding by #{attribute}: #{args.first}"
else
super
end
end
def respond_to_missing?(method_name, include_private = false)
method_name.to_s.start_with?("find_by_") || super
end
end
finder = DynamicFinder.new
finder.find_by_email("sha@test.com")
# => "Finding by email: sha@test.com"
finder.find_by_name("Shamila")
# => "Finding by name: Shamila"Important Note:
Whenever you override method_missing, you should also override respond_to_missing?.
Get Shamila’s stories in your inbox
Join Medium for free to get updates from this writer.
Otherwise, methods like respond_to? will not work correctly
send
send calls a method dynamically using its name as a symbol or string. It is useful when the method name is determined at runtime.
class User
attr_accessor :email, :name
end
user = User.new
def update_attribute(user, name, value)
user.send("#{name}=", value)
end
update_attribute(user, :email, "new@test.com")Important Note:
send can call private and protected methods as well.
Because of this, public_send is generally safer when you don't need access to private methods.
class_eval
class_eval Modifies a class at run time
Example:
class User
end
User.class_eval do
def admin?
true
end
end
user1 = User.new
user2 = User.new
user1.admin? # => true
user2.admin? # => trueHere, admin? gets added dynamically to the User class, so all instances of User can use it.
instance_eval
instance_eval evaluates code in the context of a specific object instead of a class.
It is often used to define singleton methods or modify a single object dynamically.
Example:
# Assume User is an ActiveRecord model
user = User.new
user.instance_eval do
def admin?
true
end
end
user.admin? # => trueOnly this particular user object will have the admin? method.
Other instances of User will not.
Key Point:
class_evalmodifies a class, affects all instancesinstance_evalmodifies a specific object, affects only that object
Conclusion
Metaprogramming is one of Ruby’s most powerful features and is heavily used inside Ruby on Rails.
It helps developers write flexible, reusable, and expressive code.
However, keep these trade-offs in mind:
method_missingis slower than defined methods because Ruby walks the full method lookup chain before reaching it- Excessive metaprogramming can make applications harder to debug and understand
- Always override
respond_to_missing?alongsidemethod_missing - Prefer
public_sendoversendwhen you don't need access to private methods
The key is to use metaprogramming wisely while keeping the code readable and maintainable.





