module Audited::Auditor::AuditedInstanceMethods

Constants

REDACTED

Public Instance Methods

audited_attributes() click to toggle source

List of attributes that are audited.

# File lib/audited/auditor.rb, line 173
def audited_attributes
  audited_attributes = attributes.except(*self.class.non_audited_columns)
  normalize_enum_changes(audited_attributes)
end
combine_audits(audits_to_combine) click to toggle source

Combine multiple audits into one.

# File lib/audited/auditor.rb, line 187
def combine_audits(audits_to_combine)
  combine_target = audits_to_combine.last
  combine_target.audited_changes = audits_to_combine.pluck(:audited_changes).reduce(&:merge)
  combine_target.comment = "#{combine_target.comment}\nThis audit is the result of multiple audits being combined."

  transaction do
    combine_target.save!
    audits_to_combine.unscope(:limit).where("version < ?", combine_target.version).delete_all
  end
end
own_and_associated_audits() click to toggle source

Returns a list combined of record audits and associated audits.

# File lib/audited/auditor.rb, line 179
def own_and_associated_audits
  Audited.audit_class.unscoped
    .where("(auditable_type = :type AND auditable_id = :id) OR (associated_type = :type AND associated_id = :id)",
      type: self.class.base_class.name, id: id)
    .order(created_at: :desc)
end
revision(version) click to toggle source

Get a specific revision specified by the version number, or :previous Returns nil for versions greater than revisions count

# File lib/audited/auditor.rb, line 160
def revision(version)
  if version == :previous || audits.last.version >= version
    revision_with Audited.audit_class.reconstruct_attributes(audits_to(version))
  end
end
revision_at(date_or_time) click to toggle source

Find the oldest revision recorded prior to the date/time provided.

# File lib/audited/auditor.rb, line 167
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?
end
revisions(from_version = 1) click to toggle source

Gets an array of the revisions available

user.revisions.each do |revision|
  user.name
  user.version
end
# File lib/audited/auditor.rb, line 144
def revisions(from_version = 1)
  return [] unless audits.from_version(from_version).exists?

  all_audits = audits.select([:audited_changes, :version, :action]).to_a
  targeted_audits = all_audits.select { |audit| audit.version >= from_version }

  previous_attributes = reconstruct_attributes(all_audits - targeted_audits)

  targeted_audits.map do |audit|
    previous_attributes.merge!(audit.new_attributes)
    revision_with(previous_attributes.merge!(version: audit.version))
  end
end
save_with_auditing() click to toggle source

Temporarily turns on auditing while saving.

# File lib/audited/auditor.rb, line 123
def save_with_auditing
  with_auditing { save }
end
save_without_auditing() click to toggle source

Temporarily turns off auditing while saving.

# File lib/audited/auditor.rb, line 108
def save_without_auditing
  without_auditing { save }
end
with_auditing(&block) click to toggle source

Executes the block with the auditing callbacks enabled.

@foo.with_auditing do
  @foo.save
end
# File lib/audited/auditor.rb, line 133
def with_auditing(&block)
  self.class.with_auditing(&block)
end
without_auditing(&block) click to toggle source

Executes the block with the auditing callbacks disabled.

@foo.without_auditing do
  @foo.save
end
# File lib/audited/auditor.rb, line 118
def without_auditing(&block)
  self.class.without_auditing(&block)
end

Protected Instance Methods

revision_with(attributes) click to toggle source
# File lib/audited/auditor.rb, line 200
def revision_with(attributes)
  dup.tap do |revision|
    revision.id = id
    revision.send :instance_variable_set, "@new_record", destroyed?
    revision.send :instance_variable_set, "@persisted", !destroyed?
    revision.send :instance_variable_set, "@readonly", false
    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)

    # Remove any association proxies so that they will be recreated
    # and reference the correct object for this revision. The only way
    # to determine if an instance variable is a proxy object is to
    # see if it responds to certain methods, as it forwards almost
    # everything to its target.
    revision.instance_variables.each do |ivar|
      proxy = revision.instance_variable_get ivar
      if !proxy.nil? && proxy.respond_to?(:proxy_respond_to?)
        revision.instance_variable_set ivar, nil
      end
    end
  end
end

Private Instance Methods

audit_create() click to toggle source
# File lib/audited/auditor.rb, line 291
def audit_create
  write_audit(action: "create", audited_changes: audited_attributes,
              comment: audit_comment)
end
audit_destroy() click to toggle source
# File lib/audited/auditor.rb, line 303
def audit_destroy
  unless new_record?
    write_audit(action: "destroy", audited_changes: audited_attributes,
                comment: audit_comment)
  end
end
audit_update() click to toggle source
# File lib/audited/auditor.rb, line 296
def audit_update
  unless (changes = audited_changes).empty? && (audit_comment.blank? || audited_options[:update_with_comment_only] == false)
    write_audit(action: "update", audited_changes: changes,
                comment: audit_comment)
  end
end
audited_changes() click to toggle source
# File lib/audited/auditor.rb, line 227
def audited_changes
  all_changes = respond_to?(:changes_to_save) ? changes_to_save : changes
  filtered_changes = \
    if audited_options[:only].present?
      all_changes.slice(*self.class.audited_columns)
    else
      all_changes.except(*self.class.non_audited_columns)
    end

  filtered_changes = redact_values(filtered_changes)
  filtered_changes = normalize_enum_changes(filtered_changes)
  filtered_changes.to_hash
end
auditing_enabled() click to toggle source
# File lib/audited/auditor.rb, line 356
def auditing_enabled
  run_conditional_check(audited_options[:if]) &&
    run_conditional_check(audited_options[:unless], matching: false) &&
    self.class.auditing_enabled
end
audits_to(version = nil) click to toggle source
# File lib/audited/auditor.rb, line 279
def audits_to(version = nil)
  if version == :previous
    version = if audit_version
      audit_version - 1
    else
      previous = audits.descending.offset(1).first
      previous ? previous.version : 1
    end
  end
  audits.to_version(version)
end
combine_audits_if_needed() click to toggle source
# File lib/audited/auditor.rb, line 337
def combine_audits_if_needed
  max_audits = audited_options[:max_audits]
  if max_audits && (extra_count = audits.count - max_audits) > 0
    audits_to_combine = audits.limit(extra_count + 1)
    combine_audits(audits_to_combine)
  end
end
comment_required_state?() click to toggle source
# File lib/audited/auditor.rb, line 330
def comment_required_state?
  auditing_enabled &&
    audited_changes.present? &&
    ((audited_options[:on].include?(:create) && new_record?) ||
    (audited_options[:on].include?(:update) && persisted? && changed?))
end
normalize_enum_changes(changes) click to toggle source
# File lib/audited/auditor.rb, line 241
def normalize_enum_changes(changes)
  return changes if Audited.store_synthesized_enums

  self.class.defined_enums.each do |name, values|
    if changes.has_key?(name)
      changes[name] = \
        if changes[name].is_a?(Array)
          changes[name].map { |v| values[v] }
        elsif rails_below?("5.0")
          changes[name]
        else
          values[changes[name]]
        end
    end
  end
  changes
end
presence_of_audit_comment() click to toggle source
# File lib/audited/auditor.rb, line 324
def presence_of_audit_comment
  if comment_required_state?
    errors.add(:audit_comment, :blank) unless audit_comment.present?
  end
end
rails_below?(rails_version) click to toggle source
# File lib/audited/auditor.rb, line 275
def rails_below?(rails_version)
  Gem::Version.new(Rails::VERSION::STRING) < Gem::Version.new(rails_version)
end
reconstruct_attributes(audits) click to toggle source
# File lib/audited/auditor.rb, line 370
def reconstruct_attributes(audits)
  attributes = {}
  audits.each { |audit| attributes.merge!(audit.new_attributes) }
  attributes
end
redact_values(filtered_changes) click to toggle source
# File lib/audited/auditor.rb, line 259
def redact_values(filtered_changes)
  [audited_options[:redacted]].flatten.compact.each do |option|
    changes = filtered_changes[option.to_s]
    new_value = audited_options[:redaction_value] || REDACTED
    values = if changes.is_a? Array
      changes.map { new_value }
    else
      new_value
    end
    hash = {option.to_s => values}
    filtered_changes.merge!(hash)
  end

  filtered_changes
end
require_comment() click to toggle source
# File lib/audited/auditor.rb, line 345
def require_comment
  if auditing_enabled && audit_comment.blank?
    errors.add(:audit_comment, :blank)
    throw(:abort)
  end
end
run_conditional_check(condition, matching: true) click to toggle source
# File lib/audited/auditor.rb, line 362
def run_conditional_check(condition, matching: true)
  return true if condition.blank?
  return condition.call(self) == matching if condition.respond_to?(:call)
  return send(condition) == matching if respond_to?(condition.to_sym, true)

  true
end
write_audit(attrs) click to toggle source
# File lib/audited/auditor.rb, line 310
def write_audit(attrs)
  self.audit_comment = nil

  if auditing_enabled
    attrs[:associated] = send(audit_associated_with) unless audit_associated_with.nil?

    run_callbacks(:audit) {
      audit = audits.create(attrs)
      combine_audits_if_needed if attrs[:action] != "create"
      audit
    }
  end
end