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
end)
end
else
model_class = string_or_model_class
end
unless model_class.kind_of? ActiveModel::Naming
raise ArgumentError.new "The mock_model method can only accept as its first argument:\n* A String representing a Class that does not exist\n* A String representing a Class that extends ActiveModel::Naming\n* A Class that extends ActiveModel::Naming\n\nIt received \#{model_class.inspect}\n"
end
stubs = stubs.reverse_merge(:id => next_id)
stubs = stubs.reverse_merge(:persisted? => !!stubs[:id])
stubs = stubs.reverse_merge(:destroyed? => false)
stubs = stubs.reverse_merge(:marked_for_destruction? => false)
stubs = stubs.reverse_merge(:blank? => false)
mock("#{model_class.name}_#{stubs[:id]}", stubs).tap do |m|
m.extend ActiveModelInstanceMethods
m.singleton_class.__send__ :include, ActiveModel::Conversion
m.singleton_class.__send__ :include, ActiveModel::Validations
if defined?(ActiveRecord)
m.extend ActiveRecordInstanceMethods
[:save, :update_attributes].each do |key|
if stubs[key] == false
m.errors.stub(:empty?) { false }
end
end
end
m.__send__(:__mock_proxy).instance_eval("def @object.is_a?(other)\n\#{model_class}.ancestors.include?(other)\nend\ndef @object.kind_of?(other)\n\#{model_class}.ancestors.include?(other)\nend\ndef @object.instance_of?(other)\nother == \#{model_class}\nend\ndef @object.respond_to?(method_name, include_private=false)\n\#{model_class}.respond_to?(:column_names) && \#{model_class}.column_names.include?(method_name.to_s) || super\nend\ndef @object.class\n\#{model_class}\nend\ndef @object.to_s\n\"\#{model_class.name}_\#{to_param}\"\nend\n", __FILE__, __LINE__)
yield m if block_given?
end
end