how_is 18.0.4 → 18.0.5

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.
data/lib/how_is/pulse.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'tessellator/fetcher'
3
+ require "tessellator/fetcher"
4
4
 
5
5
  class HowIs
6
6
  # This entire class is a monstrous hack, because GitHub doesn't provide
@@ -14,33 +14,34 @@ class HowIs
14
14
  @pulse_page_response = fetch_pulse!(repository)
15
15
  end
16
16
 
17
- # This is probably dead code.
18
- def text_summary
19
- raise NotImplementedError
20
- end
21
-
22
17
  # Gets the HTML Pulse summary.
23
18
  def html_summary
24
- parts =
25
- @pulse_page_response.body
26
- .split('<div class="section diffstat-summary">')
27
-
28
- if parts.length == 1
29
- return "There hasn't been any activity on #{@repository} in the last month."
19
+ if stats_section?
20
+ stats_html_fragment.gsub('<a href="/', '<a href="https://github.com/')
21
+ else
22
+ "There hasn't been any activity on #{@repository} in the last month."
30
23
  end
31
-
32
- parts
33
- .last
34
- .split('</div>').first
35
- .gsub('<a href="/', '<a href="https://github.com/')
36
- .strip
37
24
  end
38
25
 
39
26
  private
40
27
 
28
+ HTML_SEPARATOR_FOR_STATS = '<div class="section diffstat-summary">'
29
+
30
+ def stats_section?
31
+ parts.count > 1
32
+ end
33
+
34
+ def parts
35
+ @parts ||= @pulse_page_response.body.split(HTML_SEPARATOR_FOR_STATS)
36
+ end
37
+
38
+ def stats_html_fragment
39
+ parts.last.split("</div>").first.strip
40
+ end
41
+
41
42
  # Fetch Pulse page from GitHub for scraping.
42
43
  def fetch_pulse!(repository)
43
- Tessellator::Fetcher.new.call('get', "https://github.com/#{repository}/pulse/monthly")
44
+ Tessellator::Fetcher.new.call("get", "https://github.com/#{repository}/pulse/monthly")
44
45
  end
45
46
  end
46
47
  end
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json'
3
+ require "json"
4
4
 
5
5
  class HowIs
6
+ BaseReport = Struct.new(:analysis)
7
+
6
8
  ##
7
9
  # Subclasses of BaseReport represent complete reports.
8
- class BaseReport < Struct.new(:analysis)
10
+ class BaseReport
9
11
  def generate_report_text!
10
12
  # title, text, header, horizontal_bar_graph, etc,
11
13
  # append to @r, which is returned at the end of the function.
@@ -29,11 +31,11 @@ class HowIs
29
31
  issue_or_pr_summary "issue", "issue"
30
32
 
31
33
  header "Issues Per Label"
32
- issues_per_label = analysis.issues_with_label.to_a.sort_by { |(_, v)| v['total'].to_i }.reverse
34
+ issues_per_label = analysis.issues_with_label.to_a.sort_by { |(_, v)| v["total"].to_i }.reverse
33
35
  issues_per_label.map! do |label, hash|
34
- [label, hash['total'], hash['link']]
36
+ [label, hash["total"], hash["link"]]
35
37
  end
36
- issues_per_label << ["(No label)", analysis.issues_with_no_label['total'], nil]
38
+ issues_per_label << ["(No label)", analysis.issues_with_no_label["total"], nil]
37
39
  horizontal_bar_graph issues_per_label
38
40
 
39
41
  # See comment at beginning of function.
@@ -123,19 +125,20 @@ class HowIs
123
125
  date_format = "%b %e, %Y"
124
126
  a = analysis
125
127
 
126
- number_of_type = a.send("number_of_#{type}s")
128
+ number_of_type = a.public_send("number_of_#{type}s")
127
129
 
128
- type_link = a.send("#{type}s_url")
129
- oldest = a.send("oldest_#{type}")
130
- newest = a.send("newest_#{type}")
130
+ type_link = a.public_send("#{type}s_url")
131
+ oldest = a.public_send("oldest_#{type}")
132
+ newest = a.public_send("newest_#{type}")
131
133
 
132
- if number_of_type == 0
134
+ if number_of_type.zero?
133
135
  text "There are #{link("no #{type_label}s open", type_link)}."
134
136
  else
135
- text "There #{are_is(number_of_type)} #{link("#{number_of_type} #{pluralize(type_label, number_of_type)} open", type_link)}."
137
+ text "There #{are_is(number_of_type)} #{link("#{number_of_type} "\
138
+ "#{pluralize(type_label, number_of_type)} open", type_link)}."
136
139
 
137
140
  unordered_list [
138
- "Average age: #{a.send("average_#{type}_age")}.",
141
+ "Average age: #{a.public_send("average_#{type}_age")}.",
139
142
  "#{link('Oldest ' + type_label, oldest['html_url'])} was opened on #{oldest['date'].strftime(date_format)}.",
140
143
  "#{link('Newest ' + type_label, newest['html_url'])} was opened on #{newest['date'].strftime(date_format)}.",
141
144
  ]
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cgi'
4
- require 'how_is/report/base_report'
3
+ require "cgi"
4
+ require "how_is/report/base_report"
5
5
 
6
6
  class HowIs
7
+ # HTML Report implementation
7
8
  class HtmlReport < BaseReport
8
9
  def format
9
10
  :html
@@ -34,6 +35,14 @@ class HowIs
34
35
  @r += "</ul>\n\n"
35
36
  end
36
37
 
38
+ ROW_HTML_GRAPH = <<-EOF
39
+ <tr>
40
+ <td style="width: %{label_width}">%{label_text}</td>
41
+ <td><span class="fill" style="width: %{percentage}%%">%{link_text}</span></td>
42
+ </tr>
43
+
44
+ EOF
45
+
37
46
  def horizontal_bar_graph(data)
38
47
  if data.length == 1 && data[0][0] == "(No label)"
39
48
  text "There are no open issues to graph."
@@ -48,65 +57,63 @@ class HowIs
48
57
 
49
58
  @r += "<table class=\"horizontal-bar-graph\">\n"
50
59
  data.each do |row|
51
- percentage = get_percentage.call(row[1])
52
-
53
- label_text =
54
- if row[2]
55
- link(row[0], row[2])
56
- else
57
- row[0]
58
- end
59
-
60
- @r += <<-EOF
61
- <tr>
62
- <td style="width: #{label_width}">#{label_text}</td>
63
- <td><span class="fill" style="width: #{percentage}%">#{row[1]}</span></td>
64
- </tr>
65
-
66
- EOF
60
+ @r += Kernel.format(ROW_HTML_GRAPH, label_width: label_width,
61
+ label_text: label_text_for(row),
62
+ percentage: get_percentage.call(row[1]),
63
+ link_text: row[1])
67
64
  end
68
65
  @r += "</table>\n"
69
66
  end
70
67
 
71
68
  def export
72
- @r = ''
69
+ @r = ""
73
70
  generate_report_text!
74
71
  end
75
72
 
73
+ HTML_DOC_TEMPLATE = <<~EOF
74
+ <!DOCTYPE html>
75
+ <html>
76
+ <head>
77
+ <title>%{title}</title>
78
+ <style>
79
+ body { font: sans-serif; }
80
+ main {
81
+ max-width: 600px;
82
+ max-width: 72ch;
83
+ margin: auto;
84
+ }
85
+ .horizontal-bar-graph {
86
+ position: relative;
87
+ width: 100%;
88
+ }
89
+ .horizontal-bar-graph .fill {
90
+ display: inline-block;
91
+ background: #CCC;
92
+ }
93
+ </style>
94
+ </head>
95
+ <body>
96
+ <main>
97
+ %{report}
98
+ </main>
99
+ </body>
100
+ </html>
101
+ EOF
102
+
76
103
  def export_file(file)
77
- report = export
78
-
79
- File.open(file, 'w') do |f|
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>
109
- EOF
104
+ content = Kernel.format(HTML_DOC_TEMPLATE, title: @title, report: export)
105
+ File.open(file, "w") do |f|
106
+ f.puts content
107
+ end
108
+ end
109
+
110
+ private
111
+
112
+ def label_text_for(row)
113
+ if row[2]
114
+ link(row[0], row[2])
115
+ else
116
+ row[0]
110
117
  end
111
118
  end
112
119
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'how_is/report/base_report'
3
+ require "how_is/report/base_report"
4
4
 
5
5
  class HowIs
6
6
  ##
@@ -26,7 +26,7 @@ class HowIs
26
26
  ##
27
27
  # Generates a report and writes it to a file.
28
28
  def export_file(file)
29
- File.open(file, 'w') do |f|
29
+ File.open(file, "w") do |f|
30
30
  f.write export
31
31
  end
32
32
  end
data/lib/how_is/report.rb CHANGED
@@ -1,23 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'date'
3
+ require "date"
4
4
  require "pathname"
5
5
 
6
6
  class HowIs
7
+ # Raised when attempting to export to an unsupported format
7
8
  class UnsupportedExportFormat < StandardError
8
9
  def initialize(format)
9
10
  super("Unsupported export format: #{format}")
10
11
  end
11
12
  end
12
13
 
14
+ # Report control class with class methods to make reports for an analysis
15
+ # or to save reports in files, or otherwise interact with the files.
13
16
  class Report
14
- require 'how_is/report/json'
15
- require 'how_is/report/html'
17
+ require "how_is/report/json"
18
+ require "how_is/report/html"
16
19
 
17
20
  ##
18
21
  # Export a report to a file.
19
22
  def self.export_file(analysis, file)
20
- format = file.split('.').last
23
+ format = file.split(".").last
21
24
  report = get_report_class(format).new(analysis)
22
25
 
23
26
  report.export_file(file)
@@ -37,7 +40,7 @@ class HowIs
37
40
  # @param file [String,Pathname] Name of file to write to
38
41
  # @param report [Report] Report to store
39
42
  def self.save_report(file, report)
40
- File.open(file, 'w') do |f|
43
+ File.open(file, "w") do |f|
41
44
  f.write report
42
45
  end
43
46
  end
@@ -49,7 +52,7 @@ class HowIs
49
52
  #
50
53
  # @return [String] Report format inferred from file name
51
54
  def self.infer_format(file)
52
- Pathname(file).extname.delete('.')
55
+ Pathname(file).extname.delete(".")
53
56
  end
54
57
 
55
58
  ##
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class HowIs
4
- VERSION = "18.0.4"
4
+ VERSION = "18.0.5"
5
5
  end
data/lib/how_is.rb CHANGED
@@ -1,19 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'how_is/version'
4
- require 'contracts'
5
- require 'cacert'
3
+ require "how_is/version"
4
+ require "contracts"
5
+ require "cacert"
6
6
 
7
7
  Cacert.set_in_env
8
8
 
9
9
  C = Contracts
10
10
 
11
+ # HowIs control class used from the CLI tool.
12
+ #
13
+ # Generates an analysis and has methods to build reports from it.
11
14
  class HowIs
12
15
  include Contracts::Core
13
16
 
14
- require 'how_is/fetcher'
15
- require 'how_is/analyzer'
16
- require 'how_is/report'
17
+ require "how_is/fetcher"
18
+ require "how_is/analyzer"
19
+ require "how_is/report"
17
20
 
18
21
  DEFAULT_FORMAT = :html
19
22
 
@@ -79,7 +82,7 @@ class HowIs
79
82
  # generate.
80
83
  def self.supported_formats
81
84
  report_constants = HowIs.constants.grep(/.Report/) - [:BaseReport]
82
- report_constants.map { |x| x.to_s.split('Report').first.downcase }
85
+ report_constants.map { |x| x.to_s.split("Report").first.downcase }
83
86
  end
84
87
 
85
88
  ##
@@ -90,7 +93,7 @@ class HowIs
90
93
  # if it can't.
91
94
  def self.can_export_to?(file)
92
95
  # TODO: Check if the file is writable?
93
- supported_formats.include?(file.split('.').last)
96
+ supported_formats.include?(file.split(".").last)
94
97
  end
95
98
 
96
99
  # Generate an analysis.
@@ -147,28 +150,28 @@ class HowIs
147
150
  report_class: nil)
148
151
  report_class ||= HowIs::Report
149
152
 
150
- date = Date.strptime(Time.now.to_i.to_s, '%s')
151
- friendly_date = date.strftime('%B %d, %y')
153
+ date = Date.strptime(Time.now.to_i.to_s, "%s")
154
+ friendly_date = date.strftime("%B %d, %y")
152
155
 
153
- analysis = HowIs.generate_analysis(repository: config['repository'], github: github)
156
+ analysis = HowIs.generate_analysis(repository: config["repository"], github: github)
154
157
 
155
158
  report_data = {
156
- repository: config['repository'],
159
+ repository: config["repository"],
157
160
  date: date,
158
161
  friendly_date: friendly_date,
159
162
  }
160
163
 
161
164
  generated_reports = {}
162
165
 
163
- config['reports'].map do |format, report_config|
166
+ config["reports"].map do |format, report_config|
164
167
  # Sometimes report_data has unused keys, which generates a warning, but
165
168
  # we're okay with it.
166
- filename = silence_warnings { report_config['filename'] % report_data }
167
- file = File.join(report_config['directory'], filename)
169
+ filename = silence_warnings { report_config["filename"] % report_data }
170
+ file = File.join(report_config["directory"], filename)
168
171
 
169
172
  report = report_class.export(analysis, format)
170
173
 
171
- result = build_report(report_config['frontmatter'], report_data, report)
174
+ result = build_report(report_config["frontmatter"], report_data, report)
172
175
 
173
176
  generated_reports[file] = result
174
177
 
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
4
+ version: 18.0.5
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-07-14 00:00:00.000000000 Z
11
+ date: 2017-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: github_api
@@ -156,14 +156,28 @@ dependencies:
156
156
  requirements:
157
157
  - - "~>"
158
158
  - !ruby/object:Gem::Version
159
- version: 0.47.0
159
+ version: 0.49.1
160
160
  type: :development
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
- version: 0.47.0
166
+ version: 0.49.1
167
+ - !ruby/object:Gem::Dependency
168
+ name: github_changelog_generator
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
167
181
  description:
168
182
  email:
169
183
  - me@duckie.co
@@ -172,15 +186,16 @@ executables:
172
186
  extensions: []
173
187
  extra_rdoc_files: []
174
188
  files:
189
+ - ".github_changelog_generator"
175
190
  - ".gitignore"
176
191
  - ".hound.yml"
177
192
  - ".rspec"
178
193
  - ".rubocop.yml"
194
+ - ".rubocop_todo.yml"
179
195
  - ".travis.yml"
180
196
  - CHANGELOG.md
181
197
  - CODE_OF_CONDUCT.md
182
198
  - Gemfile
183
- - Gemfile.lock
184
199
  - ISSUES.md
185
200
  - LICENSE.txt
186
201
  - README.md
@@ -194,10 +209,13 @@ files:
194
209
  - fixtures/vcr_cassettes/how-is-example-repository.yml
195
210
  - fixtures/vcr_cassettes/how-is-from-config-frontmatter.yml
196
211
  - fixtures/vcr_cassettes/how-is-with-config-file.yml
212
+ - fixtures/vcr_cassettes/how_is_contributions_all_contributors.yml
213
+ - fixtures/vcr_cassettes/how_is_contributions_new_contributors.yml
197
214
  - how_is.gemspec
198
215
  - lib/how_is.rb
199
216
  - lib/how_is/analyzer.rb
200
217
  - lib/how_is/cli.rb
218
+ - lib/how_is/contributions.rb
201
219
  - lib/how_is/fetcher.rb
202
220
  - lib/how_is/pulse.rb
203
221
  - lib/how_is/report.rb
@@ -226,7 +244,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
226
244
  version: '0'
227
245
  requirements: []
228
246
  rubyforge_project:
229
- rubygems_version: 2.6.12
247
+ rubygems_version: 2.6.11
230
248
  signing_key:
231
249
  specification_version: 4
232
250
  summary: Quantify the health of a GitHub repository.
data/Gemfile.lock DELETED
@@ -1,110 +0,0 @@
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