metal_archives 2.2.3 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +59 -12
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +34 -20
  5. data/CHANGELOG.md +16 -1
  6. data/LICENSE.md +17 -4
  7. data/README.md +37 -29
  8. data/bin/console +8 -11
  9. data/config/inflections.rb +7 -0
  10. data/config/initializers/.keep +0 -0
  11. data/docker-compose.yml +10 -1
  12. data/lib/metal_archives.rb +57 -21
  13. data/lib/metal_archives/cache/base.rb +40 -0
  14. data/lib/metal_archives/cache/memory.rb +68 -0
  15. data/lib/metal_archives/cache/null.rb +22 -0
  16. data/lib/metal_archives/cache/redis.rb +49 -0
  17. data/lib/metal_archives/collection.rb +3 -5
  18. data/lib/metal_archives/configuration.rb +28 -21
  19. data/lib/metal_archives/errors.rb +9 -1
  20. data/lib/metal_archives/http_client.rb +42 -46
  21. data/lib/metal_archives/models/artist.rb +55 -26
  22. data/lib/metal_archives/models/band.rb +43 -36
  23. data/lib/metal_archives/models/{base_model.rb → base.rb} +57 -50
  24. data/lib/metal_archives/models/label.rb +7 -8
  25. data/lib/metal_archives/models/release.rb +21 -18
  26. data/lib/metal_archives/parsers/artist.rb +41 -36
  27. data/lib/metal_archives/parsers/band.rb +73 -29
  28. data/lib/metal_archives/parsers/base.rb +14 -0
  29. data/lib/metal_archives/parsers/country.rb +21 -0
  30. data/lib/metal_archives/parsers/date.rb +31 -0
  31. data/lib/metal_archives/parsers/genre.rb +67 -0
  32. data/lib/metal_archives/parsers/label.rb +21 -13
  33. data/lib/metal_archives/parsers/parser.rb +17 -77
  34. data/lib/metal_archives/parsers/release.rb +29 -18
  35. data/lib/metal_archives/parsers/year.rb +31 -0
  36. data/lib/metal_archives/version.rb +3 -3
  37. data/metal_archives.env.example +7 -4
  38. data/metal_archives.gemspec +7 -4
  39. data/nginx/default.conf +2 -2
  40. metadata +76 -32
  41. data/.github/workflows/release.yml +0 -69
  42. data/.rubocop_todo.yml +0 -92
  43. data/lib/metal_archives/lru_cache.rb +0 -61
  44. data/lib/metal_archives/middleware/cache_check.rb +0 -18
  45. data/lib/metal_archives/middleware/encoding.rb +0 -16
  46. data/lib/metal_archives/middleware/headers.rb +0 -38
  47. data/lib/metal_archives/middleware/rewrite_endpoint.rb +0 -38
  48. data/lib/metal_archives/nil_date.rb +0 -91
  49. data/lib/metal_archives/range.rb +0 -69
@@ -8,7 +8,7 @@ module MetalArchives
8
8
  ##
9
9
  # Represents a single performer (but not a solo artist)
10
10
  #
11
- class Artist < MetalArchives::BaseModel
11
+ class Artist < Base
12
12
  ##
13
13
  # :attr_reader: id
14
14
  #
@@ -63,24 +63,24 @@ module MetalArchives
63
63
  ##
64
64
  # :attr_reader: date_of_birth
65
65
  #
66
- # Returns rdoc-ref:NilDate
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_of_birth, type: NilDate
72
+ property :date_of_birth, type: Date
73
73
 
74
74
  ##
75
75
  # :attr_reader: date_of_death
76
76
  #
77
- # Returns rdoc-ref:NilDate
77
+ # Returns rdoc-ref:Date
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 :date_of_death, type: NilDate
83
+ property :date_of_death, type: Date
84
84
 
85
85
  ##
86
86
  # :attr_reader: cause_of_death
@@ -102,7 +102,7 @@ module MetalArchives
102
102
  # - rdoc-ref:MetalArchives::Errors::InvalidIDError when no or invalid id
103
103
  # - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
104
104
  #
105
- enum :gender, values: %i(male female)
105
+ enum :gender, values: [:male, :female]
106
106
 
107
107
  ##
108
108
  # :attr_reader: biography
@@ -165,7 +165,7 @@ module MetalArchives
165
165
  # [+bands+]
166
166
  # - +:band+: rdoc-ref:Band
167
167
  # - +:active+: Boolean
168
- # - +:date_active+: +Array+ of rdoc-ref:Range containing rdoc-ref:NilDate
168
+ # - +:years_active+: +Array+ of rdoc-ref:Range containing +Integer+
169
169
  # - +:role+: +String+
170
170
  #
171
171
  property :bands, type: Hash, multiple: true
@@ -173,6 +173,41 @@ module MetalArchives
173
173
  # TODO: guest/session bands
174
174
  # TODO: misc bands
175
175
 
176
+ ##
177
+ # Serialize to hash
178
+ #
179
+ def to_h
180
+ {
181
+ type: "artist",
182
+ id: id,
183
+ name: name,
184
+ aliases: aliases || [],
185
+ country: country&.alpha3,
186
+ location: location,
187
+ date_of_birth: date_of_birth&.iso8601,
188
+ date_of_death: date_of_death&.iso8601,
189
+ cause_of_death: cause_of_death,
190
+ gender: gender,
191
+ biography: biography,
192
+ trivia: trivia,
193
+ photo: photo,
194
+ links: links || [],
195
+ bands: bands || [],
196
+ }
197
+ end
198
+
199
+ ##
200
+ # Deserialize from hash
201
+ #
202
+ def self.from_h(hash)
203
+ return unless hash.fetch(:type) == "artist"
204
+
205
+ new(hash.slice(:id, :name, :aliases, :location, :cause_of_death, :gender, :biography, :trivial, :photo, :links, :bands))
206
+ .tap { |m| m.country = ISO3166::Country[hash[:country]] }
207
+ .tap { |m| m.date_of_birth = Date.parse(hash[:date_of_birth]) if hash[:date_of_birth] }
208
+ .tap { |m| m.date_of_death = Date.parse(hash[:date_of_death]) if hash[:date_of_death] }
209
+ end
210
+
176
211
  protected
177
212
 
178
213
  ##
@@ -184,28 +219,24 @@ module MetalArchives
184
219
  #
185
220
  def assemble # :nodoc:
186
221
  ## Base attributes
187
- url = "#{MetalArchives.config.default_endpoint}artist/view/id/#{id}"
188
- response = HTTPClient.get url
222
+ response = MetalArchives.http.get "/artist/view/id/#{id}"
189
223
 
190
- properties = Parsers::Artist.parse_html response.body
224
+ properties = Parsers::Artist.parse_html response.to_s
191
225
 
192
226
  ## Biography
193
- url = "#{MetalArchives.config.default_endpoint}artist/read-more/id/#{id}/field/biography"
194
- response = HTTPClient.get url
227
+ response = MetalArchives.http.get "/artist/read-more/id/#{id}/field/biography"
195
228
 
196
- properties[:biography] = response.body
229
+ properties[:biography] = response.to_s
197
230
 
198
231
  ## Trivia
199
- url = "#{MetalArchives.config.default_endpoint}artist/read-more/id/#{id}/field/trivia"
200
- response = HTTPClient.get url
232
+ response = MetalArchives.http.get "/artist/read-more/id/#{id}/field/trivia"
201
233
 
202
- properties[:trivia] = response.body
234
+ properties[:trivia] = response.to_s
203
235
 
204
236
  ## Related links
205
- url = "#{MetalArchives.config.default_endpoint}link/ajax-list/type/person/id/#{id}"
206
- response = HTTPClient.get url
237
+ response = MetalArchives.http.get "/link/ajax-list/type/person/id/#{id}"
207
238
 
208
- properties[:links] = Parsers::Artist.parse_links_html response.body
239
+ properties[:links] = Parsers::Artist.parse_links_html response.to_s
209
240
 
210
241
  properties
211
242
  end
@@ -220,7 +251,7 @@ module MetalArchives
220
251
  # +Integer+
221
252
  #
222
253
  def find(id)
223
- return cache[id] if cache.include? id
254
+ return MetalArchives.cache[cache(id)] if MetalArchives.cache.include? cache(id)
224
255
 
225
256
  Artist.new id: id
226
257
  end
@@ -262,11 +293,10 @@ module MetalArchives
262
293
  def find_by(query)
263
294
  raise MetalArchives::Errors::ArgumentError unless query.include? :name
264
295
 
265
- url = "#{MetalArchives.config.default_endpoint}search/ajax-artist-search/"
266
296
  params = Parsers::Artist.map_params query
267
297
 
268
- response = HTTPClient.get url, params
269
- json = JSON.parse response.body
298
+ response = MetalArchives.http.get "/search/ajax-artist-search/", params
299
+ json = JSON.parse response.to_s
270
300
 
271
301
  return nil if json["aaData"].empty?
272
302
 
@@ -314,7 +344,6 @@ module MetalArchives
314
344
  def search(name)
315
345
  raise MetalArchives::Errors::ArgumentError unless name.is_a? String
316
346
 
317
- url = "#{MetalArchives.config.default_endpoint}search/ajax-artist-search/"
318
347
  query = { name: name }
319
348
 
320
349
  params = Parsers::Artist.map_params query
@@ -325,8 +354,8 @@ module MetalArchives
325
354
  if @max_items && @start >= @max_items
326
355
  []
327
356
  else
328
- response = HTTPClient.get url, params.merge(iDisplayStart: @start)
329
- json = JSON.parse response.body
357
+ response = MetalArchives.http.get "/search/ajax-artist-search/", params.merge(iDisplayStart: @start)
358
+ json = JSON.parse response.to_s
330
359
 
331
360
  @max_items = json["iTotalRecords"]
332
361
 
@@ -8,7 +8,7 @@ module MetalArchives
8
8
  ##
9
9
  # Represents an band (person or group)
10
10
  #
11
- class Band < MetalArchives::BaseModel
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:NilDate
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: NilDate
72
+ property :date_formed, type: Date
73
73
 
74
74
  ##
75
- # :attr_reader: date_active
75
+ # :attr_reader: years_active
76
76
  #
77
- # Returns +Array+ of rdoc-ref:Range containing rdoc-ref:NilDate
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 :date_active, type: MetalArchives::Range, multiple: true
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: MetalArchives::Label
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: %i(active split_up on_hold unknown changed_name disputed)
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: MetalArchives::Release, multiple: true
160
+ property :releases, type: Release, multiple: true
161
161
 
162
- # TODO: members
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
- url = "#{MetalArchives.config.default_endpoint}band/view/id/#{id}"
229
- response = HTTPClient.get url
243
+ response = MetalArchives.http.get "/band/view/id/#{id}"
230
244
 
231
- properties = Parsers::Band.parse_html response.body
245
+ properties = Parsers::Band.parse_html response.to_s
232
246
 
233
247
  ## Comment
234
- url = "#{MetalArchives.config.default_endpoint}band/read-more/id/#{id}"
235
- response = HTTPClient.get url
248
+ response = MetalArchives.http.get "/band/read-more/id/#{id}"
236
249
 
237
- properties[:comment] = response.body
250
+ properties[:comment] = response.to_s
238
251
 
239
252
  ## Similar artists
240
- url = "#{MetalArchives.config.default_endpoint}band/ajax-recommendations/id/#{id}"
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.body
255
+ properties[:similar] = Parsers::Band.parse_similar_bands_html response.to_s
244
256
 
245
257
  ## Related links
246
- url = "#{MetalArchives.config.default_endpoint}link/ajax-list/type/band/id/#{id}"
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.body
260
+ properties[:links] = Parsers::Band.parse_related_links_html response.to_s
250
261
 
251
262
  ## Releases
252
- url = "#{MetalArchives.config.default_endpoint}band/discography/id/#{id}/tab/all"
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.body
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[cache(id)] if MetalArchives.cache.include? cache(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:NilDate
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 = HTTPClient.get url, params
325
- json = JSON.parse response.body
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:NilDate
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:NilDate
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 = HTTPClient.get url, params.merge(iDisplayStart: @start)
404
- json = JSON.parse response.body
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 BaseModel
7
+ class Base
8
8
  ##
9
9
  # Generic shallow copy constructor
10
10
  #
11
- def initialize(hash = {})
12
- raise Errors::NotImplementedError, "no :id property in model" unless respond_to? :id?, true
11
+ def initialize(attributes = {})
12
+ raise Errors::NotImplementedError, "no :id property in model" unless respond_to? :id
13
13
 
14
- hash.each do |property, value|
15
- instance_variable_set("@#{property}", value) if self.class.properties.include? property
16
- end
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 ==(obj)
23
- obj.instance_of?(self.class) && id == obj.id
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:MetalArchives::Errors::InvalidIDError when no id
31
- # - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
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
- initialize assemble
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
- self.class.cache[id] = self
46
+ MetalArchives.cache[self.class.cache(id)] = self
46
47
  rescue StandardError => e
47
48
  # Don't cache invalid requests
48
- self.class.cache.delete id
49
+ MetalArchives.cache.delete self.class.cache(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
- !@loaded.nil?
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? && self.class.cache.include?(id)
64
+ loaded? && MetalArchives.cache.include?(self.class.cache(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:MetalArchives::Errors::InvalidIDError when no or invalid id
75
- # - rdoc-ref:MetalArchives::Errors::APIError when receiving a status code >= 400 (except 404)
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,17 @@ module MetalArchives
80
88
 
81
89
  class << self
82
90
  ##
83
- # +Array+ of declared properties
91
+ # Declared properties
84
92
  #
85
- attr_accessor :properties
93
+ def properties
94
+ @properties ||= {}
95
+ end
86
96
 
87
97
  ##
88
- # Get class-level object cache
98
+ # Generate cache key for id
89
99
  #
90
- def cache
91
- @cache ||= MetalArchives::LRUCache.new MetalArchives.config.cache_size
100
+ def cache(id)
101
+ "#{self.class.name}//#{id}"
92
102
  end
93
103
 
94
104
  protected
@@ -110,42 +120,38 @@ module MetalArchives
110
120
  # turns it into an +Array+ of +type+)
111
121
  #
112
122
  def property(name, opts = {})
113
- (@properties ||= []) << name
123
+ properties[name] = opts
114
124
 
115
125
  # property
116
126
  define_method(name) do
117
- load! unless loaded? && instance_variable_defined?("@#{name}") || name == :id
118
- instance_variable_get("@#{name}")
127
+ # Load only when not loaded or id property
128
+ load! unless loaded? || name == :id
129
+
130
+ instance_variable_get(:"@#{name}")
119
131
  end
120
132
 
121
133
  # property?
122
134
  define_method("#{name}?") do
123
- load! unless loaded? && instance_variable_defined?("@#{name}") || name == :id
124
-
125
- property = instance_variable_get("@#{name}")
126
- property.respond_to?(:empty?) ? !property.empty? : !property.nil?
135
+ send(name).present?
127
136
  end
128
137
 
129
138
  # property=
130
139
  define_method("#{name}=") do |value|
131
- if value.nil?
132
- instance_variable_set "@#{name}", value
133
- return
134
- end
140
+ return instance_variable_set(:"@#{name}", value) if value.nil?
135
141
 
136
142
  # Check value type
137
143
  type = opts[:type] || String
138
144
  if opts[:multiple]
139
- raise MetalArchives::Errors::TypeError, "invalid type #{value.class}, must be Array for #{name}" unless value.is_a? Array
145
+ raise Errors::TypeError, "invalid type #{value.class}, must be Array for #{name}" unless value.is_a? Array
140
146
 
141
147
  value.each do |val|
142
- raise MetalArchives::Errors::TypeError, "invalid type #{val.class}, must be #{type} for #{name}" unless val.is_a? type
148
+ raise Errors::TypeError, "invalid type #{val.class}, must be #{type} for #{name}" unless val.is_a? type
143
149
  end
144
150
  else
145
- raise MetalArchives::Errors::TypeError, "invalid type #{value.class}, must be #{type} for #{name}" unless value.is_a? type
151
+ raise Errors::TypeError, "invalid type #{value.class}, must be #{type} for #{name}" unless value.is_a? type
146
152
  end
147
153
 
148
- instance_variable_set "@#{name}", value
154
+ instance_variable_set(:"@#{name}", value)
149
155
  end
150
156
  end
151
157
 
@@ -166,19 +172,20 @@ module MetalArchives
166
172
  def enum(name, opts)
167
173
  raise ArgumentError, "opts[:values] is required" unless opts && opts[:values]
168
174
 
169
- (@properties ||= []) << name
175
+ properties[name] = opts
170
176
 
171
177
  # property
172
178
  define_method(name) do
173
- load! unless loaded? && instance_variable_defined?("@#{name}")
174
- instance_variable_get("@#{name}")
179
+ load! unless loaded?
180
+
181
+ instance_variable_get(:"@#{name}")
175
182
  end
176
183
 
177
184
  # property?
178
185
  define_method("#{name}?") do
179
186
  load! unless loaded? && instance_variable_defined?("@#{name}")
180
187
 
181
- property = instance_variable_get("@#{name}")
188
+ property = instance_variable_get(:"@#{name}")
182
189
  property.respond_to?(:empty?) ? !property.empty? : !property.nil?
183
190
  end
184
191
 
@@ -186,16 +193,16 @@ module MetalArchives
186
193
  define_method("#{name}=") do |value|
187
194
  # Check enum type
188
195
  if opts[:multiple]
189
- raise MetalArchives::Errors::TypeError, "invalid enum value #{value}, must be Array for #{name}" unless value.is_a? Array
196
+ raise Errors::TypeError, "invalid enum value #{value}, must be Array for #{name}" unless value.is_a? Array
190
197
 
191
198
  value.each do |val|
192
- raise MetalArchives::Errors::TypeError, "invalid enum value #{val} for #{name}" unless opts[:values].include? val
199
+ raise Errors::TypeError, "invalid enum value #{val} for #{name}" unless opts[:values].include? val
193
200
  end
194
201
  else
195
- raise MetalArchives::Errors::TypeError, "invalid enum value #{value} for #{name}" unless opts[:values].include? value
202
+ raise Errors::TypeError, "invalid enum value #{value} for #{name}" unless opts[:values].include? value
196
203
  end
197
204
 
198
- instance_variable_set name, value
205
+ instance_variable_set(:"@#{name}", value)
199
206
  end
200
207
  end
201
208