module Finance
class Accounts
def debit(account, amount, memo:) = …
end
end public_send goes through the same wrapper as a direct
call and is recorded. A method of the same name on another class is a different method and is not listed.
The problem
You’re about to change Finance::Accounts#debit and want to know who calls it, so you search:
$ grep -rn "debit" app lib
app/models/invoice.rb:53: accounts.debit(account, total, memo: number)
app/models/ledger.rb:6: def debit(amount, memo:)
app/models/ledger.rb:7: @lines << [:debit, amount, memo]
app/finance/finance/accounts.rb:14: def debit(account, amount, memo:)
app/jobs/nightly_fees_job.rb:7: ledger.debit(account.plan.monthly_fee, memo: "fees #{account.id}")
app/services/refund.rb:9: entry = ledger.debit(@invoice.account, @invoice.total, memo: "refund #{@invoice.number}")
app/services/fee_schedule.rb:6: accounts.debit(account, fee, memo: "fee")
app/services/transfer.rb:2: def initialize(account, amount, direction: :debit)
app/billing/billing/checkout.rb:12: Finance::Accounts.new.debit(order.account, order.total, memo: "#{order.number} #{card.masked}")
The list includes lines that are not callers and leaves out a caller. Two lines are in Ledger, a
different class with its own #debit. ledger.debit appears twice, in Refund and in the nightly
job, and neither line says whether ledger is a Ledger or a Finance::Accounts. Transfer
matches only on a default argument. Its call is on line 9, through
public_send(@direction, ...), where the word debit does not appear.
An agent asked the same question runs the same search and gets the same list.
The fix
Ask the recording:
$ ra what_calls Finance::Accounts#debit
Finance::Accounts#debit
· 7 callers in 2 recorded runs
Billing::Checkout#complete app/billing/billing/checkout.rb:12 63
FeeSchedule#charge app/services/fee_schedule.rb:6 12
Refund#process app/services/refund.rb:9 7
Transfer#call app/services/transfer.rb:9 5 via public_send
spec/finance/accounts_spec.rb spec/finance/accounts_spec.rb:7 2 its own spec
Invoice#settle app/models/invoice.rb:53 1
bin/traffic bin/traffic:44 1
The report lists seven callers. They include the public_send call in Transfer#call, which
grep had no line for. Refund#process is among them, so its ledger is a Finance::Accounts, and
the nightly job is absent, so its ledger is the other class. The
two recorded runs are the spec suite and a scripted traffic run, so the counts say how often each
caller called the method in those runs. The change affects five callers in app code, a spec and a
script.
what_it_calls answers the reverse question and lists the methods it calls:
$ ra what_it_calls Finance::Accounts#debit
Finance::Accounts#debit
· calls 10 methods
Account#balance 182
Money#- 178
Money#<=> 91
Account#balance= 89
Money.zero 89
Finance::Ledger#record 89
Reporting::Formatters::Currency#initialize 89
Reporting::Formatters::Currency#to_ledger_s 89
Finance::Audit#log 89
Money#to_s 4
Where it shows up
- In your editor (LSP), “Find references” on a method returns the recorded call sites, with the file and line each call was made from.
- Over MCP,
what_callsreturns the same table as structured blocks. Your agent can ask it before editing a method and get the callers in one call, in place of a page of grep output. - On the command line,
ra what_callsandra what_it_callstake anyClass#methodorClass.method, or a bare method name when one class alone defines it.
How it works
Every call into a method defined in your app is recorded as an edge: the method that made the call,
the file and line it was made from, and the method that received it. what_calls groups those edges
by caller and counts them. A call made from code that is not traced, a spec body or a script, has no
calling method, so it is listed under its file, and a spec file named after the class is marked as
the method’s own spec.
The recorder wraps the method itself, so every way of calling it is recorded the same way. A direct
call, send, public_send, a callback or a method name built from a string all go through the same
wrapper. The via public_send note comes from reading the source line at the call site.
The receiver’s class is part of the edge, so Finance::Accounts#debit and Ledger#debit are
recorded as two separate methods.
Run it on your app
The output on this page comes from our example Rails app, and it is regenerated whenever a tool
changes. These are the commands behind it. On your own app, run ra record first, then put your
own class, method or file in their place.
ra what_calls 'Finance::Accounts#debit'
ra what_it_calls 'Finance::Accounts#debit'
The method is app/finance/finance/accounts.rb:14. The public_send caller is
app/services/transfer.rb:9 and the other debit, Ledger#debit, is app/models/ledger.rb:6.
Reading the result
- Calls into your own classes are recorded. A caller inside a gem that calls back into your code is listed at the point where it enters your code, and the gem method’s own name is not shown.
- The counts measure traffic in what you recorded. The 63 calls from
Billing::Checkout#completeare the checkouts the traffic script ran. They are not a measure of how often production calls it.
Every tool reads the same recording. What gets recorded and how far to trust an answer are on the questions page.
Related tools
how_it_got_here
How execution got here. The call chains that reached a method, in call order, taken from recorded runs. It is the stack trace you'd have got if the method had raised, for every route into it.
See more →what_a_change_touches
What a signature change affects. For each caller of a method, the argument classes it passed and the messages it sent to the return value, so you can see which callers a signature change affects before you make it.
See more →dead_api
Find dead public API. The public methods that no caller reached in any recorded run, including those whose only caller is their own spec. Shown as a hint in your editor.
See more →