From 12918c63fe80becceaa981297146fe82aab8ba9d Mon Sep 17 00:00:00 2001 From: Usman Anwar Date: Sun, 25 Jan 2026 23:41:19 +0500 Subject: [PATCH 1/2] feat: added support for mutliple audit classes(db tables) --- lib/audited.rb | 9 ++++++++- lib/audited/auditor.rb | 18 +++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/audited.rb b/lib/audited.rb index 096959d7..acfa8c61 100644 --- a/lib/audited.rb +++ b/lib/audited.rb @@ -18,13 +18,20 @@ class << self :store_synthesized_enums attr_writer :audit_class - def audit_class + def default_audit_class # The audit_class is set as String in the initializer. It can not be constantized during initialization and must # be constantized at runtime. See https://github.com/collectiveidea/audited/issues/608 @audit_class = @audit_class.safe_constantize if @audit_class.is_a?(String) @audit_class ||= Audited::Audit end + def audit_class(custom_class_name = nil) + if custom_class_name.is_a?(String) + return custom_class_name.safe_constantize || default_audit_class + end + default_audit_class + end + # remove audit_model in next major version it was only shortly present in 5.1.0 alias_method :audit_model, :audit_class deprecate audit_model: "use Audited.audit_class instead of Audited.audit_model. This method will be removed.", diff --git a/lib/audited/auditor.rb b/lib/audited/auditor.rb index 6f197075..98526edc 100644 --- a/lib/audited/auditor.rb +++ b/lib/audited/auditor.rb @@ -83,8 +83,8 @@ def set_audit(options) before_destroy :require_comment if audited_options[:on].include?(:destroy) end - has_many :audits, -> { order(version: :asc) }, as: :auditable, class_name: Audited.audit_class.name, inverse_of: :auditable - Audited.audit_class.audited_class_names << to_s + has_many :audits, -> { order(version: :asc) }, as: :auditable, class_name: Audited.audit_class(audited_options[:class_name]).name, inverse_of: :auditable + Audited.audit_class(audited_options[:class_name]).audited_class_names << to_s after_create :audit_create if audited_options[:on].include?(:create) before_update :audit_update if audited_options[:on].include?(:update) @@ -102,7 +102,7 @@ def set_audit(options) end def has_associated_audits - has_many :associated_audits, as: :associated, class_name: Audited.audit_class.name + has_many :associated_audits, as: :associated, class_name: Audited.audit_class(audited_options[:class_name]).name end def update_audited_options(new_options) @@ -176,14 +176,14 @@ def revisions(from_version = 1) # Returns nil for versions greater than revisions count def revision(version) if version == :previous || audits.last.version >= version - revision_with Audited.audit_class.reconstruct_attributes(audits_to(version)) + revision_with Audited.audit_class(audited_options[:class_name]).reconstruct_attributes(audits_to(version)) end end # Find the oldest revision recorded prior to the date/time provided. def revision_at(date_or_time) audits = self.audits.up_until(date_or_time) - revision_with Audited.audit_class.reconstruct_attributes(audits) unless audits.empty? + revision_with Audited.audit_class(audited_options[:class_name]).reconstruct_attributes(audits) unless audits.empty? end # List of attributes that are audited. @@ -196,8 +196,8 @@ def audited_attributes # Returns a list combined of record audits and associated audits. def own_and_associated_audits - Audited.audit_class.unscoped.where(auditable: self) - .or(Audited.audit_class.unscoped.where(associated: self)) + Audited.audit_class(audited_options[:class_name]).unscoped.where(auditable: self) + .or(Audited.audit_class(audited_options[:class_name]).unscoped.where(associated: self)) .order(created_at: :desc) end @@ -229,7 +229,7 @@ def revision_with(attributes) revision.send :instance_variable_set, "@destroyed", false revision.send :instance_variable_set, "@_destroyed", false revision.send :instance_variable_set, "@marked_for_destruction", false - Audited.audit_class.assign_revision_attributes(revision, attributes) + Audited.audit_class(audited_options[:class_name]).assign_revision_attributes(revision, attributes) # Remove any association proxies so that they will be recreated # and reference the correct object for this revision. The only way @@ -508,7 +508,7 @@ def enable_auditing # convenience wrapper around # @see Audit#as_user. def audit_as(user, &block) - Audited.audit_class.as_user(user, &block) + Audited.audit_class(audited_options[:class_name]).as_user(user, &block) end def auditing_enabled From 176f89e0560e84c748e9a0baf7a0a8e0e8da44c5 Mon Sep 17 00:00:00 2001 From: Usman Anwar Date: Sun, 25 Jan 2026 23:53:24 +0500 Subject: [PATCH 2/2] updated README --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 7a076c2c..eec99eb9 100644 --- a/README.md +++ b/README.md @@ -425,6 +425,24 @@ Audited.config do |config| end ``` +### Multiple `Audit` models + +If you want to use a different audit model for a specfic audited model, create a new class that +inherits from `Audited::Audit`: +```ruby +class CustomAudit < Audited::Audit + def some_custom_behavior + "Hiya!" + end +end +``` +Then provide the class name in options: +```ruby +class User < ActiveRecord::Base + audited class_name: "CustomAudit" +end +``` + ### Enum Storage In 4.10, the default behavior for enums changed from storing the value synthesized by Rails to the value stored in the DB. You can restore the previous behavior by setting the store_synthesized_enums configuration value: