public_activity 0.5.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. data/Gemfile +19 -2
  2. data/README.md +103 -19
  3. data/UPGRADING +4 -13
  4. data/lib/generators/public_activity/migration/templates/migration.rb +4 -0
  5. data/lib/public_activity.rb +15 -11
  6. data/lib/public_activity/{creation.rb → actions/creation.rb} +0 -0
  7. data/lib/public_activity/{destruction.rb → actions/destruction.rb} +0 -0
  8. data/lib/public_activity/{update.rb → actions/update.rb} +0 -0
  9. data/lib/public_activity/common.rb +195 -18
  10. data/lib/public_activity/config.rb +49 -3
  11. data/lib/public_activity/orm/active_record.rb +5 -0
  12. data/lib/public_activity/orm/active_record/activist.rb +42 -0
  13. data/lib/public_activity/orm/active_record/activity.rb +23 -0
  14. data/lib/public_activity/orm/active_record/adapter.rb +14 -0
  15. data/lib/public_activity/orm/active_record/trackable.rb +11 -0
  16. data/lib/public_activity/orm/mongoid.rb +4 -0
  17. data/lib/public_activity/orm/mongoid/activist.rb +42 -0
  18. data/lib/public_activity/orm/mongoid/activity.rb +25 -0
  19. data/lib/public_activity/orm/mongoid/adapter.rb +14 -0
  20. data/lib/public_activity/orm/mongoid/trackable.rb +11 -0
  21. data/lib/public_activity/{activity.rb → renderable.rb} +40 -56
  22. data/lib/public_activity/roles/deactivatable.rb +39 -0
  23. data/lib/public_activity/roles/tracked.rb +183 -0
  24. data/lib/public_activity/{store_controller.rb → utility/store_controller.rb} +0 -0
  25. data/lib/public_activity/{view_helpers.rb → utility/view_helpers.rb} +0 -0
  26. data/lib/public_activity/version.rb +1 -1
  27. metadata +40 -154
  28. data/lib/public_activity/activist.rb +0 -37
  29. data/lib/public_activity/tracked.rb +0 -337
@@ -1,12 +1,58 @@
1
+ require 'singleton'
2
+
1
3
  module PublicActivity
2
4
  # Class used to initialize configuration object.
3
5
  class Config
4
- include Singleton
6
+ include ::Singleton
5
7
  attr_accessor :enabled
6
8
 
9
+ @@orm = :active_record
10
+
7
11
  def initialize
8
12
  # Indicates whether PublicActivity is enabled globally
9
- @enabled = true
13
+ @enabled = true
14
+ load_orm
15
+ end
16
+
17
+ def self.set &block
18
+ b = Block.new
19
+ b.instance_eval &block
20
+ orm = b.instance_variable_get(:@orm)
21
+ @@orm = orm unless orm.nil?
22
+ enabled = b.instance_variable_get(:@en)
23
+ instance
24
+ instance.instance_variable_set(:@enabled, enabled) unless enabled.nil?
25
+ end
26
+
27
+ def self.orm(orm = nil)
28
+ @@orm = (orm ? orm.to_sym : false) || @@orm
29
+ end
30
+
31
+ def self.orm=(orm = nil)
32
+ orm(orm)
33
+ end
34
+
35
+ def orm(orm=nil)
36
+ self.class.orm(orm)
37
+ end
38
+
39
+ def load_orm
40
+ require "public_activity/orm/#{@@orm.to_s}"
41
+ m = "PublicActivity::ORM::#{@@orm.to_s.classify}".constantize
42
+ ::PublicActivity.const_set(:Activity, m.const_get(:Activity))
43
+ ::PublicActivity.const_set(:Adapter, m.const_get(:Adapter))
44
+ ::PublicActivity.const_set(:Activist, m.const_get(:Activist))
45
+ ::PublicActivity.const_set(:Trackable, m.const_get(:Trackable))
46
+ end
47
+
48
+ class Block
49
+ def orm(orm = nil)
50
+ @orm = (orm ? orm.to_sym : false) || @orm
51
+ end
52
+
53
+ def enabled(en = nil)
54
+ @en = (en.nil? ? @en : en)
55
+ end
10
56
  end
11
57
  end
12
- end
58
+ end
@@ -0,0 +1,5 @@
1
+ require 'active_record'
2
+ require_relative 'active_record/activity.rb'
3
+ require_relative 'active_record/adapter.rb'
4
+ require_relative 'active_record/activist.rb'
5
+ require_relative 'active_record/trackable.rb'
@@ -0,0 +1,42 @@
1
+ module PublicActivity
2
+ module ORM
3
+ module ActiveRecord
4
+ # Module extending classes that serve as owners
5
+ module Activist
6
+ extend ActiveSupport::Concern
7
+
8
+ # Association of activities as their owner.
9
+ # @!method activities
10
+ # @return [Array<Activity>] Activities which self is the owner of.
11
+
12
+ # Association of activities as their recipient.
13
+ # @!method private_activities
14
+ # @return [Array<Activity>] Activities which self is the recipient of.
15
+
16
+ # Module extending classes that serve as owners
17
+ module ClassMethods
18
+ # Adds ActiveRecord associations to model to simplify fetching
19
+ # so you can list activities performed by the owner.
20
+ # It is completely optional. Any model can be an owner to an activity
21
+ # even without being an explicit activist.
22
+ #
23
+ # == Usage:
24
+ # In model:
25
+ #
26
+ # class User < ActiveRecord::Base
27
+ # include PublicActivity::Model
28
+ # activist
29
+ # end
30
+ #
31
+ # In controller:
32
+ # User.first.activities
33
+ #
34
+ def activist
35
+ has_many :activities_as_owner, :class_name => "PublicActivity::Activity", :as => :owner
36
+ has_many :activities_as_recipient, :class_name => "PublicActivity::Activity", :as => :recipient
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,23 @@
1
+ module PublicActivity
2
+ module ORM
3
+ module ActiveRecord
4
+ # The ActiveRecord model containing
5
+ # details about recorded activity.
6
+ class Activity < ::ActiveRecord::Base
7
+ include Renderable
8
+
9
+ # Define polymorphic association to the parent
10
+ belongs_to :trackable, :polymorphic => true
11
+ # Define ownership to a resource responsible for this activity
12
+ belongs_to :owner, :polymorphic => true
13
+ # Define ownership to a resource targeted by this activity
14
+ belongs_to :recipient, :polymorphic => true
15
+ # Serialize parameters Hash
16
+ serialize :parameters, Hash
17
+
18
+ # should recipient and owner be accessible?
19
+ attr_accessible :key, :owner, :parameters, :recipient, :trackable
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,14 @@
1
+ module PublicActivity
2
+ module ORM
3
+ module ActiveRecord
4
+ module Adapter
5
+ class << self
6
+ # Creates the activity on `trackable` with `options`
7
+ def create_activity(trackable, options)
8
+ trackable.activities.create options
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ module PublicActivity
2
+ module ORM
3
+ module ActiveRecord
4
+ module Trackable
5
+ def self.included(base)
6
+ base.has_many :activities, :class_name => "::PublicActivity::Activity", :as => :trackable
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,4 @@
1
+ require_relative "mongoid/activity.rb"
2
+ require_relative "mongoid/adapter.rb"
3
+ require_relative "mongoid/activist.rb"
4
+ require_relative "mongoid/trackable.rb"
@@ -0,0 +1,42 @@
1
+ module PublicActivity
2
+ module ORM
3
+ module Mongoid
4
+ # Module extending classes that serve as owners
5
+ module Activist
6
+ extend ActiveSupport::Concern
7
+
8
+ # Association of activities as their owner.
9
+ # @!method activities
10
+ # @return [Array<Activity>] Activities which self is the owner of.
11
+
12
+ # Association of activities as their recipient.
13
+ # @!method private_activities
14
+ # @return [Array<Activity>] Activities which self is the recipient of.
15
+
16
+ # Module extending classes that serve as owners
17
+ module ClassMethods
18
+ # Adds ActiveRecord associations to model to simplify fetching
19
+ # so you can list activities performed by the owner.
20
+ # It is completely optional. Any model can be an owner to an activity
21
+ # even without being an explicit activist.
22
+ #
23
+ # == Usage:
24
+ # In model:
25
+ #
26
+ # class User < ActiveRecord::Base
27
+ # include PublicActivity::Model
28
+ # activist
29
+ # end
30
+ #
31
+ # In controller:
32
+ # User.first.activities
33
+ #
34
+ def activist
35
+ has_many :activities_as_owner, :class_name => "PublicActivity::Activity", :inverse_of => :owner
36
+ has_many :activities_as_recipient, :class_name => "PublicActivity::Activity", :inverse_of => :recipient
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,25 @@
1
+ require 'mongoid'
2
+
3
+ module PublicActivity
4
+ module ORM
5
+ module Mongoid
6
+ # The ActiveRecord model containing
7
+ # details about recorded activity.
8
+ class Activity
9
+ include ::Mongoid::Document
10
+ include ::Mongoid::Timestamps
11
+ include Renderable
12
+
13
+ # Define polymorphic association to the parent
14
+ belongs_to :trackable, polymorphic: true
15
+ # Define ownership to a resource responsible for this activity
16
+ belongs_to :owner, polymorphic: true
17
+ # Define ownership to a resource targeted by this activity
18
+ belongs_to :recipient, polymorphic: true
19
+
20
+ field :key, type: String
21
+ field :parameters, type: Hash
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ module PublicActivity
2
+ module ORM
3
+ module Mongoid
4
+ module Adapter
5
+ class << self
6
+ # Creates the activity on `trackable` with `options`
7
+ def create_activity(trackable, options)
8
+ trackable.activities.create options
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ module PublicActivity
2
+ module ORM
3
+ module Mongoid
4
+ module Trackable
5
+ def self.included(base)
6
+ base.has_many :activities, :class_name => "::PublicActivity::Activity", :as => :trackable
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,46 +1,7 @@
1
1
  module PublicActivity
2
- # The ActiveRecord model containing
3
- # details about recorded activity.
4
- class Activity < ActiveRecord::Base
5
- # Define polymorphic association to the parent
6
- belongs_to :trackable, :polymorphic => true
7
- # Define ownership to a resource responsible for this activity
8
- belongs_to :owner, :polymorphic => true
9
- # Define ownership to a resource targeted by this activity
10
- belongs_to :recipient, :polymorphic => true
11
- # Serialize parameters Hash
12
- serialize :parameters, Hash
13
-
14
- class_attribute :template
15
-
16
- # should recipient and owner be accessible?
17
- attr_accessible :key, :owner, :parameters, :recipient, :trackable
2
+ module Renderable
18
3
  # Virtual attribute returning text description of the activity
19
- # using basic ERB templating
20
- #
21
- # == Example:
22
- #
23
- # Let's say you want to show article's title inside Activity message.
24
- #
25
- # #config/pba.yml
26
- # activity:
27
- # article:
28
- # create: "New <%= trackable.name %> article has been created"
29
- # update: 'Someone modified the article'
30
- # destroy: 'Someone deleted the article!'
31
- #
32
- # And in controller:
33
- #
34
- # def create
35
- # @article = Article.new
36
- # @article.title = "Rails 3.0.5 released!"
37
- # @article.activity_params = {:title => @article.title}
38
- # @article.save
39
- # end
40
- #
41
- # Now when you list articles, you should see:
42
- # @article.activities.last.text #=> "Someone has created an article 'Rails 3.0.5 released!'"
43
- # @see #render Advanced rendering
4
+ # using the activity's key to translate using i18n.
44
5
  def text(params = {})
45
6
  # TODO: some helper for key transformation for two supported formats
46
7
  k = key.split('.')
@@ -75,6 +36,21 @@ module PublicActivity
75
36
  # <% end %>
76
37
  # </ul>
77
38
  #
39
+ # = Layouts
40
+ # You can supply a layout that will be used for activity partials
41
+ # with :layout param.
42
+ # Keep in mind that layouts for partials are also partials.
43
+ # @example Supply a layout
44
+ # # in views:
45
+ # # All examples look for a layout in app/views/layouts/_activity.erb
46
+ # render_activity @activity, :layout => "activity"
47
+ # render_activity @activity, :layout => "layouts/activity"
48
+ # render_activity @activity, :layout => :activity
49
+ #
50
+ # # app/views/layouts/_activity.erb
51
+ # <p><%= a.created_at %></p>
52
+ # <%= yield %>
53
+ #
78
54
  # = Creating a template
79
55
  # To use templates for formatting how the activity should render,
80
56
  # create a template based on activity key, for example:
@@ -84,7 +60,7 @@ module PublicActivity
84
60
  #
85
61
  # Note that if a key consists of more than three parts splitted by commas, your
86
62
  # directory structure will have to be deeper, for example:
87
- # activity.article.comments.destroy => /app/views/public_activity/articles/comments/_destroy.html.erb
63
+ # activity.article.comments.destroy => app/views/public_activity/articles/comments/_destroy.html.erb
88
64
  #
89
65
  # == Variables in templates
90
66
  # From within a template there are two variables at your disposal:
@@ -98,21 +74,29 @@ module PublicActivity
98
74
  # <%= distance_of_time_in_words_to_now(a.created_at) %>
99
75
  # </p>
100
76
  def render(context, params = {})
101
- begin
102
- params_indifferent = self.parameters.with_indifferent_access
103
- params_indifferent.merge!(params)
104
- controller = PublicActivity.get_controller
105
- context.render :partial => self.template_path(self.key),
106
- :layout => params_indifferent.delete(:layout),
107
- :locals =>
108
- {:a => self, :activity => self,
109
- :controller => controller,
110
- :current_user => controller.respond_to?(:current_user) ?
111
- controller.current_user : nil ,
112
- :p => params_indifferent, :params => params_indifferent}
113
- rescue ActionView::MissingTemplate
114
- context.render :text => self.text(params)
77
+ partial_path = nil
78
+ if params.has_key? :display
79
+ # if i18n has been requested, let it render and bail
80
+ return context.render :text => self.text(params) if params[:display].to_sym == :"i18n"
81
+ partial_path = 'public_activity/'+params[:display].to_s
82
+ end
83
+ # if we're going to render a partial, let it throw when none can be found
84
+ params_indifferent = self.parameters.with_indifferent_access
85
+ params_indifferent.merge!(params)
86
+ controller = PublicActivity.get_controller
87
+ layout = params_indifferent.delete(:layout)
88
+ if layout
89
+ layout = layout.to_s
90
+ layout = layout[0,8] == "layouts/" ? layout : "layouts/#{layout}"
115
91
  end
92
+ context.render :partial => (partial_path || self.template_path(self.key)),
93
+ :layout => layout,
94
+ :locals =>
95
+ {:a => self, :activity => self,
96
+ :controller => controller,
97
+ :current_user => controller.respond_to?(:current_user) ?
98
+ controller.current_user : nil ,
99
+ :p => params_indifferent, :params => params_indifferent}
116
100
  end
117
101
 
118
102
  protected
@@ -0,0 +1,39 @@
1
+ module PublicActivity
2
+ module Deactivatable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_attribute :public_activity_enabled_for_model
7
+ set_public_activity_class_defaults
8
+ end
9
+
10
+ # Returns true if PublicActivity is enabled
11
+ # globally and for this class.
12
+ # @return [Boolean]
13
+ # @api private
14
+ # @since 0.5.0
15
+ # overrides the method from Common
16
+ def public_activity_enabled?
17
+ PublicActivity.enabled? && self.class.public_activity_enabled_for_model
18
+ end
19
+
20
+ module ClassMethods
21
+ # Switches public_activity off for this class
22
+ def public_activity_off
23
+ self.public_activity_enabled_for_model = false
24
+ end
25
+
26
+ # Switches public_activity on for this class
27
+ def public_activity_on
28
+ self.public_activity_enabled_for_model = true
29
+ end
30
+
31
+ # @since 1.0.0
32
+ # @api private
33
+ def set_public_activity_class_defaults
34
+ super
35
+ self.public_activity_enabled_for_model = true
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,183 @@
1
+ module PublicActivity
2
+ # Main module extending classes we want to keep track of.
3
+ module Tracked
4
+ extend ActiveSupport::Concern
5
+ # A shortcut method for setting custom key, owner and parameters of {Activity}
6
+ # in one line. Accepts a hash with 3 keys:
7
+ # :key, :owner, :params. You can specify all of them or just the ones you want to overwrite.
8
+ #
9
+ # == Options
10
+ #
11
+ # [:key]
12
+ # See {#activity_key}
13
+ # [:owner]
14
+ # See {#activity_owner}
15
+ # [:params]
16
+ # See {#activity_params}
17
+ # [:recipient]
18
+ # Set the recipient for this activity. Useful for private notifications, which should only be visible to a certain user. See {#activity_recipient}.
19
+ # @example
20
+ #
21
+ # @article = Article.new
22
+ # @article.title = "New article"
23
+ # @article.activity :key => "my.custom.article.key", :owner => @article.author, :params => {:title => @article.title}
24
+ # @article.save
25
+ # @article.activities.last.key #=> "my.custom.article.key"
26
+ # @article.activities.last.parameters #=> {:title => "New article"}
27
+ #
28
+ # @param options [Hash] instance options to set on the tracked model
29
+ # @return [nil]
30
+ def activity(options = {})
31
+ rest = options.clone
32
+ self.activity_key = rest.delete(:key) if rest[:key]
33
+ self.activity_owner = rest.delete(:owner) if rest[:owner]
34
+ self.activity_params = rest.delete(:params) if rest[:params]
35
+ self.activity_recipient = rest.delete(:recipient) if rest[:recipient]
36
+ self.activity_custom_fields = rest if rest.count > 0
37
+ nil
38
+ end
39
+
40
+ # Module with basic +tracked+ method that enables tracking models.
41
+ module ClassMethods
42
+ # Adds required callbacks for creating and updating
43
+ # tracked models and adds +activities+ relation for listing
44
+ # associated activities.
45
+ #
46
+ # == Parameters:
47
+ # [:owner]
48
+ # Specify the owner of the {Activity} (person responsible for the action).
49
+ # It can be a Proc, Symbol or an ActiveRecord object:
50
+ # == Examples:
51
+ #
52
+ # tracked :owner => :author
53
+ # tracked :owner => {|o| o.author}
54
+ #
55
+ # Keep in mind that owner relation is polymorphic, so you can't just
56
+ # provide id number of the owner object.
57
+ # [:recipient]
58
+ # Specify the recipient of the {Activity}
59
+ # It can be a Proc, Symbol, or an ActiveRecord object
60
+ # == Examples:
61
+ #
62
+ # tracked :recipient => :author
63
+ # tracked :recipient => {|o| o.author}
64
+ #
65
+ # Keep in mind that recipient relation is polymorphic, so you can't just
66
+ # provide id number of the owner object.
67
+ # [:params]
68
+ # Accepts a Hash with custom parameters you want to pass to i18n.translate
69
+ # method. It is later used in {Activity#text} method.
70
+ # == Example:
71
+ # class Article < ActiveRecord::Base
72
+ # include PublicActivity::Model
73
+ # tracked :params => {
74
+ # :title => :title,
75
+ # :author_name => "Michael",
76
+ # :category_name => proc {|controller, model_instance| model_instance.category.name},
77
+ # :summary => proc {|controller, model_instance| truncate(model.text, :length => 30)}
78
+ # }
79
+ # end
80
+ #
81
+ # Values in the :params hash can either be an *exact* *value*, a *Proc/Lambda* executed before saving the activity or a *Symbol*
82
+ # which is a an attribute or a method name executed on the tracked model's instance.
83
+ #
84
+ # Everything specified here has a lower priority than parameters
85
+ # specified directly in {#activity} method.
86
+ # So treat it as a place where you provide 'default' values or where you
87
+ # specify what data should be gathered for every activity.
88
+ # For more dynamic settings refer to {Activity} model documentation.
89
+ # [:skip_defaults]
90
+ # Disables recording of activities on create/update/destroy leaving that to programmer's choice. Check {PublicActivity::Common#create_activity}
91
+ # for a guide on how to manually record activities.
92
+ # [:only]
93
+ # Accepts a symbol or an array of symbols, of which any combination of the three is accepted:
94
+ # * _:create_
95
+ # * _:update_
96
+ # * _:destroy_
97
+ # Selecting one or more of these will make PublicActivity create activities
98
+ # automatically for the tracked model on selected actions.
99
+ #
100
+ # Resulting activities will have have keys assigned to, respectively:
101
+ # * _article.create_
102
+ # * _article.update_
103
+ # * _article.destroy_
104
+ # Since only three options are valid,
105
+ # see _:except_ option for a shorter version
106
+ # [:except]
107
+ # Accepts a symbol or an array of symbols with values like in _:only_, above.
108
+ # Values provided will be subtracted from all default actions:
109
+ # (create, update, destroy).
110
+ #
111
+ # So, passing _create_ would track and automatically create
112
+ # activities on _update_ and _destroy_ actions,
113
+ # but not on the _create_ action.
114
+ # [:on]
115
+ # Accepts a Hash with key being the *action* on which to execute *value* (proc)
116
+ # Currently supported only for CRUD actions which are enabled in _:only_
117
+ # or _:except_ options on this method.
118
+ #
119
+ # Key-value pairs in this option define callbacks that can decide
120
+ # whether to create an activity or not. Procs have two attributes for
121
+ # use: _model_ and _controller_. If the proc returns true, the activity
122
+ # will be created, if not, then activity will not be saved.
123
+ #
124
+ # == Example:
125
+ # # app/models/article.rb
126
+ # tracked :on => {:update => proc {|model, controller| model.published? }}
127
+ #
128
+ # In the example above, given a model Article with boolean column _published_.
129
+ # The activities with key _article.update_ will only be created
130
+ # if the published status is set to true on that article.
131
+ # @param options [Hash] options
132
+ # @return [nil] options
133
+ def tracked(opts = {})
134
+ options = opts.clone
135
+
136
+ all_options = [:create, :update, :destroy]
137
+
138
+ if !options.has_key?(:skip_defaults) && !options[:only] && !options[:except]
139
+ include Creation
140
+ include Destruction
141
+ include Update
142
+ end
143
+ options.delete(:skip_defaults)
144
+
145
+ if options[:except]
146
+ options[:only] = all_options - Array(options.delete(:except))
147
+ end
148
+
149
+ if options[:only]
150
+ Array(options[:only]).each do |opt|
151
+ if opt.eql?(:create)
152
+ include Creation
153
+ elsif opt.eql?(:destroy)
154
+ include Destruction
155
+ elsif opt.eql?(:update)
156
+ include Update
157
+ end
158
+ end
159
+ options.delete(:only)
160
+ end
161
+
162
+ if options[:owner]
163
+ self.activity_owner_global = options.delete(:owner)
164
+ end
165
+ if options[:recipient]
166
+ self.activity_recipient_global = options.delete(:recipient)
167
+ end
168
+ if options[:params]
169
+ self.activity_params_global = options.delete(:params)
170
+ end
171
+ if options.has_key?(:on) and options[:on].is_a? Hash
172
+ self.activity_hooks = options.delete(:on).select {|_, v| v.is_a? Proc}.symbolize_keys
173
+ end
174
+
175
+ options.each do |k, v|
176
+ self.activity_custom_fields_global[k] = v
177
+ end
178
+
179
+ nil
180
+ end
181
+ end
182
+ end
183
+ end