turntabler 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.rspec +2 -0
- data/.yardopts +7 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +383 -0
- data/Rakefile +11 -0
- data/examples/Gemfile +3 -0
- data/examples/Gemfile.lock +29 -0
- data/examples/autobop.rb +13 -0
- data/examples/autofan.rb +13 -0
- data/examples/blacklist.rb +16 -0
- data/examples/bop.rb +15 -0
- data/examples/bopcount.rb +20 -0
- data/examples/chat_bot.rb +16 -0
- data/examples/modlist.rb +19 -0
- data/examples/switch.rb +40 -0
- data/examples/time_afk_list.rb +46 -0
- data/lib/turntabler/assertions.rb +36 -0
- data/lib/turntabler/authorized_user.rb +217 -0
- data/lib/turntabler/avatar.rb +34 -0
- data/lib/turntabler/boot.rb +22 -0
- data/lib/turntabler/client.rb +457 -0
- data/lib/turntabler/connection.rb +176 -0
- data/lib/turntabler/digest_helpers.rb +13 -0
- data/lib/turntabler/error.rb +5 -0
- data/lib/turntabler/event.rb +239 -0
- data/lib/turntabler/handler.rb +67 -0
- data/lib/turntabler/loggable.rb +11 -0
- data/lib/turntabler/message.rb +24 -0
- data/lib/turntabler/playlist.rb +50 -0
- data/lib/turntabler/preferences.rb +70 -0
- data/lib/turntabler/resource.rb +194 -0
- data/lib/turntabler/room.rb +377 -0
- data/lib/turntabler/room_directory.rb +133 -0
- data/lib/turntabler/snag.rb +16 -0
- data/lib/turntabler/song.rb +247 -0
- data/lib/turntabler/sticker.rb +48 -0
- data/lib/turntabler/sticker_placement.rb +25 -0
- data/lib/turntabler/user.rb +274 -0
- data/lib/turntabler/version.rb +9 -0
- data/lib/turntabler/vote.rb +19 -0
- data/lib/turntabler.rb +102 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/turntabler_spec.rb +4 -0
- data/turntable.gemspec +24 -0
- metadata +173 -0
@@ -0,0 +1,239 @@
|
|
1
|
+
require 'turntabler/boot'
|
2
|
+
require 'turntabler/message'
|
3
|
+
require 'turntabler/snag'
|
4
|
+
require 'turntabler/song'
|
5
|
+
|
6
|
+
module Turntabler
|
7
|
+
# Provides access to all of the events that get triggered by incoming messages
|
8
|
+
# from the Turntable API
|
9
|
+
# @api private
|
10
|
+
class Event
|
11
|
+
class << self
|
12
|
+
# Maps Turntable command => event name
|
13
|
+
# @return [Hash<String, String>]
|
14
|
+
attr_reader :commands
|
15
|
+
|
16
|
+
# Defines a new event that maps to the given Turntable command. The
|
17
|
+
# block defines how to typecast the data that is received from Turntable.
|
18
|
+
#
|
19
|
+
# @param [String] name The name of the event exposed to the rest of the library
|
20
|
+
# @param [String] command The Turntable command that this event name maps to
|
21
|
+
# @yield [data] Gives the data to typecast to the block
|
22
|
+
# @yieldparam [Hash] data The data received from Turntable
|
23
|
+
# @yieldreturn The typecasted data that should be passed into any handlers bound to the event
|
24
|
+
# @return [nil]
|
25
|
+
def handle(name, command = name, &block)
|
26
|
+
block ||= lambda {}
|
27
|
+
commands[command] = name
|
28
|
+
|
29
|
+
define_method("typecast_#{command}_event", &block)
|
30
|
+
protected :"typecast_#{command}_event"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Determines whether the given command is handled
|
34
|
+
#
|
35
|
+
# @param [String] command The command to check for the existence of
|
36
|
+
# @return [Boolean] +true+ if the command exists, otherwise +false+
|
37
|
+
def command?(command)
|
38
|
+
command && commands.include?(command.to_sym)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
@commands = {}
|
43
|
+
|
44
|
+
# An authenticated session is missing
|
45
|
+
handle :session_missing, :no_session
|
46
|
+
|
47
|
+
# The client is being asked to disconnect
|
48
|
+
handle :session_end_requested, :killdashnine do
|
49
|
+
room_id = data['roomid']
|
50
|
+
if !room_id || room && room.id == room_id
|
51
|
+
client.close(true)
|
52
|
+
data['msg'] || 'Unknown reason'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# The client's connection has closed
|
57
|
+
handle :session_ended
|
58
|
+
|
59
|
+
# A heartbeat was received from Turntable to ensure the client's connection
|
60
|
+
# is still valid
|
61
|
+
handle :heartbeat
|
62
|
+
|
63
|
+
# A response was receivied from a prior command sent to Turntable
|
64
|
+
handle :response_received do
|
65
|
+
data
|
66
|
+
end
|
67
|
+
|
68
|
+
# Information about the room was updated
|
69
|
+
handle :room_updated, :update_room do
|
70
|
+
room.attributes = data
|
71
|
+
room
|
72
|
+
end
|
73
|
+
|
74
|
+
# One or more users have entered the room
|
75
|
+
handle :user_entered, :registered do
|
76
|
+
data['user'].map do |attrs|
|
77
|
+
user = room.build_user(attrs)
|
78
|
+
room.listeners << user
|
79
|
+
user
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# One or more users have left the room
|
84
|
+
handle :user_left, :deregistered do
|
85
|
+
data['user'].map do |attrs|
|
86
|
+
user = room.build_user(attrs)
|
87
|
+
room.listeners.delete(user)
|
88
|
+
user
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# A user has been booted from the room
|
93
|
+
handle :user_booted, :booted_user do
|
94
|
+
boot = Boot.new(client, data)
|
95
|
+
client.room = nil if boot.user == client.user
|
96
|
+
boot
|
97
|
+
end
|
98
|
+
|
99
|
+
# A user's name / profile has been updated
|
100
|
+
handle :user_updated, :update_user do
|
101
|
+
fans_change = data.delete('fans')
|
102
|
+
user = room.build_user(data)
|
103
|
+
user.attributes = {'fans' => user.fans_count + fans_change} if fans_change
|
104
|
+
user
|
105
|
+
end
|
106
|
+
|
107
|
+
# A user's stickers have been updated
|
108
|
+
handle :user_updated, :update_sticker_placements do
|
109
|
+
room.build_user(data)
|
110
|
+
end
|
111
|
+
|
112
|
+
# A user spoke in the chat room
|
113
|
+
handle :user_spoke, :speak do
|
114
|
+
Message.new(client, data)
|
115
|
+
end
|
116
|
+
|
117
|
+
# A new dj was added to the room
|
118
|
+
handle :dj_added, :add_dj do
|
119
|
+
new_djs = []
|
120
|
+
data['user'].each_with_index do |attrs, index|
|
121
|
+
new_djs << user = room.build_user(attrs.merge('placements' => data['placements'][index]))
|
122
|
+
room.listeners << user
|
123
|
+
room.djs << user
|
124
|
+
end
|
125
|
+
new_djs
|
126
|
+
end
|
127
|
+
|
128
|
+
# A dj was removed from the room
|
129
|
+
handle :dj_removed, :rem_dj do
|
130
|
+
data['user'].map do |attrs|
|
131
|
+
user = room.build_user(attrs)
|
132
|
+
room.listeners << user
|
133
|
+
room.djs.delete(user)
|
134
|
+
user
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# A new moderator was added to the room
|
139
|
+
handle :moderator_added, :new_moderator do
|
140
|
+
user = room.build_user(data)
|
141
|
+
room.listeners << user
|
142
|
+
room.moderators << user
|
143
|
+
user
|
144
|
+
end
|
145
|
+
|
146
|
+
# A moderator was removed from the room
|
147
|
+
handle :moderator_removed, :rem_moderator do
|
148
|
+
user = room.build_user(data)
|
149
|
+
room.moderators.delete(user)
|
150
|
+
user
|
151
|
+
end
|
152
|
+
|
153
|
+
# There are no more songs to play in the room
|
154
|
+
handle :song_unavailable, :nosong do
|
155
|
+
room.attributes = data['room'].merge('current_song' => nil)
|
156
|
+
nil
|
157
|
+
end
|
158
|
+
|
159
|
+
# A new song has started playing
|
160
|
+
handle :song_started, :newsong do
|
161
|
+
room.attributes = data['room']
|
162
|
+
room.current_song
|
163
|
+
end
|
164
|
+
|
165
|
+
# The current song has ended
|
166
|
+
handle :song_ended, :endsong do
|
167
|
+
room.attributes = data['room']
|
168
|
+
room.current_song
|
169
|
+
end
|
170
|
+
|
171
|
+
# A vote was cast for the song
|
172
|
+
handle :song_voted, :update_votes do
|
173
|
+
room.attributes = data['room']
|
174
|
+
room.current_song
|
175
|
+
end
|
176
|
+
|
177
|
+
# A user in the room has queued the current song onto their playlist
|
178
|
+
handle :song_snagged, :snagged do
|
179
|
+
Snag.new(client, data.merge(:song => room.current_song))
|
180
|
+
end
|
181
|
+
|
182
|
+
# A song was skipped due to a copyright claim
|
183
|
+
handle :song_blocked do
|
184
|
+
Song.new(client, data)
|
185
|
+
end
|
186
|
+
|
187
|
+
# A song was skipped due to a limit on # of plays per hour
|
188
|
+
handle :song_limited, :dmca_error do
|
189
|
+
Song.new(client, data)
|
190
|
+
end
|
191
|
+
|
192
|
+
# A private message was received from another user in the room
|
193
|
+
handle :message_received, :pmmed do
|
194
|
+
Message.new(client, data)
|
195
|
+
end
|
196
|
+
|
197
|
+
# A song search has completed and the results are available
|
198
|
+
handle :search_completed, :search_complete do
|
199
|
+
[data['docs'].map {|attrs| Song.new(client, attrs)}]
|
200
|
+
end
|
201
|
+
|
202
|
+
# A song search failed to complete
|
203
|
+
handle :search_failed
|
204
|
+
|
205
|
+
# The name of the event that was triggered
|
206
|
+
# @return [String]
|
207
|
+
attr_reader :name
|
208
|
+
|
209
|
+
# The raw hash of data parsed from the event
|
210
|
+
# @return [Hash]
|
211
|
+
attr_reader :data
|
212
|
+
|
213
|
+
# The typecasted results parsed from the data
|
214
|
+
# @return [Array]
|
215
|
+
attr_reader :results
|
216
|
+
|
217
|
+
# Creates a new event triggered with the given data
|
218
|
+
#
|
219
|
+
# @param [Turntabler::Client] client The client that this event is bound to
|
220
|
+
# @param [Hash] data The response data from Turntable
|
221
|
+
def initialize(client, data)
|
222
|
+
@client = client
|
223
|
+
@data = data
|
224
|
+
command = data['command'].to_sym
|
225
|
+
@name = self.class.commands[command]
|
226
|
+
@results = __send__("typecast_#{command}_event")
|
227
|
+
@results = [@results] unless @results.is_a?(Array)
|
228
|
+
end
|
229
|
+
|
230
|
+
private
|
231
|
+
# The client that all APIs filter through
|
232
|
+
attr_reader :client
|
233
|
+
|
234
|
+
# Gets the current room the user is in
|
235
|
+
def room
|
236
|
+
client.room
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'turntabler/event'
|
2
|
+
|
3
|
+
module Turntabler
|
4
|
+
# Represents a callback that's been bound to a particular event
|
5
|
+
# @api private
|
6
|
+
class Handler
|
7
|
+
include Assertions
|
8
|
+
|
9
|
+
# The event this handler is bound to
|
10
|
+
# @return [String]
|
11
|
+
attr_reader :event
|
12
|
+
|
13
|
+
# Whether to only call the handler once and then never again
|
14
|
+
# @return [Boolean] +true+ if only called once, otherwise +false+
|
15
|
+
attr_reader :once
|
16
|
+
|
17
|
+
# The data that must be matched in order for the handler to run
|
18
|
+
# @return [Hash<String, Object>]
|
19
|
+
attr_reader :conditions
|
20
|
+
|
21
|
+
# Builds a new handler bound to the given event.
|
22
|
+
#
|
23
|
+
# @param [String] event The name of the event to bind to
|
24
|
+
# @param [Hash] options The configuration options
|
25
|
+
# @option options [Boolean] :once (false) Whether to only call the handler once
|
26
|
+
# @option options [Hash] :if (nil) Data that must be matched to run
|
27
|
+
# @raise [ArgumentError] if an invalid option is specified
|
28
|
+
def initialize(event, options = {}, &block)
|
29
|
+
assert_valid_values(event, *Event.commands.values)
|
30
|
+
assert_valid_keys(options, :once, :if)
|
31
|
+
options = {:once => false, :if => nil}.merge(options)
|
32
|
+
|
33
|
+
@event = event
|
34
|
+
@once = options[:once]
|
35
|
+
@conditions = options[:if]
|
36
|
+
@block = block
|
37
|
+
end
|
38
|
+
|
39
|
+
# Runs this handler with results from the given event.
|
40
|
+
#
|
41
|
+
# @param [Array] event The event being triggered
|
42
|
+
# @return [Boolean] +true+ if conditions were matcher to run the handler, otherwise +false+
|
43
|
+
def run(event)
|
44
|
+
if conditions_match?(event.data)
|
45
|
+
# Run the block for each individual result
|
46
|
+
event.results.each do |result|
|
47
|
+
@block.call(*[result].compact)
|
48
|
+
end
|
49
|
+
|
50
|
+
true
|
51
|
+
else
|
52
|
+
false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
# Determines whether the conditions configured for this handler match the
|
58
|
+
# event data
|
59
|
+
def conditions_match?(data)
|
60
|
+
if conditions
|
61
|
+
conditions.all? {|(key, value)| data[key] == value}
|
62
|
+
else
|
63
|
+
true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'turntabler/resource'
|
2
|
+
require 'turntabler/user'
|
3
|
+
|
4
|
+
module Turntabler
|
5
|
+
# Represents a message that was sent to or from the current user. This can
|
6
|
+
# either be within the context of a room or a private conversation.
|
7
|
+
class Message < Resource
|
8
|
+
# The user who sent the message
|
9
|
+
# @return [Turntabler::User]
|
10
|
+
attribute :sender, :senderid do |id|
|
11
|
+
room? ? room.build_user(:_id => id) : User.new(client, :_id => id)
|
12
|
+
end
|
13
|
+
|
14
|
+
# The text of the message
|
15
|
+
# @return [String]
|
16
|
+
attribute :content, :text
|
17
|
+
|
18
|
+
# The time at which the message was created
|
19
|
+
# @return [Time]
|
20
|
+
attribute :created_at, :time do |value|
|
21
|
+
Time.at(value)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'turntabler/resource'
|
2
|
+
require 'turntabler/song'
|
3
|
+
|
4
|
+
module Turntabler
|
5
|
+
# Represents a collection of songs managed by the user and that can be played
|
6
|
+
# within a room
|
7
|
+
class Playlist < Resource
|
8
|
+
# The songs that have been added to this playlist
|
9
|
+
# @return [Array<Turntabler::Song>]
|
10
|
+
attribute :songs, :list do |songs|
|
11
|
+
songs.map {|attrs| Song.new(client, attrs)}
|
12
|
+
end
|
13
|
+
|
14
|
+
# Loads the attributes for this playlist. Attributes will automatically load
|
15
|
+
# when accessed, but this allows data to be forcefully loaded upfront.
|
16
|
+
#
|
17
|
+
# @param [Hash] options The configuration options
|
18
|
+
# @option options [Boolean] minimal (false) Whether to only include the identifiers for songs and not the entire metadata
|
19
|
+
# @return [true]
|
20
|
+
# @raise [Turntabler::Error] if the command fails
|
21
|
+
# @example
|
22
|
+
# playlist.load # => true
|
23
|
+
# playlist.songs # => [#<Turntabler::Song ...>, ...]
|
24
|
+
def load(options = {})
|
25
|
+
assert_valid_keys(options, :minimal)
|
26
|
+
options = {:minimal => false}.merge(options)
|
27
|
+
|
28
|
+
data = api('playlist.all', options)
|
29
|
+
self.attributes = data
|
30
|
+
super()
|
31
|
+
end
|
32
|
+
|
33
|
+
# Gets the song with the given id.
|
34
|
+
#
|
35
|
+
# @param [String] song_id The id for the song
|
36
|
+
# @return [Turntabler::Song]
|
37
|
+
# @raise [Turntabler::Error] if the list of songs fail to load
|
38
|
+
# @example
|
39
|
+
# playlist.song('4fd8...') # => #<Turntabler::Song ...>
|
40
|
+
def song(song_id)
|
41
|
+
songs.detect {|song| song.id == song_id}
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def api(command, options = {})
|
46
|
+
options[:playlist_name] = id
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'turntabler/resource'
|
2
|
+
|
3
|
+
module Turntabler
|
4
|
+
# Represents the site preferences for the authorized user
|
5
|
+
class Preferences < Resource
|
6
|
+
# Send e-mails if a fan starts DJing
|
7
|
+
# @return [Boolean]
|
8
|
+
attribute :notify_dj
|
9
|
+
|
10
|
+
# Send e-mails when someone becomes a fan
|
11
|
+
# @return [Boolean]
|
12
|
+
attribute :notify_fan
|
13
|
+
|
14
|
+
# Sends infrequent e-mails about news
|
15
|
+
# @return [Boolean]
|
16
|
+
attribute :notify_news
|
17
|
+
|
18
|
+
# Sends e-mails at random times with a different subsection of the digits of pi
|
19
|
+
# @return [Boolean]
|
20
|
+
attribute :notify_random
|
21
|
+
|
22
|
+
# Publishes to facebook songs awesomed in public rooms
|
23
|
+
# @return [Boolean]
|
24
|
+
attribute :facebook_awesome
|
25
|
+
|
26
|
+
# Publishes to facebook when a public room is joined
|
27
|
+
# @return [Boolean]
|
28
|
+
attribute :facebook_join
|
29
|
+
|
30
|
+
# Publishes to facebook when DJing in a public room
|
31
|
+
# @return [Boolean]
|
32
|
+
attribute :facebook_dj
|
33
|
+
|
34
|
+
# Loads the user's current Turntable preferences.
|
35
|
+
#
|
36
|
+
# @return [true]
|
37
|
+
# @raise [Turntabler::Error] if the command fails
|
38
|
+
# @example
|
39
|
+
# preferences.load # => true
|
40
|
+
def load
|
41
|
+
data = api('user.get_prefs')
|
42
|
+
self.attributes = data['result'].inject({}) do |result, (preference, value, id, description)|
|
43
|
+
result[preference] = value
|
44
|
+
result
|
45
|
+
end
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
# Updates the preferences.
|
50
|
+
#
|
51
|
+
# @param [Hash] attributes The attributes to update
|
52
|
+
# @option attributes [Boolean] :notify_dj
|
53
|
+
# @option attributes [Boolean] :notify_fan
|
54
|
+
# @option attributes [Boolean] :notify_news
|
55
|
+
# @option attributes [Boolean] :notify_random
|
56
|
+
# @option attributes [Boolean] :facbeook_awesome
|
57
|
+
# @option attributes [Boolean] :facebook_join
|
58
|
+
# @option attributes [Boolean] :facebook_dj
|
59
|
+
# @return [true]
|
60
|
+
# @raise [Turntabler::Error] if the command fails
|
61
|
+
# @example
|
62
|
+
# preferences.update(:notify_dj => false) # => true
|
63
|
+
def update(attributes = {})
|
64
|
+
assert_valid_values(attributes, :notify_dj, :notify_fan, :notify_news, :notify_random, :facebook_awesome, :facebook_join, :facebook_dj)
|
65
|
+
api('user.edit_prefs', attributes)
|
66
|
+
self.attributes = attributes
|
67
|
+
true
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'digest/sha1'
|
3
|
+
require 'turntabler/assertions'
|
4
|
+
require 'turntabler/digest_helpers'
|
5
|
+
|
6
|
+
module Turntabler
|
7
|
+
# Represents an object that's been created using content from Turntable. This
|
8
|
+
# encapsulates responsibilities such as reading and writing attributes.
|
9
|
+
#
|
10
|
+
# By default all Turntable resources have a +:id+ attribute defined.
|
11
|
+
class Resource
|
12
|
+
include Assertions
|
13
|
+
include DigestHelpers
|
14
|
+
|
15
|
+
class << self
|
16
|
+
include Assertions
|
17
|
+
|
18
|
+
# Defines a new Turntable attribute on this class. By default, the name
|
19
|
+
# of the attribute is assumed to be the same name that Turntable specifies
|
20
|
+
# in its API. If the names are different, this can be overridden on a
|
21
|
+
# per-attribute basis.
|
22
|
+
#
|
23
|
+
# Configuration options:
|
24
|
+
# * +:load+ - Whether the resource should be loaded remotely from
|
25
|
+
# Turntable in order to access the attribute. Default is true.
|
26
|
+
#
|
27
|
+
# == Examples
|
28
|
+
#
|
29
|
+
# # Define a "name" attribute that maps to a Turntable "name" attribute
|
30
|
+
# attribute :name
|
31
|
+
#
|
32
|
+
# # Define an "id" attribute that maps to a Turntable "_id" attribute
|
33
|
+
# attribute :id, :_id
|
34
|
+
#
|
35
|
+
# # Define an "user_id" attribute that maps to both a Turntable "user_id" and "userid" attribute
|
36
|
+
# attribute :user_id, :user_id, :userid
|
37
|
+
#
|
38
|
+
# # Define a "time" attribute that maps to a Turntable "time" attribute
|
39
|
+
# # and converts the value to a Time object
|
40
|
+
# attribute :time do |value|
|
41
|
+
# Time.at(value)
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# # Define a "created_at" attribute that maps to a Turntable "time" attribute
|
45
|
+
# # and converts the value to a Time object
|
46
|
+
# attribute :created_at, :time do |value|
|
47
|
+
# Time.at(value)
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# # Define a "friends" attribute that does *not* get loaded from Turntable
|
51
|
+
# # when accessed
|
52
|
+
# attribute :friends, :load => false
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
# @!macro [attach] attribute
|
56
|
+
# @!attribute [r] $1
|
57
|
+
def attribute(name, *turntable_names, &block)
|
58
|
+
options = turntable_names.last.is_a?(Hash) ? turntable_names.pop : {}
|
59
|
+
assert_valid_keys(options, :load)
|
60
|
+
options = {:load => true}.merge(options)
|
61
|
+
|
62
|
+
# Reader
|
63
|
+
define_method(name) do
|
64
|
+
load if instance_variable_get("@#{name}").nil? && !loaded? && options[:load]
|
65
|
+
instance_variable_get("@#{name}")
|
66
|
+
end
|
67
|
+
|
68
|
+
# Query
|
69
|
+
define_method("#{name}?") do
|
70
|
+
!!__send__(name)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Typecasting
|
74
|
+
block ||= lambda {|value| value}
|
75
|
+
define_method("typecast_#{name}", &block)
|
76
|
+
protected :"typecast_#{name}"
|
77
|
+
|
78
|
+
# Attribute name conversion
|
79
|
+
turntable_names = [name] if turntable_names.empty?
|
80
|
+
turntable_names.each do |turntable_name|
|
81
|
+
define_method("#{turntable_name}=") do |value|
|
82
|
+
instance_variable_set("@#{name}", value.nil? ? nil : __send__("typecast_#{name}", value))
|
83
|
+
end
|
84
|
+
protected :"#{turntable_name}="
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# The unique identifier for this resource
|
90
|
+
# @return [String, Fixnum]
|
91
|
+
attribute :id, :_id, :load => false
|
92
|
+
|
93
|
+
# Initializes this resources with the given attributes. This will continue
|
94
|
+
# to call the superclass's constructor with any additional arguments that
|
95
|
+
# get specified.
|
96
|
+
#
|
97
|
+
# @api private
|
98
|
+
def initialize(client, attributes = {}, *args)
|
99
|
+
@loaded = false
|
100
|
+
@client = client
|
101
|
+
self.attributes = attributes
|
102
|
+
super(*args)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Loads the attributes for this resource from Turntable. By default this is
|
106
|
+
# a no-op and just marks the resource as loaded.
|
107
|
+
#
|
108
|
+
# @return [true]
|
109
|
+
def load
|
110
|
+
@loaded = true
|
111
|
+
end
|
112
|
+
alias :reload :load
|
113
|
+
|
114
|
+
# Determines whether the current resource has been loaded from Turntable.
|
115
|
+
#
|
116
|
+
# @return [Boolean] +true+ if the resource has been loaded, otherwise +false+
|
117
|
+
def loaded?
|
118
|
+
@loaded
|
119
|
+
end
|
120
|
+
|
121
|
+
# Attempts to set attributes on the object only if they've been explicitly
|
122
|
+
# defined by the class. Note that this will also attempt to interpret any
|
123
|
+
# "metadata" properties as additional attributes.
|
124
|
+
#
|
125
|
+
# @api private
|
126
|
+
def attributes=(attributes)
|
127
|
+
if attributes
|
128
|
+
attributes.each do |attribute, value|
|
129
|
+
attribute = attribute.to_s
|
130
|
+
if attribute == 'metadata'
|
131
|
+
self.attributes = value
|
132
|
+
else
|
133
|
+
__send__("#{attribute}=", value) if respond_to?("#{attribute}=")
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Forces this object to use PP's implementation of inspection.
|
140
|
+
#
|
141
|
+
# @api private
|
142
|
+
def pretty_print(q)
|
143
|
+
q.pp_object(self)
|
144
|
+
end
|
145
|
+
alias inspect pretty_print_inspect
|
146
|
+
|
147
|
+
# Defines the instance variables that should be printed when inspecting this
|
148
|
+
# object. This ignores the +client+ and +loaded+ attributes.
|
149
|
+
#
|
150
|
+
# @api private
|
151
|
+
def pretty_print_instance_variables
|
152
|
+
(instance_variables - [:'@client', :'@loaded']).sort
|
153
|
+
end
|
154
|
+
|
155
|
+
# Determines whether this resource is equal to another based on their
|
156
|
+
# unique identifiers.
|
157
|
+
#
|
158
|
+
# @return [Boolean] +true+ if the resource ids are equal, otherwise +false+
|
159
|
+
def ==(other)
|
160
|
+
if other && other.respond_to?(:id) && other.id
|
161
|
+
other.id == id
|
162
|
+
else
|
163
|
+
false
|
164
|
+
end
|
165
|
+
end
|
166
|
+
alias :eql? :==
|
167
|
+
|
168
|
+
# Generates a hash for this resource based on the unique identifier
|
169
|
+
#
|
170
|
+
# @return [Fixnum]
|
171
|
+
def hash
|
172
|
+
id.hash
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
# The client that all APIs filter through
|
177
|
+
attr_reader :client
|
178
|
+
|
179
|
+
# Runs the given API command on the client.
|
180
|
+
def api(command, options = {})
|
181
|
+
client.api(command, options)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Gets the current room the user is in
|
185
|
+
def room
|
186
|
+
client.room || raise(Turntabler::Error, 'User is not currently in a room')
|
187
|
+
end
|
188
|
+
|
189
|
+
# Determines whether the user is currently in a room
|
190
|
+
def room?
|
191
|
+
!client.room.nil?
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|