plezi 0.11.2 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,19 +3,19 @@ module Plezi
3
3
 
4
4
  module_function
5
5
 
6
- # Defers any missing methods to the GReactor Library.
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
- ::GReactor.send name, *args, &block
9
+ ::Iodine.__send__ name, *args, &block
10
10
  end
11
- # Defers any missing methods to the GReactor Library.
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 = ::GReactor.public_methods(false)
18
+ REACTOR_METHODS = ::Iodine.public_methods(false)
19
19
 
20
20
  end
21
21
 
@@ -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 to the current service (the last `listen` call) or switches to an existing host within the active service.
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 `listen` command, except `protocol`, `handler`, and `ssl` parameters.
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 server object
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
- # json serving apps are advised to use required parameters, empty sections indicating missing required parameters (i.e. /path///foo/bar///).
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 { start_services }
91
+ at_exit do
92
+ undef listen
93
+ undef host
94
+ undef route
95
+ undef shared_route
96
+ end
108
97
  end
@@ -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
- GRHttp::Base::WSHandler.unicast data[:target], data
27
+ data[:type].___faild_unicast( data ) unless Iodine::Http::Websockets.unicast data[:target], data
28
28
  else
29
- GRHttp::Base::WSHandler.broadcast data
29
+ Iodine::Http::Websockets.broadcast data
30
30
  end
31
31
  rescue => e
32
- GReactor.error "The following could be a security breach attempt:"
33
- GReactor.error e
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
- GReactor.error e
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 ||= 'Plezi_Redis_Channel'
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
- GRHttp.ws_message_size_limit = val
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
- GRHttp.ws_message_size_limit
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.io[:params]
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
- response.session
41
+ session
42
42
  # complete handshake
43
43
  return self
44
44
  end
45
45
  # handles websocket opening.
46
- def on_open ws
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 ws
56
- super(ws.data) if defined?(super)
50
+ def on_message data
51
+ super if defined?(super)
57
52
  end
58
53
  # handles websocket being closed.
59
- def on_close ws
60
- super() if defined? 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 session object will be created. The session object must be created BEFORE the headers are set , if it is to be used.
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 `listen` / `add_service` call).
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
- response['content-type'] ||= "#{MimeTypeHelper::MIME_DICTIONARY[".#{options[:type]}".freeze]}; charset=utf-8".freeze
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.match(/POST|PUT|PATCH/) && (params[:id].nil? || params[:id] == 'new')
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
- class HTTPRouter
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('/')}\/(.+)/.freeze
16
- params[:assets_refuse_templates] = /(#{AssetManager.all_extentions.join('|')}|\.\.\/)$/.freeze
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.io[:params] = host.params
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 an HTTP router (the normal Handler for HTTP requests)
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
- def initialize
38
- @hosts = {}
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.listen})
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
- host_name = (host_name ? (host_name.is_a?(String) ? host_name.to_s.downcase : host_name) : :default)
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
- return false unless host
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
- raise 'No Host defined.' unless @active_host
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
- raise 'No Host defined.' if @hosts.empty?
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.io[:params] = host.params
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 request[:io].params[:http_handler] == ::GRHttp::Base::Rack
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
- GReactor.error e
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.match(params[:assets_public_regex])[1].split('/')))
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, params[:assets_public].to_s, *request.path.match(params[:assets_public_regex])[1].split('/') )
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
- # class MyListener
9
- # def _my_method_name *args
10
- # #logic
11
- # end
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
- # Plezi::Placebo.new MyListener
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 io
41
- @io = io
42
- @io[:websocket_handler] = self
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
- # notice of disconnect
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
- GR.warn "Placebo #{self.class.superclass.name} disconnected. Ignore if this message appears during shutdown."
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 < GReactor::BasicIO
64
- def clear?
65
- io.closed?
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
- self.read
69
- GR.warn "Placebo IO recieved IO signal - this is unexpected..."
84
+ read
85
+ Iodine.warn "Placebo IO recieved IO signal - this is unexpected..."
70
86
  end
71
- def on_close
72
- @params[:out].close rescue nil
73
- @cache[:websocket_handler].on_close if @cache[:websocket_handler]
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
- io = Placebo::Base::PlaceboIO.new i, out: o, reactor: ::GReactor
90
- new_class.new(io)
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)