plezi 0.12.22 → 0.14.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/LICENSE.txt +17 -18
- data/README.md +54 -698
- data/Rakefile +7 -4
- data/bin/config.ru +22 -0
- data/{test → bin}/console +4 -6
- data/bin/hello_world +52 -0
- data/bin/setup +8 -0
- data/exe/plezi +145 -0
- data/lib/plezi.rb +24 -137
- data/lib/plezi/activation.rb +28 -0
- data/lib/plezi/api.rb +62 -0
- data/lib/plezi/controller/controller.rb +259 -0
- data/lib/plezi/controller/controller_class.rb +176 -0
- data/lib/plezi/controller/cookies.rb +40 -0
- data/lib/plezi/helpers.rb +60 -0
- data/lib/plezi/rake.rb +2 -24
- data/lib/plezi/render/erb.rb +34 -0
- data/lib/plezi/render/has_cache.rb +36 -0
- data/lib/plezi/render/markdown.rb +63 -0
- data/lib/plezi/render/render.rb +49 -0
- data/lib/plezi/render/sass.rb +55 -0
- data/lib/plezi/render/slim.rb +33 -0
- data/lib/plezi/router/adclient.rb +23 -0
- data/lib/plezi/router/assets.rb +67 -0
- data/lib/plezi/router/errors.rb +29 -0
- data/lib/plezi/router/route.rb +112 -0
- data/lib/plezi/router/router.rb +120 -0
- data/lib/plezi/version.rb +1 -1
- data/lib/plezi/websockets/message_dispatch.rb +91 -0
- data/lib/plezi/websockets/redis.rb +55 -0
- data/plezi.gemspec +25 -16
- data/resources/404.erb +5 -4
- data/resources/500.erb +5 -4
- data/resources/{500.html → 503.html} +8 -9
- data/resources/client.js +253 -0
- data/resources/config.ru +5 -36
- data/resources/ctrlr.rb +34 -0
- data/resources/gemfile +4 -0
- data/resources/mini_app.rb +28 -82
- data/resources/mini_exec +7 -0
- data/resources/mini_welcome_page.html +0 -0
- data/resources/procfile +3 -0
- data/resources/rakefile +4 -8
- data/resources/routes.rb +9 -26
- data/resources/{websockets.js → simple-client.js} +3 -3
- metadata +60 -85
- data/bin/plezi +0 -104
- data/docs/async_helpers.md +0 -245
- data/docs/controllers.md +0 -27
- data/docs/logging.md +0 -49
- data/docs/routes.md +0 -209
- data/docs/websockets.md +0 -213
- data/lib/plezi/builders/ac_model.rb +0 -59
- data/lib/plezi/builders/app_builder.rb +0 -137
- data/lib/plezi/builders/builder.rb +0 -43
- data/lib/plezi/builders/form_builder.rb +0 -27
- data/lib/plezi/common/api.rb +0 -92
- data/lib/plezi/common/cache.rb +0 -122
- data/lib/plezi/common/defer.rb +0 -21
- data/lib/plezi/common/dsl.rb +0 -94
- data/lib/plezi/common/redis.rb +0 -65
- data/lib/plezi/common/renderer.rb +0 -141
- data/lib/plezi/common/settings.rb +0 -52
- data/lib/plezi/handlers/controller_core.rb +0 -106
- data/lib/plezi/handlers/controller_magic.rb +0 -284
- data/lib/plezi/handlers/http_router.rb +0 -205
- data/lib/plezi/handlers/placebo.rb +0 -112
- data/lib/plezi/handlers/route.rb +0 -216
- data/lib/plezi/handlers/session.rb +0 -109
- data/lib/plezi/handlers/stubs.rb +0 -156
- data/lib/plezi/handlers/ws_identity.rb +0 -253
- data/lib/plezi/handlers/ws_object.rb +0 -308
- data/lib/plezi/helpers/http_sender.rb +0 -84
- data/lib/plezi/helpers/magic_helpers.rb +0 -104
- data/lib/plezi/helpers/mime_types.rb +0 -1995
- data/lib/plezi/oauth.rb +0 -5
- data/lib/plezi/oauth/auth_controller.rb +0 -229
- data/logo/dark.png +0 -0
- data/logo/light.png +0 -0
- data/logo/sign.png +0 -0
- data/resources/404.haml +0 -121
- data/resources/404.html +0 -124
- data/resources/404.slim +0 -120
- data/resources/500.haml +0 -120
- data/resources/500.slim +0 -120
- data/resources/Gemfile +0 -86
- data/resources/code.rb +0 -8
- data/resources/controller.rb +0 -142
- data/resources/database.yml +0 -33
- data/resources/db_ac_config.rb +0 -59
- data/resources/db_dm_config.rb +0 -51
- data/resources/db_sequel_config.rb +0 -33
- data/resources/en.yml +0 -204
- data/resources/haml_config.rb +0 -6
- data/resources/i18n_config.rb +0 -14
- data/resources/initialize.rb +0 -49
- data/resources/mini_exec.rb +0 -7
- data/resources/oauth_config.rb +0 -24
- data/resources/plezi_client.js +0 -198
- data/resources/plezi_websockets.html +0 -47
- data/resources/redis_config.rb +0 -42
- data/resources/slim_config.rb +0 -11
- data/resources/welcome_page.html +0 -272
- data/test/dispatch +0 -58
- data/test/hello_world +0 -13
- data/test/plezi_tests.rb +0 -581
@@ -1,253 +0,0 @@
|
|
1
|
-
module Plezi
|
2
|
-
|
3
|
-
module Base
|
4
|
-
|
5
|
-
module WSObject
|
6
|
-
|
7
|
-
# Used to emulate the Redis connection when the Identoty API
|
8
|
-
# is used on a single process with no Redis support.
|
9
|
-
module RedisEmultaion
|
10
|
-
public
|
11
|
-
def lrange key, first, last = -1
|
12
|
-
sync do
|
13
|
-
return [] unless @cache[key]
|
14
|
-
@cache[key][first..last] || []
|
15
|
-
end
|
16
|
-
end
|
17
|
-
def llen key
|
18
|
-
sync do
|
19
|
-
return 0 unless @cache[key]
|
20
|
-
@cache[key].count
|
21
|
-
end
|
22
|
-
end
|
23
|
-
def ltrim key, first, last = -1
|
24
|
-
sync do
|
25
|
-
return "OK".freeze unless @cache[key]
|
26
|
-
@cache[key] = @cache[key][first..last]
|
27
|
-
"OK".freeze
|
28
|
-
end
|
29
|
-
end
|
30
|
-
def del *keys
|
31
|
-
sync do
|
32
|
-
ret = 0
|
33
|
-
keys.each {|k| ret += 1 if @cache.delete k }
|
34
|
-
ret
|
35
|
-
end
|
36
|
-
end
|
37
|
-
def lpush key, value
|
38
|
-
sync do
|
39
|
-
@cache[key] ||= []
|
40
|
-
@cache[key].unshift value
|
41
|
-
@cache[key].count
|
42
|
-
end
|
43
|
-
end
|
44
|
-
def lpop key
|
45
|
-
sync do
|
46
|
-
@cache[key] ||= []
|
47
|
-
@cache[key].shift
|
48
|
-
end
|
49
|
-
end
|
50
|
-
def lrem key, count, value
|
51
|
-
sync do
|
52
|
-
@cache[key] ||= []
|
53
|
-
@cache[key].delete(value)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
def rpush key, value
|
57
|
-
sync do
|
58
|
-
@cache[key] ||= []
|
59
|
-
@cache[key].push value
|
60
|
-
@cache[key].count
|
61
|
-
end
|
62
|
-
end
|
63
|
-
def expire key, seconds
|
64
|
-
@warning_sent ||= Iodine.warn "Identity API requires Redis - no persistent storage!".freeze
|
65
|
-
sync do
|
66
|
-
return 0 unless @cache[key]
|
67
|
-
if @timers[key]
|
68
|
-
@timers[key].stop!
|
69
|
-
end
|
70
|
-
@timers[key] = (Iodine.run_after(seconds) { self.del key })
|
71
|
-
end
|
72
|
-
end
|
73
|
-
def multi
|
74
|
-
sync do
|
75
|
-
@results = []
|
76
|
-
yield(self)
|
77
|
-
ret = @results
|
78
|
-
@results = nil
|
79
|
-
ret
|
80
|
-
end
|
81
|
-
end
|
82
|
-
alias :pipelined :multi
|
83
|
-
protected
|
84
|
-
@locker = Mutex.new
|
85
|
-
@cache = Hash.new
|
86
|
-
@timers = Hash.new
|
87
|
-
|
88
|
-
def sync &block
|
89
|
-
if @locker.locked? && @locker.owned?
|
90
|
-
ret = yield
|
91
|
-
@results << ret if @results
|
92
|
-
ret
|
93
|
-
else
|
94
|
-
@locker.synchronize { sync(&block) }
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
public
|
99
|
-
extend self
|
100
|
-
end
|
101
|
-
|
102
|
-
# the following are additions to the WebSocket Object module,
|
103
|
-
# to establish identity to websocket realtionships, allowing for a
|
104
|
-
# websocket message bank.
|
105
|
-
|
106
|
-
module InstanceMethods
|
107
|
-
protected
|
108
|
-
|
109
|
-
# @!visibility public
|
110
|
-
# The following method registers the connections as a unique global identity.
|
111
|
-
#
|
112
|
-
# The Identity API works best when a Redis server is used. See {Plezi#redis} for more information.
|
113
|
-
#
|
114
|
-
# By default, only one connection at a time can respond to identity events. If the same identity
|
115
|
-
# connects more than once, only the last connection will receive the notifications.
|
116
|
-
# This default may be controlled by setting the `:max_connections` option to a number greater than 1.
|
117
|
-
#
|
118
|
-
# The method accepts:
|
119
|
-
# identity:: a global application wide unique identifier that will persist throughout all of the identity's connections.
|
120
|
-
# options:: an option's hash that sets the properties of the identity.
|
121
|
-
#
|
122
|
-
# The option's Hash, at the moment, accepts only the following (optional) options:
|
123
|
-
# lifetime:: sets how long the identity can survive. defaults to `604_800` seconds (7 days).
|
124
|
-
# max_connections:: sets the amount of concurrent connections an identity can have (akin to open browser tabs receiving notifications). defaults to 1 (a single connection).
|
125
|
-
#
|
126
|
-
# Lifetimes are renewed with each registration and when a connected Identoty receives a notification.
|
127
|
-
# Identities should have a reasonable lifetime. For example, a 10 minutes long lifetime (60*10)
|
128
|
-
# may prove ineffective. When using such short lifetimes, consider the possibility that `unicast` might provide be a better alternative.
|
129
|
-
#
|
130
|
-
# A lifetime cannot (by design) be shorter than 10 minutes.
|
131
|
-
#
|
132
|
-
# Calling this method will also initiate any events waiting in the identity's queue.
|
133
|
-
# make sure that the method is only called once all other initialization is complete.
|
134
|
-
#
|
135
|
-
# i.e.
|
136
|
-
#
|
137
|
-
# register_as session.id, lifetime: 60*60*24, max_connections: 4
|
138
|
-
#
|
139
|
-
# Do NOT call this method asynchronously unless Plezi is set to run as in a single threaded mode - doing so
|
140
|
-
# will execute any pending events outside the scope of the IO's mutex lock, thus introducing race conditions.
|
141
|
-
def register_as identity, options = {}
|
142
|
-
redis = Plezi.redis || ::Plezi::Base::WSObject::RedisEmultaion
|
143
|
-
options[:max_connections] ||= 1
|
144
|
-
options[:max_connections] = 1 if options[:max_connections].to_i < 1
|
145
|
-
options[:lifetime] ||= 604_800
|
146
|
-
options[:lifetime] = 600 if options[:lifetime].to_i < 600
|
147
|
-
identity = identity.to_s.freeze
|
148
|
-
@___identity ||= {}
|
149
|
-
@___identity[identity] = options
|
150
|
-
redis.multi do
|
151
|
-
redis.lpop(identity)
|
152
|
-
redis.lpush(identity, ''.freeze)
|
153
|
-
redis.lrem "#{identity}_uuid".freeze, 0, uuid
|
154
|
-
redis.lpush "#{identity}_uuid".freeze, uuid
|
155
|
-
redis.ltrim "#{identity}_uuid".freeze, 0, (options[:max_connections]-1)
|
156
|
-
redis.expire identity, options[:lifetime]
|
157
|
-
redis.expire "#{identity}_uuid".freeze, options[:lifetime]
|
158
|
-
end
|
159
|
-
___review_identity identity
|
160
|
-
identity
|
161
|
-
end
|
162
|
-
|
163
|
-
# @!visibility public
|
164
|
-
# sends a notification to an Identity. Returns false if the Identity never registered or it's registration expired.
|
165
|
-
def notify identity, event_name, *args
|
166
|
-
self.class.notify identity, event_name, *args
|
167
|
-
end
|
168
|
-
# @!visibility public
|
169
|
-
# returns true if the Identity in question is registered to receive notifications.
|
170
|
-
def registered? identity
|
171
|
-
self.class.registered? identity
|
172
|
-
end
|
173
|
-
# # handles websocket being closed.
|
174
|
-
# def on_close
|
175
|
-
# super if defined? super
|
176
|
-
# redis = Plezi.redis || ::Plezi::Base::WSObject::RedisEmultaion
|
177
|
-
# @___identity.each { |identity| redis.lrem "#{identity}_uuid".freeze, 0, uuid }
|
178
|
-
# end
|
179
|
-
end
|
180
|
-
module ClassMethods
|
181
|
-
end
|
182
|
-
module SuperInstanceMethods
|
183
|
-
protected
|
184
|
-
|
185
|
-
# this is the identity event and ittells the connection to "read" the messages in the "mailbox",
|
186
|
-
# and forward the messages to the rest of the connections.
|
187
|
-
def ___review_identity identity
|
188
|
-
redis = Plezi.redis || ::Plezi::Base::WSObject::RedisEmultaion
|
189
|
-
identity = identity.to_s.freeze
|
190
|
-
return Iodine.warn("Identity message reached wrong target (ignored).").clear unless @___identity[identity]
|
191
|
-
messages = redis.multi do
|
192
|
-
redis.lrange identity, 1, -1
|
193
|
-
redis.ltrim identity, 0, 0
|
194
|
-
redis.expire identity, @___identity[identity][:lifetime]
|
195
|
-
redis.expire "#{identity}_uuid".freeze, @___identity[identity][:lifetime]
|
196
|
-
end[0]
|
197
|
-
targets = redis.lrange "#{identity}_uuid", 0, -1
|
198
|
-
targets.delete(uuid)
|
199
|
-
while msg = messages.shift
|
200
|
-
msg = ::Plezi::Base::WSObject.translate_message(msg)
|
201
|
-
next unless msg
|
202
|
-
Iodine.error("Notification recieved but no method can handle it - dump:\r\n #{msg.to_s}") && next unless self.class.has_super_method?(msg[:method])
|
203
|
-
Iodine.run do
|
204
|
-
targets.each {|target| unicast(target, msg[:method], *msg[:data]) }
|
205
|
-
end
|
206
|
-
self.__send__(msg[:method], *msg[:data])
|
207
|
-
end
|
208
|
-
# ___extend_lifetime identity
|
209
|
-
end
|
210
|
-
|
211
|
-
# # re-registers the Identity, extending it's lifetime
|
212
|
-
# # and making sure it's still valid.
|
213
|
-
# def ___extend_lifetime identity
|
214
|
-
# return unless @___identity
|
215
|
-
# redis = Plezi.redis || ::Plezi::Base::WSObject::RedisEmultaion
|
216
|
-
# options = @___identity[identity]
|
217
|
-
# return unless options
|
218
|
-
# redis.multi do
|
219
|
-
# # redis.lpop(identity)
|
220
|
-
# # redis.lpush(identity, ''.freeze)
|
221
|
-
# # redis.lrem "#{identity}_uuid".freeze, 0, uuid
|
222
|
-
# # redis.lpush "#{identity}_uuid".freeze, uuid
|
223
|
-
# # redis.ltrim "#{identity}_uuid".freeze, 0, (options[:max_connections]-1)
|
224
|
-
# redis.expire identity, options[:lifetime]
|
225
|
-
# redis.expire "#{identity}_uuid".freeze, options[:lifetime]
|
226
|
-
# end
|
227
|
-
# end
|
228
|
-
end
|
229
|
-
|
230
|
-
module SuperClassMethods
|
231
|
-
public
|
232
|
-
|
233
|
-
# sends a notification to an Identity. Returns false if the Identity never registered or it's registration expired.
|
234
|
-
def notify identity, event_name, *args
|
235
|
-
redis = Plezi.redis || ::Plezi::Base::WSObject::RedisEmultaion
|
236
|
-
identity = identity.to_s.freeze
|
237
|
-
return false unless redis.llen(identity).to_i > 0
|
238
|
-
redis.rpush identity, ({method: event_name, data: args}).to_yaml
|
239
|
-
redis.lrange("#{identity}_uuid".freeze, 0, -1).each {|target| unicast target, :___review_identity, identity }
|
240
|
-
# puts "pushed notification #{event_name}"
|
241
|
-
true
|
242
|
-
end
|
243
|
-
|
244
|
-
# returns true if the Identity in question is registered to receive notifications.
|
245
|
-
def registered? identity
|
246
|
-
redis = Plezi.redis || ::Plezi::Base::WSObject::RedisEmultaion
|
247
|
-
identity = identity.to_s.freeze
|
248
|
-
redis.llen(identity).to_i > 0
|
249
|
-
end
|
250
|
-
end
|
251
|
-
end
|
252
|
-
end
|
253
|
-
end
|
@@ -1,308 +0,0 @@
|
|
1
|
-
module Plezi
|
2
|
-
|
3
|
-
module Base
|
4
|
-
|
5
|
-
# This module includes all the methods that will be injected into Websocket objects,
|
6
|
-
# specifically into Plezi Controllers and Placebo objects.
|
7
|
-
#
|
8
|
-
# the methods defined in this module will be injected into the Controller class passed to
|
9
|
-
# Plezi (using the `route` or `shared_route` commands), and will be available
|
10
|
-
# for the controller to use within it's methods.
|
11
|
-
#
|
12
|
-
# for some reason, the documentation ignores the following additional attributes, which are listed here:
|
13
|
-
#
|
14
|
-
# request:: the HTTPRequest object containing all the data from the HTTP request. If a WebSocket connection was established, the `request` object will continue to contain the HTTP request establishing the connection (cookies, parameters sent and other information).
|
15
|
-
# params:: any parameters sent with the request (short-cut for `request.params`), will contain any GET or POST form data sent (including file upload and JSON format support).
|
16
|
-
# cookies:: a cookie-jar to get and set cookies (set: `cookie\[:name] = data` or get: `cookie\[:name]`). Cookies and some other data must be set BEFORE the response's headers are sent.
|
17
|
-
# flash:: a temporary cookie-jar, good for one request. this is a short-cut for the `response.flash` which handles this magical cookie style.
|
18
|
-
# response:: the HTTPResponse **OR** the WSResponse object that formats the response and sends it. use `response << data`. This object can be used to send partial data (such as headers, or partial html content) in blocking mode as well as sending data in the default non-blocking mode.
|
19
|
-
# host_params:: a copy of the parameters used to create the host and service which accepted the request and created this instance of the controller class.
|
20
|
-
#
|
21
|
-
module WSObject
|
22
|
-
def self.included base
|
23
|
-
base.send :include, InstanceMethods
|
24
|
-
base.extend ClassMethods
|
25
|
-
base.superclass.instance_eval {extend SuperClassMethods}
|
26
|
-
base.superclass.instance_eval {include SuperInstanceMethods}
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.translate_message msg
|
30
|
-
begin
|
31
|
-
@safe_types ||= [Symbol, Date, Time, Encoding, Struct, Regexp, Range, Set]
|
32
|
-
YAML.safe_load(msg, @safe_types)
|
33
|
-
rescue => e
|
34
|
-
Iodine.error "The following could be a security breach attempt:"
|
35
|
-
Iodine.error e
|
36
|
-
nil
|
37
|
-
end
|
38
|
-
end
|
39
|
-
def self.forward_message data
|
40
|
-
begin
|
41
|
-
return false if data[:server] == Plezi::Settings.uuid
|
42
|
-
data[:type] = Object.const_get(data[:type]) unless data[:type].nil? || data[:type] == :all
|
43
|
-
if data[:target]
|
44
|
-
data[:type].___faild_unicast( data ) unless Iodine::Http::Websockets.unicast data[:target], data
|
45
|
-
else
|
46
|
-
Iodine::Http::Websockets.broadcast data
|
47
|
-
end
|
48
|
-
rescue => e
|
49
|
-
Iodine.error "The following could be a security breach attempt:"
|
50
|
-
Iodine.error e
|
51
|
-
nil
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
module InstanceMethods
|
56
|
-
public
|
57
|
-
|
58
|
-
# handles websocket opening.
|
59
|
-
def on_open
|
60
|
-
@ws_io = @request[:io]
|
61
|
-
super() if defined?(super)
|
62
|
-
end
|
63
|
-
# handles websocket messages.
|
64
|
-
def on_message data
|
65
|
-
super if defined?(super)
|
66
|
-
end
|
67
|
-
# handles websocket being closed.
|
68
|
-
def on_close
|
69
|
-
super if defined? super
|
70
|
-
end
|
71
|
-
|
72
|
-
# handles broadcasts / unicasts
|
73
|
-
def on_broadcast data
|
74
|
-
unless data.is_a?(Hash) && (data[:type] || data[:target]) && data[:method] && data[:data]
|
75
|
-
Iodine.warn "Broadcast message unknown... falling back on base broadcasting"
|
76
|
-
return super(data) if defined? super
|
77
|
-
return false
|
78
|
-
end
|
79
|
-
return false if data[:type] && data[:type] != :all && !self.is_a?(data[:type])
|
80
|
-
# return (data[:data].each {|e| emit(e)}) if data[:method] == :emit
|
81
|
-
return ((data[:type] == :all) ? false : (raise "Broadcasting recieved but no method can handle it - dump:\r\n #{data.to_s}") ) unless self.class.has_super_method?(data[:method])
|
82
|
-
self.__send__(data[:method], *data[:data])
|
83
|
-
end
|
84
|
-
|
85
|
-
# Get's the websocket's unique identifier for unicast transmissions.
|
86
|
-
#
|
87
|
-
# This UUID is also used to make sure Radis broadcasts don't triger the
|
88
|
-
# boadcasting object's event.
|
89
|
-
def uuid
|
90
|
-
return @uuid if @uuid
|
91
|
-
if __get_io
|
92
|
-
return (@uuid ||= Plezi::Settings.uuid + @io.id)
|
93
|
-
end
|
94
|
-
nil
|
95
|
-
end
|
96
|
-
alias :unicast_id :uuid
|
97
|
-
|
98
|
-
protected
|
99
|
-
|
100
|
-
# @!visibility public
|
101
|
-
# allows writing of data to the websocket (if opened). Otherwise appends the message to the Http response.
|
102
|
-
def write data
|
103
|
-
(@ws_io || @response) << data
|
104
|
-
end
|
105
|
-
|
106
|
-
# # @!visibility public
|
107
|
-
# # A helper method for easily sending JSON data. Accepts a Hash that will be translated to JSON and sent to the client as a JSON string.
|
108
|
-
# #
|
109
|
-
# # This method is available as a broadcast event.
|
110
|
-
# def emit event
|
111
|
-
# write event.to_json
|
112
|
-
# end
|
113
|
-
|
114
|
-
# @!visibility public
|
115
|
-
# Closes the connection
|
116
|
-
def close
|
117
|
-
# @request[:io] contains the Websockets Protocol instance
|
118
|
-
(@ws_io || @request[:io]).go_away
|
119
|
-
end
|
120
|
-
|
121
|
-
# @!visibility public
|
122
|
-
# Performs a websocket unicast to the specified target.
|
123
|
-
def unicast target_uuid, method_name, *args
|
124
|
-
self.class.unicast target_uuid, method_name, *args
|
125
|
-
end
|
126
|
-
|
127
|
-
# @!visibility public
|
128
|
-
# Use this to brodcast an event to all 'sibling' objects (websockets that have been created using the same Controller class).
|
129
|
-
#
|
130
|
-
# Accepts:
|
131
|
-
# method_name:: a Symbol with the method's name that should respond to the broadcast.
|
132
|
-
# args*:: The method's argumenst - It MUST be possible to stringify the arguments into a YAML string, or broadcasting and unicasting will fail when scaling beyond one process / one machine.
|
133
|
-
#
|
134
|
-
# The method will be called asynchrnously for each sibling instance of this Controller class.
|
135
|
-
#
|
136
|
-
def broadcast method_name, *args
|
137
|
-
return false unless self.class.has_method? method_name
|
138
|
-
self.class._inner_broadcast({ method: method_name, data: args, type: self.class}, __get_io )
|
139
|
-
end
|
140
|
-
|
141
|
-
# @!visibility public
|
142
|
-
# Use this to multicast an event to ALL websocket connections on EVERY controller, including Placebo controllers.
|
143
|
-
#
|
144
|
-
# Accepts:
|
145
|
-
# method_name:: a Symbol with the method's name that should respond to the broadcast.
|
146
|
-
# args*:: The method's argumenst - It MUST be possible to stringify the arguments into a YAML string, or broadcasting and unicasting will fail when scaling beyond one process / one machine.
|
147
|
-
#
|
148
|
-
# The method will be called asynchrnously for ALL websocket connections.
|
149
|
-
#
|
150
|
-
def multicast method_name, *args
|
151
|
-
self.class._inner_broadcast({ method: method_name, data: args, type: :all}, __get_io )
|
152
|
-
end
|
153
|
-
|
154
|
-
def __get_io
|
155
|
-
@io ||= (@request ? @request[:io] : nil)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
module ClassMethods
|
159
|
-
|
160
|
-
def reset_routing_cache
|
161
|
-
@methods_list = nil
|
162
|
-
@exposed_methods_list = nil
|
163
|
-
@super_methods_list = nil
|
164
|
-
@auto_dispatch_list = nil
|
165
|
-
has_method? nil
|
166
|
-
has_exposed_method? nil
|
167
|
-
has_super_method? nil
|
168
|
-
has_auto_dispatch_method? nil
|
169
|
-
end
|
170
|
-
def has_method? method_name
|
171
|
-
@methods_list ||= self.instance_methods.to_set
|
172
|
-
@methods_list.include? method_name
|
173
|
-
end
|
174
|
-
def has_super_method? method_name
|
175
|
-
@super_methods_list ||= self.superclass.instance_methods.to_set
|
176
|
-
@super_methods_list.include? method_name
|
177
|
-
end
|
178
|
-
def has_exposed_method? method_name
|
179
|
-
@reserved_methods_list ||= Class.new.public_instance_methods +
|
180
|
-
Plezi::Base::WSObject::InstanceMethods.public_instance_methods +
|
181
|
-
Plezi::Base::WSObject::SuperInstanceMethods.public_instance_methods +
|
182
|
-
Plezi::ControllerMagic::InstanceMethods.public_instance_methods +
|
183
|
-
Plezi::Base::ControllerCore::InstanceMethods.public_instance_methods +
|
184
|
-
[:before, :after, :save, :show, :update, :delete, :initialize]
|
185
|
-
@exposed_methods_list ||= ( (self.public_instance_methods - @reserved_methods_list ).delete_if {|m| m.to_s[0] == '_'} ).to_set
|
186
|
-
@exposed_methods_list.include? method_name
|
187
|
-
end
|
188
|
-
def has_auto_dispatch_method? method_name
|
189
|
-
@auto_dispatch_list ||= (( self.instance_methods - (Class.new.instance_methods +
|
190
|
-
Plezi::Base::WSObject::InstanceMethods.instance_methods +
|
191
|
-
Plezi::Base::WSObject::SuperInstanceMethods.instance_methods +
|
192
|
-
Plezi::ControllerMagic::InstanceMethods.instance_methods +
|
193
|
-
Plezi::Base::ControllerCore::InstanceMethods.instance_methods +
|
194
|
-
[:before, :after, :initialize, :unknown , :unknown_event]) ).delete_if {|m| m.to_s[0] == '_' || instance_method(m).arity == 0 }).to_set
|
195
|
-
@auto_dispatch_list.include? method_name
|
196
|
-
end
|
197
|
-
|
198
|
-
protected
|
199
|
-
|
200
|
-
# a callback that resets the class router whenever a method (a potential route) is added
|
201
|
-
def method_added(id)
|
202
|
-
reset_routing_cache
|
203
|
-
end
|
204
|
-
# a callback that resets the class router whenever a method (a potential route) is removed
|
205
|
-
def method_removed(id)
|
206
|
-
reset_routing_cache
|
207
|
-
end
|
208
|
-
# a callback that resets the class router whenever a method (a potential route) is undefined (using #undef_method).
|
209
|
-
def method_undefined(id)
|
210
|
-
reset_routing_cache
|
211
|
-
end
|
212
|
-
|
213
|
-
end
|
214
|
-
module SuperInstanceMethods
|
215
|
-
end
|
216
|
-
|
217
|
-
module SuperClassMethods
|
218
|
-
public
|
219
|
-
|
220
|
-
# answers the question if this is a placebo object.
|
221
|
-
def placebo?; false end
|
222
|
-
|
223
|
-
# WebSockets: fires an event on all of this controller's active websocket connections.
|
224
|
-
#
|
225
|
-
# Class method.
|
226
|
-
#
|
227
|
-
# Use this to brodcast an event to all connections.
|
228
|
-
#
|
229
|
-
# accepts:
|
230
|
-
# method_name:: a Symbol with the method's name that should respond to the broadcast.
|
231
|
-
# *args:: any arguments that should be passed to the method (IF REDIS IS USED, LIMITATIONS APPLY).
|
232
|
-
#
|
233
|
-
# this method accepts and optional block (NON-REDIS ONLY) to be used as a callback for each sibling's event.
|
234
|
-
#
|
235
|
-
# the method will be called asynchrnously for each sibling instance of this Controller class.
|
236
|
-
def broadcast method_name, *args
|
237
|
-
return false unless has_method? method_name
|
238
|
-
_inner_broadcast method: method_name, data: args, type: self
|
239
|
-
end
|
240
|
-
|
241
|
-
# WebSockets: fires an event on a specific websocket connection using it's UUID.
|
242
|
-
#
|
243
|
-
# Use this to unidcast an event to specific websocket connection using it's UUID.
|
244
|
-
#
|
245
|
-
# accepts:
|
246
|
-
# target_uuid:: the target's unique UUID.
|
247
|
-
# method_name:: a Symbol with the method's name that should respond to the broadcast.
|
248
|
-
# *args:: any arguments that should be passed to the method (IF REDIS IS USED, LIMITATIONS APPLY).
|
249
|
-
def unicast target_uuid, method_name, *args
|
250
|
-
raise 'No target specified for unicasting!' unless target_uuid
|
251
|
-
@uuid_cutoff ||= Plezi::Settings.uuid.length
|
252
|
-
_inner_broadcast method: method_name, data: args, target: target_uuid[@uuid_cutoff..-1], to_server: target_uuid[0...@uuid_cutoff], type: :all
|
253
|
-
end
|
254
|
-
|
255
|
-
# Use this to multicast an event to ALL websocket connections on EVERY controller, including Placebo controllers.
|
256
|
-
#
|
257
|
-
# Accepts:
|
258
|
-
# method_name:: a Symbol with the method's name that should respond to the broadcast.
|
259
|
-
# args*:: The method's argumenst - It MUST be possible to stringify the arguments into a YAML string, or broadcasting and unicasting will fail when scaling beyond one process / one machine.
|
260
|
-
#
|
261
|
-
# The method will be called asynchrnously for ALL websocket connections.
|
262
|
-
#
|
263
|
-
def multicast method_name, *args
|
264
|
-
_inner_broadcast method: method_name, data: args, type: :all
|
265
|
-
end
|
266
|
-
|
267
|
-
# WebSockets
|
268
|
-
|
269
|
-
# sends the broadcast
|
270
|
-
def _inner_broadcast data, ignore_io = nil
|
271
|
-
if data[:target]
|
272
|
-
if data[:to_server] == Plezi::Settings.uuid
|
273
|
-
return ( ::Iodine::Http::Websockets.unicast( data[:target], data ) || ___faild_unicast( data ) )
|
274
|
-
end
|
275
|
-
return ( data[:to_server].nil? && ::Iodine::Http::Websockets.unicast(data[:target], data) ) || ( Plezi::Base::AutoRedis.away?(data[:to_server]) && ___faild_unicast( data ) ) || __inner_redis_broadcast(data)
|
276
|
-
else
|
277
|
-
::Iodine::Http::Websockets.broadcast data, ignore_io
|
278
|
-
__inner_redis_broadcast data
|
279
|
-
end
|
280
|
-
true
|
281
|
-
end
|
282
|
-
|
283
|
-
def __inner_redis_broadcast data
|
284
|
-
return unless conn = Plezi.redis
|
285
|
-
data = data.dup
|
286
|
-
data[:type] = data[:type].name if data[:type].is_a?(Class)
|
287
|
-
data[:server] = Plezi::Settings.uuid
|
288
|
-
return conn.publish( ( data[:to_server] || Plezi::Settings.redis_channel_name ), data.to_yaml ) if conn
|
289
|
-
false
|
290
|
-
end
|
291
|
-
|
292
|
-
def ___faild_unicast data
|
293
|
-
has_class_method?(:failed_unicast) && failed_unicast( data[:to_server].to_s + data[:target], data[:method], data[:data] )
|
294
|
-
true
|
295
|
-
end
|
296
|
-
|
297
|
-
def has_method? method_name
|
298
|
-
@methods_list ||= self.instance_methods.to_set
|
299
|
-
@methods_list.include? method_name
|
300
|
-
end
|
301
|
-
def has_class_method? method_name
|
302
|
-
@class_methods_list ||= self.methods.to_set - Object.methods
|
303
|
-
@class_methods_list.include? method_name
|
304
|
-
end
|
305
|
-
end
|
306
|
-
end
|
307
|
-
end
|
308
|
-
end
|