rbrainz 0.1.1 → 0.2.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 (85) hide show
  1. data/CHANGES +31 -0
  2. data/LICENSE +1 -1
  3. data/README +3 -2
  4. data/Rakefile +40 -22
  5. data/TODO +6 -23
  6. data/doc/README.rdoc +50 -21
  7. data/examples/getartist.rb +6 -4
  8. data/examples/getuser.rb +30 -0
  9. data/examples/searchartists.rb +35 -0
  10. data/lib/rbrainz.rb +12 -7
  11. data/lib/rbrainz/core_ext.rb +8 -0
  12. data/lib/rbrainz/core_ext/mbid.rb +30 -0
  13. data/lib/rbrainz/core_ext/net_http_digest.rb +52 -0
  14. data/lib/rbrainz/core_ext/range.rb +28 -0
  15. data/lib/rbrainz/core_ext/range/equality.rb +232 -0
  16. data/lib/rbrainz/data/countrynames.rb +7 -5
  17. data/lib/rbrainz/data/languagenames.rb +8 -5
  18. data/lib/rbrainz/data/releasetypenames.rb +34 -0
  19. data/lib/rbrainz/data/scriptnames.rb +8 -5
  20. data/lib/rbrainz/model.rb +27 -35
  21. data/lib/rbrainz/model/alias.rb +31 -7
  22. data/lib/rbrainz/model/artist.rb +30 -41
  23. data/lib/rbrainz/model/collection.rb +102 -0
  24. data/lib/rbrainz/model/default_factory.rb +78 -0
  25. data/lib/rbrainz/model/disc.rb +45 -8
  26. data/lib/rbrainz/model/entity.rb +122 -53
  27. data/lib/rbrainz/model/incomplete_date.rb +31 -47
  28. data/lib/rbrainz/model/individual.rb +103 -0
  29. data/lib/rbrainz/model/label.rb +42 -33
  30. data/lib/rbrainz/model/mbid.rb +111 -40
  31. data/lib/rbrainz/model/relation.rb +78 -14
  32. data/lib/rbrainz/model/release.rb +119 -31
  33. data/lib/rbrainz/model/release_event.rb +38 -9
  34. data/lib/rbrainz/model/scored_collection.rb +99 -0
  35. data/lib/rbrainz/model/tag.rb +39 -0
  36. data/lib/rbrainz/model/track.rb +37 -13
  37. data/lib/rbrainz/model/user.rb +48 -0
  38. data/lib/rbrainz/utils.rb +9 -0
  39. data/lib/rbrainz/utils/data.rb +78 -0
  40. data/lib/rbrainz/utils/helper.rb +22 -0
  41. data/lib/rbrainz/version.rb +15 -0
  42. data/lib/rbrainz/webservice.rb +32 -6
  43. data/lib/rbrainz/webservice/filter.rb +124 -47
  44. data/lib/rbrainz/webservice/includes.rb +49 -10
  45. data/lib/rbrainz/webservice/mbxml.rb +228 -173
  46. data/lib/rbrainz/webservice/query.rb +312 -25
  47. data/lib/rbrainz/webservice/webservice.rb +164 -27
  48. data/test/lib/mock_webservice.rb +53 -0
  49. data/test/lib/test_entity.rb +27 -8
  50. data/test/lib/test_factory.rb +47 -0
  51. data/test/lib/testing_helper.rb +7 -5
  52. data/test/test-data/invalid/artist/tags_1.xml +6 -0
  53. data/test/test-data/valid/artist/Tchaikovsky-2.xml +12 -0
  54. data/test/test-data/valid/label/Atlantic_Records_2.xml +3 -0
  55. data/test/test-data/valid/label/Atlantic_Records_3.xml +11 -0
  56. data/test/test-data/valid/release/Highway_61_Revisited_2.xml +12 -0
  57. data/test/test-data/valid/track/Silent_All_These_Years_6.xml +8 -0
  58. data/test/test_alias.rb +13 -7
  59. data/test/test_artist.rb +26 -4
  60. data/test/test_artist_filter.rb +11 -6
  61. data/test/test_artist_includes.rb +11 -6
  62. data/test/test_collection.rb +66 -0
  63. data/test/test_default_factory.rb +75 -0
  64. data/test/test_disc.rb +9 -4
  65. data/test/test_incomplete_date.rb +21 -14
  66. data/test/test_label.rb +56 -18
  67. data/test/test_label_filter.rb +10 -5
  68. data/test/test_label_includes.rb +11 -6
  69. data/test/test_mbid.rb +34 -19
  70. data/test/test_mbxml.rb +242 -72
  71. data/test/test_query.rb +92 -7
  72. data/test/test_range_equality.rb +144 -0
  73. data/test/test_relation.rb +18 -7
  74. data/test/test_release.rb +15 -4
  75. data/test/test_release_event.rb +16 -4
  76. data/test/test_release_filter.rb +11 -5
  77. data/test/test_release_includes.rb +11 -6
  78. data/test/test_scored_collection.rb +86 -0
  79. data/test/test_tag.rb +39 -0
  80. data/test/test_track.rb +15 -4
  81. data/test/test_track_filter.rb +11 -5
  82. data/test/test_track_includes.rb +11 -6
  83. data/test/test_utils.rb +41 -0
  84. data/test/test_webservice.rb +16 -17
  85. metadata +93 -57
@@ -1,17 +1,19 @@
1
- # $Id: mbxml.rb 36 2007-05-29 18:43:36Z phw $
2
- # Copyright (c) 2007, Philipp Wolfer
3
- # All rights reserved.
4
- # See LICENSE for permissions.
1
+ # $Id: mbxml.rb 148 2007-07-19 17:26:33Z phw $
2
+ #
3
+ # Author:: Philipp Wolfer (mailto:phw@rubyforge.org)
4
+ # Copyright:: Copyright (c) 2007, Nigel Graham, Philipp Wolfer
5
+ # License:: RBrainz is free software distributed under a BSD style license.
6
+ # See LICENSE[file:../LICENSE.html] for permissions.
5
7
 
6
8
  require 'rbrainz/model'
9
+ require 'rbrainz/model/scored_collection'
7
10
  require 'rexml/document'
8
- include REXML
9
11
 
10
12
  module MusicBrainz
11
13
  module Webservice
12
14
 
13
15
  # Class to read the XML data returned by the MusicBrainz
14
- # webservice and create the corresponding model classes.
16
+ # web service and create the corresponding model classes.
15
17
  # The class understands the MusicBrainz XML Metadata Version 1.0
16
18
  # schema.
17
19
  #
@@ -19,12 +21,31 @@ module MusicBrainz
19
21
  # information on the MusicBrainz XML Metadata schema.
20
22
  class MBXML
21
23
 
22
- def initialize(xml)
23
- @document = Document.new(xml)
24
+ # Exception to be raised if a parse error occurs in MBXML.
25
+ class ParseError < Exception
26
+ end
27
+
28
+ # Create a new MBXML instance to parse a MusicBrainz metadata document.
29
+ #
30
+ # Parameters:
31
+ # [stream] An IO object to read the MusicBrainz metadata XML document from.
32
+ # [factory] A model factory. An instance of Model::DefaultFactory
33
+ # will be used if none is given.
34
+ #
35
+ # Raises:: MBXML::ParseError
36
+ def initialize(stream, factory=nil)
37
+ begin
38
+ @document = REXML::Document.new(stream)
39
+ rescue REXML::ParseException => e
40
+ raise ParseError.new(e.to_s)
41
+ end
42
+
43
+ # Set the model factory
44
+ factory = Model::DefaultFactory.new unless factory
45
+ @factory = factory
24
46
 
25
- # Already loaded artists, releases, tracks
26
- # and labels will get cached in these variables
27
- # to link to them if they occure multiple times
47
+ # Already loaded artists, releases, tracks and labels will get cached
48
+ # in these variables to link to them if they occure multiple times
28
49
  # inside the same document.
29
50
  @artists = Hash.new
30
51
  @releases = Hash.new
@@ -32,9 +53,9 @@ module MusicBrainz
32
53
  @labels = Hash.new
33
54
  end
34
55
 
35
- # Read the XML string and create an entity model
36
- # for the given entity type if it is present in
37
- # the document.
56
+ # Read the XML string and create an entity model for the given entity
57
+ # type if it is present in the document.
58
+ #
38
59
  # Returns nil if no entity of the given type is present.
39
60
  def get_entity(entity_type)
40
61
  # Search for the first occuring node of type entity which is a child node
@@ -43,80 +64,53 @@ module MusicBrainz
43
64
  [Model::NS_MMD_1, entity_type]]
44
65
 
45
66
  unless entity.nil? or entity.is_a? REXML::Text
46
- case entity.name
47
- when 'artist'
48
- return create_artist(entity)
49
- when 'release'
50
- return create_release(entity)
51
- when 'track'
52
- return create_track(entity)
53
- when 'label'
54
- return create_label(entity)
55
- end
67
+ create_method = method('create_' + entity.name)
68
+ create_method.call(entity) if create_method
56
69
  else
57
70
  return nil
58
71
  end
59
72
  end
60
73
 
61
- # Read the XML string and create a list of entity
62
- # models for the given entity type. Ther must be
63
- # an entity-list element as a child of the metadata
64
- # element in the document.
65
- # Returns nil if no entity list of the given type is present.
66
- # Returns an empty array if the list is empty.
67
- def get_entity_list(entity_type)
74
+ # Read the XML string and create a list of entity models for the given
75
+ # entity type. There must be an entity-list element as a child of the
76
+ # +metadata+ element in the document.
77
+ #
78
+ # Returns an empty Collection if the list is empty.
79
+ # Raises a MBXML::ParseError if no entity-list element can be found.
80
+ #
81
+ # Raises:: MBXML::ParseError
82
+ def get_entity_list(entity_type, ns=Model::NS_MMD_1)
68
83
  # Search for the first occuring node of type entity which is a child node
69
84
  # of the metadata element.
70
- entity_list = @document.elements["//[local-name()='metadata' and namespace-uri()='%s']/%s-list[1]" %
71
- [Model::NS_MMD_1, entity_type]]
85
+ entity_list = @document.elements[
86
+ "//[local-name()='metadata' and namespace-uri()='%s']/[local-name()='%s-list' and namespace-uri()='%s'][1]" %
87
+ [Model::NS_MMD_1, entity_type, ns]]
72
88
 
73
89
  unless entity_list.nil? or entity_list.is_a? REXML::Text
74
- list = Array.new
90
+ collection = Model::ScoredCollection.new(entity_list.attributes['count'],
91
+ entity_list.attributes['offset'])
92
+ # Select the method to use for reading the list.
93
+ read_list_method = method('read_' + entity_list.name.gsub('-', '_'))
75
94
 
76
- case entity_list.name
77
- when 'artist-list'
78
- read_artist_list(entity_list) {|model|
79
- list << model
80
- }
81
- return list
82
- when 'release-list'
83
- read_release_list(entity_list) {|model|
84
- list << model
85
- }
86
- return list
87
- when 'track-list'
88
- read_track_list(entity_list) {|model|
89
- list << model
90
- }
91
- return list
92
- when 'label-list'
93
- read_label_list(entity_list) {|model|
94
- list << model
95
- }
96
- return list
97
- end
95
+ # Read the entity list and store the entities in the collection.
96
+ read_list_method.call(entity_list, collection, true) if read_list_method
98
97
 
99
- if iterator
100
- list = Array.new
101
- iterator.each {|model|
102
- raise model.inspect
103
- list << model
104
- }
105
- return list
106
- end
98
+ return collection
99
+ else
100
+ raise ParseError.new("no element %s-list found" % entity_type)
107
101
  end
108
102
  return nil
109
103
  end
110
104
 
111
105
  private # ----------------------------------------------------------------
112
106
 
113
- # Iterate over a list of artists.
107
+ # Iterate over a list of artists and add them to the target collection.
114
108
  #
115
109
  # The node must be of the type <em>artist-list</em>.
116
- def read_artist_list(node)
117
- node.elements.each('artist') {|child|
118
- yield create_artist(child)
119
- }
110
+ def read_artist_list(list_node, target_collection, read_scores=false)
111
+ read_list(list_node, target_collection, 'artist', read_scores) do |a|
112
+ yield a if block_given?
113
+ end
120
114
  end
121
115
 
122
116
  # Create an +Artist+ object from the given artist node.
@@ -124,7 +118,7 @@ module MusicBrainz
124
118
  if node.attributes['id'] and @artists[node.attributes['id']]
125
119
  artist = @artists[node.attributes['id']]
126
120
  else
127
- artist = Model::Artist.new
121
+ artist = @factory.new_artist
128
122
  @artists[node.attributes['id']] = artist
129
123
  end
130
124
 
@@ -148,19 +142,12 @@ module MusicBrainz
148
142
  end
149
143
 
150
144
  # Read the alias list
151
- if node.elements['alias-list']
152
- read_alias_list(node.elements['alias-list']) {|artist_alias|
153
- artist.aliases << artist_alias
154
- }
155
- end
145
+ read_alias_list(node.elements['alias-list'], artist.aliases)
156
146
 
157
147
  # Read the release list
158
- if node.elements['release-list']
159
- read_release_list(node.elements['release-list']) {|release|
160
- release.artist = artist unless release.artist
161
- artist.releases << release
162
- }
163
- end
148
+ read_release_list(node.elements['release-list'], artist.releases) {|release|
149
+ release.artist = artist unless release.artist
150
+ }
164
151
 
165
152
  # Read the relation list
166
153
  if node.elements['relation-list']
@@ -171,26 +158,29 @@ module MusicBrainz
171
158
  }
172
159
  end
173
160
 
161
+ # Read the tag list
162
+ read_tag_list(node.elements['tag-list'], artist.tags)
163
+
174
164
  return artist
175
165
  end
176
166
 
177
- # Iterate over a list of releases.
167
+ # Iterate over a list of releases and add them to the target collection.
178
168
  #
179
169
  # The node must be of the type <em>release-list</em>.
180
- def read_release_list(node)
181
- node.elements.each('release') {|child|
182
- yield create_release(child)
183
- }
170
+ def read_release_list(list_node, target_collection, read_scores=false)
171
+ read_list(list_node, target_collection, 'release', read_scores) do |a|
172
+ yield a if block_given?
173
+ end
184
174
  end
185
175
 
186
176
  # Create a +Release+ object from the given release node.
187
177
  #
188
- # TODO: PUID list
178
+ # TODO:: PUID list
189
179
  def create_release(node)
190
180
  if node.attributes['id'] and @releases[node.attributes['id']]
191
181
  release = @releases[node.attributes['id']]
192
182
  else
193
- release = Model::Release.new
183
+ release = @factory.new_release
194
184
  @releases[node.attributes['id']] = release
195
185
  end
196
186
 
@@ -212,28 +202,17 @@ module MusicBrainz
212
202
  end
213
203
 
214
204
  # Read the track list
215
- if node.elements['track-list']
216
- read_track_list(node.elements['track-list']) {|track|
217
- track.artist = release.artist unless track.artist
218
- track.releases << release
219
- release.tracks << track
220
- }
221
- end
205
+ read_track_list(node.elements['track-list'], release.tracks) {|track|
206
+ track.artist = release.artist unless track.artist
207
+ track.releases << release
208
+ }
222
209
 
223
210
  # Read the release event list
224
- if node.elements['release-event-list']
225
- read_release_event_list(node.elements['release-event-list']) {|event|
226
- release.release_events << event
227
- }
228
- end
211
+ read_release_event_list(node.elements['release-event-list'], release.release_events)
229
212
 
230
213
  # Read the disc list
231
- if node.elements['disc-list']
232
- read_disc_list(node.elements['disc-list']) {|disc|
233
- release.discs << disc
234
- }
235
- end
236
-
214
+ read_disc_list(node.elements['disc-list'], release.discs)
215
+
237
216
  # Read the relation list
238
217
  if node.elements['relation-list']
239
218
  node.elements.each('relation-list') {|relation_node|
@@ -243,16 +222,19 @@ module MusicBrainz
243
222
  }
244
223
  end
245
224
 
225
+ # Read the tag list
226
+ read_tag_list(node.elements['tag-list'], release.tags)
227
+
246
228
  return release
247
229
  end
248
230
 
249
- # Iterate over a list of tracks.
231
+ # Iterate over a list of tracks and add them to the target collection.
250
232
  #
251
233
  # The node must be of the type <em>track-list</em>.
252
- def read_track_list(node)
253
- node.elements.each('track') {|child|
254
- yield create_track(child)
255
- }
234
+ def read_track_list(list_node, target_collection, read_scores=false)
235
+ read_list(list_node, target_collection, 'track', read_scores) do |a|
236
+ yield a if block_given?
237
+ end
256
238
  end
257
239
 
258
240
  # Create a +Track+ object from the given track node.
@@ -260,7 +242,7 @@ module MusicBrainz
260
242
  if node.attributes['id'] and @tracks[node.attributes['id']]
261
243
  track = @tracks[node.attributes['id']]
262
244
  else
263
- track = Model::Track.new
245
+ track = @factory.new_track
264
246
  @tracks[node.attributes['id']] = track
265
247
  end
266
248
 
@@ -271,19 +253,12 @@ module MusicBrainz
271
253
  track.artist = create_artist(node.elements['artist']) if node.elements['artist']
272
254
 
273
255
  # Read the release list
274
- if node.elements['release-list']
275
- read_release_list(node.elements['release-list']) {|release|
276
- release.tracks << track
277
- track.releases << release
278
- }
279
- end
256
+ read_release_list(node.elements['release-list'], track.releases) {|release|
257
+ release.tracks << track
258
+ }
280
259
 
281
260
  # Read the PUID list
282
- if node.elements['puid-list']
283
- read_puid_list(node.elements['puid-list']) {|puid|
284
- track.puids << puid
285
- }
286
- end
261
+ read_puid_list(node.elements['puid-list'], track.puids)
287
262
 
288
263
  # Read the relation list
289
264
  if node.elements['relation-list']
@@ -294,16 +269,19 @@ module MusicBrainz
294
269
  }
295
270
  end
296
271
 
272
+ # Read the tag list
273
+ read_tag_list(node.elements['tag-list'], track.tags)
274
+
297
275
  return track
298
276
  end
299
277
 
300
- # Iterate over a list of labels.
278
+ # Iterate over a list of labels and add them to the target collection.
301
279
  #
302
280
  # The node must be of the type <em>label-list</em>.
303
- def read_label_list(node)
304
- node.elements.each('label') {|child|
305
- yield create_label(child)
306
- }
281
+ def read_label_list(list_node, target_collection, read_scores=false)
282
+ read_list(list_node, target_collection, 'label', read_scores) do |a|
283
+ yield a if block_given?
284
+ end
307
285
  end
308
286
 
309
287
  # Create a +Label+ object from the given label node.
@@ -311,7 +289,7 @@ module MusicBrainz
311
289
  if node.attributes['id'] and @labels[node.attributes['id']]
312
290
  label = @labels[node.attributes['id']]
313
291
  else
314
- label = Model::Label.new
292
+ label = @factory.new_label
315
293
  @labels[node.attributes['id']] = label
316
294
  end
317
295
 
@@ -329,19 +307,18 @@ module MusicBrainz
329
307
 
330
308
  if life_span = node.elements['life-span']
331
309
  if life_span.attributes['begin']
332
- label.founding_date = Model::IncompleteDate.new life_span.attributes['begin']
310
+ label.begin_date = Model::IncompleteDate.new life_span.attributes['begin']
333
311
  end
334
312
  if life_span.attributes['end']
335
- label.dissolving_date = Model::IncompleteDate.new life_span.attributes['end']
313
+ label.end_date = Model::IncompleteDate.new life_span.attributes['end']
336
314
  end
337
315
  end
338
316
 
339
317
  # Read the alias list
340
- if node.elements['release-list']
341
- read_release_list(node.elements['release-list']) {|release|
342
- label.releases << release
343
- }
344
- end
318
+ read_alias_list(node.elements['alias-list'], label.aliases)
319
+
320
+ # Read the release list
321
+ read_release_list(node.elements['release-list'], label.releases)
345
322
 
346
323
  # Read the relation list
347
324
  if node.elements['relation-list']
@@ -352,39 +329,61 @@ module MusicBrainz
352
329
  }
353
330
  end
354
331
 
332
+ # Read the tag list
333
+ read_tag_list(node.elements['tag-list'], label.tags)
334
+
355
335
  return label
356
336
  end
357
337
 
358
- # Iterate over a list of aliases.
338
+ # Iterate over a list of aliases and add them to the target collection.
359
339
  #
360
340
  # The node must be of the type <em>alias-list</em>.
361
- def read_alias_list(node)
362
- node.elements.each('alias') {|child|
363
- yield create_alias(child)
364
- }
341
+ def read_alias_list(list_node, target_collection)
342
+ read_list(list_node, target_collection, 'alias') do |a|
343
+ yield a if block_given?
344
+ end
365
345
  end
366
346
 
367
347
  # Create an +Alias+ object from the given alias node.
368
348
  def create_alias(node)
369
- alias_model = Model::Alias.new
349
+ alias_model = @factory.new_alias
370
350
  alias_model.name = node.text
371
- alias_model.type = node.attributes['type']
351
+ if node.attributes['type']
352
+ alias_model.type = MBXML.add_namespace(node.attributes['type'], Model::NS_MMD_1)
353
+ end
372
354
  alias_model.script = node.attributes['script']
373
355
  return alias_model
374
356
  end
375
357
 
376
- # Iterate over a list of release events.
358
+ # Iterate over a list of tags and add them to the target collection.
359
+ #
360
+ # The node must be of the type <em>tag-list</em>.
361
+ def read_tag_list(list_node, target_collection)
362
+ read_list(list_node, target_collection, 'tag') do |a|
363
+ yield a if block_given?
364
+ end
365
+ end
366
+
367
+ # Create an +Tag+ object from the given tag node.
368
+ def create_tag(node)
369
+ tag = @factory.new_tag
370
+ tag.text = node.text
371
+ tag.count = node.attributes['count'].to_i if node.attributes['count']
372
+ return tag
373
+ end
374
+
375
+ # Iterate over a list of release events and add them to the target collection.
377
376
  #
378
377
  # The node must be of the type <em>release-event-list</em>.
379
- def read_release_event_list(node)
380
- node.elements.each('event') {|child|
381
- yield create_release_event(child)
382
- }
378
+ def read_release_event_list(list_node, target_collection)
379
+ read_list(list_node, target_collection, 'event') do |a|
380
+ yield a if block_given?
381
+ end
383
382
  end
384
383
 
385
384
  # Create an +ReleaseEvent+ object from the given release event node.
386
- def create_release_event(node)
387
- event = Model::ReleaseEvent.new
385
+ def create_event(node)
386
+ event = @factory.new_release_event
388
387
 
389
388
  # Read all defined data fields
390
389
  if node.attributes['date']
@@ -398,40 +397,50 @@ module MusicBrainz
398
397
  return event
399
398
  end
400
399
 
401
- # Iterate over a list of PUIDs.
400
+ # Iterate over a list of PUIDs and add them to the target collection.
402
401
  #
403
402
  # The node must be of the type <em>puid-list</em>.
404
- def read_puid_list(node)
405
- node.elements.each('puid') {|child|
406
- yield child.attributes['id']
407
- }
403
+ def read_puid_list(list_node, target_collection)
404
+ read_list(list_node, target_collection, 'puid') do |a|
405
+ yield a if block_given?
406
+ end
408
407
  end
409
408
 
410
- # Iterate over a list of discs.
409
+ # Create a PUID
410
+ def create_puid(node)
411
+ return node.attributes['id']
412
+ end
413
+
414
+ # Iterate over a list of discs and add them to the target collection.
411
415
  #
412
416
  # The node must be of the type <em>disc-list</em>.
413
- def read_disc_list(node)
414
- node.elements.each('disc') {|child|
415
- disc = Model::Disc.new
416
- disc.id = child.attributes['id']
417
- disc.sectors = child.attributes['sectors'].to_i
418
- yield disc
419
- }
417
+ def read_disc_list(list_node, target_collection)
418
+ read_list(list_node, target_collection, 'disc') do |a|
419
+ yield a if block_given?
420
+ end
421
+ end
422
+
423
+ # Create a +Disc+ object from the given disc node.
424
+ def create_disc(node)
425
+ disc = @factory.new_disc
426
+ disc.id = node.attributes['id']
427
+ disc.sectors = node.attributes['sectors'].to_i
428
+ return disc
420
429
  end
421
430
 
422
431
  # Iterate over a list of relations.
423
432
  #
424
433
  # The node must be of the type <em>relation-list</em>.
425
434
  def read_relation_list(node)
435
+ target_type = MBXML.add_namespace(node.attributes['target-type'], Model::NS_REL_1)
426
436
  node.elements.each('relation') {|child|
427
- target_type = MBXML.add_namespace(node.attributes['target-type'], Model::NS_REL_1)
428
437
  yield create_relation(child, target_type)
429
438
  }
430
439
  end
431
440
 
432
441
  # Create a +Relation+ object from the given relation node.
433
442
  def create_relation(node, target_type)
434
- relation = Model::Relation.new
443
+ relation = @factory.new_relation
435
444
 
436
445
  # Read all defined data fields
437
446
  if node.attributes['direction']
@@ -464,29 +473,29 @@ module MusicBrainz
464
473
  if node.elements['artist']
465
474
  target = create_artist node.elements['artist']
466
475
  else
467
- target = Model::Artist.new
468
- target.id = Model::MBID.from_uuid(:artist, node.attributes['target'])
476
+ target = @factory.new_artist
477
+ target.id = Model::MBID.parse(node.attributes['target'], :artist)
469
478
  end
470
479
  when Model::Relation::TO_RELEASE
471
480
  if node.elements['release']
472
481
  target = create_release node.elements['release']
473
482
  else
474
- target = Model::Release.new
475
- target.id = Model::MBID.from_uuid(:release, node.attributes['target'])
483
+ target = @factory.new_release
484
+ target.id = Model::MBID.parse(node.attributes['target'], :release)
476
485
  end
477
486
  when Model::Relation::TO_TRACK
478
487
  if node.elements['track']
479
488
  target = create_track node.elements['track']
480
489
  else
481
- target = Model::Track.new
482
- target.id = Model::MBID.from_uuid(:track, node.attributes['target'])
490
+ target = @factory.new_track
491
+ target.id = Model::MBID.parse(node.attributes['target'], :track)
483
492
  end
484
493
  when Model::Relation::TO_LABEL
485
494
  if node.elements['label']
486
495
  target = create_label node.elements['label']
487
496
  else
488
- target = Model::Label.new
489
- target.id = Model::MBID.from_uuid(:label, node.attributes['target'])
497
+ target = @factory.new_label
498
+ target.id = Model::MBID.parse(node.attributes['target'], :label)
490
499
  end
491
500
  when Model::Relation::TO_URL
492
501
  target = node.attributes['target']
@@ -497,6 +506,45 @@ module MusicBrainz
497
506
  return relation
498
507
  end
499
508
 
509
+ def read_user_list(list_node, target_collection, read_scores=false)
510
+ read_list(list_node, target_collection, 'user', read_scores, Model::NS_EXT_1) {|a|
511
+ yield a if block_given?
512
+ }
513
+ end
514
+
515
+ def create_user(node)
516
+ user_model = @factory.new_user
517
+ # Read the types
518
+ node.attributes['type'].split(' ').each {|type|
519
+ user_model.types << MBXML.add_namespace(type, Model::NS_EXT_1)
520
+ } if node.attributes['type']
521
+
522
+ user_model.name = node.elements['name'].text
523
+ user_model.show_nag = MBXML.get_element(node, 'nag', Model::NS_EXT_1).attributes['show'] == 'true'
524
+ return user_model
525
+ end
526
+
527
+ # Helper method that reads a list of a special node type.
528
+ # There must be a method create_{child_name} which returns an
529
+ # instance of the corresponding model.
530
+ def read_list(list_node, target_collection, child_name, read_scores=false, ns=Model::NS_MMD_1)
531
+ if list_node
532
+ target_collection.offset = list_node.attributes['offset'].to_i
533
+ target_collection.count = list_node.attributes['count'].to_i
534
+ MBXML.each_element(list_node, child_name, ns) do |child|
535
+ model = method('create_' + child_name).call(child)
536
+ if read_scores
537
+ score = child.attributes['ext:score'].to_i
538
+ entry = Model::ScoredCollection::Entry.new(model, score)
539
+ else
540
+ entry = model
541
+ end
542
+ target_collection << entry
543
+ yield entry if block_given?
544
+ end
545
+ end
546
+ end
547
+
500
548
  # Helper method which will return the given property
501
549
  # extended by the namespace. If the property
502
550
  # already includes the namespace it will be returned
@@ -510,6 +558,13 @@ module MusicBrainz
510
558
  end
511
559
  end
512
560
 
561
+ def self.get_element(node, local_name, ns)
562
+ node.elements["*[local-name() = '#{local_name}' and namespace-uri() = '#{ns}']"]
563
+ end
564
+
565
+ def self.each_element(node, local_name, ns, &block)
566
+ node.elements.each("*[local-name() = '#{local_name}' and namespace-uri()='#{ns}']", &block)
567
+ end
513
568
  end
514
569
 
515
570
  end