Home > acts_as_wall

acts_as_wall

Acts_as_wall is a project mainly written in Ruby, based on the MIT license.

Plugin for implementing social network walls

= ActsAsWall

Plugin that allows implementing Facebook/Twitter style walls, where events from multiple walls are aggregated. Requires Rails 3.

= Models

Suppose an application where users have walls you can post on. There are also groups the users can join, and they also have an associated wall. While a user belongs to a group, he will be subscribed to the events of that wall. The implementation using acts_as_wall is as follows:

class Group < ActiveRecord::Base acts_as_wall # This model has an associated wall acts_as_event # This model will appear in events (e.g. a group was created by Bob) end

class User < ActiveRecord::Base acts_as_wall acts_as_listener # Users listen to walls end

class Membership < ActiveRecord::Base belongs_to :user belongs_to :group

# We want that a user listens to a group's wall whenever the user belongs to the group.
keeps_listener :user=>:group

end

class Friendship < ActiveRecord::Base belongs_to :user belongs_to :user_target, :class_name => 'User'

# A user listens to another user's wall if they're friends.
keeps_listener :user=>:user_target

end

To get all the events from walls that a user is subscribed to (usually employed in a home page):

user.feed.events # Returns events from the walls a user is subscribed

To get all the events from a wall:

group.wall.events # Returns events from a group's wall user.wall.events # Returns events from a user's wall

Also, in some cases you will want that a user is subscribed to its own wall. Remember to create a self-listener for that purpose:

class User < ActiveRecord::Base acts_as_wall acts_as_listener after_create :add_self_listener

protected
def add_self_listener
  listen_to self.wall
end

end

This is done automatically by using:

acts_as_listener :self => true

= Controllers

There is a simple way to fire events. First, you need to add WallResponder to your controller responders. An easy way to achieve this using the responders gem is:

class ApplicationController < ActionController::Base responders :wall end

Then, for each controller, you can use the announce method to declare that an event must be logged and announced in a wall. It is important to note that you need to use respond_with(@resource) for everything to work:

class GroupsController < ActionController::Base announce :current_user, :user, :except=>:destroy end

This will access controller.current_user and @group.user to get the walls to add the event to. It will process any non-get action and announce the event in these walls.

You can call announce more than once in case there's not a single rule that suits all your cases:

class GroupsController < ActionController::Base announce :current_user announce :group, :except=>:update end

Also, procs are supported, and other conditions can be used:

announce :current_user, :if=>lambda{ |post, controller| controller.current_user.friend_of?(post.user) }, :except=>:destroy

Finally, what if you don't want a different object to be included into the event? Sometimes you don't want to include a highly volatile object which is likely to be destroyed, as you would lose its associated event. This happens often with nested resources, or with resources that represent relationships. To solve those cases, you can specify the object that will be stored in the event:

class Membership < ActiveRecord::Base belongs_to :group belongs_to :user

# Don't include the membership object, but the group the user belongs to
acts_as_event :object=>:group

end

Events can be tested by using test helper methods assert_event, assert_no_event, assert_listener and assert_no_listener.

= Grouping events

When rendering events, most times you want a timeline with some events grouped. You'd rather show this:

Alice and Bob commented the post "Ruby on Rails"

instead of

Alice commented the post "Ruby on Rails" Bob commented the post "Ruby on Rails"

How to do this? The plugin makes it easy to group events using the :group_by argument when calling acts_as_event:

class Comment acts_as_event :subobject=>:commentable, :group_by=>:commentable end

This will group all events in which the commentable field is the same. In order for the events to be grouped, it is important to store the commentable field, so it is stored into the event's subobject field thanks to :subobject => :commentable.

When querying for the events, just use the arrange scope to get a list of grouped events:

@user.feed.past.arrange

It works well with will_paginate plugin:

@user.feed.past.paginate(:page=>params[:page], :per_page=>20).arrange

= Notifications

You may usually want to notify users instead of posting to their walls. This can be specified at the model:

class User < ActiveRecourd::Base acts_as_listener :mailer => true end

This will make the responder call automatically UserMailer.groups_update(user, event), UserMailer.memberships_create(user, event), etc.

To notify users instead of announcing events on their walls, use notify method instead of announce:

class PostsController < ActionController::Base notify :author, :only=>:destroy end

To get your notifications:

current_user.tray.events

= Other controllers

You can fire events from other controllers, while keeping the settings from the original controllers:

class GroupsController < ActionController::Base notify :author, :only=>:destroy def destroy @group = Group.find params[:id]

  posts = @group.posts
  @group.destroy
  fire_event posts, :destroy, :posts

  respond_with @group
end

end

= If defaults don't suit you

You can always use fire_event method if you don't want to use responders:

class GroupsController < ActionController::Base notify :members, :only=>:update

def update
  @group = Group.find params[:id]
  @group.update_attributes(params[:group])
  fire_event @group # equivalent to fire_event @group, :update, :groups
  ...
end

If none of these methods suit you, you can still manage listeners and events manually in those problematic cases.

For example, you can create listeners:

Listener.create :user=>current_user, :wall=>group.wall

or

current_user.listen_to group.wall

Or you can create events:

Event.create :object=>group, :controller=>'groups', :action=>'create', :actor=>current_user, :start_at=>DateTime.now

And so on. Enjoy the plugin :)

Copyright (c) 2010 José Ignacio Fernández (joseignacio.fernandez at gmail.com), released under the MIT license

Previous:Slobo