spoom 1.1.11 → 1.1.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.md +6 -0
- data/Rakefile +1 -0
- data/lib/spoom/cli/bump.rb +11 -7
- data/lib/spoom/cli/coverage.rb +11 -7
- data/lib/spoom/cli/helper.rb +5 -4
- data/lib/spoom/cli/lsp.rb +2 -1
- data/lib/spoom/cli/run.rb +16 -6
- data/lib/spoom/cli.rb +6 -4
- data/lib/spoom/context.rb +219 -0
- data/lib/spoom/coverage/d3/base.rb +12 -8
- data/lib/spoom/coverage/d3/circle_map.rb +40 -37
- data/lib/spoom/coverage/d3/pie.rb +30 -26
- data/lib/spoom/coverage/d3/timeline.rb +86 -82
- data/lib/spoom/coverage/d3.rb +72 -70
- data/lib/spoom/coverage/report.rb +6 -6
- data/lib/spoom/coverage/snapshot.rb +40 -33
- data/lib/spoom/coverage.rb +84 -81
- data/lib/spoom/file_tree.rb +2 -0
- data/lib/spoom/git.rb +107 -97
- data/lib/spoom/printer.rb +4 -0
- data/lib/spoom/sorbet/errors.rb +29 -11
- data/lib/spoom/sorbet/lsp/base.rb +1 -1
- data/lib/spoom/sorbet/lsp/errors.rb +23 -17
- data/lib/spoom/sorbet/lsp/structures.rb +85 -54
- data/lib/spoom/sorbet/lsp.rb +71 -63
- data/lib/spoom/sorbet/metrics.rb +18 -16
- data/lib/spoom/sorbet/sigils.rb +59 -54
- data/lib/spoom/sorbet.rb +11 -8
- data/lib/spoom/timeline.rb +1 -0
- data/lib/spoom/version.rb +1 -1
- data/lib/spoom.rb +43 -25
- metadata +20 -20
- data/lib/spoom/test_helpers/project.rb +0 -138
data/lib/spoom/coverage.rb
CHANGED
@@ -9,94 +9,97 @@ require "date"
|
|
9
9
|
|
10
10
|
module Spoom
|
11
11
|
module Coverage
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
12
|
+
class << self
|
13
|
+
extend T::Sig
|
14
|
+
|
15
|
+
sig { params(path: String, rbi: T::Boolean, sorbet_bin: T.nilable(String)).returns(Snapshot) }
|
16
|
+
def snapshot(path: ".", rbi: true, sorbet_bin: nil)
|
17
|
+
config = sorbet_config(path: path)
|
18
|
+
config.allowed_extensions.push(".rb", ".rbi") if config.allowed_extensions.empty?
|
19
|
+
|
20
|
+
new_config = config.copy
|
21
|
+
new_config.allowed_extensions.reject! { |ext| !rbi && ext == ".rbi" }
|
22
|
+
|
23
|
+
metrics = Spoom::Sorbet.srb_metrics(
|
24
|
+
"--no-config",
|
25
|
+
"--no-error-sections",
|
26
|
+
"--no-error-count",
|
27
|
+
"--isolate-error-code=0",
|
28
|
+
new_config.options_string,
|
29
|
+
path: path,
|
30
|
+
capture_err: true,
|
31
|
+
sorbet_bin: sorbet_bin
|
32
|
+
)
|
33
|
+
|
34
|
+
snapshot = Snapshot.new
|
35
|
+
return snapshot unless metrics
|
36
|
+
|
37
|
+
sha = Spoom::Git.last_commit(path: path)
|
38
|
+
snapshot.commit_sha = sha
|
39
|
+
snapshot.commit_timestamp = Spoom::Git.commit_timestamp(sha, path: path).to_i if sha
|
40
|
+
|
41
|
+
snapshot.files = metrics.fetch("types.input.files", 0)
|
42
|
+
snapshot.modules = metrics.fetch("types.input.modules.total", 0)
|
43
|
+
snapshot.classes = metrics.fetch("types.input.classes.total", 0)
|
44
|
+
snapshot.singleton_classes = metrics.fetch("types.input.singleton_classes.total", 0)
|
45
|
+
snapshot.methods_with_sig = metrics.fetch("types.sig.count", 0)
|
46
|
+
snapshot.methods_without_sig = metrics.fetch("types.input.methods.total", 0) - snapshot.methods_with_sig
|
47
|
+
snapshot.calls_typed = metrics.fetch("types.input.sends.typed", 0)
|
48
|
+
snapshot.calls_untyped = metrics.fetch("types.input.sends.total", 0) - snapshot.calls_typed
|
49
|
+
|
50
|
+
snapshot.duration += metrics.fetch("run.utilization.system_time.us", 0)
|
51
|
+
snapshot.duration += metrics.fetch("run.utilization.user_time.us", 0)
|
52
|
+
|
53
|
+
Snapshot::STRICTNESSES.each do |strictness|
|
54
|
+
next unless metrics.key?("types.input.files.sigil.#{strictness}")
|
55
|
+
|
56
|
+
snapshot.sigils[strictness] = T.must(metrics["types.input.files.sigil.#{strictness}"])
|
57
|
+
end
|
58
|
+
|
59
|
+
snapshot.version_static = Spoom::Sorbet.version_from_gemfile_lock(gem: "sorbet-static", path: path)
|
60
|
+
snapshot.version_runtime = Spoom::Sorbet.version_from_gemfile_lock(gem: "sorbet-runtime", path: path)
|
61
|
+
|
62
|
+
files = Spoom::Sorbet.srb_files(new_config, path: path)
|
63
|
+
snapshot.rbi_files = files.count { |file| file.end_with?(".rbi") }
|
64
|
+
|
65
|
+
snapshot
|
55
66
|
end
|
56
67
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
Report.new(
|
72
|
-
project_name: File.basename(File.expand_path(path)),
|
73
|
-
palette: palette,
|
74
|
-
snapshots: snapshots,
|
75
|
-
sigils_tree: sigils_tree(path: path),
|
76
|
-
sorbet_intro_commit: intro_commit,
|
77
|
-
sorbet_intro_date: intro_date,
|
78
|
-
)
|
79
|
-
end
|
68
|
+
sig { params(snapshots: T::Array[Snapshot], palette: D3::ColorPalette, path: String).returns(Report) }
|
69
|
+
def report(snapshots, palette:, path: ".")
|
70
|
+
intro_commit = Git.sorbet_intro_commit(path: path)
|
71
|
+
intro_date = intro_commit ? Git.commit_time(intro_commit, path: path) : nil
|
72
|
+
|
73
|
+
Report.new(
|
74
|
+
project_name: File.basename(File.expand_path(path)),
|
75
|
+
palette: palette,
|
76
|
+
snapshots: snapshots,
|
77
|
+
sigils_tree: sigils_tree(path: path),
|
78
|
+
sorbet_intro_commit: intro_commit,
|
79
|
+
sorbet_intro_date: intro_date,
|
80
|
+
)
|
81
|
+
end
|
80
82
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
83
|
+
sig { params(path: String).returns(Sorbet::Config) }
|
84
|
+
def sorbet_config(path: ".")
|
85
|
+
Sorbet::Config.parse_file("#{path}/#{Spoom::Sorbet::CONFIG_PATH}")
|
86
|
+
end
|
85
87
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
88
|
+
sig { params(path: String).returns(FileTree) }
|
89
|
+
def sigils_tree(path: ".")
|
90
|
+
config = sorbet_config(path: path)
|
91
|
+
files = Sorbet.srb_files(config, path: path)
|
90
92
|
|
91
|
-
|
92
|
-
|
93
|
-
|
93
|
+
extensions = config.allowed_extensions
|
94
|
+
extensions = [".rb"] if extensions.empty?
|
95
|
+
extensions -= [".rbi"]
|
94
96
|
|
95
|
-
|
96
|
-
|
97
|
-
|
97
|
+
pattern = /\.(#{Regexp.union(extensions.map { |ext| ext[1..-1] })})$/
|
98
|
+
files.select! { |file| file =~ pattern }
|
99
|
+
files.reject! { |file| file =~ %r{/test/} }
|
98
100
|
|
99
|
-
|
101
|
+
FileTree.new(files, strip_prefix: path)
|
102
|
+
end
|
100
103
|
end
|
101
104
|
end
|
102
105
|
end
|
data/lib/spoom/file_tree.rb
CHANGED
@@ -33,6 +33,7 @@ module Spoom
|
|
33
33
|
if path.empty? || parts.size == 1
|
34
34
|
return @roots[path] ||= Node.new(parent: nil, name: path)
|
35
35
|
end
|
36
|
+
|
36
37
|
parent_path = T.must(parts[0...-1]).join("/")
|
37
38
|
parent = add_path(parent_path)
|
38
39
|
name = T.must(parts.last)
|
@@ -105,6 +106,7 @@ module Spoom
|
|
105
106
|
def path
|
106
107
|
parent = self.parent
|
107
108
|
return name unless parent
|
109
|
+
|
108
110
|
"#{parent.path}/#{name}"
|
109
111
|
end
|
110
112
|
end
|
data/lib/spoom/git.rb
CHANGED
@@ -6,119 +6,129 @@ require "time"
|
|
6
6
|
module Spoom
|
7
7
|
# Execute git commands
|
8
8
|
module Git
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
9
|
+
class << self
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
# Execute a `command`
|
13
|
+
sig { params(command: String, arg: String, path: String).returns(ExecResult) }
|
14
|
+
def exec(command, *arg, path: ".")
|
15
|
+
return ExecResult.new(
|
16
|
+
out: "",
|
17
|
+
err: "Error: `#{path}` is not a directory.",
|
18
|
+
status: false,
|
19
|
+
exit_code: 1
|
20
|
+
) unless File.directory?(path)
|
21
|
+
|
22
|
+
T.unsafe(Open3).popen3(command, *arg, chdir: path) do |_, stdout, stderr, thread|
|
23
|
+
status = T.cast(thread.value, Process::Status)
|
24
|
+
ExecResult.new(
|
25
|
+
out: stdout.read,
|
26
|
+
err: stderr.read,
|
27
|
+
status: T.must(status.success?),
|
28
|
+
exit_code: T.must(status.exitstatus)
|
29
|
+
)
|
30
|
+
end
|
29
31
|
end
|
30
|
-
end
|
31
32
|
|
32
|
-
|
33
|
+
# Git commands
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
sig { params(arg: String, path: String).returns(ExecResult) }
|
36
|
+
def checkout(*arg, path: ".")
|
37
|
+
exec("git checkout -q #{arg.join(" ")}", path: path)
|
38
|
+
end
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
sig { params(arg: String, path: String).returns(ExecResult) }
|
41
|
+
def diff(*arg, path: ".")
|
42
|
+
exec("git diff #{arg.join(" ")}", path: path)
|
43
|
+
end
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
45
|
+
sig { params(arg: String, path: String).returns(ExecResult) }
|
46
|
+
def log(*arg, path: ".")
|
47
|
+
exec("git log #{arg.join(" ")}", path: path)
|
48
|
+
end
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
50
|
+
sig { params(arg: String, path: String).returns(ExecResult) }
|
51
|
+
def rev_parse(*arg, path: ".")
|
52
|
+
exec("git rev-parse --short #{arg.join(" ")}", path: path)
|
53
|
+
end
|
53
54
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
sig { params(arg: String, path: String).returns(ExecResult) }
|
56
|
+
def show(*arg, path: ".")
|
57
|
+
exec("git show #{arg.join(" ")}", path: path)
|
58
|
+
end
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
result.out.strip
|
64
|
-
end
|
60
|
+
sig { params(path: String).returns(T.nilable(String)) }
|
61
|
+
def current_branch(path: ".")
|
62
|
+
result = exec("git branch --show-current", path: path)
|
63
|
+
return nil unless result.status
|
65
64
|
|
66
|
-
|
65
|
+
result.out.strip
|
66
|
+
end
|
67
67
|
|
68
|
-
|
69
|
-
sig { params(sha: String, path: String).returns(T.nilable(Integer)) }
|
70
|
-
def self.commit_timestamp(sha, path: ".")
|
71
|
-
result = show("--no-notes --no-patch --pretty=%at #{sha}", path: path)
|
72
|
-
return nil unless result.status
|
73
|
-
result.out.strip.to_i
|
74
|
-
end
|
68
|
+
# Utils
|
75
69
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
epoch_to_time(timestamp.to_s)
|
82
|
-
end
|
70
|
+
# Get the commit epoch timestamp for a `sha`
|
71
|
+
sig { params(sha: String, path: String).returns(T.nilable(Integer)) }
|
72
|
+
def commit_timestamp(sha, path: ".")
|
73
|
+
result = show("--no-notes --no-patch --pretty=%at #{sha}", path: path)
|
74
|
+
return nil unless result.status
|
83
75
|
|
84
|
-
|
85
|
-
|
86
|
-
def self.last_commit(path: ".")
|
87
|
-
result = rev_parse("HEAD", path: path)
|
88
|
-
return nil unless result.status
|
89
|
-
result.out.strip
|
90
|
-
end
|
76
|
+
result.out.strip.to_i
|
77
|
+
end
|
91
78
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
79
|
+
# Get the commit Time for a `sha`
|
80
|
+
sig { params(sha: String, path: String).returns(T.nilable(Time)) }
|
81
|
+
def commit_time(sha, path: ".")
|
82
|
+
timestamp = commit_timestamp(sha, path: path)
|
83
|
+
return nil unless timestamp
|
97
84
|
|
98
|
-
|
99
|
-
|
100
|
-
def self.workdir_clean?(path: ".")
|
101
|
-
diff("HEAD", path: path).out.empty?
|
102
|
-
end
|
85
|
+
epoch_to_time(timestamp.to_s)
|
86
|
+
end
|
103
87
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
out = result.out.strip
|
110
|
-
return nil if out.empty?
|
111
|
-
out
|
112
|
-
end
|
88
|
+
# Get the last commit sha
|
89
|
+
sig { params(path: String).returns(T.nilable(String)) }
|
90
|
+
def last_commit(path: ".")
|
91
|
+
result = rev_parse("HEAD", path: path)
|
92
|
+
return nil unless result.status
|
113
93
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
94
|
+
result.out.strip
|
95
|
+
end
|
96
|
+
|
97
|
+
# Translate a git epoch timestamp into a Time
|
98
|
+
sig { params(timestamp: String).returns(Time) }
|
99
|
+
def epoch_to_time(timestamp)
|
100
|
+
Time.strptime(timestamp, "%s")
|
101
|
+
end
|
102
|
+
|
103
|
+
# Is there uncommited changes in `path`?
|
104
|
+
sig { params(path: String).returns(T::Boolean) }
|
105
|
+
def workdir_clean?(path: ".")
|
106
|
+
diff("HEAD", path: path).out.empty?
|
107
|
+
end
|
108
|
+
|
109
|
+
# Get the hash of the commit introducing the `sorbet/config` file
|
110
|
+
sig { params(path: String).returns(T.nilable(String)) }
|
111
|
+
def sorbet_intro_commit(path: ".")
|
112
|
+
result = Spoom::Git.log("--diff-filter=A --format='%h' -1 -- sorbet/config", path: path)
|
113
|
+
return nil unless result.status
|
114
|
+
|
115
|
+
out = result.out.strip
|
116
|
+
return nil if out.empty?
|
117
|
+
|
118
|
+
out
|
119
|
+
end
|
120
|
+
|
121
|
+
# Get the hash of the commit removing the `sorbet/config` file
|
122
|
+
sig { params(path: String).returns(T.nilable(String)) }
|
123
|
+
def sorbet_removal_commit(path: ".")
|
124
|
+
result = Spoom::Git.log("--diff-filter=D --format='%h' -1 -- sorbet/config", path: path)
|
125
|
+
return nil unless result.status
|
126
|
+
|
127
|
+
out = result.out.strip
|
128
|
+
return nil if out.empty?
|
129
|
+
|
130
|
+
out
|
131
|
+
end
|
122
132
|
end
|
123
133
|
end
|
124
134
|
end
|
data/lib/spoom/printer.rb
CHANGED
@@ -38,6 +38,7 @@ module Spoom
|
|
38
38
|
sig { params(string: T.nilable(String)).void }
|
39
39
|
def print(string)
|
40
40
|
return unless string
|
41
|
+
|
41
42
|
@out.print(string)
|
42
43
|
end
|
43
44
|
|
@@ -47,6 +48,7 @@ module Spoom
|
|
47
48
|
sig { params(string: T.nilable(String), color: Color).void }
|
48
49
|
def print_colored(string, *color)
|
49
50
|
return unless string
|
51
|
+
|
50
52
|
string = T.unsafe(self).colorize(string, *color)
|
51
53
|
@out.print(string)
|
52
54
|
end
|
@@ -61,6 +63,7 @@ module Spoom
|
|
61
63
|
sig { params(string: T.nilable(String)).void }
|
62
64
|
def printl(string)
|
63
65
|
return unless string
|
66
|
+
|
64
67
|
printt
|
65
68
|
print(string)
|
66
69
|
printn
|
@@ -76,6 +79,7 @@ module Spoom
|
|
76
79
|
sig { params(string: String, color: Spoom::Color).returns(String) }
|
77
80
|
def colorize(string, *color)
|
78
81
|
return string unless @colors
|
82
|
+
|
79
83
|
T.unsafe(self).set_color(string, *color)
|
80
84
|
end
|
81
85
|
end
|
data/lib/spoom/sorbet/errors.rb
CHANGED
@@ -4,10 +4,16 @@
|
|
4
4
|
module Spoom
|
5
5
|
module Sorbet
|
6
6
|
module Errors
|
7
|
-
extend T::Sig
|
8
|
-
|
9
7
|
DEFAULT_ERROR_URL_BASE = "https://srb.help/"
|
10
8
|
|
9
|
+
class << self
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
sig { params(errors: T::Array[Error]).returns(T::Array[Error]) }
|
13
|
+
def sort_errors_by_code(errors)
|
14
|
+
errors.sort_by { |e| [e.code, e.file, e.line, e.message] }
|
15
|
+
end
|
16
|
+
end
|
11
17
|
# Parse errors from Sorbet output
|
12
18
|
class Parser
|
13
19
|
extend T::Sig
|
@@ -20,10 +26,14 @@ module Spoom
|
|
20
26
|
"or set SORBET_SILENCE_DEV_MESSAGE=1 in your shell environment.",
|
21
27
|
], T::Array[String])
|
22
28
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
29
|
+
class << self
|
30
|
+
extend T::Sig
|
31
|
+
|
32
|
+
sig { params(output: String, error_url_base: String).returns(T::Array[Error]) }
|
33
|
+
def parse_string(output, error_url_base: DEFAULT_ERROR_URL_BASE)
|
34
|
+
parser = Spoom::Sorbet::Errors::Parser.new(error_url_base: error_url_base)
|
35
|
+
parser.parse(output)
|
36
|
+
end
|
27
37
|
end
|
28
38
|
|
29
39
|
sig { params(error_url_base: String).void }
|
@@ -85,12 +95,14 @@ module Spoom
|
|
85
95
|
sig { params(error: Error).void }
|
86
96
|
def open_error(error)
|
87
97
|
raise "Error: Already parsing an error!" if @current_error
|
98
|
+
|
88
99
|
@current_error = error
|
89
100
|
end
|
90
101
|
|
91
102
|
sig { void }
|
92
103
|
def close_error
|
93
104
|
raise "Error: Not already parsing an error!" unless @current_error
|
105
|
+
|
94
106
|
@errors << @current_error
|
95
107
|
@current_error = nil
|
96
108
|
end
|
@@ -98,6 +110,11 @@ module Spoom
|
|
98
110
|
sig { params(line: String).void }
|
99
111
|
def append_error(line)
|
100
112
|
raise "Error: Not already parsing an error!" unless @current_error
|
113
|
+
|
114
|
+
filepath_match = line.match(/^ (.*?):\d+/)
|
115
|
+
if filepath_match && filepath_match[1]
|
116
|
+
@current_error.files_from_error_sections << T.must(filepath_match[1])
|
117
|
+
end
|
101
118
|
@current_error.more << line
|
102
119
|
end
|
103
120
|
end
|
@@ -115,6 +132,10 @@ module Spoom
|
|
115
132
|
sig { returns(T::Array[String]) }
|
116
133
|
attr_reader :more
|
117
134
|
|
135
|
+
# Other files associated with the error
|
136
|
+
sig { returns(T::Set[String]) }
|
137
|
+
attr_reader :files_from_error_sections
|
138
|
+
|
118
139
|
sig do
|
119
140
|
params(
|
120
141
|
file: T.nilable(String),
|
@@ -130,12 +151,14 @@ module Spoom
|
|
130
151
|
@message = message
|
131
152
|
@code = code
|
132
153
|
@more = more
|
154
|
+
@files_from_error_sections = T.let(Set.new, T::Set[String])
|
133
155
|
end
|
134
156
|
|
135
157
|
# By default errors are sorted by location
|
136
158
|
sig { params(other: T.untyped).returns(Integer) }
|
137
159
|
def <=>(other)
|
138
160
|
return 0 unless other.is_a?(Error)
|
161
|
+
|
139
162
|
[file, line, code, message] <=> [other.file, other.line, other.code, other.message]
|
140
163
|
end
|
141
164
|
|
@@ -144,11 +167,6 @@ module Spoom
|
|
144
167
|
"#{file}:#{line}: #{message} (#{code})"
|
145
168
|
end
|
146
169
|
end
|
147
|
-
|
148
|
-
sig { params(errors: T::Array[Error]).returns(T::Array[Error]) }
|
149
|
-
def self.sort_errors_by_code(errors)
|
150
|
-
errors.sort_by { |e| [e.code, e.file, e.line, e.message] }
|
151
|
-
end
|
152
170
|
end
|
153
171
|
end
|
154
172
|
end
|
@@ -16,18 +16,23 @@ module Spoom
|
|
16
16
|
sig { returns(T::Array[Diagnostic]) }
|
17
17
|
attr_reader :diagnostics
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
class << self
|
20
|
+
extend T::Sig
|
21
|
+
|
22
|
+
sig { params(json: T::Hash[T.untyped, T.untyped]).returns(Diagnostics) }
|
23
|
+
def from_json(json)
|
24
|
+
Diagnostics.new(
|
25
|
+
json["uri"],
|
26
|
+
json["diagnostics"].map { |d| Diagnostic.from_json(d) }
|
27
|
+
)
|
28
|
+
end
|
25
29
|
end
|
26
30
|
|
27
31
|
sig { params(uri: String, diagnostics: T::Array[Diagnostic]).void }
|
28
32
|
def initialize(uri, diagnostics)
|
29
33
|
@uri = uri
|
30
34
|
@diagnostics = diagnostics
|
35
|
+
super()
|
31
36
|
end
|
32
37
|
end
|
33
38
|
end
|
@@ -38,25 +43,26 @@ module Spoom
|
|
38
43
|
sig { returns(Integer) }
|
39
44
|
attr_reader :code
|
40
45
|
|
41
|
-
sig { returns(String) }
|
42
|
-
attr_reader :message
|
43
|
-
|
44
46
|
sig { returns(T::Hash[T.untyped, T.untyped]) }
|
45
47
|
attr_reader :data
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
49
|
+
class << self
|
50
|
+
extend T::Sig
|
51
|
+
|
52
|
+
sig { params(json: T::Hash[T.untyped, T.untyped]).returns(ResponseError) }
|
53
|
+
def from_json(json)
|
54
|
+
ResponseError.new(
|
55
|
+
json["code"],
|
56
|
+
json["message"],
|
57
|
+
json["data"]
|
58
|
+
)
|
59
|
+
end
|
54
60
|
end
|
55
61
|
|
56
62
|
sig { params(code: Integer, message: String, data: T::Hash[T.untyped, T.untyped]).void }
|
57
63
|
def initialize(code, message, data)
|
64
|
+
super(message)
|
58
65
|
@code = code
|
59
|
-
@message = message
|
60
66
|
@data = data
|
61
67
|
end
|
62
68
|
end
|