spoom 1.0.4 → 1.0.9

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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -1
  3. data/README.md +296 -1
  4. data/Rakefile +1 -0
  5. data/lib/spoom.rb +21 -2
  6. data/lib/spoom/cli.rb +56 -10
  7. data/lib/spoom/cli/bump.rb +138 -0
  8. data/lib/spoom/cli/config.rb +51 -0
  9. data/lib/spoom/cli/coverage.rb +206 -0
  10. data/lib/spoom/cli/helper.rb +149 -0
  11. data/lib/spoom/cli/lsp.rb +165 -0
  12. data/lib/spoom/cli/run.rb +109 -0
  13. data/lib/spoom/coverage.rb +89 -0
  14. data/lib/spoom/coverage/d3.rb +110 -0
  15. data/lib/spoom/coverage/d3/base.rb +50 -0
  16. data/lib/spoom/coverage/d3/circle_map.rb +195 -0
  17. data/lib/spoom/coverage/d3/pie.rb +175 -0
  18. data/lib/spoom/coverage/d3/timeline.rb +486 -0
  19. data/lib/spoom/coverage/report.rb +308 -0
  20. data/lib/spoom/coverage/snapshot.rb +132 -0
  21. data/lib/spoom/file_tree.rb +196 -0
  22. data/lib/spoom/git.rb +98 -0
  23. data/lib/spoom/printer.rb +80 -0
  24. data/lib/spoom/sorbet.rb +99 -47
  25. data/lib/spoom/sorbet/config.rb +30 -0
  26. data/lib/spoom/sorbet/errors.rb +33 -15
  27. data/lib/spoom/sorbet/lsp.rb +2 -4
  28. data/lib/spoom/sorbet/lsp/structures.rb +108 -14
  29. data/lib/spoom/sorbet/metrics.rb +10 -79
  30. data/lib/spoom/sorbet/sigils.rb +98 -0
  31. data/lib/spoom/test_helpers/project.rb +112 -0
  32. data/lib/spoom/timeline.rb +53 -0
  33. data/lib/spoom/version.rb +2 -2
  34. data/templates/card.erb +8 -0
  35. data/templates/card_snapshot.erb +22 -0
  36. data/templates/page.erb +50 -0
  37. metadata +28 -11
  38. data/lib/spoom/cli/commands/base.rb +0 -36
  39. data/lib/spoom/cli/commands/config.rb +0 -67
  40. data/lib/spoom/cli/commands/lsp.rb +0 -156
  41. data/lib/spoom/cli/commands/run.rb +0 -92
  42. data/lib/spoom/cli/symbol_printer.rb +0 -71
  43. data/lib/spoom/config.rb +0 -11
@@ -0,0 +1,112 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "fileutils"
5
+ require "open3"
6
+ require "pathname"
7
+
8
+ require_relative "../git"
9
+
10
+ module Spoom
11
+ module TestHelpers
12
+ # A simple project abstraction for testing purposes
13
+ class Project
14
+ extend T::Sig
15
+
16
+ # The absolute path to this test project
17
+ sig { returns(String) }
18
+ attr_reader :path
19
+
20
+ # Create a new test project at `path`
21
+ sig { params(path: String).void }
22
+ def initialize(path)
23
+ @path = path
24
+ FileUtils.rm_rf(@path)
25
+ FileUtils.mkdir_p(@path)
26
+ end
27
+
28
+ # Content
29
+
30
+ # Set the content of the Gemfile in this project
31
+ sig { params(content: String).void }
32
+ def gemfile(content)
33
+ write("Gemfile", content)
34
+ end
35
+
36
+ # Set the content of `sorbet/config` in this project
37
+ sig { params(content: String).void }
38
+ def sorbet_config(content)
39
+ write("sorbet/config", content)
40
+ end
41
+
42
+ # Write `content` in the file at `rel_path`
43
+ sig { params(rel_path: String, content: String).void }
44
+ def write(rel_path, content = "")
45
+ path = absolute_path(rel_path)
46
+ FileUtils.mkdir_p(File.dirname(path))
47
+ File.write(path, content)
48
+ end
49
+
50
+ # Remove `rel_path`
51
+ sig { params(rel_path: String).void }
52
+ def remove(rel_path)
53
+ FileUtils.rm_rf(absolute_path(rel_path))
54
+ end
55
+
56
+ # List all files in this project
57
+ sig { returns(T::Array[String]) }
58
+ def files
59
+ Dir.glob("#{@path}/**/*").sort
60
+ end
61
+
62
+ # Actions
63
+
64
+ # Run `git init` in this project
65
+ sig { void }
66
+ def git_init
67
+ Spoom::Git.exec("git init -q", path: path)
68
+ Spoom::Git.exec("git config user.name 'spoom-tests'", path: path)
69
+ Spoom::Git.exec("git config user.email 'spoom@shopify.com'", path: path)
70
+ end
71
+
72
+ # Commit all new changes in this project
73
+ sig { params(message: String, date: Time).void }
74
+ def commit(message = "message", date: Time.now.utc)
75
+ Spoom::Git.exec("git add --all", path: path)
76
+ Spoom::Git.exec("GIT_COMMITTER_DATE=\"#{date}\" git commit -m '#{message}' --date '#{date}'", path: path)
77
+ end
78
+
79
+ # Run `bundle install` in this project
80
+ sig { returns([T.nilable(String), T.nilable(String), T::Boolean]) }
81
+ def bundle_install
82
+ opts = {}
83
+ opts[:chdir] = path
84
+ out, err, status = Open3.capture3("bundle", "install", opts)
85
+ [out, err, status.success?]
86
+ end
87
+
88
+ # Run a command with `bundle exec` in this project
89
+ sig { params(cmd: String, args: String).returns([T.nilable(String), T.nilable(String), T::Boolean]) }
90
+ def bundle_exec(cmd, *args)
91
+ opts = {}
92
+ opts[:chdir] = path
93
+ out, err, status = Open3.capture3(["bundle", "exec", cmd, *args].join(' '), opts)
94
+ [out, err, status.success?]
95
+ end
96
+
97
+ # Delete this project and its content
98
+ sig { void }
99
+ def destroy
100
+ FileUtils.rm_rf(path)
101
+ end
102
+
103
+ private
104
+
105
+ # Create an absolute path from `self.path` and `rel_path`
106
+ sig { params(rel_path: String).returns(String) }
107
+ def absolute_path(rel_path)
108
+ (Pathname.new(path) / rel_path).to_s
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,53 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "git"
5
+
6
+ module Spoom
7
+ class Timeline
8
+ extend T::Sig
9
+
10
+ sig { params(from: Time, to: Time, path: String).void }
11
+ def initialize(from, to, path: ".")
12
+ @from = from
13
+ @to = to
14
+ @path = path
15
+ end
16
+
17
+ # Return one commit for each month between `from` and `to`
18
+ sig { returns(T::Array[String]) }
19
+ def ticks
20
+ commits_for_dates(months)
21
+ end
22
+
23
+ # Return all months between `from` and `to`
24
+ sig { returns(T::Array[Time]) }
25
+ def months
26
+ d = Date.new(@from.year, @from.month, 1)
27
+ to = Date.new(@to.year, @to.month, 1)
28
+ res = [d.to_time]
29
+ while d < to
30
+ d = d.next_month
31
+ res << d.to_time
32
+ end
33
+ res
34
+ end
35
+
36
+ # Return one commit for each date in `dates`
37
+ sig { params(dates: T::Array[Time]).returns(T::Array[String]) }
38
+ def commits_for_dates(dates)
39
+ dates.map do |t|
40
+ out, _, _ = Spoom::Git.log(
41
+ "--since='#{t}'",
42
+ "--until='#{t.to_date.next_month}'",
43
+ "--format='format:%h'",
44
+ "--author-date-order",
45
+ "-1",
46
+ path: @path,
47
+ )
48
+ next if out.empty?
49
+ out
50
+ end.compact.uniq
51
+ end
52
+ end
53
+ end
data/lib/spoom/version.rb CHANGED
@@ -1,6 +1,6 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Spoom
5
- VERSION = "1.0.4"
5
+ VERSION = "1.0.9"
6
6
  end
@@ -0,0 +1,8 @@
1
+ <div class="card">
2
+ <% if title %>
3
+ <h5 class="card-header"><%= title %></h5>
4
+ <% end %>
5
+ <div class="card-body">
6
+ <%= body %>
7
+ </div>
8
+ </div>
@@ -0,0 +1,22 @@
1
+ <div class="card">
2
+ <% if title %>
3
+ <h5 class="card-header"><%= title %></h5>
4
+ <% end %>
5
+ <div class="card-body">
6
+ <div class="container-fluid">
7
+ <div class="row justify-content-md-center">
8
+ <div class="col-12 col-sm-4 col-xl-3">
9
+ <%= pie_sigils.html %>
10
+ </div>
11
+ <div class="d-none d-xl-block col-xl-1"></div>
12
+ <div class="col-12 col-sm-4 col-xl-3">
13
+ <%= pie_calls.html %>
14
+ </div>
15
+ <div class="d-none d-xl-block col-xl-1"></div>
16
+ <div class="col-12 col-sm-4 col-xl-3">
17
+ <%= pie_sigs.html %>
18
+ </div>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </div>
@@ -0,0 +1,50 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta http-equiv="x-ua-compatible" content="ie=edge" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+
8
+ <title><%= title %></title>
9
+ <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
10
+
11
+ <style>
12
+ .card {
13
+ margin: 30px 0 0 0;
14
+ }
15
+
16
+ .footer {
17
+ color: #6c757d;
18
+ text-align: center;
19
+ margin: 30px 0 0 0;
20
+ }
21
+
22
+ <%= header_style %>
23
+ </style>
24
+ </head>
25
+ <body>
26
+ <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
27
+ <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
28
+ <script src="https://d3js.org/d3.v4.min.js"></script>
29
+
30
+ <script>
31
+ <%= header_script %>
32
+ </script>
33
+
34
+ <div class="px-5 py-4 container-fluid">
35
+ <div class="row justify-content-center">
36
+ <div class="col-xs-12 col-md-12 col-lg-9 col-xl-8">
37
+ <div class="header">
38
+ <%= header_html %>
39
+ </div>
40
+ <div class="body">
41
+ <%= body_html %>
42
+ </div>
43
+ <div class="footer">
44
+ <%= footer_html %>
45
+ </div>
46
+ </div>
47
+ </div>
48
+ </div>
49
+ </body>
50
+ </html>
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.0.4
4
+ version: 1.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre Terrasa
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-08-05 00:00:00.000000000 Z
11
+ date: 2021-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: 13.0.1
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: 13.0.1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -110,7 +110,7 @@ dependencies:
110
110
  version: '0'
111
111
  description:
112
112
  email:
113
- - alexandre.terrasa@shopify.com
113
+ - ruby@shopify.com
114
114
  executables:
115
115
  - spoom
116
116
  extensions: []
@@ -122,12 +122,23 @@ files:
122
122
  - exe/spoom
123
123
  - lib/spoom.rb
124
124
  - lib/spoom/cli.rb
125
- - lib/spoom/cli/commands/base.rb
126
- - lib/spoom/cli/commands/config.rb
127
- - lib/spoom/cli/commands/lsp.rb
128
- - lib/spoom/cli/commands/run.rb
129
- - lib/spoom/cli/symbol_printer.rb
130
- - lib/spoom/config.rb
125
+ - lib/spoom/cli/bump.rb
126
+ - lib/spoom/cli/config.rb
127
+ - lib/spoom/cli/coverage.rb
128
+ - lib/spoom/cli/helper.rb
129
+ - lib/spoom/cli/lsp.rb
130
+ - lib/spoom/cli/run.rb
131
+ - lib/spoom/coverage.rb
132
+ - lib/spoom/coverage/d3.rb
133
+ - lib/spoom/coverage/d3/base.rb
134
+ - lib/spoom/coverage/d3/circle_map.rb
135
+ - lib/spoom/coverage/d3/pie.rb
136
+ - lib/spoom/coverage/d3/timeline.rb
137
+ - lib/spoom/coverage/report.rb
138
+ - lib/spoom/coverage/snapshot.rb
139
+ - lib/spoom/file_tree.rb
140
+ - lib/spoom/git.rb
141
+ - lib/spoom/printer.rb
131
142
  - lib/spoom/sorbet.rb
132
143
  - lib/spoom/sorbet/config.rb
133
144
  - lib/spoom/sorbet/errors.rb
@@ -136,7 +147,13 @@ files:
136
147
  - lib/spoom/sorbet/lsp/errors.rb
137
148
  - lib/spoom/sorbet/lsp/structures.rb
138
149
  - lib/spoom/sorbet/metrics.rb
150
+ - lib/spoom/sorbet/sigils.rb
151
+ - lib/spoom/test_helpers/project.rb
152
+ - lib/spoom/timeline.rb
139
153
  - lib/spoom/version.rb
154
+ - templates/card.erb
155
+ - templates/card_snapshot.erb
156
+ - templates/page.erb
140
157
  homepage: https://github.com/Shopify/spoom
141
158
  licenses:
142
159
  - MIT
@@ -1,36 +0,0 @@
1
- # typed: true
2
- # frozen_string_literal: true
3
-
4
- require 'stringio'
5
-
6
- module Spoom
7
- module Cli
8
- module Commands
9
- class Base < Thor
10
- no_commands do
11
- def say_error(message, status = "Error")
12
- status = set_color(status, :red)
13
-
14
- buffer = StringIO.new
15
- buffer << "#{status}: #{message}"
16
- buffer << "\n" unless message.end_with?("\n")
17
-
18
- $stderr.print(buffer.string)
19
- $stderr.flush
20
- end
21
-
22
- def in_sorbet_project?
23
- File.file?(Spoom::Config::SORBET_CONFIG)
24
- end
25
-
26
- def in_sorbet_project!
27
- unless in_sorbet_project?
28
- say_error("not in a Sorbet project (no sorbet/config)")
29
- exit(1)
30
- end
31
- end
32
- end
33
- end
34
- end
35
- end
36
- end
@@ -1,67 +0,0 @@
1
- # typed: true
2
- # frozen_string_literal: true
3
-
4
- require_relative "base"
5
- require_relative "../../sorbet/config"
6
-
7
- module Spoom
8
- module Cli
9
- module Commands
10
- class Config < Base
11
- default_task :show
12
-
13
- desc "show", "show Sorbet config"
14
- def show
15
- in_sorbet_project!
16
- config = Spoom::Sorbet::Config.parse_file(Spoom::Config::SORBET_CONFIG)
17
-
18
- say("Found Sorbet config at `#{Spoom::Config::SORBET_CONFIG}`.")
19
-
20
- say("\nPaths typechecked:")
21
- if config.paths.empty?
22
- say(" * (default: .)")
23
- else
24
- config.paths.each do |path|
25
- say(" * #{path}")
26
- end
27
- end
28
-
29
- say("\nPaths ignored:")
30
- if config.ignore.empty?
31
- say(" * (default: none)")
32
- else
33
- config.ignore.each do |path|
34
- say(" * #{path}")
35
- end
36
- end
37
-
38
- say("\nAllowed extensions:")
39
- if config.allowed_extensions.empty?
40
- say(" * .rb (default)")
41
- say(" * .rbi (default)")
42
- else
43
- config.allowed_extensions.each do |ext|
44
- say(" * #{ext}")
45
- end
46
- end
47
- end
48
-
49
- desc "files", "show files matching Sorbet config"
50
- def files
51
- in_sorbet_project!
52
- config = Spoom::Sorbet::Config.parse_file(Spoom::Config::SORBET_CONFIG)
53
- files = Spoom::Sorbet.srb_files(config)
54
-
55
- say("Files matching `#{Spoom::Config::SORBET_CONFIG}`:")
56
- if files.empty?
57
- say(" NONE")
58
- else
59
- files.each do |path|
60
- say(" * #{path}")
61
- end
62
- end
63
- end
64
- end
65
- end
66
- end
67
- end