lib/websocket_rails/dispatcher.rb
module WebsocketRails
class Dispatcher
include Logging
attr_reader :event_map, :connection_manager, :controller_factory
delegate :filtered_channels, to: WebsocketRails
def initialize(connection_manager)
@connection_manager = connection_manager
@controller_factory = ControllerFactory.new(self)
@event_map = EventMap.new(self)
end
def receive_encoded(encoded_data,connection)
event = Event.new_from_json( encoded_data, connection )
dispatch( event )
end
def receive(event_name,data,connection)
event = Event.new event_name, data, connection
dispatch( event )
end
def dispatch(event)
return if event.is_invalid?
if event.is_channel?
filter_channel(event)
else
reload_event_map! unless event.is_internal?
route event
end
end
def send_message(event)
event.connection.trigger event
end
def broadcast_message(event)
connection_manager.connections.map do |_, connection|
connection.trigger event
end
end
def reload_event_map!
return unless defined?(Rails) and !Rails.configuration.cache_classes
begin
load "#{Rails.root}/config/events.rb"
@event_map = EventMap.new(self)
rescue Exception => ex
log(:warn, "EventMap reload failed: #{ex.message}")
end
end
private
def route(event)
actions = []
event_map.routes_for event do |controller_class, method|
actions << Fiber.new do
begin
log_event(event) do
controller = controller_factory.new_for_event(event, controller_class, method)
controller.process_action(method, event)
end
rescue Exception => ex
event.success = false
event.data = extract_exception_data ex
event.trigger
end
end
end
execute actions
end
def filter_channel(event)
actions = []
actions << Fiber.new do
begin
log_event(event) do
controller_class, catch_all = filtered_channels[event.channel]
controller = controller_factory.new_for_event(event, controller_class, event.name)
# send to the method of the event name
# silently skip routing to the controller on event.name if it doesnt respond
controller.process_action(event.name, event) if controller.respond_to?(event.name)
# send to our defined catch all method
controller.process_action(catch_all, event) if catch_all
end
rescue Exception => ex
event.success = false
event.data = extract_exception_data ex
event.trigger
end
end if filtered_channels[event.channel]
actions << Fiber.new{ WebsocketRails[event.channel].trigger_event(event) }
execute actions
end
def execute(actions)
actions.map do |action|
EM.next_tick { action.resume }
end
end
def extract_exception_data(ex)
if record_invalid_defined? and ex.is_a? ActiveRecord::RecordInvalid
{
:record => ex.record.attributes,
:errors => ex.record.errors,
:full_messages => ex.record.errors.full_messages
}
else
ex if ex.respond_to?(:to_json)
end
end
def record_invalid_defined?
Object.const_defined?('ActiveRecord') and ActiveRecord.const_defined?('RecordInvalid')
end
end
end