plezi 0.11.2 → 0.12.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 +20 -0
- data/README.md +32 -33
- data/docs/async_helpers.md +90 -40
- data/docs/logging.md +38 -3
- data/docs/routes.md +177 -5
- data/docs/websockets.md +5 -0
- data/lib/plezi.rb +2 -10
- data/lib/plezi/common/api.rb +48 -101
- data/lib/plezi/common/defer.rb +4 -4
- data/lib/plezi/common/dsl.rb +13 -24
- data/lib/plezi/common/redis.rb +9 -5
- data/lib/plezi/common/settings.rb +4 -24
- data/lib/plezi/handlers/controller_core.rb +7 -14
- data/lib/plezi/handlers/controller_magic.rb +12 -10
- data/lib/plezi/handlers/http_router.rb +27 -22
- data/lib/plezi/handlers/placebo.rb +40 -33
- data/lib/plezi/handlers/route.rb +233 -234
- data/lib/plezi/handlers/session.rb +2 -2
- data/lib/plezi/handlers/stubs.rb +7 -0
- data/lib/plezi/handlers/ws_object.rb +25 -15
- data/lib/plezi/helpers/http_sender.rb +3 -3
- data/lib/plezi/helpers/magic_helpers.rb +3 -3
- data/lib/plezi/version.rb +1 -1
- data/plezi.gemspec +1 -1
- data/resources/config.ru +12 -18
- data/resources/environment.rb +6 -7
- data/resources/mini_app.rb +22 -22
- data/resources/redis_config.rb +4 -2
- data/test/plezi_tests.rb +76 -87
- data/websocket chatroom.md +3 -5
- metadata +6 -6
- data/docs/http_helpers.md +0 -9
data/lib/plezi/common/defer.rb
CHANGED
@@ -3,19 +3,19 @@ module Plezi
|
|
3
3
|
|
4
4
|
module_function
|
5
5
|
|
6
|
-
# Defers any missing methods to the
|
6
|
+
# Defers any missing methods to the Iodine Library.
|
7
7
|
def method_missing name, *args, &block
|
8
8
|
return super unless REACTOR_METHODS.include? name
|
9
|
-
::
|
9
|
+
::Iodine.__send__ name, *args, &block
|
10
10
|
end
|
11
|
-
# Defers any missing methods to the
|
11
|
+
# Defers any missing methods to the Iodine Library.
|
12
12
|
def respond_to_missing?(name, include_private = false)
|
13
13
|
REACTOR_METHODS.include?(name) || super
|
14
14
|
end
|
15
15
|
|
16
16
|
protected
|
17
17
|
|
18
|
-
REACTOR_METHODS = ::
|
18
|
+
REACTOR_METHODS = ::Iodine.public_methods(false)
|
19
19
|
|
20
20
|
end
|
21
21
|
|
data/lib/plezi/common/dsl.rb
CHANGED
@@ -3,22 +3,24 @@ PL = Plezi
|
|
3
3
|
|
4
4
|
unless defined? PLEZI_NON_DSL
|
5
5
|
|
6
|
-
# shortcut for Plezi.listen.
|
6
|
+
# shortcut for Plezi.listen. Deprecated.
|
7
7
|
#
|
8
8
|
def listen(params = {})
|
9
9
|
Plezi.listen params
|
10
10
|
end
|
11
11
|
|
12
|
-
# adds a virtul host
|
12
|
+
# adds a virtul host or switches to an existing host, for routes setup or parameters update.
|
13
13
|
#
|
14
14
|
# accepts:
|
15
15
|
# host_name: a String with the full host name (i.e. "www.google.com" / "mail.google.com")
|
16
|
-
# params:: any of the parameters accepted by the
|
16
|
+
# params:: any of the parameters accepted by the {Plezi.host} command.
|
17
|
+
#
|
18
|
+
# If no host is specified or host name is `false`, the default host would be set as the active host and returned.
|
17
19
|
def host(host_name = false, params = {})
|
18
20
|
Plezi.host host_name, params
|
19
21
|
end
|
20
22
|
|
21
|
-
# adds a route to the last
|
23
|
+
# adds a route to the last (or default) host
|
22
24
|
#
|
23
25
|
# path:: the path for the route
|
24
26
|
# controller:: The controller class which will accept the route.
|
@@ -49,7 +51,7 @@ unless defined? PLEZI_NON_DSL
|
|
49
51
|
#
|
50
52
|
# magic routes make for difficult debugging - the smarter the routes, the more difficult the debugging.
|
51
53
|
# use with care and avoid complex routes when possible. RESTful routes are recommended when possible.
|
52
|
-
#
|
54
|
+
# JSON serving apps are advised to use required parameters and empty sections indicating missing required parameters (i.e. /path///foo/bar///).
|
53
55
|
#
|
54
56
|
def route(path, controller = nil, &block)
|
55
57
|
Plezi.route(path, controller, &block)
|
@@ -74,28 +76,10 @@ unless defined? PLEZI_NON_DSL
|
|
74
76
|
end
|
75
77
|
|
76
78
|
|
77
|
-
|
78
|
-
# finishes setup of the servers and starts them up. This will hange the proceess.
|
79
|
-
#
|
80
|
-
# this method is called automatically by the Plezi framework.
|
81
|
-
#
|
82
|
-
# it is recommended that you DO NOT CALL this method.
|
83
|
-
# if any post shut-down actions need to be performed, use Plezi.on_shutdown instead.
|
84
|
-
def start_services
|
85
|
-
return 0 if defined?(NO_PLEZI_AUTO_START)
|
86
|
-
undef listen
|
87
|
-
undef host
|
88
|
-
undef route
|
89
|
-
undef shared_route
|
90
|
-
Plezi.start
|
91
|
-
end
|
92
|
-
|
93
79
|
# sets information to be used when restarting
|
94
80
|
$PL_SCRIPT = $0
|
95
81
|
$PL_ARGV = $*.dup
|
96
82
|
|
97
|
-
# sets up a generic session-token name based on the script name
|
98
|
-
GRHttp.session_token = "#{($0).split(/[\\\/]/).last.split(/[\s\.]+/).first}_uuid"
|
99
83
|
# restarts the Plezi app with the same arguments as when it was started.
|
100
84
|
#
|
101
85
|
# EXPERIMENTAL
|
@@ -104,5 +88,10 @@ unless defined? PLEZI_NON_DSL
|
|
104
88
|
end
|
105
89
|
|
106
90
|
# sets to start the services once dsl script is finished loading.
|
107
|
-
at_exit
|
91
|
+
at_exit do
|
92
|
+
undef listen
|
93
|
+
undef host
|
94
|
+
undef route
|
95
|
+
undef shared_route
|
96
|
+
end
|
108
97
|
end
|
data/lib/plezi/common/redis.rb
CHANGED
@@ -24,18 +24,18 @@ module Plezi
|
|
24
24
|
next if data[:server] == Plezi::Settings.uuid
|
25
25
|
data[:type] = Object.const_get(data[:type]) unless data[:type].nil? || data[:type] == :all
|
26
26
|
if data[:target]
|
27
|
-
|
27
|
+
data[:type].___faild_unicast( data ) unless Iodine::Http::Websockets.unicast data[:target], data
|
28
28
|
else
|
29
|
-
|
29
|
+
Iodine::Http::Websockets.broadcast data
|
30
30
|
end
|
31
31
|
rescue => e
|
32
|
-
|
33
|
-
|
32
|
+
Iodine.error "The following could be a security breach attempt:"
|
33
|
+
Iodine.error e
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
37
37
|
rescue => e
|
38
|
-
|
38
|
+
Iodine.error e
|
39
39
|
retry
|
40
40
|
end
|
41
41
|
end
|
@@ -46,6 +46,10 @@ module Plezi
|
|
46
46
|
return @redis if (@redis_sub_thread && @redis_sub_thread.alive?) && @redis
|
47
47
|
inner_init_redis
|
48
48
|
end
|
49
|
+
def away? server
|
50
|
+
return true unless get_redis
|
51
|
+
@redis.pubsub('CHANNELS', server).empty?
|
52
|
+
end
|
49
53
|
end
|
50
54
|
end
|
51
55
|
|
@@ -6,26 +6,6 @@ module Plezi
|
|
6
6
|
|
7
7
|
module_function
|
8
8
|
|
9
|
-
# The maximum number of threads that are used for concurrency.
|
10
|
-
def max_threads
|
11
|
-
@max_threads ||= 30
|
12
|
-
end
|
13
|
-
# Sets the maximum number of threads that are used for concurrency.
|
14
|
-
def max_threads=val
|
15
|
-
@max_threads = val
|
16
|
-
end
|
17
|
-
|
18
|
-
# The number of second between pings automatically sent by an open websocket.
|
19
|
-
def autoping
|
20
|
-
@autoping ||= 45
|
21
|
-
end
|
22
|
-
# Sets the number of second between pings automatically sent by an open websocket.
|
23
|
-
#
|
24
|
-
# Set to nil or false to disable auto-pinging.
|
25
|
-
def autoping=val
|
26
|
-
@autoping = 45
|
27
|
-
end
|
28
|
-
|
29
9
|
# Sets the Redis Channel Name.
|
30
10
|
def redis_channel_name=val
|
31
11
|
return false unless defined? Redis
|
@@ -35,7 +15,7 @@ module Plezi
|
|
35
15
|
# Returns the Redis Channel Name used by this app.
|
36
16
|
# @return [String]
|
37
17
|
def redis_channel_name
|
38
|
-
@redis_channel_name ||= '
|
18
|
+
@redis_channel_name ||= "#{File.basename($0, '.*')}_Redis_Channel"
|
39
19
|
end
|
40
20
|
|
41
21
|
# Sets the message byte size limit for a Websocket message. Defaults to 0 (no limit)
|
@@ -45,11 +25,11 @@ module Plezi
|
|
45
25
|
#
|
46
26
|
# If the sessage size limit is exceeded, the disconnection will be immidiate as an attack will be assumed. The protocol's normal disconnect sequesnce will be discarded.
|
47
27
|
def ws_message_size_limit=val
|
48
|
-
|
28
|
+
Iodine::Http::Websockets.message_size_limit = val
|
49
29
|
end
|
50
30
|
# Gets the message byte size limit for a Websocket message. Defaults to 0 (no limit)
|
51
31
|
def ws_message_size_limit
|
52
|
-
|
32
|
+
Iodine::Http::Websockets.message_size_limit
|
53
33
|
end
|
54
34
|
|
55
35
|
# This Server's UUID, for Redis and unicasting identification.
|
@@ -58,4 +38,4 @@ module Plezi
|
|
58
38
|
end
|
59
39
|
end
|
60
40
|
end
|
61
|
-
|
41
|
+
Iodine.threads = 30
|
@@ -16,7 +16,7 @@ module Plezi
|
|
16
16
|
@request = request
|
17
17
|
@params = request.params
|
18
18
|
@flash = response.flash
|
19
|
-
@host_params = request
|
19
|
+
@host_params = request[:host_settings]
|
20
20
|
@response = response
|
21
21
|
@cookies = request.cookies
|
22
22
|
# # \@response["content-type"] ||= ::Plezi.default_content_type
|
@@ -38,31 +38,24 @@ module Plezi
|
|
38
38
|
# finish if the response was sent
|
39
39
|
return false if response.headers_sent?
|
40
40
|
# make sure that the session object is available for websocket connections
|
41
|
-
|
41
|
+
session
|
42
42
|
# complete handshake
|
43
43
|
return self
|
44
44
|
end
|
45
45
|
# handles websocket opening.
|
46
|
-
def on_open
|
47
|
-
# set broadcasts and return true
|
48
|
-
@response = ws
|
49
|
-
ws.autoping Plezi::Settings.autoping if Plezi::Settings.autoping
|
50
|
-
# create the redis connection (in case this in the first instance of this class)
|
51
|
-
Plezi.redis
|
46
|
+
def on_open
|
52
47
|
super() if defined?(super)
|
53
48
|
end
|
54
49
|
# handles websocket messages.
|
55
|
-
def on_message
|
56
|
-
super
|
50
|
+
def on_message data
|
51
|
+
super if defined?(super)
|
57
52
|
end
|
58
53
|
# handles websocket being closed.
|
59
|
-
def on_close
|
60
|
-
super
|
54
|
+
def on_close
|
55
|
+
super if defined? super
|
61
56
|
end
|
62
57
|
|
63
58
|
# Inner Routing
|
64
|
-
#
|
65
|
-
#
|
66
59
|
def _route_path_to_methods_and_set_the_response_
|
67
60
|
#run :before filter
|
68
61
|
return false if self.class.has_method?(:before) && self.before == false
|
@@ -42,7 +42,7 @@ module Plezi
|
|
42
42
|
|
43
43
|
# Session data can be stored here (session data will be stored on the Redis server, if Redis is available).
|
44
44
|
#
|
45
|
-
# The first time this method is called, the
|
45
|
+
# The first time this method is called, the `n object will be created. The session object must be created BEFORE the headers are set , if it is to be used.
|
46
46
|
#
|
47
47
|
# Sessions are not automatically created, because they require more resources. The one exception is the Websocket connection that will force a session object into existence, as it's very common to use session data in Websocket connections and the extra connection time is less relevant for a long term connection.
|
48
48
|
def session
|
@@ -56,7 +56,7 @@ module Plezi
|
|
56
56
|
# these cookies will live for one successful request to a Controller and will then be removed.
|
57
57
|
attr_reader :flash
|
58
58
|
|
59
|
-
# the parameters used to create the host (the parameters passed to the `
|
59
|
+
# the parameters used to create the host (the parameters passed to the `Plezi.host`).
|
60
60
|
attr_reader :host_params
|
61
61
|
|
62
62
|
# this method does two things.
|
@@ -79,7 +79,6 @@ module Plezi
|
|
79
79
|
#
|
80
80
|
def redirect_to url, options = {}
|
81
81
|
return super *[] if defined? super
|
82
|
-
raise 'Cannot redirect once a Websocket connection was established.' if response.is_a?(::GRHttp::WSEvent)
|
83
82
|
raise 'Cannot redirect after headers were sent.' if response.headers_sent?
|
84
83
|
url = "#{request.base_url}/#{url.to_s.gsub('_', '/')}" if url.is_a?(Symbol) || ( url.is_a?(String) && url.empty? ) || url.nil?
|
85
84
|
# redirect
|
@@ -129,8 +128,6 @@ module Plezi
|
|
129
128
|
# filename:: sets a filename for the browser to "save as". defaults to empty.
|
130
129
|
#
|
131
130
|
def send_data data, options = {}
|
132
|
-
raise 'Cannot use "send_data" once a Websocket connection was established.' if response.is_a?(::GRHttp::WSEvent)
|
133
|
-
# return response.write(data) if response.is_a?(::GRHttp::WSEvent)
|
134
131
|
raise 'Cannot use "send_data" after headers were sent' if response.headers_sent?
|
135
132
|
Plezi.warn 'HTTP response buffer is cleared by `#send_data`' if response.body && response.body.any? && response.body.clear
|
136
133
|
response << data
|
@@ -180,7 +177,12 @@ module Plezi
|
|
180
177
|
options[:type] ||= 'html'.freeze
|
181
178
|
options[:locale] ||= params[:locale].to_sym if params[:locale]
|
182
179
|
#update content-type header
|
183
|
-
|
180
|
+
case options[:type]
|
181
|
+
when 'html', 'js', 'txt'
|
182
|
+
response['content-type'] ||= "#{MimeTypeHelper::MIME_DICTIONARY[".#{options[:type]}".freeze]}; charset=utf-8".freeze
|
183
|
+
else
|
184
|
+
response['content-type'] ||= "#{MimeTypeHelper::MIME_DICTIONARY[".#{options[:type]}".freeze]}".freeze
|
185
|
+
end
|
184
186
|
# Circumvents I18n persistance issues (live updating and thread data storage).
|
185
187
|
I18n.locale = options[:locale] || I18n.default_locale if defined?(I18n) # sets the locale to nil for default behavior even if the locale was set by a previous action - removed: # && options[:locale]
|
186
188
|
# find template and create template object
|
@@ -201,19 +203,19 @@ module Plezi
|
|
201
203
|
# respond to websocket special case
|
202
204
|
return :pre_connect if request.upgrade?
|
203
205
|
# respond to save 'new' special case
|
204
|
-
return (self.class.has_method?(:save) ? :save : false) if request.request_method
|
206
|
+
return (self.class.has_method?(:save) ? :save : false) if (request.request_method =~ /POST|PUT|PATCH/i.freeze) && (params[:id].nil? || params[:id] == 'new')
|
205
207
|
# set DELETE method if simulated
|
206
208
|
request.request_method = 'DELETE' if params[:_method].to_s.downcase == 'delete'
|
207
209
|
# respond to special :id routing
|
208
210
|
return params[:id].to_s.to_sym if params[:id] && self.class.has_exposed_method?(params[:id].to_s.to_sym)
|
209
211
|
#review general cases
|
210
212
|
case request.request_method
|
211
|
-
when 'GET', 'HEAD'
|
213
|
+
when 'GET'.freeze, 'HEAD'.freeze
|
212
214
|
return (self.class.has_method?(:index) ? :index : false) unless params[:id]
|
213
215
|
return (self.class.has_method?(:show) ? :show : false)
|
214
|
-
when 'POST', 'PUT', 'PATCH'
|
216
|
+
when 'POST'.freeze, 'PUT'.freeze, 'PATCH'.freeze
|
215
217
|
return (self.class.has_method?(:update) ? :update : false)
|
216
|
-
when 'DELETE'
|
218
|
+
when 'DELETE'.freeze
|
217
219
|
return (self.class.has_method?(:delete) ? :delete : false)
|
218
220
|
end
|
219
221
|
false
|
@@ -4,7 +4,7 @@ module Plezi
|
|
4
4
|
|
5
5
|
#####
|
6
6
|
# handles the HTTP Routing
|
7
|
-
|
7
|
+
module HTTPRouter
|
8
8
|
|
9
9
|
class Host
|
10
10
|
attr_reader :params
|
@@ -12,8 +12,9 @@ module Plezi
|
|
12
12
|
def initialize params
|
13
13
|
@params = params
|
14
14
|
@routes = []
|
15
|
-
params[:assets_public_regex] = /^#{params[:assets_public].to_s.chomp('/')}
|
16
|
-
params[:
|
15
|
+
@params[:assets_public_regex] = /^#{params[:assets_public].to_s.chomp('/')}\//i.freeze
|
16
|
+
@params[:assets_public_length] = @params[:assets_public].to_s.chomp('/').length + 1
|
17
|
+
@params[:assets_refuse_templates] = /(#{AssetManager.all_extentions.join('|')}|\.\.\/)$/i.freeze
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
@@ -25,23 +26,25 @@ module Plezi
|
|
25
26
|
def on_upgrade request, response
|
26
27
|
host = get_host(request[:host_name].to_s.downcase) || @hosts[:default]
|
27
28
|
return false unless host
|
28
|
-
request
|
29
|
+
request[:host_settings] = host.params
|
29
30
|
# return if a route answered the request
|
30
31
|
host.routes.each {|r| a = r.on_request(request, response); return a if a}
|
31
32
|
# websockets should cut out here
|
32
33
|
false
|
33
34
|
end
|
34
|
-
# initializes
|
35
|
+
# initializes the HTTP router (the normal Handler for HTTP requests)
|
35
36
|
#
|
36
37
|
# the router holds the different hosts and sends them messages/requests.
|
37
|
-
|
38
|
-
|
39
|
-
@active_host = nil
|
40
|
-
end
|
38
|
+
@hosts = {}
|
39
|
+
@active_host = nil
|
41
40
|
|
42
|
-
# adds a host to the router (or activates an existing host to add new routes). accepts a host name and any parameters not related to the actual connection (ssl etc') (see {Plezi.
|
41
|
+
# adds a host to the router (or activates an existing host to add new routes). accepts a host name and any parameters not related to the actual connection (ssl etc') (see {Plezi.host})
|
43
42
|
def add_host host_name, params = {}
|
44
|
-
|
43
|
+
params[:index_file] ||= 'index.html'
|
44
|
+
params[:assets_public] ||= '/assets'
|
45
|
+
params[:assets_public].chomp! '/'
|
46
|
+
params[:public] ||= params[:root] # backwards compatability
|
47
|
+
host_name = (host_name.is_a?(String) ? host_name.to_s.downcase : (host_name.is_a?(Regexp) ? host_name : :default))
|
45
48
|
@active_host = get_host(host_name) || ( @hosts[host_name] = Host.new(params) )
|
46
49
|
add_alias host_name, *params[:alias] if params[:alias]
|
47
50
|
@active_host
|
@@ -49,21 +52,21 @@ module Plezi
|
|
49
52
|
# adds an alias to an existing host name (normally through the :alias parameter in the `add_host` method).
|
50
53
|
def add_alias host_name, *aliases
|
51
54
|
host = get_host host_name
|
52
|
-
|
55
|
+
host ||= add_host :default
|
53
56
|
aliases.each {|a| @hosts[a.to_s.downcase] = host}
|
54
57
|
true
|
55
58
|
end
|
56
59
|
|
57
60
|
# adds a route to the active host. The active host is the last host referenced by the `add_host`.
|
58
61
|
def add_route path, controller, &block
|
59
|
-
|
60
|
-
@active_host.routes << Route.new(path, controller, &block)
|
62
|
+
@active_host ||= add_host :default
|
63
|
+
@active_host.routes << ::Plezi::Base::Route.new(path, controller, &block)
|
61
64
|
end
|
62
65
|
|
63
66
|
# adds a route to all existing hosts.
|
64
67
|
def add_shared_route path, controller, &block
|
65
|
-
|
66
|
-
@hosts.each {|n, h| h.routes << Route.new(path, controller, &block) }
|
68
|
+
add_host :default if @hosts.empty?
|
69
|
+
@hosts.each {|n, h| h.routes << ::Plezi::Base::Route.new(path, controller, &block) }
|
67
70
|
end
|
68
71
|
|
69
72
|
# handles requests send by the HTTP Protocol (HTTPRequest objects)
|
@@ -71,7 +74,7 @@ module Plezi
|
|
71
74
|
begin
|
72
75
|
host = get_host(request[:host_name].to_s.downcase) || @hosts[:default]
|
73
76
|
return false unless host
|
74
|
-
request
|
77
|
+
request[:host_settings] = host.params
|
75
78
|
# render any assets?
|
76
79
|
return true if render_assets request, response, host.params
|
77
80
|
# send static file, if exists and root is set.
|
@@ -79,10 +82,10 @@ module Plezi
|
|
79
82
|
# return if a route answered the request
|
80
83
|
host.routes.each {|r| a = r.on_request(request, response); return a if a}
|
81
84
|
#return error code or 404 not found
|
82
|
-
return Base::HTTPSender.send_by_code request, response, 404 unless
|
85
|
+
return Base::HTTPSender.send_by_code request, response, 404 unless ( @avoid_404 ||= ( Iodine::Http.on_http == ::Iodine::Http::Rack ? 1 : 0 ) ) == 1
|
83
86
|
rescue => e
|
84
87
|
# return 500 internal server error.
|
85
|
-
|
88
|
+
Iodine.error e
|
86
89
|
Base::HTTPSender.send_by_code request, response, 500
|
87
90
|
end
|
88
91
|
end
|
@@ -105,14 +108,14 @@ module Plezi
|
|
105
108
|
return true if params[:assets_callback] && params[:assets_callback].call(request, response)
|
106
109
|
|
107
110
|
# get file requested
|
108
|
-
source_file = File.join(params[:assets], *(request.path
|
111
|
+
source_file = File.join(params[:assets], *(request.path[params[:assets_public_length]..-1].split('/')))
|
109
112
|
|
110
113
|
|
111
114
|
# stop if file name is reserved / has security issues
|
112
115
|
return false if File.directory?(source_file) || source_file =~ params[:assets_refuse_templates]
|
113
116
|
|
114
117
|
# set where to store the rendered asset
|
115
|
-
target_file = File.join( params[:public].to_s,
|
118
|
+
target_file = File.join( params[:public].to_s, *request.path.split('/') )
|
116
119
|
|
117
120
|
# send the file if it exists (no render needed)
|
118
121
|
if File.exists?(source_file)
|
@@ -134,7 +137,9 @@ module Plezi
|
|
134
137
|
# return false if an asset couldn't be rendered and wasn't found.
|
135
138
|
return false
|
136
139
|
end
|
137
|
-
|
140
|
+
extend self
|
138
141
|
end
|
142
|
+
Iodine::Http.on_http ::Plezi::Base::HTTPRouter
|
143
|
+
Iodine::Http.on_websocket ::Plezi::Base::HTTPRouter.upgrade_proc
|
139
144
|
end
|
140
145
|
end
|
@@ -3,15 +3,27 @@ module Plezi
|
|
3
3
|
# This API wil allows you to listen to Websocket Broadcasts sent to any object and to accept unicasts
|
4
4
|
# even when NOT connected to a websocket.
|
5
5
|
#
|
6
|
-
# Simpley create a class to handle any events and call `Plezi::Placebo.new ClassName` :
|
6
|
+
# Simpley create a class to handle any events and call `Plezi::Placebo.new ClassName` or use the {Plezi.start_placebo} shortcut:
|
7
7
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# end
|
8
|
+
# # Important: set up a unique - but shared - main redis channel for BOTH Apps (The Plezi and the Placebo).
|
9
|
+
# Plezi::Settings.redis_channel_name = 'unique_channel_name_for_app_b24270e2'
|
10
|
+
# # Important: set Plezi's auo-Redis pub/sub server.
|
11
|
+
# ENV['PL_REDIS_URL'] ||= "redis://redis:password@redis.server.com:9999"
|
13
12
|
#
|
14
|
-
#
|
13
|
+
# # create the Placebo bridge handler
|
14
|
+
# class PleziBridge
|
15
|
+
# def on_open
|
16
|
+
# multicast :print, "Hello from Placebo!"
|
17
|
+
# end
|
18
|
+
# def print data
|
19
|
+
# Iodine.info "Placebo message: #{data}"
|
20
|
+
# puts "Placebo message: #{data}"
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# # initiate Placebo mode using the bridge class. it's possible to create multiple
|
25
|
+
# # it's possible to create multiple bridge classes this way.
|
26
|
+
# Plezi.start_placebo(PleziBridge)
|
15
27
|
#
|
16
28
|
# A new instance will be created and that instance will answer any broadcasts, for ALL possible
|
17
29
|
# Plezi controllers, as long as it had a method defined that is capable to handle the broadcast.
|
@@ -37,15 +49,17 @@ module Plezi
|
|
37
49
|
module InstanceMethods
|
38
50
|
public
|
39
51
|
attr_accessor :io
|
40
|
-
def initialize
|
41
|
-
@
|
42
|
-
@
|
52
|
+
def initialize io_in, io_out, request
|
53
|
+
@io_in = io_in
|
54
|
+
@io_out = io_out
|
55
|
+
@request = request
|
43
56
|
super()
|
44
57
|
end
|
45
|
-
#
|
58
|
+
# Cleanup on disconnection
|
46
59
|
def on_close
|
60
|
+
io_out.close unless io_out.closed?
|
47
61
|
return super() if defined? super
|
48
|
-
|
62
|
+
Iodine.warn "Placebo #{self.class.superclass.name} disconnected. Ignore if this message appears during shutdown."
|
49
63
|
end
|
50
64
|
def placebo?
|
51
65
|
true
|
@@ -60,17 +74,19 @@ module Plezi
|
|
60
74
|
end
|
61
75
|
end
|
62
76
|
end
|
63
|
-
class PlaceboIO <
|
64
|
-
|
65
|
-
|
77
|
+
class PlaceboIO < ::Iodine::Http::Websockets
|
78
|
+
# emulate Iodine::Protocol#timeout?
|
79
|
+
def timeout? time
|
80
|
+
false
|
66
81
|
end
|
82
|
+
# emulate Iodine::Protocol#call
|
67
83
|
def call
|
68
|
-
|
69
|
-
|
84
|
+
read
|
85
|
+
Iodine.warn "Placebo IO recieved IO signal - this is unexpected..."
|
70
86
|
end
|
71
|
-
|
72
|
-
|
73
|
-
|
87
|
+
# override "go_away"
|
88
|
+
def go_away
|
89
|
+
close
|
74
90
|
end
|
75
91
|
end
|
76
92
|
end
|
@@ -86,19 +102,10 @@ module Plezi
|
|
86
102
|
Object.const_set(new_class_name, new_class)
|
87
103
|
end
|
88
104
|
i, o = IO.pipe
|
89
|
-
|
90
|
-
new_class.new(
|
105
|
+
req = {}
|
106
|
+
handler = new_class.new(i, o, req)
|
107
|
+
io = Placebo::Base::PlaceboIO.new i, handler, req
|
108
|
+
handler
|
91
109
|
end
|
92
110
|
end
|
93
111
|
end
|
94
|
-
|
95
|
-
|
96
|
-
# class A
|
97
|
-
# def _hi
|
98
|
-
# 'hi'
|
99
|
-
# end
|
100
|
-
# end
|
101
|
-
# Plezi::Placebo.new A
|
102
|
-
# a = nil
|
103
|
-
# GReactor.each {|h| a= h}
|
104
|
-
# a[:websocket_handler].on_broadcast GRHttp::WSEvent.new(nil, type: true, data: [], method: :_hi)
|