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.
- checksums.yaml +5 -5
- data/.codeclimate.yml +1 -0
- data/.rubocop.yml +16 -0
- data/CHANGELOG.md +30 -3
- data/Gemfile +0 -6
- data/README.md +5 -2
- data/exe/how_is +7 -2
- data/fixtures/vcr_cassettes/how-is-example-empty-repository.yml +446 -727
- data/fixtures/vcr_cassettes/how-is-example-repository.yml +508 -727
- data/fixtures/vcr_cassettes/how-is-from-config-frontmatter.yml +43522 -1218
- data/fixtures/vcr_cassettes/how-is-how-is-travis-api-repos-builds.yml +287 -0
- data/fixtures/vcr_cassettes/how-is-with-config-file.yml +44307 -23286
- data/fixtures/vcr_cassettes/how_is_contributions_additions_count.yml +307 -0
- data/fixtures/vcr_cassettes/how_is_contributions_all_contributors.yml +81 -0
- data/fixtures/vcr_cassettes/how_is_contributions_changed_files.yml +307 -0
- data/fixtures/vcr_cassettes/how_is_contributions_changes.yml +307 -0
- data/fixtures/vcr_cassettes/how_is_contributions_commits.yml +307 -0
- data/fixtures/vcr_cassettes/how_is_contributions_compare_url.yml +74 -0
- data/fixtures/vcr_cassettes/how_is_contributions_deletions_count.yml +307 -0
- data/fixtures/vcr_cassettes/how_is_contributions_new_contributors.yml +234 -0
- data/fixtures/vcr_cassettes/how_is_contributions_summary.yml +378 -0
- data/fixtures/vcr_cassettes/how_is_contributions_summary_2.yml +378 -0
- data/fixtures/vcr_cassettes/how_is_fetcher_call.yml +572 -0
- data/how_is.gemspec +3 -2
- data/lib/how_is/cli.rb +4 -6
- data/lib/how_is/frontmatter.rb +46 -0
- data/lib/how_is/report.rb +59 -63
- data/lib/how_is/sources/github/contributions.rb +164 -0
- data/lib/how_is/sources/github/issues.rb +142 -0
- data/lib/how_is/sources/github/pulls.rb +20 -0
- data/lib/how_is/sources/github.rb +11 -0
- data/lib/how_is/sources/github_helpers.rb +191 -0
- data/lib/how_is/sources/travis.rb +34 -0
- data/lib/how_is/sources.rb +9 -0
- data/lib/how_is/templates/issues_or_pulls_partial.html_template +7 -0
- data/lib/how_is/templates/report.html_template +27 -0
- data/lib/how_is/templates/report_partial.html_template +12 -0
- data/lib/how_is/version.rb +2 -2
- data/lib/how_is.rb +59 -158
- metadata +42 -16
- data/lib/how_is/analyzer.rb +0 -222
- data/lib/how_is/builds.rb +0 -36
- data/lib/how_is/contributions.rb +0 -156
- data/lib/how_is/fetcher.rb +0 -77
- data/lib/how_is/pulse.rb +0 -47
- data/lib/how_is/report/base_report.rb +0 -148
- data/lib/how_is/report/html.rb +0 -120
- 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,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>
|
data/lib/how_is/version.rb
CHANGED
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 "
|
5
|
-
require "
|
4
|
+
require "how_is/report"
|
5
|
+
require "github_api"
|
6
6
|
|
7
|
-
|
7
|
+
module HowIs
|
8
|
+
DEFAULT_REPORT_FILE = "report.html"
|
8
9
|
|
9
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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::
|
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
|
145
|
-
#
|
146
|
-
|
147
|
-
|
148
|
-
|
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
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
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
|
-
|
63
|
+
report_export = report.send("to_#{format}", report_config["frontmatter"])
|
173
64
|
|
174
|
-
|
65
|
+
[file, report_export]
|
66
|
+
}
|
175
67
|
|
176
|
-
|
177
|
-
|
178
|
-
result
|
179
|
-
end
|
180
|
-
|
181
|
-
generated_reports
|
68
|
+
generated_reports.to_h
|
182
69
|
end
|
183
70
|
|
184
|
-
|
185
|
-
#
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
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
|
-
|
79
|
+
def self.template(filename)
|
80
|
+
dir = File.expand_path("./how_is/templates/", __dir__)
|
81
|
+
path = File.join(dir, filename)
|
196
82
|
|
197
|
-
|
83
|
+
open(path).read
|
198
84
|
end
|
199
85
|
|
200
|
-
|
201
|
-
#
|
202
|
-
#
|
203
|
-
|
204
|
-
|
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:
|
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-
|
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.
|
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.
|
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:
|
42
|
+
name: okay
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
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:
|
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/
|
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/
|
213
|
-
- lib/how_is/
|
214
|
-
- lib/how_is/
|
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.
|
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.
|