class Shoulda::Matchers::ActiveRecord::HaveDbColumnMatcher

@private

def actual_primary?

def actual_primary?
  model_class.primary_key == matched_column.name
end

def actual_scale

def actual_scale
  matched_column.scale
end

def column_exists?

def column_exists?
  if model_class.column_names.include?(@column.to_s)
    true
  else
    @missing =
      "#{model_class} does not have a db column named #{@column}."
    false
  end
end

def correct_array?

def correct_array?
  return true unless @options.key?(:array)
  if matched_column.array? == @options[:array]
    true
  else
    @missing = "#{model_class} has a db column named #{@column} "
    @missing <<
      if @options[:primary]
        'that is not array, but should be'
      else
        'that is array, but should not be'
      end
    false
  end
end

def correct_column_type?

def correct_column_type?
  return true unless @options.key?(:column_type)
  if matched_column.type.to_s == @options[:column_type].to_s
    true
  else
    @missing =
      "#{model_class} has a db column named #{@column} " <<
      "of type #{matched_column.type}, not #{@options[:column_type]}."
    false
  end
end

def correct_default?

def correct_default?
  return true unless @options.key?(:default)
  if matched_column.type_cast_default.to_s == @options[:default].to_s
    true
  else
    @missing = "#{model_class} has a db column named #{@column} " <<
               "of default #{matched_column.type_cast_default}, " <<
               "not #{@options[:default]}."
    false
  end
end

def correct_limit?

def correct_limit?
  return true unless @options.key?(:limit)
  if matched_column.limit.to_s == @options[:limit].to_s
    true
  else
    @missing = "#{model_class} has a db column named #{@column} " <<
               "of limit #{matched_column.limit}, " <<
               "not #{@options[:limit]}."
    false
  end
end

def correct_null?

def correct_null?
  return true unless @options.key?(:null)
  if matched_column.null.to_s == @options[:null].to_s
    true
  else
    @missing = "#{model_class} has a db column named #{@column} " <<
               "of null #{matched_column.null}, " <<
               "not #{@options[:null]}."
    false
  end
end

def correct_precision?

def correct_precision?
  return true unless @options.key?(:precision)
  if matched_column.precision.to_s == @options[:precision].to_s
    true
  else
    @missing = "#{model_class} has a db column named #{@column} " <<
               "of precision #{matched_column.precision}, " <<
               "not #{@options[:precision]}."
    false
  end
end

def correct_primary?

def correct_primary?
  return true unless @options.key?(:primary)
  if matched_column.primary? == @options[:primary]
    true
  else
    @missing = "#{model_class} has a db column named #{@column} "
    @missing <<
      if @options[:primary]
        'that is not primary, but should be'
      else
        'that is primary, but should not be'
      end
    false
  end
end

def correct_scale?

def correct_scale?
  return true unless @options.key?(:scale)
  if actual_scale.to_s == @options[:scale].to_s
    true
  else
    @missing = "#{model_class} has a db column named #{@column} "
    @missing << "of scale #{actual_scale}, not #{@options[:scale]}."
    false
  end
end

def correct_sql_column_type?

def correct_sql_column_type?
  return true unless @options.key?(:sql_column_type)
  if matched_column.sql_type.to_s == @options[:sql_column_type].to_s
    true
  else
    @missing =
      "#{model_class} has a db column named #{@column} " <<
      "of sql type #{matched_column.sql_type}, not #{@options[:sql_column_type]}."
    false
  end
end

def description

def description
  desc = "have db column named #{@column}"
  desc << " of type #{@options[:column_type]}" if @options.key?(:column_type)
  desc << " of sql_type #{@options[:sql_column_type]}" if @options.key?(:sql_column_type)
  desc << " of precision #{@options[:precision]}" if @options.key?(:precision)
  desc << " of limit #{@options[:limit]}" if @options.key?(:limit)
  desc << " of default #{@options[:default]}" if @options.key?(:default)
  desc << " of null #{@options[:null]}" if @options.key?(:null)
  desc << " of primary #{@options[:primary]}" if @options.key?(:primary)
  desc << " of scale #{@options[:scale]}" if @options.key?(:scale)
  desc
end

def expectation

def expectation
  "#{model_class.name} to #{description}"
end

def failure_message

def failure_message
  "Expected #{expectation} (#{@missing})"
end

def failure_message_when_negated

def failure_message_when_negated
  "Did not expect #{expectation}"
end

def initialize(column)

def initialize(column)
  @column = column
  @options = {}
end

def matched_column

def matched_column
  @_matched_column ||= begin
    column = model_class.columns.detect do |each|
      each.name == @column.to_s
    end
    DecoratedColumn.new(model_class, column)
  end
end

def matches?(subject)

def matches?(subject)
  @subject = subject
  column_exists? &&
    correct_column_type? &&
    correct_sql_column_type? &&
    correct_precision? &&
    correct_limit? &&
    correct_default? &&
    correct_null? &&
    correct_scale? &&
    correct_primary? &&
    correct_array?
end

def model_class

def model_class
  @subject.class
end

def of_sql_type(sql_column_type)

def of_sql_type(sql_column_type)
  @options[:sql_column_type] = sql_column_type
  self
end

def of_type(column_type)

def of_type(column_type)
  @options[:column_type] = column_type
  self
end

def validate_options(opts)

def validate_options(opts)
  invalid_options = opts.keys.map(&:to_sym) - OPTIONS
  if invalid_options.any?
    raise(
      ArgumentError,
      "Unknown option(s): #{invalid_options.map(&:inspect).join(', ')}",
    )
  end
end

def with_options(opts = {})

def with_options(opts = {})
  validate_options(opts)
  OPTIONS.each do |attribute|
    if opts.key?(attribute.to_sym)
      @options[attribute.to_sym] = opts[attribute.to_sym]
    end
  end
  self
end