From 0b7f231899408e8c126237ec74577f06e6ab4330 Mon Sep 17 00:00:00 2001 From: michael groble Date: Sat, 28 Sep 2019 10:40:09 -0500 Subject: [PATCH] move Audit behavior into a concern --- lib/audited/audit.rb | 144 +++++++++++++++++++++++-------------------- 1 file changed, 78 insertions(+), 66 deletions(-) diff --git a/lib/audited/audit.rb b/lib/audited/audit.rb index a1512ea3f..01f5e6e08 100644 --- a/lib/audited/audit.rb +++ b/lib/audited/audit.rb @@ -33,28 +33,83 @@ def dump(obj) end end - class Audit < ::ActiveRecord::Base - belongs_to :auditable, polymorphic: true - belongs_to :user, polymorphic: true - belongs_to :associated, polymorphic: true + module Auditable + extend ::ActiveSupport::Concern + + included do + belongs_to :auditable, polymorphic: true + belongs_to :user, polymorphic: true + belongs_to :associated, polymorphic: true + + before_create :set_version_number, :set_audit_user, :set_request_uuid, :set_remote_address + + cattr_accessor :audited_class_names + self.audited_class_names = Set.new + + serialize :audited_changes, YAMLIfTextColumnType + + scope :ascending, ->{ reorder(version: :asc) } + scope :descending, ->{ reorder(version: :desc)} + scope :creates, ->{ where(action: 'create')} + scope :updates, ->{ where(action: 'update')} + scope :destroys, ->{ where(action: 'destroy')} + + scope :up_until, ->(date_or_time){ where("created_at <= ?", date_or_time) } + scope :from_version, ->(version){ where('version >= ?', version) } + scope :to_version, ->(version){ where('version <= ?', version) } + scope :auditable_finder, ->(auditable_id, auditable_type){ where(auditable_id: auditable_id, auditable_type: auditable_type)} + + alias_method :user_as_model, :user + alias_method :user, :user_as_string + alias_method :user_as_model=, :user= + alias_method :user=, :user_as_string= + end - before_create :set_version_number, :set_audit_user, :set_request_uuid, :set_remote_address + class_methods do + # Returns the list of classes that are being audited + def audited_classes + audited_class_names.map(&:constantize) + end + + # All audits made during the block called will be recorded as made + # by +user+. This method is hopefully threadsafe, making it ideal + # for background operations that require audit information. + def as_user(user) + last_audited_user = ::Audited.store[:audited_user] + ::Audited.store[:audited_user] = user + yield + ensure + ::Audited.store[:audited_user] = last_audited_user + end - cattr_accessor :audited_class_names - self.audited_class_names = Set.new + # @private + def reconstruct_attributes(audits) + audits.each_with_object({}) do |audit, all| + all.merge!(audit.new_attributes) + all[:audit_version] = audit.version + end + end - serialize :audited_changes, YAMLIfTextColumnType + # @private + def assign_revision_attributes(record, attributes) + attributes.each do |attr, val| + record = record.dup if record.frozen? - scope :ascending, ->{ reorder(version: :asc) } - scope :descending, ->{ reorder(version: :desc)} - scope :creates, ->{ where(action: 'create')} - scope :updates, ->{ where(action: 'update')} - scope :destroys, ->{ where(action: 'destroy')} + if record.respond_to?("#{attr}=") + record.attributes.key?(attr.to_s) ? + record[attr] = val : + record.send("#{attr}=", val) + end + end + record + end + + # use created_at as timestamp cache key + def collection_cache_key(collection = all, *) + super(collection, :created_at) + end + end - scope :up_until, ->(date_or_time){ where("created_at <= ?", date_or_time) } - scope :from_version, ->(version){ where('version >= ?', version) } - scope :to_version, ->(version){ where('version <= ?', version) } - scope :auditable_finder, ->(auditable_id, auditable_type){ where(auditable_id: auditable_id, auditable_type: auditable_type)} # Return all audits older than the current one. def ancestors self.class.ascending.auditable_finder(auditable_id, auditable_type).to_version(version) @@ -109,61 +164,14 @@ def user_as_string=(user) # reset both either way self.user_as_model = self.username = nil user.is_a?(::ActiveRecord::Base) ? - self.user_as_model = user : - self.username = user + self.user_as_model = user : + self.username = user end - alias_method :user_as_model=, :user= - alias_method :user=, :user_as_string= # @private def user_as_string user_as_model || username end - alias_method :user_as_model, :user - alias_method :user, :user_as_string - - # Returns the list of classes that are being audited - def self.audited_classes - audited_class_names.map(&:constantize) - end - - # All audits made during the block called will be recorded as made - # by +user+. This method is hopefully threadsafe, making it ideal - # for background operations that require audit information. - def self.as_user(user) - last_audited_user = ::Audited.store[:audited_user] - ::Audited.store[:audited_user] = user - yield - ensure - ::Audited.store[:audited_user] = last_audited_user - end - - # @private - def self.reconstruct_attributes(audits) - audits.each_with_object({}) do |audit, all| - all.merge!(audit.new_attributes) - all[:audit_version] = audit.version - end - end - - # @private - def self.assign_revision_attributes(record, attributes) - attributes.each do |attr, val| - record = record.dup if record.frozen? - - if record.respond_to?("#{attr}=") - record.attributes.key?(attr.to_s) ? - record[attr] = val : - record.send("#{attr}=", val) - end - end - record - end - - # use created_at as timestamp cache key - def self.collection_cache_key(collection = all, *) - super(collection, :created_at) - end private @@ -191,4 +199,8 @@ def set_remote_address self.remote_address ||= ::Audited.store[:current_remote_address] end end + + class Audit < ::ActiveRecord::Base + include Auditable + end end