plezi 0.12.22 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/LICENSE.txt +17 -18
  4. data/README.md +54 -698
  5. data/Rakefile +7 -4
  6. data/bin/config.ru +22 -0
  7. data/{test → bin}/console +4 -6
  8. data/bin/hello_world +52 -0
  9. data/bin/setup +8 -0
  10. data/exe/plezi +145 -0
  11. data/lib/plezi.rb +24 -137
  12. data/lib/plezi/activation.rb +28 -0
  13. data/lib/plezi/api.rb +62 -0
  14. data/lib/plezi/controller/controller.rb +259 -0
  15. data/lib/plezi/controller/controller_class.rb +176 -0
  16. data/lib/plezi/controller/cookies.rb +40 -0
  17. data/lib/plezi/helpers.rb +60 -0
  18. data/lib/plezi/rake.rb +2 -24
  19. data/lib/plezi/render/erb.rb +34 -0
  20. data/lib/plezi/render/has_cache.rb +36 -0
  21. data/lib/plezi/render/markdown.rb +63 -0
  22. data/lib/plezi/render/render.rb +49 -0
  23. data/lib/plezi/render/sass.rb +55 -0
  24. data/lib/plezi/render/slim.rb +33 -0
  25. data/lib/plezi/router/adclient.rb +23 -0
  26. data/lib/plezi/router/assets.rb +67 -0
  27. data/lib/plezi/router/errors.rb +29 -0
  28. data/lib/plezi/router/route.rb +112 -0
  29. data/lib/plezi/router/router.rb +120 -0
  30. data/lib/plezi/version.rb +1 -1
  31. data/lib/plezi/websockets/message_dispatch.rb +91 -0
  32. data/lib/plezi/websockets/redis.rb +55 -0
  33. data/plezi.gemspec +25 -16
  34. data/resources/404.erb +5 -4
  35. data/resources/500.erb +5 -4
  36. data/resources/{500.html → 503.html} +8 -9
  37. data/resources/client.js +253 -0
  38. data/resources/config.ru +5 -36
  39. data/resources/ctrlr.rb +34 -0
  40. data/resources/gemfile +4 -0
  41. data/resources/mini_app.rb +28 -82
  42. data/resources/mini_exec +7 -0
  43. data/resources/mini_welcome_page.html +0 -0
  44. data/resources/procfile +3 -0
  45. data/resources/rakefile +4 -8
  46. data/resources/routes.rb +9 -26
  47. data/resources/{websockets.js → simple-client.js} +3 -3
  48. metadata +60 -85
  49. data/bin/plezi +0 -104
  50. data/docs/async_helpers.md +0 -245
  51. data/docs/controllers.md +0 -27
  52. data/docs/logging.md +0 -49
  53. data/docs/routes.md +0 -209
  54. data/docs/websockets.md +0 -213
  55. data/lib/plezi/builders/ac_model.rb +0 -59
  56. data/lib/plezi/builders/app_builder.rb +0 -137
  57. data/lib/plezi/builders/builder.rb +0 -43
  58. data/lib/plezi/builders/form_builder.rb +0 -27
  59. data/lib/plezi/common/api.rb +0 -92
  60. data/lib/plezi/common/cache.rb +0 -122
  61. data/lib/plezi/common/defer.rb +0 -21
  62. data/lib/plezi/common/dsl.rb +0 -94
  63. data/lib/plezi/common/redis.rb +0 -65
  64. data/lib/plezi/common/renderer.rb +0 -141
  65. data/lib/plezi/common/settings.rb +0 -52
  66. data/lib/plezi/handlers/controller_core.rb +0 -106
  67. data/lib/plezi/handlers/controller_magic.rb +0 -284
  68. data/lib/plezi/handlers/http_router.rb +0 -205
  69. data/lib/plezi/handlers/placebo.rb +0 -112
  70. data/lib/plezi/handlers/route.rb +0 -216
  71. data/lib/plezi/handlers/session.rb +0 -109
  72. data/lib/plezi/handlers/stubs.rb +0 -156
  73. data/lib/plezi/handlers/ws_identity.rb +0 -253
  74. data/lib/plezi/handlers/ws_object.rb +0 -308
  75. data/lib/plezi/helpers/http_sender.rb +0 -84
  76. data/lib/plezi/helpers/magic_helpers.rb +0 -104
  77. data/lib/plezi/helpers/mime_types.rb +0 -1995
  78. data/lib/plezi/oauth.rb +0 -5
  79. data/lib/plezi/oauth/auth_controller.rb +0 -229
  80. data/logo/dark.png +0 -0
  81. data/logo/light.png +0 -0
  82. data/logo/sign.png +0 -0
  83. data/resources/404.haml +0 -121
  84. data/resources/404.html +0 -124
  85. data/resources/404.slim +0 -120
  86. data/resources/500.haml +0 -120
  87. data/resources/500.slim +0 -120
  88. data/resources/Gemfile +0 -86
  89. data/resources/code.rb +0 -8
  90. data/resources/controller.rb +0 -142
  91. data/resources/database.yml +0 -33
  92. data/resources/db_ac_config.rb +0 -59
  93. data/resources/db_dm_config.rb +0 -51
  94. data/resources/db_sequel_config.rb +0 -33
  95. data/resources/en.yml +0 -204
  96. data/resources/haml_config.rb +0 -6
  97. data/resources/i18n_config.rb +0 -14
  98. data/resources/initialize.rb +0 -49
  99. data/resources/mini_exec.rb +0 -7
  100. data/resources/oauth_config.rb +0 -24
  101. data/resources/plezi_client.js +0 -198
  102. data/resources/plezi_websockets.html +0 -47
  103. data/resources/redis_config.rb +0 -42
  104. data/resources/slim_config.rb +0 -11
  105. data/resources/welcome_page.html +0 -272
  106. data/test/dispatch +0 -58
  107. data/test/hello_world +0 -13
  108. 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