Skip to content

Commit 7d9b0d6

Browse files
Define audit_class per model
1 parent 39d29e2 commit 7d9b0d6

File tree

5 files changed

+49
-28
lines changed

5 files changed

+49
-28
lines changed

lib/audited/audit.rb

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,39 @@ module Audited
1616
#
1717

1818
class YAMLIfTextColumnType
19-
class << self
20-
def load(obj)
21-
if text_column?
22-
ActiveRecord::Coders::YAMLColumn.new(Object).load(obj)
23-
else
24-
obj
25-
end
26-
end
19+
def initialize(model)
20+
@model = model
21+
end
2722

28-
def dump(obj)
29-
if text_column?
30-
ActiveRecord::Coders::YAMLColumn.new(Object).dump(obj)
31-
else
32-
obj
33-
end
23+
def load(obj)
24+
if text_column?
25+
ActiveRecord::Coders::YAMLColumn.new(Object).load(obj)
26+
else
27+
obj
3428
end
29+
end
3530

36-
def text_column?
37-
Audited.audit_class.columns_hash["audited_changes"].type.to_s == "text"
31+
def dump(obj)
32+
if text_column?
33+
ActiveRecord::Coders::YAMLColumn.new(Object).dump(obj)
34+
else
35+
obj
3836
end
3937
end
38+
39+
private
40+
41+
def text_column?
42+
@model.columns_hash["audited_changes"].type.to_s == "text"
43+
end
4044
end
4145

4246
class Audit < ::ActiveRecord::Base
47+
def self.inherited(klass)
48+
super
49+
klass.serialize :audited_changes, YAMLIfTextColumnType.new(klass)
50+
end
51+
4352
belongs_to :auditable, polymorphic: true
4453
belongs_to :user, polymorphic: true
4554
belongs_to :associated, polymorphic: true
@@ -49,7 +58,7 @@ class Audit < ::ActiveRecord::Base
4958
cattr_accessor :audited_class_names
5059
self.audited_class_names = Set.new
5160

52-
serialize :audited_changes, YAMLIfTextColumnType
61+
serialize :audited_changes, YAMLIfTextColumnType.new(self)
5362

5463
scope :ascending, -> { reorder(version: :asc) }
5564
scope :descending, -> { reorder(version: :desc) }

lib/audited/auditor.rb

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ module Auditor #:nodoc:
1919
CALLBACKS = [:audit_create, :audit_update, :audit_destroy]
2020

2121
module ClassMethods
22+
def audit_class
23+
audit_class_name&.safe_constantize || Audited.audit_class
24+
end
25+
2226
# == Configuration options
2327
#
2428
#
@@ -67,20 +71,23 @@ def audited(options = {})
6771

6872
class_attribute :audit_associated_with, instance_writer: false
6973
class_attribute :audited_options, instance_writer: false
74+
class_attribute :audit_class_name, instance_writer: false
75+
7076
attr_accessor :audit_version, :audit_comment
7177

7278
self.audited_options = options
7379
normalize_audited_options
7480

7581
self.audit_associated_with = audited_options[:associated_with]
82+
self.audit_class_name = audited_options[:audit_class_name]
7683

7784
if audited_options[:comment_required]
7885
validate :presence_of_audit_comment
7986
before_destroy :require_comment if audited_options[:on].include?(:destroy)
8087
end
8188

82-
has_many :audits, -> { order(version: :asc) }, as: :auditable, class_name: Audited.audit_class.name, inverse_of: :auditable
83-
Audited.audit_class.audited_class_names << to_s
89+
has_many :audits, -> { order(version: :asc) }, as: :auditable, class_name: audit_class.name, inverse_of: :auditable
90+
audit_class.audited_class_names << to_s
8491

8592
after_create :audit_create if audited_options[:on].include?(:create)
8693
before_update :audit_update if audited_options[:on].include?(:update)
@@ -96,14 +103,18 @@ def audited(options = {})
96103
enable_auditing
97104
end
98105

99-
def has_associated_audits
100-
has_many :associated_audits, as: :associated, class_name: Audited.audit_class.name
106+
def has_associated_audits(audit_class_name: Audited.audit_class.name)
107+
has_many :associated_audits, as: :associated, class_name: audit_class_name
101108
end
102109
end
103110

104111
module AuditedInstanceMethods
105112
REDACTED = "[REDACTED]"
106113

114+
def audit_class
115+
self.class.audit_class
116+
end
117+
107118
# Temporarily turns off auditing while saving.
108119
def save_without_auditing
109120
without_auditing { save }
@@ -159,14 +170,14 @@ def revisions(from_version = 1)
159170
# Returns nil for versions greater than revisions count
160171
def revision(version)
161172
if version == :previous || audits.last.version >= version
162-
revision_with Audited.audit_class.reconstruct_attributes(audits_to(version))
173+
revision_with audit_class.reconstruct_attributes(audits_to(version))
163174
end
164175
end
165176

166177
# Find the oldest revision recorded prior to the date/time provided.
167178
def revision_at(date_or_time)
168179
audits = self.audits.up_until(date_or_time)
169-
revision_with Audited.audit_class.reconstruct_attributes(audits) unless audits.empty?
180+
revision_with audit_class.reconstruct_attributes(audits) unless audits.empty?
170181
end
171182

172183
# List of attributes that are audited.
@@ -177,7 +188,7 @@ def audited_attributes
177188

178189
# Returns a list combined of record audits and associated audits.
179190
def own_and_associated_audits
180-
Audited.audit_class.unscoped
191+
audit_class.unscoped
181192
.where("(auditable_type = :type AND auditable_id = :id) OR (associated_type = :type AND associated_id = :id)",
182193
type: self.class.base_class.name, id: id)
183194
.order(created_at: :desc)
@@ -206,7 +217,7 @@ def revision_with(attributes)
206217
revision.send :instance_variable_set, "@destroyed", false
207218
revision.send :instance_variable_set, "@_destroyed", false
208219
revision.send :instance_variable_set, "@marked_for_destruction", false
209-
Audited.audit_class.assign_revision_attributes(revision, attributes)
220+
audit_class.assign_revision_attributes(revision, attributes)
210221

211222
# Remove any association proxies so that they will be recreated
212223
# and reference the correct object for this revision. The only way
@@ -431,7 +442,7 @@ def enable_auditing
431442
# convenience wrapper around
432443
# @see Audit#as_user.
433444
def audit_as(user, &block)
434-
Audited.audit_class.as_user(user, &block)
445+
audit_class.as_user(user, &block)
435446
end
436447

437448
def auditing_enabled

lib/audited/rspec_matchers.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ def reflection
221221
def association_exists?
222222
!reflection.nil? &&
223223
reflection.macro == :has_many &&
224-
reflection.options[:class_name] == Audited.audit_class.name
224+
reflection.options[:class_name] == model_class.audit_class.name
225225
end
226226
end
227227
end

spec/audited/audit_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class Models::ActiveRecord::CustomUserSubclass < Models::ActiveRecord::CustomUse
7070
end
7171

7272
it "does not unserialize from binary columns" do
73-
allow(Audited::YAMLIfTextColumnType).to receive(:text_column?).and_return(false)
73+
allow_any_instance_of(Audited::YAMLIfTextColumnType).to receive(:text_column?).and_return(false)
7474
audit.audited_changes = {foo: "bar"}
7575
expect(audit.audited_changes).to eq "{:foo=>\"bar\"}"
7676
end

spec/spec_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
Dir[SPEC_ROOT.join("support/*.rb")].sort.each { |f| require f }
1919

20+
ActiveRecord.use_yaml_unsafe_load = true
2021
RSpec.configure do |config|
2122
config.include AuditedSpecHelpers
2223
config.use_transactional_fixtures = false if Rails.version.start_with?("4.")

0 commit comments

Comments
 (0)