module RSpec::Rails::Mocks

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
NOTE that only ActiveModel's 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 = {})
  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
        extend ActiveModel::Naming
        def self.primary_key; :id; end
      end)
    end
  else
    model_class = string_or_model_class
  end
  unless model_class.kind_of? ActiveModel::Naming
    raise ArgumentError.new <<-EOM
ck_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
eived #{model_class.inspect}
  end
  stubs = stubs.reverse_merge(:id => next_id)
  stubs = stubs.reverse_merge(:persisted? => !!stubs[:id],
                              :destroyed? => false,
                              :marked_for_destruction? => false,
                              :valid? => true,
                              :blank? => false)
  mock("#{model_class.name}_#{stubs[:id]}", stubs).tap do |m|
    m.singleton_class.class_eval do
      include ActiveModelInstanceMethods
      include ActiveRecordInstanceMethods if defined?(ActiveRecord)
      include ActiveModel::Conversion
      include ActiveModel::Validations
    end
    if defined?(ActiveRecord)
      [:save, :update_attributes].each do |key|
        if stubs[key] == false
          m.errors.stub(:empty? => false)
        end
      end
    end
    m.__send__(:__mock_proxy).instance_eval(<<-CODE, __FILE__, __LINE__)
      def @object.is_a?(other)
        #{model_class}.ancestors.include?(other)
      end unless #{stubs.has_key?(:is_a?)}
      def @object.kind_of?(other)
        #{model_class}.ancestors.include?(other)
      end unless #{stubs.has_key?(:kind_of?)}
      def @object.instance_of?(other)
        other == #{model_class}
      end unless #{stubs.has_key?(:instance_of?)}
      def @object.__model_class_has_column?(method_name)
        #{model_class}.respond_to?(:column_names) && #{model_class}.column_names.include?(method_name.to_s)
      end
      def @object.respond_to?(method_name, include_private=false)
        __model_class_has_column?(method_name) ? true : super
      end unless #{stubs.has_key?(:respond_to?)}
      def @object.method_missing(m, *a, &b)
        respond_to?(m) ? null_object? ? self : nil : super
      end
      def @object.class
        #{model_class}
      end unless #{stubs.has_key?(:class)}
      def @object.to_s
        "#{model_class.name}_#{to_param}"
      end unless #{stubs.has_key?(:to_s)}
    CODE
    yield m if block_given?
  end
end