From ec9b41538b290696bd589445b1671d90659d506a Mon Sep 17 00:00:00 2001 From: Akash Manohar J Date: Mon, 12 Mar 2012 22:02:48 +0530 Subject: [PATCH] Adds user notifications, basic Kandan.Data APIs and some other re-organization Signed-off-by: Akash Manohar J --- .../backbone/broadcasters/faye.js.coffee | 6 ++ .../backbone/data/active_users.js.coffee | 13 +++++ .../javascripts/backbone/data/users.js.coffee | 3 + .../backbone/helpers/active_users.js.coffee | 4 ++ .../javascripts/backbone/kandan.js.coffee | 2 + .../javascripts/backbone/post_init.js.coffee | 6 ++ .../backbone/views/show_activity.js.coffee | 6 +- .../templates/user_notification.jst.eco | 1 + app/models/activity_observer.rb | 10 ++-- app/models/channel.rb | 16 +++++ config.ru | 30 +--------- lib/active_users.rb | 58 +++++++++++++++++++ lib/faye_extensions/devise_auth.rb | 22 +++++++ 13 files changed, 145 insertions(+), 32 deletions(-) create mode 100644 app/assets/javascripts/backbone/data/active_users.js.coffee create mode 100644 app/assets/javascripts/backbone/data/users.js.coffee create mode 100644 app/assets/javascripts/backbone/helpers/active_users.js.coffee create mode 100644 app/assets/javascripts/backbone/post_init.js.coffee create mode 100644 app/assets/templates/user_notification.jst.eco create mode 100644 lib/active_users.rb create mode 100644 lib/faye_extensions/devise_auth.rb diff --git a/app/assets/javascripts/backbone/broadcasters/faye.js.coffee b/app/assets/javascripts/backbone/broadcasters/faye.js.coffee index 61ea0962..52e02868 100644 --- a/app/assets/javascripts/backbone/broadcasters/faye.js.coffee +++ b/app/assets/javascripts/backbone/broadcasters/faye.js.coffee @@ -12,6 +12,12 @@ class Kandan.Broadcasters.FayeBroadcaster callback(message) } @faye_client.addExtension(auth_extension) + @faye_client.subscribe "/app/activities", (data)=> + console.log "activities", data.data.user + Kandan.Helpers.Channels.add_activity({ + user: data, + action: data.event.split("#")[1] + }) subscribe: (channel)-> diff --git a/app/assets/javascripts/backbone/data/active_users.js.coffee b/app/assets/javascripts/backbone/data/active_users.js.coffee new file mode 100644 index 00000000..e6256175 --- /dev/null +++ b/app/assets/javascripts/backbone/data/active_users.js.coffee @@ -0,0 +1,13 @@ +class Kandan.Data.ActiveUsers + @callbacks: [] + + @all: ()-> + Kandan.Helpers.ActiveUsers.all() + + @register_callback: (event, callback)-> + @callbacks.push(callback) + + @unregister_callback: (event, callback)-> + delete @callbacks[@callbacks.indexOf(callback)] + @callbacks.filter (element, index, array)-> + element!=undefined diff --git a/app/assets/javascripts/backbone/data/users.js.coffee b/app/assets/javascripts/backbone/data/users.js.coffee new file mode 100644 index 00000000..019fe0df --- /dev/null +++ b/app/assets/javascripts/backbone/data/users.js.coffee @@ -0,0 +1,3 @@ +class Kandan.Data.Users + @current_user: ()-> + Kandan.Helpers.Users.current_user() diff --git a/app/assets/javascripts/backbone/helpers/active_users.js.coffee b/app/assets/javascripts/backbone/helpers/active_users.js.coffee new file mode 100644 index 00000000..3d97c6a8 --- /dev/null +++ b/app/assets/javascripts/backbone/helpers/active_users.js.coffee @@ -0,0 +1,4 @@ +class Kandan.Helpers.ActiveUsers + + @all: ()-> + # TODO return the active users list diff --git a/app/assets/javascripts/backbone/kandan.js.coffee b/app/assets/javascripts/backbone/kandan.js.coffee index d6385dc2..b80f8f34 100644 --- a/app/assets/javascripts/backbone/kandan.js.coffee +++ b/app/assets/javascripts/backbone/kandan.js.coffee @@ -13,6 +13,8 @@ window.Kandan = Routers: {} Helpers: {} Broadcasters: {} + Data: {} + init: -> channels = new Kandan.Collections.Channels() channels.fetch({success: ()=> diff --git a/app/assets/javascripts/backbone/post_init.js.coffee b/app/assets/javascripts/backbone/post_init.js.coffee new file mode 100644 index 00000000..07dcc321 --- /dev/null +++ b/app/assets/javascripts/backbone/post_init.js.coffee @@ -0,0 +1,6 @@ + +$(document).ready -> + $(document).bind "changeData", (event, name, value)-> + if name == "active_users" + for callback in Kandan.Helpers.ActiveUsers.callbacks + callback() \ No newline at end of file diff --git a/app/assets/javascripts/backbone/views/show_activity.js.coffee b/app/assets/javascripts/backbone/views/show_activity.js.coffee index 2825c7e9..d159e86c 100644 --- a/app/assets/javascripts/backbone/views/show_activity.js.coffee +++ b/app/assets/javascripts/backbone/views/show_activity.js.coffee @@ -1,9 +1,13 @@ class Kandan.Views.ShowActivity extends Backbone.View - template: JST['activity'] tagName: 'p' className: 'activity' render: ()-> + if @options.activity.get('message') + @template = JST['activity'] + else + @template = JST['user_notification'] + $(@el).html(@template({activity: @options.activity})) @ \ No newline at end of file diff --git a/app/assets/templates/user_notification.jst.eco b/app/assets/templates/user_notification.jst.eco new file mode 100644 index 00000000..2e13a00f --- /dev/null +++ b/app/assets/templates/user_notification.jst.eco @@ -0,0 +1 @@ +<%= @activity.get('user').first_name %> <%= @activity.get('action') %> \ No newline at end of file diff --git a/app/models/activity_observer.rb b/app/models/activity_observer.rb index 55ce4aac..64165b6f 100644 --- a/app/models/activity_observer.rb +++ b/app/models/activity_observer.rb @@ -1,9 +1,11 @@ class ActivityObserver < ActiveRecord::Observer def after_save(activity) - faye_channel = "/channels/#{activity.channel.to_param}" - # TODO move this to a rabl template - broadcast_data = activity.attributes.merge({:user => activity.user.attributes}) - Kandan::Config.broadcaster.broadcast(faye_channel, broadcast_data) + if activity.action == "message" + faye_channel = "/channels/#{activity.channel.to_param}" + # TODO move this to a rabl template + broadcast_data = activity.attributes.merge({:user => activity.user.attributes}) + Kandan::Config.broadcaster.broadcast(faye_channel, broadcast_data) + end end end diff --git a/app/models/channel.rb b/app/models/channel.rb index 46deb395..e1bb2cd3 100644 --- a/app/models/channel.rb +++ b/app/models/channel.rb @@ -1,3 +1,19 @@ class Channel < ActiveRecord::Base has_many :activities + + class << self + def user_connected(user) + Channel.all.each do |channel| + activity = channel.activities.build(:user_id => user.id, :action => "connect") + activity.save + end + end + + def user_disconnected(user) + Channel.all.each do |channel| + activity = channel.activities.build(:user_id => user.id, :action => "disconnect") + activity.save + end + end + end end diff --git a/config.ru b/config.ru index 716293d2..8c12de21 100644 --- a/config.ru +++ b/config.ru @@ -2,26 +2,8 @@ require ::File.expand_path('../config/environment', __FILE__) require 'faye' - -# TODO move this to the lib dir -class DeviseAuth - def incoming(message, callback) - if message['channel'] == "/meta/subscribe" - auth_token = message['ext']['auth_token'] - user = User.find_by_authentication_token(auth_token) - if user - return callback.call(message) - else - message['error'] = "Invalid auth token" - end - end - puts "Message: #{message.inspect}" - callback.call(message) - end - - #TODO disable publishing by users -end - +require ::File.expand_path("../lib/active_users.rb", __FILE__) +require ::File.expand_path("../lib/faye_extensions/devise_auth.rb", __FILE__) faye_server = Faye::RackAdapter.new(:mount => "/faye", :timeout => 5) faye_server.add_extension(DeviseAuth.new) @@ -31,16 +13,10 @@ faye_server.add_extension(DeviseAuth.new) # which should then make the faye server object available # via the get_client() method on the server - FAYE_CLIENT = faye_server.get_client -faye_server.bind(:subscribe) do |client_id| - puts "SUBSCRIBE #{client_id}" -end - faye_server.bind(:disconnect) do |client_id| - puts "DISCONNECT #{client_id}" - User + ActiveUsers.remove_by_client_id(client_id) end run Rack::URLMap.new({ diff --git a/lib/active_users.rb b/lib/active_users.rb new file mode 100644 index 00000000..c380b386 --- /dev/null +++ b/lib/active_users.rb @@ -0,0 +1,58 @@ +class ActiveUsers + + # TODO has to account for users signed on from multiple devices + @@users = {} + + class << self + + def add(client_id, user) + publish_message "connected", user if not find_by_user_id(user.id) + @@users[client_id] = user + end + + def remove_by_client_id(client_id) + disconnected_user = @@users.delete(client_id) + publish_message "disconnected", disconnected_user if not find_by_user_id(disconnected_user.id) + end + + def remove_by_user_id(user_id) + client_id = find_by_user_id(user_id) + if not client_id + remove_by_client_id(client_id) + return true + end + false + end + + def find_by_client_id(client_id) + @@users[client_id] + end + + def find_by_user_id(user_id) + @@users.each_pair do |client_id, user| + return client_id if user.id == user_id + end + false + end + + def all + @@users.values + end + + def publish_message(event, user) + # TODO this is cheating. + # Have a common log (activities) with no channelID + # Or find some other way + + Channel.send("user_#{event}", user) + + FAYE_CLIENT.publish("/app/activities", { + :event => "user##{event}", + :data => { + :user => user, + :active_users => ActiveUsers.all + } + }) + end + end +end diff --git a/lib/faye_extensions/devise_auth.rb b/lib/faye_extensions/devise_auth.rb new file mode 100644 index 00000000..5152e1ed --- /dev/null +++ b/lib/faye_extensions/devise_auth.rb @@ -0,0 +1,22 @@ +class DeviseAuth + def incoming(message, callback) + if message['channel'] == "/meta/subscribe" + auth_token = message['ext']['auth_token'] + user = User.find_by_authentication_token(auth_token) + if user + ActiveUsers.add(message['clientId'], user) # if not meta_channels?(message['subscription']) + return callback.call(message) + else + message['error'] = "Invalid auth token" + end + end + puts "Message: #{message.inspect}" + callback.call(message) + end + + # def meta_channels?(channel) + #if ("/app/activities" =~ /\/app\/.*/ || "/app/activities" =~ /\/app\/.*/ + # end + + #TODO disable publishing by users or use only user-published msgs +end