hallon 0.11.0 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +1 -0
- data/{CHANGELOG → CHANGELOG.md} +82 -27
- data/Gemfile +1 -0
- data/README.markdown +1 -1
- data/Rakefile +8 -6
- data/examples/adding_tracks_to_playlist.rb +3 -0
- data/examples/logging_in.rb +3 -0
- data/examples/playing_audio.rb +130 -0
- data/examples/printing_link_information.rb +3 -0
- data/examples/show_published_playlists_of_user.rb +5 -13
- data/hallon.gemspec +2 -2
- data/lib/hallon.rb +15 -0
- data/lib/hallon/album.rb +1 -1
- data/lib/hallon/album_browse.rb +4 -3
- data/lib/hallon/artist.rb +1 -1
- data/lib/hallon/artist_browse.rb +5 -4
- data/lib/hallon/base.rb +7 -2
- data/lib/hallon/error.rb +1 -1
- data/lib/hallon/ext/spotify.rb +26 -42
- data/lib/hallon/image.rb +7 -8
- data/lib/hallon/observable.rb +134 -62
- data/lib/hallon/observable/album_browse.rb +30 -0
- data/lib/hallon/observable/artist_browse.rb +31 -0
- data/lib/hallon/observable/image.rb +31 -0
- data/lib/hallon/observable/player.rb +13 -0
- data/lib/hallon/observable/playlist.rb +194 -0
- data/lib/hallon/observable/playlist_container.rb +74 -0
- data/lib/hallon/observable/post.rb +30 -0
- data/lib/hallon/observable/search.rb +29 -0
- data/lib/hallon/observable/session.rb +236 -0
- data/lib/hallon/observable/toplist.rb +30 -0
- data/lib/hallon/player.rb +8 -17
- data/lib/hallon/playlist.rb +11 -7
- data/lib/hallon/playlist_container.rb +11 -4
- data/lib/hallon/queue.rb +71 -0
- data/lib/hallon/search.rb +10 -7
- data/lib/hallon/session.rb +18 -21
- data/lib/hallon/toplist.rb +4 -3
- data/lib/hallon/user.rb +5 -5
- data/lib/hallon/version.rb +1 -1
- data/spec/hallon/album_browse_spec.rb +4 -0
- data/spec/hallon/artist_browse_spec.rb +4 -0
- data/spec/hallon/base_spec.rb +26 -9
- data/spec/hallon/hallon_spec.rb +0 -18
- data/spec/hallon/image_spec.rb +0 -1
- data/spec/hallon/link_spec.rb +14 -0
- data/spec/hallon/observable/album_browse_spec.rb +7 -0
- data/spec/hallon/observable/artist_browse_spec.rb +7 -0
- data/spec/hallon/observable/image_spec.rb +8 -0
- data/spec/hallon/observable/playlist_container_spec.rb +21 -0
- data/spec/hallon/observable/playlist_spec.rb +85 -0
- data/spec/hallon/observable/post_spec.rb +8 -0
- data/spec/hallon/observable/search_spec.rb +7 -0
- data/spec/hallon/observable/session_spec.rb +143 -0
- data/spec/hallon/observable/toplist_spec.rb +7 -0
- data/spec/hallon/observable_spec.rb +134 -65
- data/spec/hallon/playlist_container_spec.rb +24 -18
- data/spec/hallon/playlist_spec.rb +2 -0
- data/spec/hallon/queue_spec.rb +35 -0
- data/spec/hallon/session_spec.rb +4 -4
- data/spec/hallon/spotify_spec.rb +35 -9
- data/spec/mockspotify.rb +2 -3
- data/spec/spec_helper.rb +0 -1
- data/spec/support/common_objects.rb +27 -15
- data/spec/support/enumerable_comparison.rb +9 -0
- data/spec/support/shared_for_callbacks.rb +60 -0
- data/spec/support/shared_for_linkable_objects.rb +1 -1
- metadata +56 -20
@@ -0,0 +1,30 @@
|
|
1
|
+
module Hallon::Observable
|
2
|
+
# Callbacks related to the {Hallon::Toplist} object.
|
3
|
+
module Toplist
|
4
|
+
# Includes {Hallon::Observable} for you.
|
5
|
+
def self.extended(other)
|
6
|
+
other.send(:include, Hallon::Observable)
|
7
|
+
end
|
8
|
+
|
9
|
+
protected
|
10
|
+
|
11
|
+
# @return [Method] load callback
|
12
|
+
def initialize_callbacks
|
13
|
+
callback_for(:load)
|
14
|
+
end
|
15
|
+
|
16
|
+
# This callback is fired when the Image object is fully loaded.
|
17
|
+
#
|
18
|
+
# @example listening to this callback
|
19
|
+
# toplist.on(:load) do
|
20
|
+
# puts "the toplist has loaded!"
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# @yield [self]
|
24
|
+
# @yieldparam [Toplist] self
|
25
|
+
def load_callback(pointer, userdata)
|
26
|
+
trigger(pointer, :load)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
data/lib/hallon/player.rb
CHANGED
@@ -3,12 +3,11 @@ module Hallon
|
|
3
3
|
# A wrapper around Session for playing, stopping and otherwise
|
4
4
|
# controlling the playback features of libspotify.
|
5
5
|
#
|
6
|
-
# @note This is very much a work in progress.
|
7
|
-
# takes care of all callbacks, and the callbacks themselves
|
8
|
-
# must still be handled by means of Ruby FFI.
|
6
|
+
# @note This is very much a work in progress.
|
9
7
|
# @see Session
|
10
8
|
class Player
|
11
|
-
|
9
|
+
# meep?
|
10
|
+
extend Observable::Player
|
12
11
|
|
13
12
|
# @return [Spotify::Pointer<Session>] session pointer
|
14
13
|
attr_reader :pointer
|
@@ -25,7 +24,7 @@ module Hallon
|
|
25
24
|
#
|
26
25
|
# @example
|
27
26
|
# Hallon::Player.new(session) do
|
28
|
-
# on(:music_delivery) do
|
27
|
+
# on(:music_delivery) do |format, frames|
|
29
28
|
# end
|
30
29
|
#
|
31
30
|
# on(:start_playback) do
|
@@ -56,20 +55,12 @@ module Hallon
|
|
56
55
|
@session = session
|
57
56
|
@pointer = @session.pointer
|
58
57
|
|
59
|
-
%w[
|
58
|
+
%w[
|
59
|
+
start_playback stop_playback play_token_lost end_of_track
|
60
|
+
streaming_error get_audio_buffer_stats music_delivery
|
61
|
+
].each do |cb|
|
60
62
|
@session.on(cb) { |*args| trigger(cb, *args) }
|
61
63
|
end
|
62
|
-
|
63
|
-
@session.on(:audio_buffer_stats) do |stats_ptr|
|
64
|
-
stats = Spotify::AudioBufferStats.new(stats_ptr)
|
65
|
-
samples, dropouts = trigger(:buffer_size?)
|
66
|
-
stats[:samples] = samples || 0
|
67
|
-
stats[:dropouts] = dropouts || 0
|
68
|
-
end
|
69
|
-
|
70
|
-
@session.on(:music_delivery) do |format, frames, num_frames|
|
71
|
-
trigger(:music_delivery, format, frames, num_frames)
|
72
|
-
end
|
73
64
|
end
|
74
65
|
|
75
66
|
# Set preferred playback bitrate.
|
data/lib/hallon/playlist.rb
CHANGED
@@ -6,9 +6,6 @@ module Hallon
|
|
6
6
|
#
|
7
7
|
# @see http://developer.spotify.com/en/libspotify/docs/group__playlist.html
|
8
8
|
class Playlist < Base
|
9
|
-
include Observable
|
10
|
-
extend Linkable
|
11
|
-
|
12
9
|
# Playlist::Track is a {Track} with additional information attached to it,
|
13
10
|
# that is specific to the playlist it was created from. The returned track
|
14
11
|
# is a snapshot of the information, so even if the underlying track moves,
|
@@ -80,6 +77,11 @@ module Hallon
|
|
80
77
|
end
|
81
78
|
end
|
82
79
|
|
80
|
+
extend Linkable
|
81
|
+
|
82
|
+
# CAN HAZ CALLBAKZ
|
83
|
+
extend Observable::Playlist
|
84
|
+
|
83
85
|
from_link :playlist do |pointer|
|
84
86
|
Spotify.playlist_create!(session.pointer, pointer)
|
85
87
|
end
|
@@ -92,8 +94,10 @@ module Hallon
|
|
92
94
|
def initialize(link)
|
93
95
|
@pointer = to_pointer(link, :playlist)
|
94
96
|
|
95
|
-
|
96
|
-
|
97
|
+
subscribe_for_callbacks do |callbacks|
|
98
|
+
Spotify.playlist_remove_callbacks(pointer, callbacks, nil)
|
99
|
+
Spotify.playlist_add_callbacks(pointer, callbacks, nil)
|
100
|
+
end
|
97
101
|
end
|
98
102
|
|
99
103
|
# @return [Boolean] true if the playlist is loaded
|
@@ -167,8 +171,8 @@ module Hallon
|
|
167
171
|
def name=(name)
|
168
172
|
name = name.to_s.encode('UTF-8')
|
169
173
|
|
170
|
-
unless name.
|
171
|
-
raise ArgumentError, "name must be shorter than 256
|
174
|
+
unless name.bytesize < 256
|
175
|
+
raise ArgumentError, "name must be shorter than 256 bytes"
|
172
176
|
end
|
173
177
|
|
174
178
|
unless name =~ /[^ ]/u
|
@@ -64,6 +64,11 @@ module Hallon
|
|
64
64
|
end if other.is_a?(Folder)
|
65
65
|
end
|
66
66
|
|
67
|
+
# @return [Enumerator<Playlist, Folder>] contents of this folder
|
68
|
+
def contents
|
69
|
+
container.contents[(@begin + 1)..(@end - 1)]
|
70
|
+
end
|
71
|
+
|
67
72
|
# @return [Boolean] true if the folder has moved.
|
68
73
|
def moved?
|
69
74
|
Spotify.playlistcontainer_playlist_folder_id(container.pointer, @begin) != id or
|
@@ -71,7 +76,7 @@ module Hallon
|
|
71
76
|
end
|
72
77
|
end
|
73
78
|
|
74
|
-
|
79
|
+
extend Observable::PlaylistContainer
|
75
80
|
|
76
81
|
# Wrap an existing PlaylistContainer pointer in an object.
|
77
82
|
#
|
@@ -79,8 +84,10 @@ module Hallon
|
|
79
84
|
def initialize(pointer)
|
80
85
|
@pointer = to_pointer(pointer, :playlistcontainer)
|
81
86
|
|
82
|
-
|
83
|
-
|
87
|
+
subscribe_for_callbacks do |callbacks|
|
88
|
+
Spotify.playlistcontainer_remove_callbacks(pointer, callbacks, nil)
|
89
|
+
Spotify.playlistcontainer_add_callbacks(pointer, callbacks, nil)
|
90
|
+
end
|
84
91
|
end
|
85
92
|
|
86
93
|
# @return [Boolean] true if the container is loaded.
|
@@ -224,7 +231,7 @@ module Hallon
|
|
224
231
|
# @return [Boolean] true if the operation can be performed
|
225
232
|
def can_move?(from, to)
|
226
233
|
error = move_playlist(from, to, true)
|
227
|
-
|
234
|
+
_, symbol = Error.disambiguate(error)
|
228
235
|
symbol == :ok
|
229
236
|
end
|
230
237
|
|
data/lib/hallon/queue.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
module Hallon
|
5
|
+
# Hallon::Queue is a non-blocking (well, not entirely) sized FIFO queue.
|
6
|
+
#
|
7
|
+
# You initialize the queue with a `max_size`, and then push data to it.
|
8
|
+
# For every push operation, the Queue will tell you how much of your data
|
9
|
+
# it could consume. If the queue becomes full, it won’t accept any more
|
10
|
+
# data (and will return 0 on the #push operation) until you pull some data
|
11
|
+
# out of it with #pop.
|
12
|
+
#
|
13
|
+
# Hallon::Queue is useful for handling {Hallon::Observable::Session#music_delivery_callback}.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# queue = Hallon::Queue.new(4)
|
17
|
+
# queue.push([1, 2]) # => 2
|
18
|
+
# queue.push([3]) # => 1
|
19
|
+
# queue.push([4, 5, 6]) # => 1
|
20
|
+
# queue.push([5, 6]) # => 0
|
21
|
+
# queue.pop(1) # => [1]
|
22
|
+
# queue.push([5, 6]) # => 1
|
23
|
+
# queue.pop # => [2, 3, 4, 5]
|
24
|
+
class Queue
|
25
|
+
attr_reader :max_size
|
26
|
+
|
27
|
+
# @param [Integer] max_size
|
28
|
+
def initialize(max_size)
|
29
|
+
@mutex = Mutex.new
|
30
|
+
@condv = ConditionVariable.new
|
31
|
+
|
32
|
+
@max_size = max_size
|
33
|
+
@samples = []
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param [#take] data
|
37
|
+
# @return [Integer] how much of the data that was added to the queue
|
38
|
+
def push(samples)
|
39
|
+
synchronize do
|
40
|
+
can_accept = max_size - size
|
41
|
+
new_samples = samples.take(can_accept)
|
42
|
+
|
43
|
+
@samples.concat(new_samples)
|
44
|
+
@condv.signal
|
45
|
+
|
46
|
+
new_samples.size
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# @note If the queue is empty, this operation will block until data is available.
|
51
|
+
# @param [Integer] num_samples max number of samples to pop off the queue
|
52
|
+
# @return [Array] data, where data.size might be less than num_samples but never more
|
53
|
+
def pop(num_samples = max_size)
|
54
|
+
synchronize do
|
55
|
+
@condv.wait(@mutex) while @samples.empty?
|
56
|
+
@samples.shift(num_samples)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [Integer] number of samples in buffer
|
61
|
+
def size
|
62
|
+
@samples.size
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
# @yield (merely a wrapper over @mutex.synchronize)
|
67
|
+
def synchronize
|
68
|
+
@mutex.synchronize { return yield }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/hallon/search.rb
CHANGED
@@ -5,7 +5,7 @@ module Hallon
|
|
5
5
|
#
|
6
6
|
# @see http://developer.spotify.com/en/libspotify/docs/group__search.html
|
7
7
|
class Search < Base
|
8
|
-
|
8
|
+
extend Observable::Search
|
9
9
|
|
10
10
|
# @return [Array<Symbol>] a list of radio genres available for search
|
11
11
|
def self.genres
|
@@ -35,10 +35,11 @@ module Hallon
|
|
35
35
|
|
36
36
|
search = allocate
|
37
37
|
search.instance_eval do
|
38
|
-
|
39
|
-
|
38
|
+
subscribe_for_callbacks do |callback|
|
39
|
+
@pointer = Spotify.radio_search_create!(session.pointer, from_year, to_year, genres, callback, nil)
|
40
|
+
end
|
40
41
|
|
41
|
-
raise FFI::NullPointerError, "radio search failed" if
|
42
|
+
raise FFI::NullPointerError, "radio search failed" if pointer.null?
|
42
43
|
end
|
43
44
|
|
44
45
|
search
|
@@ -57,10 +58,12 @@ module Hallon
|
|
57
58
|
# @see http://developer.spotify.com/en/libspotify/docs/group__search.html#gacf0b5e902e27d46ef8b1f40e332766df
|
58
59
|
def initialize(query, options = {})
|
59
60
|
o = Search.defaults.merge(options)
|
60
|
-
@callback = proc { trigger(:load) }
|
61
|
-
@pointer = Spotify.search_create!(session.pointer, query, o[:tracks_offset].to_i, o[:tracks].to_i, o[:albums_offset].to_i, o[:albums].to_i, o[:artists_offset].to_i, o[:artists].to_i, @callback, nil)
|
62
61
|
|
63
|
-
|
62
|
+
subscribe_for_callbacks do |callback|
|
63
|
+
@pointer = Spotify.search_create!(session.pointer, query, o[:tracks_offset].to_i, o[:tracks].to_i, o[:albums_offset].to_i, o[:albums].to_i, o[:artists_offset].to_i, o[:artists].to_i, callback, nil)
|
64
|
+
end
|
65
|
+
|
66
|
+
raise FFI::NullPointerError, "search for “#{query}” failed" if pointer.null?
|
64
67
|
end
|
65
68
|
|
66
69
|
# @return [Boolean] true if the search has been fully loaded.
|
data/lib/hallon/session.rb
CHANGED
@@ -30,8 +30,8 @@ module Hallon
|
|
30
30
|
undef :instance
|
31
31
|
end
|
32
32
|
|
33
|
-
# Session
|
34
|
-
|
33
|
+
# We have Session callbacks that you can listen to!
|
34
|
+
extend Observable::Session
|
35
35
|
|
36
36
|
# Initializes the Spotify session. If you need to access the
|
37
37
|
# instance at a later time, you can use {instance}.
|
@@ -105,22 +105,23 @@ module Hallon
|
|
105
105
|
raise ArgumentError, "User-agent must be less than 256 bytes long"
|
106
106
|
end
|
107
107
|
|
108
|
-
# Set configuration, as well as callbacks
|
109
|
-
config = Spotify::SessionConfig.new
|
110
|
-
config[:api_version] = Hallon::API_VERSION
|
111
|
-
config.application_key = appkey
|
112
|
-
@options.each { |(key, value)| config.send(:"#{key}=", value) }
|
113
|
-
config[:callbacks] = Spotify::SessionCallbacks.create(self, @sp_callbacks = {})
|
114
|
-
|
115
108
|
# Default cache size is 0 (automatic)
|
116
109
|
@cache_size = 0
|
117
110
|
|
118
|
-
|
111
|
+
subscribe_for_callbacks do |callbacks|
|
112
|
+
config = Spotify::SessionConfig.new
|
113
|
+
config[:api_version] = Hallon::API_VERSION
|
114
|
+
config.application_key = appkey
|
115
|
+
@options.each { |(key, value)| config.send(:"#{key}=", value) }
|
116
|
+
config[:callbacks] = callbacks
|
117
|
+
|
118
|
+
instance_eval(&block) if block_given?
|
119
119
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
120
|
+
# You pass a pointer to the session pointer to libspotify >:)
|
121
|
+
FFI::MemoryPointer.new(:pointer) do |p|
|
122
|
+
Error::maybe_raise Spotify.session_create(config, p)
|
123
|
+
@pointer = p.read_pointer
|
124
|
+
end
|
124
125
|
end
|
125
126
|
end
|
126
127
|
|
@@ -154,7 +155,8 @@ module Hallon
|
|
154
155
|
def process_events_on(*events)
|
155
156
|
yield or protecting_handlers do
|
156
157
|
channel = SizedQueue.new(1)
|
157
|
-
|
158
|
+
block = proc { |*args| channel << args }
|
159
|
+
events.each { |event| on(event, &block) }
|
158
160
|
on(:notify_main_thread) { channel << :notify }
|
159
161
|
|
160
162
|
loop do
|
@@ -399,11 +401,6 @@ module Hallon
|
|
399
401
|
status == :offline
|
400
402
|
end
|
401
403
|
|
402
|
-
# @return [String] string representation of the Session.
|
403
|
-
def to_s
|
404
|
-
"<#{self.class.name}:0x#{object_id.to_s(16)} status=#{status} @options=#{options.inspect}>"
|
405
|
-
end
|
406
|
-
|
407
404
|
private
|
408
405
|
# Set starred status of given tracks.
|
409
406
|
#
|
@@ -426,7 +423,7 @@ module Hallon
|
|
426
423
|
# if the user does not have premium, libspotify will still fire logged_in as :ok,
|
427
424
|
# but a few moments later it fires connection_error; waiting for both and checking
|
428
425
|
# for errors on both hopefully circumvents this!
|
429
|
-
wait_for(:logged_in, :connection_error) do |
|
426
|
+
wait_for(:logged_in, :connection_error) do |error|
|
430
427
|
Error.maybe_raise(error)
|
431
428
|
session.logged_in?
|
432
429
|
end
|
data/lib/hallon/toplist.rb
CHANGED
@@ -6,7 +6,7 @@ module Hallon
|
|
6
6
|
#
|
7
7
|
# @see http://developer.spotify.com/en/libspotify/docs/group__toplist.html
|
8
8
|
class Toplist < Base
|
9
|
-
|
9
|
+
extend Observable::Toplist
|
10
10
|
|
11
11
|
# Create a Toplist browsing object.
|
12
12
|
#
|
@@ -36,8 +36,9 @@ module Hallon
|
|
36
36
|
region = to_country(region)
|
37
37
|
end
|
38
38
|
|
39
|
-
|
40
|
-
|
39
|
+
subscribe_for_callbacks do |callback|
|
40
|
+
@pointer = Spotify.toplistbrowse_create!(session.pointer, type, region, user, callback, nil)
|
41
|
+
end
|
41
42
|
end
|
42
43
|
|
43
44
|
# @return [Boolean] true if the toplist is loaded.
|
data/lib/hallon/user.rb
CHANGED
@@ -13,15 +13,15 @@ module Hallon
|
|
13
13
|
#
|
14
14
|
# @see http://developer.spotify.com/en/libspotify/docs/group__inbox.html
|
15
15
|
class Post < Base
|
16
|
-
|
16
|
+
extend Observable::Post
|
17
17
|
|
18
18
|
# @param [Spotify::Pointer<inbox>]
|
19
19
|
def initialize(username, message, tracks, &block)
|
20
|
-
|
20
|
+
ary = FFI::MemoryPointer.new(:pointer, tracks.length)
|
21
|
+
ary.write_array_of_pointer tracks.map(&:pointer)
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
@pointer = Spotify.inbox_post_tracks!(session.pointer, username, ary, tracks.length, message, @callback, nil)
|
23
|
+
subscribe_for_callbacks do |callback|
|
24
|
+
@pointer = Spotify.inbox_post_tracks!(session.pointer, username, ary, tracks.length, message, callback, nil)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
data/lib/hallon/version.rb
CHANGED
@@ -5,6 +5,10 @@ describe Hallon::AlbumBrowse do
|
|
5
5
|
Spotify.should_receive(:albumbrowse_create).and_return(null_pointer)
|
6
6
|
expect { mock_session { Hallon::AlbumBrowse.new(mock_album) } }.to raise_error(FFI::NullPointerError)
|
7
7
|
end
|
8
|
+
|
9
|
+
it "should raise an error given a non-album spotify pointer" do
|
10
|
+
expect { Hallon::AlbumBrowse.new(mock_artist) }.to raise_error(TypeError)
|
11
|
+
end
|
8
12
|
end
|
9
13
|
|
10
14
|
let(:browse) do
|
@@ -5,6 +5,10 @@ describe Hallon::ArtistBrowse do
|
|
5
5
|
Spotify.should_receive(:artistbrowse_create).and_return(null_pointer)
|
6
6
|
expect { mock_session { Hallon::ArtistBrowse.new(mock_artist) } }.to raise_error(FFI::NullPointerError)
|
7
7
|
end
|
8
|
+
|
9
|
+
it "should raise an error given a non-album spotify pointer" do
|
10
|
+
expect { Hallon::ArtistBrowse.new(mock_album) }.to raise_error(TypeError)
|
11
|
+
end
|
8
12
|
end
|
9
13
|
|
10
14
|
let(:browse) do
|
data/spec/hallon/base_spec.rb
CHANGED
@@ -2,34 +2,51 @@ describe Hallon::Base do
|
|
2
2
|
let(:klass) do
|
3
3
|
Class.new(Hallon::Base) do
|
4
4
|
def initialize(pointer)
|
5
|
-
@pointer = pointer
|
5
|
+
@pointer = to_pointer(pointer, :base) { |x| x }
|
6
6
|
end
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
+
let(:base_pointer) do
|
11
|
+
Spotify.stub!(:base_add_ref, :base_release)
|
12
|
+
Spotify::Pointer.new(a_pointer, :base, true)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#to_pointer" do
|
16
|
+
it "should not accept raw FFI pointers" do
|
17
|
+
expect { klass.new(a_pointer) }.to raise_error(TypeError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should raise an error if given an invalid pointer type" do
|
21
|
+
expect { klass.new(mock_album) }.to raise_error(TypeError)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
10
25
|
describe ".from" do
|
11
26
|
it "should return a new object if given pointer is not null" do
|
12
|
-
|
13
|
-
klass.from(a_pointer).should_not be_nil
|
27
|
+
klass.from(base_pointer).should_not be_nil
|
14
28
|
end
|
15
29
|
|
16
30
|
it "should return nil if given pointer is null" do
|
17
|
-
|
18
|
-
|
31
|
+
klass.from(null_pointer).should be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should return nil if given object is nil" do
|
35
|
+
klass.from(nil).should be_nil
|
19
36
|
end
|
20
37
|
end
|
21
38
|
|
22
39
|
describe "#==" do
|
23
40
|
it "should compare the pointers if applicable" do
|
24
|
-
one = klass.new(
|
25
|
-
two = klass.new(
|
41
|
+
one = klass.new(base_pointer)
|
42
|
+
two = klass.new(base_pointer)
|
26
43
|
|
27
44
|
one.should eq two
|
28
45
|
end
|
29
46
|
|
30
47
|
it "should fall back to default object comparison" do
|
31
|
-
one = klass.new(
|
32
|
-
two = klass.new(
|
48
|
+
one = klass.new(base_pointer)
|
49
|
+
two = klass.new(base_pointer)
|
33
50
|
two.stub(:respond_to?).and_return(false)
|
34
51
|
|
35
52
|
one.should_not eq two
|