turntabler 0.1.4 → 0.2.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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # master
2
2
 
3
+ ## 0.2.0 / 2013-02-16
4
+
5
+ * Respect the keepalive update interval from API responses
6
+ * Add official support for trigger custom events
7
+ * Rename RoomDirectory#list to RoomDirectory#all
8
+ * Add full support for playlists API
9
+ * Fix Modlist example not sending messages to the room
10
+
3
11
  ## 0.1.4 / 2012-01-08
4
12
 
5
13
  * Fix Bop / ChatBot / Switch examples not working
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Aaron Pfeifer
1
+ Copyright (c) 2013 Aaron Pfeifer
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -50,6 +50,7 @@ At a high level, this project features:
50
50
  * HTTP / Web Socket interface implementations
51
51
  * Room state / user list management
52
52
  * DSL syntax support
53
+ * Custom events
53
54
 
54
55
  Turntable features include management of:
55
56
 
@@ -117,40 +118,47 @@ Turntabler.run do
117
118
  end
118
119
 
119
120
  # Authorized user interactions
120
- user = client.user # => #<Turntabler::AuthorizedUser:0x95631b0 @email="ben.zelano@gmail.com" ...>
121
- user.fan_of # => [#<Turntabler::User:0x95ccb38 @id="d5616b31654e8b22a7a1eef0">, ...]
122
- user.fans # => [#<Turntabler::User:0x95dd1cc @id="d5616b31654e8b22a7a1eef0">, ...]
123
- user.playlist.songs # => [#<Turntabler::Song:0x9610b44 @album="Abbey Road" ...>, ...]
124
- user.blocks # => [#<Turntabler::User:0x9792724 @id="19125d4da3b09562b2cf68b6">, ...]
125
- user.buddies # => [#<Turntabler::User:0x9792580 @id="efff38aeb7b9334164c1b630">, ...]
121
+ user = client.user # => #<Turntabler::AuthorizedUser @email="ben.zelano@gmail.com" ...>
122
+ user.fan_of # => [#<Turntabler::User @id="d5616b31654e8b22a7a1eef0">, ...]
123
+ user.fans # => [#<Turntabler::User @id="d5616b31654e8b22a7a1eef0">, ...]
124
+ user.playlist.songs # => [#<Turntabler::Song @album="Abbey Road" ...>, ...]
125
+ user.blocks # => [#<Turntabler::User @id="19125d4da3b09562b2cf68b6">, ...]
126
+ user.buddies # => [#<Turntabler::User @id="efff38aeb7b9334164c1b630">, ...]
126
127
 
127
128
  # Room Directory
128
- client.rooms.list(:favorites => true) # => []
129
- client.rooms.list(:genre => :rock) # => [#<Turntabler::Room:0x985d474 @id="4e4986bb14169c5f241318a6", ...>, ...]
130
- client.rooms.list(:genre => :rock, :available_djs => true, :minimum_listeners => 5)
131
- # => [#<Turntabler::Room:0x91c1d04 @id="4f4a5874a3f75128aa006c17", ...>, ...]
132
- client.rooms.with_friends # => [#<Turntabler::Room:0x9674c98 @id="50b4c1e2df5bcf4af666f876", ...>, ...]
133
- client.room('4dff1eac14169c565800892e').listeners # => #<Set: {#<Turntabler::User:0x96340bc @id="4e1341e2a3f75114d003c591" ...>, ...}>
129
+ client.rooms.all(:favorites => true) # => []
130
+ client.rooms.all(:genre => :rock) # => [#<Turntabler::Room @id="4e4986bb14169c5f241318a6", ...>, ...]
131
+ client.rooms.all(:genre => :rock, :available_djs => true, :minimum_listeners => 5)
132
+ # => [#<Turntabler::Room @id="4f4a5874a3f75128aa006c17", ...>, ...]
133
+ client.rooms.with_friends # => [#<Turntabler::Room @id="50b4c1e2df5bcf4af666f876", ...>, ...]
134
+ client.room('4dff1eac14169c565800892e').listeners # => #<Set: {#<Turntabler::User @id="4e1341e2a3f75114d003c591" ...>, ...}>
134
135
 
135
136
  # Room interaction
136
137
  client.rooms.create("My Test Room #{rand}").enter # => true
137
- room = client.room # => #<Turntabler::Room:0x99a16dc @name="My Test Room 0.24300857307298018" ...>
138
+ room = client.room # => #<Turntabler::Room @name="My Test Room 0.24300857307298018" ...>
138
139
  room.add_as_favorite # => true
139
140
  room.become_dj # => true
140
141
  room.say "Hey guys!" # => true
141
142
 
142
143
  # User interaction
143
- listeners = room.listeners # => #<Set: {#<Turntabler::User:0x95631b0 @id="309ba75b6385b83e110923bd" ..., ...}>
144
+ listeners = room.listeners # => #<Set: {#<Turntabler::User @id="309ba75b6385b83e110923bd" ..., ...}>
144
145
  listeners.each do |listener|
145
- listener.messages # => [#<Turntabler::Message:0x99aa1ec @content="Hey man!" ...>, ...]
146
+ listener.messages # => [#<Turntabler::Message @content="Hey man!" ...>, ...]
146
147
  listener.website # => "http://mypersonalwebsite.com"
147
148
  listener.facebook_url # => "https://www.facebook.com/firstname.lastname"
148
- listener.sticker_placements # => [#<Turntabler::StickerPlacement:0x9861024 @angle=0 ...>, ...]
149
+ listener.sticker_placements # => [#<Turntabler::StickerPlacement @angle=0 ...>, ...]
149
150
  listener.say "Welcome to the room!" # => true
150
151
  end
151
152
 
153
+ # Playlist interaction
154
+ client.user.playlists.all # => [#<Turntabler::Playlist @id="default" ...>, ...]
155
+ client.user.playlists.create("rock") # => #<Turntabler::Playlist @id="rock" ...>
156
+ client.user.playlist # => #<Turntabler::Playlist @id="default" ...>
157
+ client.user.playlist("rock").activate # => true
158
+ client.user.playlist("rock").songs # => []
159
+
152
160
  # Songs
153
- songs = client.search_song('Rolling Stones') # => [#<Turntabler::Song:0x983c198 @album="Tattoo You (2009 Remaster)" ...>, ...]
161
+ songs = client.search_song('Rolling Stones') # => [#<Turntabler::Song @album="Tattoo You (2009 Remaster)" ...>, ...]
154
162
  songs.each do
155
163
  song.enqueue # => true
156
164
  end
@@ -171,6 +179,43 @@ can do with turntabler. For a *complete* list, see the API documentation, espec
171
179
  For additional examples, see the [examples](https://github.com/obrie/turntabler/tree/master/examples)
172
180
  directory in the repository.
173
181
 
182
+ ### Custom events
183
+
184
+ In addition to the default Turntable events supported out of the box, turntabler
185
+ also allows you to define your own events. This is particularly useful in cases
186
+ where you may want to provide extensions on top of the turntabler library for
187
+ others to use. These extensions may be higher-order events, such as a user
188
+ reaching their maximum play count for a turn or a user timing out.
189
+
190
+ For example:
191
+
192
+ ```ruby
193
+ require 'turntabler'
194
+
195
+ EMAIL = ENV['EMAIL']
196
+ PASSWORD = ENV['PASSWORD']
197
+
198
+ # Register custom events
199
+ Turntabler.events :user_greeted
200
+
201
+ Turntabler.run do
202
+ client = Turntabler::Client.new(EMAIL, PASSWORD)
203
+
204
+ # Events
205
+ client.on :user_spoke do |message|
206
+ if (message.content =~ /^\/hello$/)
207
+ # Trigger the custom event
208
+ client.trigger(:user_greeted, message.sender)
209
+ end
210
+ end
211
+
212
+ # Handle custom event
213
+ client.on :user_greeted do |user|
214
+ client.room.say "Hey! How are you #{message.sender.name}?"
215
+ end
216
+ end
217
+ ```
218
+
174
219
  ## Additional Topics
175
220
 
176
221
  ### Differences with existing libraries
@@ -332,7 +377,7 @@ Notice that in this example the syntax is essentially the same except that we're
332
377
  one level out and need to interact directly with the `Turntabler::Client`
333
378
  instance itself.
334
379
 
335
- ## Usage
380
+ ## Deployment
336
381
 
337
382
  ### Web Server Usage
338
383
 
@@ -17,7 +17,7 @@ GEM
17
17
  faye-websocket (0.4.6)
18
18
  eventmachine (>= 0.12.0)
19
19
  http_parser.rb (0.5.3)
20
- turntabler (0.1.4)
20
+ turntabler (0.2.0)
21
21
  em-http-request
22
22
  em-synchrony
23
23
  faye-websocket
data/examples/modlist.rb CHANGED
@@ -13,7 +13,7 @@ TT.run(EMAIL, PASSWORD, :room => ROOM) do
13
13
  on :user_spoke do |message|
14
14
  # Response to "/mod" command
15
15
  if moderator_ids.include?(message.sender.id) && message.content =~ /^\/mod$/
16
- user.say("Yo #{message.sender.name}, it looks like you are a bot moderator!")
16
+ room.say("Yo #{message.sender.name}, it looks like you are a bot moderator!")
17
17
  end
18
18
  end
19
19
  end
@@ -1,4 +1,4 @@
1
- require 'turntabler/playlist'
1
+ require 'turntabler/playlist_directory'
2
2
  require 'turntabler/preferences'
3
3
  require 'turntabler/user'
4
4
  require 'turntabler/sticker_placement'
@@ -32,6 +32,10 @@ module Turntabler
32
32
  # @return [String]
33
33
  attribute :password
34
34
 
35
+ # The user's custom playlists
36
+ # @return [Turntabler::PlaylistDirectory]
37
+ attr_reader :playlists
38
+
35
39
  # The user's current Turntable preferences
36
40
  # @return [Turntabler::Preferences]
37
41
  attr_reader :preferences
@@ -39,7 +43,7 @@ module Turntabler
39
43
  # @api private
40
44
  def initialize(client, *)
41
45
  @status = 'available'
42
- @playlists = {}
46
+ @playlists = PlaylistDirectory.new(client)
43
47
  @preferences = Preferences.new(client)
44
48
  super
45
49
  end
@@ -192,7 +196,7 @@ module Turntabler
192
196
  # user.playlist # => #<Turntabler::Playlist id="default" ...>
193
197
  # user.playlist("rock") # => #<Turntabler::Playlist id="rock" ...>
194
198
  def playlist(id = 'default')
195
- @playlists[id] ||= Playlist.new(client, :_id => id)
199
+ playlists.build(:_id => id)
196
200
  end
197
201
 
198
202
  # Gets the stickers that have been purchased by this user.
@@ -245,8 +249,10 @@ module Turntabler
245
249
  def update_status(status = self.status)
246
250
  assert_valid_values(status, *%w(available unavailable away))
247
251
 
248
- api('presence.update', :status => status)
252
+ result = api('presence.update', :status => status)
253
+ client.reset_keepalive(result['interval']) if result['interval']
249
254
  self.attributes = {'status' => status}
255
+
250
256
  true
251
257
  end
252
258
  end
@@ -99,7 +99,7 @@ module Turntabler
99
99
 
100
100
  # Create a new connection to the given url
101
101
  @connection = Connection.new(url, :timeout => timeout, :params => {:clientid => id, :userid => user.id, :userauth => user.auth})
102
- @connection.handler = lambda {|data| on_message(data)}
102
+ @connection.handler = lambda {|data| trigger(data.delete('command'), data)}
103
103
  @connection.start
104
104
 
105
105
  # Wait until the connection is authenticated
@@ -116,8 +116,8 @@ module Turntabler
116
116
  # @return [true]
117
117
  def close(allow_reconnect = false)
118
118
  if @connection
119
- @update_timer.cancel if @update_timer
120
- @update_timer = nil
119
+ @keepalive_timer.cancel if @keepalive_timer
120
+ @keepalive_timer = nil
121
121
  @connection.close
122
122
 
123
123
  wait do |&resume|
@@ -439,21 +439,64 @@ module Turntabler
439
439
  songs || raise(Error, 'Search failed to complete')
440
440
  end
441
441
 
442
- # Callback when a message has been received from Turntable. This will run
443
- # any handlers registered for the event associated with the message.
442
+ # Triggers callback handlers for the given Turntable command. This should
443
+ # either be invoked when responses are received for Turntable or when
444
+ # triggering custom events.
444
445
  #
445
- # @api private
446
- # @param [Hash<String, Object>] data The message data received
447
- # @return nil
448
- def on_message(data)
449
- if Event.command?(data['command'])
450
- event = Event.new(self, data)
446
+ # @note If the command is unknown, it will simply get skipped and not raise an exception
447
+ # @param [Symbol] command The name of the command triggered. This is typically the same name as the event.
448
+ # @param [Array] args The arguments to be processed by the event
449
+ # @return [true]
450
+ #
451
+ # == Triggering custom events
452
+ #
453
+ # After defining custom events, `trigger` can be used to invoke any handler
454
+ # that's been registered for that event. The argument list passed into
455
+ # `trigger` will be passed, exactly as specified, to the registered
456
+ # handlers.
457
+ #
458
+ # @example
459
+ # # Define names of events
460
+ # Turntabler.events(:no_args, :one_arg, :multiple_args)
461
+ #
462
+ # # ...
463
+ #
464
+ # # Register handlers
465
+ # client.on(:no_args) { }
466
+ # client.on(:one_arg) {|arg| }
467
+ # client.on(:multiple_args) {|arg1, arg2| }
468
+ #
469
+ # # Trigger handlers registered for events
470
+ # client.trigger(:no_args) # => true
471
+ # client.trigger(:one_arg, 1) # => true
472
+ # client.trigger(:multiple_args, 1, 2) # => true
473
+ def trigger(command, *args)
474
+ command = command.to_sym if command
475
+
476
+ if Event.command?(command)
477
+ event = Event.new(self, command, args)
451
478
  handlers = @event_handlers[event.name] || []
452
479
  handlers.each do |handler|
453
480
  success = handler.run(event)
454
481
  handlers.delete(handler) if success && handler.once
455
482
  end
456
483
  end
484
+
485
+ true
486
+ end
487
+
488
+ # Resets the keepalive timer to run at the given interval.
489
+ #
490
+ # @param [Fixnum] interval The frequency with which keepalives get sent (in seconds)
491
+ # @api private
492
+ def reset_keepalive(interval = 10)
493
+ if !@keepalive_timer || @keepalive_interval != interval
494
+ @keepalive_interval = interval
495
+
496
+ # Periodically update the user's status to remain available
497
+ @keepalive_timer.cancel if @keepalive_timer
498
+ @keepalive_timer = EM::Synchrony.add_periodic_timer(interval) { user.update(:status => user.status) }
499
+ end
457
500
  end
458
501
 
459
502
  private
@@ -470,10 +513,7 @@ module Turntabler
470
513
  user.authenticate
471
514
  user.fan_of
472
515
  user.update(:status => user.status)
473
-
474
- # Periodically update the user's status to remain available
475
- @update_timer.cancel if @update_timer
476
- @update_timer = EM::Synchrony.add_periodic_timer(10) { user.update(:status => user.status) }
516
+ reset_keepalive
477
517
  end
478
518
 
479
519
  # Callback when the session has ended. This will automatically reconnect if
@@ -488,7 +528,7 @@ module Turntabler
488
528
  if @reconnect && allow_reconnect
489
529
  EM::Synchrony.add_timer(@reconnect_wait) do
490
530
  room ? room.enter : connect(url)
491
- on_message('command' => 'reconnected')
531
+ trigger(:reconnected)
492
532
  end
493
533
  end
494
534
  end
@@ -35,7 +35,7 @@ module Turntabler
35
35
  # @param [String] command The command to check for the existence of
36
36
  # @return [Boolean] +true+ if the command exists, otherwise +false+
37
37
  def command?(command)
38
- command && commands.include?(command.to_sym)
38
+ commands.include?(command)
39
39
  end
40
40
  end
41
41
 
@@ -79,7 +79,7 @@ module Turntabler
79
79
  data['user'].map do |attrs|
80
80
  user = room.build_user(attrs)
81
81
  room.listeners << user
82
- user
82
+ [user]
83
83
  end
84
84
  end
85
85
 
@@ -88,7 +88,7 @@ module Turntabler
88
88
  data['user'].map do |attrs|
89
89
  user = room.build_user(attrs)
90
90
  room.listeners.delete(user)
91
- user
91
+ [user]
92
92
  end
93
93
  end
94
94
 
@@ -122,7 +122,8 @@ module Turntabler
122
122
  handle :dj_added, :add_dj do
123
123
  new_djs = []
124
124
  data['user'].each_with_index do |attrs, index|
125
- new_djs << user = room.build_user(attrs.merge('placements' => data['placements'][index]))
125
+ user = room.build_user(attrs.merge('placements' => data['placements'][index]))
126
+ new_djs << [user]
126
127
  room.djs << user
127
128
  end
128
129
  new_djs
@@ -133,7 +134,7 @@ module Turntabler
133
134
  data['user'].map do |attrs|
134
135
  user = room.build_user(attrs)
135
136
  room.djs.delete(user)
136
- user
137
+ [user]
137
138
  end
138
139
  end
139
140
 
@@ -153,14 +154,14 @@ module Turntabler
153
154
 
154
155
  # There are no more songs to play in the room
155
156
  handle :song_unavailable, :nosong do
156
- client.on_message('command' => 'song_ended') if room.current_song
157
+ client.trigger(:song_ended) if room.current_song
157
158
  room.attributes = data['room'].merge('current_song' => nil)
158
159
  nil
159
160
  end
160
161
 
161
162
  # A new song has started playing
162
163
  handle :song_started, :newsong do
163
- client.on_message('command' => 'song_ended') if room.current_song
164
+ client.trigger(:song_ended) if room.current_song
164
165
  room.attributes = data['room']
165
166
  room.current_song
166
167
  end
@@ -190,13 +191,13 @@ module Turntabler
190
191
 
191
192
  # A song was skipped due to a copyright claim
192
193
  handle :song_blocked do
193
- client.on_message('command' => 'song_ended') if room.current_song
194
+ client.trigger(:song_ended) if room.current_song
194
195
  Song.new(client, data)
195
196
  end
196
197
 
197
198
  # A song was skipped due to a limit on # of plays per hour
198
199
  handle :song_limited, :dmca_error do
199
- client.on_message('command' => 'song_ended') if room.current_song
200
+ client.trigger(:song_ended) if room.current_song
200
201
  Song.new(client, data)
201
202
  end
202
203
 
@@ -207,7 +208,7 @@ module Turntabler
207
208
 
208
209
  # A song search has completed and the results are available
209
210
  handle :search_completed, :search_complete do
210
- [data['docs'].map {|attrs| Song.new(client, attrs)}]
211
+ [[data['docs'].map {|attrs| Song.new(client, attrs)}]]
211
212
  end
212
213
 
213
214
  # A song search failed to complete
@@ -217,25 +218,29 @@ module Turntabler
217
218
  # @return [String]
218
219
  attr_reader :name
219
220
 
221
+ # The raw arguments list from the event
222
+ # @return [Array<Object>]
223
+ attr_reader :args
224
+
220
225
  # The raw hash of data parsed from the event
221
226
  # @return [Hash<String, Object>]
222
227
  attr_reader :data
223
228
 
224
- # The typecasted results parsed from the data
225
- # @return [Array]
229
+ # The typecasted results args parsed from the event
230
+ # @return [Array<Array<Object>>]
226
231
  attr_reader :results
227
232
 
228
233
  # Creates a new event triggered with the given data
229
234
  #
230
235
  # @param [Turntabler::Client] client The client that this event is bound to
231
236
  # @param [Hash] data The response data from Turntable
232
- def initialize(client, data)
237
+ def initialize(client, command, args)
233
238
  @client = client
234
- @data = data
235
- command = data['command'].to_sym
239
+ @args = args
240
+ @data = args[0]
236
241
  @name = self.class.commands[command]
237
242
  @results = __send__("typecast_#{command}_event")
238
- @results = [@results] unless @results.is_a?(Array)
243
+ @results = [[@results].compact] unless @results.is_a?(Array)
239
244
  end
240
245
 
241
246
  private
@@ -46,9 +46,9 @@ module Turntabler
46
46
  def run(event)
47
47
  if conditions_match?(event.data)
48
48
  # Run the block for each individual result
49
- event.results.each do |result|
49
+ event.results.each do |args|
50
50
  begin
51
- @block.call(*[result].compact)
51
+ @block.call(*args)
52
52
  rescue StandardError => ex
53
53
  logger.error(([ex.message] + ex.backtrace) * "\n")
54
54
  end
@@ -5,6 +5,14 @@ module Turntabler
5
5
  # Represents a collection of songs managed by the user and that can be played
6
6
  # within a room
7
7
  class Playlist < Resource
8
+ # Allow the id to be set via the "name" attribute
9
+ # @return [String]
10
+ attribute :id, :name, :load => false
11
+
12
+ # Whether this is the currently active playlist
13
+ # @return [Boolean]
14
+ attribute :active, :load => false
15
+
8
16
  # The songs that have been added to this playlist
9
17
  # @return [Array<Turntabler::Song>]
10
18
  attribute :songs, :list do |songs|
@@ -30,6 +38,60 @@ module Turntabler
30
38
  super()
31
39
  end
32
40
 
41
+ # Updates this playlist's information.
42
+ #
43
+ # @param [Hash] attributes The attributes to update
44
+ # @option attributes [String] :id
45
+ # @return [true]
46
+ # @raise [ArgumentError] if an invalid attribute or value is specified
47
+ # @raise [Turntabler::Error] if the command fails
48
+ # @example
49
+ # playlist.update(:id => "rock") # => true
50
+ def update(attributes = {})
51
+ assert_valid_keys(attributes, :id)
52
+
53
+ # Update id
54
+ id = attributes.delete(:id)
55
+ update_id(id) if id
56
+
57
+ true
58
+ end
59
+
60
+ # Whether this is the currently active playlist
61
+ #
62
+ # @return [Boolean]
63
+ # @raise [Turntabler::Error] if the command fails
64
+ # @example
65
+ # playlist.active # => true
66
+ def active
67
+ @active = client.user.playlists.all.any? {|playlist| playlist == self && playlist.active?} if @active.nil?
68
+ @active
69
+ end
70
+
71
+ # Changes this playlist to be used for queueing new songs with the room.
72
+ #
73
+ # @return [true]
74
+ # @raise [Turntabler::Error] if the command fails
75
+ # @example
76
+ # playlist.switch # => true
77
+ def activate
78
+ api('playlist.switch')
79
+ self.attributes = {'active' => true}
80
+ true
81
+ end
82
+
83
+ # Permanently deletes this playlist and the list of songs within it. If this
84
+ # is the currently active playlist, the "default" playlist will become active.
85
+ #
86
+ # @return [true]
87
+ # @raise [Turntabler::Error] if the command fails
88
+ # @example
89
+ # playlist.delete # => true
90
+ def delete
91
+ api('playlist.delete')
92
+ true
93
+ end
94
+
33
95
  # Gets the song with the given id.
34
96
  #
35
97
  # @param [String] song_id The id for the song
@@ -42,6 +104,13 @@ module Turntabler
42
104
  end
43
105
 
44
106
  private
107
+ # Updates the name used to identify this playlist
108
+ def update_id(id)
109
+ client.api('playlist.rename', :old_playlist_name => self.id, :new_playlist_name => id)
110
+ self.attributes = {'name' => id}
111
+ true
112
+ end
113
+
45
114
  def api(command, options = {})
46
115
  options[:playlist_name] = id
47
116
  super
@@ -0,0 +1,70 @@
1
+ require 'turntabler/playlist'
2
+
3
+ module Turntabler
4
+ # Provides a set of helper methods for interacting with a user's playlists.
5
+ class PlaylistDirectory
6
+ include Assertions
7
+
8
+ # @api private
9
+ def initialize(client)
10
+ @client = client
11
+ @playlists = {}
12
+ end
13
+
14
+ # Creates a new playlist with the given id. This should only be used if
15
+ # the playlist doesn't already exist.
16
+ #
17
+ # @note This will automatically enter the playlist when it is created
18
+ # @param [String] id The unique identifier of the playlist
19
+ # @return [Turntabler::Playlist]
20
+ # @raise [Turntabler::Error] if the command fails
21
+ # @example
22
+ # playlists.create("Rock") # => #<Turntabler::Playlist ...>
23
+ def create(id)
24
+ api('playlist.create', :playlist_name => id)
25
+ build(:_id => id)
26
+ end
27
+
28
+ # Gets the list of playlists created.
29
+ #
30
+ # @return [Array<Turntabler::Playlist>]
31
+ # @raise [Turntabler::Error] if the command fails
32
+ # @example
33
+ # playlists.all # => [#<Turntabler::Playlist ...>, ...]
34
+ def all
35
+ data = api('playlist.list_all')
36
+ data['list'].map {|attrs| build(attrs)}
37
+ end
38
+
39
+ # Gets the playlist represented by the given attributes.
40
+ #
41
+ # If the playlist hasn't been previously accessed, then a new Playlist
42
+ # instance will get created.
43
+ #
44
+ # @api private
45
+ # @param [Hash] attrs The attributes representing the playlist
46
+ # @return [Turntabler::Playlist]
47
+ def build(attrs)
48
+ playlist = Playlist.new(client, attrs)
49
+
50
+ # Update existing in cache or cache a new playlist
51
+ if existing = @playlists[playlist.id]
52
+ playlist = existing
53
+ playlist.attributes = attrs
54
+ else
55
+ @playlists[playlist.id] = playlist
56
+ end
57
+
58
+ playlist
59
+ end
60
+
61
+ private
62
+ # The client that all APIs filter through
63
+ attr_reader :client
64
+
65
+ # Runs the given API command on the client.
66
+ def api(command, options = {})
67
+ client.api(command, options)
68
+ end
69
+ end
70
+ end
@@ -54,11 +54,11 @@ module Turntabler
54
54
  # @raise [ArgumentError] if an invalid option or value is specified
55
55
  # @raise [Turntabler::Error] if the command fails
56
56
  # @example
57
- # rooms.list # => [#<Turntabler::Room ...>, ...]
58
- # rooms.list(:favorites => true) # => [#<Turntabler::Room ...>, ...]
59
- # rooms.list(:available_djs => true, :genre => :rock) # => [#<Turntabler::Room ...>, ...]
60
- # rooms.list(:sort => :random) # => [#<Turntabler::Room ...>, ...]
61
- def list(options = {})
57
+ # rooms.all # => [#<Turntabler::Room ...>, ...]
58
+ # rooms.all(:favorites => true) # => [#<Turntabler::Room ...>, ...]
59
+ # rooms.all(:available_djs => true, :genre => :rock) # => [#<Turntabler::Room ...>, ...]
60
+ # rooms.all(:sort => :random) # => [#<Turntabler::Room ...>, ...]
61
+ def all(options = {})
62
62
  assert_valid_keys(options, :limit, :skip, :favorites, :available_djs, :genre, :minimum_listeners, :sort)
63
63
  assert_valid_values(options[:genre], :rock, :electronic, :indie, :hiphop, :pop, :dubstep) if options[:genre]
64
64
  assert_valid_values(options[:sort], :created, :listeners, :random) if options[:sort]
@@ -2,8 +2,8 @@ module Turntabler
2
2
  # The current version of the library
3
3
  module Version
4
4
  MAJOR = 0
5
- MINOR = 1
6
- PATCH = 4
5
+ MINOR = 2
6
+ PATCH = 0
7
7
  STRING = [MAJOR, MINOR, PATCH].join(".")
8
8
  end
9
9
  end
data/lib/turntabler.rb CHANGED
@@ -4,6 +4,7 @@ require 'em-synchrony'
4
4
  # Turntable.FM API for Ruby
5
5
  module Turntabler
6
6
  autoload :Client, 'turntabler/client'
7
+ autoload :Event, 'turntabler/event'
7
8
 
8
9
  class << self
9
10
  # The logger to use for all Turntable messages. By default, everything is
@@ -97,6 +98,25 @@ module Turntabler
97
98
  EM.synchrony { run(*args, &block) }
98
99
  end
99
100
  end
101
+
102
+ # Defines one or more custom events for which handlers can be registered
103
+ # and triggered via Turntabler::Client#on and Turntabler::Client#trigger,
104
+ # respectively.
105
+ #
106
+ # @note Events must be defined before handlers can be registered for them
107
+ # @param [Array<String>] names The names of the events to define
108
+ # @raise [ArgumentError] if the event has already been defined
109
+ # @example
110
+ # Turntabler::Client.events :custom_event1, :custom_event2
111
+ def events(*names)
112
+ names.each do |name|
113
+ if Event.command?(name)
114
+ raise ArgumentError, "Event :#{name} is already defined"
115
+ else
116
+ Event.handle(name) { [args] }
117
+ end
118
+ end
119
+ end
100
120
  end
101
121
 
102
122
  @logger = Logger.new(STDOUT)
metadata CHANGED
@@ -1,121 +1,94 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: turntabler
3
- version: !ruby/object:Gem::Version
4
- version: 0.1.4
3
+ version: !ruby/object:Gem::Version
5
4
  prerelease:
5
+ version: 0.2.0
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Aaron Pfeifer
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-08 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
12
+
13
+ date: 2013-02-16 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
15
16
  name: em-synchrony
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '0'
22
- type: :runtime
23
17
  prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
18
+ requirement: &id001 !ruby/object:Gem::Requirement
25
19
  none: false
26
- requirements:
27
- - - ! '>='
28
- - !ruby/object:Gem::Version
29
- version: '0'
30
- - !ruby/object:Gem::Dependency
31
- name: em-http-request
32
- requirement: !ruby/object:Gem::Requirement
33
- none: false
34
- requirements:
35
- - - ! '>='
36
- - !ruby/object:Gem::Version
37
- version: '0'
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
38
24
  type: :runtime
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: em-http-request
39
28
  prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
29
+ requirement: &id002 !ruby/object:Gem::Requirement
41
30
  none: false
42
- requirements:
43
- - - ! '>='
44
- - !ruby/object:Gem::Version
45
- version: '0'
46
- - !ruby/object:Gem::Dependency
47
- name: faye-websocket
48
- requirement: !ruby/object:Gem::Requirement
49
- none: false
50
- requirements:
51
- - - ! '>='
52
- - !ruby/object:Gem::Version
53
- version: '0'
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
54
35
  type: :runtime
36
+ version_requirements: *id002
37
+ - !ruby/object:Gem::Dependency
38
+ name: faye-websocket
55
39
  prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
40
+ requirement: &id003 !ruby/object:Gem::Requirement
57
41
  none: false
58
- requirements:
59
- - - ! '>='
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- - !ruby/object:Gem::Dependency
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ type: :runtime
47
+ version_requirements: *id003
48
+ - !ruby/object:Gem::Dependency
63
49
  name: rake
64
- requirement: !ruby/object:Gem::Requirement
65
- none: false
66
- requirements:
67
- - - ! '>='
68
- - !ruby/object:Gem::Version
69
- version: '0'
70
- type: :development
71
50
  prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
- requirements:
75
- - - ! '>='
76
- - !ruby/object:Gem::Version
77
- version: '0'
78
- - !ruby/object:Gem::Dependency
79
- name: rspec
80
- requirement: !ruby/object:Gem::Requirement
51
+ requirement: &id004 !ruby/object:Gem::Requirement
81
52
  none: false
82
- requirements:
83
- - - ~>
84
- - !ruby/object:Gem::Version
85
- version: '2.11'
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
86
57
  type: :development
58
+ version_requirements: *id004
59
+ - !ruby/object:Gem::Dependency
60
+ name: rspec
87
61
  prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
62
+ requirement: &id005 !ruby/object:Gem::Requirement
89
63
  none: false
90
- requirements:
64
+ requirements:
91
65
  - - ~>
92
- - !ruby/object:Gem::Version
93
- version: '2.11'
94
- - !ruby/object:Gem::Dependency
95
- name: simplecov
96
- requirement: !ruby/object:Gem::Requirement
97
- none: false
98
- requirements:
99
- - - ! '>='
100
- - !ruby/object:Gem::Version
101
- version: '0'
66
+ - !ruby/object:Gem::Version
67
+ version: "2.11"
102
68
  type: :development
69
+ version_requirements: *id005
70
+ - !ruby/object:Gem::Dependency
71
+ name: simplecov
103
72
  prerelease: false
104
- version_requirements: !ruby/object:Gem::Requirement
73
+ requirement: &id006 !ruby/object:Gem::Requirement
105
74
  none: false
106
- requirements:
107
- - - ! '>='
108
- - !ruby/object:Gem::Version
109
- version: '0'
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ type: :development
80
+ version_requirements: *id006
110
81
  description: Turntable.FM API for Ruby
111
82
  email: aaron.pfeifer@gmail.com
112
83
  executables: []
84
+
113
85
  extensions: []
114
- extra_rdoc_files:
86
+
87
+ extra_rdoc_files:
115
88
  - README.md
116
89
  - CHANGELOG.md
117
90
  - LICENSE
118
- files:
91
+ files:
119
92
  - .gitignore
120
93
  - .rspec
121
94
  - .yardopts
@@ -149,6 +122,7 @@ files:
149
122
  - lib/turntabler/loggable.rb
150
123
  - lib/turntabler/message.rb
151
124
  - lib/turntabler/playlist.rb
125
+ - lib/turntabler/playlist_directory.rb
152
126
  - lib/turntabler/preferences.rb
153
127
  - lib/turntabler/resource.rb
154
128
  - lib/turntabler/room.rb
@@ -165,33 +139,36 @@ files:
165
139
  - turntabler.gemspec
166
140
  homepage: http://github.com/obrie/turntabler
167
141
  licenses: []
142
+
168
143
  post_install_message:
169
- rdoc_options:
144
+ rdoc_options:
170
145
  - --line-numbers
171
146
  - --inline-source
172
147
  - --title
173
148
  - turntabler
174
149
  - --main
175
150
  - README.md
176
- require_paths:
151
+ require_paths:
177
152
  - lib
178
- required_ruby_version: !ruby/object:Gem::Requirement
153
+ required_ruby_version: !ruby/object:Gem::Requirement
179
154
  none: false
180
- requirements:
181
- - - ! '>='
182
- - !ruby/object:Gem::Version
183
- version: '0'
184
- required_rubygems_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: "0"
159
+ required_rubygems_version: !ruby/object:Gem::Requirement
185
160
  none: false
186
- requirements:
187
- - - ! '>='
188
- - !ruby/object:Gem::Version
189
- version: '0'
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: "0"
190
165
  requirements: []
166
+
191
167
  rubyforge_project:
192
168
  rubygems_version: 1.8.24
193
169
  signing_key:
194
170
  specification_version: 3
195
171
  summary: Turntable.FM API for Ruby
196
172
  test_files: []
173
+
197
174
  has_rdoc: