rbrainz 0.1.0 → 0.1.1

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