public_activity 0.3.4 → 0.4.0.rc1

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.
@@ -2,15 +2,14 @@ module PublicActivity
2
2
  # Handles creation of Activities upon destruction of tracked model.
3
3
  module Destruction
4
4
  extend ActiveSupport::Concern
5
-
5
+
6
6
  included do
7
7
  before_destroy :activity_on_destroy
8
- end
8
+ end
9
9
  private
10
10
  # Records an activity upon destruction of the tracked model
11
11
  def activity_on_destroy
12
- settings = prepare_settings
13
- create_activity("activity."+self.class.name.parameterize('_')+".destroy", settings[:owner], settings[:parameters])
12
+ create_activity(:destroy)
14
13
  end
15
14
  end
16
15
  end
@@ -0,0 +1,36 @@
1
+ module PublicActivity
2
+ # @private
3
+ @@controllers = Hash.new
4
+ Finalizer = lambda { |id|
5
+ @@controllers.delete id
6
+ }
7
+
8
+ class << self
9
+ # Setter for remembering controller instance
10
+ def set_controller(controller)
11
+ unless @@controllers.has_key?(Thread.current.object_id)
12
+ ObjectSpace.define_finalizer Thread.current, Finalizer
13
+ end if RUBY_VERSION != "1.9.3"
14
+ @@controllers[Thread.current.object_id] = controller
15
+ end
16
+
17
+ # Getter for accessing the controller instance
18
+ def get_controller
19
+ @@controllers[Thread.current.object_id]
20
+ end
21
+ end
22
+
23
+ # Module included in controllers to allow p_a access to controller instance
24
+ module StoreController
25
+ extend ActiveSupport::Concern
26
+
27
+ included do
28
+ before_filter :store_controller_for_public_activity
29
+ end
30
+
31
+ # Before filter executed to remember current controller
32
+ def store_controller_for_public_activity
33
+ PublicActivity.set_controller(self)
34
+ end
35
+ end
36
+ end
@@ -4,69 +4,133 @@ module PublicActivity
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- class_attribute :activity_owner_global, :activity_params_global
8
- self.activity_owner_global = nil
9
- self.activity_params_global = {}
7
+ class_attribute :activity_owner_global, :activity_recipient_global,
8
+ :activity_params_global, :activity_hooks
9
+ self.activity_owner_global = nil
10
+ self.activity_recipient_global = nil
11
+ self.activity_params_global = {}
12
+ self.activity_hooks = {}
10
13
  end
14
+
15
+ # @!group Global options
16
+
17
+ # @!attribute activity_owner_global
18
+ # Global version of activity owner
19
+ # @see #activity_owner
20
+ # @return [Model]
21
+
22
+ # @!attribute activity_recipient_global
23
+ # Global version of activity recipient
24
+ # @see #activity_recipient
25
+ # @return [Model]
26
+
27
+ # @!attribute activity_params_global
28
+ # Global version of activity parameters
29
+ # @see #activity_params
30
+ # @return [Hash<Symbol, Object>]
31
+
32
+ # @!attribute activity_hooks
33
+ # @return [Hash<Symbol, Proc>]
34
+ # Hooks/functions that will be used to decide *if* the activity should get
35
+ # created.
36
+ #
37
+ # The supported keys are:
38
+ # * :create
39
+ # * :update
40
+ # * :destroy
41
+
42
+ # @!endgroup
43
+
44
+ # @!group Instance options
45
+
11
46
  # Set or get parameters that will be passed to {Activity} when saving
12
47
  #
13
48
  # == Usage:
14
- # In model:
15
49
  #
16
- # class Article < ActiveRecord::Base
17
- # tracked
18
- # end
19
- #
20
- # In controller
21
- # @article = Article.new
22
50
  # @article.activity_params = {:article_title => @article.title}
23
51
  # @article.save
24
- # This way you can pass strings that should remain constant, even when Article
25
- # changes after creating this {Activity}.
52
+ #
53
+ # This way you can pass strings that should remain constant, even when model attributes
54
+ # change after creating this {Activity}.
55
+ # @return [Hash<Symbol, Object>]
26
56
  attr_accessor :activity_params
27
57
  @activity_params = {}
28
58
  # Set or get owner object responsible for the {Activity}.
29
59
  #
30
60
  # == Usage:
31
- # In model:
32
61
  #
33
- # class Article < ActiveRecord::Base
34
- # tracked
35
- # end
36
- # Controller:
37
- #
38
- # @article = Article.new
39
- # @article.activity_owner = current_user # where current_user is an object of logged in user
40
- # @article.activity_owner = :author # OR: take @article.author attribute
41
- # @article.activity_owner = proc {|o| o.author } # OR: provide a Proc with custom code
62
+ # # where current_user is an object of logged in user
63
+ # @article.activity_owner = current_user
64
+ # # OR: take @article.author association
65
+ # @article.activity_owner = :author
66
+ # # OR: provide a Proc with custom code
67
+ # @article.activity_owner = proc {|controller, model| model.author }
42
68
  # @article.save
43
69
  # @article.activities.last.owner #=> Returns owner object
70
+ # @return [Model] Polymorphic model
71
+ # @see #activity_owner_global
44
72
  attr_accessor :activity_owner
45
73
  @activity_owner = nil
74
+
75
+ # Set or get recipient for activity.
76
+ #
77
+ # Association is polymorphic, thus allowing assignment of
78
+ # all types of models. This can be used for example in the case of sending
79
+ # private notifications for only a single user.
80
+ # @return (see #activity_owner)
81
+ attr_accessor :activity_recipient
82
+ @activity_recipient = nil
46
83
  # Set or get custom i18n key passed to {Activity}, later used in {Activity#text}
47
84
  #
48
85
  # == Usage:
49
- # In model:
50
- #
51
- # class Article < ActiveRecord::Base
52
- # tracked
53
- # end
54
- #
55
- # In controller:
56
86
  #
57
87
  # @article = Article.new
58
- # @article.save
59
- # @article.activities.last.key #=> "activity.article.create"
60
- # By default, key looks like "activity.class_name.create|update|destroy"
61
- #
62
- # You can customize it, by setting your own key:
63
- # @article = Article.new
64
88
  # @article.activity_key = "my.custom.article.key"
65
89
  # @article.save
66
90
  # @article.activities.last.key #=> "my.custom.article.key"
91
+ #
92
+ # @return [String]
67
93
  attr_accessor :activity_key
68
94
  @activity_key = nil
69
95
 
96
+ # @!visibility private
97
+ @@activity_hooks = {}
98
+
99
+ # @!endgroup
100
+
101
+ # A shortcut method for setting custom key, owner and parameters of {Activity}
102
+ # in one line. Accepts a hash with 3 keys:
103
+ # :key, :owner, :params. You can specify all of them or just the ones you want to overwrite.
104
+ #
105
+ # == Options
106
+ #
107
+ # [:key]
108
+ # See {#activity_key}
109
+ # [:owner]
110
+ # See {#activity_owner}
111
+ # [:params]
112
+ # See {#activity_params}
113
+ # [:recipient]
114
+ # Set the recipient for this activity. Useful for private notifications, which should only be visible to a certain user. See {#activity_recipient}.
115
+ # @example
116
+ #
117
+ # @article = Article.new
118
+ # @article.title = "New article"
119
+ # @article.activity :key => "my.custom.article.key", :owner => @article.author, :params => {:title => @article.title}
120
+ # @article.save
121
+ # @article.activities.last.key #=> "my.custom.article.key"
122
+ # @article.activities.last.parameters #=> {:title => "New article"}
123
+ #
124
+ # @param options [Hash] instance options to set on the tracked model
125
+ # @return [nil]
126
+ def activity(options = {})
127
+ self.activity_key = options[:key] if options[:key]
128
+ self.activity_owner = options[:owner] if options[:owner]
129
+ self.activity_params = options[:params] if options[:params]
130
+ self.activity_recipient = options[:recipient] if options[:recipient]
131
+ nil
132
+ end
133
+
70
134
  # Module with basic +tracked+ method that enables tracking models.
71
135
  module ClassMethods
72
136
  # Adds required callbacks for creating and updating
@@ -78,28 +142,93 @@ module PublicActivity
78
142
  # Specify the owner of the {Activity} (person responsible for the action).
79
143
  # It can be a Proc, Symbol or an ActiveRecord object:
80
144
  # == Examples:
81
- # @article.activity :owner => :author
82
- # @article.activity :owner => {|o| o.author}
83
- # @article.activity :owner => User.where(:login => 'piotrek').first
84
- # Keep in mind that owner relation is polymorphic, so you can't just provide id number of the owner object.
145
+ #
146
+ # tracked :owner => :author
147
+ # tracked :owner => {|o| o.author}
148
+ #
149
+ # Keep in mind that owner relation is polymorphic, so you can't just
150
+ # provide id number of the owner object.
151
+ # [:recipient]
152
+ # Specify the recipient of the {Activity}
153
+ # It can be a Proc, Symbol, or an ActiveRecord object
154
+ # == Examples:
155
+ #
156
+ # tracked :recipient => :author
157
+ # tracked :recipient => {|o| o.author}
158
+ #
159
+ # Keep in mind that recipient relation is polymorphic, so you can't just
160
+ # provide id number of the owner object.
85
161
  # [:params]
86
162
  # Accepts a Hash with custom parameters you want to pass to i18n.translate
87
163
  # method. It is later used in {Activity#text} method.
88
164
  # == Example:
89
- # @article.activity :parameters => {:title => @article.title, :short => truncate(@article.text, :length => 50)}
90
- # Everything specified here has a lower priority than parameters specified directly in {#activity} method.
91
- # So treat it as a place where you provide 'default' values.
92
- # For more dynamic settings refer to {Activity} model
93
- # documentation.
165
+ # class Article < ActiveRecord::Base
166
+ # include PublicActivity::Model
167
+ # tracked :params => {
168
+ # :title => :title,
169
+ # :author_name => "Michael",
170
+ # :category_name => proc {|controller, model_instance| model_instance.category.name},
171
+ # :summary => proc {|controller, model_instance| truncate(model.text, :length => 30)}
172
+ # }
173
+ # end
174
+ #
175
+ # Values in the :params hash can either be an *exact* *value*, a *Proc/Lambda* executed before saving the activity or a *Symbol*
176
+ # which is a an attribute or a method name executed on the tracked model's instance.
177
+ #
178
+ # Everything specified here has a lower priority than parameters
179
+ # specified directly in {#activity} method.
180
+ # So treat it as a place where you provide 'default' values or where you
181
+ # specify what data should be gathered for every activity.
182
+ # For more dynamic settings refer to {Activity} model documentation.
94
183
  # [:skip_defaults]
95
184
  # Disables recording of activities on create/update/destroy leaving that to programmer's choice. Check {PublicActivity::Common#create_activity}
96
185
  # for a guide on how to manually record activities.
186
+ # [:only]
187
+ # Accepts array of symbols, of which correct is any combination of the three:
188
+ # * _:create_
189
+ # * _:update_
190
+ # * _:destroy_
191
+ # Selecting one or more of these will make PublicActivity create activities
192
+ # automatically for the tracked model on selected actions.
193
+ #
194
+ # Resulting activities will have have keys assigned to, respectively:
195
+ # * _article.create_
196
+ # * _article.update_
197
+ # * _article.destroy_
198
+ # Since only three options are valid in this array,
199
+ # see _:except_ option for a shorter version
200
+ # [:except]
201
+ # Accepts array of symbols with values like in _:only_, above.
202
+ # Values provided will be subtracted from all default actions:
203
+ # (create, update, destroy).
204
+ #
205
+ # So, passing _create_ would track and automatically create
206
+ # activities on _update_ and _destroy_ actions.
207
+ # [:on]
208
+ # Accepts a Hash with key being the *action* on which to execute *value* (proc)
209
+ # Currently supported only for CRUD actions which are enabled in _:only_
210
+ # or _:except_ options on this method.
211
+ #
212
+ # Key-value pairs in this option define callbacks that can decide
213
+ # whether to create an activity or not. Procs have two attributes for
214
+ # use: _model_ and _controller_. If the proc returns true, the activity
215
+ # will be created, if not, then activity will not be saved.
216
+ #
217
+ # == Example:
218
+ # # app/models/article.rb
219
+ # tracked :on => {:update => proc {|model, controller| model.published? }}
220
+ #
221
+ # In the example above, given a model Article with boolean column _published_.
222
+ # The activities with key _article.update_ will only be created
223
+ # if the published status is set to true on that article.
224
+ # @param options [Hash] options
225
+ # @return [nil] options
97
226
  def tracked(options = {})
98
227
  include Common
99
228
 
100
229
  all_options = [:create, :update, :destroy]
101
230
 
102
- if !options[:skip_defaults] && !options[:only] && !options[:except]
231
+ if !options.has_key?(:skip_defaults) && !options[:only] && !options[:except]
103
232
  include Creation
104
233
  include Destruction
105
234
  include Update
@@ -124,57 +253,65 @@ module PublicActivity
124
253
  if options[:owner]
125
254
  self.activity_owner_global = options[:owner]
126
255
  end
256
+ if options[:recipient]
257
+ self.activity_recipient_global = options[:recipient]
258
+ end
127
259
  if options[:params]
128
260
  self.activity_params_global = options[:params]
129
261
  end
262
+ if options.has_key?(:on) and options[:on].is_a? Hash
263
+ self.activity_hooks = options[:on].delete_if {|_, v| !v.is_a? Proc}.symbolize_keys if RUBY_VERSION == "1.8.7"
264
+ self.activity_hooks = options[:on].select {|_, v| v.is_a? Proc}.symbolize_keys if RUBY_VERSION != "1.8.7"
265
+ end
130
266
  has_many :activities, :class_name => "PublicActivity::Activity", :as => :trackable
131
267
 
132
268
  nil
133
269
  end
270
+
271
+ # Extracts a hook from the _:on_ option provided in
272
+ # {Tracked::ClassMethods#tracked}. Returns nil when no hook exists for
273
+ # given action
274
+ # {Tracked#get_hook}
275
+ #
276
+ # @see Tracked#get_hook
277
+ # @param key [String, Symbol] action to retrieve a hook for
278
+ # @return [Proc, nil] callable hook or nil
279
+ # @since 0.4.0
280
+ # @api private
281
+ def get_hook(key)
282
+ key = key.to_sym
283
+ if self.activity_hooks.has_key?(key) and self.activity_hooks[key].is_a? Proc
284
+ self.activity_hooks[key]
285
+ else
286
+ nil
287
+ end
288
+ end
134
289
  end
135
290
 
136
- # A shortcut method for setting custom key, owner and parameters of {Activity}
137
- # in one line. Accepts a hash with 3 keys:
138
- # :key, :owner, :params. You can specify all of them or just the ones you want to overwrite.
139
- #
140
- # === Options
141
- #
142
- # [:key]
143
- # Accepts a string that will be used as a i18n key for {Activity#text} method.
144
- # [:owner]
145
- # Specify the owner of the {Activity} (person responsible for the action).
146
- # It can be a Proc, Symbol or an ActiveRecord object:
147
- # == Examples:
148
- # @article.activity :owner => :author
149
- # @article.activity :owner => {|o| o.author}
150
- # @article.activity :owner => User.where(:login => 'piotrek').first
151
- # Keep in mind that owner relation is polymorphic, so you can't just provide id number of the owner object.
152
- # [:params]
153
- # Accepts a Hash with custom parameters you want to pass to i18n.translate
154
- # method. It is later used in {Activity#text} method.
155
- # == Example:
156
- # @article.activity :parameters => {:title => @article.title, :short => truncate(@article.text, :length => 50)}
157
- #
158
- # == Usage:
159
- # In model:
160
- #
161
- # class Article < ActiveRecord::Base
162
- # tracked
163
- # end
164
- #
165
- # In controller:
166
- #
167
- # @article = Article.new
168
- # @article.title = "New article"
169
- # @article.activity :key => "my.custom.article.key", :owner => @article.author, :params => {:title => @article.title}
170
- # @article.save
171
- # @article.activities.last.key #=> "my.custom.article.key"
172
- # @article.activities.last.parameters #=> {:title => "New article"}
173
- def activity(options = {})
174
- self.activity_key = options[:key] if options[:key]
175
- self.activity_owner = options[:owner] if options[:owner]
176
- self.activity_params = options[:params] if options[:params]
291
+ # Shortcut for {Tracked::ClassMethods#get_hook}
292
+ # @param (see Tracked::ClassMethods#get_hook)
293
+ # @return (see Tracked::ClassMethods#get_hook)
294
+ # @since (see Tracked::ClassMethods#get_hook)
295
+ # @api (see Tracked::ClassMethods#get_hook)
296
+ def get_hook(key)
297
+ self.class.get_hook(key)
177
298
  end
178
299
 
300
+ # Calls hook safely.
301
+ # If a hook for given action exists, calls it with model (self) and
302
+ # controller (if available, see {StoreController})
303
+ # @param key (see #get_hook)
304
+ # @return [Boolean] if hook exists, it's decision, if there's no hook, true
305
+ # @since 0.4.0
306
+ # @api private
307
+ def call_hook_safe(key)
308
+ hook = self.get_hook(key)
309
+ if hook
310
+ # provides hook with model and controller
311
+ hook.call(self, PublicActivity.get_controller)
312
+ else
313
+ true
314
+ end
315
+ end
179
316
  end
180
317
  end
@@ -5,12 +5,11 @@ module PublicActivity
5
5
 
6
6
  included do
7
7
  after_update :activity_on_update
8
- end
8
+ end
9
9
  private
10
10
  # Creates activity upon modification of the tracked model
11
11
  def activity_on_update
12
- settings = prepare_settings
13
- create_activity(settings[:key] || "activity."+self.class.name.parameterize('_')+".update", settings[:owner], settings[:parameters])
12
+ create_activity(:update)
14
13
  end
15
14
  end
16
15
  end
@@ -1,4 +1,4 @@
1
1
  module PublicActivity
2
2
  # A constant with gem's version
3
- VERSION = '0.3.4'
3
+ VERSION = '0.4.0.rc1'
4
4
  end
@@ -0,0 +1,12 @@
1
+ # Provides a shortcut from views to the rendering method.
2
+ module PublicActivity
3
+ # Module extending ActionView::Base and adding `render_activity` helper.
4
+ module ViewHelpers
5
+ # View helper for rendering an activity, calls {PublicActivity::Activity#render} internally.
6
+ def render_activity activity, options = {}
7
+ activity.render self, options
8
+ end
9
+ end
10
+
11
+ ActionView::Base.class_eval { include ViewHelpers }
12
+ end
@@ -1,56 +1,33 @@
1
- require 'active_support/concern'
2
- require 'active_support/dependencies'
1
+ require 'active_support'
2
+ require 'action_view'
3
3
  require 'active_record'
4
- require 'pusher'
4
+
5
5
  # +public_activity+ keeps track of changes made to models
6
- # and allows for easy displaying of them.
7
- #
8
- # Basic usage requires adding one line to your models:
9
- #
10
- # class Article < ActiveRecord::Base
11
- # tracked
12
- # end
13
- #
14
- # And creating a table for activities, by doing this:
15
- # rails generate public_activity:migration
16
- # rake db:migrate
17
- #
18
- # Now when saved, public_activity will create
19
- # an Activity record containing information about that changed/created
20
- # model.
21
- # == Displaying Activities:
6
+ # and allows you to display them to the users.
22
7
  #
23
- # Minimal example would be:
24
- #
25
- # <% for activity in PublicActivity::Activity.all %>
26
- # <%= activity.text %><br/>
27
- # <% end %>
28
- # Now you will need to add translations in your locale .yml, for the example
29
- # provided above that would be:
30
- # en:
31
- # activity:
32
- # create: 'New article has been created'
33
- # update: 'Someone modified the article'
34
- # destroy: 'Someone deleted the article!'
35
- #
36
- # Check {PublicActivity::ClassMethods#tracked} for more details about customizing and specifing
8
+ # Check {PublicActivity::Tracked::ClassMethods#tracked} for more details about customizing and specifying
37
9
  # ownership to users.
38
10
  module PublicActivity
39
11
  extend ActiveSupport::Concern
40
12
  extend ActiveSupport::Autoload
41
13
  autoload :Activist
42
14
  autoload :Activity
15
+ autoload :StoreController
43
16
  autoload :Tracked
44
17
  autoload :Creation
45
- autoload :Update
18
+ autoload :Update
46
19
  autoload :Destruction
47
20
  autoload :VERSION
48
21
  autoload :Common
49
-
50
- included do
51
- include Tracked
52
- include Activist
53
- end
22
+
23
+ # Module to be included in ActiveRecord models. Adds required functionality.
24
+ module Model
25
+ extend ActiveSupport::Concern
26
+ included do
27
+ include Tracked
28
+ include Activist
29
+ end
30
+ end
54
31
  end
55
32
 
56
- ActiveRecord::Base.send :include, PublicActivity
33
+ require 'public_activity/view_helpers'