module RSpec::Rails::Mocks

def mock_model(string_or_model_class, options_and_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

+model_class+ can be any of:

(via add_stubs) if +stubs+ is passed.
common methods stubbed out. Additional methods may be easily stubbed
Creates a mock object instance for a +string_or_model_class+ with
def mock_model(string_or_model_class, options_and_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
      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
  id = options_and_stubs.has_key?(:id) ? options_and_stubs[:id] : next_id
  options_and_stubs = options_and_stubs.reverse_merge({
    :id => id,
    :destroyed? => false,
    :marked_for_destruction? => false
  })
  derived_name = "#{model_class.name}_#{id}"
  m = mock(derived_name, options_and_stubs)
  m.extend InstanceMethods
  m.extend ActiveModel::Conversion
  errors = ActiveModel::Errors.new(m)
  [:save, :update_attributes].each do |key|
    if options_and_stubs[key] == false
      errors.stub(:empty?) { false }
    end
  end
  m.stub(:errors) { errors }
  m.__send__(:__mock_proxy).instance_eval(<<-CODE, __FILE__, __LINE__)
    def @object.is_a?(other)
      #{model_class}.ancestors.include?(other)
    end
    def @object.kind_of?(other)
      #{model_class}.ancestors.include?(other)
    end
    def @object.instance_of?(other)
      other == #{model_class}
    end
    def @object.respond_to?(method_name)
      #{model_class}.respond_to?(:column_names) && #{model_class}.column_names.include?(method_name.to_s) || super
    end
    def @object.class
      #{model_class}
    end
    def @object.to_s
      "#{model_class.name}_#{id}"
    end
  CODE
  yield m if block_given?
  m
end

def next_id

def next_id
  @@model_id += 1
end

def stub_model(model_class, stubs={})

end
person.first_name = "David"
stub_model(Person) do |person|
stub_model(Person, :id => 37)
stub_model(Person).as_new_record
stub_model(Person)

== Examples

a look at libraries like unit_record or NullDB.
from the object itself. To completely decouple from the database, take
loading up its columns from the database. It just prevents data access
database-independent. It does not stop the model class itself from
+stub_model+ does not make your examples entirely

== Database Independence

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 new_record? will return true, 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 new_record? will return false. If you want
new_record? is overridden to return the result of id.nil?

mocking/stubbing framework.
key/value pair is assigned as a stub return value using RSpec's
submitted values. If the model does not have a matching attribute, the
matching attribute (determined by asking it) are simply assigned the
database*. For each key in +hash_of_stubs+, if the model has a
Creates an instance of +Model+ that is prohibited from accessing the

stub_model(Model, instance_variable_name, hash_of_stubs)
stub_model(Model, hash_of_stubs)
stub_model(Model).as_new_record
stub_model(Model)
:call-seq:
def stub_model(model_class, stubs={})
  primary_key = model_class.primary_key.to_sym
  stubs = {primary_key => next_id}.merge(stubs)
  model_class.new.tap do |m|
    m.__send__("#{primary_key}=", stubs.delete(primary_key))
    m.extend ModelStubber
    m.stub(stubs)
    yield m if block_given?
  end
end