module RSpec::ActiveModel::Mocks::Mocks

def self.<(other); other == ActiveRecord::Base; end

For detection of being a valid association in 7+
def self.<(other); other == ActiveRecord::Base; end

def self._reflect_on_association(_other); nil; end

def self._reflect_on_association(_other); nil; end

def self.composite_primary_key?; false; end

def self.composite_primary_key?; false; end

def self.has_query_constraints?; false; end

def self.has_query_constraints?; false; end

def self.param_delimiter; "-"; end

def self.param_delimiter; "-"; end

def self.primary_key; :id; end

def self.primary_key; :id; end

def mock_model(string_or_model_class, stubs={})

* A Class that extends ActiveModel::Naming
* A String representing a Class that extends ActiveModel::Naming
* A String representing a Class that does not exist

`string_or_model_class` can be any of:

new_record?).
ActiveModel API (which declares persisted?, not
extension frameworks that have yet to update themselves to the
persisted?, and is present only for compatibility with
stubbed out implicitly. new_record? returns the inverse of
ActiveModel methods, plus new_record?, are

impersonating models that don't exist yet.
stubbed (via add_stubs) if `stubs` is passed. This is most useful for
ActiveModel methods stubbed out. Additional methods may be easily
Creates a test double representing `string_or_model_class` with common
def mock_model(string_or_model_class, stubs={})
  @__rspec_active_model_mocks ||= Hash.new { |h, k| h[k] = [] }
  if String === string_or_model_class
    if Object.const_defined?(string_or_model_class)
      model_class = Object.const_get(string_or_model_class)
    else
      model_class = Object.const_set(string_or_model_class, Class.new do
        # rubocop:disable  Style/SingleLineMethods
        extend ::ActiveModel::Naming
        def self.primary_key; :id; end
        # For detection of being a valid association in 7+
        def self.<(other); other == ActiveRecord::Base; end
        def self._reflect_on_association(_other); nil; end
        def self.composite_primary_key?; false; end
        def self.has_query_constraints?; false; end
        def self.param_delimiter; "-"; end
        # rubocop:enable  Style/SingleLineMethods
      end)
    end
  else
    model_class = string_or_model_class
  end
  unless model_class.kind_of? ::ActiveModel::Naming
    raise ArgumentError, <<-EOM
mock_model method can only accept as its first argument:
String representing a Class that does not exist
String representing a Class that extends ActiveModel::Naming
Class that extends ActiveModel::Naming
eceived #{model_class.inspect}
    EOM
  end
  stubs = { :id => next_id }.merge(stubs)
  stubs = { :persisted? => !!stubs[:id],
            :destroyed? => false,
            :marked_for_destruction? => false,
            :valid? => true,
            :blank? => false }.merge(stubs)
  double("#{model_class.name}_#{stubs[:id]}", stubs).tap do |m|
    if model_class.method(:===).owner == Module && !stubs.key?(:===)
      allow(model_class).to receive(:===).and_wrap_original do |original, other|
        @__rspec_active_model_mocks[model_class].include?(other) || original.call(other)
      end
    end
    @__rspec_active_model_mocks[model_class] << m
    msingleton = class << m; self; end
    msingleton.class_eval do
      include ActiveModelInstanceMethods
      include ActiveRecordInstanceMethods if defined?(ActiveRecord)
      include ActiveModel::Conversion
      include ActiveModel::Validations
    end
    if defined?(ActiveRecord) && stubs.values_at(:save, :update_attributes, :update).include?(false)
      RSpec::Mocks.allow_message(m.errors, :empty?).and_return(false)
      RSpec::Mocks.allow_message(m.errors, :blank?).and_return(false)
    end
    msingleton.__send__(:define_method, :is_a?) do |other|
      model_class.ancestors.include?(other)
    end unless stubs.key?(:is_a?)
    msingleton.__send__(:define_method, :kind_of?) do |other|
      model_class.ancestors.include?(other)
    end unless stubs.key?(:kind_of?)
    msingleton.__send__(:define_method, :instance_of?) do |other|
      other == model_class
    end unless stubs.key?(:instance_of?)
    msingleton.__send__(:define_method, :__model_class_has_column?) do |method_name|
      model_class.respond_to?(:column_names) && model_class.column_names.include?(method_name.to_s)
    end
    msingleton.__send__(:define_method, :has_attribute?) do |attr_name|
      __model_class_has_column?(attr_name)
    end unless stubs.key?(:has_attribute?)
    msingleton.__send__(:define_method, :respond_to?) do |method_name, *args|
      include_private = args.first || false
      __model_class_has_column?(method_name) ? true : super(method_name, include_private)
    end unless stubs.key?(:respond_to?)
    msingleton.__send__(:define_method, :method_missing) do |missing_m, *a, &b|
      if respond_to?(missing_m)
        null_object? ? self : nil
      else
        super(missing_m, *a, &b)
      end
    end
    msingleton.__send__(:define_method, :class) do
      model_class
    end unless stubs.key?(:class)
    mock_param = to_param
    msingleton.__send__(:define_method, :to_s) do
      "#{model_class.name}_#{mock_param}"
    end unless stubs.key?(:to_s)
    yield m if block_given?
  end
end

def next_id

def next_id
  @@model_id += 1
end

def stub_model(model_class, stubs={})

stub_model(Person) {|person| person.first_name = "David"}
stub_model(Person, :to_param => 37)
stub_model(Person).as_new_record
stub_model(Person)

@example

inherently more state-based than interaction-based.
helper), it is especially useful in view examples, which are
While you can use stub_model in any example (model, view, controller,

example a bit more descriptive.
case persisted? will return false, but using `as_new_record` makes the
set the id to nil. You can also explicitly set :id => nil, in which
the object to behave as a new record, sending it `as_new_record` will
This means that by default persisted? will return true. If you want
persisted? is overridden to return the result of !id.nil?

framework.
assigned as a stub return value using RSpec's mocking/stubbing
If the model does not have a matching attribute, the key/value pair is
(determined by `respond_to?`) it is simply assigned the submitted values.
For each key in `stubs`, if the model has a matching attribute

`ActiveRecord` model, it is prohibited from accessing the database.
generated value that is unique to each object. If `Model` is an
Creates an instance of `Model` with `to_param` stubbed using a
def stub_model(model_class, stubs={})
  model_class.new.tap do |m|
    m.extend ActiveModelStubExtensions
    if defined?(ActiveRecord) && model_class < ActiveRecord::Base && model_class.primary_key
      m.extend ActiveRecordStubExtensions
      primary_key = model_class.primary_key.to_sym
      stubs = { primary_key => next_id }.merge(stubs)
      stubs = { :persisted? => !!stubs[primary_key] }.merge(stubs)
    else
      stubs = { :id => next_id }.merge(stubs)
      stubs = { :persisted? => !!stubs[:id] }.merge(stubs)
    end
    stubs = { :blank? => false }.merge(stubs)
    stubs.each do |message, return_value|
      if m.respond_to?("#{message}=")
        begin
          m.__send__("#{message}=", return_value)
        rescue ActiveModel::MissingAttributeError
          RSpec::Mocks.allow_message(m, message).and_return(return_value)
        end
      else
        RSpec::Mocks.allow_message(m, message).and_return(return_value)
      end
    end
    yield m if block_given?
  end
end