rails_log_book 0.3.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +168 -12
- data/lib/generators/log_book/install_generator.rb +7 -1
- data/lib/generators/log_book/templates/install.rb +2 -2
- data/lib/log_book/controller_record.rb +1 -1
- data/lib/log_book/recorder.rb +16 -17
- data/lib/log_book/squash_records.rb +18 -7
- data/lib/log_book/version.rb +1 -1
- data/log_book.gemspec +2 -2
- metadata +8 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 33c84201892d61f332f7f60bd66ac9d052c37b9d9a894e5210eecf528efcb1ee
|
4
|
+
data.tar.gz: 2dd8618869a1a1955e8c24261edbd11561662792d39e736de94513ea32b28358
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f0b026c941e50454283953976c11eef81cba06e8c37ddb6ffaffbfb1ca1c3ef030c1b6e82c5be68c9151948465fa335ae4f3b79a8e2606a4a0190559e6f07cd
|
7
|
+
data.tar.gz: 0b321da7a85b6fe8620fe1863ac26560c3dd40a5fa06eabf1a7d4d1ec0f299d296994634370bac9c66a82990440ae2d578ced623b617474d2f864ec8b6443bde
|
data/README.md
CHANGED
@@ -1,29 +1,185 @@
|
|
1
1
|
# LogBook
|
2
2
|
|
3
|
-
|
3
|
+
LogBook is a gem that tracks changes on your records. Created for the purpuse of auditing and showing activity log.
|
4
|
+
For comparison with [paper_\__trail](https://github.com/airblade/paper_trail) and [audited](https://github.com/collectiveidea/audited) see []()
|
4
5
|
|
5
|
-
|
6
|
+
## Supported ORMs
|
6
7
|
|
7
|
-
|
8
|
+
Currently only supports ActiveRecord.
|
8
9
|
|
9
|
-
|
10
|
+
## Instalation
|
11
|
+
|
12
|
+
Add to your Gemfile:
|
13
|
+
|
14
|
+
``` ruby
|
15
|
+
gem 'rails_log_book'
|
16
|
+
```
|
17
|
+
|
18
|
+
Then run:
|
10
19
|
|
11
20
|
```ruby
|
12
|
-
|
21
|
+
rails generate log_book:install
|
22
|
+
rake db:migrate
|
13
23
|
```
|
14
24
|
|
15
|
-
|
25
|
+
## Usage
|
16
26
|
|
17
|
-
|
27
|
+
Add to models you want to keep track of:
|
18
28
|
|
19
|
-
|
29
|
+
``` ruby
|
30
|
+
class User < ActiveRecord::Base
|
31
|
+
include LogBook::Recorder
|
20
32
|
|
21
|
-
|
33
|
+
has_log_book_records
|
34
|
+
end
|
35
|
+
```
|
22
36
|
|
23
|
-
|
37
|
+
Add to controlers and actions you want the tracker to be active:
|
38
|
+
|
39
|
+
``` ruby
|
40
|
+
class UsersController < ApplicationController
|
41
|
+
include LogBook::ControllerRecord
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
By default, whenever a user record is created, updated or deleted in any actions of users\_controller a new log\_book record will be created.
|
46
|
+
|
47
|
+
## ActiveRecord Options
|
48
|
+
|
49
|
+
### fields
|
50
|
+
|
51
|
+
``` ruby
|
52
|
+
class User < ActiveRecord::Base
|
53
|
+
include LogBook::Recorder
|
54
|
+
|
55
|
+
# all fields
|
56
|
+
# has_log_book_records
|
24
57
|
|
25
|
-
|
58
|
+
# Only fields
|
59
|
+
# has_log_book_records only: [:email, :name]
|
60
|
+
|
61
|
+
# Ignored fields
|
62
|
+
# has_log_book_records except: [:password]
|
63
|
+
|
64
|
+
# Default ignored fields
|
65
|
+
# primary_key (id), LogBook.config.ignored_attributes (:created_at, :updated_at)
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
### callbacks
|
70
|
+
|
71
|
+
``` ruby
|
72
|
+
class User < ActiveRecord::Base
|
73
|
+
include LogBook::Recorder
|
74
|
+
|
75
|
+
# all events
|
76
|
+
# has_log_book_records
|
77
|
+
|
78
|
+
# Only record on create and destroy (not update)
|
79
|
+
# has_log_book_records on: [:create, :destroy]
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
### parent
|
84
|
+
|
85
|
+
Define who is a parent of this object. Will be recorded in a `parent` polymorphic columns
|
86
|
+
|
87
|
+
``` ruby
|
88
|
+
class User < ActiveRecord::Base
|
89
|
+
include LogBook::Recorder
|
90
|
+
belongs_to :company
|
91
|
+
|
92
|
+
# Parent is Company and will be recorded with each user change
|
93
|
+
# has_log_book_records parent: :company
|
94
|
+
end
|
95
|
+
```
|
26
96
|
|
97
|
+
### meta
|
98
|
+
|
99
|
+
Arbitrary column. This is a jsonb field which can have all kinds of information. Useful when you want to cache fields at the exact point of record creation
|
100
|
+
|
101
|
+
``` ruby
|
102
|
+
class User < ActiveRecord::Base
|
103
|
+
include LogBook::Recorder
|
104
|
+
|
105
|
+
# runs `log_book_meta(record)` method to assign to `:meta` field
|
106
|
+
# has_log_book_records meta: true
|
107
|
+
|
108
|
+
# runs `meta_method` method to assing to `:meta` field
|
109
|
+
# has_log_book_records meta: :meta_method
|
110
|
+
|
111
|
+
# runs passed proc to assing to `:meta` field
|
112
|
+
# has_log_book_records meta: -> { { slug: email.split('@').first } }
|
113
|
+
end
|
114
|
+
```
|
115
|
+
|
116
|
+
### squash
|
117
|
+
|
118
|
+
Enables/disables suashing on this model
|
119
|
+
|
120
|
+
``` ruby
|
121
|
+
class User < ActiveRecord::Base
|
122
|
+
include LogBook::Recorder
|
123
|
+
|
124
|
+
# Enables squashing (defaults to false)
|
125
|
+
# has_log_book_records squash: true
|
126
|
+
```
|
127
|
+
|
128
|
+
## ActionController options
|
129
|
+
|
130
|
+
### current\_author
|
131
|
+
|
132
|
+
Defines what method is run when looking for the author for recording
|
133
|
+
|
134
|
+
``` ruby
|
135
|
+
class Admin::UsersController < ActionController::Base
|
136
|
+
inlcude LogBook::ControllerRecord
|
137
|
+
|
138
|
+
# defaults to `current_user`
|
139
|
+
def current_author
|
140
|
+
current_admin
|
141
|
+
end
|
142
|
+
```
|
143
|
+
|
144
|
+
## Configuration
|
145
|
+
|
146
|
+
``` ruby
|
147
|
+
# config/initializers/log_book.rb
|
148
|
+
LogBook.configure do |config|
|
149
|
+
config.records_table_name = 'records'
|
150
|
+
config.ignored_attributes = [:updated_at, :created_at]
|
151
|
+
config.author_method = :current_user
|
152
|
+
config.record_squashing = false
|
153
|
+
config.recording_enabled = false
|
154
|
+
config.skip_if_empty_actions = [:update]
|
155
|
+
end
|
156
|
+
```
|
157
|
+
|
158
|
+
## Additional methods
|
159
|
+
|
160
|
+
``` ruby
|
161
|
+
LogBook.with_recording {} #=> Enables recording within block
|
162
|
+
LogBook.without_recording {} #=> Disables recording within block
|
163
|
+
LogBook.record_as(author) {} #=> Records as a different author within block
|
164
|
+
LogBook.with_record_squashing {} #=> Squashes records within block
|
165
|
+
LogBook.enable_recording #=> Enables recording from this point
|
166
|
+
LogBook.disable_recording #=> Disables recording from this point
|
167
|
+
LogBook.record_squashing_enabled #=> Enables record squashing from this point
|
168
|
+
LogBook.recording_enabled #=> Returns true if recording is enabled
|
169
|
+
LogBook.recording_enabled=(val) #=> Enables/Disables recording
|
170
|
+
LogBook.squash_records #=> Squash records with current :request_uuid
|
171
|
+
```
|
172
|
+
|
173
|
+
## Squashing
|
174
|
+
|
175
|
+
The idea of squashing came when we needed to show an activity page where all changes made in the single request as one change.
|
176
|
+
|
177
|
+
Each request has its own unique `request_uuid` and it is recorded with each record. If squashing is enabled, `after_action` method with squashing is called.
|
178
|
+
All records with the same `request_uuid` are "squashed" into one record.
|
179
|
+
|
180
|
+
``` sql
|
181
|
+
INSERT INTO "users" ("name", "email") VALUES ("test", "test@test.com")
|
182
|
+
```
|
27
183
|
## Development
|
28
184
|
|
29
185
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -32,7 +188,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
32
188
|
|
33
189
|
## Contributing
|
34
190
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
191
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/infinum/log_book. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
36
192
|
|
37
193
|
|
38
194
|
## License
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'rails/generators/migration'
|
2
2
|
require 'generators/log_book/migration'
|
3
|
-
require '
|
3
|
+
require 'rails_log_book'
|
4
4
|
|
5
5
|
module LogBook
|
6
6
|
module Generators
|
@@ -17,6 +17,12 @@ module LogBook
|
|
17
17
|
def copy_migration
|
18
18
|
migration_template 'install.rb', 'db/migrate/install_log_book.rb'
|
19
19
|
end
|
20
|
+
|
21
|
+
def migration_version
|
22
|
+
if rails5?
|
23
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
24
|
+
end
|
25
|
+
end
|
20
26
|
end
|
21
27
|
end
|
22
28
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class <%= migration_class_name %> < ActiveRecord::Migration
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
|
2
2
|
def change
|
3
3
|
create_table :records do |t|
|
4
4
|
t.belongs_to :author, polymorphic: true, index: true
|
@@ -6,7 +6,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration
|
|
6
6
|
t.belongs_to :parent, polymorphic: true, index: true
|
7
7
|
t.jsonb :record_changes, default: {}
|
8
8
|
t.jsonb :meta, default: {}
|
9
|
-
t.string :request_uuid
|
9
|
+
t.string :request_uuid, index: true
|
10
10
|
t.string :action
|
11
11
|
|
12
12
|
t.datetime :created_at
|
@@ -21,7 +21,7 @@ module LogBook
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def current_author
|
24
|
-
raise NotImplementedError unless respond_to?(LogBook.config.author_method)
|
24
|
+
raise NotImplementedError unless respond_to?(LogBook.config.author_method, true)
|
25
25
|
send(LogBook.config.author_method)
|
26
26
|
end
|
27
27
|
end
|
data/lib/log_book/recorder.rb
CHANGED
@@ -34,19 +34,6 @@ module LogBook
|
|
34
34
|
self.class.with_recording(&block)
|
35
35
|
end
|
36
36
|
|
37
|
-
def new_record
|
38
|
-
record = LogBook::Record.new(
|
39
|
-
action: LogBook.store[:action],
|
40
|
-
record_changes: record_changes,
|
41
|
-
author: LogBook.store[:author],
|
42
|
-
subject: self,
|
43
|
-
meta: {}
|
44
|
-
)
|
45
|
-
record.meta = log_book_meta_info(record) if recording_options[:meta].present?
|
46
|
-
record.parent = send(recording_options[:parent]) if recording_options[:parent].present?
|
47
|
-
record
|
48
|
-
end
|
49
|
-
|
50
37
|
def recording_attributes
|
51
38
|
attributes.except(*non_recording_columns)
|
52
39
|
end
|
@@ -60,9 +47,9 @@ module LogBook
|
|
60
47
|
def record_changes
|
61
48
|
if recording_options[:only]
|
62
49
|
recording_columns = self.class.recording_columns.map(&:name)
|
63
|
-
|
50
|
+
saved_changes.slice(*recording_columns)
|
64
51
|
else
|
65
|
-
|
52
|
+
saved_changes.except(*non_recording_columns)
|
66
53
|
end
|
67
54
|
end
|
68
55
|
|
@@ -80,12 +67,24 @@ module LogBook
|
|
80
67
|
|
81
68
|
def write_record(action)
|
82
69
|
return unless LogBook.recording_enabled
|
83
|
-
LogBook.store[:action]
|
84
|
-
record = new_record
|
70
|
+
record = new_record(LogBook.store[:action] || action)
|
85
71
|
return unless record.changes_to_record?
|
86
72
|
record.save
|
87
73
|
end
|
88
74
|
|
75
|
+
def new_record(action)
|
76
|
+
record = LogBook::Record.new(
|
77
|
+
action: action,
|
78
|
+
record_changes: record_changes,
|
79
|
+
author: LogBook.store[:author],
|
80
|
+
subject: self,
|
81
|
+
meta: {}
|
82
|
+
)
|
83
|
+
record.meta = log_book_meta_info(record) if recording_options[:meta].present?
|
84
|
+
record.parent = send(recording_options[:parent]) if recording_options[:parent].present?
|
85
|
+
record
|
86
|
+
end
|
87
|
+
|
89
88
|
def log_book_meta_info(record)
|
90
89
|
case recording_options[:meta]
|
91
90
|
when NilClass then nil
|
@@ -6,17 +6,28 @@ module LogBook
|
|
6
6
|
|
7
7
|
def call
|
8
8
|
records.group_by(&:parent).each do |parent, children|
|
9
|
-
next if parent.nil?
|
10
9
|
children_to_squash = children.select { |child| child.subject.try(:to_squash?) }
|
11
10
|
next if children_to_squash.empty?
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
if parent.present?
|
13
|
+
parent_in_records = parent_in_records(parent)
|
14
|
+
parent_in_records.record_changes.merge!(squashed_changes(children_to_squash, :record_changes))
|
15
|
+
parent_in_records.meta.merge!(squashed_changes(children_to_squash, :meta))
|
16
|
+
parent_in_records.created_at ||= children_to_squash.first.created_at
|
17
|
+
parent_in_records.save
|
18
18
|
|
19
|
-
|
19
|
+
children_to_squash.each(&:delete)
|
20
|
+
else
|
21
|
+
next if children_to_squash.one?
|
22
|
+
|
23
|
+
records.reduce do |main_record, record|
|
24
|
+
main_record.record_changes.merge!(record.record_changes)
|
25
|
+
main_record.meta.merge!(record.meta)
|
26
|
+
|
27
|
+
record.delete
|
28
|
+
main_record
|
29
|
+
end.save
|
30
|
+
end
|
20
31
|
end
|
21
32
|
end
|
22
33
|
|
data/lib/log_book/version.rb
CHANGED
data/log_book.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |spec|
|
|
6
6
|
spec.name = 'rails_log_book'
|
7
7
|
spec.version = LogBook::VERSION
|
8
8
|
spec.authors = ['Stjepan Hadjic']
|
9
|
-
spec.email = ['
|
9
|
+
spec.email = ['stjepan.hadjic@infinum.co']
|
10
10
|
|
11
11
|
spec.summary = 'Write a short summary, because Rubygems requires one.'
|
12
12
|
spec.description = 'Write a longer description or delete this line.'
|
@@ -32,5 +32,5 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.add_development_dependency 'pry-byebug'
|
33
33
|
|
34
34
|
spec.add_dependency 'request_store'
|
35
|
-
spec.add_dependency 'activerecord', '
|
35
|
+
spec.add_dependency 'activerecord', '> 5.2'
|
36
36
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_log_book
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stjepan Hadjic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-09-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -112,25 +112,19 @@ dependencies:
|
|
112
112
|
name: activerecord
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - "
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '4.0'
|
118
|
-
- - "<"
|
115
|
+
- - ">"
|
119
116
|
- !ruby/object:Gem::Version
|
120
|
-
version: '5.
|
117
|
+
version: '5.2'
|
121
118
|
type: :runtime
|
122
119
|
prerelease: false
|
123
120
|
version_requirements: !ruby/object:Gem::Requirement
|
124
121
|
requirements:
|
125
|
-
- - "
|
126
|
-
- !ruby/object:Gem::Version
|
127
|
-
version: '4.0'
|
128
|
-
- - "<"
|
122
|
+
- - ">"
|
129
123
|
- !ruby/object:Gem::Version
|
130
|
-
version: '5.
|
124
|
+
version: '5.2'
|
131
125
|
description: Write a longer description or delete this line.
|
132
126
|
email:
|
133
|
-
-
|
127
|
+
- stjepan.hadjic@infinum.co
|
134
128
|
executables: []
|
135
129
|
extensions: []
|
136
130
|
extra_rdoc_files: []
|
@@ -180,7 +174,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
180
174
|
version: '0'
|
181
175
|
requirements: []
|
182
176
|
rubyforge_project:
|
183
|
-
rubygems_version: 2.
|
177
|
+
rubygems_version: 2.7.3
|
184
178
|
signing_key:
|
185
179
|
specification_version: 4
|
186
180
|
summary: Write a short summary, because Rubygems requires one.
|