discogs-wrapper 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +81 -0
- data/Rakefile +27 -0
- data/discogs.gemspec +19 -0
- data/lib/discogs.rb +20 -0
- data/lib/wrapper/resource.rb +79 -0
- data/lib/wrapper/resource_mappings.rb +84 -0
- data/lib/wrapper/resources/artist.rb +37 -0
- data/lib/wrapper/resources/artist_release.rb +17 -0
- data/lib/wrapper/resources/format.rb +11 -0
- data/lib/wrapper/resources/generic_list.rb +29 -0
- data/lib/wrapper/resources/image.rb +11 -0
- data/lib/wrapper/resources/label.rb +16 -0
- data/lib/wrapper/resources/label_release.rb +17 -0
- data/lib/wrapper/resources/release.rb +22 -0
- data/lib/wrapper/resources/release_artist.rb +18 -0
- data/lib/wrapper/resources/release_label.rb +10 -0
- data/lib/wrapper/resources/search.rb +61 -0
- data/lib/wrapper/resources/search_result.rb +16 -0
- data/lib/wrapper/resources/track.rb +15 -0
- data/lib/wrapper/wrapper.rb +105 -0
- data/spec/resource_spec.rb +27 -0
- data/spec/resources/artist_release_spec.rb +59 -0
- data/spec/resources/artist_spec.rb +15 -0
- data/spec/resources/format_spec.rb +41 -0
- data/spec/resources/generic_list_spec.rb +66 -0
- data/spec/resources/image_spec.rb +43 -0
- data/spec/resources/label_release_spec.rb +55 -0
- data/spec/resources/label_spec.rb +15 -0
- data/spec/resources/release_artist_spec.rb +43 -0
- data/spec/resources/release_label_spec.rb +31 -0
- data/spec/resources/release_spec.rb +15 -0
- data/spec/resources/search_result_spec.rb +47 -0
- data/spec/resources/search_spec.rb +15 -0
- data/spec/resources/track_spec.rb +56 -0
- data/spec/spec_helper.rb +36 -0
- data/spec/wrapper_methods/get_artist_spec.rb +109 -0
- data/spec/wrapper_methods/get_label_spec.rb +89 -0
- data/spec/wrapper_methods/get_release_spec.rb +123 -0
- data/spec/wrapper_methods/search_spec.rb +330 -0
- data/spec/wrapper_spec.rb +162 -0
- metadata +126 -0
data/README.markdown
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
Discogs::Wrapper
|
2
|
+
================
|
3
|
+
|
4
|
+
ABOUT
|
5
|
+
-----
|
6
|
+
A 100% Ruby wrapper of the Discogs.com API. No dependencies, no extra gems. :)
|
7
|
+
|
8
|
+
Discogs::Wrapper abstracts all the nasty boilerplate code needed to interact with the Discogs API. It gives you direct access to the information you need.
|
9
|
+
|
10
|
+
The master branch aims to give full support for version 2.0 of the API. If you need support for everything in version 1.0, see the api-v1 branch.
|
11
|
+
|
12
|
+
Specifically:
|
13
|
+
|
14
|
+
* Artists
|
15
|
+
* Releases
|
16
|
+
* Labels
|
17
|
+
* Searching (all of the above)
|
18
|
+
|
19
|
+
Please, [see the Wiki](http://github.com/buntine/discogs/wiki) for helpful documentation.
|
20
|
+
|
21
|
+
The Discogs API is [documented here](http://www.discogs.com/help/api).
|
22
|
+
|
23
|
+
INSTALLATION
|
24
|
+
------------
|
25
|
+
You can install the library via Rubygems:
|
26
|
+
|
27
|
+
$ gem sources -a http://gems.github.com
|
28
|
+
$ sudo gem install buntine-discogs
|
29
|
+
|
30
|
+
USAGE
|
31
|
+
-----
|
32
|
+
To use this library, you must supply a valid User-agent value. For example:
|
33
|
+
|
34
|
+
require 'discogs'
|
35
|
+
wrapper = Discogs::Wrapper.new("Mozilla/5.0 (X11; Linux i686; rv:6.0.2) Gecko/20100101 Firefox/6.0.2")
|
36
|
+
|
37
|
+
I suggest passing on your users User-agent.
|
38
|
+
|
39
|
+
Accessing information is easy:
|
40
|
+
|
41
|
+
artist = wrapper.get_artist("Master's Hammer")
|
42
|
+
release = wrapper.get_release("611973") # Supply an ID.
|
43
|
+
label = wrapper.get_label("Monitor Records")
|
44
|
+
search_results = wrapper.search("Necrovore")
|
45
|
+
|
46
|
+
artist.name # => "Master's Hammer"
|
47
|
+
artist.releases[0].title # => "Finished"
|
48
|
+
artist.releases[1].year # => "1989"
|
49
|
+
artist.releases[4].extraartists # => [ "Arakain", "Debustrol" ]
|
50
|
+
|
51
|
+
release.title # => "Ritual"
|
52
|
+
release.labels[0].name # => "Osmose Productions"
|
53
|
+
release.formats[0].descriptions[0] # => "LP"
|
54
|
+
release.styles # => [ "Black Metal", "Death Metal" ]
|
55
|
+
release.tracklist[1].title # => "Pad modly"
|
56
|
+
|
57
|
+
label.images[0].width # => "220"
|
58
|
+
label.releases.length # => 22
|
59
|
+
label.releases[3].artist # => "Root"
|
60
|
+
label.releases[7].catno # => "MON007"
|
61
|
+
|
62
|
+
search.total_results # => 124
|
63
|
+
search.total_pages # => 7
|
64
|
+
search.current_page # => 1
|
65
|
+
|
66
|
+
# Exact results
|
67
|
+
search.exact[0].type # => "artist"
|
68
|
+
search.exact[0].title # => "Necrovore"
|
69
|
+
search.exact(:label)[0].title # => "Necrovores Records"
|
70
|
+
search.closest(:artist) # => <Discogs::Search::Result:0x324ad3e2>
|
71
|
+
|
72
|
+
# All results
|
73
|
+
search.results[3].title # => "Necrovore - Demo '87"
|
74
|
+
search.results[3].summary # => "First and only demo tape"
|
75
|
+
search.results(:release)[0] # => <Discogs::Search::Result:0x343de34a>
|
76
|
+
|
77
|
+
|
78
|
+
LICENSE
|
79
|
+
-------
|
80
|
+
|
81
|
+
<a rel="license" href="http://creativecommons.org/licenses/by/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by/3.0/80x15.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">Discogs::Wrapper</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="https://github.com/buntine/discogs" property="cc:attributionName" rel="cc:attributionURL">Andrew Buntine</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>.<br />Based on a work at <a xmlns:dct="http://purl.org/dc/terms/" href="http://www.discogs.com/help/api" rel="dct:source">www.discogs.com</a>.
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'spec'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
|
6
|
+
task :default => :spec
|
7
|
+
|
8
|
+
desc "Run all specs in spec directory"
|
9
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
10
|
+
t.spec_opts = ["--colour", "--format progress", "--loadby mtime", "--reverse"]
|
11
|
+
t.spec_files = FileList[ 'spec/**/*_spec.rb' ]
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Run all specs and generate RCov report"
|
15
|
+
Spec::Rake::SpecTask.new('cov') do |t|
|
16
|
+
t.spec_files = FileList['spec/**/*.rb']
|
17
|
+
t.spec_opts = ["--colour"]
|
18
|
+
t.rcov = true
|
19
|
+
t.rcov_opts = ['-T --no-html --exclude', 'spec\/,gems\/']
|
20
|
+
end
|
21
|
+
|
22
|
+
spec = eval(File.read("discogs.gemspec"))
|
23
|
+
|
24
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
25
|
+
# pkg.need_zip = true
|
26
|
+
# pkg.need_tar = true
|
27
|
+
end
|
data/discogs.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
|
3
|
+
s.name = "discogs-wrapper"
|
4
|
+
s.version = "1.0.0"
|
5
|
+
s.date = "2011-09-27"
|
6
|
+
s.summary = "Discogs::Wrapper is a full wrapper for the http://www.discogs.com API V2"
|
7
|
+
s.homepage = "http://www.github.com/buntine/discogs"
|
8
|
+
s.email = "info@andrewbuntine.com"
|
9
|
+
s.authors = ["Andrew Buntine", "Ed Hickey"]
|
10
|
+
|
11
|
+
s.description = "Discogs::Wrapper is a full wrapper for the http://www.discogs.com API V2"
|
12
|
+
|
13
|
+
s.files = ["lib/wrapper", "lib/wrapper/resource_mappings.rb", "lib/wrapper/wrapper.rb", "lib/wrapper/resource.rb", "lib/wrapper/resources", "lib/wrapper/resources/format.rb", "lib/wrapper/resources/search.rb", "lib/wrapper/resources/label_release.rb", "lib/wrapper/resources/artist.rb", "lib/wrapper/resources/release_artist.rb", "lib/wrapper/resources/artist_release.rb", "lib/wrapper/resources/release_label.rb", "lib/wrapper/resources/generic_list.rb", "lib/wrapper/resources/search_result.rb", "lib/wrapper/resources/label.rb", "lib/wrapper/resources/image.rb", "lib/wrapper/resources/track.rb", "lib/wrapper/resources/release.rb", "lib/discogs.rb", "Rakefile", "README.markdown", "discogs.gemspec"]
|
14
|
+
|
15
|
+
s.test_files = ["spec/wrapper_methods/get_release_spec.rb", "spec/wrapper_methods/search_spec.rb", "spec/wrapper_methods/get_artist_spec.rb", "spec/wrapper_methods/get_label_spec.rb", "spec/resource_spec.rb", "spec/resources/label_spec.rb", "spec/resources/generic_list_spec.rb", "spec/resources/artist_release_spec.rb", "spec/resources/artist_spec.rb", "spec/resources/release_artist_spec.rb", "spec/resources/search_spec.rb", "spec/resources/label_release_spec.rb", "spec/resources/release_label_spec.rb", "spec/resources/release_spec.rb", "spec/resources/search_result_spec.rb", "spec/resources/image_spec.rb", "spec/resources/track_spec.rb", "spec/resources/format_spec.rb", "spec/wrapper_spec.rb", "spec/spec_helper.rb"]
|
16
|
+
|
17
|
+
s.platform = Gem::Platform::RUBY
|
18
|
+
|
19
|
+
end
|
data/lib/discogs.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
## Discogs::Wrapper
|
3
|
+
## by Andrew Buntine, 2009
|
4
|
+
##
|
5
|
+
## This library provides full access to the Discogs.com API v1.0
|
6
|
+
##
|
7
|
+
## Please file all bug reports at http://www.github.com/buntine/discogs, or
|
8
|
+
## email me at info@andrewbuntine.com.
|
9
|
+
##
|
10
|
+
## Enjoy!
|
11
|
+
|
12
|
+
# Application namespace.
|
13
|
+
module Discogs; end
|
14
|
+
|
15
|
+
# Custom exceptions.
|
16
|
+
class Discogs::UnknownResource < Exception; end
|
17
|
+
class Discogs::InternalServerError < Exception; end
|
18
|
+
|
19
|
+
# Loading sequence.
|
20
|
+
require File.dirname(__FILE__) + "/wrapper/wrapper"
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# Represents a generic resource in the Discogs API.
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + "/resource_mappings"
|
4
|
+
|
5
|
+
class Discogs::Resource
|
6
|
+
|
7
|
+
include Discogs::ResourceMappings
|
8
|
+
|
9
|
+
def initialize(content)
|
10
|
+
@content = content
|
11
|
+
end
|
12
|
+
|
13
|
+
def original_content
|
14
|
+
@content
|
15
|
+
end
|
16
|
+
|
17
|
+
# Builds the resource with it's content.
|
18
|
+
def build!(remove_resp=true)
|
19
|
+
document = REXML::Document.new(@content, :ignore_whitespace_nodes => :all)
|
20
|
+
root_node = document.root
|
21
|
+
|
22
|
+
# Ignore the <resp> element if necessary.
|
23
|
+
if remove_resp and document.root.expanded_name == "resp"
|
24
|
+
root_node = root_node[0]
|
25
|
+
end
|
26
|
+
|
27
|
+
set_accessors_from_attributes(root_node)
|
28
|
+
|
29
|
+
# Traverse node children.
|
30
|
+
root_node.each_element do |element|
|
31
|
+
name = element.expanded_name.to_sym
|
32
|
+
setter = (name.to_s + "=").to_sym
|
33
|
+
|
34
|
+
singular = find_resource_for_name(name, :singular)
|
35
|
+
plural = singular ? nil : find_resource_for_name(name, :plural)
|
36
|
+
|
37
|
+
# Create an instance of the named resource and build it.
|
38
|
+
if !singular.nil?
|
39
|
+
nested_object = singular.send(:new, element.to_s)
|
40
|
+
self.send(setter, nested_object.build!)
|
41
|
+
|
42
|
+
# Setup an array and build each child.
|
43
|
+
elsif !plural.nil?
|
44
|
+
set_accessors_from_attributes(element)
|
45
|
+
|
46
|
+
self.send(setter, [])
|
47
|
+
element.each_element do |sub_element|
|
48
|
+
nested_object = plural.send(:new, sub_element.to_s)
|
49
|
+
self.send(name) << nested_object.build!
|
50
|
+
end
|
51
|
+
|
52
|
+
elsif self.respond_to? setter
|
53
|
+
self.send(setter, element.text)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def build_with_resp!
|
61
|
+
build!(false)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
# Sets accessors on _self_ from the attributes of the given element.
|
67
|
+
def set_accessors_from_attributes(element)
|
68
|
+
element.attributes.each_attribute do |attribute|
|
69
|
+
setter = (attribute.expanded_name + "=").to_sym
|
70
|
+
|
71
|
+
if self.respond_to? setter
|
72
|
+
self.send(setter, attribute.value)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
Dir[File.join(File.dirname(__FILE__), "resources", "*.rb")].each { |file| require file }
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Abstracts the resource-mapping class methods.
|
2
|
+
#
|
3
|
+
# Each "resource" in the wrapper maps to one or more elements
|
4
|
+
# in the API response. This way they can be recursively built
|
5
|
+
# without having to manually specify it in each class.
|
6
|
+
|
7
|
+
module Discogs::ResourceMappings
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.extend ClassMethods
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
|
15
|
+
# Helper method to map resource to element in API response.
|
16
|
+
def map_to(*elements)
|
17
|
+
self.class_eval <<-EOF
|
18
|
+
def self.element_names
|
19
|
+
#{elements.inspect}
|
20
|
+
end
|
21
|
+
EOF
|
22
|
+
end
|
23
|
+
|
24
|
+
# Helper method to map pluralised resource to element in API response.
|
25
|
+
def map_to_plural(*elements)
|
26
|
+
self.class_eval <<-EOF
|
27
|
+
def self.plural_element_names
|
28
|
+
#{elements.inspect}
|
29
|
+
end
|
30
|
+
EOF
|
31
|
+
end
|
32
|
+
|
33
|
+
# Used by root classes (Discogs::Artist, etc) that should be built internally.
|
34
|
+
def no_mapping
|
35
|
+
self.class_eval <<-EOF
|
36
|
+
def self.element_names; []; end
|
37
|
+
def self.plural_element_names; []; end
|
38
|
+
EOF
|
39
|
+
end
|
40
|
+
|
41
|
+
# Element defaults to prevent excess boilerplate code.
|
42
|
+
def element_names
|
43
|
+
[ self.to_s.split("::")[-1].downcase.to_sym ]
|
44
|
+
end
|
45
|
+
def plural_element_names
|
46
|
+
[ (self.element_names[0].to_s + "s").to_sym ]
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def find_resource_for_name(name, type=:singular)
|
54
|
+
method = if type == :singular
|
55
|
+
:element_names
|
56
|
+
else
|
57
|
+
:plural_element_names
|
58
|
+
end
|
59
|
+
|
60
|
+
find_resource do |klass|
|
61
|
+
klass.constants.find do |const|
|
62
|
+
if klass.const_get(const).respond_to? method
|
63
|
+
klass.const_get(const).send(method).any? { |element| element.eql?(name) }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Look in _namespace_ for a matching resource class.
|
70
|
+
# First looks in the children of _self_ namespace, and then looks more
|
71
|
+
# generally. Returns nil if nothing is found.
|
72
|
+
def find_resource(namespace=self.class, &matcher)
|
73
|
+
match = matcher.call(namespace)
|
74
|
+
|
75
|
+
if match
|
76
|
+
match = namespace.const_get(match)
|
77
|
+
elsif namespace == self.class
|
78
|
+
match = find_resource(Discogs, &matcher)
|
79
|
+
end
|
80
|
+
|
81
|
+
return match
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Represents an artist in the Discogs API.
|
2
|
+
|
3
|
+
class Discogs::Artist < Discogs::Resource
|
4
|
+
|
5
|
+
no_mapping
|
6
|
+
|
7
|
+
attr_accessor :name,
|
8
|
+
:realname,
|
9
|
+
:images,
|
10
|
+
:urls,
|
11
|
+
:namevariations,
|
12
|
+
:aliases,
|
13
|
+
:members,
|
14
|
+
:releases
|
15
|
+
|
16
|
+
def main_releases
|
17
|
+
filter_releases("Main")
|
18
|
+
end
|
19
|
+
|
20
|
+
def bootlegs
|
21
|
+
filter_releases("UnofficialRelease")
|
22
|
+
end
|
23
|
+
|
24
|
+
def appearances
|
25
|
+
filter_releases("Appearance")
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# Simple helper for filtering a particular type of release.
|
31
|
+
def filter_releases(type)
|
32
|
+
self.releases.find_all do |release|
|
33
|
+
release.type == type
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Represents an artist's release in the Discogs API.
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + "/artist"
|
4
|
+
|
5
|
+
class Discogs::Artist::Release < Discogs::Resource
|
6
|
+
|
7
|
+
attr_accessor :id,
|
8
|
+
:status,
|
9
|
+
:type,
|
10
|
+
:title,
|
11
|
+
:artist,
|
12
|
+
:format,
|
13
|
+
:year,
|
14
|
+
:label,
|
15
|
+
:trackinfo
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Represents a generic list of items in the Discogs API.
|
2
|
+
|
3
|
+
class Discogs::GenericList < Discogs::Resource
|
4
|
+
|
5
|
+
map_to :descriptions, :genres, :aliases, :namevariations, :styles, :urls, :members, :sublabels
|
6
|
+
|
7
|
+
map_to_plural :lists
|
8
|
+
|
9
|
+
# Overload build method to provide custom process for
|
10
|
+
# converting contents into something useful.
|
11
|
+
def build!
|
12
|
+
@items = []
|
13
|
+
document = REXML::Document.new(@content)
|
14
|
+
|
15
|
+
document.root.each_element do |element|
|
16
|
+
@items << element.text
|
17
|
+
end
|
18
|
+
|
19
|
+
@items
|
20
|
+
end
|
21
|
+
|
22
|
+
# Provides post-build access to the list.
|
23
|
+
def [](index)
|
24
|
+
if @items.is_a?(Array)
|
25
|
+
@items[index]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|