Project

calltally

0.0
No release in over 3 years
A simple yet powerful tool to analyze method usage in Ruby/Rails codebases. Track which methods are called most frequently, filter by receivers or method names, and export results in table, JSON, or CSV format. Perfect for understanding code patterns, refactoring decisions, and identifying heavily-used APIs.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

~> 13.0

Runtime

>= 3.0
>= 1.0
 Project Readme

Calltally

Tally your method calls.

A simple yet powerful tool to analyze method usage in Ruby/Rails codebases. Quickly identify your most-used methods, understand code patterns, and make informed refactoring decisions.

Installation

Add to your Gemfile:

gem 'calltally', group: :development

Or install globally:

gem install calltally

Quick Start

# Analyze current directory
calltally

# Analyze specific directory
calltally app/models

# Show top 20 results
calltally --top 20

# Focus on specific classes
calltally --receivers User,Post --top 10

Usage Examples

Rails Projects

Calltally automatically detects Rails projects and scans the right directories:

# Auto-detects Rails and scans app/, lib/, config/
calltally

# Analyze specific file patterns
calltally app/models

# Focus on ActiveRecord methods
calltally --methods where,find,joins --mode pairs

Output Formats

# Default table format
calltally

# JSON format for further processing
calltally --format json > analysis.json

# CSV for spreadsheet analysis
calltally --format csv -o results.csv

Filtering and Analysis

# Show only method names (no receivers)
calltally --mode methods

# Show only receivers (classes being called)
calltally --mode receivers

# Show receiver-method pairs (default)
calltally --mode pairs

# Include methods called without explicit receivers
calltally --include-nil-receiver

Configuration

Create .calltally.yml in your project root for persistent settings:

# .calltally.yml
profile: rails          # auto|rails|default
dirs:                   # Directories to scan
  - app
  - lib
exclude:                # Patterns to exclude
  - spec
  - test
  - vendor
top: 50                 # Number of results to show
mode: pairs            # pairs|methods|receivers
skip_operators: true    # Skip operators like +, -, ==

You can also use a custom config file:

calltally --config config/calltally-production.yml

Advanced Usage

Filter by Variable Types
# Only local variables
calltally --only-locals

# Only instance variables
calltally --only-ivars

# Only class/module constants
calltally --only-constants

# Class variables
calltally --only-cvars

# Global variables
calltally --only-gvars

# Combine filters
calltally --only-locals --only-constants

# Show variable names instead of grouping
calltally --split-variables
# Shows: (var:user).name instead of (var).name
All CLI Options
calltally [PATH] [options]

Options:
  --profile PROFILE        auto|rails|default (default: auto)
  -d, --dirs x,y           Directories to include
  -x, --exclude x,y        Path parts to exclude
  -n, --top N              Show top N results (default: 100)
  -v, --verbose            Verbose output

  --mode MODE              Output mode:
                           - pairs: receiver-method pairs (default)
                           - methods: method names only
                           - receivers: receiver names only

  --receivers x,y          Filter by receiver constants (e.g. User,Post)
  --methods x,y            Filter by method names (e.g. where,find)

  --include-nil-receiver   Count calls without explicit receiver
  --split-variables        Show variable names (e.g. '(var:user)' vs '(var)')

  --only-locals            Show only local variable receivers
  --only-ivars             Show only instance variable receivers
  --only-cvars             Show only class variable receivers
  --only-gvars             Show only global variable receivers
  --only-constants         Show only constant receivers

  --[no-]skip-operators    Skip operator methods like +, -, ==, [] (default: true)

  --format FORMAT          Output format: table|json|csv (default: table)
  -o, --output PATH        Write result to file instead of STDOUT
  --config PATH            Use a specific config file
  -h, --help               Show help

Understanding the Output

Calltally shows method calls in your codebase with their receivers:

10  User.where          # User class, where method, called 10 times
 5  (var).each          # Local variable, each method, called 5 times
 3  (ivar).save         # Instance variable, save method
 2  Post#.validate      # validate called within Post class (implicit receiver)

Receiver Types

  • User - Class or module constant
  • (var) - Local variable (use --split-variables to see names)
  • (ivar) - Instance variable
  • (cvar) - Class variable
  • (gvar) - Global variable
  • (self) - Explicit self receiver
  • (result) - Method calls on results (e.g., user.posts.first(var).posts + (result).first)
  • # - Implicit receiver (when using --include-nil-receiver)

Use Cases

  1. Find most-used methods - Identify candidates for optimization
  2. Understand code patterns - See how your team uses APIs
  3. Refactoring decisions - Know what methods are heavily depended upon
  4. API design - Understand which methods are actually used
  5. Code reviews - Quickly analyze unfamiliar codebases
  6. Gem development - See how your gem's methods are used

FAQ

Why does grep show different counts than CallTally?

CallTally counts method calls, not all text occurrences:

  • grep "Current.user" finds both Current.user (getter) and Current.user = value (setter)
  • CallTally only counts Current.user (the getter method call)
  • Setters like name= are separate methods and not counted as name

Which file types are analyzed?

  • Ruby files: .rb, .ru, .rake
  • Not included: JavaScript, CSS, YAML, and other file types

Why do I see (result) as a receiver?

When methods are chained, CallTally shows intermediate results as (result):

  • user.posts.first(var).posts + (result).first
  • This happens because CallTally doesn't infer types without type annotations

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/nsgc/calltally.

License

The gem is available as open source under the terms of the MIT License.