require File.expand_path('../config/environment', __FILE__)
bayeux = Faye::RackAdapter.new(
:mount => '/faye',
:timeout => 25
)
# some code
run bayeux
# ...
bayeux.add_extension(PushOnlyServer.new)
bayeux.add_extension(ServerAuth.new)
bayeux.bind(:subscribe) do |client_id, channel|
Rails.logger.info "#{client_id} subscribed #{channel}"
end
# ...
# ...
class FayeExtension
def incoming(message, callback)
# ...
callback.call(message)
end
def outgoing(message, callback)
# ...
callback.call(message)
end
end
# ...
class PushOnlyServer
def incoming(msg, callback)
unless msg['channel'] =~ /^\/meta\//
password = msg['ext'] &&
msg['ext']['password']
if password != AppConfig.faye.push_secret
msg['error'] = '403::Password required'
end
end
callback.call(msg)
end
# ...
end
class PushOnlyServer
# ...
def outgoing(message, callback)
unless message['ext'].nil?
message['ext'].delete('password')
end
callback.call(message)
end
end
class LiveUpdate
def self.send_to_faye(...)
# ...
message = {
:channel => 'channel-name',
:data => {
:event => 'event-name',
:faye_socket_id => 'id'
},
:ext => {
:password => 'secret'
}
}
uri = URI.parse(internal_url)
Net::HTTP.post_form(uri,
:message => message.to_json)
# ...
end
end
class ServerAuth
def incoming(msg, callback)
# ...
# some code on the next slide
# ...
# Call the server back now we're done
callback.call(msg)
end
end
# ...
# Let non-subscribe messages through
unless msg['channel'] == '/meta/subscribe'
return callback.call(msg)
end
# ...
# ...
if msg['ext'].nil?
msg['error'] = 'Missing ext key'
else
user_id = msg['ext']['user_id']
# check if user can subscribe the channel
# ...
# more code on the next slide
end
# ...
# ...
user_id = message['ext']['user_id']
faye_token = message['ext']['faye_token']
project_id = message['ext']['project_id']
project = Project.get(project_id)
if project.present?
# ... next slide
else
message['error'] = "Project doesn't exist"
end
# ...
# check if user has access to project etc
user = User.get(user_id)
if user && user.valid_faye_token?(faye_token)
client_id = message['fayeClientId']
# ...
$semaphore.synchronize do
$user_activities[client_id] = some_id
end
elsif user
message['error'] = "Invalid faye token"
else
message['error'] = "User doesn't exist"
end
# ...
# key: faye client id
# value: list of project memberships ids
$user_activities = {}
$semaphore = Mutex.new
$update_thread = Thread.new do
loop do
sleep 60
$semaphore.synchronize do
ids = $user_activities.values.uniq
# ...
Project.update_activities(ids)
end
end
end
# CoffeeScript
class K2.FayeConnection
connectAndBindEvents: ->
# ...
url = 'http://localhost:8090/faye'
@faye_client = new Faye.Client url,
timeout: timeout
# client-side extension (next slide)
# ...
# ...
@faye_client.addExtension outgoing: (message, callback) ->
message.ext = message.ext or {}
message.ext.user_id = K2.faye.currentUserId
message.ext.faye_token = K2.faye.fayeToken
message.ext.project_id = K2.faye.projectId
callback(message)
# ...
class K2.FayeConnection
# ...
@faye_client.bind 'transport:up', ->
# the client is online
console.log "New socket ID: " + faye_connection.faye_client._0
# stop interval refreshing kanban board
faye_connection.polling.stop()
faye_connection.polling.fullRefresh()
class K2.FayeConnection
# ...
@faye_client.bind 'transport:down', ->
# the client is offline
# update data with interval
# kanbanery stuff - not related to faye
faye_connection.polling.start()
faye_connected = false
class K2.FayeConnection
subscribeToChannelAndBindEvents: ->
subscription = @faye_client.subscribe @fayeChannelName(), (msg) ->
console.log "GOT MESSAGE", msg
# current faye socket id
id = faye_connection.faye_client._0
# ignore message sent by client
if msg.faye_socket_id != id
faye_connection.store.refresh msg.event
# subscription has been set up
# and acknowledged by the server
subscription.callback ->
console.log "SUBSCRIBED"
faye_connection.polling.stop()
# error while creating subscription
subscription.errback (error) ->
console.log(error.message)
faye_connection.polling.start()
Alternatives for Faye
https://github.com/maccman/juggernaut (deprecated)
http://socket.io
Alternatives hosted API
http://pusher.com
http://www.pubnub.com
Other:
http://railscasts.com/episodes/260-messaging-with-faye
http://net.tutsplus.com/tutorials/ruby/how-to-use-faye-as-a-real-time-push-server-in-rails/
https://blog.jcoglan.com/2012/06/09/why-you-should-never-use-hash-functions-for-message-authentication/