In Ruby, how does define_method differ from def in how the method name is chosen and what the method body can see?
answer
- literal name versus a value
- scope gate versus closure
- yield sees the wrong block
- |&blk| to take the caller's block
- private applies in the class body
basics
~20 sdef takes a literal name and starts a fresh local scope. define_method takes the name as a Symbol or String value and uses a block as the body, so the body is a closure that sees the defining scope's local variables.
solid answer
~40 s`def` fixes the name in the source and is a scope gate: the body cannot see the class body's local variables. `define_method(name) { ... }` takes the name as data and runs a block as the body, so the body closes over locals such as a loop variable, while `self` is still the instance. Both return the name as a Symbol, and both honour a preceding `private` when called in the class body. The traps: the block is made strict like a lambda, and `yield` or `block_given?` inside it refer to the defining context's block, not the caller's; declare `|&blk|` to receive the caller's block. In Ruby 4.0 a `yield` in such a block directly in a class body is even a `SyntaxError`.
code
ruby · 10 linesclass Feed
def initialize(lines) = @lines = lines
define_method(:each_line) do |&blk|
@lines.each(&blk)
end
end
Feed.new(["a", "b"]).each_line { puts it }
# prints a, then bgo deeper
Recall that define_method takes the name as a value and a block as the body, while def has a literal name.
Explain scope gate versus closure, the strict lambda arity, and why yield needs a |&blk| parameter instead.
Show judgement: def by default, define_method only when the name is data or the body must capture a value, and keep generated code greppable.
Set a team rule for when generated methods are worth the lost searchability and the closures they keep alive.
## Two ways to add an instance method Ruby has two everyday ways to put an instance method into a class: - **`def name ... end`** — a keyword. The name is written literally in the source, and the body is compiled as a method. - **`define_method(name) { |args| ... }`** — a method of `Module`, called on the class. The name is a **Symbol or String value**, and the body is a **block**. Both produce an ordinary instance method that callers use in exactly the same way, and both return the method's name as a Symbol. The differences are in what the body can see and how the name is chosen. ## Scope: a gate versus a closure `def` is a **scope gate**: the method body starts with a fresh set of local variables and cannot see locals of the class body around it. A block is a **closure**: it keeps access to the local variables that were in scope where it was written. ```ruby class Price rate = 1.2 # a local of the class body def gross_def(net) = net * rate # NameError when called: rate is not visible define_method(:gross) { |net| net * rate } # sees rate end Price.new.gross(10) # => 12.0 ``` That is the main reason to reach for `define_method`: the body needs a value that only exists while the class is being built, such as a loop variable or a configuration entry. ## The name as data Because `define_method` takes the name as an argument, the name can be computed: `define_method(:"#{field}_changed?")`. `def` cannot do this; its name is fixed when the file is parsed. A String name is converted to a Symbol. ## Where the two behave differently | Aspect | `def` | `define_method` with a block | |---|---|---| | name | literal in the source | any Symbol or String value | | outer locals | not visible | visible (closure) | | `self` in the body | the instance | the instance | | `yield`, `block_given?` | the caller's block | the block of the *defining* context | | arity | as declared | strict, like a lambda | | `return` | leaves the method | leaves the method | | visibility under `private` in the class body | private | private | | return value | name as a Symbol | name as a Symbol | Three rows need a word. 1. **`yield` does not reach the caller's block.** Inside the block body, `yield` and `block_given?` refer to whatever block the *surrounding* code received. To accept the caller's block, declare it as a block parameter: `define_method(:each_line) { |&blk| lines.each(&blk) }`. In Ruby 4.0, whose default parser is Prism, a `yield` inside a `define_method` block written directly in a class body does not even parse: it is a `SyntaxError` ("Invalid yield"). 2. **Arity is strict.** The block is turned into a lambda, so calling with the wrong number of arguments raises `ArgumentError`, exactly like a `def` method. 3. **Visibility follows where it is called.** Called in a class body after a bare `private`, `define_method` creates a private method, just as `def` would. Called from outside, as `Order.define_method(...)`, it creates a public method. `Module#define_method` has been public since Ruby 2.5; older gems wrote `send(:define_method, ...)` because it used to be private. ## When to use which - Prefer **`def`** whenever the name is known when you write the code. It is faster to read, found by a text search, and it has no closure to keep alive. - Use **`define_method`** when the name comes from data (a list of fields, statuses or formats) or the body must capture a value from the defining scope. - If you find yourself generating one method, you probably wanted `def`. ## What an interviewer is listening for The expected core is: the name is dynamic and the body is a closure, while `def` has a literal name and a fresh scope. A stronger answer mentions the `yield` trap and the `|&blk|` fix, and that `private` applies when `define_method` runs inside the class body. The best answers add the maintenance cost: generated methods do not appear in a text search, so keep the generating loop short and next to the list it reads.
- Why does older gem code call send(:define_method, ...) on a class?Before Ruby 2.5, `Module#define_method` was private, so code outside the class body had to reach it with `send`. Since 2.5 it is public, and `SomeClass.define_method(:x) { ... }` works directly. The `send` form still runs but only obscures intent in Ruby 4.0 code.
- Does a method made by define_method after a bare private in the class body end up private?Yes. When `define_method` runs in the class body, it picks up the current default visibility, so after `private` the method is private, just like a `def` there. When it is called from elsewhere, for example `Order.define_method(:x) { }` from a helper, the method is public.
saying these in an interview costs you the question
- define_method bodies cannot see the class body's local variables
- yield inside a define_method block reaches the caller's block
- define_method ignores private, so its methods are always public
- define_method returns the new Method object, not a Symbol
- a define_method body runs with self set to the class