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 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
- port to rails 3.0
17
- write the tests
18
- complete the doc on which options are available for lifestreamable and lifestreamed
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)
@@ -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
@@ -18,6 +18,7 @@ module Lifestreamable
18
18
  #
19
19
  def process(action, struct)
20
20
  # put explicitly the actions accepted
21
+ puts "calling process #{action} #{struct.inspect}"
21
22
  case action
22
23
  when :create
23
24
  create get_payload(struct)
@@ -1,15 +1,85 @@
1
- # Pour le lifestream, on ajoute une methode a ActiveRecord::Base
2
- # pour savoir si on doit effacer une entree du lifestream a la
3
- # place de faire un update.
4
- # comme dans le cas de detail.youtube_username, si on efface
5
- # la valeur, on veut effacer l'entree dans le lifestream.
6
- # Meme chose pour l'insert, si on change le youtube user name,
7
- # on veut que ca insere une nouvelle rangee dans le lifestream.
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
- options[:on].each do |option_on|
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 Exception.new("option \"#{option_on}\" is not supported for Lifestreamable")
118
+ raise LifestreamableException.new("option \"#{option_on}\" is not supported for Lifestreamable")
39
119
  end
40
120
  end
41
- @@lifestream_options = {:data=>options[:data], :on=>options[:on], :type=>options[:type], :owner=>options[:owner], :when=>options[:when], :filter=>options[:filter],
42
- :destroy_instead_of_update=>options[:destroy_instead_of_update], :create_instead_of_update=>options[:create_instead_of_update],
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
+
@@ -47,7 +47,6 @@ module Lifestreamable
47
47
  end
48
48
 
49
49
  def get_filter_option(option)
50
- puts "getting filter option"
51
50
  case option
52
51
  when NilClass, FalseClass, :false, Lifestreamable::FALSE_REGEX
53
52
  false
@@ -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)
@@ -15,6 +15,3 @@ module Lifestreamable
15
15
  end
16
16
 
17
17
  end
18
-
19
- class LifestreamableException < Exception
20
- end
@@ -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
@@ -2,12 +2,12 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{lifestreamable}
5
- s.version = "0.0.1"
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-05-19}
10
- s.description = %q{library to perform social network like lifetstream functions, this is the code used on the social network http://legrandclub.rds.ca}
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
- - 1
9
- version: 0.0.1
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-05-19 00:00:00 -04:00
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 lifetstream functions, this is the code used on the social network http://legrandclub.rds.ca
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