officialfm 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -17,10 +17,5 @@ tmtags
17
17
  coverage
18
18
  rdoc
19
19
  pkg
20
- *.gem
21
- .yardopts
22
- .yardoc
23
- .bundle
24
- Gemfile.lock
25
20
 
26
21
  ## PROJECT::SPECIFIC
data/Gemfile CHANGED
@@ -1,3 +1,9 @@
1
1
  source :rubygems
2
2
 
3
3
  gemspec
4
+
5
+ group :test do
6
+ gem 'simplecov', '~> 0.6.0', :require => false
7
+ gem 'simplecov-gem-adapter', '~> 1.0.1', :require => false
8
+ end
9
+
data/Gemfile.lock ADDED
@@ -0,0 +1,41 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ officialfm (0.2.0)
5
+ faraday (~> 0.8.1)
6
+ faraday_middleware (~> 0.8.8)
7
+ hashie (~> 1.2)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ faraday (0.8.1)
13
+ multipart-post (~> 1.1)
14
+ faraday_middleware (0.8.8)
15
+ faraday (>= 0.7.4, < 0.9)
16
+ hashie (1.2.0)
17
+ jnunemaker-matchy (0.4.0)
18
+ multi_json (1.3.6)
19
+ multipart-post (1.1.5)
20
+ rake (0.9.2.2)
21
+ shoulda (2.11.3)
22
+ simplecov (0.6.0)
23
+ multi_json (~> 1.0)
24
+ simplecov-html (~> 0.5.3)
25
+ simplecov-gem-adapter (1.0.1)
26
+ simplecov
27
+ simplecov-html (0.5.3)
28
+ test-unit (2.5.1)
29
+
30
+ PLATFORMS
31
+ ruby
32
+
33
+ DEPENDENCIES
34
+ bundler (~> 1.0)
35
+ jnunemaker-matchy (~> 0.4)
36
+ officialfm!
37
+ rake (~> 0.8)
38
+ shoulda (~> 2.11)
39
+ simplecov (~> 0.6.0)
40
+ simplecov-gem-adapter (~> 1.0.1)
41
+ test-unit (~> 2.1)
data/README.md CHANGED
@@ -1,69 +1,201 @@
1
1
  # official.fm
2
2
 
3
- Ruby wrapper for the [official.fm Simple API](http://official.fm/developers).
3
+ Ruby wrapper for the [official.fm v2 API](http://official.fm/developers).
4
4
 
5
5
  ## Installation
6
6
 
7
- sudo gem install officialfm
7
+ gem install officialfm
8
+
9
+ Alternatively put this in you `Gemfile`, then run `bundle install`:
10
+
11
+ ```ruby
12
+ gem 'officialfm'
13
+ ```
8
14
 
9
15
  ## Get your API key
10
16
 
11
- Be sure and get your API key: [http://official.fm/developers/manage](http://official.fm/developers/manage)
17
+ Coming soon.
12
18
 
13
- ## Usage
19
+ You can also access the API without an API key, but with a lower rate limit.
14
20
 
15
- ### Include the relevant files
21
+ ## Client instantiation and configuration
16
22
 
17
- require 'rubygems'
18
- require 'officialfm'
23
+ Include the relevant files
19
24
 
20
- ### Instantiate a client
21
-
22
- officialfm = OfficialFM::Client.new(:api_key => 'your_api_key')
25
+ ```ruby
26
+ require 'officialfm'
27
+ ```
23
28
 
24
- #### Examples
29
+ Instantiate a client
25
30
 
26
- user = officialfm.user('chab')
27
- puts user.name
31
+ ```ruby
32
+ officialfm = OfficialFM::Client.new
33
+ ```
28
34
 
29
- officialfm.tracks('Dare', {:limit => 10, :embed => false}).each |track| do
30
- puts "#{track.name} by #{track.artist_string}"
31
- end
32
-
33
- For a complete example of web-app using Sinatra in conjunction with
34
- this gem, see [ofmtweet](https://github.com/nddrylliog/ofmtweet).
35
+ With an API key:
35
36
 
36
- ## Additions to the original API
37
+ ```ruby
38
+ officialfm = OfficialFM::Client.new(api_key: 'your_api_key')
39
+ ```
37
40
 
38
- ### playlist.running\_time
41
+ You can also set a default configuration for all clients to use
39
42
 
40
- The original API has a `length` attribute in playlists, but unfortunately
41
- we can't use it from the Ruby side with Hashie, because it's already the number
42
- of fields of the hash (as I understand it).
43
+ ```ruby
44
+ OfficialFM.configure do |c|
45
+ c.api_key = YOUR_API_KEY
46
+ end
47
+ ```
43
48
 
44
- It can still be accessed with `playlist["length"]` but since it's not pretty,
45
- the gem allows you to access it like this:
49
+ ## Response format
46
50
 
47
- puts "#{playlist.running_time}s of pure bliss"
51
+ All methods return a
52
+ [Hashie:Mash](http://rdoc.info/github/intridea/hashie/Hashie/Mash). That means
53
+ you can access the response fields via method-like accessors. For example:
48
54
 
49
- ## Note on Patches/Pull Requests
55
+ ```ruby
56
+ search_results = officialfm.tracks('Wiz Khalifa')
57
+ track = search_results[3].track
58
+ puts "#{track.title} by #{track.artist}"
59
+ ```
50
60
 
51
- * Fork the project.
52
- * Make your feature addition or bug fix.
53
- * Add tests for it. This is important so I don't break it in a
54
- future version unintentionally.
55
- * Commit, do not mess with rakefile, version, or history.
56
- (if you want to have your own version, that is fine but
57
- bump version in a commit by itself I can ignore when I pull)
58
- * Send me a pull request. Bonus points for topic branches.
61
+ ## API response enhancements
59
62
 
60
- ## Copyright
63
+ The API wraps responses in a root element, e.g.:
64
+
65
+ ```json
66
+ {
67
+ "track": {
68
+ "title": "Some track"
69
+ ...
70
+ }
71
+ }
72
+ ```
73
+
74
+ The responses given by methods in the gem don't have a root and expose the
75
+ resource's properites directly instead (e.g. `officialfm.track('xxxx').title`).
76
+
77
+ Search results are also unwrapped. For example, the raw response of a track
78
+ search looks like:
79
+
80
+ ```json
81
+ {
82
+ "page": 1,
83
+ "total_entries": 2,
84
+ "total_pages": 1,
85
+ "tracks" : [
86
+ {
87
+ "track": {
88
+ // track properties
89
+ }
90
+ },
91
+ {
92
+ "track": {
93
+ // track properites
94
+ }
95
+ }
96
+ ]
97
+ }
98
+ ```
99
+
100
+ The gem removes the roots of the search result items, so you can access an item
101
+ directly through array access.
102
+
103
+ ```ruby
104
+ track = officialfm.tracks('foo').tracks[0]
105
+ puts track.duration
106
+ ```
107
+
108
+
109
+ ## Methods
110
+
111
+ ### Tracks
112
+ Search for a track:
113
+
114
+ ```ruby
115
+ officialfm.tracks('Nightcall')
116
+ ```
117
+
118
+ Search results being paged, you can request a specific page with the page parameter.
119
+
120
+ ```ruby
121
+ officialfm.tracks('Kids', page: 2)
122
+ ```
123
+
124
+ Get info about a specific track:
125
+
126
+ ```ruby
127
+ officialfm.track('1nnQ')
128
+ ```
129
+
130
+ ### Playlists
131
+
132
+ Search for a playlist (again you can pass an optional `page` parameter to get a
133
+ specific results page):
134
+
135
+ ```ruby
136
+ officialfm.playlists('AWOLNATION')
137
+ ```
61
138
 
62
- Copyright (c) 2011 Amos Wenger.
139
+ Get info about a specific playlist:
63
140
 
64
- This project is distributed under the New BSD License. See LICENSE for details.
141
+ ```ruby
142
+ officialfm.playlist('CbqY')
143
+ ```
144
+
145
+ Retrieve the tracks in a playlist:
146
+
147
+ ```ruby
148
+ playlists = officialfm.playlists('AWOLNATION')
149
+
150
+ # The tracks for the 3 playlist in the search results
151
+ tracks = playlists[2].tracks
152
+ ```
153
+
154
+ Note that the `tracks` method of a playlist object makes an extra request. If
155
+ you know the playlist ID in advance, you can retrieve the tracks in just one
156
+ request.
157
+
158
+ ```ruby
159
+ officialfm.playlist_tracks('CbqY')
160
+ ```
161
+
162
+ ### Projects
163
+
164
+ Search for a project (a project can be an artist or a collaboration between several artists)
165
+
166
+ ```ruby
167
+ officialfm.projects('Mac Miller x Pharrell')
168
+ ```
169
+
170
+ Similarly to playlists, you can get information about a specific project with
171
+
172
+ ```ruby
173
+ officialfm.project('edB6')
174
+ ```
175
+
176
+ Get a project's tracks or playlists when you know the project ID in advance.
177
+
178
+ ```ruby
179
+ tracks = officialfm.project_tracks('edB6')
180
+ playlists = officialfm.project_playlists('edB6')
181
+ puts artist.tracks
182
+ puts artist.playlists
183
+ ```
184
+
185
+ Of course you can get the playlists and tracks in a project even if you don't know its ID:
186
+
187
+ ```ruby
188
+ projects = officialfm.projects('Mac Miller')
189
+
190
+ puts projects[0].name # => 'Mac Miller'
191
+ puts projects[2].name # => 'Mac Miller x Pharrell'
192
+
193
+ tracks = projects[2].tracks
194
+
195
+ ```
196
+
197
+
198
+ ## Copyright
65
199
 
66
- Based on [@pengwynn's Gowalla API wrapper](https://github.com/pengwynn/gowalla)
200
+ Copyright (c) 2012 official.fm
67
201
 
68
- A huge load of thanks to pengwynn for releasing it open-source! It was wonderful
69
- to work from his extra-clean codebase.
data/Rakefile CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'rake'
2
- require "shoulda/tasks"
3
2
  require "rake/testtask"
4
3
  require 'bundler'
5
4
 
@@ -1,83 +1,50 @@
1
- require 'forwardable'
2
- require 'faraday/request/officialfm_oauth'
3
- require 'officialfm/authentication'
4
1
  require 'officialfm/version'
5
2
 
6
3
  module OfficialFM
7
4
  class Client
8
- extend Forwardable
9
5
 
10
- include Users
11
6
  include Tracks
12
7
  include Playlists
13
- include Authentication
14
-
15
- attr_reader :api_key, :api_secret
8
+ include Projects
16
9
 
17
- def_delegators :web_server, :authorize_url, :access_token_url
10
+ attr_reader :api_key
18
11
 
19
12
  def initialize(options={})
20
13
  @api_key = options[:api_key] || OfficialFM.api_key
21
- @api_secret = options[:api_secret] || OfficialFM.api_secret
22
- @access_token = options[:access_token]
23
- @access_secret = options[:access_secret]
24
- # Note: Although the default of the API is to return XML, I think
25
- # json is more appropriate in the Ruby world
26
-
27
- @format = options[:format] || :json
28
- connection unless @access_token
14
+ connection(options)
29
15
  end
30
16
 
31
- # GET request arguments for simple API calls
32
- def simple_params (options = nil)
33
- params = {
34
- :key => @api_key,
35
- :format => @format,
36
- }
37
- if options
38
- options.each { |key, value|
39
- case key
40
- when 'limit'
41
- params['api_max_responses'] = value
42
- when 'embed'
43
- params['api_embed_codes'] = value
44
- else
45
- params[key] = value
46
- end
47
- }
48
- end
49
- params
50
- end
51
17
 
52
- # Raw HTTP connection, either Faraday::Connection
18
+ # Faraday::Connection used for all HTTP requests
53
19
  #
54
20
  # @return [Faraday::Connection]
55
- def connection
56
- params =
57
-
58
- options = {
59
- :url => api_url,
21
+ def connection(options={})
22
+ config = {
23
+ :url => api_url,
60
24
  :headers => default_headers
61
- }
62
-
63
- @connection ||= Faraday.new(options) do |builder|
64
- builder.use Faraday::Request::OfficialFMOAuth, authentication if authenticated?
65
- builder.use Faraday::Request::Multipart
66
- builder.use Faraday::Request::UrlEncoded
67
-
68
- builder.use Faraday::Response::Mashify
69
- builder.use Faraday::Response::ParseJson
25
+ }.merge(options)
26
+
27
+ @connection ||= Faraday.new(config) do |builder|
70
28
  builder.adapter Faraday.default_adapter
29
+
30
+ builder.request :url_encoded
31
+
32
+ builder.response :mashify
33
+ builder.response :json
71
34
  end
72
35
 
73
36
  end
74
-
37
+
75
38
  # @private
76
39
  def default_headers
77
40
  headers = {
78
- :accept => 'application/json',
79
- :user_agent => "officialfm ruby gem version #{OfficialFM::VERSION}"
41
+ 'X-API-Version' => '2.0',
42
+ :accept => 'application/json',
43
+ :user_agent => "officialfm v2 ruby gem version #{OfficialFM::VERSION}"
80
44
  }
45
+ headers['X-API-KEY'] = @api_key if @api_key
46
+
47
+ headers
81
48
  end
82
49
 
83
50
  # Provides the URL for accessing the API
@@ -86,5 +53,42 @@ module OfficialFM
86
53
  def api_url
87
54
  "http://api.official.fm"
88
55
  end
56
+
57
+ private
58
+
59
+ # Returns the url for a resource.
60
+ #
61
+ # - id: resource id or URL
62
+ # - options: extra params. Supports 'parent' and 'child' options.
63
+ #
64
+ # resource_url('http://api.official.fm/playlists/2BHH', :child => :tracks)
65
+ # # => http://api.official.fm/playlists/2BHH/tracks
66
+ #
67
+ # resource_url('2BHH', { :parent => 'playlists', :child => 'tracks'})
68
+ # # => /playlists/2BHH/tracks
69
+ #
70
+ # Note: if you supply a full resource URL, the parent option is ignored.
71
+ #
72
+ def resource_url(id, options={})
73
+ raise "Resource id cannot be nil" unless id
74
+
75
+ parent = options.fetch(:parent, nil)
76
+ child = options.fetch(:child, nil)
77
+
78
+ if id.start_with? 'http'
79
+ return [id, child].join '/'
80
+ end
81
+
82
+ url = [parent, id, child].join('/').chomp('/')
83
+ url = "/#{url}" unless url.start_with? '/'
84
+
85
+ url
86
+ end
87
+
88
+ def extend_response(response, mixin_module)
89
+ response.obj = self
90
+ response.extend(mixin_module)
91
+ end
92
+
89
93
  end
90
94
  end
@@ -1,145 +1,49 @@
1
- require 'cgi'
2
-
3
1
  module OfficialFM
4
2
  module Playlists
5
-
6
- # Search for playlists
7
- #
8
- # @param [String] search_param: a search parameter (eg. name of the playlist)
9
- # @param [Integer] limit (50) limit per page (optional)
10
- # @return [Hashie::Mash] Playlist list
3
+
4
+ # Search for a playlist
11
5
  def playlists(search_param, options={})
12
- response = connection.get do |req|
13
- req.url "/search/playlists/#{CGI::escape(search_param)}", simple_params(options)
14
- end
15
-
16
- response.body.map do |pl| improve(pl) end
17
- end
18
-
19
- # Retrieve information about a specific playlist
20
- #
21
- # @param [String] track_id: id
22
- # @param [Bool] embed (false) should embed codes be included in the response
23
- # @return [Hashie::Mash] Playlist
24
- def playlist(playlist_id, options={})
25
- response = connection.get do |req|
26
- req.url "/playlist/#{playlist_id}", simple_params(options)
27
- end
28
- improve(response.body[0])
29
- end
30
-
31
- # Retrieve users that have voted for this playlist
32
- #
33
- # @param [String] playlist_id: id
34
- # @param [Integer] limit (50) limit per page
35
- # @return [Hashie::Mash] User list
36
- def playlist_votes(playlist_id, options={})
37
- response = connection.get do |req|
38
- req.url "/playlist/#{playlist_id}/votes", simple_params(options)
6
+ response = connection.get '/playlists/search', options.merge(:q => search_param)
7
+
8
+ response.body.playlists.map! do |p|
9
+ # remove the unnecessary root
10
+ actual_playlist = p.playlist
11
+
12
+ extend_response(actual_playlist, PlaylistMethods)
13
+
14
+ actual_playlist
39
15
  end
16
+
40
17
  response.body
41
18
  end
42
-
43
- def improve(playlist)
44
- # the length field is already used. Note: running_time is in seconds
45
- playlist.running_time = playlist["length"]
46
- playlist
47
- end
48
-
49
- ####################################################################
50
- ######################### Advanced API methods #####################
51
- ####################################################################
52
19
 
53
- # Update information of a given playlist
54
- #
55
- # @param [String] playlist_id: id
56
- # @param [String] description (optional)
57
- # @param [String] name (optional)
58
- # @param [String] password (optional)
59
- # @param [String] private (optional)
60
- # @return [Hashie::Mash] Playlist
61
- def update_playlist! (playlist_id, data = {})
62
- check_auth :update_playlist
63
-
64
- response = connection.put do |req|
65
- req.url "/playlist/update/#{playlist_id}", data
66
- req.body = { :format => @format }
67
- end
68
- response
69
- end
70
-
71
- # Upload a picture for a given playlist
72
- #
73
- # @param [String] playlist_id: id
74
- # @param [String] path: path to a picture
75
- # @param [String] mime: the mime-type of the picture (e.g. image/jpeg, image/png, etc.)
76
- # @return [Hashie::Mash] Success or error message
77
- def playlist_picture! (playlist_id, path, mime)
78
- check_auth :playlist_picture
79
-
80
- response = connection.post do |req|
81
- req.url "/playlist/picture/#{playlist_id}"
82
- req.body = { :file => Faraday::UploadIO.new(path, mime), :format => @format }
83
- end
84
- response
85
- end
86
-
87
- # Remove a playlist
88
- #
89
- # @param [String] playlist_id: id
90
- # @return [Hashie::Mash] Success or error message
91
- def playlist_remove! (playlist_id)
92
- check_auth :playlist_remove
93
-
94
- response = connection.delete do |req|
95
- req.url "/playlist/remove/#{playlist_id}"
96
- req.body = { :format => @format }
97
- end
98
- response
99
- end
100
-
101
- # Create a playlist
102
- #
103
- # @param [String] name: playlist name
104
- # @param [String] track_id: first track id of the new playlist
105
- # @return [Hashie::Mash] Playlist object
106
- def playlist_create! (name, track_id)
107
- check_auth :playlist_create
108
-
109
- response = connection.post do |req|
110
- req.url "/playlist/create"
111
- req.body = { :format => @format, :name => name, :track_id => track_id }
112
- end
113
- response
20
+ # Retrieve information about a specific playlist
21
+ def playlist(id, options={})
22
+ url = resource_url(id, parent: 'playlists')
23
+
24
+ response = connection.get(url, options).body.playlist
25
+ extend_response(response, PlaylistMethods)
114
26
  end
115
-
116
- # Vote for a playlist
117
- #
118
- # @param [String] playlist_id: id
119
- # @return [Hashie::Mash] Success or error message
120
- def playlist_vote! (playlist_id)
121
- check_auth :playlist_vote
122
-
123
- response = connection.post do |req|
124
- req.url "/playlist/vote/#{playlist_id}"
125
- req.body = { :format => @format }
27
+
28
+ def playlist_tracks(id, options={})
29
+ url = resource_url(id, { parent: 'playlists', child: 'tracks' })
30
+
31
+ response = connection.get url, options
32
+
33
+ tracks = response.body.tracks.map do |t|
34
+ # remove the unnecessary root
35
+ t.track
126
36
  end
127
- response
37
+
38
+ tracks
128
39
  end
129
-
130
- # Remote vote for a playlist
131
- #
132
- # @param [String] playlist_id: id
133
- # @return [Hashie::Mash] Success or error message
134
- def playlist_unvote! (playlist_id)
135
- check_auth :playlist_unvote
136
-
137
- response = connection.post do |req|
138
- req.url "/playlist/unvote/#{playlist_id}"
139
- req.body = { :format => @format }
40
+
41
+ module PlaylistMethods
42
+
43
+ def tracks
44
+ obj.playlist_tracks(self.src)
140
45
  end
141
- response
142
46
  end
143
-
47
+
144
48
  end
145
49
  end