hallon 0.13.0 → 0.14.0
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/CHANGELOG.md +43 -1
- data/Gemfile +0 -2
- data/LICENSE.txt +1 -1
- data/README.markdown +94 -44
- data/examples/playing_audio.rb +4 -5
- data/lib/hallon.rb +20 -0
- data/lib/hallon/album.rb +13 -12
- data/lib/hallon/album_browse.rb +1 -0
- data/lib/hallon/artist.rb +13 -12
- data/lib/hallon/artist_browse.rb +1 -0
- data/lib/hallon/base.rb +2 -0
- data/lib/hallon/image.rb +18 -10
- data/lib/hallon/loadable.rb +24 -0
- data/lib/hallon/observable.rb +1 -1
- data/lib/hallon/observable/playlist.rb +10 -16
- data/lib/hallon/observable/playlist_container.rb +12 -6
- data/lib/hallon/player.rb +3 -3
- data/lib/hallon/playlist.rb +34 -11
- data/lib/hallon/playlist_container.rb +10 -4
- data/lib/hallon/search.rb +1 -0
- data/lib/hallon/session.rb +2 -2
- data/lib/hallon/toplist.rb +17 -12
- data/lib/hallon/track.rb +1 -0
- data/lib/hallon/user.rb +48 -11
- data/lib/hallon/version.rb +1 -1
- data/spec/hallon/album_browse_spec.rb +2 -0
- data/spec/hallon/album_spec.rb +14 -7
- data/spec/hallon/artist_browse_spec.rb +2 -0
- data/spec/hallon/artist_spec.rb +14 -8
- data/spec/hallon/hallon_spec.rb +12 -0
- data/spec/hallon/image_spec.rb +18 -9
- data/spec/hallon/loadable_spec.rb +46 -0
- data/spec/hallon/observable/playlist_spec.rb +11 -5
- data/spec/hallon/observable_spec.rb +6 -0
- data/spec/hallon/playlist_container_spec.rb +6 -0
- data/spec/hallon/playlist_spec.rb +21 -4
- data/spec/hallon/search_spec.rb +2 -0
- data/spec/hallon/toplist_spec.rb +40 -23
- data/spec/hallon/track_spec.rb +2 -0
- data/spec/hallon/user_post_spec.rb +75 -0
- data/spec/hallon/user_spec.rb +7 -11
- data/spec/spec_helper.rb +2 -2
- metadata +20 -16
- data/examples/audio_driver.rb +0 -55
data/lib/hallon/version.rb
CHANGED
data/spec/hallon/album_spec.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
#
|
2
3
|
describe Hallon::Album do
|
4
|
+
it { should be_a Hallon::Loadable }
|
5
|
+
|
3
6
|
it_should_behave_like "a Linkable object" do
|
4
7
|
let(:spotify_uri) { "spotify:album:1xvnWMz2PNFf7mXOSRuLws" }
|
5
8
|
end
|
@@ -28,21 +31,25 @@ describe Hallon::Album do
|
|
28
31
|
end
|
29
32
|
end
|
30
33
|
|
31
|
-
describe "cover" do
|
34
|
+
describe "#cover" do
|
32
35
|
it "should be nil if there is no image" do
|
33
36
|
Spotify.should_receive(:album_cover).and_return(null_pointer)
|
34
37
|
album.cover.should be_nil
|
35
|
-
|
36
|
-
Spotify.should_receive(:link_create_from_album_cover).and_return(null_pointer)
|
37
|
-
album.cover(false).should be_nil
|
38
38
|
end
|
39
39
|
|
40
40
|
it "should be an image if it exists" do
|
41
|
-
|
41
|
+
stub_session { album.cover.should eq Hallon::Image.new(mock_image_id) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#cover_link" do
|
46
|
+
it "should be nil if there is no image" do
|
47
|
+
Spotify.should_receive(:link_create_from_album_cover).and_return(null_pointer)
|
48
|
+
album.cover_link.should be_nil
|
42
49
|
end
|
43
50
|
|
44
|
-
it "should be a link if
|
45
|
-
album.
|
51
|
+
it "should be a link if it exists" do
|
52
|
+
album.cover_link.should eq Hallon::Link.new("spotify:image:3ad93423add99766e02d563605c6e76ed2b0e450")
|
46
53
|
end
|
47
54
|
end
|
48
55
|
|
data/spec/hallon/artist_spec.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
describe Hallon::Artist do
|
3
|
+
it { should be_a Hallon::Loadable }
|
4
|
+
|
3
5
|
it_should_behave_like "a Linkable object" do
|
4
6
|
let(:spotify_uri) { "spotify:artist:3bftcFwl4vqRNNORRsqm1G" }
|
5
7
|
end
|
@@ -29,20 +31,24 @@ describe Hallon::Artist do
|
|
29
31
|
describe "#portrait" do
|
30
32
|
let(:link) { Hallon::Link.new(mock_image_uri) }
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
+
it "should be nil if an image is not available" do
|
35
|
+
Spotify.should_receive(:artist_portrait).and_return(null_pointer)
|
36
|
+
artist.portrait.should be_nil
|
34
37
|
end
|
35
38
|
|
36
|
-
|
37
|
-
artist.portrait
|
39
|
+
it "should be an image if it exists" do
|
40
|
+
stub_session { artist.portrait.should eq Hallon::Image.new(mock_image_id) }
|
38
41
|
end
|
42
|
+
end
|
39
43
|
|
44
|
+
describe "#portrait_link" do
|
40
45
|
it "should be nil if an image is not available" do
|
41
|
-
Spotify.should_receive(:artist_portrait).and_return(null_pointer)
|
42
|
-
artist.portrait.should be_nil
|
43
|
-
|
44
46
|
Spotify.should_receive(:link_create_from_artist_portrait).and_return(null_pointer)
|
45
|
-
artist.
|
47
|
+
artist.portrait_link.should be_nil
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should be a link if it exists" do
|
51
|
+
artist.portrait_link.should eq Hallon::Link.new(mock_image_uri)
|
46
52
|
end
|
47
53
|
end
|
48
54
|
end
|
data/spec/hallon/hallon_spec.rb
CHANGED
@@ -18,4 +18,16 @@ describe Hallon do
|
|
18
18
|
it { should match uri }
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
describe "#load_timeout" do
|
23
|
+
it "should raise an error given a negative timeout" do
|
24
|
+
expect { Hallon.load_timeout = -1 }.to raise_error(ArgumentError)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should allow setting and retrieving the value" do
|
28
|
+
Hallon.load_timeout.should eq 5
|
29
|
+
Hallon.load_timeout = 0.2
|
30
|
+
Hallon.load_timeout.should eq 0.2
|
31
|
+
end
|
32
|
+
end
|
21
33
|
end
|
data/spec/hallon/image_spec.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
require 'ostruct'
|
3
3
|
|
4
4
|
describe Hallon::Image do
|
5
|
+
it { described_class.should include Hallon::Loadable }
|
6
|
+
|
5
7
|
it_should_behave_like "a Linkable object" do
|
6
8
|
let(:spotify_uri) { "spotify:image:#{mock_image_hex}" }
|
7
9
|
let(:custom_object) { mock_image_hex }
|
@@ -24,9 +26,16 @@ describe Hallon::Image do
|
|
24
26
|
its(:status) { should be :ok }
|
25
27
|
its(:format) { should be :jpeg }
|
26
28
|
|
27
|
-
describe "id" do
|
28
|
-
|
29
|
-
|
29
|
+
describe "#id" do
|
30
|
+
it "should return the image id as a hexadecimal string" do
|
31
|
+
image.id.should eq mock_image_hex
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#raw_id" do
|
36
|
+
it "should return the image id as a binary string" do
|
37
|
+
image.raw_id.should eq mock_image_id
|
38
|
+
end
|
30
39
|
end
|
31
40
|
|
32
41
|
describe "#data" do
|
@@ -51,17 +60,17 @@ describe Hallon::Image do
|
|
51
60
|
it "should compare ids (but only if other is an Image)" do
|
52
61
|
other = double
|
53
62
|
other.should_receive(:is_a?).with(Hallon::Image).and_return(true)
|
54
|
-
other.should_receive(:
|
63
|
+
other.should_receive(:raw_id).and_return(image.raw_id)
|
55
64
|
|
56
|
-
image.should
|
57
|
-
image.should_not
|
65
|
+
image.should eq other
|
66
|
+
image.should_not eq double
|
58
67
|
end
|
59
68
|
|
60
69
|
it "should not call #id if other is not an image" do
|
61
|
-
|
62
|
-
|
70
|
+
other = double
|
71
|
+
other.should_not_receive(:raw_id)
|
63
72
|
|
64
|
-
image.should_not eq
|
73
|
+
image.should_not eq other
|
65
74
|
end
|
66
75
|
end
|
67
76
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
describe Hallon::Loadable do
|
4
|
+
let(:session) { double(:session, :process_events => 5) }
|
5
|
+
let(:loadable) do
|
6
|
+
_session = session
|
7
|
+
Class.new do
|
8
|
+
include Hallon::Loadable
|
9
|
+
|
10
|
+
define_method(:session) { _session }
|
11
|
+
end.new
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#load" do
|
15
|
+
it "should timeout if the object does not load in time" do
|
16
|
+
Hallon.stub(:load_timeout).and_return(0.001)
|
17
|
+
loadable.stub(:loaded?).and_return(false)
|
18
|
+
expect { loadable.load }.to raise_error(Hallon::TimeoutError)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should use the Hallon.load_timeout by default" do
|
22
|
+
Hallon.should_receive(:load_timeout).and_return(0.075)
|
23
|
+
Timeout.should_receive(:timeout).with(0.075, Hallon::TimeoutError).and_yield
|
24
|
+
loadable.stub(:loaded?).and_return(true)
|
25
|
+
loadable.load
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should return the object in question on success" do
|
29
|
+
loadable.stub(:loaded?).and_return(true)
|
30
|
+
loadable.load.should eq loadable
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should raise an error when status is an error" do
|
34
|
+
session.should_receive(:process_events).once
|
35
|
+
loadable.stub(:loaded?).and_return(false)
|
36
|
+
loadable.stub(:status).and_return(:other_permanent)
|
37
|
+
expect { loadable.load }.to raise_error(Hallon::Error)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should not raise an error when status is_loading" do
|
41
|
+
loadable.stub(:loaded?).and_return(false, true)
|
42
|
+
loadable.stub(:status).and_return(:is_loading)
|
43
|
+
loadable.load.should eq loadable
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -1,7 +1,13 @@
|
|
1
1
|
describe Hallon::Observable::Playlist do
|
2
2
|
let(:trackpointers_size) { 2 }
|
3
|
+
let(:track_index_pointers) do
|
4
|
+
tracks = FFI::MemoryPointer.new(:pointer, trackpointers_size)
|
5
|
+
tracks.write_array_of_int([0, 1])
|
6
|
+
tracks
|
7
|
+
end
|
8
|
+
|
3
9
|
let(:trackpointers) do
|
4
|
-
tracks = FFI::MemoryPointer.new(:pointer,
|
10
|
+
tracks = FFI::MemoryPointer.new(:pointer, trackpointers_size)
|
5
11
|
tracks.write_array_of_pointer([mock_track, mock_track_two])
|
6
12
|
tracks
|
7
13
|
end
|
@@ -16,13 +22,13 @@ describe Hallon::Observable::Playlist do
|
|
16
22
|
end
|
17
23
|
|
18
24
|
specification_for_callback "tracks_removed" do
|
19
|
-
let(:input) { [a_pointer,
|
20
|
-
let(:output) { [
|
25
|
+
let(:input) { [a_pointer, track_index_pointers, trackpointers_size, :userdata] }
|
26
|
+
let(:output) { [[0, 1], subject] }
|
21
27
|
end
|
22
28
|
|
23
29
|
specification_for_callback "tracks_moved" do
|
24
|
-
let(:input) { [a_pointer,
|
25
|
-
let(:output) { [
|
30
|
+
let(:input) { [a_pointer, track_index_pointers, trackpointers_size, 7, :userdata] }
|
31
|
+
let(:output) { [[0, 1], 7, subject] }
|
26
32
|
end
|
27
33
|
|
28
34
|
specification_for_callback "playlist_renamed" do
|
@@ -136,6 +136,12 @@ describe Hallon::Observable do
|
|
136
136
|
expect { subject.send(:subscribe_for_callbacks) {} }.to raise_error(ArgumentError)
|
137
137
|
end
|
138
138
|
|
139
|
+
it "should do nothing if the result is a null pointer" do
|
140
|
+
klass.should_not_receive(:subscribe)
|
141
|
+
klass.any_instance.stub(:pointer).and_return(FFI::Pointer::NULL)
|
142
|
+
subject.send(:subscribe_for_callbacks) {}
|
143
|
+
end
|
144
|
+
|
139
145
|
it "should always yield the *same* object" do
|
140
146
|
a = klass.new
|
141
147
|
b = klass.new
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
describe Hallon::PlaylistContainer do
|
3
|
+
it { should be_a Hallon::Loadable }
|
4
|
+
|
3
5
|
let(:container) { Hallon::PlaylistContainer.new(mock_container) }
|
4
6
|
|
5
7
|
subject { container }
|
@@ -18,6 +20,10 @@ describe Hallon::PlaylistContainer do
|
|
18
20
|
container.contents[-1].should eq playlist
|
19
21
|
end.to change{ container.size }.by(1)
|
20
22
|
end
|
23
|
+
|
24
|
+
it "should raise an error if the name is invalid" do
|
25
|
+
expect { container.add(" ") }.to raise_error(ArgumentError)
|
26
|
+
end
|
21
27
|
end
|
22
28
|
|
23
29
|
context "given a string that’s a valid spotify playlist uri" do
|
@@ -2,9 +2,11 @@
|
|
2
2
|
require 'time'
|
3
3
|
|
4
4
|
describe Hallon::Playlist do
|
5
|
+
it { should be_a Hallon::Loadable }
|
6
|
+
|
5
7
|
it_should_behave_like "a Linkable object" do
|
6
8
|
let(:spotify_uri) { "spotify:user:burgestrand:playlist:07AX9IY9Hqmj1RqltcG0fi" }
|
7
|
-
let(:described_class) { Hallon::Playlist
|
9
|
+
let(:described_class) { stub_session(Hallon::Playlist) }
|
8
10
|
end
|
9
11
|
|
10
12
|
subject { playlist }
|
@@ -12,6 +14,20 @@ describe Hallon::Playlist do
|
|
12
14
|
Hallon::Playlist.new(mock_playlist)
|
13
15
|
end
|
14
16
|
|
17
|
+
describe ".invalid_name?" do
|
18
|
+
it "should return false if the name is valid" do
|
19
|
+
Hallon::Playlist.invalid_name?("Moo").should be_false
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should return an error message when the name is blank" do
|
23
|
+
Hallon::Playlist.invalid_name?(" ").should match "blank"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return an error message when the name is too long" do
|
27
|
+
Hallon::Playlist.invalid_name?("Moo" * 256).should match "bytes"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
15
31
|
it { should be_loaded }
|
16
32
|
it { should be_collaborative }
|
17
33
|
it { should_not be_pending }
|
@@ -108,8 +124,9 @@ describe Hallon::Playlist do
|
|
108
124
|
playlist.tracks.to_a.should eq new_tracks
|
109
125
|
end
|
110
126
|
|
111
|
-
it "should raise an error if
|
112
|
-
expect { playlist.remove(-1) }.to raise_error(
|
127
|
+
it "should raise an error if given invalid parameters" do
|
128
|
+
expect { playlist.remove(-1) }.to raise_error(ArgumentError)
|
129
|
+
expect { playlist.remove(playlist.size) }.to raise_error(ArgumentError)
|
113
130
|
end
|
114
131
|
end
|
115
132
|
|
@@ -135,7 +152,7 @@ describe Hallon::Playlist do
|
|
135
152
|
end
|
136
153
|
|
137
154
|
it "should fail given an empty name" do
|
138
|
-
expect { playlist.name = "" }.to raise_error(
|
155
|
+
expect { playlist.name = "" }.to raise_error(ArgumentError)
|
139
156
|
end
|
140
157
|
|
141
158
|
it "should fail given a name of only spaces" do
|
data/spec/hallon/search_spec.rb
CHANGED
data/spec/hallon/toplist_spec.rb
CHANGED
@@ -1,23 +1,56 @@
|
|
1
1
|
describe Hallon::Toplist do
|
2
|
+
it { should be_a Hallon::Loadable }
|
3
|
+
|
2
4
|
let(:toplist) do
|
3
5
|
Spotify.registry_add 'spotify:toplist:artists:everywhere', mock_toplistbrowse
|
4
6
|
mock_session { Hallon::Toplist.new(:artists) }
|
5
7
|
end
|
6
|
-
|
7
8
|
subject { toplist }
|
8
9
|
|
9
10
|
it { should be_a Hallon::Observable }
|
11
|
+
|
12
|
+
describe ".new" do
|
13
|
+
it "should fail given an invalid type" do
|
14
|
+
expect { stub_session { Hallon::Toplist.new(:invalid_type) } }.to raise_error(ArgumentError, /invalid enum value/)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should pass the username given a string to libspotify" do
|
18
|
+
Spotify.registry_add 'spotify:toplist:user:Kim:tracks', mock_toplistbrowse
|
19
|
+
stub_session { Hallon::Toplist.new(:tracks, "Kim").should be_loaded }
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should pass the correct region to libspotify" do
|
23
|
+
Spotify.registry_add 'spotify:toplist:tracks:SE', mock_toplistbrowse
|
24
|
+
mock_session { Hallon::Toplist.new(:tracks, :se).should be_loaded }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
10
28
|
it { should be_loaded }
|
11
29
|
its(:status) { should eq :ok }
|
12
30
|
|
13
|
-
|
14
|
-
|
31
|
+
describe "#type" do
|
32
|
+
it "should be the same as the type given to .new" do
|
33
|
+
toplist = mock_session { Hallon::Toplist.new(:tracks, :se) }
|
34
|
+
toplist.type.should eq :tracks
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#results" do
|
39
|
+
it "should return an enumerator of the correct type" do
|
40
|
+
toplist.should_receive(:type).and_return(:artists)
|
41
|
+
toplist.results.to_a.should eq instantiate(Hallon::Artist, mock_artist, mock_artist_two)
|
42
|
+
end
|
15
43
|
|
16
|
-
|
17
|
-
|
44
|
+
it "should return an enumerator of the correct type" do
|
45
|
+
toplist.should_receive(:type).and_return(:albums)
|
46
|
+
toplist.results.to_a.should eq instantiate(Hallon::Album, mock_album)
|
47
|
+
end
|
18
48
|
|
19
|
-
|
20
|
-
|
49
|
+
it "should return an enumerator of the correct type" do
|
50
|
+
toplist.should_receive(:type).and_return(:tracks)
|
51
|
+
toplist.results.to_a.should eq instantiate(Hallon::Track, mock_track, mock_track_two)
|
52
|
+
end
|
53
|
+
end
|
21
54
|
|
22
55
|
describe "#request_duration" do
|
23
56
|
it "should return the request duration in seconds" do
|
@@ -29,20 +62,4 @@ describe Hallon::Toplist do
|
|
29
62
|
toplist.request_duration.should be_nil
|
30
63
|
end
|
31
64
|
end
|
32
|
-
|
33
|
-
describe ".new" do
|
34
|
-
it "should fail given an invalid type" do
|
35
|
-
expect { mock_session { Hallon::Toplist.new(:invalid_type) } }.to raise_error(ArgumentError, /invalid enum value/)
|
36
|
-
end
|
37
|
-
|
38
|
-
it "should pass the username given a string to libspotify" do
|
39
|
-
Spotify.registry_add 'spotify:toplist:user:Kim', mock_toplistbrowse
|
40
|
-
mock_session { Hallon::Toplist.new(:tracks, "Kim").should be_loaded }
|
41
|
-
end
|
42
|
-
|
43
|
-
it "should pass the correct region to libspotify" do
|
44
|
-
Spotify.registry_add 'spotify:toplist:tracks:SE', mock_toplistbrowse
|
45
|
-
mock_session { Hallon::Toplist.new(:tracks, :se).should be_loaded }
|
46
|
-
end
|
47
|
-
end
|
48
65
|
end
|