plezi 0.15.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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