require'active_support/core_ext'require'active_model'moduleRSpecmoduleRailsclassIllegalDataAccessException<StandardError;endmoduleMocksmoduleActiveModelInstanceMethods# Stubs `persisted?` to return false and `id` to return nil# @return selfdefas_new_recordRSpec::Mocks.allow_message(self,:persisted?).and_return(false)RSpec::Mocks.allow_message(self,:id).and_return(nil)selfend# Returns true by default. Override with a stub.defpersisted?trueend# Returns false for names matching <tt>/_before_type_cast$/</tt>,# otherwise delegates to super.defrespond_to?(message,include_private=false)message.to_s=~/_before_type_cast$/?false:superendendmoduleActiveRecordInstanceMethods# Stubs `persisted?` to return `false` and `id` to return `nil`.defdestroyRSpec::Mocks.allow_message(self,:persisted?).and_return(false)RSpec::Mocks.allow_message(self,:id).and_return(nil)end# Transforms the key to a method and calls it.def[](key)send(key)end# Returns the opposite of `persisted?`defnew_record?!persisted?endend# Creates a test double representing `string_or_model_class` with common# ActiveModel methods stubbed out. Additional methods may be easily# stubbed (via add_stubs) if `stubs` is passed. This is most useful for# impersonating models that don't exist yet.## NOTE that only ActiveModel's methods, plus <tt>new_record?</tt>, are# stubbed out implicitly. <tt>new_record?</tt> returns the inverse of# <tt>persisted?</tt>, and is present only for compatibility with# extension frameworks that have yet to update themselves to the# ActiveModel API (which declares <tt>persisted?</tt>, not# <tt>new_record?</tt>).## `string_or_model_class` can be any of:## * A String representing a Class that does not exist# * A String representing a Class that extends ActiveModel::Naming# * A Class that extends ActiveModel::Namingdefmock_model(string_or_model_class,stubs={})ifString===string_or_model_classifObject.const_defined?(string_or_model_class)model_class=Object.const_get(string_or_model_class)elsemodel_class=Object.const_set(string_or_model_class,Class.newdoextendActiveModel::Namingdefself.primary_key;:id;endend)endelsemodel_class=string_or_model_classendunlessmodel_class.kind_of?ActiveModel::NamingraiseArgumentError.new<<-EOM
The mock_model method can only accept as its first argument:
* A String representing a Class that does not exist
* A String representing a Class that extends ActiveModel::Naming
* A Class that extends ActiveModel::Naming
It received #{model_class.inspect}EOMendstubs=stubs.reverse_merge(:id=>next_id)stubs=stubs.reverse_merge(:persisted?=>!!stubs[:id],:destroyed?=>false,:marked_for_destruction?=>false,:valid?=>true,:blank?=>false)double("#{model_class.name}_#{stubs[:id]}",stubs).tapdo|m|m.singleton_class.class_evaldoincludeActiveModelInstanceMethodsincludeActiveRecordInstanceMethodsifdefined?(ActiveRecord)includeActiveModel::ConversionincludeActiveModel::Validationsendifdefined?(ActiveRecord)[:save,:update_attributes,:update].eachdo|key|ifstubs[key]==falseRSpec::Mocks.allow_message(m.errors,:empty?).and_return(false)endendendm.__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)} CODEyieldmifblock_given?endendmoduleActiveModelStubExtensions# Stubs `persisted` to return false and `id` to return nildefas_new_recordRSpec::Mocks.allow_message(self,:persisted?).and_return(false)RSpec::Mocks.allow_message(self,:id).and_return(nil)selfend# Returns `true` by default. Override with a stub.defpersisted?trueendendmoduleActiveRecordStubExtensions# Stubs `id` (or other primary key method) to return nildefas_new_recordself.__send__("#{self.class.primary_key}=",nil)superend# Returns the opposite of `persisted?`.defnew_record?!persisted?end# Raises an IllegalDataAccessException (stubbed models are not allowed to access the database)# @raises IllegalDataAccessExceptiondefconnectionraiseRSpec::Rails::IllegalDataAccessException.new("stubbed models are not allowed to access the database")endend# Creates an instance of `Model` with `to_param` stubbed using a# generated value that is unique to each object. If `Model` is an# `ActiveRecord` model, it is prohibited from accessing the database*.## For each key in `stubs`, if the model has a matching attribute# (determined by `respond_to?`) it is simply assigned the submitted values.# If the model does not have a matching attribute, the key/value pair is# assigned as a stub return value using RSpec's mocking/stubbing# framework.## <tt>persisted?</tt> is overridden to return the result of !id.nil?# This means that by default persisted? will return true. If you want# the object to behave as a new record, sending it `as_new_record` will# set the id to nil. You can also explicitly set :id => nil, in which# case persisted? will return false, but using `as_new_record` makes the# example a bit more descriptive.## While you can use stub_model in any example (model, view, controller,# helper), it is especially useful in view examples, which are# inherently more state-based than interaction-based.## @example## stub_model(Person)# stub_model(Person).as_new_record# stub_model(Person, :to_param => 37)# stub_model(Person) {|person| person.first_name = "David"}defstub_model(model_class,stubs={})model_class.new.tapdo|m|m.extendActiveModelStubExtensionsifdefined?(ActiveRecord)&&model_class<ActiveRecord::Basem.extendActiveRecordStubExtensionsprimary_key=model_class.primary_key.to_symstubs=stubs.reverse_merge(primary_key=>next_id)stubs=stubs.reverse_merge(:persisted?=>!!stubs[primary_key])elsestubs=stubs.reverse_merge(:id=>next_id)stubs=stubs.reverse_merge(:persisted?=>!!stubs[:id])endstubs=stubs.reverse_merge(:blank?=>false)stubs.eachdo|message,return_value|ifm.respond_to?("#{message}=")m.__send__("#{message}=",return_value)elseRSpec::Mocks.allow_message(m,message).and_return(return_value)endendyieldmifblock_given?endendprivate@@model_id=1000defnext_id@@model_id+=1endendendendRSpec.configuration.includeRSpec::Rails::Mocks