hallon 0.8.0 → 0.9.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/.travis.yml +2 -0
- data/CHANGELOG +43 -0
- data/Gemfile +2 -0
- data/README.markdown +21 -13
- data/Rakefile +84 -23
- data/dev/login.rb +16 -0
- data/examples/adding_tracks_to_playlist.rb +49 -0
- data/examples/logging_in.rb +1 -6
- data/examples/show_published_playlists_of_user.rb +9 -19
- data/hallon.gemspec +1 -1
- data/lib/hallon.rb +3 -2
- data/lib/hallon/album.rb +55 -41
- data/lib/hallon/album_browse.rb +41 -37
- data/lib/hallon/artist.rb +30 -21
- data/lib/hallon/artist_browse.rb +59 -41
- data/lib/hallon/base.rb +68 -5
- data/lib/hallon/enumerator.rb +1 -0
- data/lib/hallon/error.rb +3 -0
- data/lib/hallon/ext/spotify.rb +169 -36
- data/lib/hallon/image.rb +30 -44
- data/lib/hallon/link.rb +29 -43
- data/lib/hallon/linkable.rb +68 -20
- data/lib/hallon/observable.rb +0 -1
- data/lib/hallon/player.rb +21 -7
- data/lib/hallon/playlist.rb +291 -0
- data/lib/hallon/playlist_container.rb +27 -0
- data/lib/hallon/search.rb +52 -45
- data/lib/hallon/session.rb +129 -81
- data/lib/hallon/toplist.rb +37 -19
- data/lib/hallon/track.rb +68 -45
- data/lib/hallon/user.rb +69 -33
- data/lib/hallon/version.rb +1 -1
- data/spec/hallon/album_browse_spec.rb +15 -9
- data/spec/hallon/album_spec.rb +15 -15
- data/spec/hallon/artist_browse_spec.rb +28 -9
- data/spec/hallon/artist_spec.rb +30 -14
- data/spec/hallon/enumerator_spec.rb +0 -1
- data/spec/hallon/hallon_spec.rb +20 -1
- data/spec/hallon/image_spec.rb +18 -41
- data/spec/hallon/link_spec.rb +10 -12
- data/spec/hallon/linkable_spec.rb +37 -18
- data/spec/hallon/player_spec.rb +8 -0
- data/spec/hallon/playlist_container_spec.rb +75 -0
- data/spec/hallon/playlist_spec.rb +204 -0
- data/spec/hallon/search_spec.rb +19 -16
- data/spec/hallon/session_spec.rb +61 -29
- data/spec/hallon/spotify_spec.rb +30 -0
- data/spec/hallon/toplist_spec.rb +22 -14
- data/spec/hallon/track_spec.rb +62 -21
- data/spec/hallon/user_spec.rb +47 -36
- data/spec/mockspotify.rb +35 -10
- data/spec/mockspotify/mockspotify_spec.rb +22 -0
- data/spec/spec_helper.rb +7 -3
- data/spec/support/common_objects.rb +91 -16
- data/spec/support/shared_for_linkable_objects.rb +39 -0
- metadata +30 -20
- data/Termfile +0 -7
- data/lib/hallon/synchronizable.rb +0 -32
- data/spec/hallon/synchronizable_spec.rb +0 -19
data/spec/hallon/album_spec.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
describe Hallon::Album do
|
3
|
-
|
3
|
+
it_should_behave_like "a Linkable object" do
|
4
|
+
let(:spotify_uri) { "spotify:album:1xvnWMz2PNFf7mXOSRuLws" }
|
5
|
+
end
|
6
|
+
|
7
|
+
let(:album) { Hallon::Album.new(mock_album) }
|
8
|
+
subject { album }
|
4
9
|
|
5
10
|
its(:name) { should eq "Finally Woken" }
|
6
|
-
its(:
|
11
|
+
its(:release_year) { should be 2004 }
|
7
12
|
its(:type) { should be :single }
|
8
|
-
|
9
13
|
its(:browse) do
|
10
|
-
mock_session
|
11
|
-
Spotify.should_receive(:albumbrowse_create).exactly(2).times.and_return(mock_albumbrowse)
|
12
|
-
should eq Hallon::AlbumBrowse.new(mock_album)
|
13
|
-
end
|
14
|
+
mock_session { should eq Hallon::AlbumBrowse.new(album) }
|
14
15
|
end
|
15
16
|
|
16
17
|
it { should be_available }
|
@@ -19,27 +20,26 @@ describe Hallon::Album do
|
|
19
20
|
describe "artist" do
|
20
21
|
it "should be nil if there is no artist" do
|
21
22
|
Spotify.should_receive(:album_artist).and_return(null_pointer)
|
22
|
-
|
23
|
+
album.artist.should be_nil
|
23
24
|
end
|
24
25
|
|
25
26
|
it "should be an artist if it exists" do
|
26
|
-
|
27
|
+
album.artist.should eq Hallon::Artist.new(mock_artist)
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
30
31
|
describe "cover" do
|
31
32
|
it "should be nil if there is no image" do
|
32
33
|
Spotify.should_receive(:album_cover).and_return(null_pointer)
|
33
|
-
|
34
|
+
album.cover.should be_nil
|
34
35
|
end
|
35
36
|
|
36
37
|
it "should be an image if it exists" do
|
37
|
-
|
38
|
-
|
38
|
+
mock_session { album.cover.id.should eq mock_image_hex }
|
39
|
+
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
end
|
41
|
+
it "should be a link if requested" do
|
42
|
+
album.cover(false).to_str.should eq "spotify:image:3ad93423add99766e02d563605c6e76ed2b0e450"
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
@@ -1,22 +1,23 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
describe Hallon::ArtistBrowse do
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
Spotify.should_receive(:artistbrowse_create).and_return(mock_artistbrowse)
|
7
|
-
Hallon::ArtistBrowse.new(artist)
|
8
|
-
end
|
3
|
+
let(:browse) do
|
4
|
+
artist = Hallon::Artist.new(mock_artist)
|
5
|
+
mock_session { Hallon::ArtistBrowse.new(artist) }
|
9
6
|
end
|
10
7
|
|
8
|
+
subject { browse }
|
9
|
+
|
11
10
|
it { should be_loaded }
|
12
|
-
its(:
|
11
|
+
its(:status) { should eq :ok }
|
13
12
|
its(:artist) { should eq Hallon::Artist.new(mock_artist) }
|
14
13
|
|
15
14
|
its('portraits.size') { should eq 2 }
|
16
15
|
its('portraits.to_a') do
|
17
|
-
|
16
|
+
mock_session(2) { subject.map{ |img| img.id(true) }.should eq [mock_image_id, mock_image_id] }
|
17
|
+
end
|
18
18
|
|
19
|
-
|
19
|
+
specify 'portraits(false)' do
|
20
|
+
browse.portraits(false)[0].should eq Hallon::Link.new(mock_image_link)
|
20
21
|
end
|
21
22
|
|
22
23
|
its('tracks.size') { should eq 2 }
|
@@ -26,4 +27,22 @@ describe Hallon::ArtistBrowse do
|
|
26
27
|
its('similar_artists.size') { should eq 2 }
|
27
28
|
its('similar_artists.to_a') { should eq [mock_artist, mock_artist_two].map{ |p| Hallon::Artist.new(p) } }
|
28
29
|
its(:biography) { should eq 'grew up in DA BLOCK' }
|
30
|
+
|
31
|
+
describe "#request_duration" do
|
32
|
+
it "should return the request duration in seconds" do
|
33
|
+
browse.request_duration.should eq 2.751
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should be nil if the request was fetched from local cache" do
|
37
|
+
Spotify.should_receive(:artistbrowse_backend_request_duration).and_return(-1)
|
38
|
+
browse.request_duration.should be_nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '.types' do
|
43
|
+
subject { Hallon::ArtistBrowse.types }
|
44
|
+
|
45
|
+
it { should be_an Array }
|
46
|
+
it { should include :full }
|
47
|
+
end
|
29
48
|
end
|
data/spec/hallon/artist_spec.rb
CHANGED
@@ -1,32 +1,48 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
describe Hallon::Artist do
|
3
|
-
|
3
|
+
it_should_behave_like "a Linkable object" do
|
4
|
+
let(:spotify_uri) { "spotify:artist:3bftcFwl4vqRNNORRsqm1G" }
|
5
|
+
end
|
6
|
+
|
7
|
+
let(:artist) { Hallon::Artist.new(mock_artist) }
|
8
|
+
subject { artist }
|
4
9
|
|
5
10
|
it { should be_loaded }
|
6
11
|
its(:name) { should eq "Jem" }
|
7
|
-
its(:browse) do
|
8
|
-
Hallon::Session.should_receive(:instance).exactly(2).times.and_return(session)
|
9
|
-
Spotify.should_receive(:artistbrowse_create).exactly(2).times.and_return(mock_artistbrowse)
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
+
describe "#browse" do
|
14
|
+
it "should return an artist browsing object" do
|
15
|
+
mock_session(2) { subject.browse.should eq Hallon::ArtistBrowse.new(mock_artist) }
|
16
|
+
end
|
13
17
|
|
14
|
-
|
15
|
-
|
16
|
-
|
18
|
+
it "should default to full browsing" do
|
19
|
+
Hallon::ArtistBrowse.should_receive(:new).with(artist.pointer, :full)
|
20
|
+
artist.browse
|
21
|
+
end
|
17
22
|
|
18
|
-
|
19
|
-
Hallon::
|
20
|
-
|
23
|
+
it "should pass the browsing type along when creating the artist browsing object" do
|
24
|
+
Hallon::ArtistBrowse.should_receive(:new).with(artist.pointer, :no_tracks)
|
25
|
+
artist.browse(:no_tracks)
|
21
26
|
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#portrait" do
|
30
|
+
let(:link) { Hallon::Link.new(mock_image_uri) }
|
22
31
|
|
23
32
|
specify "as an image" do
|
24
33
|
Hallon::Session.should_receive(:instance).twice.and_return(session)
|
25
|
-
|
34
|
+
|
35
|
+
subject.portrait.should eq Hallon::Image.new(mock_image_id)
|
26
36
|
end
|
27
37
|
|
28
38
|
specify "as a link" do
|
29
|
-
subject.portrait(false).should eq
|
39
|
+
subject.portrait(false).should eq Hallon::Link.new(mock_image_uri)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should be nil if an image is not available" do
|
43
|
+
Spotify.should_receive(:artist_portrait).and_return(null_pointer)
|
44
|
+
|
45
|
+
subject.portrait.should be_nil
|
30
46
|
end
|
31
47
|
end
|
32
48
|
end
|
data/spec/hallon/hallon_spec.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
describe Hallon do
|
2
3
|
describe "VERSION" do
|
3
4
|
specify { Hallon::VERSION.should be_a String }
|
4
5
|
end
|
5
6
|
|
6
7
|
describe "API_VERSION" do
|
7
|
-
specify { Hallon::API_VERSION.should ==
|
8
|
+
specify { Hallon::API_VERSION.should == 10 }
|
8
9
|
end
|
9
10
|
|
10
11
|
describe "API_BUILD" do
|
@@ -17,4 +18,22 @@ describe Hallon do
|
|
17
18
|
it { should match uri }
|
18
19
|
end
|
19
20
|
end
|
21
|
+
|
22
|
+
describe "object callbacks" do
|
23
|
+
pending <<-REASON
|
24
|
+
|
25
|
+
Once callbacks are implemented in libmockspotify, we should also
|
26
|
+
test them on the following objects:
|
27
|
+
|
28
|
+
- Session
|
29
|
+
- Image
|
30
|
+
- AlbumBrowse
|
31
|
+
- ArtistBrowse
|
32
|
+
- Search
|
33
|
+
- Playlist
|
34
|
+
- PlaylistContainer
|
35
|
+
- Toplist
|
36
|
+
- Inbox
|
37
|
+
REASON
|
38
|
+
end
|
20
39
|
end
|
data/spec/hallon/image_spec.rb
CHANGED
@@ -1,8 +1,25 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
require 'ostruct'
|
3
|
+
|
2
4
|
describe Hallon::Image do
|
5
|
+
it_should_behave_like "a Linkable object" do
|
6
|
+
let(:spotify_uri) { "spotify:image:#{mock_image_hex}" }
|
7
|
+
let(:custom_object) { mock_image_hex }
|
8
|
+
|
9
|
+
|
10
|
+
let(:described_class) do
|
11
|
+
real_session = session
|
12
|
+
Hallon::Image.dup.tap do |klass|
|
13
|
+
klass.class_eval do
|
14
|
+
define_method(:session) { real_session }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
3
20
|
describe "an image instance" do
|
4
21
|
subject { image }
|
5
|
-
let(:image) {
|
22
|
+
let(:image) { Hallon::Image.new(mock_image) }
|
6
23
|
|
7
24
|
it { should be_loaded }
|
8
25
|
its(:status) { should be :ok }
|
@@ -21,7 +38,6 @@ describe Hallon::Image do
|
|
21
38
|
end
|
22
39
|
|
23
40
|
it "should have a binary encoding" do
|
24
|
-
pending "ruby 1.8 does not support String#encoding" unless subject.respond_to?(:encoding)
|
25
41
|
subject.encoding.name.should eq 'ASCII-8BIT'
|
26
42
|
end
|
27
43
|
end
|
@@ -43,43 +59,4 @@ describe Hallon::Image do
|
|
43
59
|
end
|
44
60
|
end
|
45
61
|
end
|
46
|
-
|
47
|
-
describe "instantiation" do
|
48
|
-
subject do
|
49
|
-
mock_session do
|
50
|
-
Hallon::Image.new(image_uri)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
context "created from an url" do
|
55
|
-
let(:image_uri) { "http://open.spotify.com/image/c78f091482e555bd2ffacfcd9cbdc0714b221663" }
|
56
|
-
its(:id) { should eq "c78f091482e555bd2ffacfcd9cbdc0714b221663" }
|
57
|
-
end
|
58
|
-
|
59
|
-
context "created from an uri" do
|
60
|
-
let(:image_uri) { "spotify:image:c78f091482e555bd2ffacfcd9cbdc0714b221663" }
|
61
|
-
its(:id) { should eq "c78f091482e555bd2ffacfcd9cbdc0714b221663" }
|
62
|
-
end
|
63
|
-
|
64
|
-
context "created from an id" do
|
65
|
-
let(:image_uri) { mock_image_id }
|
66
|
-
its(:id) { should eq mock_image_hex }
|
67
|
-
end
|
68
|
-
|
69
|
-
context "created from a link" do
|
70
|
-
let(:image_uri) { Hallon::Link.new("spotify:image:c78f091482e555bd2ffacfcd9cbdc0714b221663") }
|
71
|
-
its(:id) { should eq "c78f091482e555bd2ffacfcd9cbdc0714b221663" }
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
describe "callbacks" do
|
76
|
-
it "should trigger :load when loaded", :pending => true do
|
77
|
-
uri = "spotify:image:c78f091482e555bd2ffacfcd9cbdc0714b221663"
|
78
|
-
image = Hallon::Image.new(uri)
|
79
|
-
image.should_not be_loaded
|
80
|
-
image.should_receive(:trigger).with(:load).once
|
81
|
-
|
82
|
-
session.process_events_on { image.loaded? }
|
83
|
-
end
|
84
|
-
end
|
85
62
|
end
|
data/spec/hallon/link_spec.rb
CHANGED
@@ -2,35 +2,33 @@ describe Hallon::Link do
|
|
2
2
|
subject { Hallon::Link.new("spotify:user:burgestrand") }
|
3
3
|
|
4
4
|
context "class methods" do
|
5
|
-
subject { described_class }
|
6
|
-
|
7
5
|
describe "::new" do
|
8
6
|
it "should raise an ArgumentError on an invalid link" do
|
9
|
-
expect {
|
7
|
+
expect { Hallon::Link.new("omgwtfbbq") }.to raise_error(ArgumentError, /omgwtfbbq/)
|
10
8
|
end
|
11
9
|
|
12
10
|
it "should not raise error on valid links" do
|
13
|
-
expect {
|
11
|
+
expect { Hallon::Link.new("spotify:user:burgestrand") }.to_not raise_error
|
14
12
|
end
|
15
13
|
|
16
14
|
it "should accept an FFI pointer" do
|
17
|
-
expect {
|
15
|
+
expect { Hallon::Link.new(Spotify::Pointer.new(null_pointer, :link, false)) }.to raise_error(ArgumentError, /is not a valid spotify link/)
|
18
16
|
end
|
19
17
|
|
20
|
-
it "should
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
it "should raise an error when no session instance is about" do
|
19
|
+
# this is due to a bug in libspotify, it will segfault otherwise
|
20
|
+
Hallon::Session.stub(:instance?).and_return(false)
|
21
|
+
expect { Hallon::Link.new("spotify:user:burgestrand") }.to raise_error(/session/i)
|
24
22
|
end
|
25
23
|
end
|
26
24
|
|
27
25
|
describe "::valid?" do
|
28
26
|
it "should be true for a valid link" do
|
29
|
-
|
27
|
+
Hallon::Link.valid?("spotify:user:burgestrand").should be_true
|
30
28
|
end
|
31
29
|
|
32
30
|
it "should be false for an invalid link" do
|
33
|
-
|
31
|
+
Hallon::Link.valid?("omgwtfbbq").should be_false
|
34
32
|
end
|
35
33
|
end
|
36
34
|
end
|
@@ -84,7 +82,7 @@ describe Hallon::Link do
|
|
84
82
|
end
|
85
83
|
|
86
84
|
it "should compare underlying pointers if #to_str is unavailable" do
|
87
|
-
object = Hallon::Link.new(
|
85
|
+
object = Hallon::Link.new(subject.pointer)
|
88
86
|
|
89
87
|
def object.respond_to?(o)
|
90
88
|
return false if o == :to_str
|
@@ -10,40 +10,59 @@ describe Hallon::Linkable do
|
|
10
10
|
let(:object) { klass.new }
|
11
11
|
let(:pointer) { FFI::Pointer.new(1) }
|
12
12
|
|
13
|
-
before(:each) { Spotify.stub(:link_as_search) }
|
13
|
+
before(:each) { Spotify.stub(:link_as_search!) }
|
14
14
|
|
15
15
|
it "should define the #from_link method" do
|
16
|
-
object.
|
17
|
-
|
18
|
-
|
16
|
+
object.respond_to?(:from_link, true).should be_false
|
17
|
+
|
18
|
+
klass.instance_eval do
|
19
|
+
from_link(:as_search)
|
20
|
+
end
|
21
|
+
|
22
|
+
object.respond_to?(:from_link, true).should be_true
|
19
23
|
end
|
20
24
|
|
21
25
|
describe "#from_link" do
|
22
26
|
it "should call the appropriate Spotify function" do
|
23
|
-
Spotify.should_receive(:link_as_search).and_return(pointer)
|
27
|
+
Spotify.should_receive(:link_as_search!).and_return(pointer)
|
28
|
+
|
29
|
+
klass.instance_eval do
|
30
|
+
from_link(:as_search)
|
31
|
+
end
|
24
32
|
|
25
|
-
|
26
|
-
object.from_link 'spotify:search:moo'
|
33
|
+
object.send(:from_link, 'spotify:search:moo')
|
27
34
|
end
|
28
35
|
|
29
36
|
it "should call the given block if necessary" do
|
30
|
-
Spotify.should_not_receive(:link_as_search)
|
37
|
+
Spotify.should_not_receive(:link_as_search!)
|
38
|
+
|
39
|
+
called = false
|
40
|
+
pointer = double(:null? => false)
|
31
41
|
|
32
|
-
|
33
|
-
|
34
|
-
|
42
|
+
klass.instance_eval do
|
43
|
+
from_link(:as_search) do
|
44
|
+
called = true
|
45
|
+
pointer
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
expect { object.send(:from_link, 'spotify:search:whatever') }.to change { called }
|
35
50
|
end
|
36
51
|
|
37
52
|
it "should pass extra parameters to the defining block" do
|
38
53
|
passed_args = nil
|
39
|
-
klass.from_link(:search) { |link, *args| passed_args = args and pointer }
|
40
|
-
object.from_link("spotify:search:burgestrand", :cool, 5)
|
41
|
-
passed_args.should eq [:cool, 5]
|
42
|
-
end
|
43
54
|
|
44
|
-
|
45
|
-
|
46
|
-
|
55
|
+
pointer = double(:null? => false)
|
56
|
+
|
57
|
+
klass.instance_eval do
|
58
|
+
from_link(:search) do |link, *args|
|
59
|
+
passed_args = args
|
60
|
+
pointer
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
object.send(:from_link, "spotify:search:burgestrand", :cool, 5)
|
65
|
+
passed_args.should eq [:cool, 5]
|
47
66
|
end
|
48
67
|
end
|
49
68
|
end
|
data/spec/hallon/player_spec.rb
CHANGED
@@ -70,4 +70,12 @@ describe Hallon::Player do
|
|
70
70
|
player.seek(1)
|
71
71
|
end
|
72
72
|
end
|
73
|
+
|
74
|
+
describe "#volume_normalization" do
|
75
|
+
it "should be settable and gettable" do
|
76
|
+
player.volume_normalization?.should be_false
|
77
|
+
player.volume_normalization = true
|
78
|
+
player.volume_normalization?.should be_true
|
79
|
+
end
|
80
|
+
end
|
73
81
|
end
|