reading 0.6.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 +7 -0
- data/bin/reading +31 -0
- data/lib/reading/attribute/all_attributes.rb +83 -0
- data/lib/reading/attribute/attribute.rb +25 -0
- data/lib/reading/attribute/experiences/dates_validator.rb +94 -0
- data/lib/reading/attribute/experiences/experiences_attribute.rb +74 -0
- data/lib/reading/attribute/experiences/progress_subattribute.rb +48 -0
- data/lib/reading/attribute/experiences/spans_subattribute.rb +82 -0
- data/lib/reading/attribute/variants/extra_info_subattribute.rb +44 -0
- data/lib/reading/attribute/variants/length_subattribute.rb +45 -0
- data/lib/reading/attribute/variants/series_subattribute.rb +57 -0
- data/lib/reading/attribute/variants/sources_subattribute.rb +78 -0
- data/lib/reading/attribute/variants/variants_attribute.rb +69 -0
- data/lib/reading/config.rb +202 -0
- data/lib/reading/csv.rb +67 -0
- data/lib/reading/errors.rb +77 -0
- data/lib/reading/line.rb +23 -0
- data/lib/reading/row/blank_row.rb +23 -0
- data/lib/reading/row/compact_planned_row.rb +130 -0
- data/lib/reading/row/regular_row.rb +94 -0
- data/lib/reading/row/row.rb +88 -0
- data/lib/reading/util/blank.rb +146 -0
- data/lib/reading/util/hash_array_deep_fetch.rb +40 -0
- data/lib/reading/util/hash_compact_by_template.rb +38 -0
- data/lib/reading/util/hash_deep_merge.rb +44 -0
- data/lib/reading/util/hash_to_struct.rb +29 -0
- data/lib/reading/util/string_remove.rb +28 -0
- data/lib/reading/util/string_truncate.rb +13 -0
- data/lib/reading/version.rb +3 -0
- metadata +174 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
module Reading
|
2
|
+
# A base class that contains behaviors common to ___Row classes.
|
3
|
+
class Row
|
4
|
+
using Util::StringRemove
|
5
|
+
using Util::HashArrayDeepFetch
|
6
|
+
using Util::HashCompactByTemplate
|
7
|
+
|
8
|
+
private attr_reader :line
|
9
|
+
|
10
|
+
# @param line [Reading::Line] the Line that this Row represents.
|
11
|
+
def initialize(line)
|
12
|
+
@line = line
|
13
|
+
|
14
|
+
after_initialize
|
15
|
+
end
|
16
|
+
|
17
|
+
# Parses a CSV row into an array of hashes of item data. How this is done
|
18
|
+
# depends on how the template methods (further below) are implemented in
|
19
|
+
# subclasses of Row.
|
20
|
+
# @return [Array<Hash>] an array of hashes like the template in config.rb
|
21
|
+
def parse
|
22
|
+
return [] if skip?
|
23
|
+
|
24
|
+
before_parse
|
25
|
+
|
26
|
+
items = item_heads.map { |item_head|
|
27
|
+
item_hash(item_head)
|
28
|
+
.compact_by(template: config.deep_fetch(:item, :template))
|
29
|
+
}.compact
|
30
|
+
|
31
|
+
items
|
32
|
+
|
33
|
+
rescue Reading::Error => e
|
34
|
+
e.handle(line:)
|
35
|
+
[]
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def string
|
41
|
+
@line.string
|
42
|
+
end
|
43
|
+
|
44
|
+
def config
|
45
|
+
@line.csv.config
|
46
|
+
end
|
47
|
+
|
48
|
+
# A "head" is a string in the Head column containing a chunk of item
|
49
|
+
# information, starting with a format emoji. A typical row describes one
|
50
|
+
# item and so contains one head, but a row describing multiple items (with
|
51
|
+
# multiple heads in the Head column) is possible. Also, a row of compact
|
52
|
+
# planned items is essentially a list of heads, though with different
|
53
|
+
# elements than a normal row's head.
|
54
|
+
# @return [Array<String>]
|
55
|
+
def item_heads
|
56
|
+
string_to_be_split_by_format_emojis
|
57
|
+
.split(config.deep_fetch(:csv, :regex, :formats_split))
|
58
|
+
.tap { |item_heads|
|
59
|
+
item_heads.first.remove!(config.deep_fetch(:csv, :regex, :dnf))
|
60
|
+
item_heads.first.remove!(config.deep_fetch(:csv, :regex, :progress))
|
61
|
+
}
|
62
|
+
.map { |item_head| item_head.strip }
|
63
|
+
.partition { |item_head| item_head.match?(/\A#{config.deep_fetch(:csv, :regex, :formats)}/) }
|
64
|
+
.reject(&:empty?)
|
65
|
+
.first
|
66
|
+
end
|
67
|
+
|
68
|
+
# Below: template methods that can (or must) be overridden.
|
69
|
+
|
70
|
+
def after_initialize
|
71
|
+
end
|
72
|
+
|
73
|
+
def before_parse
|
74
|
+
end
|
75
|
+
|
76
|
+
def skip?
|
77
|
+
false
|
78
|
+
end
|
79
|
+
|
80
|
+
def string_to_be_split_by_format_emojis
|
81
|
+
raise NotImplementedError, "#{self.class} should have implemented #{__method__}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def item_hash(item_head)
|
85
|
+
raise NotImplementedError, "#{self.class} should have implemented #{__method__}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# Copied from active_support/core_ext/object/blank
|
2
|
+
# https://github.com/rails/rails/blob/main/activesupport/lib/active_support/core_ext/object/blank.rb
|
3
|
+
# Except detection of ENCODED_BLANKS is omitted here.
|
4
|
+
class Object
|
5
|
+
# An object is blank if it's false, empty, or a whitespace string.
|
6
|
+
# For example, +nil+, '', ' ', [], {}, and +false+ are all blank.
|
7
|
+
#
|
8
|
+
# This simplifies
|
9
|
+
#
|
10
|
+
# !address || address.empty?
|
11
|
+
#
|
12
|
+
# to
|
13
|
+
#
|
14
|
+
# address.blank?
|
15
|
+
#
|
16
|
+
# @return [true, false]
|
17
|
+
def blank?
|
18
|
+
respond_to?(:empty?) ? !!empty? : !self
|
19
|
+
end
|
20
|
+
|
21
|
+
# An object is present if it's not blank.
|
22
|
+
#
|
23
|
+
# @return [true, false]
|
24
|
+
def present?
|
25
|
+
!blank?
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the receiver if it's present otherwise returns +nil+.
|
29
|
+
# <tt>object.presence</tt> is equivalent to
|
30
|
+
#
|
31
|
+
# object.present? ? object : nil
|
32
|
+
#
|
33
|
+
# For example, something like
|
34
|
+
#
|
35
|
+
# state = params[:state] if params[:state].present?
|
36
|
+
# country = params[:country] if params[:country].present?
|
37
|
+
# region = state || country || 'US'
|
38
|
+
#
|
39
|
+
# becomes
|
40
|
+
#
|
41
|
+
# region = params[:state].presence || params[:country].presence || 'US'
|
42
|
+
#
|
43
|
+
# @return [Object]
|
44
|
+
def presence
|
45
|
+
self if present?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class NilClass
|
50
|
+
# +nil+ is blank:
|
51
|
+
#
|
52
|
+
# nil.blank? # => true
|
53
|
+
#
|
54
|
+
# @return [true]
|
55
|
+
def blank?
|
56
|
+
true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class FalseClass
|
61
|
+
# +false+ is blank:
|
62
|
+
#
|
63
|
+
# false.blank? # => true
|
64
|
+
#
|
65
|
+
# @return [true]
|
66
|
+
def blank?
|
67
|
+
true
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class TrueClass
|
72
|
+
# +true+ is not blank:
|
73
|
+
#
|
74
|
+
# true.blank? # => false
|
75
|
+
#
|
76
|
+
# @return [false]
|
77
|
+
def blank?
|
78
|
+
false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class Array
|
83
|
+
# An array is blank if it's empty:
|
84
|
+
#
|
85
|
+
# [].blank? # => true
|
86
|
+
# [1,2,3].blank? # => false
|
87
|
+
#
|
88
|
+
# @return [true, false]
|
89
|
+
alias_method :blank?, :empty?
|
90
|
+
end
|
91
|
+
|
92
|
+
class Hash
|
93
|
+
# A hash is blank if it's empty:
|
94
|
+
#
|
95
|
+
# {}.blank? # => true
|
96
|
+
# { key: 'value' }.blank? # => false
|
97
|
+
#
|
98
|
+
# @return [true, false]
|
99
|
+
alias_method :blank?, :empty?
|
100
|
+
end
|
101
|
+
|
102
|
+
class String
|
103
|
+
BLANK_RE = /\A[[:space:]]*\z/
|
104
|
+
|
105
|
+
# A string is blank if it's empty or contains whitespaces only:
|
106
|
+
#
|
107
|
+
# ''.blank? # => true
|
108
|
+
# ' '.blank? # => true
|
109
|
+
# "\t\n\r".blank? # => true
|
110
|
+
# ' blah '.blank? # => false
|
111
|
+
#
|
112
|
+
# Unicode whitespace is supported:
|
113
|
+
#
|
114
|
+
# "\u00a0".blank? # => true
|
115
|
+
#
|
116
|
+
# @return [true, false]
|
117
|
+
def blank?
|
118
|
+
# The regexp that matches blank strings is expensive. For the case of empty
|
119
|
+
# strings we can speed up this method (~3.5x) with an empty? call. The
|
120
|
+
# penalty for the rest of strings is marginal.
|
121
|
+
empty? || BLANK_RE.match?(self)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class Numeric # :nodoc:
|
126
|
+
# No number is blank:
|
127
|
+
#
|
128
|
+
# 1.blank? # => false
|
129
|
+
# 0.blank? # => false
|
130
|
+
#
|
131
|
+
# @return [false]
|
132
|
+
def blank?
|
133
|
+
false
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class Time # :nodoc:
|
138
|
+
# No Time is blank:
|
139
|
+
#
|
140
|
+
# Time.now.blank? # => false
|
141
|
+
#
|
142
|
+
# @return [false]
|
143
|
+
def blank?
|
144
|
+
false
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Reading
|
2
|
+
module Util
|
3
|
+
class FetchDepthExceededError < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
# 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
|
+
module HashArrayDeepFetch
|
14
|
+
def deep_fetch(*keys)
|
15
|
+
case keys.length
|
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
|
29
|
+
end
|
30
|
+
|
31
|
+
refine Hash do
|
32
|
+
import_methods HashArrayDeepFetch
|
33
|
+
end
|
34
|
+
|
35
|
+
refine Array do
|
36
|
+
import_methods HashArrayDeepFetch
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Reading
|
2
|
+
module Util
|
3
|
+
# Utility method for a hash containing parsed item data, structured as the
|
4
|
+
# template in config.rb.
|
5
|
+
module HashCompactByTemplate
|
6
|
+
refine Hash do
|
7
|
+
# Removes blank arrays of hashes from the given item hash, e.g. series,
|
8
|
+
# variants, variants[:sources], and experiences in the template in config.rb.
|
9
|
+
# If no parsed data has been added to the template values for these, they
|
10
|
+
# are considered blank, and are replaced with an empty array so that their
|
11
|
+
# emptiness is more apparent, e.g. item[:experiences].empty? will return true.
|
12
|
+
def compact_by(template:)
|
13
|
+
map { |key, val|
|
14
|
+
if is_array_of_hashes?(val)
|
15
|
+
if is_blank_like_template?(val, template.fetch(key))
|
16
|
+
[key, []]
|
17
|
+
else
|
18
|
+
[key, val.map { |el| el.compact_by(template: template.fetch(key).first) }]
|
19
|
+
end
|
20
|
+
else
|
21
|
+
[key, val]
|
22
|
+
end
|
23
|
+
}.to_h
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def is_array_of_hashes?(val)
|
29
|
+
val.is_a?(Array) && val.first.is_a?(Hash)
|
30
|
+
end
|
31
|
+
|
32
|
+
def is_blank_like_template?(val, template_val)
|
33
|
+
val.length == 1 && val == template_val
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Reading
|
2
|
+
module Util
|
3
|
+
# Modified from active_support/core_ext/hash/deep_merge
|
4
|
+
# https://github.com/rails/rails/blob/main/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
|
5
|
+
#
|
6
|
+
# This deep_merge also iterates through arrays of hashes and merges them.
|
7
|
+
module HashDeepMerge
|
8
|
+
refine Hash do
|
9
|
+
def deep_merge(other_hash, &block)
|
10
|
+
dup.deep_merge!(other_hash, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def deep_merge!(other_hash, &block)
|
14
|
+
merge!(other_hash) do |key, this_val, other_val|
|
15
|
+
if this_val.is_a?(Hash) && other_val.is_a?(Hash)
|
16
|
+
this_val.deep_merge(other_val, &block)
|
17
|
+
# I added this part for merging values that are arrays of hashes.
|
18
|
+
elsif this_val.is_a?(Array) && other_val.is_a?(Array) &&
|
19
|
+
this_val.all? { |el| el.is_a?(Hash) } &&
|
20
|
+
other_val.all? { |el| el.is_a?(Hash) }
|
21
|
+
zip =
|
22
|
+
if other_val.length >= this_val.length
|
23
|
+
other_val.zip(this_val)
|
24
|
+
else
|
25
|
+
this_val.zip(other_val).map(&:reverse)
|
26
|
+
end
|
27
|
+
zip.map { |other_el, this_el|
|
28
|
+
if this_el.nil?
|
29
|
+
other_el
|
30
|
+
else
|
31
|
+
this_el.deep_merge(other_el || {}, &block)
|
32
|
+
end
|
33
|
+
}
|
34
|
+
elsif block_given?
|
35
|
+
block.call(key, this_val, other_val)
|
36
|
+
else
|
37
|
+
other_val
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Reading
|
2
|
+
module Util
|
3
|
+
# Converts a Hash to a Struct. Converts inner hashes (and inner arrays of hashes) as well.
|
4
|
+
module HashToStruct
|
5
|
+
refine Hash do
|
6
|
+
def to_struct
|
7
|
+
MEMOIZED_STRUCTS[keys] ||= Struct.new(*keys)
|
8
|
+
struct_class = MEMOIZED_STRUCTS[keys]
|
9
|
+
|
10
|
+
struct_values = transform_values { |v|
|
11
|
+
if v.is_a?(Hash)
|
12
|
+
v.to_struct
|
13
|
+
elsif v.is_a?(Array) && v.all? { |el| el.is_a?(Hash) }
|
14
|
+
v.map(&:to_struct)
|
15
|
+
else
|
16
|
+
v
|
17
|
+
end
|
18
|
+
}.values
|
19
|
+
|
20
|
+
struct_class.new(*struct_values)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
MEMOIZED_STRUCTS = {}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Reading
|
2
|
+
module Util
|
3
|
+
# Shortcuts for String#sub and String#gsub when replacing with an empty string.
|
4
|
+
module StringRemove
|
5
|
+
refine String do
|
6
|
+
def remove(pattern)
|
7
|
+
sub(pattern, EMPTY_STRING)
|
8
|
+
end
|
9
|
+
|
10
|
+
def remove!(pattern)
|
11
|
+
sub!(pattern, EMPTY_STRING)
|
12
|
+
end
|
13
|
+
|
14
|
+
def remove_all(pattern)
|
15
|
+
gsub(pattern, EMPTY_STRING)
|
16
|
+
end
|
17
|
+
|
18
|
+
def remove_all!(pattern)
|
19
|
+
gsub!(pattern, EMPTY_STRING)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
EMPTY_STRING = "".freeze
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Reading
|
2
|
+
module Util
|
3
|
+
module StringTruncate
|
4
|
+
refine String do
|
5
|
+
def truncate(max, padding: 0, min: 30)
|
6
|
+
end_index = max - padding
|
7
|
+
end_index = min if end_index < min
|
8
|
+
self.length + padding > max ? "#{self[0...end_index]}..." : self
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: reading
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Felipe Vogel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-12-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pastel
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.8'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: debug
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.0.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.0.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest-reporters
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pretty-diffs
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: amazing_print
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubycritic
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description:
|
112
|
+
email:
|
113
|
+
- fps.vogel@gmail.com
|
114
|
+
executables:
|
115
|
+
- reading
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- bin/reading
|
120
|
+
- lib/reading/attribute/all_attributes.rb
|
121
|
+
- lib/reading/attribute/attribute.rb
|
122
|
+
- lib/reading/attribute/experiences/dates_validator.rb
|
123
|
+
- lib/reading/attribute/experiences/experiences_attribute.rb
|
124
|
+
- lib/reading/attribute/experiences/progress_subattribute.rb
|
125
|
+
- lib/reading/attribute/experiences/spans_subattribute.rb
|
126
|
+
- lib/reading/attribute/variants/extra_info_subattribute.rb
|
127
|
+
- lib/reading/attribute/variants/length_subattribute.rb
|
128
|
+
- lib/reading/attribute/variants/series_subattribute.rb
|
129
|
+
- lib/reading/attribute/variants/sources_subattribute.rb
|
130
|
+
- lib/reading/attribute/variants/variants_attribute.rb
|
131
|
+
- lib/reading/config.rb
|
132
|
+
- lib/reading/csv.rb
|
133
|
+
- lib/reading/errors.rb
|
134
|
+
- lib/reading/line.rb
|
135
|
+
- lib/reading/row/blank_row.rb
|
136
|
+
- lib/reading/row/compact_planned_row.rb
|
137
|
+
- lib/reading/row/regular_row.rb
|
138
|
+
- lib/reading/row/row.rb
|
139
|
+
- lib/reading/util/blank.rb
|
140
|
+
- lib/reading/util/hash_array_deep_fetch.rb
|
141
|
+
- lib/reading/util/hash_compact_by_template.rb
|
142
|
+
- lib/reading/util/hash_deep_merge.rb
|
143
|
+
- lib/reading/util/hash_to_struct.rb
|
144
|
+
- lib/reading/util/string_remove.rb
|
145
|
+
- lib/reading/util/string_truncate.rb
|
146
|
+
- lib/reading/version.rb
|
147
|
+
homepage: https://github.com/fpsvogel/reading
|
148
|
+
licenses:
|
149
|
+
- MIT
|
150
|
+
metadata:
|
151
|
+
allowed_push_host: https://rubygems.org
|
152
|
+
homepage_uri: https://github.com/fpsvogel/reading
|
153
|
+
source_code_uri: https://github.com/fpsvogel/reading
|
154
|
+
changelog_uri: https://github.com/fpsvogel/reading/blob/master/CHANGELOG.md
|
155
|
+
post_install_message:
|
156
|
+
rdoc_options: []
|
157
|
+
require_paths:
|
158
|
+
- lib
|
159
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - ">="
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: 3.0.0
|
164
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
|
+
requirements:
|
166
|
+
- - ">="
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: '0'
|
169
|
+
requirements: []
|
170
|
+
rubygems_version: 3.4.1
|
171
|
+
signing_key:
|
172
|
+
specification_version: 4
|
173
|
+
summary: reading parses a CSV reading log.
|
174
|
+
test_files: []
|