spoom 1.1.4 → 1.1.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c138290b530402f788fd25c8238340609f68e370cdfce7166f82f910f6352125
4
- data.tar.gz: '08595ccc6f1485d1b935c98a318daf9de6475d2fbcc742efafd955679e372cd3'
3
+ metadata.gz: 859ebe3fbc7461a158b56eba3eaaa938df4fcc8fec0c91d40c2a1986b28eec72
4
+ data.tar.gz: 953222ea44625ba7aeefcb7e19c8bff30a94fcaedd95c31784cce7ffcf768250
5
5
  SHA512:
6
- metadata.gz: 8617496b57ade807623ef5cad109d20ff1024c8b6125c6286092859c69aab3e34f2a367fcb3ed531f425425af41cbc04492c0fd098f6e42170a9edf4bb41d120
7
- data.tar.gz: b8040df444748a8769739caf0699d784ffcc4518e1a55c152d72f2073ae5a62d08ef02085aaa8808eb48c078baba7655e3880700ca8fb0b79830a52d66f95c7d
6
+ metadata.gz: 3baf1551ea1d40e6ea17bacaf67dd6361fe36e07b50edb2de6b0b3905e03ef7de568d877f51a2bbc9272c180f96ad7657a924a3465c39adb8fe5f23d658f0cd9
7
+ data.tar.gz: 3fcd666d6d2e32af0f3ce6792fe1ce4572811d6bc52fd81532f5b5754dd4d82df61a85759e14410e4abb6948754dd0a087498d3a162c154021496096acc5b1ed
data/Gemfile CHANGED
@@ -5,8 +5,9 @@ source "https://rubygems.org"
5
5
 
6
6
  gemspec
7
7
 
8
- group(:development) do
9
- gem('rubocop-shopify', require: false)
10
- gem('rubocop-sorbet', require: false)
11
- gem('pry-byebug')
8
+ group :development do
9
+ gem "pry-byebug"
10
+ gem "rubocop-shopify", require: false
11
+ gem "rubocop-sorbet", require: false
12
+ gem "tapioca", require: false
12
13
  end
data/README.md CHANGED
@@ -358,4 +358,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
358
358
 
359
359
  ## Code of Conduct
360
360
 
361
- Everyone interacting in the Spoom project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/Shopify/spoom/blob/master/CODE_OF_CONDUCT.md).
361
+ Everyone interacting in the Spoom project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/Shopify/spoom/blob/main/CODE_OF_CONDUCT.md).
@@ -83,20 +83,30 @@ module Spoom
83
83
  exit(files_to_bump.empty?)
84
84
  end
85
85
 
86
- output, no_errors = Sorbet.srb_tc(
86
+ error_url_base = Spoom::Sorbet::Errors::DEFAULT_ERROR_URL_BASE
87
+ output, status, exit_code = Sorbet.srb_tc(
87
88
  "--no-error-sections",
89
+ "--error-url-base=#{error_url_base}",
88
90
  path: exec_path,
89
91
  capture_err: true,
90
92
  sorbet_bin: options[:sorbet]
91
93
  )
92
94
 
93
- if no_errors
95
+ check_sorbet_segfault(exit_code) do
96
+ say_error(<<~ERR, status: nil)
97
+ It means one of the file bumped to `typed: #{to}` made Sorbet crash.
98
+ Run `spoom bump -f` locally followed by `bundle exec srb tc` to investigate the problem.
99
+ ERR
100
+ undo_changes(files_to_bump, from)
101
+ end
102
+
103
+ if status
94
104
  print_changes(files_to_bump, command: cmd, from: from, to: to, dry: dry, path: exec_path)
95
105
  undo_changes(files_to_bump, from) if dry
96
106
  exit(files_to_bump.empty?)
97
107
  end
98
108
 
99
- errors = Sorbet::Errors::Parser.parse_string(output)
109
+ errors = Sorbet::Errors::Parser.parse_string(output, error_url_base: error_url_base)
100
110
 
101
111
  files_with_errors = errors.map do |err|
102
112
  path = File.expand_path(err.file)
@@ -10,7 +10,8 @@ module Spoom
10
10
  module Helper
11
11
  extend T::Sig
12
12
  extend T::Helpers
13
- include Thor::Shell
13
+
14
+ include Colorize
14
15
 
15
16
  requires_ancestor { Thor }
16
17
 
@@ -82,10 +83,24 @@ module Spoom
82
83
  Sorbet::Config.parse_file(sorbet_config_file)
83
84
  end
84
85
 
86
+ sig { params(exit_code: Integer, block: T.nilable(T.proc.void)).void }
87
+ def check_sorbet_segfault(exit_code, &block)
88
+ return unless exit_code == Spoom::Sorbet::SEGFAULT_CODE
89
+
90
+ say_error(<<~ERR, status: nil)
91
+ #{red("!!! Sorbet exited with code #{exit_code} - SEGFAULT !!!")}
92
+
93
+ This is most likely related to a bug in Sorbet.
94
+ ERR
95
+
96
+ block&.call
97
+ exit(exit_code)
98
+ end
99
+
85
100
  # Colors
86
101
 
87
102
  # Color used to highlight expressions in backticks
88
- HIGHLIGHT_COLOR = :blue
103
+ HIGHLIGHT_COLOR = T.let(Spoom::Color::BLUE, Spoom::Color)
89
104
 
90
105
  # Is the `--color` option true?
91
106
  sig { returns(T::Boolean) }
@@ -117,35 +132,40 @@ module Spoom
117
132
  end
118
133
 
119
134
  # Colorize a string if `color?`
120
- sig { params(string: String, color: Symbol).returns(String) }
121
- def colorize(string, color)
135
+ sig { params(string: String, color: Color).returns(String) }
136
+ def colorize(string, *color)
122
137
  return string unless color?
123
- string.colorize(color)
138
+ T.unsafe(self).set_color(string, *color)
124
139
  end
125
140
 
126
141
  sig { params(string: String).returns(String) }
127
142
  def blue(string)
128
- colorize(string, :blue)
143
+ colorize(string, Color::BLUE)
144
+ end
145
+
146
+ sig { params(string: String).returns(String) }
147
+ def cyan(string)
148
+ colorize(string, Color::CYAN)
129
149
  end
130
150
 
131
151
  sig { params(string: String).returns(String) }
132
152
  def gray(string)
133
- colorize(string, :light_black)
153
+ colorize(string, Color::LIGHT_BLACK)
134
154
  end
135
155
 
136
156
  sig { params(string: String).returns(String) }
137
157
  def green(string)
138
- colorize(string, :green)
158
+ colorize(string, Color::GREEN)
139
159
  end
140
160
 
141
161
  sig { params(string: String).returns(String) }
142
162
  def red(string)
143
- colorize(string, :red)
163
+ colorize(string, Color::RED)
144
164
  end
145
165
 
146
166
  sig { params(string: String).returns(String) }
147
167
  def yellow(string)
148
- colorize(string, :yellow)
168
+ colorize(string, Color::YELLOW)
149
169
  end
150
170
  end
151
171
  end
data/lib/spoom/cli/run.rb CHANGED
@@ -35,12 +35,27 @@ module Spoom
35
35
  sorbet = options[:sorbet]
36
36
 
37
37
  unless limit || code || sort
38
- output, status = T.unsafe(Spoom::Sorbet).srb_tc(*arg, path: path, capture_err: false, sorbet_bin: sorbet)
38
+ output, status, exit_code = T.unsafe(Spoom::Sorbet).srb_tc(
39
+ *arg,
40
+ path: path,
41
+ capture_err: false,
42
+ sorbet_bin: sorbet
43
+ )
44
+
45
+ check_sorbet_segfault(exit_code)
39
46
  say_error(output, status: nil, nl: false)
40
47
  exit(status)
41
48
  end
42
49
 
43
- output, status = T.unsafe(Spoom::Sorbet).srb_tc(*arg, path: path, capture_err: true, sorbet_bin: sorbet)
50
+ output, status, exit_code = T.unsafe(Spoom::Sorbet).srb_tc(
51
+ *arg,
52
+ path: path,
53
+ capture_err: true,
54
+ sorbet_bin: sorbet
55
+ )
56
+
57
+ check_sorbet_segfault(exit_code)
58
+
44
59
  if status
45
60
  say_error(output, status: nil, nl: false)
46
61
  exit(0)
@@ -99,7 +114,7 @@ module Spoom
99
114
  cyan = !cyan
100
115
  next
101
116
  end
102
- word << (cyan ? c.cyan : c.red)
117
+ word << (cyan ? cyan(c) : red(c))
103
118
  end
104
119
  word.string
105
120
  end
@@ -0,0 +1,45 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Spoom
5
+ class Color < T::Enum
6
+ extend T::Sig
7
+
8
+ enums do
9
+ CLEAR = new("\e[0m")
10
+ BOLD = new("\e[1m")
11
+
12
+ BLACK = new("\e[30m")
13
+ RED = new("\e[31m")
14
+ GREEN = new("\e[32m")
15
+ YELLOW = new("\e[33m")
16
+ BLUE = new("\e[34m")
17
+ MAGENTA = new("\e[35m")
18
+ CYAN = new("\e[36m")
19
+ WHITE = new("\e[37m")
20
+
21
+ LIGHT_BLACK = new("\e[90m")
22
+ LIGHT_RED = new("\e[91m")
23
+ LIGHT_GREEN = new("\e[92m")
24
+ LIGHT_YELLOW = new("\e[93m")
25
+ LIGHT_BLUE = new("\e[94m")
26
+ LIGHT_MAGENTA = new("\e[95m")
27
+ LIGHT_CYAN = new("\e[96m")
28
+ LIGHT_WHITE = new("\e[97m")
29
+ end
30
+
31
+ sig { returns(String) }
32
+ def ansi_code
33
+ serialize
34
+ end
35
+ end
36
+
37
+ module Colorize
38
+ extend T::Sig
39
+
40
+ sig { params(string: String, color: Color).returns(String) }
41
+ def set_color(string, *color)
42
+ "#{color.map(&:ansi_code).join}#{string}#{Color::CLEAR.ansi_code}"
43
+ end
44
+ end
45
+ end
@@ -156,7 +156,7 @@ module Spoom
156
156
  end
157
157
  print("\n")
158
158
  else
159
- print_colored(node.name, :blue)
159
+ print_colored(node.name, Color::BLUE)
160
160
  print("/")
161
161
  printn
162
162
  indent
@@ -180,15 +180,15 @@ module Spoom
180
180
  Spoom::Sorbet::Sigils.file_strictness(path)
181
181
  end
182
182
 
183
- sig { params(strictness: T.nilable(String)).returns(Symbol) }
183
+ sig { params(strictness: T.nilable(String)).returns(Color) }
184
184
  def strictness_color(strictness)
185
185
  case strictness
186
186
  when "false"
187
- :red
187
+ Color::RED
188
188
  when "true", "strict", "strong"
189
- :green
189
+ Color::GREEN
190
190
  else
191
- :uncolored
191
+ Color::CLEAR
192
192
  end
193
193
  end
194
194
  end
data/lib/spoom/printer.rb CHANGED
@@ -8,6 +8,8 @@ module Spoom
8
8
  extend T::Sig
9
9
  extend T::Helpers
10
10
 
11
+ include Colorize
12
+
11
13
  abstract!
12
14
 
13
15
  sig { returns(T.any(IO, StringIO)) }
@@ -42,11 +44,10 @@ module Spoom
42
44
  # Print `string` colored with `color` into `out`
43
45
  #
44
46
  # Does not use colors unless `@colors`.
45
- sig { params(string: T.nilable(String), color: Symbol, colors: Symbol).void }
46
- def print_colored(string, color, *colors)
47
+ sig { params(string: T.nilable(String), color: Color).void }
48
+ def print_colored(string, *color)
47
49
  return unless string
48
- string = colorize(string, color)
49
- colors.each { |c| string = colorize(string, c) }
50
+ string = T.unsafe(self).colorize(string, *color)
50
51
  @out.print(string)
51
52
  end
52
53
 
@@ -72,9 +73,10 @@ module Spoom
72
73
  end
73
74
 
74
75
  # Colorize `string` with color if `@colors`
75
- sig { params(string: String, color: Symbol).returns(String) }
76
- def colorize(string, color)
77
- @colors ? string.colorize(color) : string
76
+ sig { params(string: String, color: Spoom::Color).returns(String) }
77
+ def colorize(string, *color)
78
+ return string unless @colors
79
+ T.unsafe(self).set_color(string, *color)
78
80
  end
79
81
  end
80
82
  end
@@ -6,6 +6,8 @@ module Spoom
6
6
  module Errors
7
7
  extend T::Sig
8
8
 
9
+ DEFAULT_ERROR_URL_BASE = "https://srb.help/"
10
+
9
11
  # Parse errors from Sorbet output
10
12
  class Parser
11
13
  extend T::Sig
@@ -18,28 +20,16 @@ module Spoom
18
20
  "or set SORBET_SILENCE_DEV_MESSAGE=1 in your shell environment.",
19
21
  ]
20
22
 
21
- ERROR_LINE_MATCH_REGEX = %r{
22
- ^ # match beginning of line
23
- (\S[^:]*) # capture filename as something that starts with a non-space character
24
- # followed by anything that is not a colon character
25
- : # match the filename - line number seperator
26
- (\d+) # capture the line number
27
- :\s # match the line number - error message separator
28
- (.*) # capture the error message
29
- \shttps://srb.help/ # match the error code url prefix
30
- (\d+) # capture the error code
31
- $ # match end of line
32
- }x.freeze
33
-
34
- sig { params(output: String).returns(T::Array[Error]) }
35
- def self.parse_string(output)
36
- parser = Spoom::Sorbet::Errors::Parser.new
23
+ sig { params(output: String, error_url_base: String).returns(T::Array[Error]) }
24
+ def self.parse_string(output, error_url_base: DEFAULT_ERROR_URL_BASE)
25
+ parser = Spoom::Sorbet::Errors::Parser.new(error_url_base: error_url_base)
37
26
  parser.parse(output)
38
27
  end
39
28
 
40
- sig { void }
41
- def initialize
29
+ sig { params(error_url_base: String).void }
30
+ def initialize(error_url_base: DEFAULT_ERROR_URL_BASE)
42
31
  @errors = []
32
+ @error_line_match_regex = error_line_match_regexp(error_url_base)
43
33
  @current_error = nil
44
34
  end
45
35
 
@@ -66,9 +56,26 @@ module Spoom
66
56
 
67
57
  private
68
58
 
59
+ sig { params(error_url_base: String).returns(Regexp) }
60
+ def error_line_match_regexp(error_url_base)
61
+ url = Regexp.escape(error_url_base)
62
+ %r{
63
+ ^ # match beginning of line
64
+ (\S[^:]*) # capture filename as something that starts with a non-space character
65
+ # followed by anything that is not a colon character
66
+ : # match the filename - line number seperator
67
+ (\d+) # capture the line number
68
+ :\s # match the line number - error message separator
69
+ (.*) # capture the error message
70
+ \s#{url} # match the error code url prefix
71
+ (\d+) # capture the error code
72
+ $ # match end of line
73
+ }x
74
+ end
75
+
69
76
  sig { params(line: String).returns(T.nilable(Error)) }
70
77
  def match_error_line(line)
71
- match = line.match(ERROR_LINE_MATCH_REGEX)
78
+ match = line.match(@error_line_match_regex)
72
79
  return unless match
73
80
 
74
81
  file, line, message, code = match.captures
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require_relative "../../printer"
5
+ require "set"
5
6
 
6
7
  module Spoom
7
8
  module LSP
@@ -56,7 +57,7 @@ module Spoom
56
57
 
57
58
  sig { override.params(printer: SymbolPrinter).void }
58
59
  def accept_printer(printer)
59
- printer.print_colored("#{line}:#{char}", :light_black)
60
+ printer.print_colored("#{line}:#{char}", Color::LIGHT_BLACK)
60
61
  end
61
62
 
62
63
  def to_s
@@ -81,7 +82,7 @@ module Spoom
81
82
  sig { override.params(printer: SymbolPrinter).void }
82
83
  def accept_printer(printer)
83
84
  printer.print_object(start)
84
- printer.print_colored("-", :light_black)
85
+ printer.print_colored("-", Color::LIGHT_BLACK)
85
86
  printer.print_object(self.end)
86
87
  end
87
88
 
@@ -106,7 +107,7 @@ module Spoom
106
107
 
107
108
  sig { override.params(printer: SymbolPrinter).void }
108
109
  def accept_printer(printer)
109
- printer.print_colored("#{printer.clean_uri(uri)}:", :light_black)
110
+ printer.print_colored("#{printer.clean_uri(uri)}:", Color::LIGHT_BLACK)
110
111
  printer.print_object(range)
111
112
  end
112
113
 
@@ -203,14 +204,14 @@ module Spoom
203
204
  printer.printt
204
205
  printer.print(kind_string)
205
206
  printer.print(' ')
206
- printer.print_colored(name, :blue, :bold)
207
- printer.print_colored(' (', :light_black)
207
+ printer.print_colored(name, Color::BLUE, Color::BOLD)
208
+ printer.print_colored(' (', Color::LIGHT_BLACK)
208
209
  if range
209
210
  printer.print_object(range)
210
211
  elsif location
211
212
  printer.print_object(location)
212
213
  end
213
- printer.print_colored(')', :light_black)
214
+ printer.print_colored(')', Color::LIGHT_BLACK)
214
215
  printer.printn
215
216
  unless children.empty?
216
217
  printer.indent
data/lib/spoom/sorbet.rb CHANGED
@@ -15,6 +15,8 @@ module Spoom
15
15
  GEM_PATH = Gem::Specification.find_by_name("sorbet-static").full_gem_path
16
16
  BIN_PATH = (Pathname.new(GEM_PATH) / "libexec" / "sorbet").to_s
17
17
 
18
+ SEGFAULT_CODE = 139
19
+
18
20
  class << self
19
21
  extend T::Sig
20
22
 
@@ -24,7 +26,7 @@ module Spoom
24
26
  path: String,
25
27
  capture_err: T::Boolean,
26
28
  sorbet_bin: T.nilable(String)
27
- ).returns([String, T::Boolean])
29
+ ).returns([String, T::Boolean, Integer])
28
30
  end
29
31
  def srb(*arg, path: '.', capture_err: false, sorbet_bin: nil)
30
32
  if sorbet_bin
@@ -41,7 +43,7 @@ module Spoom
41
43
  path: String,
42
44
  capture_err: T::Boolean,
43
45
  sorbet_bin: T.nilable(String)
44
- ).returns([String, T::Boolean])
46
+ ).returns([String, T::Boolean, Integer])
45
47
  end
46
48
  def srb_tc(*arg, path: '.', capture_err: false, sorbet_bin: nil)
47
49
  arg.prepend("tc") unless sorbet_bin
@@ -62,9 +62,9 @@ module Spoom
62
62
  # Actions
63
63
 
64
64
  # Run `git init` in this project
65
- sig { void }
66
- def git_init
67
- Spoom::Git.exec("git init -q", path: path)
65
+ sig { params(branch: String).void }
66
+ def git_init(branch: "main")
67
+ Spoom::Git.exec("git init -q -b #{branch}", path: path)
68
68
  Spoom::Git.exec("git config user.name 'spoom-tests'", path: path)
69
69
  Spoom::Git.exec("git config user.email 'spoom@shopify.com'", path: path)
70
70
  end
data/lib/spoom/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Spoom
5
- VERSION = "1.1.4"
5
+ VERSION = "1.1.8"
6
6
  end
data/lib/spoom.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
- require "colorize"
5
4
  require "sorbet-runtime"
5
+ require "pathname"
6
6
 
7
7
  module Spoom
8
8
  extend T::Sig
@@ -17,16 +17,19 @@ module Spoom
17
17
  arg: String,
18
18
  path: String,
19
19
  capture_err: T::Boolean
20
- ).returns([String, T::Boolean])
20
+ ).returns([String, T::Boolean, Integer])
21
21
  end
22
22
  def self.exec(cmd, *arg, path: '.', capture_err: false)
23
23
  method = capture_err ? "popen2e" : "popen2"
24
- Open3.send(method, [cmd, *arg].join(" "), chdir: path) do |_, o, t|
25
- [o.read, T.cast(t.value, Process::Status).success?]
24
+ Open3.send(method, [cmd, *arg].join(" "), chdir: path) do |_, stdout, thread|
25
+ out = stdout.read
26
+ status = T.cast(thread.value, Process::Status)
27
+ [out, status.success?, status.exitstatus]
26
28
  end
27
29
  end
28
30
  end
29
31
 
32
+ require "spoom/colors"
30
33
  require "spoom/sorbet"
31
34
  require "spoom/cli"
32
35
  require "spoom/version"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spoom
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.4
4
+ version: 1.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre Terrasa
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-10-06 00:00:00.000000000 Z
11
+ date: 2022-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -53,21 +53,21 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '5.0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: sorbet-runtime
56
+ name: minitest-reporters
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: 0.5.9204
62
- type: :runtime
61
+ version: '0'
62
+ type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: 0.5.9204
68
+ version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: sorbet
70
+ name: sorbet-runtime
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -81,33 +81,33 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: 0.5.9204
83
83
  - !ruby/object:Gem::Dependency
84
- name: thor
84
+ name: sorbet
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: 0.19.2
89
+ version: 0.5.9204
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: 0.19.2
96
+ version: 0.5.9204
97
97
  - !ruby/object:Gem::Dependency
98
- name: colorize
98
+ name: thor
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
102
102
  - !ruby/object:Gem::Version
103
- version: '0'
103
+ version: 0.19.2
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
- version: '0'
110
+ version: 0.19.2
111
111
  description:
112
112
  email:
113
113
  - ruby@shopify.com
@@ -128,6 +128,7 @@ files:
128
128
  - lib/spoom/cli/helper.rb
129
129
  - lib/spoom/cli/lsp.rb
130
130
  - lib/spoom/cli/run.rb
131
+ - lib/spoom/colors.rb
131
132
  - lib/spoom/coverage.rb
132
133
  - lib/spoom/coverage/d3.rb
133
134
  - lib/spoom/coverage/d3/base.rb