wiser_trails 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +145 -0
  6. data/Rakefile +2 -0
  7. data/lib/generators/wiser_trails.rb +12 -0
  8. data/lib/generators/wiser_trails/migration/migration_generator.rb +17 -0
  9. data/lib/generators/wiser_trails/migration/templates/migration.rb +23 -0
  10. data/lib/generators/wiser_trails/model/model_generator.rb +17 -0
  11. data/lib/generators/wiser_trails/model/templates/model.rb +3 -0
  12. data/lib/wiser_trails.rb +69 -0
  13. data/lib/wiser_trails/actions/creation.rb +15 -0
  14. data/lib/wiser_trails/actions/destruction.rb +15 -0
  15. data/lib/wiser_trails/actions/update.rb +15 -0
  16. data/lib/wiser_trails/activity.rb +6 -0
  17. data/lib/wiser_trails/common.rb +334 -0
  18. data/lib/wiser_trails/config.rb +63 -0
  19. data/lib/wiser_trails/models/activist.rb +9 -0
  20. data/lib/wiser_trails/models/activity.rb +4 -0
  21. data/lib/wiser_trails/models/adapter.rb +5 -0
  22. data/lib/wiser_trails/models/trackable.rb +9 -0
  23. data/lib/wiser_trails/orm/active_record.rb +5 -0
  24. data/lib/wiser_trails/orm/active_record/activist.rb +48 -0
  25. data/lib/wiser_trails/orm/active_record/activity.rb +25 -0
  26. data/lib/wiser_trails/orm/active_record/adapter.rb +16 -0
  27. data/lib/wiser_trails/orm/active_record/trackable.rb +15 -0
  28. data/lib/wiser_trails/renderable.rb +118 -0
  29. data/lib/wiser_trails/roles/deactivatable.rb +42 -0
  30. data/lib/wiser_trails/roles/trail_it.rb +180 -0
  31. data/lib/wiser_trails/utility/store_controller.rb +37 -0
  32. data/lib/wiser_trails/utility/view_helpers.rb +26 -0
  33. data/lib/wiser_trails/version.rb +3 -0
  34. data/wiser_trails.gemspec +27 -0
  35. metadata +147 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NTVhMjgwMTM0ZGYyYWYzOWI3Y2RjNzU3NTk3OWUzNDdiMzhiOTM5Nw==
5
+ data.tar.gz: !binary |-
6
+ ZWI0ZDBhNTY4MGYyYWQyNjdjYTNhNjg0ZWE1NDFhZmQ1Y2FlOGIyYQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MzVhN2ZjZTgyZGIwMGM4MWQzMTFiNmY1MmZhYTc1MjA4NTVjMmMxNmZjYjU1
10
+ NjM2ZDEyYzg1YzQ3OTdiZjllZGM3ZTY0ZDA5OTliMDQ5ZTQwNDBkY2I1YjAy
11
+ ZTg4OGVkMTY1NjA1OWU2NGJjMjFlMjI5NjgxMjM4NjJhODQ2MjM=
12
+ data.tar.gz: !binary |-
13
+ MGE2Y2Y3ZTE5MWIxNjU5MjcyNTIyOWNiN2E5M2VlYWY4ZTQ4ZmQ5MjkyMjc1
14
+ M2FmNGJkNTYxNzljYjYwYmZhNGU3NjhlODgyOWQ5NTI4YTBhOWJhMjA5YmM3
15
+ NTM3ZTI3Yzc5NmU5MGI3OTcwNjU3YzJhNDEyMWM1NmFhMWU3Zjk=
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in wiser_trails.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Kenneth John Balgos
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # WiserTrails
2
+
3
+ Audit Trails in Harmony
4
+
5
+ ## Requirements
6
+
7
+ _Audit Trails_ only support Rails 3.x and 4.0 with **Active Record**.
8
+
9
+ ## Setup
10
+
11
+ ### Gem Installation
12
+
13
+ You can do normal gem installation for `wiser_trails`:
14
+
15
+ gem install wiser_trails
16
+
17
+ or in your Gemfile:
18
+
19
+ ```ruby
20
+ gem 'wiser_trails'
21
+ ```
22
+
23
+ ### Database
24
+
25
+ **(ActiveRecord only)** Generate a migration for trails and migrate the database (in your Rails project):
26
+
27
+ rails g wiser_trails:migration
28
+ rake db:migrate
29
+
30
+ ## Usage
31
+
32
+ ### Monitoring CRUD Actions
33
+
34
+ To record the Create, Update, and Delete actions, include `WiserTrails::Model` and add `trail_it` to the model you want to keep track of:
35
+
36
+ ```ruby
37
+ class Notes < ActiveRecord::Base
38
+ include WiserTrails::Model
39
+ trail_it
40
+ end
41
+ ```
42
+
43
+ If you want to automatically send the owner of the trail, include `WiserTrails::StoreController` to your `application_controller.rb`:
44
+
45
+ ```ruby
46
+ class ApplicationController < ActionController::Base
47
+ include WiserTrails::StoreController
48
+ end
49
+ ```
50
+
51
+ Then you can specify the `owner` of the trail:
52
+
53
+ ```ruby
54
+ class Notes < ActiveRecord::Base
55
+ include WiserTrails::Model
56
+ trail_it owner: ->(controller, model) { controller && controller.current_user }
57
+ end
58
+ ```
59
+
60
+ Or even the `account` if you're having a multi-account structure:
61
+
62
+ ```ruby
63
+ class Notes < ActiveRecord::Base
64
+ include WiserTrails::Model
65
+ trail_it
66
+ owner: ->(controller, model) { controller && controller.current\_user },
67
+ account: ->(controller, model) { controller && controller.current\_account }
68
+ end
69
+ ```
70
+
71
+ ### Monitoring Custom Actions
72
+
73
+ To record custom actions, you can manually trigger the `create_activity` method on the model record:
74
+
75
+ ```ruby
76
+ @note.create_activity(key: 'note.published', owner: current_user)
77
+ ```
78
+
79
+ Or even the `account` if you're having a multi-account structure:
80
+
81
+ ```ruby
82
+ @note.create_activity(key: 'note.published', owner: current_user, account: current_account)
83
+ ```
84
+
85
+ ## Fetching Trails
86
+
87
+ ### Query for the WiserTrails::Activity model
88
+ To get the trails them you simply query the `WiserTrails::Activity` model in your controllers:
89
+
90
+ ```ruby
91
+ def index
92
+ @trails = WiserTrails::Activity.order('id DESC')
93
+ end
94
+ ```
95
+
96
+ Or with account:
97
+
98
+ ```ruby
99
+ def index
100
+ @trails = WiserTrails::Activity.where(account: current_account)
101
+ end
102
+ ```
103
+
104
+ ### Getting the objects
105
+
106
+ You can get the `owner`, `account`, and the `trackable` objects from the `WiserTrails::Activity`:
107
+
108
+ ```ruby
109
+ def show
110
+ @trail = WiserTrails::Activity.find(params[:id])
111
+ # @trail.owner => current_user object
112
+ # @trail.account => current_account object
113
+ # @trail.trackable => Note object
114
+ end
115
+ ```
116
+
117
+ You can also get the new trackable value by calling `new_value`:
118
+
119
+ ```ruby
120
+ def show
121
+ @trail = WiserTrails::Activity.find(params[:id])
122
+ # @trail.new_value => exact Note attributes after the trail was recorded
123
+ end
124
+ ```
125
+
126
+ ## Contributing
127
+
128
+ 1. Fork it
129
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
130
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
131
+ 4. Push to the branch (`git push origin my-new-feature`)
132
+ 5. Create new Pull Request
133
+
134
+ ## Support
135
+ Open an issue in https://github.com/kennethjohnbalgos/wiser_trails if you need further support or want to report a bug.
136
+
137
+ ## License
138
+
139
+ The MIT License (MIT) Copyright (c) <year> <copyright holders>
140
+
141
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
142
+
143
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
144
+
145
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake'
@@ -0,0 +1,12 @@
1
+ require 'rails/generators/named_base'
2
+
3
+ module WiserTrails
4
+ module Generators
5
+ module Base
6
+ # Get path for migration template
7
+ def source_root
8
+ @_wiser_trails_source_root ||= File.expand_path(File.join('../wiser_trails', generator_name, 'templates'), __FILE__)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,17 @@
1
+ require 'generators/wiser_trails'
2
+ require 'rails/generators/active_record'
3
+
4
+ module WiserTrails
5
+ module Generators
6
+ # Migration generator that creates migration file from template
7
+ class MigrationGenerator < ActiveRecord::Generators::Base
8
+ extend Base
9
+
10
+ argument :name, :type => :string, :default => 'create_wiser_trails'
11
+ # Create migration in project's folder
12
+ def generate_files
13
+ migration_template 'migration.rb', "db/migrate/#{name}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ # Migration responsible for creating a table with trails
2
+ class CreateWiserTrails < ActiveRecord::Migration
3
+ # Create table
4
+ def self.up
5
+ create_table :wiser_trails do |t|
6
+ t.belongs_to :trackable, :polymorphic => true
7
+ t.belongs_to :account, :polymorphic => true
8
+ t.belongs_to :owner, :polymorphic => true
9
+ t.string :key
10
+ t.text :new_value
11
+
12
+ t.timestamps
13
+ end
14
+
15
+ add_index :wiser_trails, [:trackable_id, :trackable_type]
16
+ add_index :wiser_trails, [:account_id, :account_type]
17
+ add_index :wiser_trails, [:owner_id, :owner_type]
18
+ end
19
+ # Drop table
20
+ def self.down
21
+ drop_table :wiser_trails
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ require 'generators/wiser_date'
2
+ require 'rails/generators/active_record'
3
+
4
+ module WiserTrails
5
+ module Generators
6
+ # Activity generator that creates activity model file from template
7
+ class ActivityGenerator < ActiveRecord::Generators::Base
8
+ extend Base
9
+
10
+ argument :name, :type => :string, :default => 'wiser_trails'
11
+ # Create model in project's folder
12
+ def generate_files
13
+ copy_file 'model.rb', "app/models/#{name}.rb"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ # WiserTrails model for customisation & custom methods
2
+ class WiserTrails < WiserTrails::Activity
3
+ end
@@ -0,0 +1,69 @@
1
+ require 'wiser_trails/version'
2
+ require 'active_support'
3
+ require 'action_view'
4
+
5
+ # +wiser_trails+ keeps track of changes made to models
6
+ # and allows you to display them to the users.
7
+ #
8
+ # Check {WiserTrails::Tracked::ClassMethods#tracked} for more details about customizing and specifying
9
+ # ownership to users.
10
+
11
+ module WiserTrails
12
+ extend ActiveSupport::Concern
13
+ extend ActiveSupport::Autoload
14
+
15
+ autoload :Activity, 'wiser_trails/models/activity'
16
+ autoload :Activist, 'wiser_trails/models/activist'
17
+ autoload :Adapter, 'wiser_trails/models/adapter'
18
+ autoload :Trackable, 'wiser_trails/models/trackable'
19
+ autoload :Common
20
+ autoload :Config
21
+ autoload :Creation, 'wiser_trails/actions/creation.rb'
22
+ autoload :Deactivatable,'wiser_trails/roles/deactivatable.rb'
23
+ autoload :Destruction, 'wiser_trails/actions/destruction.rb'
24
+ autoload :Renderable
25
+ autoload :TrailIt, 'wiser_trails/roles/trail_it.rb'
26
+ autoload :Update, 'wiser_trails/actions/update.rb'
27
+ autoload :VERSION
28
+
29
+ # Switches WiserTrails on or off.
30
+ # @param value [Boolean]
31
+ # @since 0.5.0
32
+ def self.enabled=(value)
33
+ WiserTrails.config.enabled = value
34
+ end
35
+
36
+ # Returns `true` if WiserTrails is on, `false` otherwise.
37
+ # Enabled by default.
38
+ # @return [Boolean]
39
+ # @since 0.5.0
40
+ def self.enabled?
41
+ !!WiserTrails.config.enabled
42
+ end
43
+
44
+ def self.config
45
+ @@config ||= WiserTrails::Config.instance
46
+ end
47
+
48
+ # Method used to choose which ORM to load
49
+ # when WiserTrails::Activity class is being autoloaded
50
+ def self.inherit_orm(model="Activity")
51
+ orm = WiserTrails.config.orm
52
+ require "wiser_trails/orm/#{orm.to_s}"
53
+ "WiserTrails::ORM::#{orm.to_s.classify}::#{model}".constantize
54
+ end
55
+
56
+ # Module to be included in ActiveRecord models. Adds required functionality.
57
+ module Model
58
+ extend ActiveSupport::Concern
59
+ included do
60
+ include Common
61
+ include Deactivatable
62
+ include TrailIt
63
+ include Activist # optional associations by account|owner
64
+ end
65
+ end
66
+ end
67
+
68
+ require 'wiser_trails/utility/store_controller'
69
+ require 'wiser_trails/utility/view_helpers'
@@ -0,0 +1,15 @@
1
+ module WiserTrails
2
+ # Handles creation of Activities upon destruction and update of tracked model.
3
+ module Creation
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ after_create :activity_on_create
8
+ end
9
+ private
10
+ # Creates activity upon creation of the tracked model
11
+ def activity_on_create
12
+ create_activity(:create)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module WiserTrails
2
+ # Handles creation of Activities upon destruction of tracked model.
3
+ module Destruction
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ before_destroy :activity_on_destroy
8
+ end
9
+ private
10
+ # Records an activity upon destruction of the tracked model
11
+ def activity_on_destroy
12
+ create_activity(:destroy)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module WiserTrails
2
+ # Handles creation of Activities upon destruction and update of tracked model.
3
+ module Update
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ after_update :activity_on_update
8
+ end
9
+ private
10
+ # Creates activity upon modification of the tracked model
11
+ def activity_on_update
12
+ create_activity(:update)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ module WiserTrails
2
+ # Main model, stores all information about what happened,
3
+ # who caused it, when and anything else.
4
+ class Activity < inherit_orm("Activity")
5
+ end
6
+ end
@@ -0,0 +1,334 @@
1
+ module WiserTrails
2
+ # Happens when creating custom activities without either action or a key.
3
+ class NoKeyProvided < Exception; end
4
+
5
+ # Used to smartly transform value from metadata to data.
6
+ # Accepts Symbols, which it will send against context.
7
+ # Accepts Procs, which it will execute with controller and context.
8
+ # @since 0.4.0
9
+ def self.resolve_value(context, thing)
10
+ case thing
11
+ when Symbol
12
+ context.__send__(thing)
13
+ when Proc
14
+ thing.call(WiserTrails.get_controller, context)
15
+ else
16
+ thing
17
+ end
18
+ end
19
+
20
+ # Common methods shared across the gem.
21
+ module Common
22
+ extend ActiveSupport::Concern
23
+
24
+ included do
25
+ include Trackable
26
+ class_attribute :activity_owner_global, :activity_account_global,
27
+ :activity_new_value_global, :activity_hooks, :activity_custom_fields_global
28
+ set_wiser_trails_class_defaults
29
+ end
30
+
31
+ # @!group Global options
32
+
33
+ # @!attribute activity_owner_global
34
+ # Global version of activity owner
35
+ # @see #activity_owner
36
+ # @return [Model]
37
+
38
+ # @!attribute activity_account_global
39
+ # Global version of activity recipient
40
+ # @see #activity_account
41
+ # @return [Model]
42
+
43
+ # @!attribute activity_new_value_global
44
+ # Global version of activity parameters
45
+ # @see #activity_new_value
46
+ # @return [Hash<Symbol, Object>]
47
+
48
+ # @!attribute activity_hooks
49
+ # @return [Hash<Symbol, Proc>]
50
+ # Hooks/functions that will be used to decide *if* the activity should get
51
+ # created.
52
+ #
53
+ # The supported keys are:
54
+ # * :create
55
+ # * :update
56
+ # * :destroy
57
+
58
+ # @!endgroup
59
+
60
+ # @!group Instance options
61
+
62
+ # Set or get parameters that will be passed to {Activity} when saving
63
+ #
64
+ # == Usage:
65
+ #
66
+ # @article.activity_new_value = {:article_title => @article.title}
67
+ # @article.save
68
+ #
69
+ # This way you can pass strings that should remain constant, even when model attributes
70
+ # change after creating this {Activity}.
71
+ # @return [Hash<Symbol, Object>]
72
+ attr_accessor :activity_new_value
73
+ @activity_new_value = {}
74
+ # Set or get owner object responsible for the {Activity}.
75
+ #
76
+ # == Usage:
77
+ #
78
+ # # where current_user is an object of logged in user
79
+ # @article.activity_owner = current_user
80
+ # # OR: take @article.author association
81
+ # @article.activity_owner = :author
82
+ # # OR: provide a Proc with custom code
83
+ # @article.activity_owner = proc {|controller, model| model.author }
84
+ # @article.save
85
+ # @article.activities.last.owner #=> Returns owner object
86
+ # @return [Model] Polymorphic model
87
+ # @see #activity_owner_global
88
+ attr_accessor :activity_owner
89
+ @activity_owner = nil
90
+
91
+ # Set or get recipient for activity.
92
+ #
93
+ # Association is polymorphic, thus allowing assignment of
94
+ # all types of models. This can be used for example in the case of sending
95
+ # private notifications for only a single user.
96
+ # @return (see #activity_owner)
97
+ attr_accessor :activity_account
98
+ @activity_account = nil
99
+ # Set or get custom i18n key passed to {Activity}, later used in {Renderable#text}
100
+ #
101
+ # == Usage:
102
+ #
103
+ # @article = Article.new
104
+ # @article.activity_key = "my.custom.article.key"
105
+ # @article.save
106
+ # @article.activities.last.key #=> "my.custom.article.key"
107
+ #
108
+ # @return [String]
109
+ attr_accessor :activity_key
110
+ @activity_key = nil
111
+
112
+ # Set or get custom fields for later processing
113
+ #
114
+ # @return [Hash]
115
+ attr_accessor :activity_custom_fields
116
+ @activity_custom_fields = {}
117
+
118
+ # @!visibility private
119
+ @@activity_hooks = {}
120
+
121
+ # @!endgroup
122
+
123
+ # Provides some global methods for every model class.
124
+ module ClassMethods
125
+ #
126
+ # @since 1.0.0
127
+ # @api private
128
+ def set_wiser_trails_class_defaults
129
+ self.activity_owner_global = nil
130
+ self.activity_account_global = nil
131
+ self.activity_new_value_global = {}
132
+ self.activity_hooks = {}
133
+ self.activity_custom_fields_global = {}
134
+ end
135
+
136
+ # Extracts a hook from the _:on_ option provided in
137
+ # {Tracked::ClassMethods#tracked}. Returns nil when no hook exists for
138
+ # given action
139
+ # {Common#get_hook}
140
+ #
141
+ # @see Tracked#get_hook
142
+ # @param key [String, Symbol] action to retrieve a hook for
143
+ # @return [Proc, nil] callable hook or nil
144
+ # @since 0.4.0
145
+ # @api private
146
+ def get_hook(key)
147
+ key = key.to_sym
148
+ if self.activity_hooks.has_key?(key) and self.activity_hooks[key].is_a? Proc
149
+ self.activity_hooks[key]
150
+ else
151
+ nil
152
+ end
153
+ end
154
+ end
155
+ #
156
+ # Returns true if WiserTrails is enabled
157
+ # globally and for this class.
158
+ # @return [Boolean]
159
+ # @api private
160
+ # @since 0.5.0
161
+ def wiser_trails_enabled?
162
+ WiserTrails.enabled?
163
+ end
164
+ #
165
+ # Shortcut for {ClassMethods#get_hook}
166
+ # @param (see ClassMethods#get_hook)
167
+ # @return (see ClassMethods#get_hook)
168
+ # @since (see ClassMethods#get_hook)
169
+ # @api (see ClassMethods#get_hook)
170
+ def get_hook(key)
171
+ self.class.get_hook(key)
172
+ end
173
+
174
+ # Calls hook safely.
175
+ # If a hook for given action exists, calls it with model (self) and
176
+ # controller (if available, see {StoreController})
177
+ # @param key (see #get_hook)
178
+ # @return [Boolean] if hook exists, it's decision, if there's no hook, true
179
+ # @since 0.4.0
180
+ # @api private
181
+ def call_hook_safe(key)
182
+ hook = self.get_hook(key)
183
+ if hook
184
+ # provides hook with model and controller
185
+ hook.call(self, WiserTrails.get_controller)
186
+ else
187
+ true
188
+ end
189
+ end
190
+
191
+ # Directly creates activity record in the database, based on supplied options.
192
+ #
193
+ # It's meant for creating custom activities while *preserving* *all*
194
+ # *configuration* defined before. If you fire up the simplest of options:
195
+ #
196
+ # current_user.create_activity(:avatar_changed)
197
+ #
198
+ # It will still gather data from any procs or symbols you passed as params
199
+ # to {Tracked::ClassMethods#tracked}. It will ask the hooks you defined
200
+ # whether to really save this activity.
201
+ #
202
+ # But you can also overwrite instance and global settings with your options:
203
+ #
204
+ # @article.activity :owner => proc {|controller| controller.current_user }
205
+ # @article.create_activity(:commented_on, :owner => @user)
206
+ #
207
+ # And it's smart! It won't execute your proc, since you've chosen to
208
+ # overwrite instance parameter _:owner_ with @user.
209
+ #
210
+ # [:key]
211
+ # The key will be generated from either:
212
+ # * the first parameter you pass that is not a hash (*action*)
213
+ # * the _:action_ option in the options hash (*action*)
214
+ # * the _:key_ option in the options hash (it has to be a full key,
215
+ # including model name)
216
+ # When you pass an *action* (first two options above), they will be
217
+ # added to parameterized model name:
218
+ #
219
+ # Given Article model and instance: @article,
220
+ #
221
+ # @article.create_activity :commented_on
222
+ # @article.activities.last.key # => "article.commented_on"
223
+ #
224
+ # For other parameters, see {Tracked#activity}, and "Instance options"
225
+ # accessors at {Tracked}, information on hooks is available at
226
+ # {Tracked::ClassMethods#tracked}.
227
+ # @see #prepare_settings
228
+ # @return [Model, nil] If created successfully, new activity
229
+ # @since 0.4.0
230
+ # @api public
231
+ # @overload create_activity(action, options = {})
232
+ # @param [Symbol,String] action Name of the action
233
+ # @param [Hash] options Options with quality higher than instance options
234
+ # set in {Tracked#activity}
235
+ # @option options [Activist] :owner Owner
236
+ # @option options [Activist] :recipient Recipient
237
+ # @option options [Hash] :params Parameters, see
238
+ # {WiserTrails.resolve_value}
239
+ # @overload create_activity(options = {})
240
+ # @param [Hash] options Options with quality higher than instance options
241
+ # set in {Tracked#activity}
242
+ # @option options [Symbol,String] :action Name of the action
243
+ # @option options [String] :key Full key
244
+ # @option options [Activist] :owner Owner
245
+ # @option options [Activist] :recipient Recipient
246
+ # @option options [Hash] :params Parameters, see
247
+ # {WiserTrails.resolve_value}
248
+ def create_activity(*args)
249
+ return unless self.wiser_trails_enabled?
250
+ options = prepare_settings(*args)
251
+
252
+ if call_hook_safe(options[:key].split('.').last)
253
+ reset_activity_instance_options
254
+ return WiserTrails::Adapter.create_activity(self, options)
255
+ end
256
+
257
+ nil
258
+ end
259
+
260
+ # Prepares settings used during creation of Activity record.
261
+ # params passed directly to tracked model have priority over
262
+ # settings specified in tracked() method
263
+ #
264
+ # @see #create_activity
265
+ # @return [Hash] Settings with preserved options that were passed
266
+ # @api private
267
+ # @overload prepare_settings(action, options = {})
268
+ # @see #create_activity
269
+ # @overload prepare_settings(options = {})
270
+ # @see #create_activity
271
+ def prepare_settings(*args)
272
+ # key
273
+ all_options = args.extract_options!
274
+ options = {
275
+ key: all_options.delete(:key),
276
+ action: all_options.delete(:action)
277
+ }
278
+ action = (args.first || options[:action]).try(:to_s)
279
+
280
+ options[:key] = extract_key(action, options)
281
+
282
+ raise NoKeyProvided, "No key provided for #{self.class.name}" unless options[:key]
283
+
284
+ options.delete(:action)
285
+
286
+ # user responsible for the activity
287
+ options[:owner] = WiserTrails.resolve_value(self,
288
+ (all_options.has_key?(:owner) ? all_options[:owner] : (
289
+ self.activity_owner || self.class.activity_owner_global
290
+ )
291
+ )
292
+ )
293
+
294
+ # recipient of the activity
295
+ options[:account] = WiserTrails.resolve_value(self,
296
+ (all_options.has_key?(:account) ? all_options[:account] : (
297
+ self.activity_account || self.class.activity_account_global
298
+ )
299
+ )
300
+ )
301
+ options[:new_value] = self.attributes
302
+ options.delete(:params)
303
+
304
+ customs = self.class.activity_custom_fields_global.clone
305
+ customs.merge!(self.activity_custom_fields) if self.activity_custom_fields
306
+ customs.merge!(all_options)
307
+ customs.each do |k, v|
308
+ customs[k] = WiserTrails.resolve_value(self, v)
309
+ end.merge options
310
+ end
311
+
312
+ # Helper method to serialize class name into relevant key
313
+ # @return [String] the resulted key
314
+ # @param [Symbol] or [String] the name of the operation to be done on class
315
+ # @param [Hash] options to be used on key generation, defaults to {}
316
+ def extract_key(action, options = {})
317
+ (options[:key] || self.activity_key ||
318
+ ((self.class.name.underscore.gsub('/', '_') + "." + action.to_s) if action)
319
+ ).try(:to_s)
320
+ end
321
+
322
+ # Resets all instance options on the object
323
+ # triggered by a successful #create_activity, should not be
324
+ # called from any other place, or from application code.
325
+ # @private
326
+ def reset_activity_instance_options
327
+ @activity_new_value = {}
328
+ @activity_key = nil
329
+ @activity_owner = nil
330
+ @activity_account = nil
331
+ @activity_custom_fields = {}
332
+ end
333
+ end
334
+ end