turntabler 0.0.1
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/.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
|