reading 0.8.0 → 0.9.1
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 +95 -10
- data/lib/reading/config.rb +27 -5
- data/lib/reading/errors.rb +4 -1
- data/lib/reading/item/time_length.rb +60 -23
- data/lib/reading/item/view.rb +14 -19
- data/lib/reading/item.rb +324 -54
- data/lib/reading/parsing/attributes/attribute.rb +0 -7
- data/lib/reading/parsing/attributes/experiences/dates_and_head_transformer.rb +17 -13
- data/lib/reading/parsing/attributes/experiences/history_transformer.rb +172 -60
- data/lib/reading/parsing/attributes/experiences/spans_validator.rb +19 -20
- data/lib/reading/parsing/attributes/experiences.rb +5 -5
- data/lib/reading/parsing/attributes/shared.rb +17 -7
- data/lib/reading/parsing/attributes/variants.rb +9 -6
- data/lib/reading/parsing/csv.rb +38 -35
- data/lib/reading/parsing/parser.rb +23 -24
- data/lib/reading/parsing/rows/blank.rb +23 -0
- data/lib/reading/parsing/rows/comment.rb +6 -7
- data/lib/reading/parsing/rows/compact_planned.rb +9 -9
- data/lib/reading/parsing/rows/compact_planned_columns/head.rb +2 -2
- data/lib/reading/parsing/rows/custom_config.rb +42 -0
- data/lib/reading/parsing/rows/regular.rb +15 -14
- data/lib/reading/parsing/rows/regular_columns/length.rb +8 -8
- data/lib/reading/parsing/rows/regular_columns/sources.rb +16 -10
- data/lib/reading/parsing/rows/regular_columns/start_dates.rb +5 -1
- data/lib/reading/parsing/transformer.rb +13 -17
- data/lib/reading/stats/filter.rb +738 -0
- data/lib/reading/stats/grouping.rb +257 -0
- data/lib/reading/stats/operation.rb +345 -0
- data/lib/reading/stats/query.rb +37 -0
- data/lib/reading/stats/terminal_result_formatters.rb +91 -0
- data/lib/reading/util/exclude.rb +12 -0
- data/lib/reading/util/hash_array_deep_fetch.rb +1 -23
- data/lib/reading/util/hash_to_data.rb +2 -2
- data/lib/reading/version.rb +1 -1
- data/lib/reading.rb +36 -21
- metadata +28 -24
- data/bin/readingfile +0 -31
- data/lib/reading/util/string_remove.rb +0 -28
- data/lib/reading/util/string_truncate.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e30d2e08f85d4ecb58352862165f63b62dbcbeb436103cf0761f318592359571
|
4
|
+
data.tar.gz: 7991e2a241345bd8f5b8fe268c462b721d610157d2c2652aec16882d3833c65f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1357b2ecd226209ff58e5fa7215ca073bfda99225a9d1623b111417d9bf01ecc9ec61406b2bde9fb3a26f1c7ce6a24e9495eb2bedca403a45d029d9e94e83c0e
|
7
|
+
data.tar.gz: 10285aa58757b8fa07e94945811a9195f6e5015e06ace666c963885b5a5eb19839bae855013a54a76fb4d2a038a9dd1fc53611b2ea0d28a4385073811064cb46
|
data/bin/reading
CHANGED
@@ -1,31 +1,116 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
#
|
3
|
+
# Starts the reading statistics interactive CLI, if a CSV file path arg is given.
|
4
|
+
# If a CSV string is given instead, then parsing output (item hashes) is displayed.
|
4
5
|
#
|
5
6
|
# Usage:
|
6
7
|
# Run on the command line:
|
7
|
-
# reading "<CSV string>" "<optional comma-separated names of enabled columns>"
|
8
|
+
# reading "<CSV file path or string>" "<optional comma-separated names of enabled columns>"
|
8
9
|
#
|
9
10
|
# Examples:
|
11
|
+
# reading /home/felipe/reading.csv
|
12
|
+
# reading /home/felipe/reading.csv 'head, sources'
|
10
13
|
# reading '3|📕Trying|Little Library 1970147288'
|
11
14
|
# reading '📕Trying|Little Library 1970147288' 'head, sources'
|
12
15
|
|
16
|
+
require_relative '../lib/reading'
|
17
|
+
require_relative '../lib/reading/stats/terminal_result_formatters'
|
18
|
+
require 'debug'
|
19
|
+
require 'amazing_print'
|
20
|
+
require 'readline'
|
21
|
+
require 'pastel'
|
13
22
|
|
14
|
-
|
15
|
-
|
16
|
-
|
23
|
+
EXIT_COMMANDS = %w[exit e quit q]
|
24
|
+
PASTEL = Pastel.new
|
25
|
+
GROUP_HEADING_FORMATTERS = [
|
26
|
+
-> { PASTEL.magenta.bold.underline(_1) },
|
27
|
+
-> { PASTEL.green.bold.underline(_1) },
|
28
|
+
-> { PASTEL.yellow.bold.underline(_1) },
|
29
|
+
-> { PASTEL.cyan.bold.underline(_1) },
|
30
|
+
-> { PASTEL.magenta.on_white(_1) },
|
31
|
+
-> { PASTEL.green.on_white(_1) },
|
32
|
+
-> { PASTEL.yellow.on_white(_1) },
|
33
|
+
]
|
34
|
+
|
35
|
+
# Recursively prints a hash of results (possibly grouped).
|
36
|
+
# @param grouped_results [Hash, Array]
|
37
|
+
# @param group_heading_formatters [Array<Proc>] a subset of GROUP_HEADING_FORMATTERS
|
38
|
+
def print_grouped_results(grouped_results, group_heading_formatters)
|
39
|
+
indent_level = GROUP_HEADING_FORMATTERS.count - group_heading_formatters.count
|
40
|
+
|
41
|
+
if grouped_results.nil? || (grouped_results.respond_to?(:empty?) && grouped_results.empty?)
|
42
|
+
puts " " * indent_level + PASTEL.bright_black("none") + "\n"
|
43
|
+
return
|
44
|
+
end
|
45
|
+
|
46
|
+
if grouped_results.is_a?(Hash) ||
|
47
|
+
(grouped_results.is_a?(Array) && grouped_results.first.length == 2)
|
48
|
+
|
49
|
+
grouped_results.each do |group_name, grouped|
|
50
|
+
puts " " * indent_level + group_heading_formatters.first.call(group_name)
|
51
|
+
print_grouped_results(grouped, group_heading_formatters[1..])
|
52
|
+
end
|
53
|
+
elsif grouped_results.is_a?(Array)
|
54
|
+
numbered_results = grouped_results.map.with_index { |v, i| "#{i + 1}. #{v}" }
|
55
|
+
|
56
|
+
puts " " * indent_level + numbered_results.join("\n" + " " * indent_level) + "\n"
|
57
|
+
else
|
58
|
+
puts " " * indent_level + grouped_results.to_s + "\n"
|
59
|
+
end
|
60
|
+
end
|
17
61
|
|
18
62
|
input = ARGV[0]
|
19
63
|
unless input
|
20
|
-
raise ArgumentError,
|
64
|
+
raise ArgumentError,
|
65
|
+
"Argument required, either a CSV file path or a CSV string.\nExamples:\n" \
|
66
|
+
"reading /home/felipe/reading.csv\n" \
|
67
|
+
"reading '3|📕Trying|Little Library 1970147288'"
|
21
68
|
end
|
22
69
|
|
23
|
-
config = {}
|
24
70
|
if ARGV[1]
|
25
71
|
enabled_columns = ARGV[1].split(",").map(&:strip).map(&:to_sym)
|
26
|
-
|
72
|
+
Reading::Config.build(enabled_columns:)
|
27
73
|
end
|
28
74
|
|
29
|
-
|
75
|
+
input_is_csv_path = input.end_with?('.csv')
|
76
|
+
|
77
|
+
if input_is_csv_path
|
78
|
+
error_handler = ->(e) { puts "Skipped a row due to a parsing error: #{e}" }
|
79
|
+
|
80
|
+
items = Reading.parse(path: input, item_view: false, error_handler:)
|
30
81
|
|
31
|
-
|
82
|
+
loop do
|
83
|
+
raw_input = Readline.readline(PASTEL.bright_cyan("> "), true)
|
84
|
+
|
85
|
+
exit if EXIT_COMMANDS.include?(raw_input)
|
86
|
+
|
87
|
+
input = raw_input.presence
|
88
|
+
next if raw_input.blank?
|
89
|
+
|
90
|
+
results = Reading.stats(
|
91
|
+
input:,
|
92
|
+
items:,
|
93
|
+
result_formatters: Reading::Stats::ResultFormatters::TERMINAL,
|
94
|
+
)
|
95
|
+
|
96
|
+
if results.is_a?(Array) && results.first.is_a?(Reading::Item) # `debug` operation
|
97
|
+
r = results
|
98
|
+
puts PASTEL.red.bold("Enter 'c' to leave the debugger.")
|
99
|
+
debugger
|
100
|
+
else
|
101
|
+
print_grouped_results(results, GROUP_HEADING_FORMATTERS)
|
102
|
+
end
|
103
|
+
rescue Reading::Error => e
|
104
|
+
puts e
|
105
|
+
end
|
106
|
+
else # CSV string arg
|
107
|
+
input = input.gsub("\\|", "|") # because some pipes are escaped when pasting into the terminal
|
108
|
+
|
109
|
+
begin
|
110
|
+
item_hashes = Reading.parse(lines: input, hash_output: true, item_view: false)
|
111
|
+
rescue Reading::Error => e
|
112
|
+
puts "Skipped a row due to a parsing error: #{e}"
|
113
|
+
end
|
114
|
+
|
115
|
+
ap item_hashes
|
116
|
+
end
|
data/lib/reading/config.rb
CHANGED
@@ -1,15 +1,26 @@
|
|
1
|
-
require_relative
|
2
|
-
require_relative "util/hash_array_deep_fetch"
|
3
|
-
require_relative "errors"
|
1
|
+
require_relative 'errors'
|
4
2
|
|
5
3
|
module Reading
|
6
|
-
# Builds a hash config.
|
4
|
+
# Builds a singleton hash config.
|
7
5
|
class Config
|
8
6
|
using Util::HashDeepMerge
|
9
7
|
using Util::HashArrayDeepFetch
|
10
8
|
|
11
9
|
attr_reader :hash
|
12
10
|
|
11
|
+
# Builds an entire config hash from a custom config hash (which is typically
|
12
|
+
# not an entire config, but it can be, in which case a copy is returned).
|
13
|
+
# @param custom_config [Hash, Config]
|
14
|
+
# @return [Hash]
|
15
|
+
def self.build(custom_config = {})
|
16
|
+
@hash = new(custom_config).hash
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Hash]
|
20
|
+
def self.hash
|
21
|
+
@hash ||= build
|
22
|
+
end
|
23
|
+
|
13
24
|
# @param custom_config [Hash] a custom config which overrides the defaults,
|
14
25
|
# e.g. { enabled_columns: [:head, :end_dates] }
|
15
26
|
def initialize(custom_config = {})
|
@@ -55,6 +66,16 @@ module Reading
|
|
55
66
|
column_separator: "|",
|
56
67
|
ignored_characters: "✅❌💲❓⏳",
|
57
68
|
skip_compact_planned: false,
|
69
|
+
pages_per_hour: 35,
|
70
|
+
length_group_boundaries: [200, 400, 600, 1000, 2000],
|
71
|
+
speed: # e.g. listening speed for audiobooks and podcasts.
|
72
|
+
{
|
73
|
+
format:
|
74
|
+
{
|
75
|
+
audiobook: 1.0,
|
76
|
+
audio: 1.0,
|
77
|
+
},
|
78
|
+
},
|
58
79
|
# The Head column is always enabled; the others can be disabled by
|
59
80
|
# using a custom config that omits columns from this array.
|
60
81
|
enabled_columns:
|
@@ -86,6 +107,7 @@ module Reading
|
|
86
107
|
},
|
87
108
|
source_names_from_urls:
|
88
109
|
{
|
110
|
+
"audible.com" => "Audible",
|
89
111
|
"youtube.com" => "YouTube",
|
90
112
|
"youtu.be" => "YouTube",
|
91
113
|
"books.google.com" => "Google Books",
|
@@ -157,7 +179,7 @@ module Reading
|
|
157
179
|
[{
|
158
180
|
dates: nil,
|
159
181
|
amount: 0,
|
160
|
-
progress:
|
182
|
+
progress: 1.0,
|
161
183
|
name: nil,
|
162
184
|
favorite?: false,
|
163
185
|
}],
|
data/lib/reading/errors.rb
CHANGED
@@ -7,7 +7,7 @@ module Reading
|
|
7
7
|
# Means there is something wrong with the user-supplied custom config.
|
8
8
|
class ConfigError < Reading::Error; end
|
9
9
|
|
10
|
-
# Means unexpected input was encountered during parsing.
|
10
|
+
# Means unexpected input was encountered during CSV parsing.
|
11
11
|
class ParsingError < Reading::Error; end
|
12
12
|
|
13
13
|
# Means something in the Head column (author, title, etc.) is invalid.
|
@@ -21,4 +21,7 @@ module Reading
|
|
21
21
|
|
22
22
|
# Means a date is unparsable, or a set of dates does not make logical sense.
|
23
23
|
class InvalidDateError < Reading::Error; end
|
24
|
+
|
25
|
+
# Means a stats command was entered incorrectly.
|
26
|
+
class InputError < Reading::Error; end
|
24
27
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Reading
|
2
2
|
class Item
|
3
3
|
# The length of an item when it is a time, as opposed to pages. (Pages are
|
4
|
-
# represented simply with an Integer.)
|
4
|
+
# represented simply with an Integer or Float.)
|
5
5
|
class Item::TimeLength
|
6
6
|
include Comparable
|
7
7
|
|
@@ -14,16 +14,32 @@ module Reading
|
|
14
14
|
|
15
15
|
# Builds an Item::TimeLength from a string.
|
16
16
|
# @param string [String] a time duration in "h:mm" format.
|
17
|
-
# @return [TimeLength]
|
17
|
+
# @return [TimeLength, nil]
|
18
18
|
def self.parse(string)
|
19
|
+
return nil unless string.match? /\A\d+:\d\d\z/
|
20
|
+
|
19
21
|
hours, minutes = string.split(':').map(&:to_i)
|
20
22
|
new((hours * 60) + minutes)
|
21
23
|
end
|
22
24
|
|
25
|
+
# Builds an Item::TimeLength based on a page count.
|
26
|
+
# @param pages [Integer, Float]
|
27
|
+
# @return [TimeLength]
|
28
|
+
def self.from_pages(pages)
|
29
|
+
new(pages_to_minutes(pages))
|
30
|
+
end
|
31
|
+
|
32
|
+
# Converts a page count to minutes.
|
33
|
+
# @param pages [Integer, Float]
|
34
|
+
# @return [Integer]
|
35
|
+
def self.pages_to_minutes(pages)
|
36
|
+
(pages.to_f / Config.hash.fetch(:pages_per_hour) * 60)
|
37
|
+
end
|
38
|
+
|
23
39
|
# Only the hours, e.g. the "h" value in "h:mm".
|
24
40
|
# @return [Numeric]
|
25
41
|
def hours
|
26
|
-
value / 60
|
42
|
+
value.to_i / 60
|
27
43
|
end
|
28
44
|
|
29
45
|
# Only the hours, e.g. the "mm" value in "h:mm".
|
@@ -35,7 +51,13 @@ module Reading
|
|
35
51
|
# A string in "h:mm" format.
|
36
52
|
# @return [String]
|
37
53
|
def to_s
|
38
|
-
"#{hours}:#{minutes}"
|
54
|
+
"#{hours}:#{minutes.round.to_s.rjust(2, '0')} or #{(value / 60.0 * Config.hash.fetch(:pages_per_hour)).round} pages"
|
55
|
+
end
|
56
|
+
|
57
|
+
# To pages.
|
58
|
+
# @return [Integer]
|
59
|
+
def to_i
|
60
|
+
((value / 60.0) * Config.hash.fetch(:pages_per_hour)).to_i
|
39
61
|
end
|
40
62
|
|
41
63
|
# @return [Boolean]
|
@@ -43,6 +65,12 @@ module Reading
|
|
43
65
|
value.zero?
|
44
66
|
end
|
45
67
|
|
68
|
+
# A copy of self with a rounded @value.
|
69
|
+
# @return [TimeLength]
|
70
|
+
def round
|
71
|
+
self.class.new(@value.round)
|
72
|
+
end
|
73
|
+
|
46
74
|
# Converts @value to an Integer if it's a whole number, and returns self.
|
47
75
|
# @return [TimeLength]
|
48
76
|
def to_i_if_whole!
|
@@ -62,27 +90,25 @@ module Reading
|
|
62
90
|
self.class.new(@value.to_i)
|
63
91
|
end
|
64
92
|
|
65
|
-
#
|
66
|
-
# @param other [TimeLength, Integer] must be zero if it's an Integer.
|
93
|
+
# @param other [TimeLength, Numeric]
|
67
94
|
# @return [TimeLength]
|
68
95
|
def +(other)
|
69
96
|
if other.is_a? Item::TimeLength
|
70
97
|
self.class.new(value + other.value)
|
71
|
-
elsif other.
|
72
|
-
self
|
98
|
+
elsif other.is_a? Numeric
|
99
|
+
self.class.new(value + self.class.pages_to_minutes(other))
|
73
100
|
else
|
74
101
|
raise TypeError, "#{other.class} can't be added to Item::TimeLength."
|
75
102
|
end
|
76
103
|
end
|
77
104
|
|
78
|
-
#
|
79
|
-
# @param other [TimeLength, Integer] must be zero if it's an Integer.
|
105
|
+
# @param other [TimeLength, Numeric]
|
80
106
|
# @return [TimeLength]
|
81
107
|
def -(other)
|
82
108
|
if other.is_a? Item::TimeLength
|
83
109
|
self.class.new(value - other.value)
|
84
|
-
elsif other.
|
85
|
-
self
|
110
|
+
elsif other.is_a? Numeric
|
111
|
+
self.class.new(value - self.class.pages_to_minutes(other))
|
86
112
|
else
|
87
113
|
raise TypeError, "#{other.class} can't be subtracted from Item::TimeLength."
|
88
114
|
end
|
@@ -108,25 +134,23 @@ module Reading
|
|
108
134
|
end
|
109
135
|
end
|
110
136
|
|
111
|
-
#
|
112
|
-
#
|
113
|
-
# @param other [Integer] must be zero.
|
137
|
+
# See https://web.archive.org/web/20221206095821/https://www.mutuallyhuman.com/blog/class-coercion-in-ruby/
|
138
|
+
# @param other [Numeric]
|
114
139
|
def coerce(other)
|
115
|
-
if other.
|
116
|
-
[self.class.
|
140
|
+
if other.is_a? Numeric
|
141
|
+
[self.class.from_pages(other), self]
|
117
142
|
else
|
118
|
-
raise TypeError, "
|
143
|
+
raise TypeError, "#{other.class} can't be coerced into a TimeLength."
|
119
144
|
end
|
120
145
|
end
|
121
146
|
|
122
|
-
#
|
123
|
-
# @
|
147
|
+
# @param other [TimeLength, Numeric]
|
148
|
+
# @return [Integer]
|
124
149
|
def <=>(other)
|
125
150
|
return 1 if other.nil?
|
126
151
|
|
127
|
-
if other.
|
128
|
-
|
129
|
-
return 1
|
152
|
+
if other.is_a? Numeric
|
153
|
+
other = self.class.from_pages(other)
|
130
154
|
end
|
131
155
|
|
132
156
|
unless other.is_a? Item::TimeLength
|
@@ -135,6 +159,19 @@ module Reading
|
|
135
159
|
|
136
160
|
value <=> other.value
|
137
161
|
end
|
162
|
+
|
163
|
+
# Must be implemented for hash key equality checks.
|
164
|
+
# @param other [TimeLength, Numeric]
|
165
|
+
# @return [Boolean]
|
166
|
+
def eql?(other)
|
167
|
+
hash == other.hash
|
168
|
+
end
|
169
|
+
|
170
|
+
# Must be implemented along with #eql? for hash key equality checks.
|
171
|
+
# @return [Integer]
|
172
|
+
def hash
|
173
|
+
value
|
174
|
+
end
|
138
175
|
end
|
139
176
|
end
|
140
177
|
end
|
data/lib/reading/item/view.rb
CHANGED
@@ -9,13 +9,12 @@ module Reading
|
|
9
9
|
:isbn, :url, :experience_count, :groups, :blurb, :public_notes
|
10
10
|
|
11
11
|
# @param item [Item] the Item from which to extract view information.
|
12
|
-
|
13
|
-
def initialize(item, config)
|
12
|
+
def initialize(item)
|
14
13
|
@genres = item.genres
|
15
|
-
@rating = extract_star_or_rating(item
|
16
|
-
@isbn, @url, variant = extract_first_source_info(item
|
17
|
-
@name = extract_name(item, variant
|
18
|
-
@type_emoji = extract_type_emoji(variant&.format
|
14
|
+
@rating = extract_star_or_rating(item)
|
15
|
+
@isbn, @url, variant = extract_first_source_info(item)
|
16
|
+
@name = extract_name(item, variant)
|
17
|
+
@type_emoji = extract_type_emoji(variant&.format)
|
19
18
|
@date_or_status = extract_date_or_status(item)
|
20
19
|
@experience_count = item.experiences.count
|
21
20
|
@groups = item.experiences.map(&:group).compact
|
@@ -28,10 +27,9 @@ module Reading
|
|
28
27
|
# A star (or nil if the item doesn't make the cut), or the number rating if
|
29
28
|
# star ratings are disabled.
|
30
29
|
# @param item [Item]
|
31
|
-
# @param config [Hash] an entire config.
|
32
30
|
# @return [String, Integer, Float]
|
33
|
-
def extract_star_or_rating(item
|
34
|
-
minimum_rating =
|
31
|
+
def extract_star_or_rating(item)
|
32
|
+
minimum_rating = Config.hash.deep_fetch(:item, :view, :minimum_rating_for_star)
|
35
33
|
if minimum_rating
|
36
34
|
"⭐" if item.rating && item.rating >= minimum_rating
|
37
35
|
else
|
@@ -44,13 +42,12 @@ module Reading
|
|
44
42
|
# an ISBN/ASIN or URL. If an ISBN/ASIN is found first, it is used to build a
|
45
43
|
# Goodreads URL. If a URL is found first, the ISBN/ASIN is nil.
|
46
44
|
# @param item [Item]
|
47
|
-
# @param config [Hash] an entire config.
|
48
45
|
# @return [Array(String, String, Symbol, Array<String>)]
|
49
|
-
def extract_first_source_info(item
|
46
|
+
def extract_first_source_info(item)
|
50
47
|
item.variants.map { |variant|
|
51
48
|
isbn = variant.isbn
|
52
49
|
if isbn
|
53
|
-
url =
|
50
|
+
url = Config.hash.deep_fetch(:item, :view, :url_from_isbn).sub('%{isbn}', isbn)
|
54
51
|
else
|
55
52
|
url = variant.sources.map { |source| source.url }.compact.first
|
56
53
|
end
|
@@ -64,9 +61,8 @@ module Reading
|
|
64
61
|
# The view name of the item.
|
65
62
|
# @param item [Item]
|
66
63
|
# @param variant [Data, nil] a variant from the Item.
|
67
|
-
# @param config [Hash] an entire config.
|
68
64
|
# @return [String]
|
69
|
-
def extract_name(item, variant
|
65
|
+
def extract_name(item, variant)
|
70
66
|
author_and_title = "#{item.author + " – " if item.author}#{item.title}"
|
71
67
|
return author_and_title if variant.nil?
|
72
68
|
|
@@ -79,7 +75,7 @@ module Reading
|
|
79
75
|
end
|
80
76
|
}
|
81
77
|
|
82
|
-
name_separator =
|
78
|
+
name_separator = Config.hash.deep_fetch(:item, :view, :name_separator)
|
83
79
|
series_and_extra_info = name_separator +
|
84
80
|
(pretty_series + variant.extra_info).join(name_separator)
|
85
81
|
end
|
@@ -89,10 +85,9 @@ module Reading
|
|
89
85
|
|
90
86
|
# The emoji for the type that represents (encompasses) a given format.
|
91
87
|
# @param format [Symbol, nil]
|
92
|
-
# @param config [Hash] an entire config.
|
93
88
|
# @return [String]
|
94
|
-
def extract_type_emoji(format
|
95
|
-
types =
|
89
|
+
def extract_type_emoji(format)
|
90
|
+
types = Config.hash.deep_fetch(:item, :view, :types)
|
96
91
|
|
97
92
|
return types.deep_fetch(format, :emoji) if types.has_key?(format)
|
98
93
|
|
@@ -101,7 +96,7 @@ module Reading
|
|
101
96
|
&.first # key
|
102
97
|
|
103
98
|
types.deep_fetch(
|
104
|
-
type ||
|
99
|
+
type || Config.hash.deep_fetch(:item, :view, :default_type),
|
105
100
|
:emoji,
|
106
101
|
)
|
107
102
|
end
|