lib/plezi/controller/controller.rb in plezi-0.15.1 vs lib/plezi/controller/controller.rb in plezi-0.16.0
- old
+ new
@@ -1,14 +1,18 @@
require 'plezi/render/render'
require 'plezi/controller/identification'
require 'plezi/controller/cookies'
require 'plezi/controller/controller_class'
+require 'plezi/controller/bridge'
module Plezi
# This module contains the functionality provided to any Controller class.
#
# This module will be included within every Class that is asigned to a route, providing the functionality without forcing an inheritance model.
+ #
+ # Any Controller can suppoert WebSocket connections by either implementing an `on_message(data)` callback or setting the `@auto_dispatch` class instance variable to `true`.
+ #
module Controller
def self.included(base)
base.extend ::Plezi::Controller::ClassMethods
end
@@ -34,10 +38,14 @@
# cookies["name"] = nil
#
attr_reader :cookies
# @private
+ # Used internally to access the Iodine::Connection client data (if available).
+ attr_reader :_pl__client
+
+ # @private
# This function is used internally by Plezi, do not call.
def _pl_respond(request, response, params)
@request = request
@response = response
@params = params
@@ -117,28 +125,52 @@
# Returns a relative URL for the controller, placing the requested parameters in the URL (inline, where possible and as query data when not possible).
def url_for(func, params = {})
::Plezi::Base::Router.url_for self.class, func, params
end
- # A connection's Plezi ID uniquely identifies the connection across application instances.
- def id
- @_pl_id ||= (conn_id && "#{::Plezi::Base::Identification.pid}-#{conn_id.to_s(16)}")
- end
-
- # @private
- # This is the process specific Websocket's ID. This function is here to protect you from yourself. Don't call it.
- def conn_id
- defined?(super) && super
- end
-
- # Override this method to read / write cookies, perform authentication or perform validation before establishing a Websocket connecion.
+ # Override this method to read / write cookies, perform authentication or perform validation before establishing a Websocket or SSE connecion.
#
# Return `false` or `nil` to refuse the websocket connection.
def pre_connect
true
end
+ # Writes to an SSE / WebSocket connection (raises an error unless the connection was already established).
+ def write data
+ _pl__client.write data
+ end
+
+ # Closes an SSE / WebSocket connection (raises an error unless the connection was already established).
+ def close
+ _pl__client.close
+ end
+ # Tests the known state for an SSE / WebSocket connection (the known state might not be the same as the actual state).
+ def open?
+ _pl__client && _pl__client.open?
+ end
+ # Returns the number of pending `write` operations that need to complete before the next `on_drained` callback is called.
+ def pending
+ return 0 unless _pl__client
+ _pl__client.pending
+ end
+
+ # 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).
+ def subscribe *args, &block
+ raise "WebSocket / SSE connection missing" unless _pl__client
+ if(block)
+ _pl__client.subscribe *args, &block
+ else
+ _pl__client.subscribe *args
+ end
+ end
+
+ # Publishes to a Pub/Sub stream / channel (routes to Iodine.publish).
+ def publish *args
+ ::Iodine.publish *args
+ end
+
+
# Experimental: takes a module to be used for Websocket callbacks events.
#
# This function can only be called **after** a websocket connection was established (i.e., within the `on_open` callback).
#
# This allows a module "library" to be used similar to the way "rooms" are used in node.js, so that a number of different Controllers can listen to shared events.
@@ -167,10 +199,27 @@
def _pl_ad_map
@_pl_ad_map ||= self.class._pl_ad_map.dup
end
# @private
+ # Overload this method to handle event.
+ def on_open
+ end
+ # @private
+ # Overload this method to handle event.
+ def on_close
+ end
+ # @private
+ # Overload this method to handle event.
+ def on_drained
+ end
+ # @private
+ # Overload this method to handle event.
+ def on_shutdown
+ end
+
+ # @private
# This function is used internally by Plezi, for Auto-Dispatch support do not call.
def on_message(data)
json = nil
begin
json = JSON.parse(data, symbolize_names: true)
@@ -184,23 +233,23 @@
if json[:event].nil? || envt.nil?
puts _pl_ad_map
puts "AutoDispatch Warnnig: JSON missing/invalid `event` name '#{json[:event]}' for class #{self.class.name}. Closing Connection."
close
end
- write("{\"event\":\"_ack_\",\"_EID_\":#{json[:_EID_].to_json}}") if json[:_EID_]
+ _pl__client.write("{\"event\":\"_ack_\",\"_EID_\":#{json[:_EID_].to_json}}") if json[:_EID_]
_pl_ad_review __send__(envt, json)
end
# @private
# This function is used internally by Plezi, do not call.
def _pl_ad_review(data)
return data unless self.class._pl_is_ad?
case data
when Hash
- write data.to_json
+ _pl__client.write data.to_json
when String
- write data
+ _pl__client.write data
# when Array
# write data.to_json
end
end
@@ -215,10 +264,11 @@
# @private
# This function is used internally by Plezi, do not call.
def preform_upgrade
return false unless pre_connect
- request.env['upgrade.websocket'.freeze] = self
+ request.env[::Plezi::Base::Bridge::CONTROLLER_NAME] = self
+ request.env['rack.upgrade'.freeze] = ::Plezi::Base::Bridge
@params = @params.dup # disable memory saving (used a single object per thread)
@_pl_ws_map = self.class._pl_ws_map.dup
@_pl_ad_map = self.class._pl_ad_map.dup
true
end