metal_archives 2.1.1 → 3.1.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 +5 -5
- data/.github/workflows/ci.yml +93 -0
- data/.gitignore +6 -6
- data/.overcommit.yml +35 -0
- data/.rspec +2 -0
- data/.rubocop.yml +69 -6
- data/CHANGELOG.md +29 -0
- data/Gemfile +1 -1
- data/LICENSE.md +17 -4
- data/README.md +65 -86
- data/Rakefile +8 -7
- data/bin/console +38 -0
- data/bin/setup +8 -0
- data/config/inflections.rb +7 -0
- data/config/initializers/.keep +0 -0
- data/docker-compose.yml +23 -0
- data/lib/metal_archives.rb +82 -25
- data/lib/metal_archives/cache/base.rb +40 -0
- data/lib/metal_archives/cache/memory.rb +68 -0
- data/lib/metal_archives/cache/null.rb +22 -0
- data/lib/metal_archives/cache/redis.rb +49 -0
- data/lib/metal_archives/{utils/collection.rb → collection.rb} +3 -5
- data/lib/metal_archives/configuration.rb +33 -50
- data/lib/metal_archives/{error.rb → errors.rb} +9 -1
- data/lib/metal_archives/http_client.rb +45 -44
- data/lib/metal_archives/models/artist.rb +90 -45
- data/lib/metal_archives/models/band.rb +80 -55
- data/lib/metal_archives/models/base.rb +218 -0
- data/lib/metal_archives/models/label.rb +14 -15
- data/lib/metal_archives/models/release.rb +349 -0
- data/lib/metal_archives/parsers/artist.rb +86 -50
- data/lib/metal_archives/parsers/band.rb +155 -88
- data/lib/metal_archives/parsers/base.rb +14 -0
- data/lib/metal_archives/parsers/country.rb +21 -0
- data/lib/metal_archives/parsers/date.rb +31 -0
- data/lib/metal_archives/parsers/genre.rb +67 -0
- data/lib/metal_archives/parsers/label.rb +39 -31
- data/lib/metal_archives/parsers/parser.rb +16 -63
- data/lib/metal_archives/parsers/release.rb +242 -0
- data/lib/metal_archives/parsers/year.rb +29 -0
- data/lib/metal_archives/version.rb +12 -1
- data/metal_archives.env.example +10 -0
- data/metal_archives.gemspec +43 -28
- data/nginx/default.conf +60 -0
- metadata +181 -72
- data/.travis.yml +0 -12
- data/lib/metal_archives/middleware/cache_check.rb +0 -20
- data/lib/metal_archives/middleware/encoding.rb +0 -16
- data/lib/metal_archives/middleware/headers.rb +0 -38
- data/lib/metal_archives/middleware/rewrite_endpoint.rb +0 -38
- data/lib/metal_archives/models/base_model.rb +0 -215
- data/lib/metal_archives/utils/lru_cache.rb +0 -61
- data/lib/metal_archives/utils/nil_date.rb +0 -99
- data/lib/metal_archives/utils/range.rb +0 -66
- data/spec/configuration_spec.rb +0 -96
- data/spec/factories/artist_factory.rb +0 -37
- data/spec/factories/band_factory.rb +0 -60
- data/spec/factories/nil_date_factory.rb +0 -9
- data/spec/factories/range_factory.rb +0 -8
- data/spec/models/artist_spec.rb +0 -138
- data/spec/models/band_spec.rb +0 -164
- data/spec/models/base_model_spec.rb +0 -219
- data/spec/parser_spec.rb +0 -19
- data/spec/spec_helper.rb +0 -111
- data/spec/support/factory_girl.rb +0 -5
- data/spec/support/metal_archives.rb +0 -33
- data/spec/utils/collection_spec.rb +0 -72
- data/spec/utils/lru_cache_spec.rb +0 -53
- data/spec/utils/nil_date_spec.rb +0 -156
- data/spec/utils/range_spec.rb +0 -62
data/.travis.yml
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'faraday'
|
4
|
-
|
5
|
-
module MetalArchives
|
6
|
-
module Middleware
|
7
|
-
##
|
8
|
-
# Log cache status
|
9
|
-
#
|
10
|
-
class CacheCheck < Faraday::Response::Middleware # :nodoc:
|
11
|
-
def on_complete(env)
|
12
|
-
return unless MetalArchives.config.endpoint
|
13
|
-
|
14
|
-
if env[:response_headers].has_key? 'x-cache-status'
|
15
|
-
MetalArchives.config.logger.info "Cache #{env[:response_headers]['x-cache-status'].downcase} for #{env.url.to_s}"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'faraday'
|
4
|
-
|
5
|
-
module MetalArchives
|
6
|
-
module Middleware
|
7
|
-
##
|
8
|
-
# Force UTF-8 conversion
|
9
|
-
#
|
10
|
-
class Encoding < Faraday::Response::Middleware # :nodoc:
|
11
|
-
def on_complete(env)
|
12
|
-
env.response.body.force_encoding('utf-8')
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'faraday'
|
4
|
-
|
5
|
-
module MetalArchives
|
6
|
-
module Middleware
|
7
|
-
##
|
8
|
-
# Add appropriate request headers
|
9
|
-
#
|
10
|
-
class Headers < Faraday::Middleware # :nodoc:
|
11
|
-
def call(env)
|
12
|
-
headers = {
|
13
|
-
'User-Agent' => user_agent_string,
|
14
|
-
'Via' => via_string,
|
15
|
-
'Accept' => accept_string
|
16
|
-
}
|
17
|
-
|
18
|
-
env[:request_headers].merge! headers
|
19
|
-
|
20
|
-
@app.call env
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
def user_agent_string
|
26
|
-
"#{MetalArchives.config.app_name}/#{MetalArchives.config.app_version} ( #{MetalArchives.config.app_contact} )"
|
27
|
-
end
|
28
|
-
|
29
|
-
def accept_string
|
30
|
-
'application/json'
|
31
|
-
end
|
32
|
-
|
33
|
-
def via_string
|
34
|
-
"gem metal_archives/#{VERSION}"
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'faraday'
|
4
|
-
|
5
|
-
module MetalArchives
|
6
|
-
module Middleware
|
7
|
-
##
|
8
|
-
# Dynamically rewrite endpoints
|
9
|
-
#
|
10
|
-
class RewriteEndpoint < Faraday::Middleware
|
11
|
-
def call(env)
|
12
|
-
env[:url] = RewriteEndpoint.rewrite(env[:url])
|
13
|
-
|
14
|
-
@app.call env
|
15
|
-
end
|
16
|
-
|
17
|
-
class << self
|
18
|
-
def rewrite(uri)
|
19
|
-
return uri unless MetalArchives.config.endpoint
|
20
|
-
|
21
|
-
new_uri = uri.clone
|
22
|
-
|
23
|
-
default_uri = URI MetalArchives.config.default_endpoint
|
24
|
-
rewritten_uri = URI MetalArchives.config.endpoint
|
25
|
-
|
26
|
-
if uri.host == default_uri.host && uri.scheme == default_uri.scheme
|
27
|
-
new_uri.host = rewritten_uri.host
|
28
|
-
new_uri.scheme = rewritten_uri.scheme
|
29
|
-
|
30
|
-
MetalArchives.config.logger.debug "Rewrite #{uri.to_s} to #{new_uri}"
|
31
|
-
end
|
32
|
-
|
33
|
-
new_uri
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,215 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module MetalArchives
|
3
|
-
##
|
4
|
-
# Abstract model class
|
5
|
-
#
|
6
|
-
class BaseModel
|
7
|
-
##
|
8
|
-
# Generic shallow copy constructor
|
9
|
-
#
|
10
|
-
def initialize(hash = {})
|
11
|
-
raise Errors::NotImplementedError, 'no :id property in model' unless respond_to? :id?, true
|
12
|
-
|
13
|
-
hash.each do |property, value|
|
14
|
-
instance_variable_set("@#{property}", value) if self.class.properties.include? property
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
##
|
19
|
-
# Returns true if two objects have the same type and id
|
20
|
-
#
|
21
|
-
def ==(obj)
|
22
|
-
obj.instance_of?(self.class) && id ==(obj.id)
|
23
|
-
end
|
24
|
-
|
25
|
-
##
|
26
|
-
# Fetch, parse and load the data
|
27
|
-
#
|
28
|
-
# [Raises]
|
29
|
-
# - rdoc-ref:MetalArchives::Errors::InvalidIDError when no id
|
30
|
-
# - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
|
31
|
-
#
|
32
|
-
def load!
|
33
|
-
raise Errors::InvalidIDError, 'no id present' unless !!id
|
34
|
-
|
35
|
-
# Use constructor to set attributes
|
36
|
-
initialize assemble
|
37
|
-
|
38
|
-
# Set empty properties to nil
|
39
|
-
self.class.properties.each do |prop|
|
40
|
-
instance_variable_set("@#{prop}", nil) unless instance_variable_defined? "@#{prop}"
|
41
|
-
end
|
42
|
-
|
43
|
-
@loaded = true
|
44
|
-
self.class.cache[id] = self
|
45
|
-
rescue => e
|
46
|
-
# Don't cache invalid requests
|
47
|
-
self.class.cache.delete id
|
48
|
-
raise e
|
49
|
-
end
|
50
|
-
|
51
|
-
##
|
52
|
-
# Whether or not the object is currently loaded
|
53
|
-
#
|
54
|
-
def loaded?
|
55
|
-
!@loaded.nil?
|
56
|
-
end
|
57
|
-
|
58
|
-
##
|
59
|
-
# Whether or not the object is currently cached
|
60
|
-
#
|
61
|
-
def cached?
|
62
|
-
loaded? && self.class.cache.include?(id)
|
63
|
-
end
|
64
|
-
|
65
|
-
protected
|
66
|
-
|
67
|
-
##
|
68
|
-
# Fetch the data and assemble the model
|
69
|
-
#
|
70
|
-
# Override this method
|
71
|
-
#
|
72
|
-
# [Raises]
|
73
|
-
# - rdoc-ref:MetalArchives::Errors::InvalidIDError when no or invalid id
|
74
|
-
# - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
|
75
|
-
#
|
76
|
-
def assemble
|
77
|
-
raise Errors::NotImplementedError, 'method :assemble not implemented'
|
78
|
-
end
|
79
|
-
|
80
|
-
class << self
|
81
|
-
##
|
82
|
-
# +Array+ of declared properties
|
83
|
-
#
|
84
|
-
attr_accessor :properties
|
85
|
-
|
86
|
-
##
|
87
|
-
# Get class-level object cache
|
88
|
-
#
|
89
|
-
def cache
|
90
|
-
@cache ||= MetalArchives::LRUCache.new MetalArchives.config.cache_size
|
91
|
-
end
|
92
|
-
|
93
|
-
protected
|
94
|
-
|
95
|
-
##
|
96
|
-
# Defines a model property.
|
97
|
-
#
|
98
|
-
# [+name+]
|
99
|
-
# Name of the property
|
100
|
-
#
|
101
|
-
# [+opts+]
|
102
|
-
# [+type+]
|
103
|
-
# Data type of property (a constant)
|
104
|
-
#
|
105
|
-
# Default: +String+
|
106
|
-
#
|
107
|
-
# [+multiple+]
|
108
|
-
# Whether or not the property has multiple values (which
|
109
|
-
# turns it into an +Array+ of +type+)
|
110
|
-
#
|
111
|
-
def property(name, opts = {})
|
112
|
-
(@properties ||= []) << name
|
113
|
-
|
114
|
-
# property
|
115
|
-
define_method(name) do
|
116
|
-
load! unless loaded? && instance_variable_defined?("@#{name}") || name ==(:id)
|
117
|
-
instance_variable_get("@#{name}")
|
118
|
-
end
|
119
|
-
|
120
|
-
# property?
|
121
|
-
define_method("#{name}?") do
|
122
|
-
load! unless loaded? && instance_variable_defined?("@#{name}") || name ==(:id)
|
123
|
-
|
124
|
-
property = instance_variable_get("@#{name}")
|
125
|
-
property.respond_to?(:empty?) ? !property.empty? : !!property
|
126
|
-
end
|
127
|
-
|
128
|
-
# property=
|
129
|
-
define_method("#{name}=") do |value|
|
130
|
-
if value.nil?
|
131
|
-
instance_variable_set "@#{name}", value
|
132
|
-
return
|
133
|
-
end
|
134
|
-
|
135
|
-
# Check value type
|
136
|
-
type = opts[:type] || String
|
137
|
-
if opts[:multiple]
|
138
|
-
raise MetalArchives::Errors::TypeError, "invalid type #{value.class}, must be Array for #{name}" unless value.is_a? Array
|
139
|
-
value.each do |val|
|
140
|
-
raise MetalArchives::Errors::TypeError, "invalid type #{val.class}, must be #{type} for #{name}" unless val.is_a? type
|
141
|
-
end
|
142
|
-
else
|
143
|
-
raise MetalArchives::Errors::TypeError, "invalid type #{value.class}, must be #{type} for #{name}" unless value.is_a? type
|
144
|
-
end
|
145
|
-
|
146
|
-
instance_variable_set "@#{name}", value
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
##
|
151
|
-
# Defines a model enum property.
|
152
|
-
#
|
153
|
-
# [+name+]
|
154
|
-
# Name of the property
|
155
|
-
#
|
156
|
-
# [+opts+]
|
157
|
-
# [+values+]
|
158
|
-
# Required. An array of possible values
|
159
|
-
#
|
160
|
-
# [+multiple+]
|
161
|
-
# Whether or not the property has multiple values (which
|
162
|
-
# turns it into an +Array+ of +type+)
|
163
|
-
#
|
164
|
-
def enum(name, opts)
|
165
|
-
raise ArgumentError, 'opts[:values] is required' unless opts and opts[:values]
|
166
|
-
|
167
|
-
(@properties ||= []) << name
|
168
|
-
|
169
|
-
# property
|
170
|
-
define_method(name) do
|
171
|
-
load! unless loaded? && instance_variable_defined?("@#{name}")
|
172
|
-
instance_variable_get("@#{name}")
|
173
|
-
end
|
174
|
-
|
175
|
-
# property?
|
176
|
-
define_method("#{name}?") do
|
177
|
-
load! unless loaded? && instance_variable_defined?("@#{name}")
|
178
|
-
|
179
|
-
property = instance_variable_get("@#{name}")
|
180
|
-
property.respond_to?(:empty?) ? !property.empty? : !!property
|
181
|
-
end
|
182
|
-
|
183
|
-
# property=
|
184
|
-
define_method("#{name}=") do |value|
|
185
|
-
# Check enum type
|
186
|
-
if opts[:multiple]
|
187
|
-
raise MetalArchives::Errors::TypeError, "invalid enum value #{value}, must be Array for #{name}" unless value.is_a? Array
|
188
|
-
value.each do |val|
|
189
|
-
raise MetalArchives::Errors::TypeError, "invalid enum value #{val} for #{name}" unless opts[:values].include? val
|
190
|
-
end
|
191
|
-
else
|
192
|
-
raise MetalArchives::Errors::TypeError, "invalid enum value #{value} for #{name}" unless opts[:values].include? value
|
193
|
-
end
|
194
|
-
|
195
|
-
instance_variable_set name, value
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
##
|
200
|
-
# Defines a model boolean property. This method is an alias for <tt>enum name, :values => [true, false]</tt>
|
201
|
-
#
|
202
|
-
# [+name+]
|
203
|
-
# Name of the property
|
204
|
-
#
|
205
|
-
# [+opts+]
|
206
|
-
# [+multiple+]
|
207
|
-
# Whether or not the property has multiple values (which
|
208
|
-
# turns it into an +Array+ of +type+)
|
209
|
-
#
|
210
|
-
def boolean(name, opts = {})
|
211
|
-
enum name, opts.merge(:values => [true, false])
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|
215
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module MetalArchives
|
4
|
-
##
|
5
|
-
# Generic LRU memory cache
|
6
|
-
#
|
7
|
-
class LRUCache
|
8
|
-
def initialize(size = 100)
|
9
|
-
@size = size
|
10
|
-
|
11
|
-
# Underlying data store
|
12
|
-
@cache = {}
|
13
|
-
|
14
|
-
# Array of keys in order of insertion
|
15
|
-
@keys = []
|
16
|
-
end
|
17
|
-
|
18
|
-
def []=(key, value)
|
19
|
-
@cache[key] = value
|
20
|
-
|
21
|
-
@keys.delete key if @keys.include? key
|
22
|
-
|
23
|
-
@keys << key
|
24
|
-
|
25
|
-
pop if @keys.size > @size
|
26
|
-
end
|
27
|
-
|
28
|
-
def [](key)
|
29
|
-
if @keys.include? key
|
30
|
-
MetalArchives.config.logger.debug "Cache hit for #{key}"
|
31
|
-
@keys.delete key
|
32
|
-
@keys << key
|
33
|
-
else
|
34
|
-
MetalArchives.config.logger.debug "Cache miss for #{key}"
|
35
|
-
end
|
36
|
-
|
37
|
-
@cache[key]
|
38
|
-
end
|
39
|
-
|
40
|
-
def clear
|
41
|
-
@cache.clear
|
42
|
-
@keys.clear
|
43
|
-
end
|
44
|
-
|
45
|
-
def include?(key)
|
46
|
-
@cache.include? key
|
47
|
-
end
|
48
|
-
|
49
|
-
def delete(key)
|
50
|
-
@cache.delete key
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
def pop
|
56
|
-
to_remove = @keys.shift @keys.size - @size
|
57
|
-
|
58
|
-
to_remove.each { |key| @cache.delete key }
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
@@ -1,99 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'date'
|
4
|
-
|
5
|
-
module MetalArchives
|
6
|
-
##
|
7
|
-
# Date with nullable year, month and day
|
8
|
-
#
|
9
|
-
# WARNING: No validation on actual date is performed
|
10
|
-
#
|
11
|
-
class NilDate
|
12
|
-
include Comparable
|
13
|
-
|
14
|
-
attr_accessor :year, :month, :day
|
15
|
-
|
16
|
-
def initialize(year = nil, month = nil, day = nil)
|
17
|
-
@year = year
|
18
|
-
@month = month
|
19
|
-
@day = day
|
20
|
-
end
|
21
|
-
|
22
|
-
def year?
|
23
|
-
!@year.nil?
|
24
|
-
end
|
25
|
-
|
26
|
-
def month?
|
27
|
-
!@month.nil?
|
28
|
-
end
|
29
|
-
|
30
|
-
def day?
|
31
|
-
!@day.nil?
|
32
|
-
end
|
33
|
-
|
34
|
-
##
|
35
|
-
# Return a +Date+ object
|
36
|
-
#
|
37
|
-
def date
|
38
|
-
raise MetalArchives::Errors::ArgumentError, "Invalid conversion to Date: #{self.to_s}" unless year?
|
39
|
-
|
40
|
-
::Date.new @year, month || 1, day || 1
|
41
|
-
end
|
42
|
-
|
43
|
-
##
|
44
|
-
# Parse YYYY[-MM[-DD]]
|
45
|
-
#
|
46
|
-
def self.parse(value)
|
47
|
-
split = value.split('-')
|
48
|
-
|
49
|
-
year = Integer(split[0], 10) if split.any? && split[0] && !split[0].empty?
|
50
|
-
year = nil if year == 0
|
51
|
-
|
52
|
-
month = Integer(split[1], 10) if split.length > 1 && split[1] && !split[1].empty?
|
53
|
-
month = nil if month == 0
|
54
|
-
|
55
|
-
day = Integer(split[2], 10) if split.length > 2 && split[2] && !split[2].empty?
|
56
|
-
day = nil if day == 0
|
57
|
-
|
58
|
-
return MetalArchives::NilDate.new year, month, day
|
59
|
-
rescue => e
|
60
|
-
raise MetalArchives::Errors::ArgumentError, "Invalid date: #{value}: #{e}"
|
61
|
-
end
|
62
|
-
|
63
|
-
def to_s
|
64
|
-
year = (@year || 0).to_s.rjust 2, '0'
|
65
|
-
month = (@month || 0).to_s.rjust 2, '0'
|
66
|
-
day = (@day || 0).to_s.rjust 2, '0'
|
67
|
-
|
68
|
-
"#{year}-#{month}-#{day}"
|
69
|
-
end
|
70
|
-
|
71
|
-
##
|
72
|
-
# Comparison operator
|
73
|
-
#
|
74
|
-
def <=>(other)
|
75
|
-
return nil if other.nil?
|
76
|
-
|
77
|
-
# Return nil if one of the two years is nil
|
78
|
-
return nil if (@year.nil? && !other.year.nil?) || !@year.nil? && other.year.nil?
|
79
|
-
|
80
|
-
# Return nil if one of the two months is nil
|
81
|
-
return nil if (@month.nil? && !other.month.nil?) || !@month.nil? && other.month.nil?
|
82
|
-
|
83
|
-
# Return nil if one of the two months is nil
|
84
|
-
return nil if (@day.nil? && !other.day.nil?) || !@day.nil? && other.day.nil?
|
85
|
-
|
86
|
-
comp_year = @year <=> other.year
|
87
|
-
if comp_year == 0
|
88
|
-
comp_month = @month <=> other.month
|
89
|
-
if comp_month == 0
|
90
|
-
return @day <=> other.day
|
91
|
-
else
|
92
|
-
return comp_month
|
93
|
-
end
|
94
|
-
else
|
95
|
-
comp_year
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|