Module: Lifestreamable
- Defined in:
- lib/lifestreamable/lifestreamable.rb,
lib/lifestreamable.rb,
lib/lifestreamable/observer.rb,
lib/lifestreamable/lifestream.rb,
lib/lifestreamable/lifestreamed.rb,
lib/lifestreamable/lifestreamer.rb,
lib/lifestreamable/create_observer.rb,
lib/lifestreamable/update_observer.rb,
lib/lifestreamable/destroy_observer.rb
Overview
:title: Lifestreamable
DESCRIPTION:
Lifestreamable is a rails gem that allows social network life lifestream operations. A lifestream is a series of events that occured and that are related to an owner. It has been designed to collect data upfront in model observers to minimize the number of request done at display time. the goal being that if the lifestream dislays several types of data over several different models, then only a single query will be run to get all the data to display instead of querying all data for each model. this radiaclly cuts down on display time. This is a port to a gem of the lifestream libraries that have been designed for the sports social network legrandclub.rds.ca
INSTALLATION:
-
gem install lifestreamable
-
script/generate lifestreamable_migration
SYNOPSIS:
Ths lifestream modules is made up of 2 mixins, the lifestreamable, and lifestreamed modules. The lifestreamed module is included for models that own events, while the lifestreamable module is included on each model that triggers the event.
for example, a user can write posts and comments, we want to report in the user’s lifestream that the user has written posts and comments.
defining the owner class
class User < ActiveRecord::Base
lifestreamed :order=>'id asc' # <= overrides the normal order which is 'id desc'
end
defining the event classes
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
lifestreamable :on=>[:create, :update, :destroy], data=>:get_data, :owner=>:user
def get_data # this method must return a data structure that is serializable by YAML
{
:user=>{:firstname=>self.user.firstname, :lastname=>self.user.lastname},
:post=>{:title=>self.title}
}
end
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
# another way to get the data is through a Proc
lifestreamable :on=>[:create, :update, :destroy], :owner=>:user, data=> lambda {|model|
{
:user=>{:firstname=>model.user.firstname, :lastname=>model.user.lastname},
:post=>{:title=>model.post.title},
:comment=>{:body=>model.body}
}
}
end
Whenever a new post is created, it will create a new entry in the lifestream model. To get the lifestream from the owner:
user=User.first
# get the lifestream for the user
lifestream = user.lifestream #=> returns an array of Lifestreamable::Lifesteam model instances
#get the data that was stored
data = lifestream.first.object_data
module Lifestreamable
module for models that trigger lifestream events.
Usage:
class Post < ActiveRecord::Base
belongs_to :user
lifestreamable :on=>[:create, :update, :destroy], :data=>:get_data, :owner=>:user
def get_data # we gather the data here, while we have all this data loaded to avoid having to fetch the models when we'll display the lifestream
{:post => {:title => this.title}, :user => {:first_name => self.user.firstname, :last_name => self.user.last_name}}
end
end
Options
Note, when using Proc for the options, the model that triggers the event is always passed to the Proc. ex. Proc.new {|model| … }
-
:data => Required, Proc or function name to call to get the data that needs to be lifestreamed, the data should be in a type that is serializable by YAML (Hash, Array and Struct are good)
-
:on => Required, Array of events that trigger the insertion into the lifestream, acceptable values are: :create, :update, :destroy
-
:owner => Required, a String, Symbol, Proc or function name that returns the name of the owner of this event, i.e. the user that triggered the event.
-
:type => An identifier that represent the type of lifestream that is generated. this is useful when displaying the lifestream to select the view that will be used to display the lifestream event. Can be a String, Symbol, Proc or function name.
-
:when => An identifier that specifies if the event should be logged. Can be true, false, a Proc or a function name. A method receives the action that triggers the event. the Proc receives the model and the event. Useful in combination with the :on=> option, we will want to update the lifestream if only a specific field is modified, but not the others.
:when=>Proc.new {|model, action| if action == :update ... } :when=>:when_function def when_function(action) ... end
-
:filter => A STATIC function that will specify how to filter the data at display time, it may be necessary to remove some data when displaying the lifestream, for example 1- Bob is now friend with Bill and 2- Bill is now friend with Bob, we may want to remove one of the two events. A Proc or function name.
-
:destroy_instead_of_update => Specifies if the last entry should be destroyed in the lifestream when an :update event is triggered. can be true, false, a Proc or a function name.
-
:create_instead_of_update => Specifies if a new entry should be created in the lifestream when an :update event is triggered. can be true, false, a Proc or a function name.
-
:create_instead_of_destroy => Specifies if a new entry should be created in the lifestream when a :destroy event is triggered. can be true, false, a Proc or a function name.
-
:update_instead_of_destroy => Specifies if a the last entry should be updated in the lifestream when a :destroy event is triggered. can be true, false, a Proc or a function name.
Usage with all options
class Post < ActiveRecord::Base
belongs_to :user
lifestreamable :on=>[:create, :update, :destroy],
:data => :get_data,
:owner => :user,
:type => 'Post',
:when => lambda {|model, action| # when something else than updated_at changes
if action==:update
(model.changed-['updated_at']).size > 0
else
true
end
},
:filter => :lifestream_filter,
:destroy_instead_of_update => Proc.new { |model| model.changed.include?('title') },
:create_instead_of_update => lambda { |model| model.changed.include?('body') },
:create_instead_of_destroy => false,
:update_instead_of_destroy => :false # this is good also, and so is 'false'
def get_data # => we gather the data here, while we have all this data loaded to avoid having to fetch the models when we'll display the lifestream
{:post => {:title => this.title}, :user => {:first_name => self.user.firstname, :last_name => self.user.last_name}}
end
def self.lifestream_filter(lifestream) # <= Note this is a static function
#remove all similar events
types={}
lifestream = lifestream.collect do |l|
unless types[l.stream_type]
types[l.stream_type]=true
l
else
nil
end
end
lifestream.compact
end
end
NOTICE
the insertion into the lifestream is done as an after filter in the controller. when debigging in the console, you may want to generate the lifestream, in this case, call Lifestreamable::Lifestreamer.generate_lifestream
Defined Under Namespace
Modules: LifestreamableClassMethods, LifestreamableInstanceMethods, Lifestreamed, Lifestreamer Classes: CreateObserver, DestroyObserver, Dummy, Lifestream, Observer, UpdateObserver
Constant Summary collapse
- VERSION =
'0.0.2'
- TRUE_REGEX =
/^[tT][rR][uU][eE]$/
- FALSE_REGEX =
/^[fF][aA][lL][sS][eE]$/
- ACCEPTABLE_OPTIONS =
[:data, :on, :owner, :type, :when, :filter, :destroy_instead_of_update, :create_instead_of_update, :create_instead_of_destroy, :update_instead_of_destroy]
- REQUIRED_OPTIONS =
[:data, :on, :owner]
Class Method Summary collapse
Class Method Details
.included(base) ⇒ Object
146 147 148 |
# File 'lib/lifestreamable/lifestreamable.rb', line 146 def self.included(base) base.extend LifestreamableClassMethods end |