spotifiery 0.0.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/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ spotifiery.sublime-project
19
+ spotifiery.sublime-workspace
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in spotifiery.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 cicloon
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # Spotifiery
2
+
3
+ Spotifiery is a simple wrapper for the Spotify web API.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'spotifiery'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install spotifiery
18
+
19
+ ## Usage
20
+
21
+ Using it is very simple:
22
+
23
+ There are three "searchable items":
24
+
25
+ - Track
26
+ - Album
27
+ - Artist
28
+
29
+ To search for a track you can do:
30
+
31
+ Spotifiery::Searchable::Track.find(:q => 'Stairway to Heaven')
32
+
33
+ Which will return a SearchResult containing tracks called "Stairway to Heaven".
34
+
35
+ It accepts the same params as the spotify API so if you want to look for the page 2 of search results you should do:
36
+
37
+ Spotifiery::Searchable::Track.find(:q => 'Stairway to Heaven', :page => 2)
38
+
39
+ A find will return a SearchResult object which contains metadata about the query performed like "num_results", "limit", "offset", "page"... and of course the result set, which can be accessed via the results method (if you've searched for tracks you can use the tracks method too, the same applies to albums or artists).
40
+
41
+ A Searchable contains all the metadata about itself and allows navigation through the API, p.e.: you can ask a track about its artits or about its album.
42
+
43
+
44
+ ## Contributing
45
+
46
+ 1. Fork it
47
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
48
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
49
+ 4. Push to the branch (`git push origin my-new-feature`)
50
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,44 @@
1
+
2
+ require 'active_support/core_ext/hash/indifferent_access'
3
+
4
+
5
+ module Spotifiery
6
+
7
+ module SearchResult
8
+
9
+ class Base
10
+
11
+ def initialize response
12
+
13
+ response_hash = HashWithIndifferentAccess.new response
14
+
15
+ @info = response_hash[:info]
16
+ response_results = response_hash[ ActiveSupport::Inflector.pluralize(@info[:type]) ]
17
+ response_class = ActiveSupport::Inflector.constantize( "Spotifiery::Searchable::" + ActiveSupport::Inflector.titleize(@info[:type]))
18
+ @results = response_results.map{|result_hash| response_class.new(result_hash) }
19
+ end
20
+
21
+ def results
22
+ @results
23
+ end
24
+
25
+
26
+ def method_missing(method, *args, &block)
27
+ if defined?(@info) && @info.has_key?(method)
28
+ @info[method]
29
+ elsif defined?(@info) && method.eql?( ActiveSupport::Inflector.pluralize(@info[:type]).to_sym )
30
+ @results
31
+ else
32
+ super
33
+ end
34
+ end
35
+
36
+ def respond_to_missing?(method, include_private = false)
37
+ @info.has_key?(method) || method.eql?(ActiveSupport::Inflector.pluralize(@info[:type]).to_sym)
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,12 @@
1
+ module Spotifiery
2
+
3
+ module SearchResult
4
+ extend ActiveSupport::Autoload
5
+
6
+ autoload :Base
7
+
8
+
9
+ end
10
+
11
+
12
+ end
@@ -0,0 +1,40 @@
1
+ module Spotifiery
2
+
3
+ module Searchable
4
+
5
+ class Album
6
+ include Base
7
+ LOOKUPS = ['artist' , 'track']
8
+ QUERY_EXTRA_DEFAULT = 'trackdetail'
9
+
10
+ private
11
+
12
+ # Spotify messed it up with albums and artists.
13
+ # An Album lookup returns artist as artist-id: "spotify:artist:7jy3rLJdDQY21OgRLCZ9sD" and artist: "Foo Fighters"
14
+ # An Album search returns artists as [{href: "spotify:artist:7jy3rLJdDQY21OgRLCZ9sD",name: "Foo Fighters"}]
15
+ # This is fixed here
16
+ def lookup_in_spotify
17
+ perform_lookup
18
+ parsed_response = @lookup_response.parsed_response
19
+ searchable_type = self.class.name.demodulize.downcase
20
+
21
+ # Build the artist hash as it is on search results
22
+ artist_hash = HashWithIndifferentAccess.new({ :name => parsed_response[searchable_type]['artist'], :href => parsed_response[searchable_type]['artist-id']})
23
+
24
+ # Remove artist references from lookup response
25
+ parsed_response[searchable_type].delete(:artist)
26
+ parsed_response[searchable_type].delete('artist-id')
27
+
28
+ # Set artists as it is on search results
29
+ parsed_response[searchable_type]['artists'] = [artist_hash]
30
+
31
+ # Initialize attributes as always
32
+ set_base_attributes_by_lookup parsed_response
33
+ end
34
+
35
+
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,13 @@
1
+ module Spotifiery
2
+
3
+ module Searchable
4
+
5
+ class Artist
6
+ include Base
7
+ LOOKUPS = ['album']
8
+ QUERY_EXTRA_DEFAULT = 'albumdetail'
9
+ end
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,158 @@
1
+ require 'active_support/core_ext/object'
2
+ require 'active_support/core_ext/string'
3
+
4
+ module Spotifiery
5
+
6
+ module Searchable
7
+
8
+ module Base
9
+
10
+ extend ActiveSupport::Concern
11
+ BASE_SEARCH_URL = "http://ws.spotify.com/search/1/"
12
+ BASE_LOOKUP_URL = "http://ws.spotify.com/lookup/1/.json"
13
+
14
+
15
+ included do
16
+ include HTTParty
17
+ base_uri BASE_SEARCH_URL
18
+ format :json
19
+ end
20
+
21
+ module ClassMethods
22
+
23
+ # Search for something (track, album or artist).
24
+ # Accept the same params as spotify search API
25
+ def find opts = {}
26
+ parsed_response = ask_spotify opts
27
+ Spotifiery::SearchResult::Base.new parsed_response
28
+ end
29
+
30
+ private
31
+
32
+ def ask_spotify opts = {}
33
+ opts = {:q => '', :page => '1'}.merge opts
34
+ self.get("/#{name.demodulize.downcase}.json" , :query => opts.to_param ).parsed_response
35
+ end
36
+ end
37
+
38
+
39
+ # Can be initialized from a spotify uri or from an search result hash.
40
+ # This way Spotify is not asked when initialized from a search result when name, popularity, etc are called
41
+ def initialize spotify_uri_or_hash
42
+
43
+ if spotify_uri_or_hash.is_a? Hash
44
+ initialize_base_attributes_getters HashWithIndifferentAccess.new spotify_uri_or_hash
45
+ @href = spotify_uri_or_hash['href']
46
+
47
+ elsif spotify_uri_or_hash.is_a? String
48
+
49
+ @href = spotify_uri_or_hash
50
+
51
+ else
52
+ raise "Wrong initialize params"
53
+ end
54
+ end
55
+
56
+
57
+ private
58
+
59
+ def method_missing(method, *args, &block)
60
+
61
+ # Looked in spotify before?
62
+ if !defined?(@lookup_response)
63
+ lookup_in_spotify
64
+ end
65
+
66
+ # Try to find the method in base_attributes
67
+ if @base_attributes.has_key? method
68
+ # Always try to parse for integers
69
+ Integer(@base_attributes[method],10) rescue @base_attributes[method]
70
+ # If not try to see if an instance variable is defined
71
+ elsif instance_variable_defined?("@#{method}")
72
+ instance_variable_get("@#{method}")
73
+ else
74
+ super
75
+ end
76
+
77
+ end
78
+
79
+ def respond_to_missing?(method, include_private = false)
80
+ if !defined? @lookup_response
81
+ lookup_in_spotify
82
+ end
83
+ @base_attributes.has_key?(method) || instance_variable_defined?("@#{method}")
84
+ end
85
+
86
+ # Perform a lookup, taking all the metadata from Spotify.
87
+ def lookup_in_spotify
88
+ perform_lookup
89
+ parsed_response = HashWithIndifferentAccess.new @lookup_response.parsed_response
90
+ set_base_attributes_by_lookup parsed_response
91
+ end
92
+
93
+ def perform_lookup
94
+ @lookup_response = HTTParty.get( Spotifiery::Searchable::Base::BASE_LOOKUP_URL, :query => {:uri => @href, :extras => self.class::QUERY_EXTRA_DEFAULT } )
95
+ end
96
+
97
+ # Define base attribute methods from an Spotify lookup
98
+ def set_base_attributes_by_lookup attributes_hash
99
+ searchable_type = self.class.name.demodulize.downcase
100
+ initialize_base_attributes_getters attributes_hash[ searchable_type ]
101
+ end
102
+
103
+ # Define methods from a hash
104
+ def initialize_base_attributes_getters attributes_hash
105
+
106
+ @base_attributes ||= {}
107
+
108
+ attributes_hash.each do |key, value|
109
+
110
+ attribute = key.underscore.to_sym
111
+ if !self.class::LOOKUPS.include? attribute.to_s.singularize
112
+
113
+ @base_attributes[ attribute ] = value
114
+ define_base_method attribute if !defined? attribute
115
+
116
+ else
117
+ initialize_lookup attribute, value
118
+ end
119
+ end
120
+ end
121
+
122
+ # Define methods that require new searchables
123
+ def initialize_lookup attribute, value
124
+ singular_attribute = attribute.to_s.singularize
125
+ searchable_class = ("Spotifiery::Searchable::" + singular_attribute.titleize ).constantize
126
+
127
+ searchable_instance = if value.is_a? Array
128
+ value.map {|val| searchable_class.new val }
129
+ else
130
+ searchable_class.new value
131
+ end
132
+
133
+ instance_variable_set("@#{attribute}", searchable_instance)
134
+ define_searchable_method attribute
135
+ end
136
+
137
+ def define_base_method method
138
+ send(:define_singleton_method, method) do
139
+ # Always try to parse for integers
140
+ Integer(@base_attributes[attribute],10) rescue @base_attributes[attribute]
141
+ end
142
+ end
143
+
144
+ def define_searchable_method method
145
+ send(:define_singleton_method, method) do
146
+ instance_variable_get("@#{method}")
147
+ end
148
+ end
149
+
150
+
151
+ end
152
+
153
+
154
+
155
+ end
156
+
157
+
158
+ end
@@ -0,0 +1,13 @@
1
+ module Spotifiery
2
+
3
+ module Searchable
4
+
5
+ class Track
6
+ include Base
7
+ LOOKUPS = ['artist' , 'album']
8
+ QUERY_EXTRA_DEFAULT = nil
9
+ end
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,14 @@
1
+ module Spotifiery
2
+
3
+ module Searchable
4
+ extend ActiveSupport::Autoload
5
+
6
+ autoload :Base
7
+ autoload :Track
8
+ autoload :Album
9
+ autoload :Artist
10
+
11
+ end
12
+
13
+
14
+ end
@@ -0,0 +1,3 @@
1
+ module Spotifiery
2
+ VERSION = "0.0.1"
3
+ end
data/lib/spotifiery.rb ADDED
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+
3
+ require 'httparty'
4
+ require 'active_support'
5
+
6
+ module Spotifiery
7
+ extend ActiveSupport::Autoload
8
+
9
+ autoload :Searchable
10
+ autoload :SearchResult
11
+
12
+
13
+ end