ruby 111 lines · 3 tabs

Metaprogramming with method_missing and define_method

Sarah Mitchell Feb 2026
3 tabs
class DynamicFinder
  def initialize(records)
    @records = records
  end

  def method_missing(method_name, *args, &block)
    if method_name.to_s =~ /^find_by_(.+)$/
      attribute = $1
      find_by_attribute(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

  private

  def find_by_attribute(attribute, value)
    @records.find { |record| record[attribute.to_sym] == value }
  end
end

# Usage:
users = [
  { id: 1, name: 'Alice', email: 'alice@example.com' },
  { id: 2, name: 'Bob', email: 'bob@example.com' }
]

finder = DynamicFinder.new(users)
finder.find_by_name('Alice')   # => { id: 1, name: 'Alice', ... }
finder.find_by_email('bob@example.com')  # => { id: 2, ... }
3 files · ruby Explain with highlit

Ruby's metaprogramming enables dynamic method definition and interception. method_missing catches undefined method calls, allowing DSL creation and proxy patterns. I implement it carefully with respond_to_missing? for proper introspection. define_method creates methods dynamically at runtime, useful for reducing repetition. Class and instance variables can be manipulated via class_variable_set and instance_variable_set. send and public_send invoke methods dynamically. Metaprogramming powers Rails' magic—associations, validations, callbacks. I use it judiciously to create expressive APIs without sacrificing clarity. Understanding Ruby's object model—classes are objects, methods are objects—unlocks powerful patterns. Metaprogramming reduces boilerplate while maintaining Ruby's elegance and readability.


Related snips

Share this code

Here's the card — post it anywhere.

Metaprogramming with method_missing and define_method — share card
Link copied