plezi 0.15.1 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 69e6f1d14836d5d173d7b090bfaecec8bcd97d7a5dccd8a9ecf313c69eadbdf1
4
- data.tar.gz: 56e065663af0fc0e012eb7d1ab4c3cef1bf19c8c5dbc8b77997b6e4331f44b4b
3
+ metadata.gz: '09945baaea8ad6231605d6d6b72df2b66d369ca413709de0af5809a21e94d634'
4
+ data.tar.gz: a1659a6945166b11210cd14bc5ba3d10e00cbae57fe8c5de20b6af6ab92dd876
5
5
  SHA512:
6
- metadata.gz: 03dee5697a4c60ad40876632291a03fde3a5ebaef094f6b7f0c0c5cfd51450b4904c5ec31d10e83b17253f612ae4807dda8b98eaa588124754fd7b98c918d86a
7
- data.tar.gz: 8ee4fc521192fb6106afb01cbea9df1ec0b663e514aac8cab6a1c06143ec3816fe7764bcedbd7830d06ac40d48bbc93892ae23205830de3c99e4896a61a82d6a
6
+ metadata.gz: 768d697c97ef0eed692f993f1b19bee20021d809d02111aaa310c5b0c6fa67d70f5e64394db7f29889d968e544bae4126994f1b55402ea477815b5440b14219b
7
+ data.tar.gz: b90e2722ae0b095b41a42769ed2e0d988d6042c41bafa9545e31740cd4316398d415c8dfb64fd6c0d3501687a9f6d494332d1eda0752ad7d9f42882bef38ac70
@@ -2,6 +2,18 @@
2
2
 
3
3
  ***
4
4
 
5
+ Change log v.0.16.0
6
+
7
+ **Update**: update gem dependency to utilize `iodine` 0.5.x versions.
8
+
9
+ ***
10
+
11
+ Change log v.0.15.1
12
+
13
+ **Fix**: fixed gem dependency to limit the allowed versions of the `iodine` server and protect against changes made to the iodine 0.5.0 pub/sub API.
14
+
15
+ ***
16
+
5
17
  Change log v.0.15.0
6
18
 
7
19
  **Deprecation**: no more `broadcast`, `unicast`, `multicast` or `write2everyone`... Plezi fully embraced the Pub/Sub design and the [Iodine Extensions to the Rack Websocket Specification Proposal](https://github.com/boazsegev/iodine/blob/master/SPEC-Websocket-Draft.md).
@@ -12,17 +12,18 @@ require 'bundler/setup'
12
12
  require 'plezi'
13
13
 
14
14
  class ShootoutApp
15
+ CHANNEL = "shootout".freeze
15
16
  # the default HTTP response
16
17
  def index
17
18
  "This application should be used with the websocket-shootout benchmark utility."
18
19
  end
19
20
  def on_open
20
- subscribe channel: "shootout"
21
+ subscribe CHANNEL
21
22
  end
22
23
  # we won't be using AutoDispatch, but directly using the `on_message` callback.
23
24
  def on_message data
24
25
  if data[0] == 'b' # binary
25
- publish(channel: "shootout", message: data)
26
+ publish(CHANNEL, data)
26
27
  data[0] = 'r'
27
28
  write data
28
29
  return
@@ -31,9 +32,7 @@ class ShootoutApp
31
32
  if cmd == 'echo'
32
33
  write({type: 'echo', payload: payload}.to_json)
33
34
  else
34
- # data = {type: 'broadcast', payload: payload}.to_json
35
- # broadcast :push2client, data
36
- publish(channel: "shootout", message: ({type: 'broadcast', payload: payload}.to_json))
35
+ publish(CHANNEL, ({type: 'broadcast', payload: payload}.to_json))
37
36
  write({type: "broadcastResult", payload: payload}.to_json)
38
37
  end
39
38
  rescue
@@ -17,13 +17,14 @@ module Plezi
17
17
  @plezi_autostart = true if @plezi_autostart.nil?
18
18
  Iodine.patch_rack
19
19
  if((ENV['PL_REDIS_URL'.freeze] ||= ENV['REDIS_URL'.freeze]))
20
- uri = URI(ENV['PL_REDIS_URL'.freeze])
21
- Iodine.default_pubsub = Iodine::PubSub::RedisEngine.new(uri.host, uri.port, (ENV['PL_REDIS_TIMEOUT'.freeze] || ENV['REDIS_TIMEOUT'.freeze]).to_i, uri.password)
22
- Iodine.default_pubsub = Iodine::PubSub::Cluster unless Iodine.default_pubsub
20
+ ping = ENV['PL_REDIS_TIMEOUT'.freeze] || ENV['REDIS_TIMEOUT'.freeze]
21
+ ping = ping.to_i if ping
22
+ Iodine::PubSub.default = Iodine::PubSub::RedisEngine.new(ENV['PL_REDIS_URL'.freeze], ping: ping)
23
+ Iodine::PubSub.default = Iodine::PubSub::CLUSTER unless Iodine::PubSub.default
23
24
  end
24
25
  at_exit do
25
26
  next if @plezi_autostart == false
26
- ::Iodine::Rack.app = ::Plezi.app
27
+ ::Iodine.listen2http app: ::Plezi.app
27
28
  ::Iodine.start
28
29
  end
29
30
  end
@@ -0,0 +1,43 @@
1
+ module Plezi
2
+ module Base
3
+ # This module bridges between the Plezi Controller and the Iodine::Connection .
4
+ module Bridge
5
+ CONTROLLER_NAME = "plezi.controller".to_sym
6
+ CLIENT_NAME = "@_pl__client".to_sym # don't rename without updating Controller
7
+ # returns a client's controller
8
+ def controller client
9
+ client.env[CONTROLLER_NAME]
10
+ end
11
+
12
+ # called when the callback object is linked with a new client
13
+ def on_open client
14
+ c = controller(client)
15
+ c.instance_variable_set(CLIENT_NAME, client)
16
+ if client.protocol == :sse
17
+ c.on_sse
18
+ else
19
+ c.on_open
20
+ end
21
+ end
22
+ # called when data is available
23
+ def on_message client, data
24
+ controller(client).on_message(data)
25
+ end
26
+ # called when the server is shutting down, before closing the client
27
+ # (it's still possible to send messages to the client)
28
+ def on_shutdown client
29
+ controller(client).on_shutdown
30
+ end
31
+ # called when the client is closed (no longer available)
32
+ def on_close client
33
+ controller(client).on_close
34
+ end
35
+ # called when all the previous calls to `client.write` have completed
36
+ # (the local buffer was drained and is now empty)
37
+ def on_drained client
38
+ controller(client).on_drained
39
+ end
40
+ extend self
41
+ end
42
+ end
43
+ end
@@ -2,11 +2,15 @@ require 'plezi/render/render'
2
2
  require 'plezi/controller/identification'
3
3
  require 'plezi/controller/cookies'
4
4
  require 'plezi/controller/controller_class'
5
+ require 'plezi/controller/bridge'
5
6
 
6
7
  module Plezi
7
8
  # This module contains the functionality provided to any Controller class.
8
9
  #
9
10
  # This module will be included within every Class that is asigned to a route, providing the functionality without forcing an inheritance model.
11
+ #
12
+ # Any Controller can suppoert WebSocket connections by either implementing an `on_message(data)` callback or setting the `@auto_dispatch` class instance variable to `true`.
13
+ #
10
14
  module Controller
11
15
  def self.included(base)
12
16
  base.extend ::Plezi::Controller::ClassMethods
@@ -35,6 +39,10 @@ module Plezi
35
39
  #
36
40
  attr_reader :cookies
37
41
 
42
+ # @private
43
+ # Used internally to access the Iodine::Connection client data (if available).
44
+ attr_reader :_pl__client
45
+
38
46
  # @private
39
47
  # This function is used internally by Plezi, do not call.
40
48
  def _pl_respond(request, response, params)
@@ -119,24 +127,48 @@ module Plezi
119
127
  ::Plezi::Base::Router.url_for self.class, func, params
120
128
  end
121
129
 
122
- # A connection's Plezi ID uniquely identifies the connection across application instances.
123
- def id
124
- @_pl_id ||= (conn_id && "#{::Plezi::Base::Identification.pid}-#{conn_id.to_s(16)}")
125
- end
126
-
127
- # @private
128
- # This is the process specific Websocket's ID. This function is here to protect you from yourself. Don't call it.
129
- def conn_id
130
- defined?(super) && super
131
- end
132
-
133
- # Override this method to read / write cookies, perform authentication or perform validation before establishing a Websocket connecion.
130
+ # Override this method to read / write cookies, perform authentication or perform validation before establishing a Websocket or SSE connecion.
134
131
  #
135
132
  # Return `false` or `nil` to refuse the websocket connection.
136
133
  def pre_connect
137
134
  true
138
135
  end
139
136
 
137
+ # Writes to an SSE / WebSocket connection (raises an error unless the connection was already established).
138
+ def write data
139
+ _pl__client.write data
140
+ end
141
+
142
+ # Closes an SSE / WebSocket connection (raises an error unless the connection was already established).
143
+ def close
144
+ _pl__client.close
145
+ end
146
+ # Tests the known state for an SSE / WebSocket connection (the known state might not be the same as the actual state).
147
+ def open?
148
+ _pl__client && _pl__client.open?
149
+ end
150
+ # Returns the number of pending `write` operations that need to complete before the next `on_drained` callback is called.
151
+ def pending
152
+ return 0 unless _pl__client
153
+ _pl__client.pending
154
+ end
155
+
156
+ # Subscribes to a Pub/Sub stream / channel or replaces an existing subscription to the same stream / channel (raises an error unless an SSE / WebSocket connection was established).
157
+ def subscribe *args, &block
158
+ raise "WebSocket / SSE connection missing" unless _pl__client
159
+ if(block)
160
+ _pl__client.subscribe *args, &block
161
+ else
162
+ _pl__client.subscribe *args
163
+ end
164
+ end
165
+
166
+ # Publishes to a Pub/Sub stream / channel (routes to Iodine.publish).
167
+ def publish *args
168
+ ::Iodine.publish *args
169
+ end
170
+
171
+
140
172
  # Experimental: takes a module to be used for Websocket callbacks events.
141
173
  #
142
174
  # This function can only be called **after** a websocket connection was established (i.e., within the `on_open` callback).
@@ -168,6 +200,23 @@ module Plezi
168
200
  @_pl_ad_map ||= self.class._pl_ad_map.dup
169
201
  end
170
202
 
203
+ # @private
204
+ # Overload this method to handle event.
205
+ def on_open
206
+ end
207
+ # @private
208
+ # Overload this method to handle event.
209
+ def on_close
210
+ end
211
+ # @private
212
+ # Overload this method to handle event.
213
+ def on_drained
214
+ end
215
+ # @private
216
+ # Overload this method to handle event.
217
+ def on_shutdown
218
+ end
219
+
171
220
  # @private
172
221
  # This function is used internally by Plezi, for Auto-Dispatch support do not call.
173
222
  def on_message(data)
@@ -186,7 +235,7 @@ module Plezi
186
235
  puts "AutoDispatch Warnnig: JSON missing/invalid `event` name '#{json[:event]}' for class #{self.class.name}. Closing Connection."
187
236
  close
188
237
  end
189
- write("{\"event\":\"_ack_\",\"_EID_\":#{json[:_EID_].to_json}}") if json[:_EID_]
238
+ _pl__client.write("{\"event\":\"_ack_\",\"_EID_\":#{json[:_EID_].to_json}}") if json[:_EID_]
190
239
  _pl_ad_review __send__(envt, json)
191
240
  end
192
241
 
@@ -196,9 +245,9 @@ module Plezi
196
245
  return data unless self.class._pl_is_ad?
197
246
  case data
198
247
  when Hash
199
- write data.to_json
248
+ _pl__client.write data.to_json
200
249
  when String
201
- write data
250
+ _pl__client.write data
202
251
  # when Array
203
252
  # write data.to_json
204
253
  end
@@ -217,7 +266,8 @@ module Plezi
217
266
  # This function is used internally by Plezi, do not call.
218
267
  def preform_upgrade
219
268
  return false unless pre_connect
220
- request.env['upgrade.websocket'.freeze] = self
269
+ request.env[::Plezi::Base::Bridge::CONTROLLER_NAME] = self
270
+ request.env['rack.upgrade'.freeze] = ::Plezi::Base::Bridge
221
271
  @params = @params.dup # disable memory saving (used a single object per thread)
222
272
  @_pl_ws_map = self.class._pl_ws_map.dup
223
273
  @_pl_ad_map = self.class._pl_ad_map.dup
@@ -19,7 +19,7 @@ module Plezi
19
19
 
20
20
  # @private
21
21
  # This is used internally by Plezi, do not use.
22
- RESERVED_METHODS = [:delete, :create, :update, :new, :show, :pre_connect, :on_open, :on_close, :on_shutdown, :on_message].freeze
22
+ RESERVED_METHODS = [:delete, :create, :update, :new, :show, :pre_connect, :on_sse, :on_open, :on_close, :on_shutdown, :on_message].freeze
23
23
  # @private
24
24
  # This function is used internally by Plezi, do not call.
25
25
  def _pl_get_map
@@ -71,6 +71,12 @@ module Plezi
71
71
  @_pl_is_websocket
72
72
  end
73
73
 
74
+ # @private
75
+ # This function is used internally by Plezi, do not call.
76
+ def _pl_is_sse?
77
+ @_pl_is_sse
78
+ end
79
+
74
80
  # @private
75
81
  # This function is used internally by Plezi, do not call.
76
82
  def _pl_is_ad?
@@ -115,9 +121,11 @@ module Plezi
115
121
  # puts "matching against #{params}"
116
122
  case params['_method'.freeze]
117
123
  when :get # since this is common, it's pushed upwards.
118
- if env['HTTP_UPGRADE'.freeze] && _pl_is_websocket? && env['HTTP_UPGRADE'.freeze].downcase.start_with?('websocket'.freeze)
119
- @_pl_init_global_data ||= ::Plezi.plezi_initialize # wake up pub/sub drivers in case of `fork`
120
- return :preform_upgrade
124
+ if env['rack.upgrade?'.freeze]
125
+ if (env['rack.upgrade?'.freeze] == :websocket && _pl_is_websocket?) || (env['rack.upgrade?'.freeze] == :sse && _pl_is_sse?)
126
+ @_pl_init_global_data ||= ::Plezi.plezi_initialize # why did we do this?
127
+ return :preform_upgrade
128
+ end
121
129
  end
122
130
  return :new if _pl_has_new && par_id == 'new'.freeze
123
131
  return meth_id || (_pl_has_show && :show) || nil
@@ -143,6 +151,7 @@ module Plezi
143
151
  @_pl_has_update = public_instance_methods(false).include?(:update)
144
152
  @_pl_has_delete = public_instance_methods(false).include?(:delete)
145
153
  @_pl_is_websocket = (instance_variable_defined?(:@auto_dispatch) && instance_variable_get(:@auto_dispatch)) || instance_methods(false).include?(:on_message)
154
+ @_pl_is_sse = instance_methods(false).include?(:on_sse)
146
155
  _pl_get_map
147
156
  _pl_ad_map
148
157
  _pl_ws_map
@@ -15,7 +15,7 @@ module Plezi
15
15
  # Creates a new router
16
16
  def new(app)
17
17
  if app && app != call_method
18
- puts 'Plezi as Middleware'
18
+ puts "Plezi #{ Plezi::VERSION } as Middleware"
19
19
  @app = app
20
20
  end
21
21
  Plezi.app
@@ -1,3 +1,3 @@
1
1
  module Plezi
2
- VERSION = '0.15.1'.freeze
2
+ VERSION = '0.16.0'.freeze
3
3
  end
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
27
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ['lib']
29
29
 
30
- spec.add_dependency 'iodine', '>= 0.4', '< 0.5'
30
+ spec.add_dependency 'iodine', '>= 0.6.0', '< 0.7.0'
31
31
  spec.add_dependency 'rack', '>= 2.0.0'
32
32
  spec.add_dependency 'bundler', '~> 1.14'
33
33
  # spec.add_dependency 'redcarpet', '> 3.3.0'
@@ -109,14 +109,17 @@ PleziClient.prototype.___dispatch =
109
109
  }
110
110
  delete this[msg._EID_];
111
111
  }
112
- if ((msg.event) && (this[msg.event])) {
112
+ if ((msg.event) && (this[msg.event]) &&
113
+ (typeof this[msg.event] === "function")) {
113
114
  this[msg.event](msg);
114
- } else if ((msg.event) && (this['on' + msg.event])) {
115
+ } else if ((msg.event) && (this['on' + msg.event]) &&
116
+ (typeof this[msg.event] === "function")) {
115
117
  console.warn('PleziClient: use a callback called "' + msg.event +
116
118
  '" instead of of "on' + msg.event + '"');
117
119
  this['on' + msg.event](msg);
118
120
  } else {
119
- if (this['unknown'] && (msg.event != '_ack_')) {
121
+ if (this['unknown'] && (msg.event != '_ack_') &&
122
+ (typeof this['unknown'] === "function")) {
120
123
  this['unknown'](msg);
121
124
  };
122
125
  }
@@ -3,6 +3,6 @@
3
3
  # load the application
4
4
  load ::File.expand_path(File.join('..', 'appname.rb'), __FILE__)
5
5
 
6
- Iodine::Rack.public ||= './public'
6
+ Iodine::DEFAULT_HTTP_ARGS[:public] = ||= './public'
7
7
 
8
8
  run Plezi.app
@@ -1,5 +1,6 @@
1
1
  # Replace this sample with real code.
2
2
  class ExampleCtrl
3
+ CHANNEL = "chat".freeze
3
4
  # HTTP
4
5
  def index
5
6
  # any String returned will be appended to the response. We return a String.
@@ -8,18 +9,18 @@ class ExampleCtrl
8
9
 
9
10
  # Websockets
10
11
  def on_open
11
- subscribe channel: "chat"
12
+ subscribe CHANNEL
12
13
  write 'Welcome to appname!'
13
14
  @handle = params['id'.freeze] || 'Somebody'
14
- publish channel: "chat", message: "#{ERB::Util.html_escape @handle} joind us :-)"
15
+ publish CHANNEL, "#{ERB::Util.html_escape @handle} joind us :-)"
15
16
  end
16
17
  def on_message(data)
17
18
  data = ERB::Util.html_escape data
18
- publish channel: "chat", message: data
19
+ publish CHANNEL, data
19
20
  end
20
21
 
21
22
  def on_close
22
- publish channel: "chat", message: "#{@handle} left us :-("
23
+ publish CHANNEL, "#{@handle} left us :-("
23
24
  end
24
25
 
25
26
  end
@@ -17,7 +17,7 @@ Dir[File.join '{controllers}', '**', '*.rb'].each { |file| load File.expand_path
17
17
  Dir[File.join '{lib}', '**', '*.rb'].each { |file| load File.expand_path(file) }
18
18
 
19
19
  ## Logging
20
- Iodine::Rack.log = 1 if Iodine::Rack.log.nil?
20
+ Iodine::DEFAULT_HTTP_ARGS[:log] = 1 if Iodine::DEFAULT_HTTP_ARGS[:log].nil?
21
21
 
22
22
  # # Optional Scaling (across processes or machines):
23
23
  ENV['PL_REDIS_URL'] ||= ENV['REDIS_URL'] ||
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plezi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.1
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-04-14 00:00:00.000000000 Z
11
+ date: 2018-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: iodine
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0.4'
19
+ version: 0.6.0
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '0.5'
22
+ version: 0.7.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: '0.4'
29
+ version: 0.6.0
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '0.5'
32
+ version: 0.7.0
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: rack
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -111,6 +111,7 @@ files:
111
111
  - lib/plezi.rb
112
112
  - lib/plezi/activation.rb
113
113
  - lib/plezi/api.rb
114
+ - lib/plezi/controller/bridge.rb
114
115
  - lib/plezi/controller/controller.rb
115
116
  - lib/plezi/controller/controller_class.rb
116
117
  - lib/plezi/controller/cookies.rb