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
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rigor
|
|
4
|
+
module ModuleGraph
|
|
5
|
+
# Converts a Ruby source path into the fully-qualified constant
|
|
6
|
+
# the Zeitwerk convention says it should define.
|
|
7
|
+
#
|
|
8
|
+
# Pure function, no I/O. The plugin instantiates one per run from
|
|
9
|
+
# `.rigor.yml` config and asks for `resolve(path)` per file. Two
|
|
10
|
+
# configuration knobs:
|
|
11
|
+
#
|
|
12
|
+
# - `autoload_paths`: roots stripped from the path before
|
|
13
|
+
# camelising. Defaults to the standard Rails layout.
|
|
14
|
+
# - `concern_dirs`: directories that act as transparent
|
|
15
|
+
# namespaces under Zeitwerk (`app/models/concerns/auditable.rb`
|
|
16
|
+
# resolves to `Auditable`, not `Concerns::Auditable`).
|
|
17
|
+
#
|
|
18
|
+
# The resolver is order-sensitive: longer / more specific roots
|
|
19
|
+
# MUST be tried before their parents so `app/models/concerns/foo.rb`
|
|
20
|
+
# picks up the concern root, not `app/models`. We sort by length
|
|
21
|
+
# descending at construction time, so config order does not matter.
|
|
22
|
+
class ZeitwerkResolver
|
|
23
|
+
DEFAULT_AUTOLOAD_PATHS = %w[
|
|
24
|
+
app/models
|
|
25
|
+
app/controllers
|
|
26
|
+
app/services
|
|
27
|
+
app/jobs
|
|
28
|
+
app/mailers
|
|
29
|
+
app/helpers
|
|
30
|
+
app/channels
|
|
31
|
+
app/workers
|
|
32
|
+
lib
|
|
33
|
+
].freeze
|
|
34
|
+
|
|
35
|
+
DEFAULT_CONCERN_DIRS = %w[
|
|
36
|
+
app/models/concerns
|
|
37
|
+
app/controllers/concerns
|
|
38
|
+
].freeze
|
|
39
|
+
|
|
40
|
+
attr_reader :autoload_paths, :concern_dirs
|
|
41
|
+
|
|
42
|
+
def initialize(autoload_paths: DEFAULT_AUTOLOAD_PATHS,
|
|
43
|
+
concern_dirs: DEFAULT_CONCERN_DIRS,
|
|
44
|
+
project_root: nil)
|
|
45
|
+
@project_root = project_root && File.expand_path(project_root)
|
|
46
|
+
@autoload_paths = normalise_roots(autoload_paths)
|
|
47
|
+
@concern_dirs = normalise_roots(concern_dirs)
|
|
48
|
+
@sorted_roots = (@concern_dirs + @autoload_paths).sort_by { |r| -r.length }.uniq
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @param path [String] either relative to the project root or
|
|
52
|
+
# absolute. Both `app/models/billing/invoice.rb` and the
|
|
53
|
+
# `realpath` form work.
|
|
54
|
+
# @return [String, nil] the inferred constant name, or nil when
|
|
55
|
+
# the path is not under any configured root or has no .rb
|
|
56
|
+
# extension.
|
|
57
|
+
def resolve(path)
|
|
58
|
+
return nil unless path
|
|
59
|
+
|
|
60
|
+
rel = relativise(path)
|
|
61
|
+
return nil unless rel
|
|
62
|
+
return nil unless rel.end_with?(".rb")
|
|
63
|
+
|
|
64
|
+
root = @sorted_roots.find { |r| rel.start_with?(r + "/") }
|
|
65
|
+
return nil unless root
|
|
66
|
+
|
|
67
|
+
suffix = rel[(root.length + 1)..]
|
|
68
|
+
camelise_path(suffix.delete_suffix(".rb"))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# True when `inferred` matches the (probably syntax-derived)
|
|
72
|
+
# `actual` constant under Zeitwerk's conventions. We compare
|
|
73
|
+
# ignoring leading "::" since absolute / relative are not a
|
|
74
|
+
# meaningful distinction here.
|
|
75
|
+
def matches?(actual, inferred)
|
|
76
|
+
return false if actual.nil? || inferred.nil?
|
|
77
|
+
|
|
78
|
+
strip_leading(actual) == strip_leading(inferred)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def relativise(path)
|
|
82
|
+
absolute = File.expand_path(path)
|
|
83
|
+
if @project_root && absolute.start_with?(@project_root + "/")
|
|
84
|
+
absolute[(@project_root.length + 1)..]
|
|
85
|
+
elsif path.start_with?("/")
|
|
86
|
+
# Absolute path with no project root configured: try every
|
|
87
|
+
# autoload root as a suffix match. Used by integration runs
|
|
88
|
+
# where files live in a tmpdir.
|
|
89
|
+
suffix = @sorted_roots.find { |r| absolute.include?("/" + r + "/") }
|
|
90
|
+
if suffix
|
|
91
|
+
idx = absolute.rindex("/" + suffix + "/")
|
|
92
|
+
absolute[(idx + 1)..]
|
|
93
|
+
end
|
|
94
|
+
else
|
|
95
|
+
path
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def normalise_roots(roots)
|
|
100
|
+
Array(roots).map { |r| r.to_s.sub(%r{/+\z}, "") }.reject(&:empty?).freeze
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def strip_leading(name)
|
|
104
|
+
name.sub(/\A::/, "")
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def camelise_path(rel_no_ext)
|
|
108
|
+
rel_no_ext.split("/").map { |seg| camelise_segment(seg) }.join("::")
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def camelise_segment(segment)
|
|
112
|
+
segment.split("_").map(&:capitalize).join
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# == rigor-module-graph
|
|
4
|
+
#
|
|
5
|
+
# Class/module/constant dependency graph for Ruby projects, built
|
|
6
|
+
# on Rigor[https://rigor.typedduck.fail/]. Loading this file pulls
|
|
7
|
+
# in every public piece of the gem: the Edge value type, the
|
|
8
|
+
# Analyzer, the renderers (Dot/Mermaid/CycleDetector), and — when
|
|
9
|
+
# +Rigor::Plugin::Base+ is already defined — the Rigor plugin that
|
|
10
|
+
# wires the node rules.
|
|
11
|
+
#
|
|
12
|
+
# Most users interact with this gem through the +rigor-module-graph+
|
|
13
|
+
# command-line wrapper (see Rigor::ModuleGraph::CLI), not by
|
|
14
|
+
# requiring it directly.
|
|
15
|
+
|
|
16
|
+
require_relative "rigor/module_graph/version"
|
|
17
|
+
require_relative "rigor/module_graph/edge"
|
|
18
|
+
require_relative "rigor/module_graph/node"
|
|
19
|
+
require_relative "rigor/module_graph/constant_name"
|
|
20
|
+
require_relative "rigor/module_graph/zeitwerk_resolver"
|
|
21
|
+
require_relative "rigor/module_graph/inflector"
|
|
22
|
+
require_relative "rigor/module_graph/visibility_map"
|
|
23
|
+
require_relative "rigor/module_graph/analyzer"
|
|
24
|
+
require_relative "rigor/module_graph/dot"
|
|
25
|
+
require_relative "rigor/module_graph/mermaid"
|
|
26
|
+
require_relative "rigor/module_graph/cycle_detector"
|
|
27
|
+
require_relative "rigor/module_graph/reachability"
|
|
28
|
+
require_relative "rigor/module_graph/stats"
|
|
29
|
+
require_relative "rigor/module_graph/packwerk_overlay"
|
|
30
|
+
require_relative "rigor/module_graph/uml/class_diagram"
|
|
31
|
+
require_relative "rigor/module_graph/html_view"
|
|
32
|
+
require_relative "rigor/module_graph/plugin"
|
metadata
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rigor-module-graph
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Nozomi Hijikata
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rigortype
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.2.1
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.2.1
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rbs
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '4.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '4.0'
|
|
40
|
+
description: |
|
|
41
|
+
Rigor plugin and CLI that extract Ruby class/module/constant dependencies
|
|
42
|
+
(inheritance, include/prepend/extend, constant references) and emit
|
|
43
|
+
Graphviz DOT, SVG, and Mermaid output. The class/module-level
|
|
44
|
+
counterpart to Packwerk/Graphwerk.
|
|
45
|
+
email:
|
|
46
|
+
- b8yukifsukeo999n@gmail.com
|
|
47
|
+
executables:
|
|
48
|
+
- rigor-module-graph
|
|
49
|
+
extensions: []
|
|
50
|
+
extra_rdoc_files:
|
|
51
|
+
- README.md
|
|
52
|
+
files:
|
|
53
|
+
- CHANGELOG.md
|
|
54
|
+
- LICENSE.txt
|
|
55
|
+
- README.md
|
|
56
|
+
- exe/rigor-module-graph
|
|
57
|
+
- lib/rigor-module-graph.rb
|
|
58
|
+
- lib/rigor/module_graph/analyzer.rb
|
|
59
|
+
- lib/rigor/module_graph/cli.rb
|
|
60
|
+
- lib/rigor/module_graph/constant_name.rb
|
|
61
|
+
- lib/rigor/module_graph/cycle_detector.rb
|
|
62
|
+
- lib/rigor/module_graph/dot.rb
|
|
63
|
+
- lib/rigor/module_graph/edge.rb
|
|
64
|
+
- lib/rigor/module_graph/html_view.rb
|
|
65
|
+
- lib/rigor/module_graph/inflector.rb
|
|
66
|
+
- lib/rigor/module_graph/mermaid.rb
|
|
67
|
+
- lib/rigor/module_graph/node.rb
|
|
68
|
+
- lib/rigor/module_graph/packwerk_overlay.rb
|
|
69
|
+
- lib/rigor/module_graph/plugin.rb
|
|
70
|
+
- lib/rigor/module_graph/plugin/rigor_plugin.rb
|
|
71
|
+
- lib/rigor/module_graph/reachability.rb
|
|
72
|
+
- lib/rigor/module_graph/stats.rb
|
|
73
|
+
- lib/rigor/module_graph/templates/view.html.erb
|
|
74
|
+
- lib/rigor/module_graph/uml/class_diagram.rb
|
|
75
|
+
- lib/rigor/module_graph/version.rb
|
|
76
|
+
- lib/rigor/module_graph/visibility_map.rb
|
|
77
|
+
- lib/rigor/module_graph/zeitwerk_resolver.rb
|
|
78
|
+
licenses:
|
|
79
|
+
- MIT
|
|
80
|
+
metadata:
|
|
81
|
+
documentation_uri: https://rubydoc.info/gems/rigor-module-graph
|
|
82
|
+
changelog_uri: https://github.com/nozomemein/rigor-module-graph/blob/main/CHANGELOG.md
|
|
83
|
+
source_code_uri: https://github.com/nozomemein/rigor-module-graph
|
|
84
|
+
bug_tracker_uri: https://github.com/nozomemein/rigor-module-graph/issues
|
|
85
|
+
homepage_uri: https://github.com/nozomemein/rigor-module-graph
|
|
86
|
+
rubygems_mfa_required: 'true'
|
|
87
|
+
rdoc_options:
|
|
88
|
+
- "--main"
|
|
89
|
+
- README.md
|
|
90
|
+
- "--markup"
|
|
91
|
+
- rdoc
|
|
92
|
+
require_paths:
|
|
93
|
+
- lib
|
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
95
|
+
requirements:
|
|
96
|
+
- - ">="
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
version: 4.0.0
|
|
99
|
+
- - "<"
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: '4.1'
|
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
|
+
requirements:
|
|
104
|
+
- - ">="
|
|
105
|
+
- !ruby/object:Gem::Version
|
|
106
|
+
version: '0'
|
|
107
|
+
requirements: []
|
|
108
|
+
rubygems_version: 4.0.3
|
|
109
|
+
specification_version: 4
|
|
110
|
+
summary: Class/module/constant dependency graph for Ruby projects, built on Rigor.
|
|
111
|
+
test_files: []
|