wiser_trails 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +145 -0
- data/Rakefile +2 -0
- data/lib/generators/wiser_trails.rb +12 -0
- data/lib/generators/wiser_trails/migration/migration_generator.rb +17 -0
- data/lib/generators/wiser_trails/migration/templates/migration.rb +23 -0
- data/lib/generators/wiser_trails/model/model_generator.rb +17 -0
- data/lib/generators/wiser_trails/model/templates/model.rb +3 -0
- data/lib/wiser_trails.rb +69 -0
- data/lib/wiser_trails/actions/creation.rb +15 -0
- data/lib/wiser_trails/actions/destruction.rb +15 -0
- data/lib/wiser_trails/actions/update.rb +15 -0
- data/lib/wiser_trails/activity.rb +6 -0
- data/lib/wiser_trails/common.rb +334 -0
- data/lib/wiser_trails/config.rb +63 -0
- data/lib/wiser_trails/models/activist.rb +9 -0
- data/lib/wiser_trails/models/activity.rb +4 -0
- data/lib/wiser_trails/models/adapter.rb +5 -0
- data/lib/wiser_trails/models/trackable.rb +9 -0
- data/lib/wiser_trails/orm/active_record.rb +5 -0
- data/lib/wiser_trails/orm/active_record/activist.rb +48 -0
- data/lib/wiser_trails/orm/active_record/activity.rb +25 -0
- data/lib/wiser_trails/orm/active_record/adapter.rb +16 -0
- data/lib/wiser_trails/orm/active_record/trackable.rb +15 -0
- data/lib/wiser_trails/renderable.rb +118 -0
- data/lib/wiser_trails/roles/deactivatable.rb +42 -0
- data/lib/wiser_trails/roles/trail_it.rb +180 -0
- data/lib/wiser_trails/utility/store_controller.rb +37 -0
- data/lib/wiser_trails/utility/view_helpers.rb +26 -0
- data/lib/wiser_trails/version.rb +3 -0
- data/wiser_trails.gemspec +27 -0
- 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
data/Gemfile
ADDED
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,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
|
data/lib/wiser_trails.rb
ADDED
@@ -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,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
|