how_is 18.0.3 → 18.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -1
- data/.rubocop.yml +36 -0
- data/.travis.yml +1 -0
- data/CHANGELOG.md +17 -0
- data/Gemfile +2 -8
- data/Gemfile.lock +110 -0
- data/README.md +0 -5
- data/Rakefile +9 -4
- data/exe/how_is +3 -2
- data/how_is.gemspec +3 -2
- data/lib/how_is/analyzer.rb +20 -16
- data/lib/how_is/cli.rb +14 -1
- data/lib/how_is/fetcher.rb +52 -44
- data/lib/how_is/pulse.rb +7 -4
- data/lib/how_is/report/base_report.rb +14 -11
- data/lib/how_is/report/html.rb +47 -44
- data/lib/how_is/report/json.rb +2 -0
- data/lib/how_is/report.rb +3 -1
- data/lib/how_is/version.rb +3 -1
- data/lib/how_is.rb +28 -8
- metadata +22 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: aa512f40b28b33221fb83a6db55d83efe39e5b5c3f5a5160a5eabb1441a58638
|
4
|
+
data.tar.gz: 4dc0c71bd1a56bb32e29efebbbc7405b21456566b47ae8b4b1fcc8ebafe90cae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f2ffd1e34fa07420631add37aa8b084956f58b6b8d67d14ccb4d58942f60a167ad9c7106bd14ce3c53db73498255e1542c2e56559733fccc05dfba157876c46
|
7
|
+
data.tar.gz: 40a4bf011b459a31a7ab9e9fedf50b8672f64f8ba3d5e62fa4492260eab05aed27f88b882f87583e128fc2fcf0891508229be723c4d75399dbb43735e0dac9de
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,8 +1,22 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.3
|
3
|
+
Exclude:
|
4
|
+
- 'how_is.gemspec'
|
5
|
+
- 'bin/*'
|
6
|
+
- '**/*~'
|
7
|
+
- 'spec/capture_warnings.rb'
|
8
|
+
- 'lib/how_is/cli.rb' # FIXME: Make HowIs::CLI.parse not a disaster.
|
9
|
+
|
1
10
|
# Exceptions should inherit from StandardError.
|
2
11
|
# (RuboCop default is to inherit from RuntimeError.)
|
3
12
|
Lint/InheritException:
|
4
13
|
EnforcedStyle: standard_error
|
5
14
|
|
15
|
+
Metrics/BlockLength:
|
16
|
+
Exclude:
|
17
|
+
- 'spec/**/*_spec.rb'
|
18
|
+
|
19
|
+
|
6
20
|
# The guiding principle of classes is SRP, SRP can't be accurately measured by LoC
|
7
21
|
#Metrics/ClassLength:
|
8
22
|
# Max: 1500
|
@@ -13,16 +27,32 @@ Lint/InheritException:
|
|
13
27
|
# It may be worth revisiting this in the future and refactoring those lines.
|
14
28
|
Metrics/LineLength:
|
15
29
|
Max: 120
|
30
|
+
AllowHeredoc: true
|
16
31
|
|
17
32
|
# Too short methods lead to extraction of single-use methods, which can make
|
18
33
|
# the code easier to read (by naming things), but can also clutter the class
|
19
34
|
Metrics/MethodLength:
|
20
35
|
Max: 20
|
21
36
|
|
37
|
+
Style/Alias:
|
38
|
+
EnforcedStyle: prefer_alias_method
|
39
|
+
|
22
40
|
# Most readable form.
|
23
41
|
Style/AlignHash:
|
24
42
|
EnforcedHashRocketStyle: table
|
25
43
|
EnforcedColonStyle: table
|
44
|
+
# Disable because it wound up conflicting with a lot of things like:
|
45
|
+
# foo('bar',
|
46
|
+
# baz: 'asdf',
|
47
|
+
# beep: 'boop')
|
48
|
+
#
|
49
|
+
# I suspect these'll disappear when overarching architectural issues are
|
50
|
+
# addressed.
|
51
|
+
Enabled: false
|
52
|
+
|
53
|
+
Style/AlignParameters:
|
54
|
+
# See Style/AlignedHash.
|
55
|
+
Enabled: false
|
26
56
|
|
27
57
|
# This codebase may be English, but some English words contain diacritics.
|
28
58
|
Style/AsciiComments:
|
@@ -86,6 +116,8 @@ Style/PercentLiteralDelimiters:
|
|
86
116
|
Enabled: true
|
87
117
|
PreferredDelimiters:
|
88
118
|
default: "[]"
|
119
|
+
'%w': '[]'
|
120
|
+
'%W': '[]'
|
89
121
|
|
90
122
|
# `has_key?` and `has_value?` are clearer than `key?` and `value?`.
|
91
123
|
Style/PreferredHashMethods:
|
@@ -115,6 +147,10 @@ Style/StringLiterals:
|
|
115
147
|
Enabled: false
|
116
148
|
#EnforcedStyle: double_quotes
|
117
149
|
|
150
|
+
# TODO: Maybe make it so you have to do [:foo, :bar] not %i[foo bar]?
|
151
|
+
Style/SymbolArray:
|
152
|
+
Enabled: false
|
153
|
+
|
118
154
|
# Require parentheses around complex ternary conditions.
|
119
155
|
Style/TernaryParentheses:
|
120
156
|
Enabled: true
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -9,6 +9,23 @@ this project adheres to [Semantic Versioning](http://semver.org).
|
|
9
9
|
|
10
10
|
(Nothing so far.)
|
11
11
|
|
12
|
+
## [v18.0.4]
|
13
|
+
|
14
|
+
This release ([snapshot](https://github.com/how-is/how_is/tree/v18.0.4))
|
15
|
+
is exclusively cleaning up RuboCop violations and updating dependencies.
|
16
|
+
There should be no noticeable changes in functionality.
|
17
|
+
|
18
|
+
### Miscellaneous
|
19
|
+
|
20
|
+
* Use Hashie stable; update Gemfile/add Gemfile.lock. ([#170](https://github.com/how-is/how_is/pull/170))
|
21
|
+
* Pass -w to Ruby when running 'rake spec'. ([#169](https://github.com/how-is/how_is/pull/169))
|
22
|
+
* Rubocop cleanup. ([#167](https://github.com/how-is/how_is/pull/167))
|
23
|
+
* Gemfile: use Hashie from master branch. ([#166](https://github.com/how-is/how_is/pull/166))
|
24
|
+
* Update github_api, contracst to latest. ([#165](https://github.com/how-is/how_is/pull/165))
|
25
|
+
* Fix (a significant number of) RuboCop violations. ([#162](https://github.com/how-is/how_is/pull/162))
|
26
|
+
* README: Drop from_config_file reference. ([#161](https://github.com/how-is/how_is/pull/161))
|
27
|
+
* Move rubocop dependency to gemspec. ([#160](https://github.com/how-is/how_is/pull/160))
|
28
|
+
|
12
29
|
## [v18.0.3]
|
13
30
|
|
14
31
|
This release ([snapshot](https://github.com/how-is/how_is/tree/v18.0.3))
|
data/Gemfile
CHANGED
@@ -1,12 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
source 'https://rubygems.org'
|
2
4
|
|
3
5
|
# Specify your gem's dependencies in how_is.gemspec
|
4
6
|
gemspec
|
5
|
-
|
6
|
-
# Everything says to put it here. It feels like it should go in the gemspec?
|
7
|
-
# SOMEBODY WHO KNOWS WHAT THEY'RE DOING PLEASE LET ME KNOW WHAT TO DO HERE.
|
8
|
-
group :test, :development do
|
9
|
-
# Matches version used by Hound, even though there's newer releases.
|
10
|
-
# https://github.com/houndci/linters/blob/master/Gemfile.lock
|
11
|
-
gem 'rubocop', '~> 0.46.0', require: false
|
12
|
-
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
how_is (18.0.4)
|
5
|
+
contracts (~> 0.16.0)
|
6
|
+
github_api (~> 0.17.0)
|
7
|
+
slop (~> 4.4.1)
|
8
|
+
tessellator-fetcher (~> 5.0.0)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
addressable (2.4.0)
|
14
|
+
ast (2.3.0)
|
15
|
+
contracts (0.16.0)
|
16
|
+
crack (0.4.3)
|
17
|
+
safe_yaml (~> 1.0.0)
|
18
|
+
curl_cacert (1.0.0)
|
19
|
+
default (1.0.0)
|
20
|
+
descendants_tracker (0.0.4)
|
21
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
22
|
+
diff-lcs (1.3)
|
23
|
+
faraday (0.9.2)
|
24
|
+
multipart-post (>= 1.2, < 3)
|
25
|
+
github_api (0.17.0)
|
26
|
+
addressable (~> 2.4.0)
|
27
|
+
descendants_tracker (~> 0.0.4)
|
28
|
+
faraday (~> 0.8, < 0.10)
|
29
|
+
hashie (>= 3.4)
|
30
|
+
mime-types (>= 1.16, < 3.0)
|
31
|
+
oauth2 (~> 1.0)
|
32
|
+
hashdiff (0.3.4)
|
33
|
+
hashie (3.5.6)
|
34
|
+
heresy (4.0.0)
|
35
|
+
default (~> 1.0.0)
|
36
|
+
heresy-string (~> 1.0.0)
|
37
|
+
net-socket (~> 1.0.0)
|
38
|
+
heresy-string (1.0.0)
|
39
|
+
jwt (1.5.6)
|
40
|
+
mayhaps (0.3.0)
|
41
|
+
mime-types (2.99.3)
|
42
|
+
multi_json (1.12.1)
|
43
|
+
multi_xml (0.6.0)
|
44
|
+
multipart-post (2.0.0)
|
45
|
+
net-socket (1.0.0)
|
46
|
+
oauth2 (1.4.0)
|
47
|
+
faraday (>= 0.8, < 0.13)
|
48
|
+
jwt (~> 1.0)
|
49
|
+
multi_json (~> 1.3)
|
50
|
+
multi_xml (~> 0.5)
|
51
|
+
rack (>= 1.2, < 3)
|
52
|
+
openssl-better_defaults (0.0.1)
|
53
|
+
parser (2.4.0.0)
|
54
|
+
ast (~> 2.2)
|
55
|
+
powerpack (0.1.1)
|
56
|
+
rack (2.0.3)
|
57
|
+
rainbow (2.2.2)
|
58
|
+
rake
|
59
|
+
rake (11.3.0)
|
60
|
+
rspec (3.6.0)
|
61
|
+
rspec-core (~> 3.6.0)
|
62
|
+
rspec-expectations (~> 3.6.0)
|
63
|
+
rspec-mocks (~> 3.6.0)
|
64
|
+
rspec-core (3.6.0)
|
65
|
+
rspec-support (~> 3.6.0)
|
66
|
+
rspec-expectations (3.6.0)
|
67
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
68
|
+
rspec-support (~> 3.6.0)
|
69
|
+
rspec-mocks (3.6.0)
|
70
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
71
|
+
rspec-support (~> 3.6.0)
|
72
|
+
rspec-support (3.6.0)
|
73
|
+
rubocop (0.47.1)
|
74
|
+
parser (>= 2.3.3.1, < 3.0)
|
75
|
+
powerpack (~> 0.1)
|
76
|
+
rainbow (>= 1.99.1, < 3.0)
|
77
|
+
ruby-progressbar (~> 1.7)
|
78
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
79
|
+
ruby-progressbar (1.8.1)
|
80
|
+
safe_yaml (1.0.4)
|
81
|
+
slop (4.4.3)
|
82
|
+
tessellator-fetcher (5.0.1)
|
83
|
+
curl_cacert
|
84
|
+
heresy (~> 4.0.0)
|
85
|
+
mayhaps (~> 0.3.0)
|
86
|
+
openssl-better_defaults
|
87
|
+
thread_safe (0.3.6)
|
88
|
+
timecop (0.8.1)
|
89
|
+
unicode-display_width (1.3.0)
|
90
|
+
vcr (3.0.3)
|
91
|
+
webmock (3.0.1)
|
92
|
+
addressable (>= 2.3.6)
|
93
|
+
crack (>= 0.3.2)
|
94
|
+
hashdiff
|
95
|
+
|
96
|
+
PLATFORMS
|
97
|
+
ruby
|
98
|
+
|
99
|
+
DEPENDENCIES
|
100
|
+
bundler (~> 1.11)
|
101
|
+
how_is!
|
102
|
+
rake (~> 11.2)
|
103
|
+
rspec (~> 3.5)
|
104
|
+
rubocop (~> 0.47.0)
|
105
|
+
timecop (~> 0.8.1)
|
106
|
+
vcr (~> 3.0)
|
107
|
+
webmock
|
108
|
+
|
109
|
+
BUNDLED WITH
|
110
|
+
1.15.1
|
data/README.md
CHANGED
@@ -102,11 +102,6 @@ Every value under `reports` is a format string, so you can do e.g.
|
|
102
102
|
report = HowIs.new('<orgname>/<reponame>').to_html
|
103
103
|
File.open('report.html', 'w') { |f| f.puts report }
|
104
104
|
|
105
|
-
# Generate a report from a config file located at ./how_is.yml.
|
106
|
-
# Example config file: https://github.com/how-is/how-is-rubygems/blob/gh-pages/how_is.yml
|
107
|
-
require 'yaml'
|
108
|
-
HowIs.from_config_file(YAML.load_file('how_is.yml'))
|
109
|
-
|
110
105
|
# Generate a report from a config Hash.
|
111
106
|
HowIs.from_config({
|
112
107
|
repository: '<orgname>/<reponame>',
|
data/Rakefile
CHANGED
@@ -1,16 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'bundler/gem_tasks'
|
2
4
|
require 'rspec/core/rake_task'
|
3
5
|
require 'timecop'
|
4
|
-
#require 'vcr'
|
5
6
|
require './spec/vcr_helper.rb'
|
6
7
|
require 'how_is'
|
7
8
|
|
8
|
-
RSpec::Core::RakeTask.new(:spec)
|
9
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
10
|
+
# Warning.warn() was added in Ruby 2.4.0, so don't use -w on older versions.
|
11
|
+
t.ruby_opts = '-w -r./spec/capture_warnings.rb' if RUBY_VERSION >= '2.4.0'
|
12
|
+
end
|
9
13
|
|
10
14
|
task :default => :spec
|
11
15
|
|
16
|
+
# Helper functions used later in the Rakefile.
|
12
17
|
class HelperFunctions
|
13
|
-
def self.freeze_time(&
|
18
|
+
def self.freeze_time(&_block)
|
14
19
|
date = DateTime.parse('2016-11-01').new_offset(0)
|
15
20
|
Timecop.freeze(date) do
|
16
21
|
yield
|
@@ -26,7 +31,7 @@ class HelperFunctions
|
|
26
31
|
format: format,
|
27
32
|
}
|
28
33
|
|
29
|
-
cassette = repository.
|
34
|
+
cassette = repository.tr('/', '-')
|
30
35
|
VCR.use_cassette(cassette) do
|
31
36
|
report = HowIs.generate_report(**options)
|
32
37
|
end
|
data/exe/how_is
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
3
5
|
require "how_is"
|
4
6
|
require "how_is/cli"
|
5
7
|
|
6
8
|
begin
|
7
|
-
result
|
9
|
+
result = HowIs::CLI.parse(ARGV)
|
8
10
|
rescue HowIs::CLI::OptionsError => e
|
9
11
|
if ENV['SHOW_TRACE']
|
10
12
|
raise
|
@@ -42,7 +44,6 @@ begin
|
|
42
44
|
HowIs::Report.to_format_based_on(options[:report], report)
|
43
45
|
)
|
44
46
|
end
|
45
|
-
|
46
47
|
rescue => e
|
47
48
|
if ENV['SHOW_TRACE']
|
48
49
|
raise
|
data/how_is.gemspec
CHANGED
@@ -19,8 +19,8 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.add_runtime_dependency "github_api", "~> 0.
|
23
|
-
spec.add_runtime_dependency "contracts", "~> 0.
|
22
|
+
spec.add_runtime_dependency "github_api", "~> 0.17.0"
|
23
|
+
spec.add_runtime_dependency "contracts", "~> 0.16.0"
|
24
24
|
spec.add_runtime_dependency "slop", "~> 4.4.1"
|
25
25
|
|
26
26
|
spec.add_runtime_dependency "tessellator-fetcher", "~> 5.0.0"
|
@@ -31,4 +31,5 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.add_development_dependency "timecop", "~> 0.8.1"
|
32
32
|
spec.add_development_dependency "vcr", "~> 3.0"
|
33
33
|
spec.add_development_dependency "webmock"
|
34
|
+
spec.add_development_dependency "rubocop", "~> 0.47.0"
|
34
35
|
end
|
data/lib/how_is/analyzer.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'contracts'
|
2
4
|
require 'ostruct'
|
3
5
|
require 'date'
|
@@ -52,7 +54,7 @@ class HowIs
|
|
52
54
|
newest_issue: issue_or_pull_to_hash(newest_for(issues)),
|
53
55
|
newest_pull: issue_or_pull_to_hash(newest_for(pulls)),
|
54
56
|
|
55
|
-
pulse: data.pulse
|
57
|
+
pulse: data.pulse
|
56
58
|
)
|
57
59
|
end
|
58
60
|
|
@@ -61,11 +63,11 @@ class HowIs
|
|
61
63
|
#
|
62
64
|
# @param data [Hash] The hash to generate an Analysis from.
|
63
65
|
def self.from_hash(data)
|
64
|
-
hash = data.map
|
66
|
+
hash = data.map { |k, v|
|
65
67
|
v = DateTime.parse(v) if k.end_with?('_date')
|
66
68
|
|
67
69
|
[k, v]
|
68
|
-
|
70
|
+
}.to_h
|
69
71
|
|
70
72
|
hash.keys.each do |key|
|
71
73
|
next unless hash[key].is_a?(Hash) && hash[key]['date']
|
@@ -116,7 +118,7 @@ class HowIs
|
|
116
118
|
def average_age_for(issues_or_pulls)
|
117
119
|
return nil if issues_or_pulls.empty?
|
118
120
|
|
119
|
-
ages = issues_or_pulls.map {|iop| time_ago_in_seconds(iop['created_at'])}
|
121
|
+
ages = issues_or_pulls.map { |iop| time_ago_in_seconds(iop['created_at']) }
|
120
122
|
raw_average = ages.reduce(:+) / ages.length
|
121
123
|
|
122
124
|
seconds_in_a_year = 31_556_926
|
@@ -140,24 +142,25 @@ class HowIs
|
|
140
142
|
[months, "month"],
|
141
143
|
[weeks, "week"],
|
142
144
|
[days, "day"],
|
143
|
-
].reject {|(v,
|
144
|
-
k
|
145
|
+
].reject { |(v, _)| v == 0 }.map { |(v, k)|
|
146
|
+
k += 's' if v != 1
|
145
147
|
[v, k]
|
146
148
|
}
|
147
149
|
|
148
|
-
most_significant = values[0, 2].map {|x| x.join(" ")}
|
150
|
+
most_significant = values[0, 2].map { |x| x.join(" ") }
|
149
151
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
152
|
+
value =
|
153
|
+
if most_significant.length < 2
|
154
|
+
most_significant.first
|
155
|
+
else
|
156
|
+
most_significant.join(" and ")
|
157
|
+
end
|
155
158
|
|
156
159
|
"approximately #{value}"
|
157
160
|
end
|
158
161
|
|
159
162
|
def sort_iops_by_created_at(issues_or_pulls)
|
160
|
-
issues_or_pulls.sort_by {|x| DateTime.parse(x['created_at']) }
|
163
|
+
issues_or_pulls.sort_by { |x| DateTime.parse(x['created_at']) }
|
161
164
|
end
|
162
165
|
|
163
166
|
# Given an Array of issues or pulls, return the oldest.
|
@@ -181,15 +184,16 @@ class HowIs
|
|
181
184
|
DateTime.parse(issue_or_pull['created_at'])
|
182
185
|
end
|
183
186
|
|
184
|
-
|
187
|
+
private
|
188
|
+
|
185
189
|
# Takes an Array of labels, and returns amodified list that includes links
|
186
190
|
# to each label.
|
187
191
|
def with_label_links(labels, repository)
|
188
|
-
labels.map
|
192
|
+
labels.map { |label, num_issues|
|
189
193
|
label_link = "https://github.com/#{repository}/issues?q=" + CGI.escape("is:open is:issue label:\"#{label}\"")
|
190
194
|
|
191
195
|
[label, {'link' => label_link, 'total' => num_issues}]
|
192
|
-
|
196
|
+
}.to_h
|
193
197
|
end
|
194
198
|
|
195
199
|
# Returns how many seconds ago a date (as a String) was.
|
data/lib/how_is/cli.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
# NOPE THIS IS BROKEN // frozen_string_literal: // true
|
2
|
+
|
1
3
|
require "how_is"
|
2
4
|
require "slop"
|
3
5
|
|
4
6
|
class HowIs::CLI
|
5
|
-
DEFAULT_REPORT_FILE = "report.#{HowIs::DEFAULT_FORMAT}"
|
7
|
+
DEFAULT_REPORT_FILE = "report.#{HowIs::DEFAULT_FORMAT}".freeze
|
6
8
|
|
7
9
|
# Parent class of all exceptions raised in HowIs::CLI.
|
8
10
|
class OptionsError < StandardError
|
@@ -44,6 +46,14 @@ class HowIs::CLI
|
|
44
46
|
opts.separator ""
|
45
47
|
opts.separator "Options:"
|
46
48
|
|
49
|
+
# The extra spaces make this a lot easier to comprehend, so we don't want
|
50
|
+
# RuboCop to complain about them.
|
51
|
+
#
|
52
|
+
# Same for line length.
|
53
|
+
#
|
54
|
+
# rubocop:disable Style/SpaceBeforeFirstArg
|
55
|
+
# rubocop:disable Metrics/LineLength
|
56
|
+
|
47
57
|
# Allowed arguments:
|
48
58
|
opts.bool "-h", "--help", "Print help text"
|
49
59
|
opts.string "--config", "YAML config file, used to generate a group of reports"
|
@@ -51,6 +61,9 @@ class HowIs::CLI
|
|
51
61
|
opts.string "--report", "output file for the report (valid extensions: #{HowIs.supported_formats.join(', ')}; default: #{DEFAULT_REPORT_FILE})"
|
52
62
|
opts.bool "-v", "--version", "prints the version"
|
53
63
|
|
64
|
+
# rubocop:enable Style/SpaceBeforeFirstArg
|
65
|
+
# rubocop:enable Metrics/LineLength
|
66
|
+
|
54
67
|
# Parse the arguments.
|
55
68
|
parser = Slop::Parser.new(opts)
|
56
69
|
result = parser.parse(argv)
|
data/lib/how_is/fetcher.rb
CHANGED
@@ -1,59 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'contracts'
|
2
4
|
require 'github_api'
|
3
5
|
require 'how_is/pulse'
|
4
6
|
|
5
7
|
##
|
6
8
|
# Fetches data from GitHub.
|
7
|
-
class HowIs
|
8
|
-
|
9
|
-
|
10
|
-
##
|
11
|
-
# Standardized representation for fetcher results.
|
12
|
-
#
|
13
|
-
# Implemented as a class instead of passing around a Hash so that it can
|
14
|
-
# be more easily referenced by Contracts.
|
15
|
-
class Results < Struct.new(:repository, :issues, :pulls, :pulse)
|
9
|
+
class HowIs
|
10
|
+
class Fetcher
|
16
11
|
include Contracts::Core
|
17
12
|
|
18
|
-
|
19
|
-
|
20
|
-
|
13
|
+
##
|
14
|
+
# Standardized representation for fetcher results.
|
15
|
+
#
|
16
|
+
# Implemented as a class instead of passing around a Hash so that it can
|
17
|
+
# be more easily referenced by Contracts.
|
18
|
+
class Results < Struct.new(:repository, :issues, :pulls, :pulse)
|
19
|
+
include Contracts::Core
|
20
|
+
|
21
|
+
Contract String, C::ArrayOf[Hash], C::ArrayOf[Hash], String => nil
|
22
|
+
def initialize(repository, issues, pulls, pulse)
|
23
|
+
super(repository, issues, pulls, pulse)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Struct defines #to_h, but not #to_hash, so we alias them.
|
27
|
+
alias_method :to_hash, :to_h
|
21
28
|
end
|
22
29
|
|
23
|
-
|
24
|
-
|
25
|
-
|
30
|
+
##
|
31
|
+
# Fetches repository information from GitHub and returns a Results object.
|
32
|
+
Contract String,
|
33
|
+
C::Or[C::RespondTo[:issues, :pulls], nil],
|
34
|
+
C::Or[C::RespondTo[:html_summary], nil] => Results
|
35
|
+
def call(repository,
|
36
|
+
github = nil,
|
37
|
+
pulse = nil)
|
38
|
+
github ||= Github.new(auto_pagination: true)
|
39
|
+
pulse ||= HowIs::Pulse.new(repository)
|
40
|
+
user, repo = repository.split('/', 2)
|
26
41
|
|
42
|
+
unless user && repo
|
43
|
+
raise HowIs::CLI::OptionsError, 'To generate a report from GitHub, ' \
|
44
|
+
'provide the repository ' \
|
45
|
+
'username/project. Quitting!'
|
46
|
+
end
|
27
47
|
|
28
|
-
|
29
|
-
|
30
|
-
Contract String,
|
31
|
-
C::Or[C::RespondTo[:issues, :pulls], nil],
|
32
|
-
C::Or[C::RespondTo[:html_summary], nil] => Results
|
33
|
-
def call(repository,
|
34
|
-
github = nil,
|
35
|
-
pulse = nil)
|
36
|
-
github ||= Github.new(auto_pagination: true)
|
37
|
-
pulse ||= HowIs::Pulse.new(repository)
|
38
|
-
user, repo = repository.split('/', 2)
|
39
|
-
raise HowIs::CLI::OptionsError, 'To generate a report from GitHub, ' \
|
40
|
-
'provide the repository username/project. ' \
|
41
|
-
'Quitting!' unless user && repo
|
42
|
-
issues = github.issues.list user: user, repo: repo
|
43
|
-
pulls = github.pulls.list user: user, repo: repo
|
44
|
-
|
45
|
-
summary = pulse.html_summary
|
46
|
-
|
47
|
-
Results.new(
|
48
|
-
repository,
|
49
|
-
obj_to_array_of_hashes(issues),
|
50
|
-
obj_to_array_of_hashes(pulls),
|
51
|
-
summary,
|
52
|
-
)
|
53
|
-
end
|
48
|
+
issues = github.issues.list user: user, repo: repo
|
49
|
+
pulls = github.pulls.list user: user, repo: repo
|
54
50
|
|
55
|
-
|
56
|
-
|
57
|
-
|
51
|
+
summary = pulse.html_summary
|
52
|
+
|
53
|
+
Results.new(
|
54
|
+
repository,
|
55
|
+
obj_to_array_of_hashes(issues),
|
56
|
+
obj_to_array_of_hashes(pulls),
|
57
|
+
summary
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def obj_to_array_of_hashes(object)
|
64
|
+
object.to_a.map(&:to_h)
|
65
|
+
end
|
58
66
|
end
|
59
67
|
end
|
data/lib/how_is/pulse.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'tessellator/fetcher'
|
2
4
|
|
3
5
|
class HowIs
|
@@ -19,7 +21,7 @@ class HowIs
|
|
19
21
|
|
20
22
|
# Gets the HTML Pulse summary.
|
21
23
|
def html_summary
|
22
|
-
parts =
|
24
|
+
parts =
|
23
25
|
@pulse_page_response.body
|
24
26
|
.split('<div class="section diffstat-summary">')
|
25
27
|
|
@@ -34,10 +36,11 @@ class HowIs
|
|
34
36
|
.strip
|
35
37
|
end
|
36
38
|
|
37
|
-
|
39
|
+
private
|
40
|
+
|
38
41
|
# Fetch Pulse page from GitHub for scraping.
|
39
|
-
def fetch_pulse!(repository
|
40
|
-
Tessellator::Fetcher.new.call('get', "https://github.com/#{repository}/pulse
|
42
|
+
def fetch_pulse!(repository)
|
43
|
+
Tessellator::Fetcher.new.call('get', "https://github.com/#{repository}/pulse/monthly")
|
41
44
|
end
|
42
45
|
end
|
43
46
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'json'
|
2
4
|
|
3
5
|
class HowIs
|
@@ -27,7 +29,7 @@ class HowIs
|
|
27
29
|
issue_or_pr_summary "issue", "issue"
|
28
30
|
|
29
31
|
header "Issues Per Label"
|
30
|
-
issues_per_label = analysis.issues_with_label.to_a.sort_by { |(
|
32
|
+
issues_per_label = analysis.issues_with_label.to_a.sort_by { |(_, v)| v['total'].to_i }.reverse
|
31
33
|
issues_per_label.map! do |label, hash|
|
32
34
|
[label, hash['total'], hash['link']]
|
33
35
|
end
|
@@ -50,37 +52,37 @@ class HowIs
|
|
50
52
|
|
51
53
|
##
|
52
54
|
# Appends a title to the report.
|
53
|
-
def title(
|
55
|
+
def title(_content)
|
54
56
|
raise NotImplementedError
|
55
57
|
end
|
56
58
|
|
57
59
|
##
|
58
60
|
# Appends a header to the report.
|
59
|
-
def header(
|
61
|
+
def header(_content)
|
60
62
|
raise NotImplementedError
|
61
63
|
end
|
62
64
|
|
63
65
|
##
|
64
66
|
# Appends a line of text to the report.
|
65
|
-
def text(
|
67
|
+
def text(_content)
|
66
68
|
raise NotImplementedError
|
67
69
|
end
|
68
70
|
|
69
71
|
##
|
70
72
|
# Appends a link to the report.
|
71
|
-
def link(
|
73
|
+
def link(_content, _url)
|
72
74
|
raise NotImplementedError
|
73
75
|
end
|
74
76
|
|
75
77
|
##
|
76
78
|
# Appends an unordered list to the report.
|
77
|
-
def unordered_list(
|
79
|
+
def unordered_list(_arr)
|
78
80
|
raise NotImplementedError
|
79
81
|
end
|
80
82
|
|
81
83
|
##
|
82
84
|
# Appends a horizontal bar graph to the report.
|
83
|
-
def horizontal_bar_graph(
|
85
|
+
def horizontal_bar_graph(_data)
|
84
86
|
raise NotImplementedError
|
85
87
|
end
|
86
88
|
|
@@ -94,26 +96,27 @@ class HowIs
|
|
94
96
|
# Exports a report to a file.
|
95
97
|
#
|
96
98
|
# NOTE: May be removed in the future.
|
97
|
-
def export_file(
|
99
|
+
def export_file(_file)
|
98
100
|
raise NotImplementedError
|
99
101
|
end
|
100
102
|
|
101
103
|
def to_h
|
102
104
|
analysis.to_h
|
103
105
|
end
|
104
|
-
|
106
|
+
alias_method :to_hash, :to_h
|
105
107
|
|
106
108
|
def to_json
|
107
109
|
JSON.pretty_generate(to_h)
|
108
110
|
end
|
109
111
|
|
110
112
|
private
|
113
|
+
|
111
114
|
def pluralize(text, number)
|
112
|
-
number == 1 ? text : "#{text}s"
|
115
|
+
(number == 1) ? text : "#{text}s"
|
113
116
|
end
|
114
117
|
|
115
118
|
def are_is(number)
|
116
|
-
number == 1 ? "is" : "are"
|
119
|
+
(number == 1) ? "is" : "are"
|
117
120
|
end
|
118
121
|
|
119
122
|
def issue_or_pr_summary(type, type_label)
|
data/lib/how_is/report/html.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cgi'
|
2
4
|
require 'how_is/report/base_report'
|
3
5
|
|
@@ -7,21 +9,21 @@ class HowIs
|
|
7
9
|
:html
|
8
10
|
end
|
9
11
|
|
10
|
-
def title(
|
11
|
-
@title =
|
12
|
-
@r += "\n<h1>#{
|
12
|
+
def title(content)
|
13
|
+
@title = content
|
14
|
+
@r += "\n<h1>#{content}</h1>\n"
|
13
15
|
end
|
14
16
|
|
15
|
-
def header(
|
16
|
-
@r += "\n<h2>#{
|
17
|
+
def header(content)
|
18
|
+
@r += "\n<h2>#{content}</h2>\n"
|
17
19
|
end
|
18
20
|
|
19
|
-
def link(
|
20
|
-
%
|
21
|
+
def link(content, url)
|
22
|
+
%[<a href="#{url}">#{content}</a>]
|
21
23
|
end
|
22
24
|
|
23
|
-
def text(
|
24
|
-
@r += "<p>#{
|
25
|
+
def text(content)
|
26
|
+
@r += "<p>#{content}</p>\n"
|
25
27
|
end
|
26
28
|
|
27
29
|
def unordered_list(arr)
|
@@ -46,13 +48,14 @@ class HowIs
|
|
46
48
|
|
47
49
|
@r += "<table class=\"horizontal-bar-graph\">\n"
|
48
50
|
data.each do |row|
|
49
|
-
percentage = get_percentage.(row[1])
|
51
|
+
percentage = get_percentage.call(row[1])
|
50
52
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
label_text =
|
54
|
+
if row[2]
|
55
|
+
link(row[0], row[2])
|
56
|
+
else
|
57
|
+
row[0]
|
58
|
+
end
|
56
59
|
|
57
60
|
@r += <<-EOF
|
58
61
|
<tr>
|
@@ -74,35 +77,35 @@ class HowIs
|
|
74
77
|
report = export
|
75
78
|
|
76
79
|
File.open(file, 'w') do |f|
|
77
|
-
f.puts
|
78
|
-
<!DOCTYPE html>
|
79
|
-
<html>
|
80
|
-
<head>
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
</head>
|
100
|
-
<body>
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
</body>
|
105
|
-
</html>
|
80
|
+
f.puts <<~EOF
|
81
|
+
<!DOCTYPE html>
|
82
|
+
<html>
|
83
|
+
<head>
|
84
|
+
<title>#{@title}</title>
|
85
|
+
<style>
|
86
|
+
body { font: sans-serif; }
|
87
|
+
main {
|
88
|
+
max-width: 600px;
|
89
|
+
max-width: 72ch;
|
90
|
+
margin: auto;
|
91
|
+
}
|
92
|
+
|
93
|
+
.horizontal-bar-graph {
|
94
|
+
position: relative;
|
95
|
+
width: 100%;
|
96
|
+
}
|
97
|
+
.horizontal-bar-graph .fill {
|
98
|
+
display: inline-block;
|
99
|
+
background: #CCC;
|
100
|
+
}
|
101
|
+
</style>
|
102
|
+
</head>
|
103
|
+
<body>
|
104
|
+
<main>
|
105
|
+
#{report}
|
106
|
+
</main>
|
107
|
+
</body>
|
108
|
+
</html>
|
106
109
|
EOF
|
107
110
|
end
|
108
111
|
end
|
data/lib/how_is/report/json.rb
CHANGED
data/lib/how_is/report.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'date'
|
2
4
|
require "pathname"
|
3
5
|
|
@@ -63,7 +65,6 @@ class HowIs
|
|
63
65
|
report.public_send("to_#{report_format}")
|
64
66
|
end
|
65
67
|
|
66
|
-
private
|
67
68
|
# Given a format name (+format+), returns the corresponding <blah>Report
|
68
69
|
# class.
|
69
70
|
def self.get_report_class(format)
|
@@ -73,5 +74,6 @@ class HowIs
|
|
73
74
|
|
74
75
|
HowIs.const_get(class_name)
|
75
76
|
end
|
77
|
+
private_class_method :get_report_class
|
76
78
|
end
|
77
79
|
end
|
data/lib/how_is/version.rb
CHANGED
data/lib/how_is.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'how_is/version'
|
2
4
|
require 'contracts'
|
3
5
|
require 'cacert'
|
@@ -54,7 +56,7 @@ class HowIs
|
|
54
56
|
# @return [HowIs] A HowIs object that can be used for generating other
|
55
57
|
# reports, treating the JSON report as a cache.
|
56
58
|
def self.from_json(json)
|
57
|
-
|
59
|
+
from_hash(JSON.parse(json))
|
58
60
|
end
|
59
61
|
|
60
62
|
##
|
@@ -67,7 +69,7 @@ class HowIs
|
|
67
69
|
def self.from_hash(data)
|
68
70
|
analysis = HowIs::Analyzer.from_hash(data)
|
69
71
|
|
70
|
-
|
72
|
+
new(analysis.repository, analysis)
|
71
73
|
end
|
72
74
|
|
73
75
|
##
|
@@ -77,7 +79,7 @@ class HowIs
|
|
77
79
|
# generate.
|
78
80
|
def self.supported_formats
|
79
81
|
report_constants = HowIs.constants.grep(/.Report/) - [:BaseReport]
|
80
|
-
report_constants.map {|x| x.to_s.split('Report').first.downcase }
|
82
|
+
report_constants.map { |x| x.to_s.split('Report').first.downcase }
|
81
83
|
end
|
82
84
|
|
83
85
|
##
|
@@ -93,6 +95,8 @@ class HowIs
|
|
93
95
|
|
94
96
|
# Generate an analysis.
|
95
97
|
# TODO: This may make more sense as Analysis.new().
|
98
|
+
# TODO: Nothing overrides +fetcher+ and +analyzer+. Remove ability to do so.
|
99
|
+
# FIXME: THIS CODE AND EVERYTHING ASSOCIATED WITH IT IS A FUCKING ATROCITY.
|
96
100
|
Contract C::KeywordArgs[repository: String,
|
97
101
|
fetcher: C::Optional[Class],
|
98
102
|
analyzer: C::Optional[Class],
|
@@ -119,7 +123,9 @@ class HowIs
|
|
119
123
|
report_data = convert_keys(report_data, :to_sym)
|
120
124
|
|
121
125
|
frontmatter = frontmatter.map { |k, v|
|
122
|
-
|
126
|
+
# Sometimes report_data has unused keys, which generates a warning, but
|
127
|
+
# we're okay with it.
|
128
|
+
v = silence_warnings { v % report_data }
|
123
129
|
|
124
130
|
[k, v]
|
125
131
|
}.to_h
|
@@ -142,7 +148,6 @@ class HowIs
|
|
142
148
|
report_class ||= HowIs::Report
|
143
149
|
|
144
150
|
date = Date.strptime(Time.now.to_i.to_s, '%s')
|
145
|
-
date_string = date.strftime('%Y-%m-%d')
|
146
151
|
friendly_date = date.strftime('%B %d, %y')
|
147
152
|
|
148
153
|
analysis = HowIs.generate_analysis(repository: config['repository'], github: github)
|
@@ -156,7 +161,9 @@ class HowIs
|
|
156
161
|
generated_reports = {}
|
157
162
|
|
158
163
|
config['reports'].map do |format, report_config|
|
159
|
-
|
164
|
+
# Sometimes report_data has unused keys, which generates a warning, but
|
165
|
+
# we're okay with it.
|
166
|
+
filename = silence_warnings { report_config['filename'] % report_data }
|
160
167
|
file = File.join(report_config['directory'], filename)
|
161
168
|
|
162
169
|
report = report_class.export(analysis, format)
|
@@ -187,11 +194,24 @@ class HowIs
|
|
187
194
|
str.string
|
188
195
|
end
|
189
196
|
|
190
|
-
private
|
191
197
|
# convert_keys({'foo' => 'bar'}, :to_sym)
|
192
198
|
# => {:foo => 'bar'}
|
193
199
|
def self.convert_keys(data, method_name)
|
194
|
-
data.map {|k, v| [k.send(method_name), v]}.to_h
|
200
|
+
data.map { |k, v| [k.send(method_name), v] }.to_h
|
195
201
|
end
|
202
|
+
private_class_method :convert_keys
|
196
203
|
|
204
|
+
def self.silence_warnings(&block)
|
205
|
+
with_warnings(nil, &block)
|
206
|
+
end
|
207
|
+
private_class_method :silence_warnings
|
208
|
+
|
209
|
+
def self.with_warnings(flag, &_block)
|
210
|
+
old_verbose = $VERBOSE
|
211
|
+
$VERBOSE = flag
|
212
|
+
yield
|
213
|
+
ensure
|
214
|
+
$VERBOSE = old_verbose
|
215
|
+
end
|
216
|
+
private_class_method :with_warnings
|
197
217
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: how_is
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 18.0.
|
4
|
+
version: 18.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ellen Marie Dash
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-07-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: github_api
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.17.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.17.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: contracts
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.16.0
|
34
34
|
type: :runtime
|
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: 0.
|
40
|
+
version: 0.16.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: slop
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -150,6 +150,20 @@ dependencies:
|
|
150
150
|
- - ">="
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rubocop
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: 0.47.0
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: 0.47.0
|
153
167
|
description:
|
154
168
|
email:
|
155
169
|
- me@duckie.co
|
@@ -166,6 +180,7 @@ files:
|
|
166
180
|
- CHANGELOG.md
|
167
181
|
- CODE_OF_CONDUCT.md
|
168
182
|
- Gemfile
|
183
|
+
- Gemfile.lock
|
169
184
|
- ISSUES.md
|
170
185
|
- LICENSE.txt
|
171
186
|
- README.md
|
@@ -211,7 +226,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
211
226
|
version: '0'
|
212
227
|
requirements: []
|
213
228
|
rubyforge_project:
|
214
|
-
rubygems_version: 2.6.
|
229
|
+
rubygems_version: 2.6.12
|
215
230
|
signing_key:
|
216
231
|
specification_version: 4
|
217
232
|
summary: Quantify the health of a GitHub repository.
|