metal_archives 0.8.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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