rbrainz 0.1.0 → 0.1.1

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.
data/Rakefile CHANGED
@@ -1,4 +1,4 @@
1
- # $Id: Rakefile 4 2007-05-21 02:04:26Z phw $
1
+ # $Id: Rakefile 34 2007-05-29 15:08:03Z phw $
2
2
  # Copyright (c) 2007, Philipp Wolfer
3
3
  # All rights reserved.
4
4
  # See LICENSE for permissions.
@@ -13,7 +13,7 @@ require 'rake/rdoctask'
13
13
  # Packaging tasks: -------------------------------------------------------
14
14
 
15
15
  PKG_NAME = 'rbrainz'
16
- PKG_VERSION = '0.1.0'
16
+ PKG_VERSION = '0.1.1'
17
17
  PKG_FILES = FileList[
18
18
  "Rakefile", "LICENSE", "README", "TODO",
19
19
  "doc/README.rdoc",
data/TODO CHANGED
@@ -1,24 +1,29 @@
1
1
  =TODO
2
2
 
3
3
  ==Short term tasks
4
- * Support relationships (0.1.1)
5
- * Query collections (0.1.2)
4
+ * Query collections (0.2.0)
6
5
  * Support for the score parameter in search result. Could be implemented
7
6
  a little bit like in PythonMusicBrainz2, but more flexible. Maybe a result
8
- object over which one can iterate to retrieve all results witch the
9
- corresponding score. (0.1.2)
7
+ object over which one can iterate to retrieve all results with the
8
+ corresponding score. (0.2.0)
10
9
  result.each {|entity, score|
11
10
  ...
12
11
  }
13
12
  * Support for the offset parameter for queries on collections
14
13
  to retrieve results below the 100 results limit. Could be easily integrated
15
- into the result object (see previous point). (0.1.2)
14
+ into the result object (see previous point). (0.2.0)
16
15
  * Make it easier to create MBID objects or allow usage of ID strings
17
- where it makes life easier.
16
+ where it makes life easier (>= 0.2.1).
17
+ * Add usefull constructor parameters to the models (>= 0.2.1).
18
+ * Improve API documentation.
18
19
 
19
20
  ==Longer term tasks
20
21
  * Calculation of disc IDs (0.3.0)
21
22
  * User authentication (0.4.0)
22
23
  * Querying of user information (0.4.0)
23
24
  * PUID submission
24
- * Full text search (lucene search queries).
25
+ * Full text search (lucene search queries).
26
+ * Implement a command line tool (+mbquery+) for querying the MusicBrainz
27
+ database which will serve as a usefull example for RBrainz.
28
+ * Support for common extensions (this could be implemented by adding
29
+ additional extension libraries which extend the existing classes).
@@ -1,4 +1,4 @@
1
- # $Id: artist.rb 13 2007-05-22 22:59:57Z phw $
1
+ # $Id: artist.rb 30 2007-05-29 02:12:33Z phw $
2
2
  # Copyright (c) 2007, Philipp Wolfer
3
3
  # All rights reserved.
4
4
  # See LICENSE for permissions.
@@ -18,12 +18,12 @@ module MusicBrainz
18
18
  TYPE_PERSON = NS_MMD_1 + 'Person'
19
19
  TYPE_GROUP = NS_MMD_1 + 'Group'
20
20
 
21
- attr_accessor :name, :sort_name, :disambiguation,
22
- :type, :aliases, :releases
21
+ attr_accessor :name, :sort_name, :disambiguation, :type
23
22
 
24
- attr_reader :begin_date, :end_date
23
+ attr_reader :begin_date, :end_date, :aliases, :releases
25
24
 
26
25
  def initialize
26
+ super
27
27
  @aliases = Array.new
28
28
  @releases = Array.new
29
29
  end
@@ -1,20 +1,29 @@
1
- # $Id: entity.rb 15 2007-05-23 14:31:36Z phw $
1
+ # $Id: entity.rb 30 2007-05-29 02:12:33Z phw $
2
2
  # Copyright (c) 2007, Philipp Wolfer
3
3
  # All rights reserved.
4
4
  # See LICENSE for permissions.
5
5
 
6
6
  require 'rbrainz/model/mbid'
7
+ require 'rbrainz/model/relation'
7
8
 
8
9
  module MusicBrainz
9
10
  module Model
10
11
 
11
12
  # Superclass for all entities.
12
- #
13
- # TODO: implement relations.
14
13
  class Entity
15
14
 
16
15
  attr_reader :id
17
16
 
17
+ def initialize
18
+ @relations = {
19
+ Relation::TO_ARTIST => Array.new,
20
+ Relation::TO_RELEASE => Array.new,
21
+ Relation::TO_TRACK => Array.new,
22
+ Relation::TO_LABEL => Array.new,
23
+ Relation::TO_URL => Array.new,
24
+ }
25
+ end
26
+
18
27
  # Set the MBID.
19
28
  #
20
29
  # +mbid+ should be an instance of +MBID+ or a string
@@ -66,6 +75,44 @@ module MusicBrainz
66
75
  self.class.entity_type
67
76
  end
68
77
 
78
+ # Add a relation to this entity.
79
+ def add_relation(relation)
80
+ @relations[relation.target_type] << relation
81
+ end
82
+
83
+ # Returns the relations of this entity.
84
+ def get_relations(options = {:target_type => nil, :relation_type => nil,
85
+ :required_attributes => [], :direction => nil})
86
+ # Select all results depending on the requested target type
87
+ if options[:target_type]
88
+ result = @relations[options[:target_type]]
89
+ else
90
+ result = []
91
+ @relations.each_value {|array| result += array}
92
+ end
93
+
94
+ # Remove relations which don't meet all the criteria.
95
+ result.delete_if {|relation|
96
+ (options[:relation_type] and relation.type != options[:relation_type]) \
97
+ or (options[:required_attributes] and
98
+ (relation.attributes & options[:required_attributes]).sort \
99
+ != options[:required_attributes].sort) \
100
+ or (options[:direction] and relation.direction != options[:direction])
101
+ }
102
+
103
+ return result
104
+ end
105
+
106
+ # Return all relation target types for which this entity
107
+ # has relations defined.
108
+ def relation_target_types
109
+ result = []
110
+ @relations.each_pair {|type, relations|
111
+ result << type unless relations.empty?
112
+ }
113
+ return result
114
+ end
115
+
69
116
  end
70
117
 
71
118
  end
@@ -1,4 +1,4 @@
1
- # $Id: label.rb 13 2007-05-22 22:59:57Z phw $
1
+ # $Id: label.rb 30 2007-05-29 02:12:33Z phw $
2
2
  # Copyright (c) 2007, Philipp Wolfer
3
3
  # All rights reserved.
4
4
  # See LICENSE for permissions.
@@ -21,11 +21,12 @@ module MusicBrainz
21
21
  TYPE_REISSUE_PRODUCTION = NS_MMD_1 + 'ReissueProduction'
22
22
 
23
23
  attr_accessor :name, :sort_name, :disambiguation,
24
- :code, :country, :type, :releases
24
+ :code, :country, :type
25
25
 
26
- attr_reader :founding_date, :dissolving_date
26
+ attr_reader :founding_date, :dissolving_date, :releases
27
27
 
28
28
  def initialize
29
+ super
29
30
  @releases = Array.new
30
31
  end
31
32
 
@@ -0,0 +1,84 @@
1
+ # $Id: relation.rb 33 2007-05-29 14:48:47Z phw $
2
+ # Copyright (c) 2007, Philipp Wolfer
3
+ # All rights reserved.
4
+ # See LICENSE for permissions.
5
+
6
+ module MusicBrainz
7
+ module Model
8
+
9
+ # Relationship class.
10
+ class Relation
11
+
12
+ DIR_BACKWARD = :backward
13
+ DIR_FORWARD = :forward
14
+ DIR_BOTH = :both
15
+
16
+ TO_ARTIST = NS_REL_1 + 'Artist'
17
+ TO_RELEASE = NS_REL_1 + 'Release'
18
+ TO_TRACK = NS_REL_1 + 'Track'
19
+ TO_LABEL = NS_REL_1 + 'Label'
20
+ TO_URL = NS_REL_1 + 'Url'
21
+
22
+ attr_accessor :type, :direction
23
+
24
+ attr_reader :target, :begin_date, :end_date, :attributes
25
+
26
+ def initialize
27
+ @attributes = Array.new
28
+ @target = nil
29
+ end
30
+
31
+ # Set the target of this relation.
32
+ #
33
+ # The target can either be a object of the type Model::Entity
34
+ # or a URL.
35
+ def target=(target)
36
+ if target.is_a? Entity
37
+ @target = target
38
+ else
39
+ @target = target.to_s
40
+ end
41
+ end
42
+
43
+ # Get the target type.
44
+ def target_type
45
+ if @target.is_a? Model::Entity
46
+ case @target.entity_type
47
+ when :artist
48
+ return TO_ARTIST
49
+ when :release
50
+ return TO_RELEASE
51
+ when :track
52
+ return TO_TRACK
53
+ when :label
54
+ return TO_LABEL
55
+ end
56
+ elsif not @target.nil?
57
+ return TO_URL
58
+ end
59
+ end
60
+
61
+ # Set the begin date of this relation.
62
+ #
63
+ # Should be an IncompleteDate object or
64
+ # a date string, which will get converted
65
+ # into an IncompleteDate.
66
+ def begin_date=(date)
67
+ date = IncompleteDate.new date unless date.is_a? IncompleteDate
68
+ @begin_date = date
69
+ end
70
+
71
+ # Set the end date of this relation.
72
+ #
73
+ # Should be an IncompleteDate object or
74
+ # a date string, which will get converted
75
+ # into an IncompleteDate.
76
+ def end_date=(date)
77
+ date = IncompleteDate.new date unless date.is_a? IncompleteDate
78
+ @end_date = date
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+ end
@@ -1,4 +1,4 @@
1
- # $Id: release.rb 15 2007-05-23 14:31:36Z phw $
1
+ # $Id: release.rb 30 2007-05-29 02:12:33Z phw $
2
2
  # Copyright (c) 2007, Philipp Wolfer
3
3
  # All rights reserved.
4
4
  # See LICENSE for permissions.
@@ -32,12 +32,13 @@ module MusicBrainz
32
32
  TYPE_SOUNDTRACK = NS_MMD_1 + 'Soundtrack'
33
33
  TYPE_SPOKENWORD = NS_MMD_1 + 'Spokenword'
34
34
 
35
- attr_accessor :title, :types, :asin, :artist,
35
+ attr_accessor :title, :asin, :artist,
36
36
  :text_language, :text_script
37
37
 
38
- attr_accessor :tracks, :release_events, :discs
38
+ attr_reader :tracks, :release_events, :discs, :types
39
39
 
40
40
  def initialize
41
+ super
41
42
  @tracks = Array.new
42
43
  @release_events = Array.new
43
44
  @discs = Array.new
@@ -1,4 +1,4 @@
1
- # $Id: track.rb 13 2007-05-22 22:59:57Z phw $
1
+ # $Id: track.rb 30 2007-05-29 02:12:33Z phw $
2
2
  # Copyright (c) 2007, Philipp Wolfer
3
3
  # All rights reserved.
4
4
  # See LICENSE for permissions.
@@ -13,10 +13,12 @@ module MusicBrainz
13
13
  # See http://musicbrainz.org/doc/Track.
14
14
  class Track < Entity
15
15
 
16
- attr_accessor :title, :duration, :artist,
17
- :puids, :releases
16
+ attr_accessor :title, :duration, :artist
17
+
18
+ attr_reader :puids, :releases
18
19
 
19
20
  def initialize
21
+ super
20
22
  @puids = Array.new
21
23
  @releases = Array.new
22
24
  end
@@ -1,6 +1,36 @@
1
- # $Id: webservice.rb 4 2007-05-21 02:04:26Z phw $
1
+ # $Id: webservice.rb 36 2007-05-29 18:43:36Z phw $
2
2
  # Copyright (c) 2007, Philipp Wolfer
3
3
  # All rights reserved.
4
4
  # See LICENSE for permissions.
5
5
 
6
+ module MusicBrainz
7
+ module Webservice
8
+
9
+ # Connecting to the web service failed.
10
+ #
11
+ # This exception is raised if the connection to the server can not be
12
+ # established due to networking problems (e.g. wrong port number or
13
+ # server down).
14
+ class ConnectionError < Exception
15
+ end
16
+
17
+ # An invalid request was made (invalid IDs or parameters).
18
+ class RequestError < Exception
19
+ end
20
+
21
+ # Client requested a resource which requires authentication via HTTP
22
+ # Digest Authentication.
23
+ #
24
+ # If sent even though user name and password were given: user name and/or
25
+ # password are incorrect.
26
+ class AuthenticationError < Exception
27
+ end
28
+
29
+ # The requested resource doesn't exist.
30
+ class ResourceNotFoundError < Exception
31
+ end
32
+
33
+ end
34
+ end
35
+
6
36
  require 'rbrainz/webservice/query'
@@ -1,4 +1,4 @@
1
- # $Id: mbxml.rb 17 2007-05-23 16:51:11Z phw $
1
+ # $Id: mbxml.rb 36 2007-05-29 18:43:36Z phw $
2
2
  # Copyright (c) 2007, Philipp Wolfer
3
3
  # All rights reserved.
4
4
  # See LICENSE for permissions.
@@ -14,6 +14,7 @@ module MusicBrainz
14
14
  # webservice and create the corresponding model classes.
15
15
  # The class understands the MusicBrainz XML Metadata Version 1.0
16
16
  # schema.
17
+ #
17
18
  # See http://musicbrainz.org/doc/MusicBrainzXMLMetaData for more
18
19
  # information on the MusicBrainz XML Metadata schema.
19
20
  class MBXML
@@ -107,7 +108,7 @@ module MusicBrainz
107
108
  return nil
108
109
  end
109
110
 
110
- private
111
+ private # ----------------------------------------------------------------
111
112
 
112
113
  # Iterate over a list of artists.
113
114
  #
@@ -119,8 +120,6 @@ module MusicBrainz
119
120
  end
120
121
 
121
122
  # Create an +Artist+ object from the given artist node.
122
- #
123
- # TODO: relation list
124
123
  def create_artist(node)
125
124
  if node.attributes['id'] and @artists[node.attributes['id']]
126
125
  artist = @artists[node.attributes['id']]
@@ -131,7 +130,9 @@ module MusicBrainz
131
130
 
132
131
  # Read all defined data fields
133
132
  artist.id = node.attributes['id']
134
- artist.type = MBXML.add_metadata_namespace(node.attributes['type']) if node.attributes['type']
133
+ if node.attributes['type']
134
+ artist.type = MBXML.add_namespace(node.attributes['type'], Model::NS_MMD_1)
135
+ end
135
136
 
136
137
  artist.name = node.elements['name'].text if node.elements['name']
137
138
  artist.sort_name = node.elements['sort-name'].text if node.elements['sort-name']
@@ -161,6 +162,15 @@ module MusicBrainz
161
162
  }
162
163
  end
163
164
 
165
+ # Read the relation list
166
+ if node.elements['relation-list']
167
+ node.elements.each('relation-list') {|relation_node|
168
+ read_relation_list(relation_node) {|relation|
169
+ artist.add_relation relation
170
+ }
171
+ }
172
+ end
173
+
164
174
  return artist
165
175
  end
166
176
 
@@ -176,7 +186,6 @@ module MusicBrainz
176
186
  # Create a +Release+ object from the given release node.
177
187
  #
178
188
  # TODO: PUID list
179
- # TODO: relation list
180
189
  def create_release(node)
181
190
  if node.attributes['id'] and @releases[node.attributes['id']]
182
191
  release = @releases[node.attributes['id']]
@@ -193,7 +202,7 @@ module MusicBrainz
193
202
 
194
203
  # Read the types
195
204
  node.attributes['type'].split(' ').each {|type|
196
- release.types << MBXML.add_metadata_namespace(type)
205
+ release.types << MBXML.add_namespace(type, Model::NS_MMD_1)
197
206
  } if node.attributes['type']
198
207
 
199
208
  # Read the text representation information.
@@ -225,6 +234,15 @@ module MusicBrainz
225
234
  }
226
235
  end
227
236
 
237
+ # Read the relation list
238
+ if node.elements['relation-list']
239
+ node.elements.each('relation-list') {|relation_node|
240
+ read_relation_list(relation_node) {|relation|
241
+ release.add_relation relation
242
+ }
243
+ }
244
+ end
245
+
228
246
  return release
229
247
  end
230
248
 
@@ -238,8 +256,6 @@ module MusicBrainz
238
256
  end
239
257
 
240
258
  # Create a +Track+ object from the given track node.
241
- #
242
- # TODO: relation list
243
259
  def create_track(node)
244
260
  if node.attributes['id'] and @tracks[node.attributes['id']]
245
261
  track = @tracks[node.attributes['id']]
@@ -269,6 +285,15 @@ module MusicBrainz
269
285
  }
270
286
  end
271
287
 
288
+ # Read the relation list
289
+ if node.elements['relation-list']
290
+ node.elements.each('relation-list') {|relation_node|
291
+ read_relation_list(relation_node) {|relation|
292
+ track.add_relation relation
293
+ }
294
+ }
295
+ end
296
+
272
297
  return track
273
298
  end
274
299
 
@@ -282,8 +307,6 @@ module MusicBrainz
282
307
  end
283
308
 
284
309
  # Create a +Label+ object from the given label node.
285
- #
286
- # TODO: Relations
287
310
  def create_label(node)
288
311
  if node.attributes['id'] and @labels[node.attributes['id']]
289
312
  label = @labels[node.attributes['id']]
@@ -294,7 +317,9 @@ module MusicBrainz
294
317
 
295
318
  # Read all defined data fields
296
319
  label.id = node.attributes['id']
297
- label.type = MBXML.add_metadata_namespace(node.attributes['type']) if node.attributes['type']
320
+ if node.attributes['type']
321
+ label.type = MBXML.add_namespace(node.attributes['type'], Model::NS_MMD_1)
322
+ end
298
323
 
299
324
  label.name = node.elements['name'].text if node.elements['name']
300
325
  label.sort_name = node.elements['sort-name'].text if node.elements['sort-name']
@@ -318,6 +343,15 @@ module MusicBrainz
318
343
  }
319
344
  end
320
345
 
346
+ # Read the relation list
347
+ if node.elements['relation-list']
348
+ node.elements.each('relation-list') {|relation_node|
349
+ read_relation_list(relation_node) {|relation|
350
+ label.add_relation relation
351
+ }
352
+ }
353
+ end
354
+
321
355
  return label
322
356
  end
323
357
 
@@ -385,16 +419,94 @@ module MusicBrainz
385
419
  }
386
420
  end
387
421
 
422
+ # Iterate over a list of relations.
423
+ #
424
+ # The node must be of the type <em>relation-list</em>.
425
+ def read_relation_list(node)
426
+ node.elements.each('relation') {|child|
427
+ target_type = MBXML.add_namespace(node.attributes['target-type'], Model::NS_REL_1)
428
+ yield create_relation(child, target_type)
429
+ }
430
+ end
431
+
432
+ # Create a +Relation+ object from the given relation node.
433
+ def create_relation(node, target_type)
434
+ relation = Model::Relation.new
435
+
436
+ # Read all defined data fields
437
+ if node.attributes['direction']
438
+ relation.direction = node.attributes['direction'].to_sym
439
+ end
440
+
441
+ if node.attributes['type']
442
+ relation.type = MBXML.add_namespace(node.attributes['type'], Model::NS_REL_1)
443
+ end
444
+
445
+ if node.attributes['begin']
446
+ relation.begin_date = Model::IncompleteDate.new node.attributes['begin']
447
+ end
448
+
449
+ if node.attributes['end']
450
+ relation.begin_end = Model::IncompleteDate.new node.attributes['end']
451
+ end
452
+
453
+ if node.attributes['attributes']
454
+ node.attributes['attributes'].split(' ').each {|attribute|
455
+ relation.attributes << MBXML.add_namespace(attribute, Model::NS_REL_1)
456
+ }
457
+ end
458
+
459
+ # Set the target. Either use the target included in the relation
460
+ # or create a new target according to the target type if no target
461
+ # is present.
462
+ case target_type
463
+ when Model::Relation::TO_ARTIST
464
+ if node.elements['artist']
465
+ target = create_artist node.elements['artist']
466
+ else
467
+ target = Model::Artist.new
468
+ target.id = Model::MBID.from_uuid(:artist, node.attributes['target'])
469
+ end
470
+ when Model::Relation::TO_RELEASE
471
+ if node.elements['release']
472
+ target = create_release node.elements['release']
473
+ else
474
+ target = Model::Release.new
475
+ target.id = Model::MBID.from_uuid(:release, node.attributes['target'])
476
+ end
477
+ when Model::Relation::TO_TRACK
478
+ if node.elements['track']
479
+ target = create_track node.elements['track']
480
+ else
481
+ target = Model::Track.new
482
+ target.id = Model::MBID.from_uuid(:track, node.attributes['target'])
483
+ end
484
+ when Model::Relation::TO_LABEL
485
+ if node.elements['label']
486
+ target = create_label node.elements['label']
487
+ else
488
+ target = Model::Label.new
489
+ target.id = Model::MBID.from_uuid(:label, node.attributes['target'])
490
+ end
491
+ when Model::Relation::TO_URL
492
+ target = node.attributes['target']
493
+ end
494
+
495
+ relation.target = target
496
+
497
+ return relation
498
+ end
499
+
388
500
  # Helper method which will return the given property
389
- # extended by the metadata namespace. If the property
501
+ # extended by the namespace. If the property
390
502
  # already includes the namespace it will be returned
391
503
  # unchanged.
392
- def self.add_metadata_namespace(metadata_property)
393
- regex = Regexp.new("/#{Model::NS_MMD_1}[a-z-]/i")
394
- unless metadata_property =~ regex
395
- return Model::NS_MMD_1 + metadata_property
504
+ def self.add_namespace(property, namespace)
505
+ regex = Regexp.new("/#{namespace}[a-z-]/i")
506
+ unless property =~ regex
507
+ return namespace + property
396
508
  else
397
- return metadata_property
509
+ return property
398
510
  end
399
511
  end
400
512