rails_audit_log 0.1.0
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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +89 -0
- data/Rakefile +38 -0
- data/app/assets/stylesheets/rails_audit_log/application.css +15 -0
- data/app/concerns/rails_audit_log/auditable.rb +43 -0
- data/app/concerns/rails_audit_log/controller.rb +31 -0
- data/app/controllers/rails_audit_log/application_controller.rb +4 -0
- data/app/helpers/rails_audit_log/application_helper.rb +4 -0
- data/app/jobs/rails_audit_log/application_job.rb +4 -0
- data/app/models/rails_audit_log/application_record.rb +5 -0
- data/app/models/rails_audit_log/audit_log_entry.rb +18 -0
- data/app/views/layouts/rails_audit_log/application.html.erb +17 -0
- data/config/routes.rb +2 -0
- data/lib/generators/rails_audit_log/install/install_generator.rb +21 -0
- data/lib/generators/rails_audit_log/install/templates/create_audit_log_entries.rb +18 -0
- data/lib/rails_audit_log/engine.rb +5 -0
- data/lib/rails_audit_log/version.rb +3 -0
- data/lib/rails_audit_log.rb +20 -0
- data/lib/tasks/rails_audit_log_tasks.rake +4 -0
- metadata +79 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: bf000427595792f1a97214b6ad5d6dc7f6875681f315570260760accf4e70ba3
|
|
4
|
+
data.tar.gz: 8978577023bb75e0832d475b8fcaeaeedb4a02ac3c5b1e1500a6d2187e1c5e5c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 897e191130a3895876e5ec8328386d5cd8e456b866a348a0c10edbacc4969883a8ca550e2fa2491f68937ed1a9558c5143a49503c9c80b4f1824789bbc40eee1
|
|
7
|
+
data.tar.gz: d1035cdcfcc8d243fc8cfe43fac0842973e9e0dba78e16f4917c83ec83e6db81b8eb999dc441620d01027984ca4a89cdaafb8fb0501b3b98fa4712571d08a706
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright Chuck Smith
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# RailsAuditLog
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/rb/rails_audit_log)
|
|
4
|
+
[](https://www.ruby-lang.org)
|
|
5
|
+
[](https://codecov.io/gh/eclectic-coding/rails_audit_log)
|
|
6
|
+
|
|
7
|
+
A modern, Zeitwerk-native Rails engine for auditing ActiveRecord changes. Tracks `create`, `update`, and `destroy` events with JSON-first storage, whodunnit actor context, and a clean query API.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Add to your `Gemfile`:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
gem "rails_audit_log"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Run the install generator to create the migration:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
bin/rails generate rails_audit_log:install
|
|
21
|
+
bin/rails db:migrate
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Tracking a model
|
|
27
|
+
|
|
28
|
+
Include `RailsAuditLog::Auditable` in any ActiveRecord model:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
class Article < ApplicationRecord
|
|
32
|
+
include RailsAuditLog::Auditable
|
|
33
|
+
end
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Every `create`, `update`, and `destroy` is now recorded automatically:
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
article = Article.create!(title: "Hello")
|
|
40
|
+
article.audit_log_entries.count # => 1
|
|
41
|
+
article.audit_log_entries.first.event # => "create"
|
|
42
|
+
|
|
43
|
+
article.update!(title: "World")
|
|
44
|
+
article.audit_log_entries.last.object_changes
|
|
45
|
+
# => { "title" => ["Hello", "World"] }
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Recording who made the change
|
|
49
|
+
|
|
50
|
+
Include `RailsAuditLog::Controller` in your `ApplicationController` and declare the actor source once:
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
class ApplicationController < ActionController::Base
|
|
54
|
+
include RailsAuditLog::Controller
|
|
55
|
+
audit_log_actor { current_user }
|
|
56
|
+
end
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The actor is captured automatically on every request and stored on each entry:
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
entry = article.audit_log_entries.last
|
|
63
|
+
entry.actor # => #<User id: 42, name: "Alice">
|
|
64
|
+
entry.actor_type # => "User"
|
|
65
|
+
entry.actor_id # => 42
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Actor context outside of controllers
|
|
69
|
+
|
|
70
|
+
Use `RailsAuditLog.with_actor` in background jobs, rake tasks, or seeds:
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
RailsAuditLog.with_actor(current_user) do
|
|
74
|
+
article.update!(status: "published")
|
|
75
|
+
end
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Requirements
|
|
79
|
+
|
|
80
|
+
- Ruby >= 3.3
|
|
81
|
+
- Rails >= 7.2
|
|
82
|
+
|
|
83
|
+
## Contributing
|
|
84
|
+
|
|
85
|
+
Bug reports and pull requests are welcome on [GitHub](https://github.com/eclectic-coding/rails_audit_log).
|
|
86
|
+
|
|
87
|
+
## License
|
|
88
|
+
|
|
89
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require "bundler/setup"
|
|
2
|
+
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "bundler/audit/task"
|
|
5
|
+
require "rubocop/rake_task"
|
|
6
|
+
require "rspec/core/rake_task"
|
|
7
|
+
|
|
8
|
+
Bundler::Audit::Task.new
|
|
9
|
+
RuboCop::RakeTask.new
|
|
10
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
11
|
+
|
|
12
|
+
desc "Verify Zeitwerk can eager-load all engine files without errors"
|
|
13
|
+
task :zeitwerk do
|
|
14
|
+
sh({ "RAILS_ENV" => "test" }, "cd spec/dummy && bundle exec rails runner 'Rails.autoloaders.main.eager_load'")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
task default: ["bundle:audit:update", "bundle:audit:check", :rubocop, :zeitwerk, :spec]
|
|
18
|
+
|
|
19
|
+
# Development database tasks (operate on spec/dummy)
|
|
20
|
+
namespace :dev do
|
|
21
|
+
dummy_env = { "RAILS_ENV" => "development" }
|
|
22
|
+
dummy_rake = "cd spec/dummy && bundle exec rake"
|
|
23
|
+
|
|
24
|
+
desc "Create and migrate the development database"
|
|
25
|
+
task :setup do
|
|
26
|
+
sh dummy_env, "#{dummy_rake} db:create db:migrate"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
desc "Seed the development database"
|
|
30
|
+
task :seed do
|
|
31
|
+
sh dummy_env, "#{dummy_rake} db:seed"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
desc "Drop, recreate, migrate, and seed the development database"
|
|
35
|
+
task :reset do
|
|
36
|
+
sh dummy_env, "#{dummy_rake} db:drop db:create db:migrate db:seed"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
|
3
|
+
* listed below.
|
|
4
|
+
*
|
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
|
7
|
+
*
|
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
|
11
|
+
* It is generally better to create a new file per style scope.
|
|
12
|
+
*
|
|
13
|
+
*= require_tree .
|
|
14
|
+
*= require_self
|
|
15
|
+
*/
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module RailsAuditLog
|
|
2
|
+
module Auditable
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
has_many :audit_log_entries,
|
|
7
|
+
class_name: "RailsAuditLog::AuditLogEntry",
|
|
8
|
+
as: :item,
|
|
9
|
+
dependent: :destroy
|
|
10
|
+
|
|
11
|
+
after_create :record_audit_create
|
|
12
|
+
after_update :record_audit_update
|
|
13
|
+
after_destroy :record_audit_destroy
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def record_audit_create
|
|
19
|
+
record_audit_entry("create", saved_changes)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def record_audit_update
|
|
23
|
+
record_audit_entry("update", saved_changes)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def record_audit_destroy
|
|
27
|
+
changes = attributes.transform_values { |v| [v, nil] }
|
|
28
|
+
record_audit_entry("destroy", changes)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def record_audit_entry(event, changes)
|
|
32
|
+
actor = RailsAuditLog.actor
|
|
33
|
+
RailsAuditLog::AuditLogEntry.create!(
|
|
34
|
+
event: event,
|
|
35
|
+
item_type: self.class.name,
|
|
36
|
+
item_id: id,
|
|
37
|
+
object_changes: changes,
|
|
38
|
+
actor_type: actor&.class&.name,
|
|
39
|
+
actor_id: actor.respond_to?(:id) ? actor.id : nil
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module RailsAuditLog
|
|
2
|
+
module Controller
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
before_action :set_audit_log_actor
|
|
7
|
+
after_action :clear_audit_log_actor
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class_methods do
|
|
11
|
+
def audit_log_actor(&block)
|
|
12
|
+
@audit_log_actor_block = block
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def audit_log_actor_block
|
|
16
|
+
@audit_log_actor_block
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def set_audit_log_actor
|
|
23
|
+
block = self.class.audit_log_actor_block
|
|
24
|
+
RailsAuditLog.actor = instance_exec(&block) if block
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def clear_audit_log_actor
|
|
28
|
+
RailsAuditLog.actor = nil
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module RailsAuditLog
|
|
2
|
+
class AuditLogEntry < ApplicationRecord
|
|
3
|
+
self.table_name = "audit_log_entries"
|
|
4
|
+
|
|
5
|
+
EVENTS = %w[create update destroy].freeze
|
|
6
|
+
|
|
7
|
+
belongs_to :item, polymorphic: true, optional: true
|
|
8
|
+
belongs_to :actor, polymorphic: true, optional: true
|
|
9
|
+
|
|
10
|
+
validates :event, presence: true, inclusion: { in: EVENTS }
|
|
11
|
+
validates :item_type, presence: true
|
|
12
|
+
validates :item_id, presence: true
|
|
13
|
+
|
|
14
|
+
scope :creates, -> { where(event: "create") }
|
|
15
|
+
scope :updates, -> { where(event: "update") }
|
|
16
|
+
scope :destroys, -> { where(event: "destroy") }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Rails audit log</title>
|
|
5
|
+
<%= csrf_meta_tags %>
|
|
6
|
+
<%= csp_meta_tag %>
|
|
7
|
+
|
|
8
|
+
<%= yield :head %>
|
|
9
|
+
|
|
10
|
+
<%= stylesheet_link_tag "rails_audit_log/application", media: "all" %>
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
|
|
14
|
+
<%= yield %>
|
|
15
|
+
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
require "rails/generators/active_record"
|
|
3
|
+
|
|
4
|
+
module RailsAuditLog
|
|
5
|
+
module Generators
|
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
|
7
|
+
include ActiveRecord::Generators::Migration
|
|
8
|
+
|
|
9
|
+
source_root File.expand_path("templates", __dir__)
|
|
10
|
+
|
|
11
|
+
desc "Creates the audit_log_entries migration in your application."
|
|
12
|
+
|
|
13
|
+
def create_migration_file
|
|
14
|
+
migration_template(
|
|
15
|
+
"create_audit_log_entries.rb",
|
|
16
|
+
"db/migrate/create_audit_log_entries.rb"
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class CreateAuditLogEntries < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :audit_log_entries do |t|
|
|
4
|
+
t.string :event, null: false
|
|
5
|
+
t.string :item_type, null: false
|
|
6
|
+
t.bigint :item_id, null: false
|
|
7
|
+
t.json :object_changes
|
|
8
|
+
t.string :actor_type
|
|
9
|
+
t.bigint :actor_id
|
|
10
|
+
t.datetime :created_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
add_index :audit_log_entries, [:item_type, :item_id]
|
|
14
|
+
add_index :audit_log_entries, [:actor_type, :actor_id]
|
|
15
|
+
add_index :audit_log_entries, :event
|
|
16
|
+
add_index :audit_log_entries, :created_at
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require "rails_audit_log/version"
|
|
2
|
+
require "rails_audit_log/engine"
|
|
3
|
+
|
|
4
|
+
module RailsAuditLog
|
|
5
|
+
def self.actor
|
|
6
|
+
Thread.current[:rails_audit_log_actor]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.actor=(actor)
|
|
10
|
+
Thread.current[:rails_audit_log_actor] = actor
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.with_actor(actor)
|
|
14
|
+
previous = self.actor
|
|
15
|
+
self.actor = actor
|
|
16
|
+
yield
|
|
17
|
+
ensure
|
|
18
|
+
self.actor = previous
|
|
19
|
+
end
|
|
20
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rails_audit_log
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Chuck Smith
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '7.2'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '7.2'
|
|
26
|
+
description: A modern Rails engine that tracks ActiveRecord create, update, and destroy
|
|
27
|
+
events with JSON-first storage, whodunnit actor context, and a clean query API.
|
|
28
|
+
Drop-in replacement for PaperTrail with no legacy baggage.
|
|
29
|
+
email:
|
|
30
|
+
- chuck@eclecticcoding.com
|
|
31
|
+
executables: []
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- MIT-LICENSE
|
|
36
|
+
- README.md
|
|
37
|
+
- Rakefile
|
|
38
|
+
- app/assets/stylesheets/rails_audit_log/application.css
|
|
39
|
+
- app/concerns/rails_audit_log/auditable.rb
|
|
40
|
+
- app/concerns/rails_audit_log/controller.rb
|
|
41
|
+
- app/controllers/rails_audit_log/application_controller.rb
|
|
42
|
+
- app/helpers/rails_audit_log/application_helper.rb
|
|
43
|
+
- app/jobs/rails_audit_log/application_job.rb
|
|
44
|
+
- app/models/rails_audit_log/application_record.rb
|
|
45
|
+
- app/models/rails_audit_log/audit_log_entry.rb
|
|
46
|
+
- app/views/layouts/rails_audit_log/application.html.erb
|
|
47
|
+
- config/routes.rb
|
|
48
|
+
- lib/generators/rails_audit_log/install/install_generator.rb
|
|
49
|
+
- lib/generators/rails_audit_log/install/templates/create_audit_log_entries.rb
|
|
50
|
+
- lib/rails_audit_log.rb
|
|
51
|
+
- lib/rails_audit_log/engine.rb
|
|
52
|
+
- lib/rails_audit_log/version.rb
|
|
53
|
+
- lib/tasks/rails_audit_log_tasks.rake
|
|
54
|
+
homepage: https://github.com/eclectic-coding/rails_audit_log
|
|
55
|
+
licenses:
|
|
56
|
+
- MIT
|
|
57
|
+
metadata:
|
|
58
|
+
homepage_uri: https://github.com/eclectic-coding/rails_audit_log
|
|
59
|
+
source_code_uri: https://github.com/eclectic-coding/rails_audit_log
|
|
60
|
+
changelog_uri: https://github.com/eclectic-coding/rails_audit_log/blob/main/CHANGELOG.md
|
|
61
|
+
rubygems_mfa_required: 'true'
|
|
62
|
+
rdoc_options: []
|
|
63
|
+
require_paths:
|
|
64
|
+
- lib
|
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - ">="
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '3.3'
|
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0'
|
|
75
|
+
requirements: []
|
|
76
|
+
rubygems_version: 3.6.9
|
|
77
|
+
specification_version: 4
|
|
78
|
+
summary: Zeitwerk-native audit logging for ActiveRecord
|
|
79
|
+
test_files: []
|