---
title: 'Dynamic Method Definition'
source: 'https://academia.sh/en/courses/object-orientation-and-metaprogramming/dynamic-method-definition'
course: 'Object Orientation and Metaprogramming'
language: en
updated: '2026-10-21T05:03:42+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Defines a method from a block with define_method'
    - 'Measures with the backtrace label where a generated method differs from one written with def'
    - 'Shows that the method_added hook fires the same way for def, define_method and attr_reader'
    - 'Classifies the author of a method generated from a string with class_eval'
---

# Dynamic Method Definition

Measuring how define_method produces methods at run time, how those methods check their parameters, and how the production classifies the author.

The previous lesson covered reflection: asking an object about its methods, state and type. `Struct.new` wrote the `Point#x` method, and the `author` helper called it **code**. There the production was a black box, and we looked only at the result. This lesson does the production itself. **Dynamic method definition** is defining a method not by writing `def` but by giving a name and a body while the program runs. In Ruby the tool for it is `define_method`. M11/K01 measured at which stage a decision is made in C; here the question is not when the definition comes into being. The question is again who wrote it.

The axis is the same: **who wrote this method?** The author in this lesson is **code**. The programmer writes a loop once, and the loop writes two methods. What leaves the programmer's hands is only the `define_method` call, not the individual method bodies. To recall the four authors: the programmer writes with `def`, an ancestor is a superclass or a module, code is a production call, and nobody means there is no definition under that name. Dynamic definition is one of the paths of code. The reason it is needed is plain too: if dozens of methods follow the same pattern, writing the pattern once and taking the names from a list is shorter than writing each method, and when a name is added to the list the method is born by itself. This lesson measures the price of that brevity: a method name that is searched for in the source may never appear in the source. The helper below is the same as in the previous lesson and is given as it is.

```ruby
# author.rb — a helper that classifies who wrote a method

# Classification asks in order: is there a definition in the receiver's chain at all,
# then does the receiver's own class carry it, then how was the definition produced.
def author(receiver, name)
  chain = receiver.singleton_class
  unless chain.method_defined?(name) || chain.private_method_defined?(name)
    return ["nobody", "no definition"]
  end

  definition = chain.instance_method(name)
  owner = definition.owner
  place = owner == chain ? "singleton class" : owner.to_s
  return ["ancestor", place] unless owner == receiver.class || owner == chain

  iseq = RubyVM::InstructionSequence.of(definition)
  return ["code", "#{place}, no ruby body"] if iseq.nil?
  return ["code", "#{place}, block body"] if iseq.label.start_with?("block")

  ["programmer", "#{place}, def body"]
end
```

This block is source; the file is written to disk and pulled in with `require_relative`.

## The Measured Classes

The observation of this lesson rests on two classes. `Account` provides a method written with `def` for comparison. `Settings` takes its methods from a loop. The excerpt of the shared definitions is as follows.

```ruby
# authors.rb — the definitions needed for this lesson's observation
require_relative "author"

module Reportable
  def report = "report: #{balance_text}"
end

class Account
  include Reportable

  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def balance_text = "#{@owner} #{@balance}"
  def net = @balance - fee

  private

  def fee = 2
end

class Settings
  %i[color width].each do |name|
    define_method(name) { "#{name} default" }
  end
end
```

This block is source too. In the `Settings` definition, `%i[color width]` builds an array of two `Symbol` values; `each` calls `define_method` for every name and makes the block the method of that name. In the `Settings` class there is no `def` named `color` or `width`.

## Who Wrote the Generated Method

The observation of this lesson is the `color` method of a `Settings` object. The file below calls the helper and prints the value the method produces next to it.

```ruby
# generated.rb — who wrote Settings#color
require_relative "authors"

s = Settings.new
p author(s, :color)
puts s.color
```
```
["code", "Settings, block body"]
color default
```

The author is **code**. The owner of the method turned out to be `Settings` itself, so it is not an ancestor; but the body is not a `def`, it is a block given to `define_method`. The helper read this distinction from the backtrace label starting with `block`. `Point#x` in the previous lesson was code too, but it had no body at all: the answer there was `no ruby body`, and here it is `block body`. Two different forms fall into the same category. **MP10.** Only the `color` method was measured; `width` comes from the same loop and will give the same result, but that is an assumption.

## The Trace of Production

The body of a method object has a **backtrace label** that Ruby writes into the compiled body. The helper looks at the label and asks whether it starts with `block`. We print the label directly to compare two methods: `color`, produced by the loop, and `net`, written with `def`.

```ruby
# label.rb — the body label of two methods
require_relative "authors"

p Settings.instance_methods(false)
iseq = RubyVM::InstructionSequence
puts iseq.of(Settings.instance_method(:color)).label
puts iseq.of(Account.instance_method(:net)).label
```
```
[:color, :width]
block (2 levels) in <class:Settings>
net
```

`instance_methods(false)` listed only the public methods of `Settings` itself. The labels show the difference: `block (2 levels) in <class:Settings>` for `color`, and `net` for `net`. The two levels say that the definition sits inside two blocks: one is the `each` block, the other is the `define_method` block. The label of `net` is the name of the method itself. The `start_with?("block")` question of the helper separates these two labels. **MP11.** The label format was measured in Ruby 4.0.7; the text may change in another version, but starting with `block` is the only thing drawn from this measurement.

## Adding a Method While Running

`define_method` takes a name and a block, binds the method to that name and returns the name as a `Symbol`. The method can be called right after that line. It works when the class is already loaded: adding a new method to a class does not require reopening it with the `class` keyword.

```ruby
# define.rb — adding and removing a method with define_method
require_relative "authors"

result = Settings.define_method(:theme) { "dark" }
p result
p Settings.new.theme
p author(Settings.new, :theme)
Settings.remove_method(:theme)
begin
  Settings.new.theme
rescue NoMethodError => e
  puts e.class
end
```
```
:theme
"dark"
["code", "Settings, block body"]
NoMethodError
```

The return value was `:theme`; the new method answered at once, and `author` classified it as **code**, just like those produced by the loop. The time of production changed and the form of the definition did not: the body is again a block. `remove_method` deleted the method from the own table of `Settings`, and the same message then raised `NoMethodError`. If an ancestor had a definition under the same name, that ancestor definition would become visible again; `remove_method` deletes only the class's own definition. **MP12.** `remove_method` was measured only in this class and with a name that has no ancestor definition; the case with an ancestor definition is not shown here.

## Deciding the Name While Running

The name of a method written with `def` is fixed in the source. `define_method` takes the name as a string or a `Symbol`; the name can come from a variable or a computation. The class below produces one reader and one writer for each of two field names, and it never carries the method names it produces as plain text in its source.

```ruby
# form.rb — methods with computed names
require_relative "authors"

class Form
  %w[first last].each do |field|
    define_method("set_#{field}") { |value| instance_variable_set("@#{field}", value) }
    define_method("get_#{field}") { instance_variable_get("@#{field}") }
  end
end

f = Form.new
f.set_first("amy")
p f.get_first
p f.get_last
p f.instance_variables
p Form.instance_methods(false).sort
p author(f, :set_first)
```
```
"amy"
nil
[:@first]
[:get_first, :get_last, :set_first, :set_last]
["code", "Form, block body"]
```

Four methods came out of a single loop: `get_first`, `get_last`, `set_first` and `set_last`. When the written field was read, `"amy"` came back; the field that was never written gave `nil`, because that instance variable was never assigned. `instance_variables` listed only `@first`: the instance variable of the second field does not exist until a first value is written to it. The author again came out **code**. Here the programmer wrote only a pattern; code produced the four instances of the pattern. **MP13.** This class was measured with two fields; the case where the name string contains characters that are invalid in a method name was not tested.

## Parameter Checking

A block normally behaves loosely: a missing argument becomes `nil`. The block given to `define_method` behaves like a method and checks the number of arguments. The measurement below tests this in two ways: giving the block directly, and passing a `proc` object with `&`.

```ruby
# parameters.rb — define_method blocks and argument checking
class Settings
  define_method(:double) { |n| n * 2 }
  define_method(:pdouble, &proc { |n| n * 2 })
end

s = Settings.new
p Settings.instance_method(:double).arity
p s.double(4)
p proc { |n| n }.call
[:double, :pdouble].each do |name|
  begin
    s.public_send(name)
  rescue ArgumentError => e
    puts "#{name} -> #{e.class}"
  end
end
```
```
1
8
nil
double -> ArgumentError
pdouble -> ArgumentError
```

The arity came out `1` for `double`: the generated method carries the parameter of the block as a required parameter. `double(4)` gave `8`. The third line shows the contrast: an ordinary `proc` called without an argument raised no error and returned `nil`. The block of the same form inside `define_method` raised `ArgumentError`; passing the `proc` object with `&` did not change this, and `pdouble` raised the error too. So at the moment of production the block moves to the rules of a method. **MP14.** Only blocks with a single required parameter were measured; blocks with default values and variable arguments were not tested in this lesson.

## Variables the Block Sees

The most useful difference between `define_method` and `def` is that the block sees the local variables around it. A **closure** is a block carrying the local variables of the place where it was defined, even when it is called later. `def` opens a new scope and does not see the local variables around it; a block does.

```ruby
# counter.rb — a define_method block sees the variable around it
class Counter
  total = 0
  define_method(:bump) { total += 1 }
  def peek = defined?(total).inspect
end

a = Counter.new
b = Counter.new
p [a.bump, a.bump, b.bump]
puts a.peek
```
```
[1, 2, 3]
nil
```

Three calls gave `[1, 2, 3]`. `total` was a local variable in the class body, and the `bump` block carried it as a closure. The counter was shared between the two objects: the first call on the second object gave `3`, because the variable is not in the object, it is in the closure of the block. `peek`, written with `def`, could not see the same name; `defined?(total)` returned `nil` there, and `inspect` printed it as `"nil"`. For this reason the state of a method produced with `define_method` may not be in instance variables; the `instance_variables` call of reflection does not show this counter. **MP15.** The closure of the block was measured with only one local variable in this class body; sharing the closure among other methods is not shown in this example.

## The Hook

When a new method is added to a class, Ruby calls the `method_added` method of the class. A **hook** is a method defined to run when a certain event happens. The class below writes this hook and collects the added names in a list. Methods are added in three different ways: `def`, `define_method` and `attr_reader`.

```ruby
# hook.rb — in which ways method_added fires
class Jar
  @added = []

  class << self
    attr_reader :added

    def method_added(name)
      @added << name
    end
  end

  def a = 1
  define_method(:b) { 2 }
  attr_reader :c
end

p Jar.added
```
```
[:a, :b, :c]
```

All three ways fired the hook the same way: `[:a, :b, :c]`. The hook does not know by which way the method was produced; it receives only the name. This must be read together with the observations of the earlier lessons: the firing of the hook does not tell the author. What tells the author is the form of the body of the definition, and the hook does not see it. So the hook is good for watching where methods are added, but it does not answer the question of who wrote it. **MP16.** The hook was measured only in this class and while the class body was running.

## The Accessor Also Produces Code

`attr_accessor` produces a reader and a writer method with the same name. **Accessor** is the joint name of these two methods; only the reader is called `attr_reader`, and only the writer `attr_writer`.

```ruby
# access.rb — attr_accessor produces two methods
require_relative "author"

class Access
  attr_accessor :color
end

p Access.instance_methods(false).sort
p author(Access.new, :color)
p author(Access.new, :color=)
```
```
[:color, :color=]
["code", "Access, no ruby body"]
["code", "Access, no ruby body"]
```

Two methods came out: `color` and `color=`. The author of both is **code**, but the form of the body differs from `define_method`: there is no label, because `attr_accessor` leaves no Ruby body. No `def` was written in the `Access` class. `block body` and `no ruby body` are the two sub-forms of code. **MP17.** Only `attr_accessor` was measured; `attr_writer` alone was not measured.

## The Bounding Measurement

So far code has meant a `define_method` or `attr_*` call. There is one more form of production: making a class interpret a string as Ruby code. `class_eval` runs a string in the context of the class. The measurement below does the same job in two ways: one with a string, the other with a block.

```ruby
# limit.rb — the same job, two ways of production
require_relative "authors"

Settings.class_eval "def from_string = 4"
Settings.define_method(:from_block) { 4 }

s = Settings.new
p [s.from_string, s.from_block]
p author(s, :from_string)
p author(s, :from_block)
```
```
[4, 4]
["programmer", "Settings, def body"]
["code", "Settings, block body"]
```

Both methods gave `4`, but their authors split. The method produced from the string was created at run time, yet `author` classified it as **programmer**, because there is a `def` body inside the string. The one produced with `define_method` is **code**. The rule reads the form of a definition, not its intent: the category measures the form in which the definition is written, not when the production happened. So production by code does not always fall to code. The reverse holds too: seeing `define_method` in the source does not say the author is code, and a `def` written inside a string falls to the programmer even though it was produced at run time. Reflection sees the owner of the method and the form of its body; it does not see the call that started the production. Tracing the production is therefore less a matter of looking at the label than of searching the source for `define_method` and `class_eval` calls. **MP18.** Production from a string was measured only with a simple one-line `def`; in more complex strings the label and the body form may differ.

## Summary

- `define_method` takes a name and a block, binds the method to that name and returns the name as a `Symbol`; the new method can be called at once.
- The author of a generated method is code: its body is a block, and its backtrace label starts with `block`. The label of a method written with `def` is the name of the method.
- The block of `define_method` checks the number of arguments like a method; an ordinary `proc` does not.
- The `method_added` hook fires the same for `def`, `define_method` and `attr_reader`; the hook does not tell the author.
- `attr_accessor` produces two methods and the author of both is code, but the form of the body is different.
- A `def` produced from a string with `class_eval` comes out programmer: the category measures the form of the definition, not the moment of production.

## Next Step

In this lesson we defined the methods ourselves, before they were called. The next lesson measures the opposite: what Ruby does when no definition exists for the called name, and how a class can fill that gap itself. In that lesson the fourth category of the author, nobody, appears.
