lifestreamable 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +12 -10
- data/lib/lifestreamable.rb +2 -0
- data/lib/lifestreamable/create_observer.rb +1 -1
- data/lib/lifestreamable/destroy_observer.rb +1 -1
- data/lib/lifestreamable/lifestream.rb +1 -0
- data/lib/lifestreamable/lifestreamable.rb +100 -18
- data/lib/lifestreamable/lifestreamed.rb +0 -1
- data/lib/lifestreamable/lifestreamer.rb +6 -0
- data/lib/lifestreamable/observer.rb +0 -3
- data/lib/lifestreamable/update_observer.rb +1 -1
- data/lifestreamable.gemspec +3 -3
- data/script/console +0 -1
- metadata +4 -4
data/README.rdoc
CHANGED
@@ -11,11 +11,13 @@ This is a port to a gem of the lifestream libraries that have been designed for
|
|
11
11
|
== FEATURES/PROBLEMS:
|
12
12
|
|
13
13
|
TODO:
|
14
|
-
add support for pagination.
|
15
|
-
add support for delayed jobs.
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
* add support for pagination.
|
15
|
+
* add support for delayed jobs.
|
16
|
+
* add support for memcached
|
17
|
+
* port to rails 3.0
|
18
|
+
* write the tests
|
19
|
+
* complete the doc on which options are available for lifestreamable and lifestreamed
|
20
|
+
* make sure that syntax like user.posts << Post.create(...) triggers the observers correctly.
|
19
21
|
|
20
22
|
== SYNOPSIS:
|
21
23
|
|
@@ -27,7 +29,7 @@ for example, a user can write posts and comments, we want to report in the user'
|
|
27
29
|
|
28
30
|
defining the owner class
|
29
31
|
class User < ActiveRecord::Base
|
30
|
-
lifestreamed
|
32
|
+
lifestreamed :order=>'id asc' # <= overrides the normal order which is 'id desc'
|
31
33
|
end
|
32
34
|
|
33
35
|
defining the event classes
|
@@ -57,26 +59,26 @@ defining the event classes
|
|
57
59
|
}
|
58
60
|
end
|
59
61
|
|
60
|
-
get the lifestream from the owner
|
62
|
+
Whenever a new post is created, it will create a new entry in the lifestream model. To get the lifestream from the owner:
|
61
63
|
user=User.first
|
62
64
|
|
63
65
|
# get the lifestream for the user
|
64
|
-
lifestream = user.lifestream #=> returns an array of Lifestreamable::Lifesteam instances
|
66
|
+
lifestream = user.lifestream #=> returns an array of Lifestreamable::Lifesteam model instances
|
65
67
|
|
66
68
|
#get the data that was stored
|
67
69
|
data = lifestream.first.object_data
|
68
70
|
|
69
|
-
|
71
|
+
see the module Lifestreamable for all the details.
|
70
72
|
|
71
73
|
== REQUIREMENTS:
|
72
74
|
|
75
|
+
* ActiveRecord
|
73
76
|
* YAML
|
74
77
|
|
75
78
|
== INSTALL:
|
76
79
|
|
77
80
|
* sudo gem install lifestreamable
|
78
81
|
|
79
|
-
|
80
82
|
== LICENSE:
|
81
83
|
|
82
84
|
(The MIT License)
|
data/lib/lifestreamable.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
$:.unshift(File.dirname(__FILE__)) unless
|
2
2
|
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
3
|
|
4
|
+
require 'active_record'
|
5
|
+
|
4
6
|
require File.join(File.dirname(__FILE__),'lifestreamable/lifestreamable')
|
5
7
|
require File.join(File.dirname(__FILE__),'lifestreamable/lifestreamed')
|
6
8
|
|
@@ -2,7 +2,7 @@ module Lifestreamable
|
|
2
2
|
class CreateObserver < Lifestreamable::Observer
|
3
3
|
observe :"lifestreamable/dummy"
|
4
4
|
def after_create(model)
|
5
|
-
if model.lifestreamable?
|
5
|
+
if model.lifestreamable?(:create)
|
6
6
|
Lifestreamable::Lifestreamer.push model.get_action_instead_of(:create), model.get_payload
|
7
7
|
end
|
8
8
|
end
|
@@ -2,7 +2,7 @@ module Lifestreamable
|
|
2
2
|
class DestroyObserver < Lifestreamable::Observer
|
3
3
|
observe :"lifestreamable/dummy"
|
4
4
|
def before_destroy(model)
|
5
|
-
if model.lifestreamable?
|
5
|
+
if model.lifestreamable?(:destroy)
|
6
6
|
Lifestreamable::Lifestreamer.push model.get_action_instead_of(:destroy), model.get_payload
|
7
7
|
end
|
8
8
|
end
|
@@ -1,15 +1,85 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# on
|
1
|
+
# :title: module Lifestreamable
|
2
|
+
# module for models that trigger lifestream events.
|
3
|
+
#
|
4
|
+
# Usage:
|
5
|
+
# class Post < ActiveRecord::Base
|
6
|
+
# belongs_to :user
|
7
|
+
# lifestreamable :on=>[:create, :update, :destroy], :data=>:get_data, :owner=>:user
|
8
|
+
#
|
9
|
+
# 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
|
10
|
+
# {:post => {:title => this.title}, :user => {:first_name => self.user.firstname, :last_name => self.user.last_name}}
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# Options
|
15
|
+
# Note, when using Proc for the options, the model that triggers the event is always passed to the Proc.
|
16
|
+
# ex. Proc.new {|model| ... }
|
17
|
+
#
|
18
|
+
# * :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)
|
19
|
+
# * :on => Required, Array of events that trigger the insertion into the lifestream, acceptable values are: :create, :update, :destroy
|
20
|
+
# * :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.
|
21
|
+
# * :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.
|
22
|
+
# * :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=>[:update] option, we will want to update the lifestream if only a specific field is modified, but not the others.
|
23
|
+
# :when=>Proc.new {|model, action| if action == :update ... }
|
24
|
+
# :when=>:when_function
|
25
|
+
# def when_function(action)
|
26
|
+
# ...
|
27
|
+
# end
|
28
|
+
# * :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.
|
29
|
+
# * :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.
|
30
|
+
# * :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.
|
31
|
+
# * :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.
|
32
|
+
# * :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.
|
33
|
+
#
|
34
|
+
#
|
35
|
+
# Usage with all options
|
36
|
+
# class Post < ActiveRecord::Base
|
37
|
+
# belongs_to :user
|
38
|
+
# lifestreamable :on=>[:create, :update, :destroy],
|
39
|
+
# :data => :get_data,
|
40
|
+
# :owner => :user,
|
41
|
+
# :type => 'Post',
|
42
|
+
# :when => lambda {|model, action| # when something else than updated_at changes
|
43
|
+
# if action==:update
|
44
|
+
# (model.changed-['updated_at']).size > 0
|
45
|
+
# else
|
46
|
+
# true
|
47
|
+
# end
|
48
|
+
# },
|
49
|
+
# :filter => :lifestream_filter,
|
50
|
+
# :destroy_instead_of_update => Proc.new { |model| model.changed.include?('title') },
|
51
|
+
# :create_instead_of_update => lambda { |model| model.changed.include?('body') },
|
52
|
+
# :create_instead_of_destroy => false,
|
53
|
+
# :update_instead_of_destroy => :false # this is good also, and so is 'false'
|
54
|
+
#
|
55
|
+
# 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
|
56
|
+
# {:post => {:title => this.title}, :user => {:first_name => self.user.firstname, :last_name => self.user.last_name}}
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# def self.lifestream_filter(lifestream) # <= Note this is a static function
|
60
|
+
# #remove all similar events
|
61
|
+
# types={}
|
62
|
+
# lifestream = lifestream.collect do |l|
|
63
|
+
# unless types[l.stream_type]
|
64
|
+
# types[l.stream_type]=true
|
65
|
+
# l
|
66
|
+
# else
|
67
|
+
# nil
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
# lifestream.compact
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# WARNING, 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
|
8
76
|
|
9
77
|
module Lifestreamable
|
10
78
|
Struct.new('LifestreamData', :reference_type, :reference_id, :owner_type, :owner_id, :stream_type, :object_data_hash)
|
11
79
|
TRUE_REGEX = /^[tT][rR][uU][eE]$/
|
12
80
|
FALSE_REGEX = /^[fF][aA][lL][sS][eE]$/
|
81
|
+
ACCEPTABLE_OPTIONS = [:data, :on, :owner, :type, :when, :filter, :destroy_instead_of_update, :create_instead_of_update, :create_instead_of_destroy, :update_instead_of_destroy]
|
82
|
+
REQUIRED_OPTIONS = [:data, :on, :owner]
|
13
83
|
|
14
84
|
def self.included(base)
|
15
85
|
base.extend LifestreamableClassMethods
|
@@ -24,9 +94,19 @@ module Lifestreamable
|
|
24
94
|
|
25
95
|
def lifestreamable(options)
|
26
96
|
include LifestreamableInstanceMethods
|
27
|
-
|
28
97
|
options.to_options
|
29
|
-
|
98
|
+
|
99
|
+
unknown_keys = options.keys - ACCEPTABLE_OPTIONS
|
100
|
+
unless unknown_keys.blank?
|
101
|
+
raise LifestreamableException.new("Unknown keys for lifestreamable #{unknown_keys.inspect}")
|
102
|
+
end
|
103
|
+
|
104
|
+
required_keys = REQUIRED_OPTIONS - options.keys
|
105
|
+
unless required_keys.blank?
|
106
|
+
raise LifestreamableException.new("Some requires option for lifestreamable are missing #{required_keys.inspect}")
|
107
|
+
end
|
108
|
+
|
109
|
+
options[:on].to_a.each do |option_on|
|
30
110
|
case option_on
|
31
111
|
when :update
|
32
112
|
Lifestreamable::UpdateObserver.instance.add_class_observer self.class_name.constantize
|
@@ -35,16 +115,14 @@ module Lifestreamable
|
|
35
115
|
when :destroy
|
36
116
|
Lifestreamable::DestroyObserver.instance.add_class_observer self.class_name.constantize
|
37
117
|
else
|
38
|
-
raise
|
118
|
+
raise LifestreamableException.new("option \"#{option_on}\" is not supported for Lifestreamable")
|
39
119
|
end
|
40
120
|
end
|
41
|
-
|
42
|
-
|
43
|
-
:create_instead_of_destroy=>options[:create_instead_of_destroy], :update_instead_of_destroy=>options[:update_instead_of_destroy]}
|
121
|
+
ACCEPTABLE_OPTIONS.each {|k| @@lifestream_options[k]=options[k] }
|
122
|
+
|
44
123
|
end
|
45
124
|
|
46
125
|
def filter(lifestream)
|
47
|
-
puts "in lifestreamable.filter"
|
48
126
|
option = self.lifestream_options[:filter]
|
49
127
|
lifestream = case option
|
50
128
|
when Proc
|
@@ -64,16 +142,16 @@ module Lifestreamable
|
|
64
142
|
self.class.lifestream_options
|
65
143
|
end
|
66
144
|
|
67
|
-
def lifestreamable?
|
145
|
+
def lifestreamable?(action)
|
68
146
|
case self.lifestream_options[:when]
|
69
147
|
when NilClass, TrueClass, :true, TRUE_REGEX
|
70
148
|
true
|
71
149
|
when FalseClass, :false, FALSE_REGEX
|
72
150
|
false
|
73
151
|
when Proc
|
74
|
-
self.lifestream_options[:when].call(self)
|
152
|
+
self.lifestream_options[:when].call(self, action)
|
75
153
|
when String, Symbol
|
76
|
-
send(self.lifestream_options[:when].to_s)
|
154
|
+
send(self.lifestream_options[:when].to_s, action)
|
77
155
|
end
|
78
156
|
end
|
79
157
|
|
@@ -188,7 +266,11 @@ private
|
|
188
266
|
end
|
189
267
|
end
|
190
268
|
|
269
|
+
class LifestreamableException < Exception
|
270
|
+
end
|
271
|
+
|
191
272
|
ActiveRecord::Base.observers << :"lifestreamable/update_observer"
|
192
273
|
ActiveRecord::Base.observers << :"lifestreamable/create_observer"
|
193
274
|
ActiveRecord::Base.observers << :"lifestreamable/destroy_observer"
|
194
|
-
ActiveRecord::Base.send(:include, Lifestreamable)
|
275
|
+
ActiveRecord::Base.send(:include, Lifestreamable)
|
276
|
+
|
@@ -1,4 +1,10 @@
|
|
1
1
|
module Lifestreamable
|
2
|
+
# :title: module Lifestreamable::Lifestreamer
|
3
|
+
# Module that generates the lifestream. this is done as an after_filter that is added to ActionController::Base.
|
4
|
+
# Hence any action that is called in the controller will call this function afterwards.
|
5
|
+
# The Lifestreamable::Lifestreamer.generate_lifestream has a very small footprint when it has nothign to do.
|
6
|
+
#
|
7
|
+
# TODO, include support for delayed_jobs to do it asynchronously
|
2
8
|
module Lifestreamer
|
3
9
|
@@stack=[]
|
4
10
|
def self.push(action, lifestream_struct)
|
@@ -2,7 +2,7 @@ module Lifestreamable
|
|
2
2
|
class UpdateObserver < Lifestreamable::Observer
|
3
3
|
observe :"lifestreamable/dummy"
|
4
4
|
def after_update(model)
|
5
|
-
if model.lifestreamable?
|
5
|
+
if model.lifestreamable?(:update)
|
6
6
|
Lifestreamable::Lifestreamer.push model.get_action_instead_of(:update), model.get_payload
|
7
7
|
end
|
8
8
|
end
|
data/lifestreamable.gemspec
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{lifestreamable}
|
5
|
-
s.version = "0.0.
|
5
|
+
s.version = "0.0.2"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Benoit Goyette"]
|
9
|
-
s.date = %q{2010-
|
10
|
-
s.description = %q{library to perform social network like
|
9
|
+
s.date = %q{2010-08-04}
|
10
|
+
s.description = %q{library to perform social network like lifestream functions, this is the code used on the social network http://legrandclub.rds.ca}
|
11
11
|
s.email = %q{benoit.goyette@gmail.com}
|
12
12
|
s.files = ["History.txt",
|
13
13
|
"Manifest.txt",
|
data/script/console
CHANGED
@@ -6,5 +6,4 @@ libs = " -r irb/completion"
|
|
6
6
|
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
7
|
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
8
|
libs << " -r #{File.dirname(__FILE__) + '/../lib/lifestreamable.rb'}"
|
9
|
-
puts "Loading lifestreamable gem"
|
10
9
|
exec "#{irb} #{libs} --simple-prompt"
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 2
|
9
|
+
version: 0.0.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Benoit Goyette
|
@@ -14,11 +14,11 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-08-04 00:00:00 -04:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
21
|
-
description: library to perform social network like
|
21
|
+
description: library to perform social network like lifestream functions, this is the code used on the social network http://legrandclub.rds.ca
|
22
22
|
email: benoit.goyette@gmail.com
|
23
23
|
executables: []
|
24
24
|
|