plezi 0.12.22 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|