reading 0.9.0 → 1.0.0
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.
- checksums.yaml +4 -4
- data/bin/reading +33 -18
- data/lib/reading/config.rb +2 -2
- data/lib/reading/item/time_length.rb +2 -2
- data/lib/reading/item/view.rb +2 -2
- data/lib/reading/item.rb +15 -12
- data/lib/reading/parsing/attributes/experiences/dates_and_head_transformer.rb +8 -3
- data/lib/reading/parsing/attributes/experiences/history_transformer.rb +151 -48
- data/lib/reading/parsing/attributes/experiences/spans_validator.rb +2 -2
- data/lib/reading/parsing/attributes/experiences.rb +3 -3
- data/lib/reading/parsing/attributes/shared.rb +4 -1
- data/lib/reading/parsing/csv.rb +4 -4
- data/lib/reading/parsing/parser.rb +6 -6
- data/lib/reading/parsing/rows/compact_planned.rb +4 -4
- data/lib/reading/parsing/rows/regular.rb +10 -10
- data/lib/reading/parsing/rows/regular_columns/sources.rb +1 -1
- data/lib/reading/parsing/rows/regular_columns/start_dates.rb +5 -1
- data/lib/reading/parsing/transformer.rb +9 -9
- data/lib/reading/stats/filter.rb +55 -51
- data/lib/reading/stats/grouping.rb +18 -4
- data/lib/reading/stats/operation.rb +104 -22
- data/lib/reading/stats/query.rb +7 -7
- data/lib/reading/stats/result_formatters.rb +140 -0
- data/lib/reading/util/hash_array_deep_fetch.rb +1 -23
- data/lib/reading/version.rb +1 -1
- data/lib/reading.rb +7 -7
- metadata +46 -22
- data/lib/reading/stats/terminal_result_formatters.rb +0 -91
@@ -1,3 +1,5 @@
|
|
1
|
+
require "bigdecimal/util"
|
2
|
+
|
1
3
|
module Reading
|
2
4
|
module Stats
|
3
5
|
# The beginning of a query which specifies what it does, e.g.
|
@@ -50,14 +52,14 @@ module Reading
|
|
50
52
|
# Items) of the given hash of grouped items.
|
51
53
|
# @param grouped_items [Hash]
|
52
54
|
# @yield [Array<Item>]
|
53
|
-
def self.apply_to_inner_items(grouped_items, &)
|
55
|
+
def self.apply_to_inner_items(grouped_items, &block)
|
54
56
|
if grouped_items.values.first.is_a? Array
|
55
57
|
grouped_items.transform_values! { |inner_items|
|
56
58
|
yield inner_items
|
57
59
|
}
|
58
60
|
else # It's a Hash, so go one level deeper.
|
59
61
|
grouped_items.each do |group_name, grouped|
|
60
|
-
apply_to_inner_items(grouped, &)
|
62
|
+
apply_to_inner_items(grouped, &block)
|
61
63
|
end
|
62
64
|
end
|
63
65
|
end
|
@@ -89,7 +91,7 @@ module Reading
|
|
89
91
|
(lengths.sum / lengths.count.to_f).to_i_if_whole
|
90
92
|
end
|
91
93
|
},
|
92
|
-
:
|
94
|
+
average_amount: proc { |items|
|
93
95
|
total_amount = items.sum { |item|
|
94
96
|
item.experiences.sum { |experience|
|
95
97
|
experience.spans.sum(&:amount)
|
@@ -105,6 +107,9 @@ module Reading
|
|
105
107
|
amounts_by_date.values.sum / amounts_by_date.count
|
106
108
|
end
|
107
109
|
},
|
110
|
+
list_item: proc { |items|
|
111
|
+
items.map { |item| author_and_title(item) }
|
112
|
+
},
|
108
113
|
total_item: proc { |items|
|
109
114
|
items.count
|
110
115
|
},
|
@@ -112,77 +117,153 @@ module Reading
|
|
112
117
|
items.sum { |item|
|
113
118
|
item.experiences.sum { |experience|
|
114
119
|
experience.spans.sum { |span|
|
115
|
-
(span.amount * span.progress).to_i_if_whole
|
120
|
+
(span.amount * (span.progress || 0.0)).to_i_if_whole
|
116
121
|
}
|
117
122
|
}
|
118
123
|
}
|
119
124
|
},
|
120
125
|
top_rating: proc { |items, number_arg|
|
121
126
|
items
|
122
|
-
.max_by(number_arg || DEFAULT_NUMBER_ARG, &:rating)
|
123
127
|
.map { |item| [author_and_title(item), item.rating] }
|
128
|
+
.max_by(number_arg || DEFAULT_NUMBER_ARG) { |_title, rating|
|
129
|
+
rating || 0
|
130
|
+
}
|
124
131
|
},
|
125
132
|
top_length: proc { |items, number_arg|
|
126
133
|
items
|
127
|
-
.map { |item|
|
134
|
+
.map { |item|
|
135
|
+
# Longest length, or if undefined length then longest experience
|
136
|
+
# (code adapted from top_amount below).
|
137
|
+
length = item.variants.map(&:length).max ||
|
138
|
+
item.experiences.map { |experience|
|
139
|
+
experience.spans.sum { |span|
|
140
|
+
(span.amount * (span.progress || 0.0)).to_i_if_whole
|
141
|
+
}
|
142
|
+
}.max
|
143
|
+
|
144
|
+
[author_and_title(item), length]
|
145
|
+
}
|
128
146
|
.reject { |_title, length| length.nil? }
|
129
|
-
.max_by(number_arg || DEFAULT_NUMBER_ARG) { |_title, length|
|
147
|
+
.max_by(number_arg || DEFAULT_NUMBER_ARG) { |_title, length|
|
148
|
+
length
|
149
|
+
}
|
130
150
|
},
|
131
151
|
top_amount: proc { |items, number_arg|
|
132
152
|
items
|
133
153
|
.map { |item|
|
134
154
|
amount = item.experiences.sum { |experience|
|
135
155
|
experience.spans.sum { |span|
|
136
|
-
(span.amount * span.progress).to_i_if_whole
|
156
|
+
(span.amount * (span.progress || 0.0)).to_i_if_whole
|
137
157
|
}
|
138
158
|
}
|
139
159
|
|
140
160
|
[author_and_title(item), amount]
|
141
161
|
}
|
142
162
|
.reject { |_title, amount| amount.zero? }
|
143
|
-
.max_by(number_arg || DEFAULT_NUMBER_ARG) { |_title, amount|
|
163
|
+
.max_by(number_arg || DEFAULT_NUMBER_ARG) { |_title, amount|
|
164
|
+
amount
|
165
|
+
}
|
144
166
|
},
|
145
167
|
top_speed: proc { |items, number_arg|
|
146
168
|
items
|
147
|
-
.map { |item|
|
169
|
+
.map { |item|
|
170
|
+
speed = calculate_speed(item)
|
171
|
+
[author_and_title(item), speed] if speed
|
172
|
+
}
|
148
173
|
.compact
|
149
174
|
.max_by(number_arg || DEFAULT_NUMBER_ARG) { |_title, speed_hash|
|
150
175
|
speed_hash[:amount] / speed_hash[:days].to_f
|
151
176
|
}
|
152
177
|
},
|
178
|
+
top_experience: proc { |items, number_arg|
|
179
|
+
items
|
180
|
+
.map { |item|
|
181
|
+
experience_count = item
|
182
|
+
.experiences
|
183
|
+
.count { |experience|
|
184
|
+
experience.spans.all? { _1.progress.to_d == "1.0".to_d }
|
185
|
+
}
|
186
|
+
|
187
|
+
[author_and_title(item), [experience_count, item.rating || 0]]
|
188
|
+
}
|
189
|
+
.max_by(number_arg || DEFAULT_NUMBER_ARG) { |_title, experience_count_and_rating|
|
190
|
+
experience_count_and_rating
|
191
|
+
}
|
192
|
+
.map { |title, (experience_count, _rating)|
|
193
|
+
[title, experience_count]
|
194
|
+
}
|
195
|
+
},
|
196
|
+
top_note: proc { |items, number_arg|
|
197
|
+
items
|
198
|
+
.map { |item|
|
199
|
+
notes_word_count = item
|
200
|
+
.notes
|
201
|
+
.sum { |note|
|
202
|
+
note.content.scan(/[\w[:punct:]]+/).count
|
203
|
+
}
|
204
|
+
|
205
|
+
[author_and_title(item), notes_word_count]
|
206
|
+
}
|
207
|
+
.max_by(number_arg || DEFAULT_NUMBER_ARG) { |_title, notes_word_count|
|
208
|
+
notes_word_count
|
209
|
+
}
|
210
|
+
},
|
153
211
|
bottom_rating: proc { |items, number_arg|
|
154
212
|
items
|
155
|
-
.min_by(number_arg || DEFAULT_NUMBER_ARG, &:rating)
|
156
213
|
.map { |item| [author_and_title(item), item.rating] }
|
214
|
+
.min_by(number_arg || DEFAULT_NUMBER_ARG) { |_title, rating|
|
215
|
+
rating || 0
|
216
|
+
}
|
157
217
|
},
|
158
218
|
bottom_length: proc { |items, number_arg|
|
159
219
|
items
|
160
|
-
.map { |item|
|
220
|
+
.map { |item|
|
221
|
+
# Longest length, or if undefined length then longest experience
|
222
|
+
# (code adapted from bottom_amount below).
|
223
|
+
length = item.variants.map(&:length).max ||
|
224
|
+
item.experiences.map { |experience|
|
225
|
+
experience.spans.sum { |span|
|
226
|
+
(span.amount * (span.progress || 0.0)).to_i_if_whole
|
227
|
+
}
|
228
|
+
}.max
|
229
|
+
|
230
|
+
[author_and_title(item), length]
|
231
|
+
}
|
161
232
|
.reject { |_title, length| length.nil? }
|
162
|
-
.min_by(number_arg || DEFAULT_NUMBER_ARG) { |_title, length|
|
233
|
+
.min_by(number_arg || DEFAULT_NUMBER_ARG) { |_title, length|
|
234
|
+
length
|
235
|
+
}
|
163
236
|
},
|
164
237
|
bottom_amount: proc { |items, number_arg|
|
165
238
|
items
|
166
239
|
.map { |item|
|
167
240
|
amount = item.experiences.sum { |experience|
|
168
241
|
experience.spans.sum { |span|
|
169
|
-
(span.amount * span.progress).to_i_if_whole
|
242
|
+
(span.amount * (span.progress || 0.0)).to_i_if_whole
|
170
243
|
}
|
171
244
|
}
|
172
245
|
|
173
246
|
[author_and_title(item), amount]
|
174
247
|
}
|
175
248
|
.reject { |_title, amount| amount.zero? }
|
176
|
-
.min_by(number_arg || DEFAULT_NUMBER_ARG) { |_title, amount|
|
249
|
+
.min_by(number_arg || DEFAULT_NUMBER_ARG) { |_title, amount|
|
250
|
+
amount
|
251
|
+
}
|
177
252
|
},
|
178
253
|
bottom_speed: proc { |items, number_arg|
|
179
254
|
items
|
180
|
-
.map { |item|
|
255
|
+
.map { |item|
|
256
|
+
speed = calculate_speed(item)
|
257
|
+
[author_and_title(item), speed] if speed
|
258
|
+
}
|
181
259
|
.compact
|
182
260
|
.min_by(number_arg || DEFAULT_NUMBER_ARG) { |_title, speed_hash|
|
183
261
|
speed_hash[:amount] / speed_hash[:days].to_f
|
184
262
|
}
|
185
263
|
},
|
264
|
+
debug: proc { |items|
|
265
|
+
items
|
266
|
+
},
|
186
267
|
}
|
187
268
|
|
188
269
|
ALIASES = {
|
@@ -190,20 +271,24 @@ module Reading
|
|
190
271
|
average_length: %w[al],
|
191
272
|
average_amount: %w[aia ai],
|
192
273
|
:"average_daily-amount" => %w[ada ad],
|
274
|
+
list_item: %w[li list],
|
193
275
|
total_item: %w[item count],
|
194
276
|
total_amount: %w[amount],
|
195
277
|
top_rating: %w[tr],
|
196
278
|
top_length: %w[tl],
|
197
279
|
top_amount: %w[ta],
|
198
280
|
top_speed: %w[ts],
|
281
|
+
top_experience: %w[te],
|
282
|
+
top_note: %w[tn],
|
199
283
|
bottom_rating: %w[br],
|
200
284
|
bottom_length: %w[bl],
|
201
285
|
bottom_amount: %w[ba],
|
202
286
|
bottom_speed: %w[bs],
|
287
|
+
debug: %w[d],
|
203
288
|
}
|
204
289
|
|
205
290
|
REGEXES = ACTIONS.map { |key, _action|
|
206
|
-
first_word, second_word = key.to_s.split(
|
291
|
+
first_word, second_word = key.to_s.split("_")
|
207
292
|
aliases = ALIASES.fetch(key)
|
208
293
|
|
209
294
|
regex =
|
@@ -228,7 +313,7 @@ module Reading
|
|
228
313
|
(
|
229
314
|
\A
|
230
315
|
\s*
|
231
|
-
(#{aliases.join(
|
316
|
+
(#{aliases.join("|")})
|
232
317
|
s?
|
233
318
|
\s*
|
234
319
|
(?<number_arg>
|
@@ -296,10 +381,7 @@ module Reading
|
|
296
381
|
|
297
382
|
return nil unless speeds.any?
|
298
383
|
|
299
|
-
|
300
|
-
.max_by { |hash| hash[:amount] / hash[:days].to_f }
|
301
|
-
|
302
|
-
[author_and_title(item), speed]
|
384
|
+
speeds.max_by { |hash| hash[:amount] / hash[:days].to_f }
|
303
385
|
end
|
304
386
|
|
305
387
|
# A shorter version of Item::View#name.
|
data/lib/reading/stats/query.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
require
|
2
|
-
require_relative
|
3
|
-
require_relative
|
4
|
-
require_relative
|
1
|
+
require "pastel"
|
2
|
+
require_relative "operation"
|
3
|
+
require_relative "filter"
|
4
|
+
require_relative "grouping"
|
5
|
+
require_relative "result_formatters"
|
5
6
|
|
6
7
|
module Reading
|
7
8
|
module Stats
|
@@ -13,9 +14,8 @@ module Reading
|
|
13
14
|
# @param items [Array<Item>] the Items to be queried.
|
14
15
|
# @param result_formatters [Boolean, Hash{Symbol => Proc}] to alter the
|
15
16
|
# appearance of results; keys should be from among the keys of
|
16
|
-
# Operation::ACTIONS. Pre-made formatters
|
17
|
-
|
18
|
-
def initialize(input:, items:, result_formatters: {})
|
17
|
+
# Operation::ACTIONS. Pre-made formatters are in result_formatters.rb.
|
18
|
+
def initialize(input:, items:, result_formatters: Reading::Stats::ResultFormatters::TRUNCATED_TITLES)
|
19
19
|
@input = input
|
20
20
|
@items = items
|
21
21
|
@result_formatters = result_formatters
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require "pastel"
|
2
|
+
|
3
|
+
module Reading
|
4
|
+
module Stats
|
5
|
+
module ResultFormatters
|
6
|
+
TRUNCATED_TITLES = {
|
7
|
+
top_length: ->(result) { with_truncated_title(result) },
|
8
|
+
top_amount: ->(result) { with_truncated_title(result) },
|
9
|
+
top_speed: ->(result) { with_truncated_title(result) },
|
10
|
+
top_experience: ->(result) { with_truncated_title(result) },
|
11
|
+
top_note: ->(result) { with_truncated_title(result) },
|
12
|
+
bottom_length: ->(result) { with_truncated_title(result) },
|
13
|
+
botom_amount: ->(result) { with_truncated_title(result) },
|
14
|
+
bottom_speed: ->(result) { with_truncated_title(result) },
|
15
|
+
}
|
16
|
+
|
17
|
+
TERMINAL = {
|
18
|
+
average_length: ->(result) { length_to_s(result) },
|
19
|
+
average_amount: ->(result) { length_to_s(result) },
|
20
|
+
:"average_daily-amount" => ->(result) { "#{length_to_s(result)} per day" },
|
21
|
+
total_item: ->(result) {
|
22
|
+
if result.zero?
|
23
|
+
PASTEL.bright_black("none")
|
24
|
+
else
|
25
|
+
color("#{result} #{result == 1 ? "item" : "items"}")
|
26
|
+
end
|
27
|
+
},
|
28
|
+
total_amount: ->(result) { length_to_s(result) },
|
29
|
+
top_rating: ->(result) { top_or_bottom_numbers_string(result, noun: "star") },
|
30
|
+
top_length: ->(result) { top_or_bottom_lengths_string(result) },
|
31
|
+
top_amount: ->(result) { top_or_bottom_lengths_string(result) },
|
32
|
+
top_speed: ->(result) { top_or_bottom_speeds_string(result) },
|
33
|
+
top_experience: ->(result) { top_or_bottom_numbers_string(result, noun: "experience") },
|
34
|
+
top_note: ->(result) { top_or_bottom_numbers_string(result, noun: "word") },
|
35
|
+
bottom_rating: ->(result) { top_or_bottom_numbers_string(result, noun: "star") },
|
36
|
+
bottom_length: ->(result) { top_or_bottom_lengths_string(result) },
|
37
|
+
botom_amount: ->(result) { top_or_bottom_lengths_string(result) },
|
38
|
+
bottom_speed: ->(result) { top_or_bottom_speeds_string(result) },
|
39
|
+
}
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
PASTEL = Pastel.new
|
44
|
+
|
45
|
+
# Applies a terminal color.
|
46
|
+
# @param string [String]
|
47
|
+
# @return [String]
|
48
|
+
private_class_method def self.color(string)
|
49
|
+
PASTEL.bright_blue(string)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Converts a length/amount (pages or time) into a string.
|
53
|
+
# @param length [Numeric, Reading::Item::TimeLength]
|
54
|
+
# @param color [Boolean] whether a terminal color should be applied.
|
55
|
+
# @return [String]
|
56
|
+
private_class_method def self.length_to_s(length, color: true)
|
57
|
+
if length.is_a?(Numeric)
|
58
|
+
length_string = "#{length.round} pages"
|
59
|
+
else
|
60
|
+
length_string = length.to_s
|
61
|
+
end
|
62
|
+
|
63
|
+
color ? color(length_string) : length_string
|
64
|
+
end
|
65
|
+
|
66
|
+
# Formats a list of top/bottom length results as a string.
|
67
|
+
# @param result [Array]
|
68
|
+
# @return [String]
|
69
|
+
private_class_method def self.top_or_bottom_lengths_string(result)
|
70
|
+
offset = result.count.digits.count
|
71
|
+
|
72
|
+
result
|
73
|
+
.map.with_index { |(title, length), index|
|
74
|
+
pad = " " * (offset - (index + 1).digits.count)
|
75
|
+
|
76
|
+
title_line = "#{index + 1}. #{pad}#{title}"
|
77
|
+
indent = " #{" " * offset}"
|
78
|
+
|
79
|
+
"#{title_line}\n#{indent}#{length_to_s(length)}"
|
80
|
+
}
|
81
|
+
.join("\n")
|
82
|
+
end
|
83
|
+
|
84
|
+
# Formats a list of top/bottom speed results as a string.
|
85
|
+
# @param result [Array]
|
86
|
+
# @return [String]
|
87
|
+
private_class_method def self.top_or_bottom_speeds_string(result)
|
88
|
+
offset = result.count.digits.count
|
89
|
+
|
90
|
+
result
|
91
|
+
.map.with_index { |(title, hash), index|
|
92
|
+
amount = length_to_s(hash[:amount], color: false)
|
93
|
+
days = "#{hash[:days]} #{hash[:days] == 1 ? "day" : "days"}"
|
94
|
+
pad = " " * (offset - (index + 1).digits.count)
|
95
|
+
|
96
|
+
title_line = "#{index + 1}. #{pad}#{title}"
|
97
|
+
indent = " #{" " * offset}"
|
98
|
+
colored_speed = color("#{amount} in #{days}")
|
99
|
+
|
100
|
+
"#{title_line}\n#{indent}#{colored_speed}"
|
101
|
+
}
|
102
|
+
.join("\n")
|
103
|
+
end
|
104
|
+
|
105
|
+
# Formats a list of top/bottom number results as a string.
|
106
|
+
private_class_method def self.top_or_bottom_numbers_string(result, noun:)
|
107
|
+
offset = result.count.digits.count
|
108
|
+
|
109
|
+
result
|
110
|
+
.map.with_index { |(title, number), index|
|
111
|
+
pad = " " * (offset - (index + 1).digits.count)
|
112
|
+
|
113
|
+
title_line = "#{index + 1}. #{pad}#{title}"
|
114
|
+
indent = " #{" " * offset}"
|
115
|
+
number_string = color("#{number} #{number == 1 ? noun : "#{noun}s"}")
|
116
|
+
|
117
|
+
"#{title_line}\n#{indent}#{number_string}"
|
118
|
+
}
|
119
|
+
.join("\n")
|
120
|
+
end
|
121
|
+
|
122
|
+
# Truncates the title of each result to a specified length.
|
123
|
+
# @param result [Array]
|
124
|
+
# @param length [Integer] the maximum length of the title.
|
125
|
+
# @return [Array]
|
126
|
+
private_class_method def self.with_truncated_title(result, length: 45)
|
127
|
+
result.map do |title, value|
|
128
|
+
truncated_title =
|
129
|
+
if title.length + 1 > length
|
130
|
+
"#{title[0...length]}…"
|
131
|
+
else
|
132
|
+
title
|
133
|
+
end
|
134
|
+
|
135
|
+
[truncated_title, value]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -1,31 +1,9 @@
|
|
1
1
|
module Reading
|
2
2
|
module Util
|
3
|
-
class FetchDepthExceededError < StandardError
|
4
|
-
end
|
5
|
-
|
6
3
|
# Similar to Array#dig and Hash#dig but raises an error for not found elements.
|
7
|
-
#
|
8
|
-
# More flexible but slightly slower alternative:
|
9
|
-
# keys.reduce(self) { |a, e| a.fetch(e) }
|
10
|
-
#
|
11
|
-
# See performance comparisons:
|
12
|
-
# https://fpsvogel.com/posts/2022/ruby-hash-dot-syntax-deep-fetch
|
13
4
|
module HashArrayDeepFetch
|
14
5
|
def deep_fetch(*keys)
|
15
|
-
|
16
|
-
when 1
|
17
|
-
fetch(keys[0])
|
18
|
-
when 2
|
19
|
-
fetch(keys[0]).fetch(keys[1])
|
20
|
-
when 3
|
21
|
-
fetch(keys[0]).fetch(keys[1]).fetch(keys[2])
|
22
|
-
when 4
|
23
|
-
fetch(keys[0]).fetch(keys[1]).fetch(keys[2]).fetch(keys[3])
|
24
|
-
when 5
|
25
|
-
fetch(keys[0]).fetch(keys[1]).fetch(keys[2]).fetch(keys[3]).fetch(keys[4])
|
26
|
-
else
|
27
|
-
raise FetchDepthExceededError, "#deep_fetch can't fetch that deep!"
|
28
|
-
end
|
6
|
+
keys.reduce(self) { |a, e| a.fetch(e) }
|
29
7
|
end
|
30
8
|
|
31
9
|
refine Hash do
|
data/lib/reading/version.rb
CHANGED
data/lib/reading.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
Dir[File.join(__dir__,
|
2
|
-
require_relative
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
7
|
-
require_relative
|
1
|
+
Dir[File.join(__dir__, "reading", "util", "*.rb")].each { |file| require file }
|
2
|
+
require_relative "reading/errors"
|
3
|
+
require_relative "reading/config"
|
4
|
+
require_relative "reading/parsing/csv"
|
5
|
+
require_relative "reading/filter"
|
6
|
+
require_relative "reading/stats/query"
|
7
|
+
require_relative "reading/item/time_length.rb"
|
8
8
|
|
9
9
|
# The gem's public API. See https://github.com/fpsvogel/reading#usage
|
10
10
|
#
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reading
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Felipe Vogel
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: pastel
|
@@ -24,48 +23,76 @@ dependencies:
|
|
24
23
|
- - "~>"
|
25
24
|
- !ruby/object:Gem::Version
|
26
25
|
version: '0.8'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: amazing_print
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '1.0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: bigdecimal
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '3.0'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '3.0'
|
27
54
|
- !ruby/object:Gem::Dependency
|
28
55
|
name: debug
|
29
56
|
requirement: !ruby/object:Gem::Requirement
|
30
57
|
requirements:
|
31
58
|
- - "~>"
|
32
59
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
60
|
+
version: '1.0'
|
34
61
|
type: :development
|
35
62
|
prerelease: false
|
36
63
|
version_requirements: !ruby/object:Gem::Requirement
|
37
64
|
requirements:
|
38
65
|
- - "~>"
|
39
66
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1.
|
67
|
+
version: '1.0'
|
41
68
|
- !ruby/object:Gem::Dependency
|
42
69
|
name: minitest
|
43
70
|
requirement: !ruby/object:Gem::Requirement
|
44
71
|
requirements:
|
45
72
|
- - "~>"
|
46
73
|
- !ruby/object:Gem::Version
|
47
|
-
version: '5.
|
74
|
+
version: '5.0'
|
48
75
|
type: :development
|
49
76
|
prerelease: false
|
50
77
|
version_requirements: !ruby/object:Gem::Requirement
|
51
78
|
requirements:
|
52
79
|
- - "~>"
|
53
80
|
- !ruby/object:Gem::Version
|
54
|
-
version: '5.
|
81
|
+
version: '5.0'
|
55
82
|
- !ruby/object:Gem::Dependency
|
56
83
|
name: minitest-reporters
|
57
84
|
requirement: !ruby/object:Gem::Requirement
|
58
85
|
requirements:
|
59
86
|
- - "~>"
|
60
87
|
- !ruby/object:Gem::Version
|
61
|
-
version: '1.
|
88
|
+
version: '1.0'
|
62
89
|
type: :development
|
63
90
|
prerelease: false
|
64
91
|
version_requirements: !ruby/object:Gem::Requirement
|
65
92
|
requirements:
|
66
93
|
- - "~>"
|
67
94
|
- !ruby/object:Gem::Version
|
68
|
-
version: '1.
|
95
|
+
version: '1.0'
|
69
96
|
- !ruby/object:Gem::Dependency
|
70
97
|
name: shoulda-context
|
71
98
|
requirement: !ruby/object:Gem::Requirement
|
@@ -95,34 +122,33 @@ dependencies:
|
|
95
122
|
- !ruby/object:Gem::Version
|
96
123
|
version: '1.0'
|
97
124
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
125
|
+
name: rake
|
99
126
|
requirement: !ruby/object:Gem::Requirement
|
100
127
|
requirements:
|
101
128
|
- - "~>"
|
102
129
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
130
|
+
version: '13.0'
|
104
131
|
type: :development
|
105
132
|
prerelease: false
|
106
133
|
version_requirements: !ruby/object:Gem::Requirement
|
107
134
|
requirements:
|
108
135
|
- - "~>"
|
109
136
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
137
|
+
version: '13.0'
|
111
138
|
- !ruby/object:Gem::Dependency
|
112
139
|
name: rubycritic
|
113
140
|
requirement: !ruby/object:Gem::Requirement
|
114
141
|
requirements:
|
115
142
|
- - "~>"
|
116
143
|
- !ruby/object:Gem::Version
|
117
|
-
version: '4.
|
144
|
+
version: '4.0'
|
118
145
|
type: :development
|
119
146
|
prerelease: false
|
120
147
|
version_requirements: !ruby/object:Gem::Requirement
|
121
148
|
requirements:
|
122
149
|
- - "~>"
|
123
150
|
- !ruby/object:Gem::Version
|
124
|
-
version: '4.
|
125
|
-
description:
|
151
|
+
version: '4.0'
|
126
152
|
email:
|
127
153
|
- fps.vogel@gmail.com
|
128
154
|
executables:
|
@@ -173,7 +199,7 @@ files:
|
|
173
199
|
- lib/reading/stats/grouping.rb
|
174
200
|
- lib/reading/stats/operation.rb
|
175
201
|
- lib/reading/stats/query.rb
|
176
|
-
- lib/reading/stats/
|
202
|
+
- lib/reading/stats/result_formatters.rb
|
177
203
|
- lib/reading/util/blank.rb
|
178
204
|
- lib/reading/util/exclude.rb
|
179
205
|
- lib/reading/util/hash_array_deep_fetch.rb
|
@@ -189,24 +215,22 @@ metadata:
|
|
189
215
|
allowed_push_host: https://rubygems.org
|
190
216
|
homepage_uri: https://github.com/fpsvogel/reading
|
191
217
|
source_code_uri: https://github.com/fpsvogel/reading
|
192
|
-
changelog_uri: https://github.com/fpsvogel/reading/blob/
|
193
|
-
post_install_message:
|
218
|
+
changelog_uri: https://github.com/fpsvogel/reading/blob/main/CHANGELOG.md
|
194
219
|
rdoc_options: []
|
195
220
|
require_paths:
|
196
221
|
- lib
|
197
222
|
required_ruby_version: !ruby/object:Gem::Requirement
|
198
223
|
requirements:
|
199
|
-
- - "
|
224
|
+
- - "~>"
|
200
225
|
- !ruby/object:Gem::Version
|
201
|
-
version: 3.
|
226
|
+
version: 3.4.4
|
202
227
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
203
228
|
requirements:
|
204
229
|
- - ">="
|
205
230
|
- !ruby/object:Gem::Version
|
206
231
|
version: '0'
|
207
232
|
requirements: []
|
208
|
-
rubygems_version: 3.
|
209
|
-
signing_key:
|
233
|
+
rubygems_version: 3.6.7
|
210
234
|
specification_version: 4
|
211
235
|
summary: Parses a CSV reading log.
|
212
236
|
test_files: []
|