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.
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