hallon 0.15.0 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +40 -0
- data/README.markdown +1 -1
- data/dev/application_key_converter.rb +11 -0
- data/examples/example_support.rb +4 -0
- data/examples/playing_audio.rb +1 -1
- data/lib/hallon.rb +11 -0
- data/lib/hallon/album_browse.rb +3 -3
- data/lib/hallon/artist_browse.rb +20 -4
- data/lib/hallon/blob.rb +11 -0
- data/lib/hallon/enumerator.rb +5 -0
- data/lib/hallon/ext/spotify.rb +2 -0
- data/lib/hallon/image.rb +7 -1
- data/lib/hallon/link.rb +5 -2
- data/lib/hallon/observable.rb +48 -1
- data/lib/hallon/player.rb +15 -26
- data/lib/hallon/playlist.rb +16 -25
- data/lib/hallon/playlist_container.rb +38 -0
- data/lib/hallon/search.rb +75 -4
- data/lib/hallon/session.rb +31 -42
- data/lib/hallon/toplist.rb +3 -3
- data/lib/hallon/track.rb +28 -10
- data/lib/hallon/version.rb +1 -1
- data/spec/hallon/album_browse_spec.rb +68 -18
- data/spec/hallon/album_spec.rb +62 -27
- data/spec/hallon/artist_browse_spec.rb +106 -31
- data/spec/hallon/artist_spec.rb +32 -18
- data/spec/hallon/blob_spec.rb +6 -0
- data/spec/hallon/enumerator_spec.rb +10 -0
- data/spec/hallon/error_spec.rb +4 -4
- data/spec/hallon/hallon_spec.rb +1 -1
- data/spec/hallon/image_spec.rb +58 -47
- data/spec/hallon/link_spec.rb +51 -43
- data/spec/hallon/observable/album_browse_spec.rb +1 -1
- data/spec/hallon/observable/artist_browse_spec.rb +1 -1
- data/spec/hallon/observable/image_spec.rb +1 -1
- data/spec/hallon/observable/playlist_container_spec.rb +4 -4
- data/spec/hallon/observable/playlist_spec.rb +14 -14
- data/spec/hallon/observable/post_spec.rb +1 -1
- data/spec/hallon/observable/search_spec.rb +1 -1
- data/spec/hallon/observable/session_spec.rb +17 -17
- data/spec/hallon/observable/toplist_spec.rb +1 -1
- data/spec/hallon/observable_spec.rb +40 -6
- data/spec/hallon/player_spec.rb +1 -1
- data/spec/hallon/playlist_container_spec.rb +96 -13
- data/spec/hallon/playlist_spec.rb +180 -45
- data/spec/hallon/search_spec.rb +211 -28
- data/spec/hallon/session_spec.rb +44 -38
- data/spec/hallon/toplist_spec.rb +31 -14
- data/spec/hallon/track_spec.rb +159 -50
- data/spec/hallon/user_post_spec.rb +10 -5
- data/spec/hallon/user_spec.rb +60 -50
- data/spec/spec_helper.rb +40 -15
- data/spec/support/album_mocks.rb +30 -0
- data/spec/support/artist_mocks.rb +36 -0
- data/spec/support/common_objects.rb +0 -201
- data/spec/support/image_mocks.rb +34 -0
- data/spec/support/playlist_container_mocks.rb +36 -0
- data/spec/support/playlist_mocks.rb +70 -0
- data/spec/support/search_mocks.rb +23 -0
- data/spec/support/session_mocks.rb +33 -0
- data/spec/support/toplist_mocks.rb +19 -0
- data/spec/support/track_mocks.rb +28 -0
- data/spec/support/user_mocks.rb +20 -0
- metadata +40 -18
- data/spec/support/context_stub_session.rb +0 -5
@@ -289,6 +289,44 @@ module Hallon
|
|
289
289
|
symbol == :ok
|
290
290
|
end
|
291
291
|
|
292
|
+
# Retrieve the number of unseen tracks for the given playlist.
|
293
|
+
#
|
294
|
+
# @param [Playlist] playlist
|
295
|
+
# @return [Integer] number of unseen tracks
|
296
|
+
def unseen_tracks_count_for(playlist)
|
297
|
+
Spotify.playlistcontainer_get_unseen_tracks(pointer, playlist.pointer, nil, 0).tap do |count|
|
298
|
+
raise OperationFailedError if count < 0
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
# Retrieve the unseen tracks for the given playlist.
|
303
|
+
#
|
304
|
+
# @note The playlist must be in this container, or this method will fail.
|
305
|
+
# @see clear_unseen_tracks_for
|
306
|
+
# @param [Playlist] playlist
|
307
|
+
# @return [Array<Track>] array of unseen tracks.
|
308
|
+
def unseen_tracks_for(playlist, count = unseen_tracks_count_for(playlist))
|
309
|
+
tracks_ary = FFI::MemoryPointer.new(:pointer, count)
|
310
|
+
real_count = Spotify.playlistcontainer_get_unseen_tracks(pointer, playlist.pointer, tracks_ary, count)
|
311
|
+
raise OperationFailedError if real_count < 0
|
312
|
+
tracks_ary.read_array_of_pointer([real_count, count].min).map do |track|
|
313
|
+
track_pointer = Spotify::Pointer.new(track, :track, true)
|
314
|
+
Hallon::Track.new(track_pointer)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
# Clears the unseen tracks for the given playlist.
|
319
|
+
#
|
320
|
+
# @note in libspotify v11.1.60, this method appears to do nothing
|
321
|
+
# @param [Playlist] playlist
|
322
|
+
# @return [PlaylistContainer] self
|
323
|
+
def clear_unseen_tracks_for(playlist)
|
324
|
+
tap do
|
325
|
+
result = Spotify.playlistcontainer_clear_unseen_tracks(pointer, playlist.pointer)
|
326
|
+
raise OperationFailedError if result < 0
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
292
330
|
protected
|
293
331
|
# Wrapper for original API; adjusts indices accordingly.
|
294
332
|
#
|
data/lib/hallon/search.rb
CHANGED
@@ -52,6 +52,49 @@ module Hallon
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
+
class PlaylistEnumerator < Enumerator
|
56
|
+
size :search_num_playlists
|
57
|
+
|
58
|
+
# @return [Integer] total playlists available from connected search result.
|
59
|
+
def total
|
60
|
+
Spotify.search_total_playlists(pointer)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Enumerates through all playlist names of a search object.
|
65
|
+
class PlaylistNames < PlaylistEnumerator
|
66
|
+
# @return [String, nil]
|
67
|
+
item :search_playlist_name
|
68
|
+
end
|
69
|
+
|
70
|
+
# Enumerates through all playlist uris of a search object.
|
71
|
+
class PlaylistUris < PlaylistEnumerator
|
72
|
+
# @return [String, nil]
|
73
|
+
item :search_playlist_uri
|
74
|
+
end
|
75
|
+
|
76
|
+
# Enumerates through all playlist image uris of a search object.
|
77
|
+
class PlaylistImageUris < PlaylistEnumerator
|
78
|
+
# @return [String, nil]
|
79
|
+
item :search_playlist_image_uri
|
80
|
+
end
|
81
|
+
|
82
|
+
# Enumerates through all playlists of a search object.
|
83
|
+
class Playlists < PlaylistEnumerator
|
84
|
+
# @return [Playlist]
|
85
|
+
item :search_playlist_uri do |uri|
|
86
|
+
Playlist.from(uri)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Enumerates through all playlist images of a search object.
|
91
|
+
class Images < PlaylistEnumerator
|
92
|
+
# @return [Playlist]
|
93
|
+
item :search_playlist_image_uri do |uri|
|
94
|
+
Image.from(uri)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
55
98
|
include Linkable
|
56
99
|
|
57
100
|
to_link :from_search
|
@@ -74,7 +117,8 @@ module Hallon
|
|
74
117
|
:tracks_offset => 0,
|
75
118
|
:albums_offset => 0,
|
76
119
|
:artists_offset => 0,
|
77
|
-
:playlists_offset => 0
|
120
|
+
:playlists_offset => 0,
|
121
|
+
:type => :standard
|
78
122
|
}
|
79
123
|
end
|
80
124
|
|
@@ -82,6 +126,7 @@ module Hallon
|
|
82
126
|
#
|
83
127
|
# @param [String, Link] search search query or spotify URI
|
84
128
|
# @param [Hash] options additional search options
|
129
|
+
# @option options [Symbol] :type (:standard) search type, either standard or suggest
|
85
130
|
# @option options [#to_i] :tracks (25) max number of tracks you want in result
|
86
131
|
# @option options [#to_i] :albums (25) max number of albums you want in result
|
87
132
|
# @option options [#to_i] :artists (25) max number of artists you want in result
|
@@ -93,6 +138,7 @@ module Hallon
|
|
93
138
|
# @see http://developer.spotify.com/en/libspotify/docs/group__search.html#gacf0b5e902e27d46ef8b1f40e332766df
|
94
139
|
def initialize(search, options = {})
|
95
140
|
opts = Search.defaults.merge(options)
|
141
|
+
type = opts.delete(:type)
|
96
142
|
opts = opts.values_at(:tracks_offset, :tracks, :albums_offset, :albums, :artists_offset, :artists, :playlists_offset, :playlists).map(&:to_i)
|
97
143
|
search = from_link(search) if Link.valid?(search)
|
98
144
|
|
@@ -100,7 +146,7 @@ module Hallon
|
|
100
146
|
@pointer = if Spotify::Pointer.typechecks?(search, :search)
|
101
147
|
search
|
102
148
|
else
|
103
|
-
Spotify.search_create!(session.pointer, search, *opts,
|
149
|
+
Spotify.search_create!(session.pointer, search, *opts, type, callback, nil)
|
104
150
|
end
|
105
151
|
|
106
152
|
raise ArgumentError, "search with #{search} failed" if @pointer.null?
|
@@ -120,12 +166,12 @@ module Hallon
|
|
120
166
|
|
121
167
|
# @return [String] search query this search was created with.
|
122
168
|
def query
|
123
|
-
Spotify.search_query(pointer)
|
169
|
+
Spotify.search_query(pointer).to_s
|
124
170
|
end
|
125
171
|
|
126
172
|
# @return [String] “did you mean?” suggestion for current search.
|
127
173
|
def did_you_mean
|
128
|
-
Spotify.search_did_you_mean(pointer)
|
174
|
+
Spotify.search_did_you_mean(pointer).to_s
|
129
175
|
end
|
130
176
|
|
131
177
|
# @return [Tracks] list of all tracks in the search result.
|
@@ -142,5 +188,30 @@ module Hallon
|
|
142
188
|
def artists
|
143
189
|
Artists.new(self)
|
144
190
|
end
|
191
|
+
|
192
|
+
# @return [PlaylistNames] list of all playlist names in the search result.
|
193
|
+
def playlist_names
|
194
|
+
PlaylistNames.new(self)
|
195
|
+
end
|
196
|
+
|
197
|
+
# @return [PlaylistUris] list of all playlist uris in the search result.
|
198
|
+
def playlist_uris
|
199
|
+
PlaylistUris.new(self)
|
200
|
+
end
|
201
|
+
|
202
|
+
# @return [PlaylistImageUris] list of all playlist image uris in the search result.
|
203
|
+
def playlist_image_uris
|
204
|
+
PlaylistImageUris.new(self)
|
205
|
+
end
|
206
|
+
|
207
|
+
# @return [Playlists] list of all playlists in the search result.
|
208
|
+
def playlists
|
209
|
+
Playlists.new(self)
|
210
|
+
end
|
211
|
+
|
212
|
+
# @return [Images] list of all images in the search result.
|
213
|
+
def playlist_images
|
214
|
+
Images.new(self)
|
215
|
+
end
|
145
216
|
end
|
146
217
|
end
|
data/lib/hallon/session.rb
CHANGED
@@ -55,7 +55,7 @@ module Hallon
|
|
55
55
|
#
|
56
56
|
# @return [Session]
|
57
57
|
def Session.instance
|
58
|
-
@__instance__ or raise "Session has not been initialized"
|
58
|
+
@__instance__ or raise NoSessionError, "Session has not been initialized"
|
59
59
|
end
|
60
60
|
|
61
61
|
# @return [Boolean] true if a Session instance exists.
|
@@ -120,11 +120,20 @@ module Hallon
|
|
120
120
|
# You pass a pointer to the session pointer to libspotify >:)
|
121
121
|
FFI::MemoryPointer.new(:pointer) do |p|
|
122
122
|
Error::maybe_raise Spotify.session_create(config, p)
|
123
|
-
@pointer =
|
123
|
+
@pointer = p.read_pointer
|
124
124
|
end
|
125
125
|
end
|
126
126
|
end
|
127
127
|
|
128
|
+
# Flushes the Session cache to disk.
|
129
|
+
#
|
130
|
+
# @note libspotify does this automatically periodically, under normal
|
131
|
+
# circumstances this method should not need to be used.
|
132
|
+
# @return [Session]
|
133
|
+
def flush_caches
|
134
|
+
Spotify.session_flush_caches(pointer)
|
135
|
+
end
|
136
|
+
|
128
137
|
# PlaylistContainer for the currently logged in session.
|
129
138
|
#
|
130
139
|
# @note returns nil if the session is not logged in.
|
@@ -144,43 +153,18 @@ module Hallon
|
|
144
153
|
end
|
145
154
|
end
|
146
155
|
|
147
|
-
# Wait for the given callbacks to fire until the block returns true
|
148
|
-
#
|
149
|
-
# @note Given block will be called once instantly without parameters.
|
150
|
-
# @note If no events happen for 0.25 seconds, the given block will be called
|
151
|
-
# with `:timeout` as parameter.
|
152
|
-
# @param [Symbol, ...] *events list of events to wait for
|
153
|
-
# @yield [Symbol, *args] name of the callback that fired, and its’ arguments
|
154
|
-
# @return [Hash<Event, Arguments>]
|
155
|
-
def process_events_on(*events)
|
156
|
-
yield or protecting_handlers do
|
157
|
-
channel = SizedQueue.new(1)
|
158
|
-
block = proc { |*args| channel << args }
|
159
|
-
events.each { |event| on(event, &block) }
|
160
|
-
on(:notify_main_thread) { channel << :notify }
|
161
|
-
|
162
|
-
loop do
|
163
|
-
begin
|
164
|
-
timeout = [process_events.fdiv(1000), 5].min # scope to five seconds
|
165
|
-
timeout = timeout + 0.010 # minimum of ten miliseconds timeout
|
166
|
-
params = Timeout::timeout(timeout) { channel.pop }
|
167
|
-
redo if params == :notify
|
168
|
-
rescue Timeout::Error
|
169
|
-
params = :timeout
|
170
|
-
end
|
171
|
-
|
172
|
-
if result = yield(*params)
|
173
|
-
return result
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|
178
|
-
alias :wait_for :process_events_on
|
179
|
-
|
180
156
|
# Log into Spotify using the given credentials.
|
181
157
|
#
|
158
|
+
# @example logging in with password
|
159
|
+
# session.login 'Kim', 'password'
|
160
|
+
#
|
161
|
+
# @example logging in with credentials blob
|
162
|
+
# session.login 'Kim', Hallon::Blob('blob string')
|
163
|
+
#
|
164
|
+
# @note it also supports logging in via a credentials blob, if you pass
|
165
|
+
# a Hallon::Blob(blob_string) as the password instead of the real password
|
182
166
|
# @param [String] username
|
183
|
-
# @param [String]
|
167
|
+
# @param [String] password_or_blob
|
184
168
|
# @param [Boolean] remember_me have libspotify remember credentials for {#relogin}
|
185
169
|
# @return [Session]
|
186
170
|
# @see login!
|
@@ -189,7 +173,8 @@ module Hallon
|
|
189
173
|
raise ArgumentError, "username and password may not be blank"
|
190
174
|
end
|
191
175
|
|
192
|
-
|
176
|
+
password, blob = blob, password if password.is_a?(Blob)
|
177
|
+
tap { Spotify.session_login(pointer, username, password, remember_me, blob) }
|
193
178
|
end
|
194
179
|
|
195
180
|
# Login the remembered user (see {#login}).
|
@@ -221,7 +206,8 @@ module Hallon
|
|
221
206
|
# @raise [Error] if failed to log in
|
222
207
|
# @see #relogin
|
223
208
|
def relogin!
|
224
|
-
|
209
|
+
relogin
|
210
|
+
tap { wait_until_logged_in }
|
225
211
|
end
|
226
212
|
|
227
213
|
# Log out the current user.
|
@@ -229,7 +215,8 @@ module Hallon
|
|
229
215
|
# @note This method will not return until you’ve logged out successfully.
|
230
216
|
# @return [Session]
|
231
217
|
def logout!
|
232
|
-
|
218
|
+
logout
|
219
|
+
tap { wait_for(:logged_out) { logged_out? } }
|
233
220
|
end
|
234
221
|
|
235
222
|
# @return [String] username of the user stored in libspotify-remembered credentials.
|
@@ -335,12 +322,14 @@ module Hallon
|
|
335
322
|
|
336
323
|
# Offline synchronization status.
|
337
324
|
#
|
338
|
-
# @return [Hash
|
325
|
+
# @return [Hash] sync status (empty hash if not applicable)
|
339
326
|
# @see http://developer.spotify.com/en/libspotify/docs/structsp__offline__sync__status.html
|
340
327
|
def offline_sync_status
|
341
328
|
struct = Spotify::OfflineSyncStatus.new
|
342
329
|
if Spotify.offline_sync_get_status(pointer, struct.pointer)
|
343
330
|
Hash[struct.members.zip(struct.values)]
|
331
|
+
else
|
332
|
+
{}
|
344
333
|
end
|
345
334
|
end
|
346
335
|
|
@@ -424,8 +413,8 @@ module Hallon
|
|
424
413
|
# @see login!
|
425
414
|
# @see relogin!
|
426
415
|
def wait_until_logged_in
|
427
|
-
wait_for(:connection_error) do |error|
|
428
|
-
Error.maybe_raise(error
|
416
|
+
wait_for(:logged_in, :connection_error) do |event, error|
|
417
|
+
Error.maybe_raise(error)
|
429
418
|
session.logged_in?
|
430
419
|
end
|
431
420
|
end
|
data/lib/hallon/toplist.rb
CHANGED
@@ -103,11 +103,11 @@ module Hallon
|
|
103
103
|
end
|
104
104
|
|
105
105
|
# @note If the object is not loaded, the result is undefined.
|
106
|
-
# @
|
107
|
-
# @return [Rational, nil] time it took for the toplistbrowse request to complete (in seconds).
|
106
|
+
# @return [Rational] time it took for the toplistbrowse request to complete (in seconds).
|
108
107
|
def request_duration
|
109
108
|
duration = Spotify.toplistbrowse_backend_request_duration(pointer)
|
110
|
-
|
109
|
+
duration = 0 if duration < 0
|
110
|
+
Rational(duration, 1000)
|
111
111
|
end
|
112
112
|
|
113
113
|
private
|
data/lib/hallon/track.rb
CHANGED
@@ -50,6 +50,24 @@ module Hallon
|
|
50
50
|
|
51
51
|
# Create a new local track.
|
52
52
|
#
|
53
|
+
# Local tracks in Spotify allows you to specify title, artist, and
|
54
|
+
# optionally also album and length. This will create a local track
|
55
|
+
# (meaning {Track#local?} returns true) that libspotify will try to
|
56
|
+
# match up with an *actual* track in the Spotify database.
|
57
|
+
#
|
58
|
+
# If the track is successfully matched, once loaded, {Track#available?}
|
59
|
+
# will return true and you’ll be able to inspect the track’s {#artist}
|
60
|
+
# and {#album}, as well as the information in the track itself. If the
|
61
|
+
# track is playable you’ll also be able to play it!
|
62
|
+
#
|
63
|
+
# @example creating a local track
|
64
|
+
# track = Hallon::Track.local "Californication", "Red Hot Chili Peppers"
|
65
|
+
# puts track.artist.name # => "Red Hot Chili Peppers"
|
66
|
+
# puts track.album.name # => "Californication"
|
67
|
+
# p track.local? # => true
|
68
|
+
#
|
69
|
+
# @note I’ve been unable to get local tracks to get matched up in libspotify v11,
|
70
|
+
# it’s possible this function does not work properly in libspotify v11.
|
53
71
|
# @param [String] title
|
54
72
|
# @param [String] artist
|
55
73
|
# @param [String] album
|
@@ -60,7 +78,6 @@ module Hallon
|
|
60
78
|
new(track)
|
61
79
|
end
|
62
80
|
|
63
|
-
# @note This’ll be an empty string unless the track is loaded.
|
64
81
|
# @return [String]
|
65
82
|
def name
|
66
83
|
Spotify.track_name(pointer)
|
@@ -68,23 +85,22 @@ module Hallon
|
|
68
85
|
|
69
86
|
# Duration of the track in seconds.
|
70
87
|
#
|
71
|
-
# @note This’ll be `0` unless the track is loaded.
|
72
88
|
# @return [Rational]
|
73
89
|
def duration
|
74
90
|
Rational(Spotify.track_duration(pointer), 1000)
|
75
91
|
end
|
76
92
|
|
77
|
-
# Track popularity, between 0 and
|
93
|
+
# Track popularity, between 0 and 100.
|
78
94
|
#
|
79
|
-
# @note This’ll be `0` unless the track is loaded.
|
80
95
|
# @return [Rational]
|
81
96
|
def popularity
|
82
|
-
|
97
|
+
Spotify.track_popularity(pointer)
|
83
98
|
end
|
84
99
|
|
85
100
|
# Disc number this track appears in.
|
86
101
|
#
|
87
102
|
# @note This function is a bit special. See libspotify docs for details.
|
103
|
+
# @return [Integer] disc index from album this track appears in.
|
88
104
|
def disc
|
89
105
|
Spotify.track_disc(pointer)
|
90
106
|
end
|
@@ -139,7 +155,6 @@ module Hallon
|
|
139
155
|
Spotify.track_offline_get_status(pointer)
|
140
156
|
end
|
141
157
|
|
142
|
-
# @note This’ll be `nil` unless the track is loaded.
|
143
158
|
# @return [Hallon::Album] album this track belongs to.
|
144
159
|
def album
|
145
160
|
album = Spotify.track_album!(pointer)
|
@@ -153,13 +168,11 @@ module Hallon
|
|
153
168
|
artists.first
|
154
169
|
end
|
155
170
|
|
156
|
-
# @note Track must be loaded, or you’ll get zero artists.
|
157
171
|
# @return [Artists] all {Artist}s who performed this Track.
|
158
172
|
def artists
|
159
173
|
Artists.new(self)
|
160
174
|
end
|
161
175
|
|
162
|
-
# @note This’ll always return false unless the track is loaded.
|
163
176
|
# @return [Boolean] true if {#availability} is available.
|
164
177
|
def available?
|
165
178
|
availability == :available
|
@@ -172,13 +185,18 @@ module Hallon
|
|
172
185
|
Spotify.track_get_availability(session.pointer, pointer)
|
173
186
|
end
|
174
187
|
|
175
|
-
# @
|
188
|
+
# @see autolinked?
|
189
|
+
# @return [Track] the track this track is autolinked to for audio playback.
|
190
|
+
def playable_track
|
191
|
+
track = Spotify.track_get_playable!(session.pointer, pointer)
|
192
|
+
Track.from(track)
|
193
|
+
end
|
194
|
+
|
176
195
|
# @return [Boolean] true if the track is a local track.
|
177
196
|
def local?
|
178
197
|
Spotify.track_is_local(session.pointer, pointer)
|
179
198
|
end
|
180
199
|
|
181
|
-
# @note This’ll always return false unless the track is loaded.
|
182
200
|
# @return [Boolean] true if the track is autolinked.
|
183
201
|
def autolinked?
|
184
202
|
Spotify.track_is_autolinked(session.pointer, pointer)
|
data/lib/hallon/version.rb
CHANGED
@@ -1,11 +1,21 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
describe Hallon::AlbumBrowse do
|
3
|
-
|
3
|
+
let(:browse) do
|
4
|
+
album = Hallon::Album.new(mock_albums[:default])
|
5
|
+
Hallon::AlbumBrowse.new(album)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:empty_browse) do
|
9
|
+
album = Hallon::Album.new(mock_albums[:empty])
|
10
|
+
Hallon::AlbumBrowse.new(album)
|
11
|
+
end
|
12
|
+
|
13
|
+
specify { browse.should be_a Hallon::Loadable }
|
4
14
|
|
5
15
|
describe ".new" do
|
6
16
|
it "should raise an error if the browse request failed" do
|
7
17
|
Spotify.should_receive(:albumbrowse_create).and_return(null_pointer)
|
8
|
-
expect {
|
18
|
+
expect { Hallon::AlbumBrowse.new(mock_album) }.to raise_error(FFI::NullPointerError)
|
9
19
|
end
|
10
20
|
|
11
21
|
it "should raise an error given a non-album spotify pointer" do
|
@@ -13,31 +23,71 @@ describe Hallon::AlbumBrowse do
|
|
13
23
|
end
|
14
24
|
end
|
15
25
|
|
16
|
-
|
17
|
-
album
|
18
|
-
|
26
|
+
describe "#loaded?" do
|
27
|
+
it "is true when the album browser is loaded" do
|
28
|
+
browse.should be_loaded
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#status" do
|
33
|
+
it "returns the album status" do
|
34
|
+
browse.status.should eq :ok
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#album" do
|
39
|
+
it "returns the album" do
|
40
|
+
browse.album.should eq Hallon::Album.new(mock_albums[:default])
|
41
|
+
end
|
19
42
|
end
|
20
43
|
|
21
|
-
|
44
|
+
describe "#artist" do
|
45
|
+
it "returns the album’s artist" do
|
46
|
+
browse.artist.should eq Hallon::Artist.new(mock_artists[:default])
|
47
|
+
end
|
22
48
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
49
|
+
it "returns nil if the album browser is not loaded" do
|
50
|
+
empty_browse.artist.should be_nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#review" do
|
55
|
+
it "returns the album’s review" do
|
56
|
+
browse.review.should eq "This album is AWESOME"
|
57
|
+
end
|
58
|
+
|
59
|
+
it "returns an empty string if the album browser is not loaded" do
|
60
|
+
empty_browse.review.should be_empty
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#copyrights" do
|
65
|
+
it "returns an enumerator of the album’s copyright texts" do
|
66
|
+
browse.copyrights.to_a.should eq %w[Kim Elin]
|
67
|
+
end
|
68
|
+
|
69
|
+
it "returns an empty enumerator when the album browser is not loaded" do
|
70
|
+
empty_browse.copyrights.size.should eq 0
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "#tracks" do
|
75
|
+
it "returns an enumerator of the album’s tracks" do
|
76
|
+
browse.tracks.to_a.should eq instantiate(Hallon::Track, mock_track, mock_track_two)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "returns an empty enumerator if the album browser is not loaded" do
|
80
|
+
empty_browse.tracks.size.should eq 0
|
81
|
+
end
|
82
|
+
end
|
32
83
|
|
33
84
|
describe "#request_duration" do
|
34
85
|
it "should return the request duration in seconds" do
|
35
86
|
browse.request_duration.should eq 2.751
|
36
87
|
end
|
37
88
|
|
38
|
-
it "should be
|
39
|
-
|
40
|
-
browse.request_duration.should be_nil
|
89
|
+
it "should be zero if the request was fetched from local cache" do
|
90
|
+
empty_browse.request_duration.should eq 0
|
41
91
|
end
|
42
92
|
end
|
43
93
|
end
|