Project

srb_lens

0.0
The project is in a healthy, maintained state
Extract method signatures, call graphs, and type information from Sorbet-typed Ruby projects
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

>= 0.9.124
 Project Readme

srb-lens

A static analysis toolkit for Sorbet-typed Ruby projects. srb-lens parses Sorbet's internal outputs (CFG, symbol tables, autogen) and builds a queryable model of your project's methods, classes, types, and control flow.

What it extracts

For every method in your project, srb-lens gives you:

  • Signature — arguments with types, return type, optional parameters
  • Call graph — what methods are called, on what receiver types, with what return types
  • Control flow — basic blocks, branch conditions, which calls happen under which branches
  • Class hierarchy — superclasses, mixins, modules
  • Instance variables — names and types
  • Source locations — file paths and line numbers
  • Block usage & rescue clauses

Project structure

srb-lens/
  crates/
    srb-lens/          # Core Rust library
    srb-lens-cli/      # Command-line interface
  gems/
    srb_lens/          # Ruby gem (native extension via Magnus)

Getting started

Prerequisites

  • Rust toolchain (stable, edition 2024)
  • A Ruby project with Sorbet configured

Install the CLI

cargo install --path crates/srb-lens-cli

Index a project

cd /path/to/your-ruby-project
srb-lens index

This runs Sorbet with --print flags, parses the output, and caches everything in a .srb-lens/ directory. If your project uses Bundler:

srb-lens index --srb-command "bundle exec srb"

Query methods

# Instance method
srb-lens query "User#activate!"

# Class method
srb-lens query "User.find_by_email"

# All methods in a class
srb-lens query "User"

# List matching method names only
srb-lens query "User" --list

# JSON output
srb-lens query "User#activate!" --json

Pipe mode

You can pipe Sorbet's cfg-text output directly:

srb tc --print=cfg-text 2>/dev/null | srb-lens pipe "User#activate!"

CLI reference

srb-lens index

Run Sorbet and build the index cache.

Flag Description
-d, --dir <DIR> Project root (default: current directory)
--srb-command <CMD> Sorbet command (default: srb)

srb-lens query <QUERY>

Query method or class information from the index.

Flag Description
-d, --dir <DIR> Project root (default: current directory)
--index Force re-index before querying
--srb-command <CMD> Sorbet command (used with --index)
--cfg <FILE> Read cfg-text from file instead of cache
--symbols <FILE> Read symbol-table-json from file
--autogen <FILE> Read autogen from file
--list List matching method names only
--json Output as JSON

srb-lens pipe <QUERY>

Read cfg-text from stdin and query.

Flag Description
--list List matching method names only
--json Output as JSON

Query format

Pattern Meaning
Foo#bar Instance method bar on Foo
Foo.bar Class method bar on Foo
Foo All methods defined in Foo

Partial class names are supported — Campaign matches Marketing::Campaign. Inherited methods are resolved by walking the superclass chain.

Example output

== Campaign#active? ==
  class: Campaign < ApplicationRecord
  mixins: Multitenancy, Archivable
  defined: app/models/campaign.rb:3
  source: app/models/campaign.rb:42
  args:
    at: Time (optional)
  returns: T.any(FalseClass, TrueClass)
  calls:
    Campaign.starts_at() -> T.nilable(Time)
    Campaign.ends_at() -> T.nilable(Time)
    Time.now() -> Time  when: <self>.starts_at() = true
  cfg:
    bb0 -> bb1
    bb1 -[<self>.starts_at()]-> true:bb2 / false:bb3
    bb2 -> return
    bb3 -> return

Ruby gem

The srb_lens Ruby gem provides in-process access to the same analysis engine via a native extension (no subprocess, no JSON serialization overhead).

require "srb_lens"

project = SrbLens::Project.load_or_index("/path/to/project", srb_command: "bundle exec srb")

project.find_methods("Campaign#active?").each do |m|
  puts "#{m.fqn} -> #{m.return_type}"
  m.calls.each { |c| puts "  calls #{c.receiver_type}.#{c.method_name}" }
end

project.find_classes("Campaign").each do |c|
  puts "#{c.fqn} < #{c.super_class}  mixins: #{c.mixins.join(', ')}"
end

See gems/srb_lens/README.md for the full Ruby API reference.

Building the gem

cd gems/srb_lens
bundle install
bundle exec rake compile

How it works

srb-lens runs Sorbet with four --print flags and combines the results:

Sorbet output What srb-lens extracts
--print=cfg-text Control flow graphs: basic blocks, method calls, branch conditions, return types, block usage
--print=symbol-table-json Class hierarchy: superclasses, mixins, method signatures
--print=autogen --stop-after=namer File paths and line numbers for class/module definitions
--print=parse-tree-json-with-locs --stop-after=parser Precise method definition locations

All outputs are cached in .srb-lens/ for fast subsequent loads.

License

MIT