lib/faye-rails/rack_adapter.rb
require 'faye'
module FayeRails
class RackAdapter < ::Faye::RackAdapter
attr_reader :server, :endpoint
def initialize(app=nil, options=nil)
super
FayeRails.servers << self
end
# Rudimentary routing support for channels to controllers.
#
# @param opts
# a Hash of mappings either string keys (channel globs)
# mapping to controller constants eg:
#
# '/widgets/**' => WidgetsController
#
# or you can set the behaviour for unknown channels:
#
# :default => :block
#
# :default can be set to :allow, :drop or :block.
# if :drop is chosen then messages to unknown channels
# will be silently dropped, whereas if you choose
# :block then the message will be returned with the
# error "Permission denied."
def map(opts)
if opts.is_a? Hash
opts.each do |channel, controller|
if channel.is_a? String
if FayeRails::Matcher.match? '/**', channel
routing_extension.map(channel, controller)
else
raise ArgumentError, "Invalid channel: #{channel}"
end
elsif channel == :default
if controller == :block
routing_extension.block_unknown_channels!
elsif controller == :drop
routing_extension.drop_unknown_channels!
elsif controller == :allow
routing_extension.allow_unknown_channels!
end
end
end
end
end
# Adds a very simple extension to the server causing
# it to log all messages in and out to Rails.logger.debug.
def debug_messages
add_extension(DebugMessagesExtension.new)
end
private
def routing_extension
if @routing_extension
@routing_extension
else
@routing_extension = RoutingExtension.new
add_extension(@routing_extension)
@routing_extension
end
end
class DebugMessagesExtension
def debug(*args)
if defined? ::Rails
Rails.logger.debug *args
else
puts *args
end
end
def incoming(m,c)
debug " ** IN: #{m.inspect}"
c.call(m)
end
def outgoing(m,c)
debug " ** OUT: #{m.inspect}"
c.call(m)
end
end
class RoutingExtension
def initialize
@default = :allow
@mappings = {}
end
def incoming(message, callback)
if message['channel'] == '/meta/subscribe'
take_action_for message, callback, message['subscription']
elsif message['channel'] == '/meta/unsubscribe'
take_action_for message, callback, message['subscription']
elsif FayeRails::Matcher.match? '/meta/*', message['channel']
callback.call(message)
elsif FayeRails::Matcher.match? '/service/**', message['channel']
callback.call(message)
else
take_action_for message, callback, message['channel']
end
end
def map(channel, controller)
if FayeRails::Matcher.match? '/**', channel
(@mappings[channel] ||= []) << controller
else
raise ArgumentError, "Invalid channel name: #{channel}"
end
end
def block_unknown_channels!
@default = :block
end
def drop_unknown_channels!
@default = :drop
end
def allow_unknown_channels!
@default = :allow
end
def take_action_for(message, callback, test='')
if @mappings.keys.select { |glob| FayeRails::Matcher.match? glob, test }.size > 0
callback.call(message)
elsif @default == :block
message['error'] = "Permission denied"
callback.call(message)
elsif @default == :drop
callback.call(nil)
elsif @default == :allow
callback.call(message)
else
callback.call(nil)
end
end
end
end
end