hallon 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/.travis.yml +2 -0
  2. data/CHANGELOG +43 -0
  3. data/Gemfile +2 -0
  4. data/README.markdown +21 -13
  5. data/Rakefile +84 -23
  6. data/dev/login.rb +16 -0
  7. data/examples/adding_tracks_to_playlist.rb +49 -0
  8. data/examples/logging_in.rb +1 -6
  9. data/examples/show_published_playlists_of_user.rb +9 -19
  10. data/hallon.gemspec +1 -1
  11. data/lib/hallon.rb +3 -2
  12. data/lib/hallon/album.rb +55 -41
  13. data/lib/hallon/album_browse.rb +41 -37
  14. data/lib/hallon/artist.rb +30 -21
  15. data/lib/hallon/artist_browse.rb +59 -41
  16. data/lib/hallon/base.rb +68 -5
  17. data/lib/hallon/enumerator.rb +1 -0
  18. data/lib/hallon/error.rb +3 -0
  19. data/lib/hallon/ext/spotify.rb +169 -36
  20. data/lib/hallon/image.rb +30 -44
  21. data/lib/hallon/link.rb +29 -43
  22. data/lib/hallon/linkable.rb +68 -20
  23. data/lib/hallon/observable.rb +0 -1
  24. data/lib/hallon/player.rb +21 -7
  25. data/lib/hallon/playlist.rb +291 -0
  26. data/lib/hallon/playlist_container.rb +27 -0
  27. data/lib/hallon/search.rb +52 -45
  28. data/lib/hallon/session.rb +129 -81
  29. data/lib/hallon/toplist.rb +37 -19
  30. data/lib/hallon/track.rb +68 -45
  31. data/lib/hallon/user.rb +69 -33
  32. data/lib/hallon/version.rb +1 -1
  33. data/spec/hallon/album_browse_spec.rb +15 -9
  34. data/spec/hallon/album_spec.rb +15 -15
  35. data/spec/hallon/artist_browse_spec.rb +28 -9
  36. data/spec/hallon/artist_spec.rb +30 -14
  37. data/spec/hallon/enumerator_spec.rb +0 -1
  38. data/spec/hallon/hallon_spec.rb +20 -1
  39. data/spec/hallon/image_spec.rb +18 -41
  40. data/spec/hallon/link_spec.rb +10 -12
  41. data/spec/hallon/linkable_spec.rb +37 -18
  42. data/spec/hallon/player_spec.rb +8 -0
  43. data/spec/hallon/playlist_container_spec.rb +75 -0
  44. data/spec/hallon/playlist_spec.rb +204 -0
  45. data/spec/hallon/search_spec.rb +19 -16
  46. data/spec/hallon/session_spec.rb +61 -29
  47. data/spec/hallon/spotify_spec.rb +30 -0
  48. data/spec/hallon/toplist_spec.rb +22 -14
  49. data/spec/hallon/track_spec.rb +62 -21
  50. data/spec/hallon/user_spec.rb +47 -36
  51. data/spec/mockspotify.rb +35 -10
  52. data/spec/mockspotify/mockspotify_spec.rb +22 -0
  53. data/spec/spec_helper.rb +7 -3
  54. data/spec/support/common_objects.rb +91 -16
  55. data/spec/support/shared_for_linkable_objects.rb +39 -0
  56. metadata +30 -20
  57. data/Termfile +0 -7
  58. data/lib/hallon/synchronizable.rb +0 -32
  59. data/spec/hallon/synchronizable_spec.rb +0 -19
@@ -0,0 +1,75 @@
1
+ # coding: utf-8
2
+ describe Hallon::PlaylistContainer do
3
+ let(:container) { Hallon::PlaylistContainer.new(mock_container) }
4
+
5
+ subject { container }
6
+
7
+ it { should be_loaded }
8
+ its(:owner) { should eq Hallon::User.new("burgestrand") }
9
+
10
+ describe "#add" do
11
+ it "should create a new Playlist at the end of the playlist"
12
+ end
13
+
14
+ describe "#insert" do
15
+ it "should add the given Playlist to the given index"
16
+ it "should add the given Folder to the given index", :pending => true
17
+ end
18
+
19
+ describe "#remove" do
20
+ it "should remove the playlist at the given index"
21
+ it "should remove the matching :folder_end if removing a folder"
22
+ end
23
+
24
+ describe "#move" do
25
+ it "should move the entity at the given index"
26
+ end
27
+
28
+ describe "#contents" do
29
+ #
30
+ # (0) playlist: Hello
31
+ # (1) start_folder: Hi
32
+ # (2) playlist: inside Hi
33
+ # (3) start_folder: Ho
34
+ # (4) playlist: inside HiHo
35
+ # (5) end_folder
36
+ # (6) playlist: inside Hi2
37
+ # (7) end_folder
38
+ # (8) playlist: World
39
+ #
40
+ # … should become:
41
+ #
42
+ # (0) Playlist #1
43
+ # (1) Folder #1…#7
44
+ # (2) Playlist #2
45
+ # (3) Folder #3…#5
46
+ # (4) Playlist #4
47
+ # (5) Folder #3…#5
48
+ # (6) Playlist #6
49
+ # (7) Folder #1…#7
50
+ # (8) Playlist #8
51
+ #
52
+ it "should be a collection of folders and playlists"
53
+ end
54
+
55
+ describe "PlaylistContainer Playlists", :pending do
56
+ subject { container.contents[1] }
57
+
58
+ its(:name) { should be "Awesome playlist" }
59
+ end
60
+
61
+ describe Hallon::PlaylistContainer::Folder, :pending do
62
+ subject { container.contents[1] }
63
+
64
+ its(:id) { should be 1337 }
65
+ its(:name) { should be "Awesome folder" }
66
+
67
+ describe "#contents" do
68
+ it "should be a collection of folders and playlists"
69
+ end
70
+
71
+ describe "#rename" do
72
+ it "should rename the playlist container"
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,204 @@
1
+ require 'time'
2
+
3
+ describe Hallon::Playlist do
4
+ it_should_behave_like "a Linkable object" do
5
+ let(:spotify_uri) { "spotify:user:burgestrand:playlist:07AX9IY9Hqmj1RqltcG0fi" }
6
+ let(:described_class) do
7
+ real_session = session
8
+ Hallon::Playlist.dup.tap do |klass|
9
+ klass.class_eval do
10
+ define_method(:session) { real_session }
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ let(:playlist) { Hallon::Playlist.new(mock_playlist) }
17
+ subject { playlist }
18
+
19
+ it { should be_loaded }
20
+ it { should be_collaborative }
21
+ it { should_not be_pending }
22
+ it { mock_session { should be_in_ram } }
23
+ it { mock_session { should_not be_available_offline } }
24
+
25
+ its(:name) { should eq "Megaplaylist" }
26
+ its(:owner) { should eq Hallon::User.new(mock_user) }
27
+ its(:description) { should eq "Playlist description...?" }
28
+ its(:image) { mock_session { should eq Hallon::Image.new(mock_image_id) } }
29
+ its(:total_subscribers) { should eq 1000 }
30
+ its(:sync_progress) { mock_session { should eq 67 } }
31
+ its(:size) { should eq 4 }
32
+
33
+ its('tracks.size') { should eq 4 }
34
+ its('tracks.to_a') { should eq instantiate(Hallon::Playlist::Track, [mock_playlist, 0], [mock_playlist, 1], [mock_playlist, 2], [mock_playlist, 3]) }
35
+ describe "tracks#[]" do
36
+ subject { playlist.tracks[0] }
37
+
38
+ it { should be_seen }
39
+ its(:create_time) { should eq Time.parse("2009-11-04") }
40
+ its(:creator) { should eq Hallon::User.new(mock_user) }
41
+ its(:message) { should eq "message this, YO!" }
42
+ end
43
+
44
+ describe "#seen" do
45
+ it "should set the seen status of the track at the given index" do
46
+ track = playlist.tracks[0]
47
+ track.should be_seen
48
+
49
+ track = playlist.seen(0, false)
50
+ track.should_not be_seen
51
+
52
+ track = playlist.seen(0, true)
53
+ track.should be_seen
54
+ end
55
+ end
56
+
57
+ describe "#subscribers" do
58
+ it "should return an array of names for the subscribers" do
59
+ subject.subscribers.should eq %w[Kim Elin Ylva]
60
+ end
61
+
62
+ it "should return an empty array when there are no subscribers" do
63
+ Spotify.should_receive(:playlist_subscribers).and_return(mock_empty_subscribers)
64
+ subject.subscribers.should eq []
65
+ end
66
+ end
67
+
68
+ describe "#insert" do
69
+ let(:tracks) { instantiate(Hallon::Track, mock_track, mock_track_two) }
70
+
71
+ it "should add the given tracks to the playlist at correct index" do
72
+ old_tracks = playlist.tracks.to_a
73
+ new_tracks = old_tracks.insert(1, *tracks)
74
+ mock_session { playlist.insert(1, tracks) }
75
+
76
+ playlist.tracks.to_a.should eq new_tracks
77
+ end
78
+
79
+ it "should default to adding tracks at the end" do
80
+ mock_session { playlist.insert(tracks) }
81
+ playlist.tracks[2, 2].should eq tracks
82
+ end
83
+
84
+ it "should raise an error if the operation cannot be completed" do
85
+ expect { mock_session { playlist.insert(-1, nil) } }.to raise_error(Hallon::Error)
86
+ end
87
+ end
88
+
89
+ describe "#remove" do
90
+ it "should remove the tracks at the given indices" do
91
+ old_tracks = playlist.tracks.to_a
92
+ new_tracks = [old_tracks[0], old_tracks[2]]
93
+
94
+ playlist.remove(1, 3)
95
+ playlist.tracks.to_a.should eq new_tracks
96
+ end
97
+
98
+ it "should raise an error if the operation cannot be completed" do
99
+ expect { playlist.remove(-1) }.to raise_error(Hallon::Error)
100
+ end
101
+ end
102
+
103
+ describe "#move" do
104
+ it "should move the tracks at the given indices to their new location" do
105
+ old_tracks = playlist.tracks.to_a
106
+ new_tracks = [old_tracks[1], old_tracks[0], old_tracks[3], old_tracks[2]]
107
+
108
+ playlist.move(2, [0, 3])
109
+ playlist.tracks.to_a.should eq new_tracks
110
+ end
111
+
112
+ it "should raise an error if the operation cannot be completed" do
113
+ expect { playlist.move(-1, [-1]) }.to raise_error(Hallon::Error)
114
+ end
115
+ end
116
+
117
+ describe "#name=" do
118
+ it "should set the new playlist name" do
119
+ playlist.name.should eq "Megaplaylist"
120
+ playlist.name = "Monoplaylist"
121
+ playlist.name.should eq "Monoplaylist"
122
+ end
123
+
124
+ it "should fail given an empty name" do
125
+ expect { playlist.name = "" }.to raise_error(Hallon::Error)
126
+ end
127
+
128
+ it "should fail given a name of only spaces" do
129
+ expect { playlist.name = " " }.to raise_error(ArgumentError)
130
+ end
131
+
132
+ it "should fail given a too long name" do
133
+ expect { playlist.name = "a" * 256 }.to raise_error(ArgumentError)
134
+ end
135
+ end
136
+
137
+ describe "#collaborative=" do
138
+ it "should set the collaborative status" do
139
+ playlist.should be_collaborative
140
+ playlist.collaborative = false
141
+ playlist.should_not be_collaborative
142
+ end
143
+ end
144
+
145
+ describe "#autolink_tracks=" do
146
+ it "should set autolink status" do
147
+ Spotify.mocksp_playlist_get_autolink_tracks(playlist.pointer).should be_false
148
+ playlist.autolink_tracks = true
149
+ Spotify.mocksp_playlist_get_autolink_tracks(playlist.pointer).should be_true
150
+ end
151
+ end
152
+
153
+ describe "#in_ram=" do
154
+ it "should set in_ram status" do
155
+ mock_session do
156
+ playlist.should be_in_ram
157
+ playlist.in_ram = false
158
+ playlist.should_not be_in_ram
159
+ end
160
+ end
161
+ end
162
+
163
+ describe "#offline_mode=" do
164
+ it "should set offline mode" do
165
+ mock_session do
166
+ playlist.should_not be_available_offline
167
+ playlist.offline_mode = true
168
+ playlist.should be_available_offline
169
+ end
170
+ end
171
+ end
172
+
173
+ describe "#update_subscribers" do
174
+ it "should ask libspotify to update the subscribers" do
175
+ expect { mock_session { playlist.update_subscribers } }.to_not raise_error
176
+ end
177
+ end
178
+
179
+ describe "offline status methods" do
180
+ def symbol_for(number)
181
+ Spotify.enum_type(:playlist_offline_status)[number]
182
+ end
183
+
184
+ specify "#available_offline?" do
185
+ Spotify.should_receive(:playlist_get_offline_status).and_return symbol_for(1)
186
+ mock_session { should be_available_offline }
187
+ end
188
+
189
+ specify "#syncing?" do
190
+ Spotify.should_receive(:playlist_get_offline_status).and_return symbol_for(2)
191
+ mock_session { should be_syncing }
192
+ end
193
+
194
+ specify "#waiting?" do
195
+ Spotify.should_receive(:playlist_get_offline_status).and_return symbol_for(3)
196
+ mock_session { should be_waiting }
197
+ end
198
+
199
+ specify "#offline_mode?" do
200
+ Spotify.should_receive(:playlist_get_offline_status).and_return symbol_for(0)
201
+ mock_session { should_not be_offline_mode }
202
+ end
203
+ end
204
+ end
@@ -1,10 +1,8 @@
1
1
  describe Hallon::Search do
2
2
  subject { search }
3
3
  let(:search) do
4
- mock_session do
5
- Spotify.should_receive(:search_create).and_return(mock_search)
6
- Hallon::Search.new("my query")
7
- end
4
+ Spotify.registry_add 'spotify:search:my query', mock_search
5
+ mock_session { Hallon::Search.new("my query") }
8
6
  end
9
7
 
10
8
  describe ".new" do
@@ -26,6 +24,11 @@ describe Hallon::Search do
26
24
 
27
25
  mock_session { Hallon::Search.new("my query", my_params) }
28
26
  end
27
+
28
+ it "should raise an error if the search failed" do
29
+ Spotify.should_receive(:search_create).and_return(null_pointer)
30
+ expect { mock_session { Hallon::Search.new("omgwtfbbq") } }.to raise_error(/search (.*?) failed/)
31
+ end
29
32
  end
30
33
 
31
34
  describe ".genres" do
@@ -36,31 +39,31 @@ describe Hallon::Search do
36
39
  it { should_not be_empty }
37
40
  end
38
41
 
39
- describe ".search" do
42
+ describe ".radio" do
40
43
  subject do
41
- Spotify.should_receive(:radio_search_create).and_return(mock_search)
44
+ Spotify.registry_add 'spotify:radio:00002200:1990-2010', mock_search
45
+ mock_session { Hallon::Search.radio(1990..2010, :jazz, :punk) }
46
+ end
42
47
 
43
- mock_session do
44
- search = Hallon::Search.radio(1990..2010, :jazz, :punk)
45
- end
48
+ it "should raise an error on invalid genres" do
49
+ Spotify.should_not_receive(:radio_search_create)
50
+ expect { Hallon::Search.radio(1990..2010, :bogus, :jazz) }.to raise_error(ArgumentError, /bogus/)
46
51
  end
47
52
 
48
- it "should simply ignore invalid genres" do
49
- mock_session do
50
- Spotify.should_receive(:radio_search_create).and_return(mock_search)
51
- expect { Hallon::Search.radio(1990..2010, :bogus, :hocum) }.to_not raise_error
52
- end
53
+ it "should raise an error if the search failed" do
54
+ Spotify.should_receive(:radio_search_create).and_return(null_pointer)
55
+ expect { mock_session { Hallon::Search.radio(1990..1990) } }.to raise_error(/search failed/)
53
56
  end
54
57
 
55
58
  it { should be_loaded }
56
- its(:error) { should eq :ok }
59
+ its(:status) { should eq :ok }
57
60
  its('tracks.size') { should eq 2 }
58
61
  # ^ should be enough
59
62
  end
60
63
 
61
64
  it { should be_a Hallon::Observable }
62
65
  it { should be_loaded }
63
- its(:error) { should eq :ok }
66
+ its(:status) { should eq :ok }
64
67
  its(:query) { should eq "my query" }
65
68
  its(:did_you_mean) { should eq "another thing" }
66
69
 
@@ -16,6 +16,10 @@ describe Hallon::Session do
16
16
  Hallon.initialize
17
17
  }.to raise_error
18
18
  end
19
+
20
+ it "should succeed if everything is right" do
21
+ expect { Hallon::Session.initialize('appkey_good') }.to_not raise_error
22
+ end
19
23
  end
20
24
 
21
25
  describe ".new" do
@@ -47,6 +51,18 @@ describe Hallon::Session do
47
51
  its([:cache_playlist_metadata]) { should == true }
48
52
  end
49
53
 
54
+ describe "#container" do
55
+ it "should return the sessions’ playlist container" do
56
+ session.login 'burgestrand', 'pass'
57
+ session.container.should be_a Hallon::PlaylistContainer
58
+ session.container.owner.should eq session.user
59
+ end
60
+
61
+ it "should return nil if not logged in" do
62
+ session.container.should be_nil
63
+ end
64
+ end
65
+
50
66
  describe "#process_events" do
51
67
  it "should return the timeout" do
52
68
  session.process_events.should be_a Fixnum
@@ -64,13 +80,15 @@ describe Hallon::Session do
64
80
  else
65
81
  session.trigger(:bogus, :bogus)
66
82
  end
83
+
84
+ 0
67
85
  end
68
86
 
69
87
  session.process_events_on(:bogus) { |e| e == :bogus }.should be_true
70
88
  end
71
89
 
72
90
  it "should time out if waiting for events too long" do
73
- session.should_receive(:process_events).once # and do nothing
91
+ session.should_receive(:process_events).once.and_return(1) # and do nothing
74
92
  session.wait_for(:ever) { |x| x }.should eq :timeout
75
93
  end
76
94
 
@@ -120,6 +138,17 @@ describe Hallon::Session do
120
138
  end
121
139
  end
122
140
 
141
+ describe "#user" do
142
+ it "should return the logged in user" do
143
+ session.login 'Kim', 'pass'
144
+ session.user.name.should eq 'Kim'
145
+ end
146
+
147
+ it "should return nil if not logged in" do
148
+ session.user.should be_nil
149
+ end
150
+ end
151
+
123
152
  describe "#country" do
124
153
  it "should retrieve the current sessions’ country as a string" do
125
154
  session.country.should eq 'SE'
@@ -144,25 +173,6 @@ describe Hallon::Session do
144
173
  end
145
174
  end
146
175
 
147
- describe "#friends" do
148
- let(:session) { mock_session_object }
149
-
150
- it "should be an enumerator" do
151
- session.friends.should be_an Hallon::Enumerator
152
- end
153
-
154
- it "should be empty if not logged in" do
155
- Spotify.should_not_receive(:session_num_friends)
156
- session.should_receive(:logged_in?).and_return(false)
157
- session.friends.size.should eq 0
158
- end
159
-
160
- it "should be an enumerator of friends" do
161
- session.login 'Kim', 'pass'
162
- session.friends[0].should eq Hallon::User.new(mock_user)
163
- end
164
- end
165
-
166
176
  describe "#cache_size" do
167
177
  it "should default to 0" do
168
178
  session.cache_size.should eq 0
@@ -201,12 +211,12 @@ describe Hallon::Session do
201
211
  end
202
212
 
203
213
  describe "#connection_rules=" do
204
- it "should not fail given an invalid rule" do
205
- expect { session.connection_rules = :lawly }.to_not raise_error
214
+ it "should fail given an invalid rule" do
215
+ expect { session.connection_rules = :lawly }.to raise_error
206
216
  end
207
217
 
208
218
  it "should succeed given correct connection thingy" do
209
- expect { session.connection_rules = :allow_network, :allow_sync_over_mobile }.to_not raise_error
219
+ expect { session.connection_rules = :network, :allow_sync_over_mobile }.to_not raise_error
210
220
  end
211
221
 
212
222
  it "should combine given rules and feed to libspotify" do
@@ -249,15 +259,37 @@ describe Hallon::Session do
249
259
  end
250
260
  end
251
261
 
252
- context "when logged in", :logged_in => true do
253
- it "should be logged in" do
262
+
263
+ describe "#starred" do
264
+ let(:starred) { mock_session { Hallon::Playlist.new("spotify:user:burgestrand:starred") } }
265
+
266
+ it "should return the sessions (current users) starred playlist" do
267
+ session.login 'burgestrand', 'pass'
268
+
254
269
  session.should be_logged_in
270
+ session.starred.should eq starred
255
271
  end
256
272
 
257
- describe "#relation_type?" do
258
- it "should retrieve the relation between the current user and given user" do
259
- session.relation_type?(session.user).should eq :none
260
- end
273
+ it "should return nil if not logged in" do
274
+ session.should_not be_logged_in
275
+ session.starred.should be_nil
276
+ end
277
+ end
278
+
279
+ describe "#inbox" do
280
+ let(:inbox) { Hallon::Playlist.new(mock_playlist) }
281
+ let(:session) { mock_session_object }
282
+
283
+ it "should return the sessions inbox" do
284
+ session.login 'burgestrand', 'pass'
285
+
286
+ session.should be_logged_in
287
+ session.inbox.should eq inbox
288
+ end
289
+
290
+ it "should return nil if not logged in" do
291
+ session.should_not be_logged_in
292
+ session.inbox.should be_nil
261
293
  end
262
294
  end
263
295
  end