how_is 19.0.0 → 20.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +1 -0
  3. data/.rubocop.yml +16 -0
  4. data/CHANGELOG.md +30 -3
  5. data/Gemfile +0 -6
  6. data/README.md +5 -2
  7. data/exe/how_is +7 -2
  8. data/fixtures/vcr_cassettes/how-is-example-empty-repository.yml +446 -727
  9. data/fixtures/vcr_cassettes/how-is-example-repository.yml +508 -727
  10. data/fixtures/vcr_cassettes/how-is-from-config-frontmatter.yml +43522 -1218
  11. data/fixtures/vcr_cassettes/how-is-how-is-travis-api-repos-builds.yml +287 -0
  12. data/fixtures/vcr_cassettes/how-is-with-config-file.yml +44307 -23286
  13. data/fixtures/vcr_cassettes/how_is_contributions_additions_count.yml +307 -0
  14. data/fixtures/vcr_cassettes/how_is_contributions_all_contributors.yml +81 -0
  15. data/fixtures/vcr_cassettes/how_is_contributions_changed_files.yml +307 -0
  16. data/fixtures/vcr_cassettes/how_is_contributions_changes.yml +307 -0
  17. data/fixtures/vcr_cassettes/how_is_contributions_commits.yml +307 -0
  18. data/fixtures/vcr_cassettes/how_is_contributions_compare_url.yml +74 -0
  19. data/fixtures/vcr_cassettes/how_is_contributions_deletions_count.yml +307 -0
  20. data/fixtures/vcr_cassettes/how_is_contributions_new_contributors.yml +234 -0
  21. data/fixtures/vcr_cassettes/how_is_contributions_summary.yml +378 -0
  22. data/fixtures/vcr_cassettes/how_is_contributions_summary_2.yml +378 -0
  23. data/fixtures/vcr_cassettes/how_is_fetcher_call.yml +572 -0
  24. data/how_is.gemspec +3 -2
  25. data/lib/how_is/cli.rb +4 -6
  26. data/lib/how_is/frontmatter.rb +46 -0
  27. data/lib/how_is/report.rb +59 -63
  28. data/lib/how_is/sources/github/contributions.rb +164 -0
  29. data/lib/how_is/sources/github/issues.rb +142 -0
  30. data/lib/how_is/sources/github/pulls.rb +20 -0
  31. data/lib/how_is/sources/github.rb +11 -0
  32. data/lib/how_is/sources/github_helpers.rb +191 -0
  33. data/lib/how_is/sources/travis.rb +34 -0
  34. data/lib/how_is/sources.rb +9 -0
  35. data/lib/how_is/templates/issues_or_pulls_partial.html_template +7 -0
  36. data/lib/how_is/templates/report.html_template +27 -0
  37. data/lib/how_is/templates/report_partial.html_template +12 -0
  38. data/lib/how_is/version.rb +2 -2
  39. data/lib/how_is.rb +59 -158
  40. metadata +42 -16
  41. data/lib/how_is/analyzer.rb +0 -222
  42. data/lib/how_is/builds.rb +0 -36
  43. data/lib/how_is/contributions.rb +0 -156
  44. data/lib/how_is/fetcher.rb +0 -77
  45. data/lib/how_is/pulse.rb +0 -47
  46. data/lib/how_is/report/base_report.rb +0 -148
  47. data/lib/how_is/report/html.rb +0 -120
  48. data/lib/how_is/report/json.rb +0 -34
@@ -0,0 +1,191 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "how_is/sources/github"
4
+ require "date"
5
+
6
+ module HowIs
7
+ module Sources
8
+ module GithubHelpers
9
+ def obj_to_array_of_hashes(object)
10
+ object.to_a.map(&:to_h)
11
+ end
12
+
13
+ # Given an Array of issues or pulls, return a Hash specifying how many
14
+ # issues or pulls use each label.
15
+ def num_with_label(issues_or_pulls)
16
+ # Returned hash maps labels to frequency.
17
+ # E.g., given 10 issues/pulls with label "label1" and 5 with label "label2",
18
+ # {
19
+ # "label1" => 10,
20
+ # "label2" => 5
21
+ # }
22
+
23
+ hash = Hash.new(0)
24
+ issues_or_pulls.each do |iop|
25
+ next unless iop["labels"]
26
+
27
+ iop["labels"].each do |label|
28
+ hash[label["name"]] += 1
29
+ end
30
+ end
31
+ hash
32
+ end
33
+
34
+ # Returns the number of issues with no label.
35
+ def num_with_no_label(issues)
36
+ issues.select { |x| x["labels"].empty? }.length
37
+ end
38
+
39
+ # Given an Array of dates, average the timestamps and return the date that
40
+ # represents.
41
+ def average_date_for(issues_or_pulls)
42
+ timestamps = issues_or_pulls.map { |iop| DateTime.parse(iop["created_at"]).strftime("%s").to_i }
43
+ average_timestamp = timestamps.reduce(:+) / issues_or_pulls.length
44
+
45
+ DateTime.strptime(average_timestamp.to_s, "%s")
46
+ end
47
+
48
+ # Given an Array of issues or pulls, return the average age of them.
49
+ # Returns nil if no issues or pulls are provided.
50
+ def average_age_for(issues_or_pulls)
51
+ return nil if issues_or_pulls.empty?
52
+
53
+ ages = issues_or_pulls.map { |iop| time_ago_in_seconds(iop["created_at"]) }
54
+ average_age_in_seconds = ages.reduce(:+) / ages.length
55
+
56
+ values = period_pairs_for(average_age_in_seconds).reject { |(v, _)| v.zero? }.map { |(v, k)|
57
+ k += "s" if v != 1
58
+ [v, k]
59
+ }
60
+
61
+ most_significant = values[0, 2].map { |x| x.join(" ") }
62
+
63
+ value =
64
+ if most_significant.length < 2
65
+ most_significant.first
66
+ else
67
+ most_significant.join(" and ")
68
+ end
69
+
70
+ "approximately #{value}"
71
+ end
72
+
73
+ def sort_iops_by_created_at(issues_or_pulls)
74
+ issues_or_pulls.sort_by { |x| DateTime.parse(x["created_at"]) }
75
+ end
76
+
77
+ # Given an Array of issues or pulls, return the oldest.
78
+ # Returns nil if no issues or pulls are provided.
79
+ def oldest_for(issues_or_pulls)
80
+ return nil if issues_or_pulls.empty?
81
+
82
+ iop = sort_iops_by_created_at(issues_or_pulls).first
83
+
84
+ {
85
+ created_at: iop["created_at"],
86
+ link: iop["html_url"],
87
+ }
88
+ end
89
+
90
+ # Given an Array of issues or pulls, return the newest.
91
+ # Returns nil if no issues or pulls are provided.
92
+ def newest_for(issues_or_pulls)
93
+ return nil if issues_or_pulls.empty?
94
+
95
+ iop = sort_iops_by_created_at(issues_or_pulls).last
96
+
97
+ {
98
+ created_at: iop["created_at"],
99
+ link: iop["html_url"],
100
+ }
101
+ end
102
+
103
+ # Given an issue or PR, returns the date it was created.
104
+ def date_for(issue_or_pull)
105
+ DateTime.parse(issue_or_pull["created_at"])
106
+ end
107
+
108
+ private
109
+
110
+ # Takes an Array of labels, and returns amodified list that includes links
111
+ # to each label.
112
+ def with_label_links(labels, repository)
113
+ labels.map { |label, num_issues|
114
+ label_link = "https://github.com/#{repository}/issues?q=" + CGI.escape("is:open is:issue label:\"#{label}\"")
115
+
116
+ [label, {"link" => label_link, "total" => num_issues}]
117
+ }.to_h
118
+ end
119
+
120
+ # Returns how many seconds ago a date (as a String) was.
121
+ def time_ago_in_seconds(x)
122
+ DateTime.now.strftime("%s").to_i - DateTime.parse(x).strftime("%s").to_i
123
+ end
124
+
125
+ def issue_or_pull_to_hash(iop)
126
+ return nil if iop.nil?
127
+
128
+ ret = {}
129
+
130
+ ret["html_url"] = iop["html_url"]
131
+ ret["number"] = iop["number"]
132
+ ret["date"] = date_for(iop)
133
+
134
+ ret
135
+ end
136
+
137
+ SECONDS_IN_A_YEAR = 31_556_926
138
+ SECONDS_IN_A_MONTH = 2_629_743
139
+ SECONDS_IN_A_WEEK = 604_800
140
+ SECONDS_IN_A_DAY = 86_400
141
+
142
+ # Calculates a list of pairs of value and period label.
143
+ #
144
+ # @param age_in_seconds [Float]
145
+ #
146
+ # @return [Array<Array>] The input age_in_seconds expressed as different
147
+ # units, as pairs of value and unit name.
148
+ def period_pairs_for(age_in_seconds)
149
+ years_remainder = age_in_seconds % SECONDS_IN_A_YEAR
150
+
151
+ months_remainder = years_remainder % SECONDS_IN_A_MONTH
152
+
153
+ weeks_remainder = months_remainder % SECONDS_IN_A_WEEK
154
+
155
+ [
156
+ [age_in_seconds / SECONDS_IN_A_YEAR, "year"],
157
+ [years_remainder / SECONDS_IN_A_MONTH, "month"],
158
+ [months_remainder / SECONDS_IN_A_WEEK, "week"],
159
+ [weeks_remainder / SECONDS_IN_A_DAY, "day"],
160
+ ]
161
+ end
162
+
163
+ def pluralize(string, number, zero_is_no: false)
164
+ number_str = number
165
+ number_str = "no" if number.zero? && zero_is_no
166
+
167
+ "#{number_str} #{string}#{(number == 1) ? '' : 's'}"
168
+ end
169
+
170
+ def are_or_is(number)
171
+ if number == 1
172
+ "is"
173
+ else
174
+ "are"
175
+ end
176
+ end
177
+
178
+ def pretty_date(date_or_str)
179
+ if date_or_str.is_a?(DateTime)
180
+ date = datetime_or_str
181
+ elsif date_or_str.is_a?(String)
182
+ date = DateTime.parse(date_or_str)
183
+ else
184
+ raise ArgumentError, "expected DateTime or String, got #{date_or_str.class}"
185
+ end
186
+
187
+ date.strftime("%b %_d, %Y")
188
+ end
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "okay/http"
4
+ require "how_is/sources/github"
5
+
6
+ module HowIs::Sources
7
+ # Fetches metadata about CI builds from travis-ci.org.
8
+ class Travis
9
+ # @param repository [String] GitHub repository name, of the format user/repo.
10
+ # @param end_date [String] End date for the report being generated.
11
+ def initialize(repository, end_date)
12
+ @repository = repository
13
+ # TODO: Do something with end_date.
14
+ # TODO: Figure out Default Branch of the repo
15
+ end
16
+
17
+ def builds
18
+ JSON.parse(fetch_builds)
19
+ end
20
+
21
+ private
22
+
23
+ # Returns API result of /repos/:user/:repo/builds for Push type Travis
24
+ # events.
25
+ #
26
+ # @return [String] JSON result
27
+ def fetch_builds
28
+ Okay::HTTP.get(
29
+ "http://api.travis-ci.org/repos/#{@repository}/builds?event_type=push",
30
+ headers: {"Accept" => "application/vnd.travis-ci.2+json"}
31
+ ).body
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "how_is/version"
4
+
5
+ module HowIs
6
+ module Sources
7
+ # Simply for creating a namespace.
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ %{summary}
2
+
3
+ <ul>
4
+ <li>Average age: %{average_age}.</li>
5
+ <li><a href="%{oldest_link}">Oldest %{pretty_type}</a> was opened on %{oldest_date}.</li>
6
+ <li><a href="%{newest_link}">Newest %{pretty_type}</a> was opened on %{newest_date}.</li>
7
+ </ul>
@@ -0,0 +1,27 @@
1
+ %{frontmatter}<!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>%{title}</title>
5
+ <style>
6
+ body { font: sans-serif; }
7
+ main {
8
+ max-width: 600px;
9
+ max-width: 72ch;
10
+ margin: auto;
11
+ }
12
+ .horizontal-bar-graph {
13
+ position: relative;
14
+ width: 100%%; /* lol Kernel.format() disapproves. */
15
+ }
16
+ .horizontal-bar-graph .fill {
17
+ display: inline-block;
18
+ background: #CCC;
19
+ }
20
+ </style>
21
+ </head>
22
+ <body>
23
+ <main>
24
+ %{report}
25
+ </main>
26
+ </body>
27
+ </html>
@@ -0,0 +1,12 @@
1
+ %{frontmatter}
2
+ <h1>%{title}</h1>
3
+ <p>%{contributions_summary}</p>
4
+
5
+ <h2>Pull Requests</h2>
6
+ %{pulls_summary}
7
+
8
+ <h2>Issues</h2>
9
+ %{issues_summary}
10
+
11
+ <h2>Issues Per Label</h2>
12
+ %{issues_per_label}
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class HowIs
4
- VERSION = "19.0.0"
3
+ module HowIs
4
+ VERSION = "20.0.0"
5
5
  end
data/lib/how_is.rb CHANGED
@@ -1,54 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "how_is/version"
4
- require "contracts"
5
- require "cacert"
4
+ require "how_is/report"
5
+ require "github_api"
6
6
 
7
- Cacert.set_in_env
7
+ module HowIs
8
+ DEFAULT_REPORT_FILE = "report.html"
8
9
 
9
- C = Contracts
10
-
11
- # HowIs control class used from the CLI tool.
12
- #
13
- # Generates an analysis and has methods to build reports from it.
14
- class HowIs
15
- include Contracts::Core
16
-
17
- require "how_is/fetcher"
18
- require "how_is/analyzer"
19
- require "how_is/report"
20
-
21
- DEFAULT_FORMAT = :html
22
-
23
- ##
24
- # Generate a HowIs instance, so you can generate reports.
25
- #
26
- # @param repository [String] The name of a GitHub repository (of the
27
- # format <user or organization>/<repository>).
28
- # @param analysis [HowIs::Analysis] Optional; if passed, this Analysis
29
- # object is used instead of generating one.
30
- def initialize(repository, analysis = nil, **kw_args)
31
- # If no Analysis is passed, generate one.
32
- analysis ||= HowIs.generate_analysis(repository: repository, **kw_args)
33
-
34
- # Used by to_html, to_json, etc.
35
- @analysis = analysis
10
+ def self.new(repository, date)
11
+ Report.new(repository, date)
36
12
  end
37
13
 
38
- ##
39
- # Generate an HTML report.
40
- #
41
- # @return [String] An HTML report.
42
- def to_html
43
- Report.export(@analysis, :html)
44
- end
45
-
46
- ##
47
- # Generate a JSON report.
48
- #
49
- # @return [String] A JSON report.
50
- def to_json
51
- Report.export(@analysis, :json)
14
+ def self.github
15
+ @@github ||=
16
+ Github.new(auto_pagination: true) do |config|
17
+ config.basic_auth = ENV["HOWIS_BASIC_AUTH"] if ENV["HOWIS_BASIC_AUTH"]
18
+ end
52
19
  end
53
20
 
54
21
  ##
@@ -70,140 +37,62 @@ class HowIs
70
37
  # @return [HowIs] A HowIs object that can be used for generating other
71
38
  # reports, treating the provided report data as a cache.
72
39
  def self.from_hash(data)
73
- analysis = HowIs::Analyzer.from_hash(data)
40
+ analysis = HowIs::Analysis.from_hash(data)
74
41
 
75
42
  new(analysis.repository, analysis)
76
43
  end
77
44
 
78
- ##
79
- # Returns a list of possible export formats.
80
- #
81
- # @return [Array<String>] An array of the types of reports you can
82
- # generate.
83
- def self.supported_formats
84
- report_constants = HowIs.constants.grep(/.Report/) - [:BaseReport]
85
- report_constants.map { |x| x.to_s.split("Report").first.downcase }
86
- end
87
-
88
- ##
89
- # Returns whether or not the specified +file+ can be exported to.
90
- #
91
- # @param file [String] A filename.
92
- # @return [Boolean] +true+ if HowIs can export to the file, +false+
93
- # if it can't.
94
- def self.can_export_to?(file)
95
- # TODO: Check if the file is writable?
96
- supported_formats.include?(file.split(".").last)
97
- end
98
-
99
- # Generate an analysis.
100
- # TODO: This may make more sense as Analysis.new().
101
- # TODO: Nothing overrides +fetcher+ and +analyzer+. Remove ability to do so.
102
- # FIXME: THIS CODE AND EVERYTHING ASSOCIATED WITH IT IS A FUCKING ATROCITY.
103
- Contract C::KeywordArgs[repository: String,
104
- fetcher: C::Optional[Class],
105
- analyzer: C::Optional[Class],
106
- github: C::Optional[C::Any]] => C::Any
107
- def self.generate_analysis(repository:,
108
- fetcher: Fetcher.new,
109
- analyzer: Analyzer.new,
110
- github: nil)
111
- raw_data = fetcher.call(repository, github)
112
- analysis = analyzer.call(raw_data)
113
-
114
- analysis
115
- end
116
-
117
- # Generates YAML frontmatter, as is used in Jekyll and other blog engines.
118
- #
119
- # E.g.,
120
- # generate_frontmatter({'foo' => "bar %{baz}"}, {'baz' => "asdf"})
121
- # => "---\nfoo: bar asdf\n"
122
- Contract C::HashOf[C::Or[String, Symbol] => String],
123
- C::HashOf[C::Or[String, Symbol] => C::Any] => String
124
- def self.generate_frontmatter(frontmatter, report_data)
125
- frontmatter = convert_keys(frontmatter, :to_s)
126
- report_data = convert_keys(report_data, :to_sym)
127
-
128
- frontmatter = frontmatter.map { |k, v|
129
- # Sometimes report_data has unused keys, which generates a warning, but
130
- # we're okay with it.
131
- v = silence_warnings { v % report_data }
132
-
133
- [k, v]
134
- }.to_h
135
-
136
- YAML.dump(frontmatter)
137
- end
138
-
139
45
  ##
140
46
  # Generates a series of report files based on a config Hash.
141
47
  #
142
48
  # @param config [Hash] A Hash specifying the formats, locations, etc
143
49
  # of the reports to generate.
144
- # @param github (You don't need this.) An object to replace the GitHub
145
- # class when fetching data.
146
- # @param report_class (You don't need this.) An object to replace the
147
- # HowIs::Report class when generating reports.
148
- def self.from_config(config,
149
- github: nil,
150
- report_class: nil)
151
- report_class ||= HowIs::Report
152
-
153
- date = Date.strptime(Time.now.to_i.to_s, "%s")
154
- friendly_date = date.strftime("%B %d, %y")
155
-
156
- analysis = HowIs.generate_analysis(repository: config["repository"], github: github)
157
-
158
- report_data = {
159
- repository: config["repository"],
160
- date: date,
161
- friendly_date: friendly_date,
162
- }
163
-
164
- generated_reports = {}
50
+ # @param date [String] A string containing the date (YYYY-MM-DD) that the
51
+ # report ends on. E.g., for Jan 1-Feb 1 2017, you'd pass 2017-02-01.
52
+ def self.from_config(config, date)
53
+ report = Report.new(config["repository"], date)
54
+ report_data = prepare_report_metadata(config["repository"], date)
165
55
 
166
- config["reports"].map do |format, report_config|
167
- # Sometimes report_data has unused keys, which generates a warning, but
168
- # we're okay with it.
169
- filename = silence_warnings { report_config["filename"] % report_data }
170
- file = File.join(report_config["directory"], filename)
56
+ generated_reports =
57
+ config["reports"].map { |format, report_config|
58
+ # Sometimes report_data has unused keys, which generates a warning, but
59
+ # we're okay with it, so we wrap it with silence_warnings {}.
60
+ filename = silence_warnings { report_config["filename"] % report_data }
61
+ file = File.join(report_config["directory"], filename)
171
62
 
172
- report = report_class.export(analysis, format)
63
+ report_export = report.send("to_#{format}", report_config["frontmatter"])
173
64
 
174
- result = build_report(report_config["frontmatter"], report_data, report)
65
+ [file, report_export]
66
+ }
175
67
 
176
- generated_reports[file] = result
177
-
178
- result
179
- end
180
-
181
- generated_reports
68
+ generated_reports.to_h
182
69
  end
183
70
 
184
- # Combine the frontmatter, report data, and raw report into a report with
185
- # frontmatter.
186
- def self.build_report(frontmatter, report_data, report)
187
- str = StringIO.new
188
-
189
- if frontmatter
190
- str.puts generate_frontmatter(frontmatter, report_data)
191
- str.puts "---"
192
- str.puts
193
- end
71
+ ##
72
+ # Returns a list of possible export formats.
73
+ #
74
+ # @return [Array<String>] An array of the types of reports you can generate.
75
+ def self.supported_formats
76
+ ["html", "json"]
77
+ end
194
78
 
195
- str.puts report
79
+ def self.template(filename)
80
+ dir = File.expand_path("./how_is/templates/", __dir__)
81
+ path = File.join(dir, filename)
196
82
 
197
- str.string
83
+ open(path).read
198
84
  end
199
85
 
200
- # @example
201
- # convert_keys({'foo' => 'bar'}, :to_sym)
202
- # # => {:foo => 'bar'}
203
- def self.convert_keys(data, method_name)
204
- data.map { |k, v| [k.send(method_name), v] }.to_h
86
+ ##
87
+ # Returns whether or not the specified +file+ can be exported to.
88
+ #
89
+ # @param file [String] A filename.
90
+ # @return [Boolean] +true+ if HowIs can export to the file, +false+
91
+ # if it can't.
92
+ def self.can_export_to?(file)
93
+ # TODO: Check if the file is writable?
94
+ supported_formats.include?(file.split(".").last)
205
95
  end
206
- private_class_method :convert_keys
207
96
 
208
97
  def self.silence_warnings(&block)
209
98
  with_warnings(nil, &block)
@@ -218,4 +107,16 @@ class HowIs
218
107
  $VERBOSE = old_verbose
219
108
  end
220
109
  private_class_method :with_warnings
110
+
111
+ def self.prepare_report_metadata(repository, date)
112
+ end_date = DateTime.strptime(date, "%Y-%m-%d")
113
+ friendly_end_date = end_date.strftime("%B %d, %y")
114
+
115
+ {
116
+ repository: repository,
117
+ date: end_date,
118
+ friendly_date: friendly_end_date,
119
+ }
120
+ end
121
+ private_class_method :prepare_report_metadata
221
122
  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: 19.0.0
4
+ version: 20.0.0
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-09-16 00:00:00.000000000 Z
11
+ date: 2017-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: github_api
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.17.0
19
+ version: 0.18.1
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.17.0
26
+ version: 0.18.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: contracts
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -39,19 +39,19 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: 0.16.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: tessellator-fetcher
42
+ name: okay
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 5.0.2
47
+ version: 4.0.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 5.0.2
54
+ version: 4.0.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - ">="
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: pry
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
@@ -197,21 +211,33 @@ files:
197
211
  - fixtures/vcr_cassettes/how-is-from-config-frontmatter.yml
198
212
  - fixtures/vcr_cassettes/how-is-how-is-travis-api-repos-builds.yml
199
213
  - fixtures/vcr_cassettes/how-is-with-config-file.yml
214
+ - fixtures/vcr_cassettes/how_is_contributions_additions_count.yml
200
215
  - fixtures/vcr_cassettes/how_is_contributions_all_contributors.yml
216
+ - fixtures/vcr_cassettes/how_is_contributions_changed_files.yml
217
+ - fixtures/vcr_cassettes/how_is_contributions_changes.yml
218
+ - fixtures/vcr_cassettes/how_is_contributions_commits.yml
219
+ - fixtures/vcr_cassettes/how_is_contributions_compare_url.yml
201
220
  - fixtures/vcr_cassettes/how_is_contributions_default_branch.yml
221
+ - fixtures/vcr_cassettes/how_is_contributions_deletions_count.yml
202
222
  - fixtures/vcr_cassettes/how_is_contributions_new_contributors.yml
223
+ - fixtures/vcr_cassettes/how_is_contributions_summary.yml
224
+ - fixtures/vcr_cassettes/how_is_contributions_summary_2.yml
225
+ - fixtures/vcr_cassettes/how_is_fetcher_call.yml
203
226
  - how_is.gemspec
204
227
  - lib/how_is.rb
205
- - lib/how_is/analyzer.rb
206
- - lib/how_is/builds.rb
207
228
  - lib/how_is/cli.rb
208
- - lib/how_is/contributions.rb
209
- - lib/how_is/fetcher.rb
210
- - lib/how_is/pulse.rb
229
+ - lib/how_is/frontmatter.rb
211
230
  - lib/how_is/report.rb
212
- - lib/how_is/report/base_report.rb
213
- - lib/how_is/report/html.rb
214
- - lib/how_is/report/json.rb
231
+ - lib/how_is/sources.rb
232
+ - lib/how_is/sources/github.rb
233
+ - lib/how_is/sources/github/contributions.rb
234
+ - lib/how_is/sources/github/issues.rb
235
+ - lib/how_is/sources/github/pulls.rb
236
+ - lib/how_is/sources/github_helpers.rb
237
+ - lib/how_is/sources/travis.rb
238
+ - lib/how_is/templates/issues_or_pulls_partial.html_template
239
+ - lib/how_is/templates/report.html_template
240
+ - lib/how_is/templates/report_partial.html_template
215
241
  - lib/how_is/version.rb
216
242
  - roadmap.markdown
217
243
  homepage: https://github.com/how-is/how_is
@@ -234,7 +260,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
234
260
  version: '0'
235
261
  requirements: []
236
262
  rubyforge_project:
237
- rubygems_version: 2.6.13
263
+ rubygems_version: 2.7.3
238
264
  signing_key:
239
265
  specification_version: 4
240
266
  summary: Quantify the health of a GitHub repository.