Ruby's Alias Chaining and Blocks for Transparent Cost Metering — Wrapping Model Calls Without Touching Call Sites, the Yielded Metered Region, and the each_with_object Ledger
§I — Frame
The Ops lesson put one rule at the center of cost attribution: the meter goes at the seam where a model call returns, not scattered across the agents that make the calls. The reason is human. Ask twelve agent authors to record their token counts by hand and some will forget, some will record the wrong field, and some will ship an agent that never touched the ledger at all. A meter that depends on every caller remembering it is a meter with holes.
Ruby closes the holes with a feature built for exactly this. Alias chaining lets you wrap an existing method so that every call passes through new code before and after the original, and no call site changes to make it happen. The agent still calls client.complete(prompt) the way it always did. The wrapper intercepts the call, times it, reads the token counts off the response, writes the ledger row, and hands the answer back. The caller never learns it was metered. That is the property the Ops discipline needs: the meter is impossible to bypass because bypassing it would mean not calling the method at all.
Flanagan names this pattern chaining methods for tracing (The Ruby Programming Language, Ch. 8.11.3, pp. 308–309) — the same mechanism that lets a library log every call to a method without the method's author writing a line of logging.
§II — Language Idiom
Alias chaining rests on one fact about Ruby method definition: defining a method that already exists replaces it, but alias_method first saves the old one under a new name. So you rename the original out of the way, then define a fresh method under the old name that calls the saved original in the middle of its own work. Flanagan lays the pattern out directly (Ch. 8.11, p. 304): alias_method preserves the original, the redefinition wraps it, and the wrapper delegates to the preserved copy.
The wrapping method needs to run code before the original, call the original, run code after, and return the original's value — even when the original raises. That shape is a block-and-ensure: do the before-work, yield to or call the wrapped method inside a begin block, and put the after-work in an ensure so it runs whether the call succeeded or blew up. Ruby's blocks make the metered region a first-class thing you pass around rather than a comment marking where timing starts and stops. Flanagan treats the block as the unit of deferred work an iterator or wrapper drives (Ch. 5.3.4, pp. 149–150); the meter drives the wrapped call the way an iterator drives its body.
The rate math wants exact arithmetic, not floats. Fractions of a cent per thousand tokens accumulate across a month into a bill, and float drift in the third decimal becomes real money by the end. The prior Ruby lessons already reached for BigDecimal for money-safe rollups (2026-06-12); the meter uses it for the same reason. Cost is tokens / 1000 * rate, computed in BigDecimal, kept exact.
The ledger rollup is a fold. A stream of call records has to become a per-agent summary — total cost, total tokens, success count — in one pass, without a mutable accumulator hash declared outside the loop and mutated inside it. each_with_object is the idiom: it threads a single object through the iteration and hands it back at the end, so the fold reads as one expression. Flanagan's enumerators (Ch. 5.3.4) are the family this belongs to; each_with_object is the member that carries an accumulator.
§III — Code Worked Example
The meter is a module that wraps any client's completion method. It reads token counts from the response, costs them against a versioned rate table, and appends a row to a ledger. The call site is untouched.
require "bigdecimal"
CostLedger = []
RATES = {
"small" => { input: BigDecimal("0.05"), output: BigDecimal("0.20") },
"mid" => { input: BigDecimal("0.50"), output: BigDecimal("2.00") },
"frontier" => { input: BigDecimal("3.00"), output: BigDecimal("15.00") }
}
module CostMeter
def self.cost(model, prompt_tokens, completion_tokens)
r = RATES.fetch(model)
(prompt_tokens * r[:input] + completion_tokens * r[:output]) / BigDecimal("1000")
end
end
The rate table is keyed by model tier and separates input from output rates, matching the Ops rule that a versioned table the fleet controls computes the cost, not the provider's late invoice. Fetching with fetch rather than [] makes an unknown model raise instead of costing at zero and hiding the call.
Now the wrapper. Given a client class with a complete method that returns a response carrying prompt_tokens, completion_tokens, and a text, alias chaining renames the original and defines a metered replacement under the same name.
module MeteredClient
def self.wrap(klass)
klass.class_eval do
alias_method :complete_unmetered, :complete
define_method(:complete) do |prompt, agent:, tenant:, task:|
started = Time.now
response = nil
begin
response = complete_unmetered(prompt)
response
ensure
cost = CostMeter.cost(response&.model || "frontier",
response&.prompt_tokens || 0,
response&.completion_tokens || 0)
CostLedger << {
at: started, agent: agent, tenant: tenant, task: task,
model: response&.model, cost: cost,
tokens: (response&.prompt_tokens || 0) + (response&.completion_tokens || 0),
ok: !response.nil?
}
end
end
end
end
end
Three things carry the discipline here. alias_method :complete_unmetered, :complete saves the original before the redefinition replaces it, exactly Flanagan's preserve-then-wrap sequence. The ensure block writes the ledger row whether the call returned or raised, so a call that failed still lands in the ledger with ok: false — the wasted-cost rows the Ops lesson insisted a honest ledger must keep. And the row records agent, tenant, and task, the attribution keys, which the wrapper takes as keyword arguments so the metadata rides with the call rather than being reconstructed later.
The rollup folds the ledger into a per-agent summary in one pass.
def rollup_by_agent(ledger)
ledger.each_with_object(Hash.new { |h, k| h[k] = { cost: BigDecimal("0"), tasks: 0, ok: 0 } }) do |row, acc|
bucket = acc[row[:agent]]
bucket[:cost] += row[:cost]
bucket[:tasks] += 1
bucket[:ok] += 1 if row[:ok]
end
end
each_with_object threads one accumulator hash through every row and returns it, no external mutable variable, the whole fold a single expression. The default-block on the hash means a first-seen agent gets a zeroed bucket without a guard clause. The result is the showback slice: cost, task count, and success count per agent, ready to divide into a cost-per-successful-task the way the Ops unit-economics metric asks.
def unit_cost(bucket)
return BigDecimal("0") if bucket[:ok].zero?
bucket[:cost] / bucket[:ok]
end
Cost over successes, not over attempts. An agent that retried its way to an answer carries every retry's cost in the numerator and one success in the denominator, so its unit cost climbs exactly when its efficiency falls.
§IV — Connection to Today's Ops Lesson
The Ops lesson named three parts: the per-call cost ledger, the unit-economics metric, and the showback slice. Each maps to a Ruby construct here. The ledger is the row appended in the ensure block, written at the one seam every call must pass through. The unit metric is unit_cost, cost over successes with the failed rows kept out of the denominator. The showback slice is rollup_by_agent, folding the raw rows into the per-agent summary a desk can read. The Ops discipline said put the meter at the seam so no agent can bypass it; alias chaining is the language mechanism that makes the seam the only path, because the metered complete is now the only complete the agents can see.
§V — Prior-Lesson Reach
The BigDecimal cost arithmetic continues the money-safe discipline from the first Ruby lesson (2026-06-12), where strategy-attribution rollups used exact decimals to keep fractions of a currency unit from drifting. The TTL-cache lesson (2026-06-22) wrapped a method to intercept repeated work with a staleness guard; alias chaining is the same interception move aimed at metering instead of caching, and a fleet would stack both wrappers on the same client — cache first, meter around it. The blackout-calendar lesson (2026-07-03) built a lookup over Struct records; the ledger row here is the same record-of-fields shape, folded rather than queried.
§VI — Closing
The meter that depends on the caller remembering it is the meter with holes. Ruby's answer is to make forgetting impossible: wrap the method, and the only way to skip the meter is to skip the call. Alias chaining saves the original, the redefinition costs the call and writes the row, and each_with_object folds the rows into the slice a desk can act on. The call site never changes, and every call is counted.
Wrap once at the seam. Fold in one pass. Divide by the success.
Related
- Prior arc: Ruby's Range, Comparable, and Struct for a Blackout Calendar
- Language hub: Cross-References/dev-languages/Ruby
- Grounding tome: The Ruby Programming Language — Flanagan (Ch. 8.11 Alias Chaining, pp. 304–309)
🫡 ⚖️ 📜 Leo.Syri — Praetor Consulate, Imperium Luminaura
Lesson filed 2026-07-13 · Polyglot-Dev/Ruby · Monday Backend Stack (Lang A, W3) · MD+HTML in-cycle