metal_archives 3.0.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 +4 -4
- data/.github/workflows/ci.yml +59 -12
- data/.rspec +1 -0
- data/.rubocop.yml +34 -20
- data/CHANGELOG.md +4 -0
- data/LICENSE.md +17 -4
- data/README.md +29 -14
- data/bin/console +8 -11
- data/config/inflections.rb +7 -0
- data/config/initializers/.keep +0 -0
- data/docker-compose.yml +10 -1
- data/lib/metal_archives.rb +56 -22
- 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/collection.rb +3 -5
- data/lib/metal_archives/configuration.rb +28 -21
- data/lib/metal_archives/errors.rb +9 -1
- data/lib/metal_archives/http_client.rb +42 -46
- data/lib/metal_archives/models/artist.rb +55 -26
- data/lib/metal_archives/models/band.rb +43 -36
- data/lib/metal_archives/models/{base_model.rb → base.rb} +53 -53
- data/lib/metal_archives/models/label.rb +7 -8
- data/lib/metal_archives/models/release.rb +11 -17
- data/lib/metal_archives/parsers/artist.rb +40 -35
- data/lib/metal_archives/parsers/band.rb +73 -29
- 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 +21 -13
- data/lib/metal_archives/parsers/parser.rb +15 -77
- data/lib/metal_archives/parsers/release.rb +22 -18
- data/lib/metal_archives/parsers/year.rb +29 -0
- data/lib/metal_archives/version.rb +3 -3
- data/metal_archives.env.example +7 -4
- data/metal_archives.gemspec +7 -4
- data/nginx/default.conf +2 -2
- metadata +76 -32
- data/.github/workflows/release.yml +0 -69
- data/.rubocop_todo.yml +0 -92
- data/lib/metal_archives/lru_cache.rb +0 -61
- data/lib/metal_archives/middleware/cache_check.rb +0 -18
- 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/nil_date.rb +0 -91
- data/lib/metal_archives/range.rb +0 -69
@@ -8,7 +8,7 @@ module MetalArchives
|
|
8
8
|
##
|
9
9
|
# Represents an band (person or group)
|
10
10
|
#
|
11
|
-
class Band <
|
11
|
+
class Band < Base
|
12
12
|
##
|
13
13
|
# :attr_reader: id
|
14
14
|
#
|
@@ -63,24 +63,24 @@ module MetalArchives
|
|
63
63
|
##
|
64
64
|
# :attr_reader: date_formed
|
65
65
|
#
|
66
|
-
# Returns rdoc-ref:
|
66
|
+
# Returns rdoc-ref:Date
|
67
67
|
#
|
68
68
|
# [Raises]
|
69
69
|
# - rdoc-ref:MetalArchives::Errors::InvalidIDError when no or invalid id
|
70
70
|
# - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
|
71
71
|
#
|
72
|
-
property :date_formed, type:
|
72
|
+
property :date_formed, type: Date
|
73
73
|
|
74
74
|
##
|
75
|
-
# :attr_reader:
|
75
|
+
# :attr_reader: years_active
|
76
76
|
#
|
77
|
-
# Returns +Array+ of rdoc-ref:Range containing
|
77
|
+
# Returns +Array+ of rdoc-ref:Range containing +Integer+
|
78
78
|
#
|
79
79
|
# [Raises]
|
80
80
|
# - rdoc-ref:MetalArchives::Errors::InvalidIDError when no or invalid id
|
81
81
|
# - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
|
82
82
|
#
|
83
|
-
property :
|
83
|
+
property :years_active, type: Range, multiple: true
|
84
84
|
|
85
85
|
##
|
86
86
|
# :attr_reader: genres
|
@@ -113,7 +113,7 @@ module MetalArchives
|
|
113
113
|
# - rdoc-ref:MetalArchives::Errors::InvalidIDError when no or invalid id
|
114
114
|
# - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
|
115
115
|
#
|
116
|
-
property :label, type:
|
116
|
+
property :label, type: Label
|
117
117
|
|
118
118
|
##
|
119
119
|
# :attr_reader: independent
|
@@ -146,7 +146,7 @@ module MetalArchives
|
|
146
146
|
# - rdoc-ref:MetalArchives::Errors::InvalidIDError when no or invalid id
|
147
147
|
# - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
|
148
148
|
#
|
149
|
-
enum :status, values:
|
149
|
+
enum :status, values: [:active, :split_up, :on_hold, :unknown, :changed_name, :disputed]
|
150
150
|
|
151
151
|
##
|
152
152
|
# :attr_reader: releases
|
@@ -157,9 +157,24 @@ module MetalArchives
|
|
157
157
|
# - rdoc-ref:MetalArchives::Errors::InvalidIDError when no or invalid id
|
158
158
|
# - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
|
159
159
|
#
|
160
|
-
property :releases, type:
|
160
|
+
property :releases, type: Release, multiple: true
|
161
161
|
|
162
|
-
|
162
|
+
##
|
163
|
+
# :attr_reader: members
|
164
|
+
#
|
165
|
+
# Returns +Array+ of +Hash+ containing the following keys
|
166
|
+
#
|
167
|
+
# [Raises]
|
168
|
+
# - rdoc-ref:MetalArchives::Errors::InvalidIDError when no or invalid id
|
169
|
+
# - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
|
170
|
+
#
|
171
|
+
# [+members+]
|
172
|
+
# - +:artist+: rdoc-ref:Artist
|
173
|
+
# - +:current+: Boolean
|
174
|
+
# - +:years_active+: +Array+ of rdoc-ref:Range containing +Integer+
|
175
|
+
# - +:role+: +String+
|
176
|
+
#
|
177
|
+
property :members, type: Artist, multiple: true
|
163
178
|
|
164
179
|
##
|
165
180
|
# :attr_reader: similar
|
@@ -225,34 +240,29 @@ module MetalArchives
|
|
225
240
|
#
|
226
241
|
def assemble # :nodoc:
|
227
242
|
## Base attributes
|
228
|
-
|
229
|
-
response = HTTPClient.get url
|
243
|
+
response = MetalArchives.http.get "/band/view/id/#{id}"
|
230
244
|
|
231
|
-
properties = Parsers::Band.parse_html response.
|
245
|
+
properties = Parsers::Band.parse_html response.to_s
|
232
246
|
|
233
247
|
## Comment
|
234
|
-
|
235
|
-
response = HTTPClient.get url
|
248
|
+
response = MetalArchives.http.get "/band/read-more/id/#{id}"
|
236
249
|
|
237
|
-
properties[:comment] = response.
|
250
|
+
properties[:comment] = response.to_s
|
238
251
|
|
239
252
|
## Similar artists
|
240
|
-
|
241
|
-
response = HTTPClient.get url
|
253
|
+
response = MetalArchives.http.get "/band/ajax-recommendations/id/#{id}"
|
242
254
|
|
243
|
-
properties[:similar] = Parsers::Band.parse_similar_bands_html response.
|
255
|
+
properties[:similar] = Parsers::Band.parse_similar_bands_html response.to_s
|
244
256
|
|
245
257
|
## Related links
|
246
|
-
|
247
|
-
response = HTTPClient.get url
|
258
|
+
response = MetalArchives.http.get "/link/ajax-list/type/band/id/#{id}"
|
248
259
|
|
249
|
-
properties[:links] = Parsers::Band.parse_related_links_html response.
|
260
|
+
properties[:links] = Parsers::Band.parse_related_links_html response.to_s
|
250
261
|
|
251
262
|
## Releases
|
252
|
-
|
253
|
-
response = HTTPClient.get url
|
263
|
+
response = MetalArchives.http.get "/band/discography/id/#{id}/tab/all"
|
254
264
|
|
255
|
-
properties[:releases] = Parsers::Band.parse_releases_html response.
|
265
|
+
properties[:releases] = Parsers::Band.parse_releases_html response.to_s
|
256
266
|
|
257
267
|
properties
|
258
268
|
end
|
@@ -267,7 +277,7 @@ module MetalArchives
|
|
267
277
|
# +Integer+
|
268
278
|
#
|
269
279
|
def find(id)
|
270
|
-
return cache[id] if cache.include? id
|
280
|
+
return MetalArchives.cache[id] if MetalArchives.cache.include? id
|
271
281
|
|
272
282
|
Band.new id: id
|
273
283
|
end
|
@@ -309,7 +319,7 @@ module MetalArchives
|
|
309
319
|
# - +:exact+: +Boolean+
|
310
320
|
# - +:genre+: +String+
|
311
321
|
# - +:country+: +ISO3166::Country+
|
312
|
-
# - +:year_formation+: rdoc-ref:Range containing rdoc-ref:
|
322
|
+
# - +:year_formation+: rdoc-ref:Range containing rdoc-ref:Date
|
313
323
|
# - +:comment+: +String+
|
314
324
|
# - +:status+: see rdoc-ref:Band.status
|
315
325
|
# - +:lyrical_themes+: +String+
|
@@ -318,11 +328,10 @@ module MetalArchives
|
|
318
328
|
# - +:independent+: boolean
|
319
329
|
#
|
320
330
|
def find_by(query)
|
321
|
-
url = "#{MetalArchives.config.default_endpoint}search/ajax-advanced/searching/bands"
|
322
331
|
params = Parsers::Band.map_params query
|
323
332
|
|
324
|
-
response =
|
325
|
-
json = JSON.parse response.
|
333
|
+
response = MetalArchives.http.get "/search/ajax-advanced/searching/bands", params
|
334
|
+
json = JSON.parse response.to_s
|
326
335
|
|
327
336
|
return nil if json["aaData"].empty?
|
328
337
|
|
@@ -349,7 +358,7 @@ module MetalArchives
|
|
349
358
|
# - +:exact+: +Boolean+
|
350
359
|
# - +:genre+: +String+
|
351
360
|
# - +:country+: +ISO3166::Country+
|
352
|
-
# - +:year_formation+: rdoc-ref:Range containing rdoc-ref:
|
361
|
+
# - +:year_formation+: rdoc-ref:Range containing rdoc-ref:Date
|
353
362
|
# - +:comment+: +String+
|
354
363
|
# - +:status+: see rdoc-ref:Band.status
|
355
364
|
# - +:lyrical_themes+: +String+
|
@@ -381,7 +390,7 @@ module MetalArchives
|
|
381
390
|
# - +:exact+: +Boolean+
|
382
391
|
# - +:genre+: +String+
|
383
392
|
# - +:country+: +ISO3166::Country+
|
384
|
-
# - +:year_formation+: rdoc-ref:Range containing rdoc-ref:
|
393
|
+
# - +:year_formation+: rdoc-ref:Range containing rdoc-ref:Date
|
385
394
|
# - +:comment+: +String+
|
386
395
|
# - +:status+: see rdoc-ref:Band.status
|
387
396
|
# - +:lyrical_themes+: +String+
|
@@ -390,8 +399,6 @@ module MetalArchives
|
|
390
399
|
# - +:independent+: boolean
|
391
400
|
#
|
392
401
|
def search_by(query)
|
393
|
-
url = "#{MetalArchives.config.default_endpoint}search/ajax-advanced/searching/bands"
|
394
|
-
|
395
402
|
params = Parsers::Band.map_params query
|
396
403
|
|
397
404
|
l = lambda do
|
@@ -400,8 +407,8 @@ module MetalArchives
|
|
400
407
|
if @max_items && @start >= @max_items
|
401
408
|
[]
|
402
409
|
else
|
403
|
-
response =
|
404
|
-
json = JSON.parse response.
|
410
|
+
response = MetalArchives.http.get "/search/ajax-advanced/searching/bands", params.merge(iDisplayStart: @start)
|
411
|
+
json = JSON.parse response.to_s
|
405
412
|
|
406
413
|
@max_items = json["iTotalRecords"]
|
407
414
|
|
@@ -4,48 +4,49 @@ module MetalArchives
|
|
4
4
|
##
|
5
5
|
# Abstract model class
|
6
6
|
#
|
7
|
-
class
|
7
|
+
class Base
|
8
8
|
##
|
9
9
|
# Generic shallow copy constructor
|
10
10
|
#
|
11
|
-
def initialize(
|
12
|
-
raise Errors::NotImplementedError, "no :id property in model" unless respond_to? :id
|
11
|
+
def initialize(attributes = {})
|
12
|
+
raise Errors::NotImplementedError, "no :id property in model" unless respond_to? :id
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
set(**attributes)
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Set properties
|
19
|
+
#
|
20
|
+
def set(**attributes)
|
21
|
+
attributes.each { |key, value| instance_variable_set(:"@#{key}", value) }
|
17
22
|
end
|
18
23
|
|
19
24
|
##
|
20
25
|
# Returns true if two objects have the same type and id
|
21
26
|
#
|
22
|
-
def ==(
|
23
|
-
|
27
|
+
def ==(other)
|
28
|
+
other.is_a?(self.class) &&
|
29
|
+
id == other.id
|
24
30
|
end
|
25
31
|
|
26
32
|
##
|
27
33
|
# Fetch, parse and load the data
|
28
34
|
#
|
29
35
|
# [Raises]
|
30
|
-
# - rdoc-ref:
|
31
|
-
# - rdoc-ref:
|
36
|
+
# - rdoc-ref:Errors::InvalidIDError when no id
|
37
|
+
# - rdoc-ref:Errors::APIError when receiving a status code >= 400 (except 404)
|
32
38
|
#
|
33
39
|
def load!
|
34
40
|
raise Errors::InvalidIDError, "no id present" unless id
|
35
41
|
|
36
42
|
# Use constructor to set attributes
|
37
|
-
|
38
|
-
|
39
|
-
# Set empty properties to nil
|
40
|
-
self.class.properties.each do |prop|
|
41
|
-
instance_variable_set("@#{prop}", nil) unless instance_variable_defined? "@#{prop}"
|
42
|
-
end
|
43
|
+
set(**assemble)
|
43
44
|
|
44
45
|
@loaded = true
|
45
|
-
|
46
|
+
MetalArchives.cache[id] = self
|
46
47
|
rescue StandardError => e
|
47
48
|
# Don't cache invalid requests
|
48
|
-
|
49
|
+
MetalArchives.cache.delete id
|
49
50
|
raise e
|
50
51
|
end
|
51
52
|
|
@@ -53,14 +54,21 @@ module MetalArchives
|
|
53
54
|
# Whether or not the object is currently loaded
|
54
55
|
#
|
55
56
|
def loaded?
|
56
|
-
|
57
|
+
@loaded ||= false
|
57
58
|
end
|
58
59
|
|
59
60
|
##
|
60
61
|
# Whether or not the object is currently cached
|
61
62
|
#
|
62
63
|
def cached?
|
63
|
-
loaded? &&
|
64
|
+
loaded? && MetalArchives.cache.include?(id)
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# String representation
|
69
|
+
#
|
70
|
+
def inspect
|
71
|
+
"#<#{self.class.name} @id=#{@id} @name=\"#{@name}\">"
|
64
72
|
end
|
65
73
|
|
66
74
|
protected
|
@@ -71,8 +79,8 @@ module MetalArchives
|
|
71
79
|
# Override this method
|
72
80
|
#
|
73
81
|
# [Raises]
|
74
|
-
# - rdoc-ref:
|
75
|
-
# - rdoc-ref:
|
82
|
+
# - rdoc-ref:Errors::InvalidIDError when no or invalid id
|
83
|
+
# - rdoc-ref:Errors::APIError when receiving a status code >= 400 (except 404)
|
76
84
|
#
|
77
85
|
def assemble
|
78
86
|
raise Errors::NotImplementedError, "method :assemble not implemented"
|
@@ -80,15 +88,10 @@ module MetalArchives
|
|
80
88
|
|
81
89
|
class << self
|
82
90
|
##
|
83
|
-
#
|
91
|
+
# Declared properties
|
84
92
|
#
|
85
|
-
|
86
|
-
|
87
|
-
##
|
88
|
-
# Get class-level object cache
|
89
|
-
#
|
90
|
-
def cache
|
91
|
-
@cache ||= MetalArchives::LRUCache.new MetalArchives.config.cache_size
|
93
|
+
def properties
|
94
|
+
@properties ||= {}
|
92
95
|
end
|
93
96
|
|
94
97
|
protected
|
@@ -110,42 +113,38 @@ module MetalArchives
|
|
110
113
|
# turns it into an +Array+ of +type+)
|
111
114
|
#
|
112
115
|
def property(name, opts = {})
|
113
|
-
|
116
|
+
properties[name] = opts
|
114
117
|
|
115
118
|
# property
|
116
119
|
define_method(name) do
|
117
|
-
|
118
|
-
|
120
|
+
# Load only when not loaded or id property
|
121
|
+
load! unless loaded? || name == :id
|
122
|
+
|
123
|
+
instance_variable_get(:"@#{name}")
|
119
124
|
end
|
120
125
|
|
121
126
|
# property?
|
122
127
|
define_method("#{name}?") do
|
123
|
-
|
124
|
-
|
125
|
-
property = instance_variable_get("@#{name}")
|
126
|
-
property.respond_to?(:empty?) ? !property.empty? : !property.nil?
|
128
|
+
send(name).present?
|
127
129
|
end
|
128
130
|
|
129
131
|
# property=
|
130
132
|
define_method("#{name}=") do |value|
|
131
|
-
if value.nil?
|
132
|
-
instance_variable_set "@#{name}", value
|
133
|
-
return
|
134
|
-
end
|
133
|
+
return instance_variable_set(:"@#{name}", value) if value.nil?
|
135
134
|
|
136
135
|
# Check value type
|
137
136
|
type = opts[:type] || String
|
138
137
|
if opts[:multiple]
|
139
|
-
raise
|
138
|
+
raise Errors::TypeError, "invalid type #{value.class}, must be Array for #{name}" unless value.is_a? Array
|
140
139
|
|
141
140
|
value.each do |val|
|
142
|
-
raise
|
141
|
+
raise Errors::TypeError, "invalid type #{val.class}, must be #{type} for #{name}" unless val.is_a? type
|
143
142
|
end
|
144
143
|
else
|
145
|
-
raise
|
144
|
+
raise Errors::TypeError, "invalid type #{value.class}, must be #{type} for #{name}" unless value.is_a? type
|
146
145
|
end
|
147
146
|
|
148
|
-
instance_variable_set
|
147
|
+
instance_variable_set(:"@#{name}", value)
|
149
148
|
end
|
150
149
|
end
|
151
150
|
|
@@ -166,19 +165,20 @@ module MetalArchives
|
|
166
165
|
def enum(name, opts)
|
167
166
|
raise ArgumentError, "opts[:values] is required" unless opts && opts[:values]
|
168
167
|
|
169
|
-
|
168
|
+
properties[name] = opts
|
170
169
|
|
171
170
|
# property
|
172
171
|
define_method(name) do
|
173
|
-
load! unless loaded?
|
174
|
-
|
172
|
+
load! unless loaded?
|
173
|
+
|
174
|
+
instance_variable_get(:"@#{name}")
|
175
175
|
end
|
176
176
|
|
177
177
|
# property?
|
178
178
|
define_method("#{name}?") do
|
179
179
|
load! unless loaded? && instance_variable_defined?("@#{name}")
|
180
180
|
|
181
|
-
property = instance_variable_get("@#{name}")
|
181
|
+
property = instance_variable_get(:"@#{name}")
|
182
182
|
property.respond_to?(:empty?) ? !property.empty? : !property.nil?
|
183
183
|
end
|
184
184
|
|
@@ -186,16 +186,16 @@ module MetalArchives
|
|
186
186
|
define_method("#{name}=") do |value|
|
187
187
|
# Check enum type
|
188
188
|
if opts[:multiple]
|
189
|
-
raise
|
189
|
+
raise Errors::TypeError, "invalid enum value #{value}, must be Array for #{name}" unless value.is_a? Array
|
190
190
|
|
191
191
|
value.each do |val|
|
192
|
-
raise
|
192
|
+
raise Errors::TypeError, "invalid enum value #{val} for #{name}" unless opts[:values].include? val
|
193
193
|
end
|
194
194
|
else
|
195
|
-
raise
|
195
|
+
raise Errors::TypeError, "invalid enum value #{value} for #{name}" unless opts[:values].include? value
|
196
196
|
end
|
197
197
|
|
198
|
-
instance_variable_set
|
198
|
+
instance_variable_set(:"@#{name}", value)
|
199
199
|
end
|
200
200
|
end
|
201
201
|
|
@@ -7,7 +7,7 @@ module MetalArchives
|
|
7
7
|
##
|
8
8
|
# Represents a record label
|
9
9
|
#
|
10
|
-
class Label <
|
10
|
+
class Label < Base
|
11
11
|
##
|
12
12
|
# :attr_reader: id
|
13
13
|
#
|
@@ -73,20 +73,20 @@ module MetalArchives
|
|
73
73
|
##
|
74
74
|
# :attr_reader: date_founded
|
75
75
|
#
|
76
|
-
# Returns +
|
76
|
+
# Returns +Date+
|
77
77
|
#
|
78
78
|
# [Raises]
|
79
79
|
# - rdoc-ref:MetalArchives::Errors::InvalidIDError when no or invalid id
|
80
80
|
# - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
|
81
81
|
#
|
82
|
-
property :date_founded, type:
|
82
|
+
property :date_founded, type: Date
|
83
83
|
|
84
84
|
##
|
85
85
|
# :attr_reader: sub_labels
|
86
86
|
#
|
87
87
|
# Returns +Array+ of rdoc-ref:Label
|
88
88
|
#
|
89
|
-
property :sub_labels, type:
|
89
|
+
property :sub_labels, type: Label, multiple: true
|
90
90
|
|
91
91
|
##
|
92
92
|
# :attr_reader: online_shopping
|
@@ -107,7 +107,7 @@ module MetalArchives
|
|
107
107
|
#
|
108
108
|
# Returns +:active+, +:closed+ or +:unknown+
|
109
109
|
#
|
110
|
-
enum :status, values:
|
110
|
+
enum :status, values: [:active, :closed, :unknown]
|
111
111
|
|
112
112
|
class << self
|
113
113
|
##
|
@@ -116,8 +116,7 @@ module MetalArchives
|
|
116
116
|
# Returns +Array+ of rdoc-ref:Label
|
117
117
|
#
|
118
118
|
def search(_name)
|
119
|
-
|
120
|
-
results
|
119
|
+
[]
|
121
120
|
end
|
122
121
|
|
123
122
|
##
|
@@ -129,7 +128,7 @@ module MetalArchives
|
|
129
128
|
client.find_resource(
|
130
129
|
:band,
|
131
130
|
name: name,
|
132
|
-
id: id
|
131
|
+
id: id,
|
133
132
|
)
|
134
133
|
end
|
135
134
|
end
|