# Metaprogramming: send, define_method and method_missing — Ruby

Source: https://www.geekswithgeeks.com/en/ruby/x-meta

> Generate and dispatch methods dynamically, and know the costs.

## Code that writes code

**Metaprogramming** means code that defines or changes code at run time, and Ruby makes it unusually easy. **`send(:method_name, *args)`** (or `public_send`, which respects visibility) calls a method chosen at run time. **`define_method`** creates methods programmatically, for example generating `paid?`, `shipped?` and `cancelled?` from a list of statuses. **`method_missing`** catches calls to undefined methods, enabling dynamic finders or proxies; when you use it, also define **`respond_to_missing?`** so `respond_to?` stays accurate. **`instance_variable_get`/`set`**, **`class_eval`** and **`instance_eval`** inspect and modify objects and classes. Hooks such as `included`, `inherited` and `method_added` let modules react when they are used. Frameworks such as Rails use metaprogramming heavily (for example `has_many :orders` defines a whole set of methods). In application code, use it sparingly: dynamic methods are harder to find with search, confuse IDEs and static analysis, and can be slower. Prefer explicit code until repetition clearly justifies generation.

## Generating predicate methods and a careful method_missing

define_method for known names; method_missing with respond_to_missing?.

```ruby
class Order
  STATUSES = %i[placed paid shipped cancelled].freeze
  attr_reader :status, :attributes

  def initialize(status:, **attributes)
    @status = status
    @attributes = attributes
  end

  STATUSES.each do |s|
    define_method(:"#{s}?") { status == s }          # placed?, paid?, shipped?, cancelled?
  end

  def method_missing(name, *args)
    key = name.to_s.delete_prefix("attr_").to_sym
    return attributes.fetch(key) if name.start_with?("attr_") && attributes.key?(key)
    super                                              # keep NoMethodError for everything else
  end

  def respond_to_missing?(name, include_private = false)
    (name.start_with?("attr_") && attributes.key?(name.to_s.delete_prefix("attr_").to_sym)) || super
  end
end

o = Order.new(status: :paid, city: "Pune", total: 1200)
puts o.paid?                     # true
puts o.attr_city                 # Pune
puts o.respond_to?(:attr_total)  # true
puts o.public_send(:shipped?)    # false
```

## A receptionist with a rulebook

method_missing is a receptionist who handles visitors asking for rooms that do not exist on the directory, using a rulebook. Useful, but if the rulebook is vague, nobody can tell which rooms really exist.

**Quiz:** What should you define alongside method_missing?

- [ ] initialize
- [ ] to_s
- [ ] each
- [x] respond_to_missing? so respond_to? reports dynamic methods correctly

*Answer:* respond_to_missing? so respond_to? reports dynamic methods correctly. respond_to_missing? keeps introspection consistent with the dynamic behaviour.
