class Audited::RspecMatchers::AuditMatcher

:nodoc:

def associated_with(model)

def associated_with(model)
  @options[:associated_with] = model
  self
end

def associated_with_model?

def associated_with_model?
  expects "#{model_class} to record audits to associated model #{@options[:associated_with]}"
  model_class.audit_associated_with == @options[:associated_with]
end

def audited_on_create_or_update?

def audited_on_create_or_update?
  model_class.audited_options[:on].include?(:create) || model_class.audited_options[:on].include?(:update)
end

def auditing_enabled?

def auditing_enabled?
  expects "#{model_class} to be audited"
  model_class.respond_to?(:auditing_enabled) && model_class.auditing_enabled
end

def build_ignored_fields_from_options

def build_ignored_fields_from_options
  default_ignored_attributes = model_class.default_ignored_attributes
  if @options[:only].present?
    (default_ignored_attributes | model_class.column_names) - @options[:only]
  elsif @options[:except].present?
    default_ignored_attributes | @options[:except]
  else
    default_ignored_attributes
  end
end

def callbacks_for(action, kind: :before)

def callbacks_for(action, kind: :before)
  model_class.send("_#{action}_callbacks").select { |cb| cb.kind == kind }.map(&:filter)
end

def comment_required_valid?

def comment_required_valid?
  expects "to require audit_comment before #{model_class.audited_options[:on]} when comment required"
  validate_callbacks_include_presence_of_comment? && destroy_callbacks_include_comment_required?
end

def description

def description
  description = "audited"
  description += " associated with #{@options[:associated_with]}" if @options.key?(:associated_with)
  description += " only => #{@options[:only].join ", "}" if @options.key?(:only)
  description += " except => #{@options[:except].join(", ")}" if @options.key?(:except)
  description += " requires audit_comment" if @options.key?(:comment_required)
  description
end

def destroy_callbacks_include_comment_required?

def destroy_callbacks_include_comment_required?
  if @options[:comment_required] && model_class.audited_options[:on].include?(:destroy)
    callbacks_for(:destroy).include?(:require_comment)
  else
    true
  end
end

def except(*fields)

def except(*fields)
  @options[:except] = fields.flatten.map(&:to_s)
  self
end

def expects(message)

def expects(message)
  @expectation = message
end

def failure_message

def failure_message
  "Expected #{@expectation}"
end

def initialize

:nodoc:
def initialize
  @options = {}
end

def matches?(subject)

def matches?(subject)
  @subject = subject
  auditing_enabled? && required_checks_for_options_satisfied?
end

def model_class

def model_class
  @subject.class
end

def negative_failure_message

def negative_failure_message
  "Did not expect #{@expectation}"
end

def on(*actions)

def on(*actions)
  @options[:on] = actions.flatten.map(&:to_sym)
  self
end

def only(*fields)

def only(*fields)
  @options[:only] = fields.flatten.map(&:to_s)
  self
end

def only_audit_on_designated_callbacks?

def only_audit_on_designated_callbacks?
  {
    create: [:after, :audit_create],
    update: [:before, :audit_update],
    destroy: [:before, :audit_destroy]
  }.map do |(action, kind_callback)|
    kind, callback = kind_callback
    callbacks_for(action, kind: kind).include?(callback) if @options[:on].include?(action)
  end.compact.all?
end

def records_changes_to_specified_fields?

def records_changes_to_specified_fields?
  ignored_fields = build_ignored_fields_from_options
  expects "non audited columns (#{model_class.non_audited_columns.inspect}) to match (#{ignored_fields})"
  model_class.non_audited_columns.to_set == ignored_fields.to_set
end

def required_checks_for_options_satisfied?

def required_checks_for_options_satisfied?
  {
    only: :records_changes_to_specified_fields?,
    except: :records_changes_to_specified_fields?,
    comment_required: :comment_required_valid?,
    associated_with: :associated_with_model?,
    on: :only_audit_on_designated_callbacks?
  }.map do |(option, check)|
    send(check) if @options[option].present?
  end.compact.all?
end

def requires_comment

def requires_comment
  @options[:comment_required] = true
  self
end

def requires_comment_before_callbacks?

def requires_comment_before_callbacks?
  [:create, :update, :destroy].map do |action|
    if @options[:comment_required] && model_class.audited_options[:on].include?(action)
      callbacks_for(action).include?(:require_comment)
    end
  end.compact.all?
end

def validate_callbacks_include_presence_of_comment?

def validate_callbacks_include_presence_of_comment?
  if @options[:comment_required] && audited_on_create_or_update?
    callbacks_for(:validate).include?(:presence_of_audit_comment)
  else
    true
  end
end