wfl_simple_activity 0.1.25 → 0.1.26

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/public_activity.rb +70 -0
  3. data/lib/public_activity/.DS_Store +0 -0
  4. data/lib/public_activity/actions/creation.rb +19 -0
  5. data/lib/public_activity/actions/destruction.rb +19 -0
  6. data/lib/public_activity/actions/update.rb +20 -0
  7. data/lib/public_activity/activity.rb +8 -0
  8. data/lib/public_activity/common.rb +363 -0
  9. data/lib/public_activity/config.rb +102 -0
  10. data/lib/public_activity/models/activist.rb +11 -0
  11. data/lib/public_activity/models/activity.rb +6 -0
  12. data/lib/public_activity/models/adapter.rb +7 -0
  13. data/lib/public_activity/models/trackable.rb +11 -0
  14. data/lib/public_activity/orm/.DS_Store +0 -0
  15. data/lib/public_activity/orm/active_record.rb +7 -0
  16. data/lib/public_activity/orm/active_record/activist.rb +35 -0
  17. data/lib/public_activity/orm/active_record/activity.rb +66 -0
  18. data/lib/public_activity/orm/active_record/adapter.rb +23 -0
  19. data/lib/public_activity/orm/active_record/trackable.rb +17 -0
  20. data/lib/public_activity/orm/mongo_mapper.rb +6 -0
  21. data/lib/public_activity/orm/mongo_mapper/activist.rb +36 -0
  22. data/lib/public_activity/orm/mongo_mapper/activity.rb +35 -0
  23. data/lib/public_activity/orm/mongo_mapper/adapter.rb +19 -0
  24. data/lib/public_activity/orm/mongo_mapper/trackable.rb +13 -0
  25. data/lib/public_activity/orm/mongoid.rb +6 -0
  26. data/lib/public_activity/orm/mongoid/activist.rb +36 -0
  27. data/lib/public_activity/orm/mongoid/activity.rb +34 -0
  28. data/lib/public_activity/orm/mongoid/adapter.rb +19 -0
  29. data/lib/public_activity/orm/mongoid/trackable.rb +13 -0
  30. data/lib/public_activity/renderable.rb +166 -0
  31. data/lib/public_activity/roles/deactivatable.rb +44 -0
  32. data/lib/public_activity/roles/tracked.rb +196 -0
  33. data/lib/public_activity/testing.rb +37 -0
  34. data/lib/public_activity/utility/store_controller.rb +32 -0
  35. data/lib/public_activity/utility/view_helpers.rb +30 -0
  36. data/lib/public_activity/version.rb +6 -0
  37. data/lib/wfl_simple_activity.rb +0 -1
  38. data/lib/wfl_simple_activity/version.rb +1 -1
  39. metadata +36 -2
  40. data/lib/wfl_simple_activity/common_activity.rb +0 -23
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mongoid'
4
+
5
+ module PublicActivity
6
+ module ORM
7
+ module Mongoid
8
+ # The ActiveRecord model containing
9
+ # details about recorded activity.
10
+ class Activity
11
+ include ::Mongoid::Document
12
+ include ::Mongoid::Timestamps
13
+ include ::Mongoid::Attributes::Dynamic if ::Mongoid::VERSION.split('.')[0].to_i >= 4
14
+ include Renderable
15
+
16
+ if ::Mongoid::VERSION.split('.')[0].to_i >= 7
17
+ opts = { polymorphic: true, optional: false }
18
+ else
19
+ opts = { polymorphic: true }
20
+ end
21
+
22
+ # Define polymorphic association to the parent
23
+ belongs_to :trackable, opts
24
+ # Define ownership to a resource responsible for this activity
25
+ belongs_to :owner, opts
26
+ # Define ownership to a resource targeted by this activity
27
+ belongs_to :recipient, opts
28
+
29
+ field :key, type: String
30
+ field :parameters, type: Hash
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PublicActivity
4
+ module ORM
5
+ module Mongoid
6
+ class Adapter
7
+ # Creates the activity on `trackable` with `options`
8
+ def self.create_activity(trackable, options)
9
+ trackable.activities.create options
10
+ end
11
+
12
+ # Creates activity on `trackable` with `options`; throws error on validation failure
13
+ def self.create_activity!(trackable, options)
14
+ trackable.activities.create! options
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PublicActivity
4
+ module ORM
5
+ module Mongoid
6
+ module Trackable
7
+ def self.extended(base)
8
+ base.has_many :activities, :class_name => "::PublicActivity::Activity", :as => :trackable
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PublicActivity
4
+ # Provides logic for rendering activities. Handles both i18n strings
5
+ # support and smart partials rendering (different templates per activity key).
6
+ module Renderable
7
+ # Virtual attribute returning text description of the activity
8
+ # using the activity's key to translate using i18n.
9
+ def text(params = {})
10
+ # TODO: some helper for key transformation for two supported formats
11
+ k = key.split('.')
12
+ k.unshift('activity') if k.first != 'activity'
13
+ k = k.join('.')
14
+
15
+ I18n.t(k, parameters.merge(params) || {})
16
+ end
17
+
18
+ # Renders activity from views.
19
+ #
20
+ # @param [ActionView::Base] context
21
+ # @return [nil] nil
22
+ #
23
+ # Renders activity to the given ActionView context with included
24
+ # AV::Helpers::RenderingHelper (most commonly just ActionView::Base)
25
+ #
26
+ # The *preferred* *way* of rendering activities is
27
+ # to provide a template specifying how the rendering should be happening.
28
+ # However, one may choose using _I18n_ based approach when developing
29
+ # an application that supports plenty of languages.
30
+ #
31
+ # If partial view exists that matches the *key* attribute
32
+ # renders that partial with local variables set to contain both
33
+ # Activity and activity_parameters (hash with indifferent access)
34
+ #
35
+ # Otherwise, it outputs the I18n translation to the context
36
+ # @example Render a list of all activities from a view (erb)
37
+ # <ul>
38
+ # <% for activity in PublicActivity::Activity.all %>
39
+ # <li><%= render_activity(activity) %></li>
40
+ # <% end %>
41
+ # </ul>
42
+ #
43
+ # = Layouts
44
+ # You can supply a layout that will be used for activity partials
45
+ # with :layout param.
46
+ # Keep in mind that layouts for partials are also partials.
47
+ # @example Supply a layout
48
+ # # in views:
49
+ # # All examples look for a layout in app/views/layouts/_activity.erb
50
+ # render_activity @activity, :layout => "activity"
51
+ # render_activity @activity, :layout => "layouts/activity"
52
+ # render_activity @activity, :layout => :activity
53
+ #
54
+ # # app/views/layouts/_activity.erb
55
+ # <p><%= a.created_at %></p>
56
+ # <%= yield %>
57
+ #
58
+ # == Custom Layout Location
59
+ # You can customize the layout directory by supplying :layout_root
60
+ # or by using an absolute path.
61
+ #
62
+ # @example Declare custom layout location
63
+ #
64
+ # # Both examples look for a layout in "app/views/custom/_layout.erb"
65
+ #
66
+ # render_activity @activity, :layout_root => "custom"
67
+ # render_activity @activity, :layout => "/custom/layout"
68
+ #
69
+ # = Creating a template
70
+ # To use templates for formatting how the activity should render,
71
+ # create a template based on activity key, for example:
72
+ #
73
+ # Given a key _activity.article.create_, create directory tree
74
+ # _app/views/public_activity/article/_ and create the _create_ partial there
75
+ #
76
+ # Note that if a key consists of more than three parts splitted by commas, your
77
+ # directory structure will have to be deeper, for example:
78
+ # activity.article.comments.destroy => app/views/public_activity/articles/comments/_destroy.html.erb
79
+ #
80
+ # == Custom Directory
81
+ # You can override the default `public_directory` template root with the :root parameter
82
+ #
83
+ # @example Custom template root
84
+ # # look for templates inside of /app/views/custom instead of /app/views/public_directory
85
+ # render_activity @activity, :root => "custom"
86
+ #
87
+ # == Variables in templates
88
+ # From within a template there are two variables at your disposal:
89
+ # * activity (aliased as *a* for a shortcut)
90
+ # * params (aliased as *p*) [converted into a HashWithIndifferentAccess]
91
+ #
92
+ # @example Template for key: _activity.article.create_ (erb)
93
+ # <p>
94
+ # Article <strong><%= p[:name] %></strong>
95
+ # was written by <em><%= p["author"] %></em>
96
+ # <%= distance_of_time_in_words_to_now(a.created_at) %>
97
+ # </p>
98
+ def render(context, params = {})
99
+ partial_root = params.delete(:root) || 'public_activity'
100
+ partial_path = nil
101
+ layout_root = params.delete(:layout_root) || 'layouts'
102
+
103
+ if params.has_key? :display
104
+ if params[:display].to_sym == :"i18n"
105
+ text = self.text(params)
106
+ return context.render :text => text, :plain => text
107
+ else
108
+ partial_path = File.join(partial_root, params[:display].to_s)
109
+ end
110
+ end
111
+
112
+ context.render(
113
+ params.merge({
114
+ :partial => prepare_partial(partial_root, partial_path),
115
+ :layout => prepare_layout(layout_root, params.delete(:layout)),
116
+ :locals => prepare_locals(params)
117
+ })
118
+ )
119
+ end
120
+
121
+ def prepare_partial(root, path)
122
+ path || self.template_path(self.key, root)
123
+ end
124
+
125
+ def prepare_locals(params)
126
+ locals = params.delete(:locals) || Hash.new
127
+
128
+ controller = PublicActivity.get_controller
129
+ prepared_params = prepare_parameters(params)
130
+ locals.merge(
131
+ {
132
+ :a => self,
133
+ :activity => self,
134
+ :controller => controller,
135
+ :current_user => controller.respond_to?(:current_user) ? controller.current_user : nil,
136
+ :p => prepared_params,
137
+ :params => prepared_params
138
+ }
139
+ )
140
+ end
141
+
142
+ def prepare_layout(root, layout)
143
+ if layout
144
+ path = layout.to_s
145
+ unless path.starts_with?(root) || path.starts_with?("/")
146
+ return File.join(root, path)
147
+ end
148
+ end
149
+ layout
150
+ end
151
+
152
+ def prepare_parameters(params)
153
+ @prepared_params ||= self.parameters.with_indifferent_access.merge(params)
154
+ end
155
+
156
+ protected
157
+
158
+ # Builds the path to template based on activity key
159
+ def template_path(key, partial_root)
160
+ path = key.split(".")
161
+ path.delete_at(0) if path[0] == "activity"
162
+ path.unshift partial_root
163
+ path.join("/")
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PublicActivity
4
+ # Enables per-class disabling of PublicActivity functionality.
5
+ module Deactivatable
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :public_activity_enabled_for_model
10
+ set_public_activity_class_defaults
11
+ end
12
+
13
+ # Returns true if PublicActivity is enabled
14
+ # globally and for this class.
15
+ # @return [Boolean]
16
+ # @api private
17
+ # @since 0.5.0
18
+ # overrides the method from Common
19
+ def public_activity_enabled?
20
+ PublicActivity.enabled? && self.class.public_activity_enabled_for_model
21
+ end
22
+
23
+ # Provides global methods to disable or enable PublicActivity on a per-class
24
+ # basis.
25
+ module ClassMethods
26
+ # Switches public_activity off for this class
27
+ def public_activity_off
28
+ self.public_activity_enabled_for_model = false
29
+ end
30
+
31
+ # Switches public_activity on for this class
32
+ def public_activity_on
33
+ self.public_activity_enabled_for_model = true
34
+ end
35
+
36
+ # @since 1.0.0
37
+ # @api private
38
+ def set_public_activity_class_defaults
39
+ super
40
+ self.public_activity_enabled_for_model = true
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PublicActivity
4
+ # Main module extending classes we want to keep track of.
5
+ module Tracked
6
+ extend ActiveSupport::Concern
7
+ # A shortcut method for setting custom key, owner and parameters of {Activity}
8
+ # in one line. Accepts a hash with 3 keys:
9
+ # :key, :owner, :params. You can specify all of them or just the ones you want to overwrite.
10
+ #
11
+ # == Options
12
+ #
13
+ # [:key]
14
+ # See {Common#activity_key}
15
+ # [:owner]
16
+ # See {Common#activity_owner}
17
+ # [:params]
18
+ # See {Common#activity_params}
19
+ # [:recipient]
20
+ # Set the recipient for this activity. Useful for private notifications, which should only be visible to a certain user. See {Common#activity_recipient}.
21
+ # @example
22
+ #
23
+ # @article = Article.new
24
+ # @article.title = "New article"
25
+ # @article.activity :key => "my.custom.article.key", :owner => @article.author, :params => {:title => @article.title}
26
+ # @article.save
27
+ # @article.activities.last.key #=> "my.custom.article.key"
28
+ # @article.activities.last.parameters #=> {:title => "New article"}
29
+ #
30
+ # @param options [Hash] instance options to set on the tracked model
31
+ # @return [nil]
32
+ def activity(options = {})
33
+ rest = options.clone
34
+ self.activity_key = rest.delete(:key) if rest[:key]
35
+ self.activity_owner = rest.delete(:owner) if rest[:owner]
36
+ self.activity_params = rest.delete(:params) if rest[:params]
37
+ self.activity_recipient = rest.delete(:recipient) if rest[:recipient]
38
+ self.activity_custom_fields = rest if rest.count > 0
39
+ nil
40
+ end
41
+
42
+ # Module with basic +tracked+ method that enables tracking models.
43
+ module ClassMethods
44
+ # Adds required callbacks for creating and updating
45
+ # tracked models and adds +activities+ relation for listing
46
+ # associated activities.
47
+ #
48
+ # == Parameters:
49
+ # [:owner]
50
+ # Specify the owner of the {Activity} (person responsible for the action).
51
+ # It can be a Proc, Symbol or an ActiveRecord object:
52
+ # == Examples:
53
+ #
54
+ # tracked :owner => :author
55
+ # tracked :owner => proc {|o| o.author}
56
+ #
57
+ # Keep in mind that owner relation is polymorphic, so you can't just
58
+ # provide id number of the owner object.
59
+ # [:recipient]
60
+ # Specify the recipient of the {Activity}
61
+ # It can be a Proc, Symbol, or an ActiveRecord object
62
+ # == Examples:
63
+ #
64
+ # tracked :recipient => :author
65
+ # tracked :recipient => proc {|o| o.author}
66
+ #
67
+ # Keep in mind that recipient relation is polymorphic, so you can't just
68
+ # provide id number of the owner object.
69
+ # [:params]
70
+ # Accepts a Hash with custom parameters you want to pass to i18n.translate
71
+ # method. It is later used in {Renderable#text} method.
72
+ # == Example:
73
+ # class Article < ActiveRecord::Base
74
+ # include PublicActivity::Model
75
+ # tracked :params => {
76
+ # :title => :title,
77
+ # :author_name => "Michael",
78
+ # :category_name => proc {|controller, model_instance| model_instance.category.name},
79
+ # :summary => proc {|controller, model_instance| truncate(model.text, :length => 30)}
80
+ # }
81
+ # end
82
+ #
83
+ # Values in the :params hash can either be an *exact* *value*, a *Proc/Lambda* executed before saving the activity or a *Symbol*
84
+ # which is a an attribute or a method name executed on the tracked model's instance.
85
+ #
86
+ # Everything specified here has a lower priority than parameters
87
+ # specified directly in {#activity} method.
88
+ # So treat it as a place where you provide 'default' values or where you
89
+ # specify what data should be gathered for every activity.
90
+ # For more dynamic settings refer to {Activity} model documentation.
91
+ # [:skip_defaults]
92
+ # Disables recording of activities on create/update/destroy leaving that to programmer's choice. Check {PublicActivity::Common#create_activity}
93
+ # for a guide on how to manually record activities.
94
+ # [:only]
95
+ # Accepts a symbol or an array of symbols, of which any combination of the three is accepted:
96
+ # * _:create_
97
+ # * _:update_
98
+ # * _:destroy_
99
+ # Selecting one or more of these will make PublicActivity create activities
100
+ # automatically for the tracked model on selected actions.
101
+ #
102
+ # Resulting activities will have have keys assigned to, respectively:
103
+ # * _article.create_
104
+ # * _article.update_
105
+ # * _article.destroy_
106
+ # Since only three options are valid,
107
+ # see _:except_ option for a shorter version
108
+ # [:except]
109
+ # Accepts a symbol or an array of symbols with values like in _:only_, above.
110
+ # Values provided will be subtracted from all default actions:
111
+ # (create, update, destroy).
112
+ #
113
+ # So, passing _create_ would track and automatically create
114
+ # activities on _update_ and _destroy_ actions,
115
+ # but not on the _create_ action.
116
+ # [:on]
117
+ # Accepts a Hash with key being the *action* on which to execute *value* (proc)
118
+ # Currently supported only for CRUD actions which are enabled in _:only_
119
+ # or _:except_ options on this method.
120
+ #
121
+ # Key-value pairs in this option define callbacks that can decide
122
+ # whether to create an activity or not. Procs have two attributes for
123
+ # use: _model_ and _controller_. If the proc returns true, the activity
124
+ # will be created, if not, then activity will not be saved.
125
+ #
126
+ # == Example:
127
+ # # app/models/article.rb
128
+ # tracked :on => {:update => proc {|model, controller| model.published? }}
129
+ #
130
+ # In the example above, given a model Article with boolean column _published_.
131
+ # The activities with key _article.update_ will only be created
132
+ # if the published status is set to true on that article.
133
+ # @param opts [Hash] options
134
+ # @return [nil] options
135
+ def tracked(opts = {})
136
+ options = opts.clone
137
+
138
+ include_default_actions(options)
139
+
140
+ assign_globals options
141
+ assign_hooks options
142
+ assign_custom_fields options
143
+
144
+ nil
145
+ end
146
+
147
+ def include_default_actions(options)
148
+ defaults = {
149
+ create: Creation,
150
+ destroy: Destruction,
151
+ update: Update
152
+ }
153
+
154
+ if options[:skip_defaults] == true
155
+ return
156
+ end
157
+
158
+ modules = if options[:except]
159
+ defaults.except(*options[:except])
160
+ elsif options[:only]
161
+ defaults.slice(*options[:only])
162
+ else
163
+ defaults
164
+ end
165
+
166
+ modules.each do |key, value|
167
+ include value
168
+ end
169
+ end
170
+
171
+ def available_options
172
+ [:skip_defaults, :only, :except, :on, :owner, :recipient, :params].freeze
173
+ end
174
+
175
+ def assign_globals(options)
176
+ [:owner, :recipient, :params].each do |key|
177
+ if options[key]
178
+ self.send("activity_#{key}_global=".to_sym, options.delete(key))
179
+ end
180
+ end
181
+ end
182
+
183
+ def assign_hooks(options)
184
+ if options[:on].is_a?(Hash)
185
+ self.activity_hooks = options[:on].select {|_, v| v.is_a? Proc}.symbolize_keys
186
+ end
187
+ end
188
+
189
+ def assign_custom_fields(options)
190
+ options.except(*available_options).each do |k, v|
191
+ self.activity_custom_fields_global[k] = v
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end