audited 5.0.2 → 5.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +15 -2
  3. data/CHANGELOG.md +105 -1
  4. data/README.md +29 -11
  5. data/Rakefile +1 -3
  6. data/audited.gemspec +38 -0
  7. data/lib/audited/audit.rb +8 -4
  8. data/lib/audited/auditor.rb +81 -28
  9. data/lib/audited/version.rb +1 -1
  10. data/lib/audited.rb +18 -9
  11. data/lib/generators/audited/migration.rb +10 -2
  12. metadata +39 -57
  13. data/.github/workflows/ci.yml +0 -115
  14. data/.gitignore +0 -17
  15. data/.standard.yml +0 -5
  16. data/.yardopts +0 -3
  17. data/gemfiles/rails50.gemfile +0 -10
  18. data/gemfiles/rails51.gemfile +0 -10
  19. data/gemfiles/rails52.gemfile +0 -10
  20. data/gemfiles/rails60.gemfile +0 -10
  21. data/gemfiles/rails61.gemfile +0 -10
  22. data/gemfiles/rails70.gemfile +0 -10
  23. data/spec/audited/audit_spec.rb +0 -357
  24. data/spec/audited/auditor_spec.rb +0 -1097
  25. data/spec/audited/rspec_matchers_spec.rb +0 -69
  26. data/spec/audited/sweeper_spec.rb +0 -133
  27. data/spec/audited_spec.rb +0 -18
  28. data/spec/audited_spec_helpers.rb +0 -32
  29. data/spec/rails_app/app/assets/config/manifest.js +0 -2
  30. data/spec/rails_app/config/application.rb +0 -13
  31. data/spec/rails_app/config/database.yml +0 -26
  32. data/spec/rails_app/config/environment.rb +0 -5
  33. data/spec/rails_app/config/environments/test.rb +0 -47
  34. data/spec/rails_app/config/initializers/backtrace_silencers.rb +0 -7
  35. data/spec/rails_app/config/initializers/inflections.rb +0 -2
  36. data/spec/rails_app/config/initializers/secret_token.rb +0 -3
  37. data/spec/rails_app/config/routes.rb +0 -3
  38. data/spec/spec_helper.rb +0 -24
  39. data/spec/support/active_record/models.rb +0 -151
  40. data/spec/support/active_record/postgres/1_change_audited_changes_type_to_json.rb +0 -11
  41. data/spec/support/active_record/postgres/2_change_audited_changes_type_to_jsonb.rb +0 -11
  42. data/spec/support/active_record/schema.rb +0 -90
  43. data/test/db/version_1.rb +0 -17
  44. data/test/db/version_2.rb +0 -18
  45. data/test/db/version_3.rb +0 -18
  46. data/test/db/version_4.rb +0 -19
  47. data/test/db/version_5.rb +0 -17
  48. data/test/db/version_6.rb +0 -19
  49. data/test/install_generator_test.rb +0 -62
  50. data/test/test_helper.rb +0 -18
  51. data/test/upgrade_generator_test.rb +0 -97
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5bb58e8eb5b0de02a18ac5d089ccade2913c399ec66bcbf97551fd9a9f4afb6b
4
- data.tar.gz: 905621826a752e1796e0b81a7b448eec483e0ef27ce5de9f02845e0f5fd8be87
3
+ metadata.gz: 6f68b2608cf53ed7087873504b4f29c97599bf31a79d284a3675c1725c91bfba
4
+ data.tar.gz: 62a32603313de542d0e9aa261110d0be63ba50c56e3073bd828e8492681ec7cf
5
5
  SHA512:
6
- metadata.gz: 62448f486877e8a6c86505a7e5f53b805bf4f3a14cad907f83e69cd5175f43a4c3083a08ab4342bad18f5b8b1b104aa20d2d3c375ff86b7e60d48db9cbe00346
7
- data.tar.gz: 4bf37e3891074e6f329961c796db27191ead60d225860d3150f310a3888ed3ab52eab1564398ce2f26ad470ba2872979dd14ee48cddf0baaa8f7cbfb63c19091
6
+ metadata.gz: de9505c3076a24b043ba3128f526709def0c4c9dbe2a6ac56dab7163b252efa20e94c84f9481fb7c3e5673eee822e18654949736693e42e4e00285149cc639f0
7
+ data.tar.gz: 88d44336e6e2e61952a973d9b44957d68375cc18e009ac0fc410638b9748dcd4a331dcb15224f073e28077c84ee0f50c8d5e848f559925ebedca77696ca35f32
data/Appraisals CHANGED
@@ -6,6 +6,8 @@ appraise "rails50" do
6
6
  gem "mysql2", ">= 0.3.18", "< 0.6.0"
7
7
  gem "pg", ">= 0.18", "< 2.0"
8
8
  gem "sqlite3", "~> 1.3.6"
9
+ gem "psych", "~> 3.1"
10
+ gem "loofah", "2.20.0"
9
11
  end
10
12
 
11
13
  appraise "rails51" do
@@ -13,13 +15,17 @@ appraise "rails51" do
13
15
  gem "mysql2", ">= 0.3.18", "< 0.6.0"
14
16
  gem "pg", ">= 0.18", "< 2.0"
15
17
  gem "sqlite3", "~> 1.3.6"
18
+ gem "psych", "~> 3.1"
19
+ gem "loofah", "2.20.0"
16
20
  end
17
21
 
18
22
  appraise "rails52" do
19
- gem "rails", ">= 5.2.0", "< 5.3"
23
+ gem "rails", ">= 5.2.8.1", "< 5.3"
20
24
  gem "mysql2", ">= 0.4.4", "< 0.6.0"
21
25
  gem "pg", ">= 0.18", "< 2.0"
22
26
  gem "sqlite3", "~> 1.3.6"
27
+ gem "psych", "~> 3.1"
28
+ gem "loofah", "2.20.0"
23
29
  end
24
30
 
25
31
  appraise "rails60" do
@@ -37,7 +43,14 @@ appraise "rails61" do
37
43
  end
38
44
 
39
45
  appraise "rails70" do
40
- gem "rails", ">= 7.0.0.alpha2", "< 7.1"
46
+ gem "rails", ">= 7.0.0", "< 7.1"
47
+ gem "mysql2", ">= 0.4.4"
48
+ gem "pg", ">= 1.1"
49
+ gem "sqlite3", ">= 1.4"
50
+ end
51
+
52
+ appraise "rails71" do
53
+ gem "rails", ">= 7.1.0.beta1", "< 7.2"
41
54
  gem "mysql2", ">= 0.4.4"
42
55
  gem "pg", ">= 1.1"
43
56
  gem "sqlite3", ">= 1.4"
data/CHANGELOG.md CHANGED
@@ -1,5 +1,109 @@
1
1
  # Audited ChangeLog
2
2
 
3
+ ### 5.6.0 (2024-04-05)
4
+
5
+ - Removed support for Rails 5.0 and 5.1.
6
+ - Replace RequestStore with ActiveSupport::CurrentAttributes - @punkisdead
7
+ [#702](https://github.com/collectiveidea/audited/pull/702)
8
+
9
+ ### 5.5.0 (2024-04-02)
10
+
11
+ - Bad release. Same code as 5.4.1. Use 5.6.0 for updated features.
12
+
13
+ ### 5.4.3 (2024-01-11)
14
+
15
+ - Ignore readonly columns in audit - @sriddbs
16
+ [#692](https://github.com/collectiveidea/audited/pull/692)
17
+ - Robustify Rails version checks - @blaet
18
+ [#689](https://github.com/collectiveidea/audited/pull/689)
19
+ - Ignore callbacks if not specifed on the model
20
+ [#679](https://github.com/collectiveidea/audited/pull/679)
21
+
22
+ ## 5.4.2 (2023-11-30)
23
+
24
+ - Revert replacing RequetStore with ActiveSupport::CurrentAttributes until it is fully tested.
25
+
26
+ ## 5.4.1 (2023-11-30)
27
+
28
+ - Replace RequestStore with ActiveSupport::CurrentAttributes - @the-spectator
29
+ [#673](https://github.com/collectiveidea/audited/pull/673/)
30
+ - Don't require railtie when used outside of Rails - @nicduke38degrees
31
+ [#665](https://github.com/collectiveidea/audited/pull/665)
32
+
33
+ ## 5.4.0 (2023-09-30)
34
+
35
+ - Add Rails 7.1 support - @yuki24
36
+ [#686](https://github.com/collectiveidea/audited/pull/686)
37
+
38
+ ## 5.3.3 (2023-03-24)
39
+
40
+ - Use RequestStore instead of Thread.current for thread-safe requests - @tiagocassio
41
+ [#669](https://github.com/collectiveidea/audited/pull/669)
42
+ - Clean up Touch audits - @mcyoung, @akostadinov
43
+ [#668](https://github.com/collectiveidea/audited/pull/668)
44
+
45
+ ## 5.3.2 (2023-02-22)
46
+
47
+ - Touch audit bug fixes - @mcyoung
48
+ [#662](https://github.com/collectiveidea/audited/pull/662)
49
+
50
+ ## 5.3.1 (2023-02-21)
51
+
52
+ - Ensure touch support doesn't cause double audits - @mcyoung
53
+ [#660](https://github.com/collectiveidea/audited/pull/660)
54
+ - Testing Improvements - @vlad-psh
55
+ [#628](https://github.com/collectiveidea/audited/pull/628)
56
+ - Testing Improvements - @mcyoung
57
+ [#658](https://github.com/collectiveidea/audited/pull/658)
58
+
59
+ ## 5.3.0 (2023-02-14)
60
+
61
+ - Audit touch calls - @mcyoung
62
+ [#657](https://github.com/collectiveidea/audited/pull/657)
63
+ - Allow using with Padrino and other non-Rails projects - @nicduke38degrees
64
+ [#655](https://github.com/collectiveidea/audited/pull/655)
65
+ - Testing updates - @jdufresne
66
+ [#652](https://github.com/collectiveidea/audited/pull/652)
67
+ [#653](https://github.com/collectiveidea/audited/pull/653)
68
+
69
+ ## 5.2.0 (2023-01-23)
70
+
71
+ Improved
72
+
73
+ - config.audit_class can take a string or constant - @rocket-turtle
74
+ Fixes overzealous change in 5.1.0 where it only took a string.
75
+ [#648](https://github.com/collectiveidea/audited/pull/648)
76
+ - README link fix - @jeremiahlukus
77
+ [#646](https://github.com/collectiveidea/audited/pull/646)
78
+ - Typo fix in GitHub Actions - @jdufresne
79
+ [#644](https://github.com/collectiveidea/audited/pull/644)
80
+
81
+ ## 5.1.0 (2022-12-23)
82
+
83
+ Changed
84
+
85
+ - config.audit_class takes a string - @simmerz
86
+ [#609](https://github.com/collectiveidea/audited/pull/609)
87
+ - Filter encrypted attributes automatically - @vlad-psh
88
+ [#630](https://github.com/collectiveidea/audited/pull/630)
89
+
90
+ Improved
91
+
92
+ - README improvements - @jess, @mstroming
93
+ [#605](https://github.com/collectiveidea/audited/pull/605)
94
+ [#640](https://github.com/collectiveidea/audited/issues/640)
95
+ - Ignore deadlocks in concurrent audit combinations - @Crammaman
96
+ [#621](https://github.com/collectiveidea/audited/pull/621)
97
+ - Fix timestamped_migrations deprecation warning - @shouichi
98
+ [#624](https://github.com/collectiveidea/audited/pull/624)
99
+ - Ensure audits are re-enabled after blocks - @dcorlett
100
+ [#632](https://github.com/collectiveidea/audited/pull/632)
101
+ - Replace raw string where clause with query methods - @macowie
102
+ [#642](https://github.com/collectiveidea/audited/pull/642)
103
+ - Test against more Ruby/Rails Versions - @enomotodev, @danielmorrison
104
+ [#610](https://github.com/collectiveidea/audited/pull/610)
105
+ [#643](https://github.com/collectiveidea/audited/pull/643)
106
+
3
107
  ## 5.0.2 (2021-09-16)
4
108
 
5
109
  Added
@@ -11,7 +115,7 @@ Improved
11
115
 
12
116
  - Improve loading - @mvastola
13
117
  [#592](https://github.com/collectiveidea/audited/pull/592)
14
- - Update README - @danirod, clement1234
118
+ - Update README - @danirod, @clement1234
15
119
  [#596](https://github.com/collectiveidea/audited/pull/596)
16
120
  [#594](https://github.com/collectiveidea/audited/pull/594)
17
121
 
data/README.md CHANGED
@@ -2,15 +2,15 @@ Audited
2
2
  [![Gem Version](https://img.shields.io/gem/v/audited.svg)](http://rubygems.org/gems/audited)
3
3
  ![Build Status](https://github.com/collectiveidea/audited/actions/workflows/ci.yml/badge.svg)
4
4
  [![Code Climate](https://codeclimate.com/github/collectiveidea/audited.svg)](https://codeclimate.com/github/collectiveidea/audited)
5
- [![Security](https://hakiri.io/github/collectiveidea/audited/master.svg)](https://hakiri.io/github/collectiveidea/audited/master)
6
5
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
7
6
  =======
8
7
 
9
8
  **Audited** (previously acts_as_audited) is an ORM extension that logs all changes to your models. Audited can also record who made those changes, save comments and associate models related to the changes.
10
9
 
11
10
 
12
- Audited currently (5.x) works with Rails 7.0, 6.1, 6.0, 5.2, 5.1, and 5.0.
11
+ Audited currently (5.6) works with Rails 7.1, 7.0, 6.1, 6.0, 5.2.
13
12
 
13
+ For Rails 5.0 & 5.1, use gem version 5.4.3
14
14
  For Rails 4, use gem version 4.x
15
15
  For Rails 3, use gem version 3.0 or see the [3.0-stable branch](https://github.com/collectiveidea/audited/tree/3.0-stable).
16
16
 
@@ -18,12 +18,14 @@ For Rails 3, use gem version 3.0 or see the [3.0-stable branch](https://github.c
18
18
 
19
19
  Audited supports and is [tested against](https://github.com/collectiveidea/audited/actions/workflows/ci.yml) the following Ruby versions:
20
20
 
21
- * 2.3
21
+ * 2.3 (only tested on Sqlite due to testing issues with other DBs)
22
22
  * 2.4
23
23
  * 2.5
24
24
  * 2.6
25
25
  * 2.7
26
26
  * 3.0
27
+ * 3.1
28
+ * 3.2
27
29
 
28
30
  Audited may work just fine with a Ruby version not listed above, but we can't guarantee that it will. If you'd like to maintain a Ruby that isn't listed, please let us know with a [pull request](https://github.com/collectiveidea/audited/pulls).
29
31
 
@@ -36,7 +38,7 @@ Audited is currently ActiveRecord-only. In a previous life, Audited worked with
36
38
  Add the gem to your Gemfile:
37
39
 
38
40
  ```ruby
39
- gem "audited", "~> 5.0"
41
+ gem "audited"
40
42
  ```
41
43
 
42
44
  And if you're using ```require: false``` you must add initializers like this:
@@ -132,18 +134,24 @@ end
132
134
 
133
135
  ### Specifying callbacks
134
136
 
135
- By default, a new audit is created for any Create, Update or Destroy action. You can, however, limit the actions audited.
137
+ By default, a new audit is created for any Create, Update, Touch (Rails 6+) or Destroy action. You can, however, limit the actions audited.
136
138
 
137
139
  ```ruby
138
140
  class User < ActiveRecord::Base
139
141
  # All fields and actions
140
142
  # audited
141
143
 
142
- # Single field, only audit Update and Destroy (not Create)
144
+ # Single field, only audit Update and Destroy (not Create or Touch)
143
145
  # audited only: :name, on: [:update, :destroy]
144
146
  end
145
147
  ```
146
148
 
149
+ You can ignore the default callbacks globally unless the callback action is specified in your model using the `:on` option. To configure default callback exclusion, put the following in an initializer file (`config/initializers/audited.rb`):
150
+
151
+ ```ruby
152
+ Audited.ignored_default_callbacks = [:create, :update] # ignore callbacks create and update
153
+ ```
154
+
147
155
  ### Comments
148
156
 
149
157
  You can attach comments to each audit using an `audit_comment` attribute on your model.
@@ -237,7 +245,7 @@ class ApplicationController < ActionController::Base
237
245
  if current_user
238
246
  current_user
239
247
  else
240
- 'Elon Musk'
248
+ 'Alexander Fleming'
241
249
  end
242
250
  end
243
251
  end
@@ -286,6 +294,7 @@ class User < ActiveRecord::Base
286
294
  end
287
295
 
288
296
  class Company < ActiveRecord::Base
297
+ audited
289
298
  has_many :users
290
299
  has_associated_audits
291
300
  end
@@ -314,8 +323,6 @@ If you want to audit only under specific conditions, you can provide conditional
314
323
  class User < ActiveRecord::Base
315
324
  audited if: :active?
316
325
 
317
- private
318
-
319
326
  def active?
320
327
  last_login > 6.months.ago
321
328
  end
@@ -386,6 +393,17 @@ User.auditing_enabled = false
386
393
  end
387
394
  ```
388
395
 
396
+ ### Encrypted attributes
397
+
398
+ If you're using ActiveRecord's encryption (available from Rails 7) to encrypt some attributes, Audited will automatically filter values of these attributes. No additional configuration is required. Changes to encrypted attributes will be logged as `[FILTERED]`.
399
+
400
+ ```ruby
401
+ class User < ActiveRecord::Base
402
+ audited
403
+ encrypts :password
404
+ end
405
+ ```
406
+
389
407
  ### Custom `Audit` model
390
408
 
391
409
  If you want to extend or modify the audit model, create a new class that
@@ -402,7 +420,7 @@ Then set it in an initializer:
402
420
  # config/initializers/audited.rb
403
421
 
404
422
  Audited.config do |config|
405
- config.audit_class = CustomAudit
423
+ config.audit_class = "CustomAudit"
406
424
  end
407
425
  ```
408
426
 
@@ -418,7 +436,7 @@ Audited.store_synthesized_enums = true
418
436
 
419
437
  ## Support
420
438
 
421
- You can find documentation at: http://rdoc.info/github/collectiveidea/audited
439
+ You can find documentation at: https://www.rubydoc.info/gems/audited
422
440
 
423
441
  Or join the [mailing list](http://groups.google.com/group/audited) to get help or offer suggestions.
424
442
 
data/Rakefile CHANGED
@@ -1,12 +1,10 @@
1
1
  #!/usr/bin/env rake
2
2
 
3
- require "bundler/gem_helper"
3
+ require "bundler/gem_tasks"
4
4
  require "rspec/core/rake_task"
5
5
  require "rake/testtask"
6
6
  require "appraisal"
7
7
 
8
- Bundler::GemHelper.install_tasks(name: "audited")
9
-
10
8
  RSpec::Core::RakeTask.new(:spec)
11
9
 
12
10
  Rake::TestTask.new do |t|
data/audited.gemspec ADDED
@@ -0,0 +1,38 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "audited/version"
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "audited"
6
+ gem.version = Audited::VERSION
7
+
8
+ gem.authors = ["Brandon Keepers", "Kenneth Kalmer", "Daniel Morrison", "Brian Ryckbost", "Steve Richert", "Ryan Glover"]
9
+ gem.email = "info@collectiveidea.com"
10
+ gem.description = "Log all changes to your models"
11
+ gem.summary = gem.description
12
+ gem.homepage = "https://github.com/collectiveidea/audited"
13
+ gem.license = "MIT"
14
+
15
+ gem.files = `git ls-files`.split($\).reject { |f| f =~ /^(\.gemspec|\.git|\.standard|\.yard|gemfiles|test|spec)/ }
16
+
17
+ gem.required_ruby_version = ">= 2.3.0"
18
+
19
+ gem.add_dependency "activerecord", ">= 5.2", "< 7.2"
20
+ gem.add_dependency "activesupport", ">= 5.2", "< 7.2"
21
+
22
+ gem.add_development_dependency "appraisal"
23
+ gem.add_development_dependency "rails", ">= 5.2", "< 7.2"
24
+ gem.add_development_dependency "rspec-rails"
25
+ gem.add_development_dependency "standard"
26
+ gem.add_development_dependency "single_cov"
27
+
28
+ # JRuby support for the test ENV
29
+ if defined?(JRUBY_VERSION)
30
+ gem.add_development_dependency "activerecord-jdbcsqlite3-adapter", "~> 1.3"
31
+ gem.add_development_dependency "activerecord-jdbcpostgresql-adapter", "~> 1.3"
32
+ gem.add_development_dependency "activerecord-jdbcmysql-adapter", "~> 1.3"
33
+ else
34
+ gem.add_development_dependency "sqlite3", ">= 1.3.6"
35
+ gem.add_development_dependency "mysql2", ">= 0.3.20"
36
+ gem.add_development_dependency "pg", ">= 0.18", "< 2.0"
37
+ end
38
+ end
data/lib/audited/audit.rb CHANGED
@@ -49,7 +49,11 @@ module Audited
49
49
  cattr_accessor :audited_class_names
50
50
  self.audited_class_names = Set.new
51
51
 
52
- serialize :audited_changes, YAMLIfTextColumnType
52
+ if Rails.gem_version >= Gem::Version.new("7.1")
53
+ serialize :audited_changes, coder: YAMLIfTextColumnType
54
+ else
55
+ serialize :audited_changes, YAMLIfTextColumnType
56
+ end
53
57
 
54
58
  scope :ascending, -> { reorder(version: :asc) }
55
59
  scope :descending, -> { reorder(version: :desc) }
@@ -78,14 +82,14 @@ module Audited
78
82
  # Returns a hash of the changed attributes with the new values
79
83
  def new_attributes
80
84
  (audited_changes || {}).each_with_object({}.with_indifferent_access) do |(attr, values), attrs|
81
- attrs[attr] = (action == "update" ? values.last : values)
85
+ attrs[attr] = (action == "update") ? values.last : values
82
86
  end
83
87
  end
84
88
 
85
89
  # Returns a hash of the changed attributes with the old values
86
90
  def old_attributes
87
91
  (audited_changes || {}).each_with_object({}.with_indifferent_access) do |(attr, values), attrs|
88
- attrs[attr] = (action == "update" ? values.first : values)
92
+ attrs[attr] = (action == "update") ? values.first : values
89
93
  end
90
94
  end
91
95
 
@@ -174,7 +178,7 @@ module Audited
174
178
  if action == "create"
175
179
  self.version = 1
176
180
  else
177
- collection = Rails::VERSION::MAJOR >= 6 ? self.class.unscoped : self.class
181
+ collection = (ActiveRecord::VERSION::MAJOR >= 6) ? self.class.unscoped : self.class
178
182
  max = collection.auditable_finder(auditable_id, auditable_type).maximum(:version) || 0
179
183
  self.version = max + 1
180
184
  end
@@ -13,7 +13,7 @@ module Audited
13
13
  #
14
14
  # See <tt>Audited::Auditor::ClassMethods#audited</tt>
15
15
  # for configuration options
16
- module Auditor #:nodoc:
16
+ module Auditor # :nodoc:
17
17
  extend ActiveSupport::Concern
18
18
 
19
19
  CALLBACKS = [:audit_create, :audit_update, :audit_destroy]
@@ -84,6 +84,7 @@ module Audited
84
84
 
85
85
  after_create :audit_create if audited_options[:on].include?(:create)
86
86
  before_update :audit_update if audited_options[:on].include?(:update)
87
+ after_touch :audit_touch if audited_options[:on].include?(:touch) && ::ActiveRecord::VERSION::MAJOR >= 6
87
88
  before_destroy :audit_destroy if audited_options[:on].include?(:destroy)
88
89
 
89
90
  # Define and set after_audit and around_audit callbacks. This might be useful if you want
@@ -172,14 +173,15 @@ module Audited
172
173
  # List of attributes that are audited.
173
174
  def audited_attributes
174
175
  audited_attributes = attributes.except(*self.class.non_audited_columns)
176
+ audited_attributes = redact_values(audited_attributes)
177
+ audited_attributes = filter_encrypted_attrs(audited_attributes)
175
178
  normalize_enum_changes(audited_attributes)
176
179
  end
177
180
 
178
181
  # Returns a list combined of record audits and associated audits.
179
182
  def own_and_associated_audits
180
- Audited.audit_class.unscoped
181
- .where("(auditable_type = :type AND auditable_id = :id) OR (associated_type = :type AND associated_id = :id)",
182
- type: self.class.base_class.name, id: id)
183
+ Audited.audit_class.unscoped.where(auditable: self)
184
+ .or(Audited.audit_class.unscoped.where(associated: self))
183
185
  .order(created_at: :desc)
184
186
  end
185
187
 
@@ -190,8 +192,13 @@ module Audited
190
192
  combine_target.comment = "#{combine_target.comment}\nThis audit is the result of multiple audits being combined."
191
193
 
192
194
  transaction do
193
- combine_target.save!
194
- audits_to_combine.unscope(:limit).where("version < ?", combine_target.version).delete_all
195
+ begin
196
+ combine_target.save!
197
+ audits_to_combine.unscope(:limit).where("version < ?", combine_target.version).delete_all
198
+ rescue ActiveRecord::Deadlocked
199
+ # Ignore Deadlocks, if the same record is getting its old audits combined more than once at the same time then
200
+ # both combining operations will be the same. Ignoring this error allows one of the combines to go through successfully.
201
+ end
195
202
  end
196
203
  end
197
204
 
@@ -224,8 +231,17 @@ module Audited
224
231
 
225
232
  private
226
233
 
227
- def audited_changes
228
- all_changes = respond_to?(:changes_to_save) ? changes_to_save : changes
234
+ def audited_changes(for_touch: false, exclude_readonly_attrs: false)
235
+ all_changes = if for_touch
236
+ previous_changes
237
+ elsif respond_to?(:changes_to_save)
238
+ changes_to_save
239
+ else
240
+ changes
241
+ end
242
+
243
+ all_changes = all_changes.except(*self.class.readonly_attributes.to_a) if exclude_readonly_attrs
244
+
229
245
  filtered_changes = \
230
246
  if audited_options[:only].present?
231
247
  all_changes.slice(*self.class.audited_columns)
@@ -233,8 +249,17 @@ module Audited
233
249
  all_changes.except(*self.class.non_audited_columns)
234
250
  end
235
251
 
236
- filtered_changes = redact_values(filtered_changes)
237
252
  filtered_changes = normalize_enum_changes(filtered_changes)
253
+
254
+ if for_touch && (last_audit = audits.last&.audited_changes)
255
+ filtered_changes.reject! do |k, v|
256
+ last_audit[k].to_json == v.to_json ||
257
+ last_audit[k].to_json == v[1].to_json
258
+ end
259
+ end
260
+
261
+ filtered_changes = redact_values(filtered_changes)
262
+ filtered_changes = filter_encrypted_attrs(filtered_changes)
238
263
  filtered_changes.to_hash
239
264
  end
240
265
 
@@ -257,19 +282,36 @@ module Audited
257
282
  end
258
283
 
259
284
  def redact_values(filtered_changes)
260
- [audited_options[:redacted]].flatten.compact.each do |option|
261
- changes = filtered_changes[option.to_s]
262
- new_value = audited_options[:redaction_value] || REDACTED
263
- values = if changes.is_a? Array
264
- changes.map { new_value }
265
- else
266
- new_value
267
- end
268
- hash = {option.to_s => values}
269
- filtered_changes.merge!(hash)
285
+ filter_attr_values(
286
+ audited_changes: filtered_changes,
287
+ attrs: Array(audited_options[:redacted]).map(&:to_s),
288
+ placeholder: audited_options[:redaction_value] || REDACTED
289
+ )
290
+ end
291
+
292
+ def filter_encrypted_attrs(filtered_changes)
293
+ filter_attr_values(
294
+ audited_changes: filtered_changes,
295
+ attrs: respond_to?(:encrypted_attributes) ? Array(encrypted_attributes).map(&:to_s) : []
296
+ )
297
+ end
298
+
299
+ # Replace values for given attrs to a placeholder and return modified hash
300
+ #
301
+ # @param audited_changes [Hash] Hash of changes to be saved to audited version record
302
+ # @param attrs [Array<String>] Array of attrs, values of which will be replaced to placeholder value
303
+ # @param placeholder [String] Placeholder to replace original attr values
304
+ def filter_attr_values(audited_changes: {}, attrs: [], placeholder: "[FILTERED]")
305
+ attrs.each do |attr|
306
+ next unless audited_changes.key?(attr)
307
+
308
+ changes = audited_changes[attr]
309
+ values = changes.is_a?(Array) ? changes.map { placeholder } : placeholder
310
+
311
+ audited_changes[attr] = values
270
312
  end
271
313
 
272
- filtered_changes
314
+ audited_changes
273
315
  end
274
316
 
275
317
  def rails_below?(rails_version)
@@ -290,20 +332,27 @@ module Audited
290
332
 
291
333
  def audit_create
292
334
  write_audit(action: "create", audited_changes: audited_attributes,
293
- comment: audit_comment)
335
+ comment: audit_comment)
294
336
  end
295
337
 
296
338
  def audit_update
297
- unless (changes = audited_changes).empty? && (audit_comment.blank? || audited_options[:update_with_comment_only] == false)
339
+ unless (changes = audited_changes(exclude_readonly_attrs: true)).empty? && (audit_comment.blank? || audited_options[:update_with_comment_only] == false)
298
340
  write_audit(action: "update", audited_changes: changes,
299
- comment: audit_comment)
341
+ comment: audit_comment)
342
+ end
343
+ end
344
+
345
+ def audit_touch
346
+ unless (changes = audited_changes(for_touch: true, exclude_readonly_attrs: true)).empty?
347
+ write_audit(action: "update", audited_changes: changes,
348
+ comment: audit_comment)
300
349
  end
301
350
  end
302
351
 
303
352
  def audit_destroy
304
353
  unless new_record?
305
354
  write_audit(action: "destroy", audited_changes: audited_attributes,
306
- comment: audit_comment)
355
+ comment: audit_comment)
307
356
  end
308
357
  end
309
358
 
@@ -397,7 +446,7 @@ module Audited
397
446
  # end
398
447
  #
399
448
  def without_auditing
400
- auditing_was_enabled = auditing_enabled
449
+ auditing_was_enabled = class_auditing_enabled
401
450
  disable_auditing
402
451
  yield
403
452
  ensure
@@ -411,7 +460,7 @@ module Audited
411
460
  # end
412
461
  #
413
462
  def with_auditing
414
- auditing_was_enabled = auditing_enabled
463
+ auditing_was_enabled = class_auditing_enabled
415
464
  enable_auditing
416
465
  yield
417
466
  ensure
@@ -435,7 +484,7 @@ module Audited
435
484
  end
436
485
 
437
486
  def auditing_enabled
438
- Audited.store.fetch("#{table_name}_auditing_enabled", true) && Audited.auditing_enabled
487
+ class_auditing_enabled && Audited.auditing_enabled
439
488
  end
440
489
 
441
490
  def auditing_enabled=(val)
@@ -450,7 +499,7 @@ module Audited
450
499
 
451
500
  def normalize_audited_options
452
501
  audited_options[:on] = Array.wrap(audited_options[:on])
453
- audited_options[:on] = [:create, :update, :destroy] if audited_options[:on].empty?
502
+ audited_options[:on] = ([:create, :update, :touch, :destroy] - Audited.ignored_default_callbacks) if audited_options[:on].empty?
454
503
  audited_options[:only] = Array.wrap(audited_options[:only]).map(&:to_s)
455
504
  audited_options[:except] = Array.wrap(audited_options[:except]).map(&:to_s)
456
505
  max_audits = audited_options[:max_audits] || Audited.max_audits
@@ -466,6 +515,10 @@ module Audited
466
515
  default_ignored_attributes
467
516
  end
468
517
  end
518
+
519
+ def class_auditing_enabled
520
+ Audited.store.fetch("#{table_name}_auditing_enabled", true)
521
+ end
469
522
  end
470
523
  end
471
524
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Audited
4
- VERSION = "5.0.2"
4
+ VERSION = "5.6.0"
5
5
  end
data/lib/audited.rb CHANGED
@@ -3,27 +3,35 @@
3
3
  require "active_record"
4
4
 
5
5
  module Audited
6
+ # Wrapper around ActiveSupport::CurrentAttributes
7
+ class RequestStore < ActiveSupport::CurrentAttributes
8
+ attribute :audited_store
9
+ end
10
+
6
11
  class << self
7
12
  attr_accessor \
8
13
  :auditing_enabled,
9
14
  :current_user_method,
10
15
  :ignored_attributes,
16
+ :ignored_default_callbacks,
11
17
  :max_audits,
12
18
  :store_synthesized_enums
13
19
  attr_writer :audit_class
14
20
 
15
21
  def audit_class
16
- @audit_class ||= Audit
22
+ # The audit_class is set as String in the initializer. It can not be constantized during initialization and must
23
+ # be constantized at runtime. See https://github.com/collectiveidea/audited/issues/608
24
+ @audit_class = @audit_class.safe_constantize if @audit_class.is_a?(String)
25
+ @audit_class ||= Audited::Audit
17
26
  end
18
27
 
19
- def store
20
- current_store_value = Thread.current.thread_variable_get(:audited_store)
28
+ # remove audit_model in next major version it was only shortly present in 5.1.0
29
+ alias_method :audit_model, :audit_class
30
+ deprecate audit_model: "use Audited.audit_class instead of Audited.audit_model. This method will be removed.",
31
+ deprecator: ActiveSupport::Deprecation.new('6.0.0', 'Audited')
21
32
 
22
- if current_store_value.nil?
23
- Thread.current.thread_variable_set(:audited_store, {})
24
- else
25
- current_store_value
26
- end
33
+ def store
34
+ RequestStore.audited_store ||= {}
27
35
  end
28
36
 
29
37
  def config
@@ -32,6 +40,7 @@ module Audited
32
40
  end
33
41
 
34
42
  @ignored_attributes = %w[lock_version created_at updated_at created_on updated_on]
43
+ @ignored_default_callbacks = []
35
44
 
36
45
  @current_user_method = :current_user
37
46
  @auditing_enabled = true
@@ -46,4 +55,4 @@ ActiveSupport.on_load :active_record do
46
55
  end
47
56
 
48
57
  require "audited/sweeper"
49
- require "audited/railtie"
58
+ require "audited/railtie" if Audited.const_defined?(:Rails)