rigor-module-graph 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +118 -0
- data/LICENSE.txt +21 -0
- data/README.md +294 -0
- data/exe/rigor-module-graph +7 -0
- data/lib/rigor/module_graph/analyzer.rb +445 -0
- data/lib/rigor/module_graph/cli.rb +1024 -0
- data/lib/rigor/module_graph/constant_name.rb +86 -0
- data/lib/rigor/module_graph/cycle_detector.rb +154 -0
- data/lib/rigor/module_graph/dot.rb +164 -0
- data/lib/rigor/module_graph/edge.rb +181 -0
- data/lib/rigor/module_graph/html_view.rb +43 -0
- data/lib/rigor/module_graph/inflector.rb +64 -0
- data/lib/rigor/module_graph/mermaid.rb +171 -0
- data/lib/rigor/module_graph/node.rb +146 -0
- data/lib/rigor/module_graph/packwerk_overlay.rb +143 -0
- data/lib/rigor/module_graph/plugin/rigor_plugin.rb +150 -0
- data/lib/rigor/module_graph/plugin.rb +25 -0
- data/lib/rigor/module_graph/reachability.rb +197 -0
- data/lib/rigor/module_graph/stats.rb +119 -0
- data/lib/rigor/module_graph/templates/view.html.erb +50 -0
- data/lib/rigor/module_graph/uml/class_diagram.rb +196 -0
- data/lib/rigor/module_graph/version.rb +10 -0
- data/lib/rigor/module_graph/visibility_map.rb +90 -0
- data/lib/rigor/module_graph/zeitwerk_resolver.rb +116 -0
- data/lib/rigor-module-graph.rb +32 -0
- metadata +111 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 5b9be2e8029422795b2b8b9e8f13998f6aefc16923e11e166f7692910637eb81
|
|
4
|
+
data.tar.gz: a723c08c549cb5368166de5971712f12d1cd5d06c142d176d842b43bd9ec6816
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: e0ea19766f2fa753a0b41fe6dd7eb9b7840b3ce70ca10a8b1b153e2d04633f7cb0a04d1b24f6481104b038ab240e6d26fa56d6a4a990f404e565ce0ddfa21cdf
|
|
7
|
+
data.tar.gz: 82153c88b36de25bad1250d5f33c10b88d4a7c049d17112c750e4cf2596739407429f730048543e9c2bc7d0fe3551d877d6c6ee5275df780dceb4f1415742304
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
Categories:
|
|
9
|
+
|
|
10
|
+
- **Added** — new functionality
|
|
11
|
+
- **Changed** — behaviour of existing functionality
|
|
12
|
+
- **Deprecated** — scheduled for removal
|
|
13
|
+
- **Removed** — gone
|
|
14
|
+
- **Fixed** — bug fixes
|
|
15
|
+
- **Security** — security fixes
|
|
16
|
+
- **Performance** — user-visible performance improvements
|
|
17
|
+
|
|
18
|
+
## [Unreleased]
|
|
19
|
+
|
|
20
|
+
## [0.1.0] — 2026-06-20
|
|
21
|
+
|
|
22
|
+
Initial release. Baseline shipping everything from the Phase 0
|
|
23
|
+
spike through Phase 5 (UML class diagram).
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **Phase 0**: Rigor plugin API spike against `rigortype 0.2.1`
|
|
28
|
+
+ `rbs ~> 4.0`, validating that `node_rule(Prism::ClassNode)`
|
|
29
|
+
and friends work and locking in the `:info` diagnostic output
|
|
30
|
+
channel.
|
|
31
|
+
- **Phase 1 (MVP)**: extraction of `inherits` / `include` /
|
|
32
|
+
`prepend` / `extend` edges. `Rigor::ModuleGraph::Edge` (a
|
|
33
|
+
`Data` subclass) with a JSONL writer, Graphviz DOT and Mermaid
|
|
34
|
+
flowchart renderers, and cycle detection via an iterative
|
|
35
|
+
Tarjan SCC.
|
|
36
|
+
- **Phase 2**: Zeitwerk-style path → constant inference
|
|
37
|
+
(`ZeitwerkResolver`), namespace collapse in both renderers
|
|
38
|
+
(DOT `subgraph cluster_*` and Mermaid `subgraph`), and
|
|
39
|
+
`const_ref` edges from constant references inside method
|
|
40
|
+
bodies (gated on `include_constant_refs`). Confidence promotes
|
|
41
|
+
from `syntax` → `zeitwerk` when the path-inferred name agrees
|
|
42
|
+
with the lexical owner.
|
|
43
|
+
- **Phase 3**: indirect mixin resolution via `scope.type_of`. A
|
|
44
|
+
`Rigor::Type::Singleton` carrier lifts the edge to
|
|
45
|
+
`confidence: "rigor_type"`; everything else degrades to
|
|
46
|
+
`"unresolved"` with the source slice preserved in `raw`. CLI
|
|
47
|
+
gains `--kind` and `--confidence` filters.
|
|
48
|
+
- **Phase 4**: `stats` subcommand reporting per-namespace
|
|
49
|
+
fan-in / fan-out / internal / nodes (text and JSON, with
|
|
50
|
+
`--grouping-depth N` and `--limit N`). Packwerk overlay
|
|
51
|
+
(`--package` / `--package-root PATH`) discovers `package.yml`
|
|
52
|
+
files recursively and uses them as the cluster boundary. The
|
|
53
|
+
Dot / Mermaid renderers accept an explicit `groups:` mapping
|
|
54
|
+
for arbitrary node → cluster assignments.
|
|
55
|
+
- **Phase 5**: UML-style class diagram. `collect` writes a
|
|
56
|
+
sibling `nodes.jsonl` covering class / module declarations,
|
|
57
|
+
method definitions, and `attr_*` attributes — with visibility
|
|
58
|
+
tracked via the `VisibilityMap`'s bare `private` / `protected`
|
|
59
|
+
/ `public` keyword walk. Rails associations land as edges
|
|
60
|
+
(`has_many` / `belongs_to` / `has_one` /
|
|
61
|
+
`has_and_belongs_to_many`, with cardinality and a tiny
|
|
62
|
+
Rails-style inflector that maps `:invoices → Invoice`). A new
|
|
63
|
+
`Uml::ClassDiagram` renderer and `class-diagram` subcommand
|
|
64
|
+
emit Mermaid `classDiagram` syntax; filters `--no-methods`,
|
|
65
|
+
`--no-attributes`, `--public-only`, `--no-private`.
|
|
66
|
+
- **`view` one-shot subcommand**: `rigor-module-graph` with no
|
|
67
|
+
args (or `view` explicitly) analyses the current directory,
|
|
68
|
+
writes a self-contained HTML report under
|
|
69
|
+
`.rigor/module_graph/`, and opens it in a browser. The
|
|
70
|
+
`--output html|mermaid|dot|svg|class-diagram` flag switches
|
|
71
|
+
format; non-html streams to stdout unless `-o PATH` is given.
|
|
72
|
+
- **Reachability filter** (`--from NAMES`, `--depth N`,
|
|
73
|
+
`--direction in/out/both`) shared by every reader subcommand.
|
|
74
|
+
Subsequent `--edge-scope cluster|walk` flag distinguishes
|
|
75
|
+
"show the neighbourhood as a cluster" (default) from "show
|
|
76
|
+
only the edges the BFS actually traversed" (Codex review
|
|
77
|
+
confirmed naming and direction-both semantics).
|
|
78
|
+
- **Billing example** (`examples/billing/`): Customer /
|
|
79
|
+
Invoice / Payment / LineItem + concerns. `build.rb` runs the
|
|
80
|
+
same `view --output` pipeline that ships in the CLI, and
|
|
81
|
+
commits `index.html`, `class-diagram.html`, `graph.svg`, and
|
|
82
|
+
a `preview.png` so the GitHub view of the repo shows the
|
|
83
|
+
rendered output directly.
|
|
84
|
+
- **RDoc** support via `rake rdoc` / `rake rdoc:preview` /
|
|
85
|
+
`rake rdoc:server`.
|
|
86
|
+
- **minitest + minitest-snapshot** test harness. Snapshots
|
|
87
|
+
refresh with `UPDATE_SNAPSHOTS=1 rake test`.
|
|
88
|
+
- **SimpleCov C2 (branch) coverage** measurement via
|
|
89
|
+
`COVERAGE=1 rake test` or `rake coverage`. Baseline is 91.19%
|
|
90
|
+
branch coverage (445 / 488 branches).
|
|
91
|
+
- **lefthook** wiring rubocop / betterleaks / rigor / zizmor on
|
|
92
|
+
pre-commit and minitest on pre-push.
|
|
93
|
+
- **GitHub Actions**: `ci.yml` (test, lint, workflow-lint) and
|
|
94
|
+
`release.yml` (`workflow_dispatch`-only, uses RubyGems trusted
|
|
95
|
+
publishing — no long-lived API token).
|
|
96
|
+
|
|
97
|
+
### Performance
|
|
98
|
+
|
|
99
|
+
- `Stats.compute` rewritten as a single pass with a mutable
|
|
100
|
+
per-namespace counter array instead of a `Data#with` cascade
|
|
101
|
+
per edge. On 2016 edges this took the call from **139 ms to
|
|
102
|
+
47 ms (3.0×)**.
|
|
103
|
+
- `Reachability.walk` swaps `Set` for `Hash<name, true>` +
|
|
104
|
+
`Array` frontier, builds only the adjacency direction it
|
|
105
|
+
actually needs, and the `:both`-direction `walked_edge_indexes`
|
|
106
|
+
shares one pair of indexed adjacencies between its out-walk
|
|
107
|
+
and in-walk. Cluster depth-3 outbound: **27 ms → 14 ms (1.9×)**;
|
|
108
|
+
both direction: **35 ms → 19 ms (1.8×)**.
|
|
109
|
+
- `Edge#dedup_key` is now a generated Data member set once in
|
|
110
|
+
`Edge.build` as a `-`-frozen joined string. Renderer dedup
|
|
111
|
+
Hashes go from `Hash<Array,_>` to `Hash<String,_>`, which
|
|
112
|
+
drove the **1.2×** Dot / Mermaid render gain.
|
|
113
|
+
- YJIT (`--yjit`) adds another ~1.5×. ZJIT measured between
|
|
114
|
+
baseline and YJIT, and trailed baseline on Stats and
|
|
115
|
+
CycleDetector — recommendation stays YJIT.
|
|
116
|
+
|
|
117
|
+
[Unreleased]: https://github.com/nozomemein/rigor-module-graph/compare/v0.1.0...HEAD
|
|
118
|
+
[0.1.0]: https://github.com/nozomemein/rigor-module-graph/releases/tag/v0.1.0
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nozomi Hijikata
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# rigor-module-graph
|
|
2
|
+
|
|
3
|
+
Class/module/constant dependency graph for Ruby projects, built on
|
|
4
|
+
[Rigor](https://rigor.typedduck.fail/). The class-level counterpart
|
|
5
|
+
to Packwerk/Graphwerk: where those look at package boundaries, this
|
|
6
|
+
looks at the Ruby nominal graph — inheritance, `include`/`prepend`/
|
|
7
|
+
`extend`, and (later) constant references.
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
The screenshot above is from `examples/billing/`. Open
|
|
12
|
+
`examples/billing/index.html` for the live Mermaid version.
|
|
13
|
+
|
|
14
|
+
## What this actually does
|
|
15
|
+
|
|
16
|
+
In principle this is a static-analysis tool that turns Ruby source
|
|
17
|
+
into a graph whose **nodes are classes / modules / constants** and
|
|
18
|
+
whose **edges are the references the language itself spells out**.
|
|
19
|
+
|
|
20
|
+
The pipeline:
|
|
21
|
+
|
|
22
|
+
1. Rigor parses Ruby into an AST with Prism.
|
|
23
|
+
2. The plugin's `node_rule`s pick up `ClassNode` / `CallNode` /
|
|
24
|
+
`ConstantReadNode` and friends.
|
|
25
|
+
3. Each interesting node becomes one or more edges:
|
|
26
|
+
- `class A < B` → `A -> B / inherits`
|
|
27
|
+
- `include M` → `A -> M / include`
|
|
28
|
+
- a `Money` constant reference → `A -> Money / const_ref`
|
|
29
|
+
(Phase 2 and later)
|
|
30
|
+
4. `from` is the lexical owner, assembled by walking
|
|
31
|
+
`context.ancestors` — so `class Billing::Invoice` produces
|
|
32
|
+
`Billing::Invoice`, not just `Invoice`.
|
|
33
|
+
5. `to` is resolved through a confidence ladder: syntax →
|
|
34
|
+
Zeitwerk convention → Rigor type information. Whatever we
|
|
35
|
+
couldn't pin down stays visible in the `confidence` field
|
|
36
|
+
rather than being dropped.
|
|
37
|
+
6. Every edge ships as a Rigor `:info` diagnostic. The `collect`
|
|
38
|
+
subcommand filters them on `rule == "edge"` and writes JSONL.
|
|
39
|
+
7. DOT, SVG, Mermaid, and cycle detection are all derived from
|
|
40
|
+
that JSONL.
|
|
41
|
+
|
|
42
|
+
So we are not watching what Ruby *does at runtime*. We're reading
|
|
43
|
+
Ruby's *named structure* and reconstructing, approximately, "which
|
|
44
|
+
constants depend on which other constants".
|
|
45
|
+
|
|
46
|
+
### This is not a call graph
|
|
47
|
+
|
|
48
|
+
We do not track who `foo.bar` resolves to at runtime. We track
|
|
49
|
+
the fact that the `Billing::Invoice` name depends on the
|
|
50
|
+
`ApplicationRecord` / `Auditable` / `Money` names. That is a
|
|
51
|
+
**nominal dependency graph** — a compiler-front-end-style view
|
|
52
|
+
of the project's syntactic and lexical structure, projected into
|
|
53
|
+
edges with explicit confidence.
|
|
54
|
+
|
|
55
|
+
Not re-implementing Ruby constant lookup is deliberate. For
|
|
56
|
+
understanding a Rails codebase's shape, it's more useful to leave
|
|
57
|
+
each edge tagged `syntax` / `zeitwerk` / `rigor_type` /
|
|
58
|
+
`unresolved` than to fake a `resolved` answer and silently get it
|
|
59
|
+
wrong.
|
|
60
|
+
|
|
61
|
+
## Installation
|
|
62
|
+
|
|
63
|
+
Via Bundler:
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
# Gemfile
|
|
67
|
+
gem "rigor-module-graph"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
bundle install
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Or globally:
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
gem install rigor-module-graph
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Both paths pull in `rigortype` and `rbs ~> 4.0` transitively. The
|
|
81
|
+
`rbs ~> 4.0` constraint is the key one: rigortype 0.2.x calls
|
|
82
|
+
`RBS::Environment::ClassEntry#each_decl`, which only exists in
|
|
83
|
+
rbs 4.x. The Ruby 4.0 stdlib bundles rbs 3.10 as a default gem,
|
|
84
|
+
so installing `rigor-module-graph` (which depends on rbs 4.x)
|
|
85
|
+
makes RubyGems activate the 4.x gem at run time and the
|
|
86
|
+
analyzer stays alive.
|
|
87
|
+
|
|
88
|
+
## Configuration
|
|
89
|
+
|
|
90
|
+
Add the plugin to your project's `.rigor.yml`:
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
target_ruby: '4.0'
|
|
94
|
+
paths:
|
|
95
|
+
- app
|
|
96
|
+
- lib
|
|
97
|
+
plugins:
|
|
98
|
+
- gem: rigor-module-graph
|
|
99
|
+
config:
|
|
100
|
+
rails_zeitwerk: true
|
|
101
|
+
autoload_paths:
|
|
102
|
+
- app/models
|
|
103
|
+
- app/controllers
|
|
104
|
+
- app/services
|
|
105
|
+
- app/jobs
|
|
106
|
+
- lib
|
|
107
|
+
concern_dirs:
|
|
108
|
+
- app/models/concerns
|
|
109
|
+
- app/controllers/concerns
|
|
110
|
+
include_constant_refs: false
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Every key shown is the default. Set `include_constant_refs: true`
|
|
114
|
+
to emit `const_ref` edges from constant references inside method
|
|
115
|
+
bodies. Set `rails_zeitwerk: false` to keep every edge at
|
|
116
|
+
`confidence: "syntax"` and skip path-based owner inference.
|
|
117
|
+
|
|
118
|
+
## Usage
|
|
119
|
+
|
|
120
|
+
### One-shot: `view`
|
|
121
|
+
|
|
122
|
+
The default subcommand analyses the current directory, writes a
|
|
123
|
+
self-contained Mermaid HTML report under `.rigor/module_graph/`,
|
|
124
|
+
and opens it in your browser. No flags needed for a Rails-shaped
|
|
125
|
+
project.
|
|
126
|
+
|
|
127
|
+
```sh
|
|
128
|
+
cd path/to/your/project
|
|
129
|
+
bundle exec rigor-module-graph # same as: rigor-module-graph view
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Useful flags:
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
# Don't open the browser (just write the HTML)
|
|
136
|
+
rigor-module-graph view --no-open
|
|
137
|
+
|
|
138
|
+
# Pick a different output format — html (default) opens a viewer
|
|
139
|
+
# in the browser; everything else streams to stdout unless `-o`
|
|
140
|
+
# is given.
|
|
141
|
+
rigor-module-graph view --no-open --output mermaid > graph.mmd
|
|
142
|
+
rigor-module-graph view --no-open --output dot > graph.dot
|
|
143
|
+
rigor-module-graph view --no-open --output svg > graph.svg
|
|
144
|
+
rigor-module-graph view --no-open --output class-diagram > class.mmd
|
|
145
|
+
rigor-module-graph view --output svg -o graph.svg
|
|
146
|
+
|
|
147
|
+
# Focus on what's around one or a few constants (Mermaid can't
|
|
148
|
+
# render 1000+-node graphs cleanly — this is the escape hatch)
|
|
149
|
+
rigor-module-graph view --from Article --depth 5
|
|
150
|
+
rigor-module-graph view --from Article --depth 5 --direction out
|
|
151
|
+
rigor-module-graph view --from Billing::Invoice,Billing::Payment --depth 2
|
|
152
|
+
|
|
153
|
+
# Pick your own collapse list (default: auto-detect top-level
|
|
154
|
+
# namespaces with ≥ 3 members)
|
|
155
|
+
rigor-module-graph view --collapse Billing,Auth
|
|
156
|
+
rigor-module-graph view --no-collapse
|
|
157
|
+
|
|
158
|
+
# Same kind / confidence filters as the lower-level commands
|
|
159
|
+
rigor-module-graph view --kind inherits,include
|
|
160
|
+
rigor-module-graph view --confidence syntax,zeitwerk
|
|
161
|
+
|
|
162
|
+
# Cluster by Packwerk packages (auto-detects package.yml under cwd)
|
|
163
|
+
rigor-module-graph view --package
|
|
164
|
+
rigor-module-graph view --package-root /path/to/repo
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`--direction` controls how the +--from+ walk follows edges:
|
|
168
|
+
|
|
169
|
+
| direction | meaning |
|
|
170
|
+
|-----------|----------------------------------------|
|
|
171
|
+
| `out` | only "what does Article depend on" |
|
|
172
|
+
| `in` | only "what depends on Article" |
|
|
173
|
+
| `both` | both (default) |
|
|
174
|
+
|
|
175
|
+
`--edge-scope` controls which edges survive once the BFS finishes:
|
|
176
|
+
|
|
177
|
+
| edge-scope | meaning |
|
|
178
|
+
|------------|------------------------------------------------------------|
|
|
179
|
+
| `cluster` | keep every edge whose endpoints both fall in the reachable set (default — good for "show me the Article neighbourhood as a cluster") |
|
|
180
|
+
| `walk` | keep only the edges the BFS actually traversed (good for "show me what depends on Article and nothing else"; drops sibling edges like `Foo inherits ApplicationRecord` that just happen to share a base class with reachable nodes) |
|
|
181
|
+
|
|
182
|
+
A 1-hop `--from Article --direction out --edge-scope walk` returns
|
|
183
|
+
exactly the edges whose `from` is `Article`, never the sibling
|
|
184
|
+
`inherits ApplicationRecord` of a reached node.
|
|
185
|
+
|
|
186
|
+
### Lower-level pipeline
|
|
187
|
+
|
|
188
|
+
The pipeline `view` runs is also exposed as discrete subcommands
|
|
189
|
+
when you want JSONL on disk or a pipeable text output:
|
|
190
|
+
|
|
191
|
+
```sh
|
|
192
|
+
# Run `rigor check` and write edges JSONL (default: .rigor/module_graph/edges.jsonl)
|
|
193
|
+
bundle exec rigor-module-graph collect
|
|
194
|
+
|
|
195
|
+
# Render the graph
|
|
196
|
+
bundle exec rigor-module-graph dot .rigor/module_graph/edges.jsonl > graph.dot
|
|
197
|
+
bundle exec rigor-module-graph mermaid .rigor/module_graph/edges.jsonl > graph.mmd
|
|
198
|
+
dot -Tsvg graph.dot -o graph.svg
|
|
199
|
+
|
|
200
|
+
# Detect cycles (exit 1 if any are found)
|
|
201
|
+
bundle exec rigor-module-graph cycles .rigor/module_graph/edges.jsonl
|
|
202
|
+
|
|
203
|
+
# Per-namespace fan-in / fan-out report
|
|
204
|
+
bundle exec rigor-module-graph stats .rigor/module_graph/edges.jsonl
|
|
205
|
+
bundle exec rigor-module-graph stats --format json --limit 10 edges.jsonl
|
|
206
|
+
|
|
207
|
+
# UML class diagram (Mermaid classDiagram). Reads edges + the
|
|
208
|
+
# sibling nodes.jsonl that `collect` writes.
|
|
209
|
+
bundle exec rigor-module-graph class-diagram .rigor/module_graph/edges.jsonl > class.mmd
|
|
210
|
+
bundle exec rigor-module-graph class-diagram --no-private --no-attributes edges.jsonl
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`collect` shells out to `rigor check --format json --no-cache` and
|
|
214
|
+
filters diagnostics on `source_family == "plugin.module-graph"` +
|
|
215
|
+
`rule == "edge"`, so re-running is deterministic and there's no
|
|
216
|
+
on-disk side-effect from the plugin itself.
|
|
217
|
+
|
|
218
|
+
`dot` / `mermaid` / `cycles` accept a file argument or read stdin.
|
|
219
|
+
|
|
220
|
+
### Filters and collapse
|
|
221
|
+
|
|
222
|
+
All three reader subcommands accept the same filter flags. They
|
|
223
|
+
prune the edge set before rendering / detecting; the JSONL on
|
|
224
|
+
disk is untouched.
|
|
225
|
+
|
|
226
|
+
```sh
|
|
227
|
+
# Drop noisy const_ref / unresolved edges
|
|
228
|
+
bundle exec rigor-module-graph dot --kind inherits,include,prepend,extend edges.jsonl
|
|
229
|
+
|
|
230
|
+
# Only the edges we're sure about
|
|
231
|
+
bundle exec rigor-module-graph dot --confidence syntax,zeitwerk,rigor_type edges.jsonl
|
|
232
|
+
|
|
233
|
+
# Fold every Billing::* node into one cluster (Dot subgraph_cluster_; Mermaid subgraph)
|
|
234
|
+
bundle exec rigor-module-graph dot --collapse Billing,Auth edges.jsonl
|
|
235
|
+
bundle exec rigor-module-graph mermaid --collapse Billing edges.jsonl
|
|
236
|
+
|
|
237
|
+
# Restrict the graph to the neighbourhood of one or a few
|
|
238
|
+
# constants (works on dot / mermaid / cycles too)
|
|
239
|
+
bundle exec rigor-module-graph dot --from Article --depth 5 edges.jsonl
|
|
240
|
+
bundle exec rigor-module-graph mermaid --from Article --depth 5 --direction out edges.jsonl
|
|
241
|
+
|
|
242
|
+
# Cluster by Packwerk packages instead of by namespace
|
|
243
|
+
bundle exec rigor-module-graph dot --package edges.jsonl # cwd
|
|
244
|
+
bundle exec rigor-module-graph mermaid --package-root /path/to/repo edges.jsonl
|
|
245
|
+
|
|
246
|
+
# Cycles that stay within structural edges only
|
|
247
|
+
bundle exec rigor-module-graph cycles --kind inherits,include edges.jsonl
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Edge format
|
|
251
|
+
|
|
252
|
+
Each edge in the JSONL file looks like:
|
|
253
|
+
|
|
254
|
+
```json
|
|
255
|
+
{"from":"Billing::Invoice","to":"ApplicationRecord","kind":"inherits","path":"app/models/billing/invoice.rb","line":2,"column":3,"confidence":"syntax"}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
- `kind`: `inherits` / `include` / `prepend` / `extend` /
|
|
259
|
+
`const_ref` (the last one is reserved for Phase 2).
|
|
260
|
+
- `confidence`: `syntax` / `zeitwerk` / `rigor_type` /
|
|
261
|
+
`unresolved`. MVP only emits `syntax`.
|
|
262
|
+
|
|
263
|
+
The renderers dedup by `(from, to, kind, confidence)` so two
|
|
264
|
+
`include Foo` on the same class across files collapse to one edge.
|
|
265
|
+
|
|
266
|
+
## Compatibility
|
|
267
|
+
|
|
268
|
+
- Ruby `>= 4.0.0, < 4.1`
|
|
269
|
+
- rigortype `~> 0.2.1`
|
|
270
|
+
- rbs `~> 4.0`
|
|
271
|
+
|
|
272
|
+
## Documentation
|
|
273
|
+
|
|
274
|
+
The public RDoc API is generated locally via `rake rdoc`,
|
|
275
|
+
served on `http://localhost:8808` via `rake rdoc:server`, and
|
|
276
|
+
published to GitHub Pages on every push to `main`.
|
|
277
|
+
|
|
278
|
+
- [API reference (GitHub Pages)](https://nozomemein.github.io/rigor-module-graph/) —
|
|
279
|
+
built from `main`, mirrors the current source.
|
|
280
|
+
- [API reference (RubyGems)](https://rubydoc.info/gems/rigor-module-graph) —
|
|
281
|
+
the last released gem on rubydoc.info.
|
|
282
|
+
- [Development guide](docs/development.md) — local setup, git
|
|
283
|
+
hooks, CI / Release workflows, test suite layout.
|
|
284
|
+
- [Design plan](docs/plan.md) — the decisions still
|
|
285
|
+
load-bearing for the code (edge model, confidence ladder,
|
|
286
|
+
output channel, owner resolution, architecture map).
|
|
287
|
+
- [Known limitations](docs/limitation.md) — rough edges shipped
|
|
288
|
+
with the current release (visibility tracker gaps, the
|
|
289
|
+
bundled inflector, Mermaid 10.x quirks).
|
|
290
|
+
- [Changelog](CHANGELOG.md) — per-version changes, formatted
|
|
291
|
+
per [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
292
|
+
with [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
293
|
+
The release workflow gates on a `## [VERSION]` entry being
|
|
294
|
+
present before pushing to RubyGems.
|