spoom 1.1.16 → 1.2.1
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 +4 -4
- data/lib/spoom/cli/bump.rb +6 -15
- data/lib/spoom/cli/config.rb +4 -3
- data/lib/spoom/cli/coverage.rb +14 -15
- data/lib/spoom/cli/helper.rb +11 -21
- data/lib/spoom/cli/lsp.rb +4 -2
- data/lib/spoom/cli/run.rb +3 -7
- data/lib/spoom/cli.rb +6 -13
- data/lib/spoom/context/bundle.rb +58 -0
- data/lib/spoom/context/exec.rb +50 -0
- data/lib/spoom/context/file_system.rb +93 -0
- data/lib/spoom/context/git.rb +121 -0
- data/lib/spoom/context/sorbet.rb +154 -0
- data/lib/spoom/context.rb +16 -200
- data/lib/spoom/coverage/d3/circle_map.rb +22 -36
- data/lib/spoom/coverage/report.rb +45 -32
- data/lib/spoom/coverage.rb +27 -42
- data/lib/spoom/file_collector.rb +79 -0
- data/lib/spoom/file_tree.rb +168 -83
- data/lib/spoom/sorbet/config.rb +3 -1
- data/lib/spoom/sorbet/sigils.rb +0 -15
- data/lib/spoom/sorbet.rb +0 -119
- data/lib/spoom/timeline.rb +5 -8
- data/lib/spoom/version.rb +1 -1
- data/lib/spoom.rb +1 -52
- metadata +9 -4
- data/lib/spoom/git.rb +0 -109
@@ -0,0 +1,154 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Spoom
|
5
|
+
class Context
|
6
|
+
# Sorbet features for a context
|
7
|
+
module Sorbet
|
8
|
+
extend T::Sig
|
9
|
+
extend T::Helpers
|
10
|
+
|
11
|
+
requires_ancestor { Context }
|
12
|
+
|
13
|
+
# Run `bundle exec srb` in this context directory
|
14
|
+
sig { params(arg: String, sorbet_bin: T.nilable(String), capture_err: T::Boolean).returns(ExecResult) }
|
15
|
+
def srb(*arg, sorbet_bin: nil, capture_err: true)
|
16
|
+
res = if sorbet_bin
|
17
|
+
exec("#{sorbet_bin} #{arg.join(" ")}", capture_err: capture_err)
|
18
|
+
else
|
19
|
+
bundle_exec("srb #{arg.join(" ")}", capture_err: capture_err)
|
20
|
+
end
|
21
|
+
|
22
|
+
case res.exit_code
|
23
|
+
when Spoom::Sorbet::KILLED_CODE
|
24
|
+
raise Spoom::Sorbet::Error::Killed.new("Sorbet was killed.", res)
|
25
|
+
when Spoom::Sorbet::SEGFAULT_CODE
|
26
|
+
raise Spoom::Sorbet::Error::Segfault.new("Sorbet segfaulted.", res)
|
27
|
+
end
|
28
|
+
|
29
|
+
res
|
30
|
+
end
|
31
|
+
|
32
|
+
sig { params(arg: String, sorbet_bin: T.nilable(String), capture_err: T::Boolean).returns(ExecResult) }
|
33
|
+
def srb_tc(*arg, sorbet_bin: nil, capture_err: true)
|
34
|
+
arg.prepend("tc") unless sorbet_bin
|
35
|
+
T.unsafe(self).srb(*arg, sorbet_bin: sorbet_bin, capture_err: capture_err)
|
36
|
+
end
|
37
|
+
|
38
|
+
sig do
|
39
|
+
params(
|
40
|
+
arg: String,
|
41
|
+
sorbet_bin: T.nilable(String),
|
42
|
+
capture_err: T::Boolean,
|
43
|
+
).returns(T.nilable(T::Hash[String, Integer]))
|
44
|
+
end
|
45
|
+
def srb_metrics(*arg, sorbet_bin: nil, capture_err: true)
|
46
|
+
metrics_file = "metrics.tmp"
|
47
|
+
|
48
|
+
T.unsafe(self).srb_tc(
|
49
|
+
"--metrics-file",
|
50
|
+
metrics_file,
|
51
|
+
*arg,
|
52
|
+
sorbet_bin: sorbet_bin,
|
53
|
+
capture_err: capture_err,
|
54
|
+
)
|
55
|
+
return nil unless file?(metrics_file)
|
56
|
+
|
57
|
+
metrics_path = absolute_path_to(metrics_file)
|
58
|
+
metrics = Spoom::Sorbet::MetricsParser.parse_file(metrics_path)
|
59
|
+
remove!(metrics_file)
|
60
|
+
metrics
|
61
|
+
end
|
62
|
+
|
63
|
+
# List all files typechecked by Sorbet from its `config`
|
64
|
+
sig { params(with_config: T.nilable(Spoom::Sorbet::Config), include_rbis: T::Boolean).returns(T::Array[String]) }
|
65
|
+
def srb_files(with_config: nil, include_rbis: true)
|
66
|
+
config = with_config || sorbet_config
|
67
|
+
|
68
|
+
allowed_extensions = config.allowed_extensions
|
69
|
+
allowed_extensions = Spoom::Sorbet::Config::DEFAULT_ALLOWED_EXTENSIONS if allowed_extensions.empty?
|
70
|
+
allowed_extensions -= [".rbi"] unless include_rbis
|
71
|
+
|
72
|
+
excluded_patterns = config.ignore.map { |string| File.join("**", string, "**") }
|
73
|
+
|
74
|
+
collector = FileCollector.new(allow_extensions: allowed_extensions, exclude_patterns: excluded_patterns)
|
75
|
+
collector.visit_paths(config.paths.map { |path| absolute_path_to(path) })
|
76
|
+
collector.files.map { |file| file.delete_prefix("#{absolute_path}/") }.sort
|
77
|
+
end
|
78
|
+
|
79
|
+
# List all files typechecked by Sorbet from its `config` that matches `strictness`
|
80
|
+
sig do
|
81
|
+
params(
|
82
|
+
strictness: String,
|
83
|
+
with_config: T.nilable(Spoom::Sorbet::Config),
|
84
|
+
include_rbis: T::Boolean,
|
85
|
+
).returns(T::Array[String])
|
86
|
+
end
|
87
|
+
def srb_files_with_strictness(strictness, with_config: nil, include_rbis: true)
|
88
|
+
srb_files(with_config: with_config, include_rbis: include_rbis)
|
89
|
+
.select { |file| read_file_strictness(file) == strictness }
|
90
|
+
end
|
91
|
+
|
92
|
+
sig { params(arg: String, sorbet_bin: T.nilable(String), capture_err: T::Boolean).returns(T.nilable(String)) }
|
93
|
+
def srb_version(*arg, sorbet_bin: nil, capture_err: true)
|
94
|
+
res = T.unsafe(self).srb_tc("--no-config", "--version", *arg, sorbet_bin: sorbet_bin, capture_err: capture_err)
|
95
|
+
return nil unless res.status
|
96
|
+
|
97
|
+
res.out.split(" ")[2]
|
98
|
+
end
|
99
|
+
|
100
|
+
# Does this context has a `sorbet/config` file?
|
101
|
+
sig { returns(T::Boolean) }
|
102
|
+
def has_sorbet_config?
|
103
|
+
file?(Spoom::Sorbet::CONFIG_PATH)
|
104
|
+
end
|
105
|
+
|
106
|
+
sig { returns(Spoom::Sorbet::Config) }
|
107
|
+
def sorbet_config
|
108
|
+
Spoom::Sorbet::Config.parse_string(read_sorbet_config)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Read the contents of `sorbet/config` in this context directory
|
112
|
+
sig { returns(String) }
|
113
|
+
def read_sorbet_config
|
114
|
+
read(Spoom::Sorbet::CONFIG_PATH)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Set the `contents` of `sorbet/config` in this context directory
|
118
|
+
sig { params(contents: String, append: T::Boolean).void }
|
119
|
+
def write_sorbet_config!(contents, append: false)
|
120
|
+
write!(Spoom::Sorbet::CONFIG_PATH, contents, append: append)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Read the strictness sigil from the file at `relative_path` (returns `nil` if no sigil)
|
124
|
+
sig { params(relative_path: String).returns(T.nilable(String)) }
|
125
|
+
def read_file_strictness(relative_path)
|
126
|
+
Spoom::Sorbet::Sigils.file_strictness(absolute_path_to(relative_path))
|
127
|
+
end
|
128
|
+
|
129
|
+
# Get the commit introducing the `sorbet/config` file
|
130
|
+
sig { returns(T.nilable(Spoom::Git::Commit)) }
|
131
|
+
def sorbet_intro_commit
|
132
|
+
res = git_log("--diff-filter=A --format='%h %at' -1 -- sorbet/config")
|
133
|
+
return nil unless res.status
|
134
|
+
|
135
|
+
out = res.out.strip
|
136
|
+
return nil if out.empty?
|
137
|
+
|
138
|
+
Spoom::Git::Commit.parse_line(out)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Get the commit removing the `sorbet/config` file
|
142
|
+
sig { returns(T.nilable(Spoom::Git::Commit)) }
|
143
|
+
def sorbet_removal_commit
|
144
|
+
res = git_log("--diff-filter=D --format='%h %at' -1 -- sorbet/config")
|
145
|
+
return nil unless res.status
|
146
|
+
|
147
|
+
out = res.out.strip
|
148
|
+
return nil if out.empty?
|
149
|
+
|
150
|
+
Spoom::Git::Commit.parse_line(out)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/lib/spoom/context.rb
CHANGED
@@ -3,8 +3,15 @@
|
|
3
3
|
|
4
4
|
require "fileutils"
|
5
5
|
require "open3"
|
6
|
+
require "time"
|
6
7
|
require "tmpdir"
|
7
8
|
|
9
|
+
require_relative "context/bundle"
|
10
|
+
require_relative "context/exec"
|
11
|
+
require_relative "context/file_system"
|
12
|
+
require_relative "context/git"
|
13
|
+
require_relative "context/sorbet"
|
14
|
+
|
8
15
|
module Spoom
|
9
16
|
# An abstraction to a Ruby project context
|
10
17
|
#
|
@@ -13,9 +20,11 @@ module Spoom
|
|
13
20
|
class Context
|
14
21
|
extend T::Sig
|
15
22
|
|
16
|
-
|
17
|
-
|
18
|
-
|
23
|
+
include Bundle
|
24
|
+
include Exec
|
25
|
+
include FileSystem
|
26
|
+
include Git
|
27
|
+
include Sorbet
|
19
28
|
|
20
29
|
class << self
|
21
30
|
extend T::Sig
|
@@ -30,6 +39,10 @@ module Spoom
|
|
30
39
|
end
|
31
40
|
end
|
32
41
|
|
42
|
+
# The absolute path to the directory this context is about
|
43
|
+
sig { returns(String) }
|
44
|
+
attr_reader :absolute_path
|
45
|
+
|
33
46
|
# Create a new context about `absolute_path`
|
34
47
|
#
|
35
48
|
# The directory will not be created if it doesn't exist.
|
@@ -38,202 +51,5 @@ module Spoom
|
|
38
51
|
def initialize(absolute_path)
|
39
52
|
@absolute_path = T.let(::File.expand_path(absolute_path), String)
|
40
53
|
end
|
41
|
-
|
42
|
-
# Returns the absolute path to `relative_path` in the context's directory
|
43
|
-
sig { params(relative_path: String).returns(String) }
|
44
|
-
def absolute_path_to(relative_path)
|
45
|
-
File.join(@absolute_path, relative_path)
|
46
|
-
end
|
47
|
-
|
48
|
-
# File System
|
49
|
-
|
50
|
-
# Does the context directory at `absolute_path` exist and is a directory?
|
51
|
-
sig { returns(T::Boolean) }
|
52
|
-
def exist?
|
53
|
-
File.directory?(@absolute_path)
|
54
|
-
end
|
55
|
-
|
56
|
-
# Create the context directory at `absolute_path`
|
57
|
-
sig { void }
|
58
|
-
def mkdir!
|
59
|
-
FileUtils.rm_rf(@absolute_path)
|
60
|
-
FileUtils.mkdir_p(@absolute_path)
|
61
|
-
end
|
62
|
-
|
63
|
-
# List all files in this context matching `pattern`
|
64
|
-
sig { params(pattern: String).returns(T::Array[String]) }
|
65
|
-
def glob(pattern = "**/*")
|
66
|
-
Dir.glob(absolute_path_to(pattern)).map do |path|
|
67
|
-
Pathname.new(path).relative_path_from(@absolute_path).to_s
|
68
|
-
end.sort
|
69
|
-
end
|
70
|
-
|
71
|
-
# List all files at the top level of this context directory
|
72
|
-
sig { returns(T::Array[String]) }
|
73
|
-
def list
|
74
|
-
glob("*")
|
75
|
-
end
|
76
|
-
|
77
|
-
# Does `relative_path` point to an existing file in this context directory?
|
78
|
-
sig { params(relative_path: String).returns(T::Boolean) }
|
79
|
-
def file?(relative_path)
|
80
|
-
File.file?(absolute_path_to(relative_path))
|
81
|
-
end
|
82
|
-
|
83
|
-
# Return the contents of the file at `relative_path` in this context directory
|
84
|
-
#
|
85
|
-
# Will raise if the file doesn't exist.
|
86
|
-
sig { params(relative_path: String).returns(String) }
|
87
|
-
def read(relative_path)
|
88
|
-
File.read(absolute_path_to(relative_path))
|
89
|
-
end
|
90
|
-
|
91
|
-
# Write `contents` in the file at `relative_path` in this context directory
|
92
|
-
#
|
93
|
-
# Append to the file if `append` is true.
|
94
|
-
sig { params(relative_path: String, contents: String, append: T::Boolean).void }
|
95
|
-
def write!(relative_path, contents = "", append: false)
|
96
|
-
absolute_path = absolute_path_to(relative_path)
|
97
|
-
FileUtils.mkdir_p(File.dirname(absolute_path))
|
98
|
-
File.write(absolute_path, contents, mode: append ? "a" : "w")
|
99
|
-
end
|
100
|
-
|
101
|
-
# Remove the path at `relative_path` (recursive + force) in this context directory
|
102
|
-
sig { params(relative_path: String).void }
|
103
|
-
def remove!(relative_path)
|
104
|
-
FileUtils.rm_rf(absolute_path_to(relative_path))
|
105
|
-
end
|
106
|
-
|
107
|
-
# Move the file or directory from `from_relative_path` to `to_relative_path`
|
108
|
-
sig { params(from_relative_path: String, to_relative_path: String).void }
|
109
|
-
def move!(from_relative_path, to_relative_path)
|
110
|
-
destination_path = absolute_path_to(to_relative_path)
|
111
|
-
FileUtils.mkdir_p(File.dirname(destination_path))
|
112
|
-
FileUtils.mv(absolute_path_to(from_relative_path), destination_path)
|
113
|
-
end
|
114
|
-
|
115
|
-
# Delete this context and its content
|
116
|
-
#
|
117
|
-
# Warning: it will `rm -rf` the context directory on the file system.
|
118
|
-
sig { void }
|
119
|
-
def destroy!
|
120
|
-
FileUtils.rm_rf(@absolute_path)
|
121
|
-
end
|
122
|
-
|
123
|
-
# Execution
|
124
|
-
|
125
|
-
# Run a command in this context directory
|
126
|
-
sig { params(command: String, capture_err: T::Boolean).returns(ExecResult) }
|
127
|
-
def exec(command, capture_err: true)
|
128
|
-
Bundler.with_unbundled_env do
|
129
|
-
opts = T.let({ chdir: @absolute_path }, T::Hash[Symbol, T.untyped])
|
130
|
-
|
131
|
-
if capture_err
|
132
|
-
out, err, status = Open3.capture3(command, opts)
|
133
|
-
ExecResult.new(out: out, err: err, status: T.must(status.success?), exit_code: T.must(status.exitstatus))
|
134
|
-
else
|
135
|
-
out, status = Open3.capture2(command, opts)
|
136
|
-
ExecResult.new(out: out, err: nil, status: T.must(status.success?), exit_code: T.must(status.exitstatus))
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
# Bundle
|
142
|
-
|
143
|
-
# Read the `contents` of the Gemfile in this context directory
|
144
|
-
sig { returns(T.nilable(String)) }
|
145
|
-
def read_gemfile
|
146
|
-
read("Gemfile")
|
147
|
-
end
|
148
|
-
|
149
|
-
# Set the `contents` of the Gemfile in this context directory
|
150
|
-
sig { params(contents: String, append: T::Boolean).void }
|
151
|
-
def write_gemfile!(contents, append: false)
|
152
|
-
write!("Gemfile", contents, append: append)
|
153
|
-
end
|
154
|
-
|
155
|
-
# Run a command with `bundle` in this context directory
|
156
|
-
sig { params(command: String, version: T.nilable(String)).returns(ExecResult) }
|
157
|
-
def bundle(command, version: nil)
|
158
|
-
command = "_#{version}_ #{command}" if version
|
159
|
-
exec("bundle #{command}")
|
160
|
-
end
|
161
|
-
|
162
|
-
# Run `bundle install` in this context directory
|
163
|
-
sig { params(version: T.nilable(String)).returns(ExecResult) }
|
164
|
-
def bundle_install!(version: nil)
|
165
|
-
bundle("install", version: version)
|
166
|
-
end
|
167
|
-
|
168
|
-
# Run a command `bundle exec` in this context directory
|
169
|
-
sig { params(command: String, version: T.nilable(String)).returns(ExecResult) }
|
170
|
-
def bundle_exec(command, version: nil)
|
171
|
-
bundle("exec #{command}", version: version)
|
172
|
-
end
|
173
|
-
|
174
|
-
# Git
|
175
|
-
|
176
|
-
# Run a command prefixed by `git` in this context directory
|
177
|
-
sig { params(command: String).returns(ExecResult) }
|
178
|
-
def git(command)
|
179
|
-
exec("git #{command}")
|
180
|
-
end
|
181
|
-
|
182
|
-
# Run `git init` in this context directory
|
183
|
-
#
|
184
|
-
# Warning: passing a branch will run `git init -b <branch>` which is only available in git 2.28+.
|
185
|
-
# In older versions, use `git_init!` followed by `git("checkout -b <branch>")`.
|
186
|
-
sig { params(branch: T.nilable(String)).returns(ExecResult) }
|
187
|
-
def git_init!(branch: nil)
|
188
|
-
if branch
|
189
|
-
git("init -b #{branch}")
|
190
|
-
else
|
191
|
-
git("init")
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
# Run `git checkout` in this context directory
|
196
|
-
sig { params(ref: String).returns(ExecResult) }
|
197
|
-
def git_checkout!(ref: "main")
|
198
|
-
git("checkout #{ref}")
|
199
|
-
end
|
200
|
-
|
201
|
-
# Get the current git branch in this context directory
|
202
|
-
sig { returns(T.nilable(String)) }
|
203
|
-
def git_current_branch
|
204
|
-
Spoom::Git.current_branch(path: @absolute_path)
|
205
|
-
end
|
206
|
-
|
207
|
-
# Get the last commit in the currently checked out branch
|
208
|
-
sig { params(short_sha: T::Boolean).returns(T.nilable(Git::Commit)) }
|
209
|
-
def git_last_commit(short_sha: true)
|
210
|
-
Spoom::Git.last_commit(path: @absolute_path, short_sha: short_sha)
|
211
|
-
end
|
212
|
-
|
213
|
-
# Sorbet
|
214
|
-
|
215
|
-
# Run `bundle exec srb` in this context directory
|
216
|
-
sig { params(command: String).returns(ExecResult) }
|
217
|
-
def srb(command)
|
218
|
-
bundle_exec("srb #{command}")
|
219
|
-
end
|
220
|
-
|
221
|
-
# Read the contents of `sorbet/config` in this context directory
|
222
|
-
sig { returns(String) }
|
223
|
-
def read_sorbet_config
|
224
|
-
read(Spoom::Sorbet::CONFIG_PATH)
|
225
|
-
end
|
226
|
-
|
227
|
-
# Set the `contents` of `sorbet/config` in this context directory
|
228
|
-
sig { params(contents: String, append: T::Boolean).void }
|
229
|
-
def write_sorbet_config!(contents, append: false)
|
230
|
-
write!(Spoom::Sorbet::CONFIG_PATH, contents, append: append)
|
231
|
-
end
|
232
|
-
|
233
|
-
# Read the strictness sigil from the file at `relative_path` (returns `nil` if no sigil)
|
234
|
-
sig { params(relative_path: String).returns(T.nilable(String)) }
|
235
|
-
def read_file_strictness(relative_path)
|
236
|
-
Spoom::Sorbet::Sigils.file_strictness(absolute_path_to(relative_path))
|
237
|
-
end
|
238
54
|
end
|
239
55
|
end
|
@@ -148,48 +148,34 @@ module Spoom
|
|
148
148
|
class Sigils < CircleMap
|
149
149
|
extend T::Sig
|
150
150
|
|
151
|
-
sig
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
151
|
+
sig do
|
152
|
+
params(
|
153
|
+
id: String,
|
154
|
+
file_tree: FileTree,
|
155
|
+
nodes_strictnesses: T::Hash[FileTree::Node, T.nilable(String)],
|
156
|
+
nodes_scores: T::Hash[FileTree::Node, Float],
|
157
|
+
).void
|
158
|
+
end
|
159
|
+
def initialize(id, file_tree, nodes_strictnesses, nodes_scores)
|
160
|
+
@nodes_strictnesses = nodes_strictnesses
|
161
|
+
@nodes_scores = nodes_scores
|
162
|
+
super(id, file_tree.roots.map { |r| tree_node_to_json(r) })
|
157
163
|
end
|
158
164
|
|
159
165
|
sig { params(node: FileTree::Node).returns(T::Hash[Symbol, T.untyped]) }
|
160
166
|
def tree_node_to_json(node)
|
161
167
|
if node.children.empty?
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
sig { params(node: FileTree::Node).returns(T.nilable(String)) }
|
173
|
-
def tree_node_strictness(node)
|
174
|
-
prefix = @sigils_tree.strip_prefix
|
175
|
-
path = node.path
|
176
|
-
path = "#{prefix}/#{path}" if prefix
|
177
|
-
@strictnesses[node] ||= Spoom::Sorbet::Sigils.file_strictness(path)
|
178
|
-
end
|
179
|
-
|
180
|
-
sig { params(node: FileTree::Node).returns(Float) }
|
181
|
-
def tree_node_score(node)
|
182
|
-
unless @scores.key?(node)
|
183
|
-
if node.name =~ /\.rbi?$/
|
184
|
-
case tree_node_strictness(node)
|
185
|
-
when "true", "strict", "strong"
|
186
|
-
@scores[node] = 1.0
|
187
|
-
end
|
188
|
-
elsif !node.children.empty?
|
189
|
-
@scores[node] = node.children.values.sum { |n| tree_node_score(n) } / node.children.size.to_f
|
190
|
-
end
|
168
|
+
{
|
169
|
+
name: node.name,
|
170
|
+
strictness: @nodes_strictnesses.fetch(node, "false"),
|
171
|
+
}
|
172
|
+
else
|
173
|
+
{
|
174
|
+
name: node.name,
|
175
|
+
children: node.children.values.map { |n| tree_node_to_json(n) },
|
176
|
+
score: @nodes_scores.fetch(node, 0.0),
|
177
|
+
}
|
191
178
|
end
|
192
|
-
@scores[node] || 0.0
|
193
179
|
end
|
194
180
|
end
|
195
181
|
end
|
@@ -153,9 +153,24 @@ module Spoom
|
|
153
153
|
class Map < Card
|
154
154
|
extend T::Sig
|
155
155
|
|
156
|
-
sig
|
157
|
-
|
158
|
-
|
156
|
+
sig do
|
157
|
+
params(
|
158
|
+
file_tree: FileTree,
|
159
|
+
nodes_strictnesses: T::Hash[FileTree::Node, T.nilable(String)],
|
160
|
+
nodes_strictness_scores: T::Hash[FileTree::Node, Float],
|
161
|
+
title: String,
|
162
|
+
).void
|
163
|
+
end
|
164
|
+
def initialize(file_tree:, nodes_strictnesses:, nodes_strictness_scores:, title: "Strictness Map")
|
165
|
+
super(
|
166
|
+
title: title,
|
167
|
+
body: D3::CircleMap::Sigils.new(
|
168
|
+
"map_sigils",
|
169
|
+
file_tree,
|
170
|
+
nodes_strictnesses,
|
171
|
+
nodes_strictness_scores,
|
172
|
+
).html
|
173
|
+
)
|
159
174
|
end
|
160
175
|
end
|
161
176
|
|
@@ -246,27 +261,14 @@ module Spoom
|
|
246
261
|
class Report < Page
|
247
262
|
extend T::Sig
|
248
263
|
|
249
|
-
sig { returns(String) }
|
250
|
-
attr_reader :project_name
|
251
|
-
|
252
|
-
sig { returns(T.nilable(String)) }
|
253
|
-
attr_reader :sorbet_intro_commit
|
254
|
-
|
255
|
-
sig { returns(T.nilable(Time)) }
|
256
|
-
attr_reader :sorbet_intro_date
|
257
|
-
|
258
|
-
sig { returns(T::Array[Snapshot]) }
|
259
|
-
attr_reader :snapshots
|
260
|
-
|
261
|
-
sig { returns(FileTree) }
|
262
|
-
attr_reader :sigils_tree
|
263
|
-
|
264
264
|
sig do
|
265
265
|
params(
|
266
266
|
project_name: String,
|
267
267
|
palette: D3::ColorPalette,
|
268
268
|
snapshots: T::Array[Snapshot],
|
269
|
-
|
269
|
+
file_tree: FileTree,
|
270
|
+
nodes_strictnesses: T::Hash[FileTree::Node, T.nilable(String)],
|
271
|
+
nodes_strictness_scores: T::Hash[FileTree::Node, Float],
|
270
272
|
sorbet_intro_commit: T.nilable(String),
|
271
273
|
sorbet_intro_date: T.nilable(Time),
|
272
274
|
).void
|
@@ -275,24 +277,28 @@ module Spoom
|
|
275
277
|
project_name:,
|
276
278
|
palette:,
|
277
279
|
snapshots:,
|
278
|
-
|
280
|
+
file_tree:,
|
281
|
+
nodes_strictnesses:,
|
282
|
+
nodes_strictness_scores:,
|
279
283
|
sorbet_intro_commit: nil,
|
280
284
|
sorbet_intro_date: nil
|
281
285
|
)
|
282
286
|
super(title: project_name, palette: palette)
|
283
287
|
@project_name = project_name
|
284
288
|
@snapshots = snapshots
|
285
|
-
@
|
289
|
+
@file_tree = file_tree
|
290
|
+
@nodes_strictnesses = nodes_strictnesses
|
291
|
+
@nodes_strictness_scores = nodes_strictness_scores
|
286
292
|
@sorbet_intro_commit = sorbet_intro_commit
|
287
293
|
@sorbet_intro_date = sorbet_intro_date
|
288
294
|
end
|
289
295
|
|
290
296
|
sig { override.returns(String) }
|
291
297
|
def header_html
|
292
|
-
last = T.must(snapshots.last)
|
298
|
+
last = T.must(@snapshots.last)
|
293
299
|
<<~ERB
|
294
300
|
<h1 class="display-3">
|
295
|
-
#{project_name}
|
301
|
+
#{@project_name}
|
296
302
|
<span class="badge badge-pill badge-dark" style="font-size: 20%;">#{last.commit_sha}</span>
|
297
303
|
</h1>
|
298
304
|
ERB
|
@@ -300,17 +306,24 @@ module Spoom
|
|
300
306
|
|
301
307
|
sig { override.returns(T::Array[Cards::Card]) }
|
302
308
|
def cards
|
303
|
-
last = T.must(snapshots.last)
|
309
|
+
last = T.must(@snapshots.last)
|
304
310
|
cards = []
|
305
311
|
cards << Cards::Snapshot.new(snapshot: last)
|
306
|
-
cards << Cards::Map.new(
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
cards << Cards::Timeline::
|
312
|
-
cards << Cards::Timeline::
|
313
|
-
cards << Cards::
|
312
|
+
cards << Cards::Map.new(
|
313
|
+
file_tree: @file_tree,
|
314
|
+
nodes_strictnesses: @nodes_strictnesses,
|
315
|
+
nodes_strictness_scores: @nodes_strictness_scores,
|
316
|
+
)
|
317
|
+
cards << Cards::Timeline::Sigils.new(snapshots: @snapshots)
|
318
|
+
cards << Cards::Timeline::Calls.new(snapshots: @snapshots)
|
319
|
+
cards << Cards::Timeline::Sigs.new(snapshots: @snapshots)
|
320
|
+
cards << Cards::Timeline::RBIs.new(snapshots: @snapshots)
|
321
|
+
cards << Cards::Timeline::Versions.new(snapshots: @snapshots)
|
322
|
+
cards << Cards::Timeline::Runtimes.new(snapshots: @snapshots)
|
323
|
+
cards << Cards::SorbetIntro.new(
|
324
|
+
sorbet_intro_commit: @sorbet_intro_commit,
|
325
|
+
sorbet_intro_date: @sorbet_intro_date,
|
326
|
+
)
|
314
327
|
cards
|
315
328
|
end
|
316
329
|
end
|