metal_archives 0.8.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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +59 -7
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +14 -0
  5. data/.travis.yml +11 -0
  6. data/Gemfile +2 -0
  7. data/{LICENSE → LICENSE.md} +0 -0
  8. data/README.md +77 -9
  9. data/Rakefile +5 -3
  10. data/lib/metal_archives.rb +8 -0
  11. data/lib/metal_archives/configuration.rb +28 -7
  12. data/lib/metal_archives/error.rb +37 -30
  13. data/lib/metal_archives/http_client.rb +21 -42
  14. data/lib/metal_archives/middleware/headers.rb +38 -0
  15. data/lib/metal_archives/middleware/rewrite_endpoint.rb +38 -0
  16. data/lib/metal_archives/models/artist.rb +51 -65
  17. data/lib/metal_archives/models/band.rb +41 -39
  18. data/lib/metal_archives/models/base_model.rb +88 -59
  19. data/lib/metal_archives/models/label.rb +7 -6
  20. data/lib/metal_archives/parsers/artist.rb +110 -99
  21. data/lib/metal_archives/parsers/band.rb +168 -156
  22. data/lib/metal_archives/parsers/label.rb +54 -52
  23. data/lib/metal_archives/parsers/parser.rb +73 -71
  24. data/lib/metal_archives/utils/collection.rb +7 -1
  25. data/lib/metal_archives/utils/lru_cache.rb +11 -4
  26. data/lib/metal_archives/utils/nil_date.rb +54 -0
  27. data/lib/metal_archives/utils/range.rb +16 -8
  28. data/lib/metal_archives/version.rb +3 -1
  29. data/metal_archives.gemspec +21 -11
  30. data/spec/configuration_spec.rb +101 -0
  31. data/spec/factories/artist_factory.rb +37 -0
  32. data/spec/factories/band_factory.rb +60 -0
  33. data/spec/factories/nil_date_factory.rb +9 -0
  34. data/spec/factories/range_factory.rb +8 -0
  35. data/spec/models/artist_spec.rb +142 -0
  36. data/spec/models/band_spec.rb +179 -0
  37. data/spec/models/base_model_spec.rb +217 -0
  38. data/spec/parser_spec.rb +19 -0
  39. data/spec/spec_helper.rb +111 -0
  40. data/spec/support/factory_girl.rb +5 -0
  41. data/spec/support/metal_archives.rb +26 -0
  42. data/spec/utils/collection_spec.rb +72 -0
  43. data/spec/utils/lru_cache_spec.rb +53 -0
  44. data/spec/utils/nil_date_spec.rb +98 -0
  45. data/spec/utils/range_spec.rb +62 -0
  46. metadata +142 -57
  47. data/test/base_model_test.rb +0 -111
  48. data/test/configuration_test.rb +0 -57
  49. data/test/parser_test.rb +0 -37
  50. data/test/property/artist_property_test.rb +0 -43
  51. data/test/property/band_property_test.rb +0 -94
  52. data/test/query/artist_query_test.rb +0 -109
  53. data/test/query/band_query_test.rb +0 -152
  54. data/test/test_helper.rb +0 -25
  55. data/test/utils/collection_test.rb +0 -51
  56. data/test/utils/lru_cache_test.rb +0 -22
  57. data/test/utils/range_test.rb +0 -42
@@ -0,0 +1,38 @@
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
@@ -0,0 +1,38 @@
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
+ 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,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'date'
2
4
  require 'countries'
3
5
 
4
6
  module MetalArchives
5
-
6
7
  ##
7
8
  # Represents a single performer (but not a solo artist)
8
9
  #
@@ -100,7 +101,7 @@ module MetalArchives
100
101
  # - rdoc-ref:MetalArchives::Errors::InvalidIDError when no or invalid id
101
102
  # - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
102
103
  #
103
- enum :gender, :values => [:male, :female]
104
+ enum :gender, :values => %i[male female]
104
105
 
105
106
  ##
106
107
  # :attr_reader: biography
@@ -124,6 +125,17 @@ module MetalArchives
124
125
  #
125
126
  property :trivia
126
127
 
128
+ ##
129
+ # :attr_reader: photo
130
+ #
131
+ # Returns +URI+ (rewritten if config option was enabled)
132
+ #
133
+ # [Raises]
134
+ # - rdoc-ref:MetalArchives::Errors::InvalidIDError when no or invalid id
135
+ # - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
136
+ #
137
+ property :photo
138
+
127
139
  ##
128
140
  # :attr_reader: links
129
141
  #
@@ -146,40 +158,41 @@ module MetalArchives
146
158
  # TODO: misc bands/albums
147
159
 
148
160
  protected
149
- ##
150
- # Fetch the data and assemble the model
151
- #
152
- # [Raises]
153
- # - rdoc-ref:MetalArchives::Errors::InvalidIDError when no or invalid id
154
- # - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
155
- #
156
- def assemble # :nodoc:
157
- ## Base attributes
158
- url = "#{MetalArchives.config.endpoint}artist/view/id/#{id}"
159
- response = HTTPClient.get url
160
161
 
161
- properties = Parsers::Artist.parse_html response.body
162
+ ##
163
+ # Fetch the data and assemble the model
164
+ #
165
+ # [Raises]
166
+ # - rdoc-ref:MetalArchives::Errors::InvalidIDError when no or invalid id
167
+ # - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
168
+ #
169
+ def assemble # :nodoc:
170
+ ## Base attributes
171
+ url = "#{MetalArchives.config.default_endpoint}artist/view/id/#{id}"
172
+ response = HTTPClient.get url
173
+
174
+ properties = Parsers::Artist.parse_html response.body
162
175
 
163
- ## Biography
164
- url = "#{MetalArchives.config.endpoint}artist/read-more/id/#{id}/field/biography"
165
- response = HTTPClient.get url
176
+ ## Biography
177
+ url = "#{MetalArchives.config.default_endpoint}artist/read-more/id/#{id}/field/biography"
178
+ response = HTTPClient.get url
166
179
 
167
- properties[:biography] = response.body
180
+ properties[:biography] = response.body
168
181
 
169
- ## Trivia
170
- url = "#{MetalArchives.config.endpoint}artist/read-more/id/#{id}/field/trivia"
171
- response = HTTPClient.get url
182
+ ## Trivia
183
+ url = "#{MetalArchives.config.default_endpoint}artist/read-more/id/#{id}/field/trivia"
184
+ response = HTTPClient.get url
172
185
 
173
- properties[:trivia] = response.body
186
+ properties[:trivia] = response.body
174
187
 
175
- ## Related links
176
- url = "#{MetalArchives.config.endpoint}link/ajax-list/type/person/id/#{id}"
177
- response = HTTPClient.get url
188
+ ## Related links
189
+ url = "#{MetalArchives.config.default_endpoint}link/ajax-list/type/person/id/#{id}"
190
+ response = HTTPClient.get url
178
191
 
179
- properties[:links] = Parsers::Artist.parse_links_html response.body
192
+ properties[:links] = Parsers::Artist.parse_links_html response.body
180
193
 
181
- properties
182
- end
194
+ properties
195
+ end
183
196
 
184
197
  class << self
185
198
  ##
@@ -191,9 +204,9 @@ module MetalArchives
191
204
  # +Integer+
192
205
  #
193
206
  def find(id)
194
- cache[id] = Artist.new :id => id unless cache.include? id
207
+ return cache[id] if cache.include? id
195
208
 
196
- cache[id]
209
+ Artist.new :id => id
197
210
  end
198
211
 
199
212
  ##
@@ -211,7 +224,7 @@ module MetalArchives
211
224
  #
212
225
  def find!(id)
213
226
  obj = find id
214
- obj.load! if obj
227
+ obj&.load!
215
228
 
216
229
  obj
217
230
  end
@@ -233,7 +246,7 @@ module MetalArchives
233
246
  def find_by(query)
234
247
  raise MetalArchives::Errors::ArgumentError unless query.include? :name
235
248
 
236
- url = "#{MetalArchives.config.endpoint}search/ajax-artist-search/"
249
+ url = "#{MetalArchives.config.default_endpoint}search/ajax-artist-search/"
237
250
  params = Parsers::Artist.map_params query
238
251
 
239
252
  response = HTTPClient.get url, params
@@ -242,7 +255,7 @@ module MetalArchives
242
255
  return nil if json['aaData'].empty?
243
256
 
244
257
  data = json['aaData'].first
245
- id = Nokogiri::HTML(data.first).xpath('//a/@href').first.value.gsub('\\', '').split('/').last.gsub(/\D/, '').to_i
258
+ id = Nokogiri::HTML(data.first).xpath('//a/@href').first.value.delete('\\').split('/').last.gsub(/\D/, '').to_i
246
259
 
247
260
  find id
248
261
  end
@@ -264,7 +277,7 @@ module MetalArchives
264
277
  #
265
278
  def find_by!(query)
266
279
  obj = find_by query
267
- obj.load! if obj
280
+ obj&.load!
268
281
 
269
282
  obj
270
283
  end
@@ -285,7 +298,7 @@ module MetalArchives
285
298
  def search(name)
286
299
  raise MetalArchives::Errors::ArgumentError unless name.is_a? String
287
300
 
288
- url = "#{MetalArchives.config.endpoint}search/ajax-artist-search/"
301
+ url = "#{MetalArchives.config.default_endpoint}search/ajax-artist-search/"
289
302
  query = { :name => name }
290
303
 
291
304
  params = Parsers::Artist.map_params query
@@ -293,7 +306,7 @@ module MetalArchives
293
306
  l = lambda do
294
307
  @start ||= 0
295
308
 
296
- if @max_items and @start >= @max_items
309
+ if @max_items && @start >= @max_items
297
310
  []
298
311
  else
299
312
  response = HTTPClient.get url, params.merge(:iDisplayStart => @start)
@@ -305,7 +318,7 @@ module MetalArchives
305
318
 
306
319
  json['aaData'].each do |data|
307
320
  # Create Artist object for every ID in the results list
308
- id = Nokogiri::HTML(data.first).xpath('//a/@href').first.value.gsub('\\', '').split('/').last.gsub(/\D/, '').to_i
321
+ id = Nokogiri::HTML(data.first).xpath('//a/@href').first.value.delete('\\').split('/').last.gsub(/\D/, '').to_i
309
322
  objects << Artist.find(id)
310
323
  end
311
324
 
@@ -328,34 +341,7 @@ module MetalArchives
328
341
  # - rdoc-ref:MetalArchives::Errors::ParserError when parsing failed. Please report this error.
329
342
  #
330
343
  def all
331
- url = "#{MetalArchives.config.endpoint}search/ajax-artist-search/"
332
-
333
- l = lambda do
334
- @start ||= 0
335
-
336
- if @max_items and @start >= @max_items
337
- []
338
- else
339
- response = HTTPClient.get url, :iDisplayStart => @start
340
- json = JSON.parse response.body
341
-
342
- @max_items = json['iTotalRecords']
343
-
344
- objects = []
345
-
346
- json['aaData'].each do |data|
347
- # Create Artist object for every ID in the results list
348
- id = Nokogiri::HTML(data.first).xpath('//a/@href').first.value.gsub('\\', '').split('/').last.gsub(/\D/, '').to_i
349
- objects << Artist.find(id)
350
- end
351
-
352
- @start += 200
353
-
354
- objects
355
- end
356
- end
357
-
358
- MetalArchives::Collection.new l
344
+ search ''
359
345
  end
360
346
  end
361
347
  end
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'date'
2
4
  require 'countries'
3
5
 
4
6
  module MetalArchives
5
-
6
7
  ##
7
8
  # Represents an band (person or group)
8
9
  #
@@ -144,7 +145,7 @@ module MetalArchives
144
145
  # - rdoc-ref:MetalArchives::Errors::InvalidIDError when no or invalid id
145
146
  # - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
146
147
  #
147
- enum :status, :values => [:active, :split_up, :on_hold, :unknown, :changed_name, :disputed]
148
+ enum :status, :values => %i[active split_up on_hold unknown changed_name disputed]
148
149
 
149
150
  # TODO: releases
150
151
  # TODO: members
@@ -167,7 +168,7 @@ module MetalArchives
167
168
  ##
168
169
  # :attr_reader: logo
169
170
  #
170
- # Returns +String+
171
+ # Returns +URI+ (rewritten if config option was enabled)
171
172
  #
172
173
  # [Raises]
173
174
  # - rdoc-ref:MetalArchives::Errors::InvalidIDError when no or invalid id
@@ -178,7 +179,7 @@ module MetalArchives
178
179
  ##
179
180
  # :attr_reader: photo
180
181
  #
181
- # Returns +String+
182
+ # Returns +URI+ (rewritten if config option was enabled)
182
183
  #
183
184
  # [Raises]
184
185
  # - rdoc-ref:MetalArchives::Errors::InvalidIDError when no or invalid id
@@ -203,40 +204,41 @@ module MetalArchives
203
204
  property :links, :multiple => true
204
205
 
205
206
  protected
206
- ##
207
- # Fetch the data and assemble the model
208
- #
209
- # [Raises]
210
- # - rdoc-ref:MetalArchives::Errors::InvalidIDError when receiving a status code == 404
211
- # - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
212
- #
213
- def assemble # :nodoc:
214
- ## Base attributes
215
- url = "#{MetalArchives.config.endpoint}band/view/id/#{id}"
216
- response = HTTPClient.get url
217
207
 
218
- properties = Parsers::Band.parse_html response.body
208
+ ##
209
+ # Fetch the data and assemble the model
210
+ #
211
+ # [Raises]
212
+ # - rdoc-ref:MetalArchives::Errors::InvalidIDError when receiving a status code == 404
213
+ # - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
214
+ #
215
+ def assemble # :nodoc:
216
+ ## Base attributes
217
+ url = "#{MetalArchives.config.default_endpoint}band/view/id/#{id}"
218
+ response = HTTPClient.get url
219
219
 
220
- ## Comment
221
- url = "#{MetalArchives.config.endpoint}band/read-more/id/#{id}"
222
- response = HTTPClient.get url
220
+ properties = Parsers::Band.parse_html response.body
223
221
 
224
- properties[:comment] = response.body
222
+ ## Comment
223
+ url = "#{MetalArchives.config.default_endpoint}band/read-more/id/#{id}"
224
+ response = HTTPClient.get url
225
225
 
226
- ## Similar artists
227
- url = "#{MetalArchives.config.endpoint}band/ajax-recommendations/id/#{id}"
228
- response = HTTPClient.get url
226
+ properties[:comment] = response.body
229
227
 
230
- properties[:similar] = Parsers::Band.parse_similar_bands_html response.body
228
+ ## Similar artists
229
+ url = "#{MetalArchives.config.default_endpoint}band/ajax-recommendations/id/#{id}"
230
+ response = HTTPClient.get url
231
231
 
232
- ## Related links
233
- url = "#{MetalArchives.config.endpoint}link/ajax-list/type/band/id/#{id}"
234
- response = HTTPClient.get url
232
+ properties[:similar] = Parsers::Band.parse_similar_bands_html response.body
235
233
 
236
- properties[:links] = Parsers::Band.parse_related_links_html response.body
234
+ ## Related links
235
+ url = "#{MetalArchives.config.default_endpoint}link/ajax-list/type/band/id/#{id}"
236
+ response = HTTPClient.get url
237
237
 
238
- properties
239
- end
238
+ properties[:links] = Parsers::Band.parse_related_links_html response.body
239
+
240
+ properties
241
+ end
240
242
 
241
243
  class << self
242
244
  ##
@@ -248,9 +250,9 @@ module MetalArchives
248
250
  # +Integer+
249
251
  #
250
252
  def find(id)
251
- cache[id] = Band.new(:id => id) unless cache.include? id
253
+ return cache[id] if cache.include? id
252
254
 
253
- cache[id]
255
+ Band.new :id => id
254
256
  end
255
257
 
256
258
  ##
@@ -299,7 +301,7 @@ module MetalArchives
299
301
  # - +:independent+: boolean
300
302
  #
301
303
  def find_by(query)
302
- url = "#{MetalArchives.config.endpoint}search/ajax-advanced/searching/bands"
304
+ url = "#{MetalArchives.config.default_endpoint}search/ajax-advanced/searching/bands"
303
305
  params = Parsers::Band.map_params query
304
306
 
305
307
  response = HTTPClient.get url, params
@@ -308,7 +310,7 @@ module MetalArchives
308
310
  return nil if json['aaData'].empty?
309
311
 
310
312
  data = json['aaData'].first
311
- id = Nokogiri::HTML(data.first).xpath('//a/@href').first.value.gsub('\\', '').split('/').last.gsub(/\D/, '').to_i
313
+ id = Nokogiri::HTML(data.first).xpath('//a/@href').first.value.delete('\\').split('/').last.gsub(/\D/, '').to_i
312
314
 
313
315
  find id
314
316
  end
@@ -340,7 +342,7 @@ module MetalArchives
340
342
  #
341
343
  def find_by!(query)
342
344
  obj = find_by query
343
- obj.load! if obj
345
+ obj&.load!
344
346
 
345
347
  obj
346
348
  end
@@ -371,14 +373,14 @@ module MetalArchives
371
373
  # - +:independent+: boolean
372
374
  #
373
375
  def search_by(query)
374
- url = "#{MetalArchives.config.endpoint}search/ajax-advanced/searching/bands"
376
+ url = "#{MetalArchives.config.default_endpoint}search/ajax-advanced/searching/bands"
375
377
 
376
378
  params = Parsers::Band.map_params query
377
379
 
378
380
  l = lambda do
379
381
  @start ||= 0
380
382
 
381
- if @max_items and @start >= @max_items
383
+ if @max_items && @start >= @max_items
382
384
  []
383
385
  else
384
386
  response = HTTPClient.get url, params.merge(:iDisplayStart => @start)
@@ -390,7 +392,7 @@ module MetalArchives
390
392
 
391
393
  json['aaData'].each do |data|
392
394
  # Create Band object for every ID in the results list
393
- id = Nokogiri::HTML(data.first).xpath('//a/@href').first.value.gsub('\\', '').split('/').last.gsub(/\D/, '').to_i
395
+ id = Nokogiri::HTML(data.first).xpath('//a/@href').first.value.delete('\\').split('/').last.gsub(/\D/, '').to_i
394
396
  objects << Band.find(id)
395
397
  end
396
398
 
@@ -432,7 +434,7 @@ module MetalArchives
432
434
  # - rdoc-ref:MetalArchives::Errors::ParserError when parsing failed. Please report this error.
433
435
  #
434
436
  def all
435
- search_by Hash.new
437
+ search_by({})
436
438
  end
437
439
  end
438
440
  end