Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

add :validate and :callbacks options to ModelStubbing::Definition

  • Loading branch information...
commit 1b0790e0caf31679389077531db98af7f991551e 1 parent 449422e
@technoweenie authored
View
5 lib/model_stubbing.rb
@@ -19,11 +19,16 @@ def self.records() @records ||= {} end
# no name is given, it defaults to the current class or :default. Multiple
# #define_models calls with the same name will modify the definition.
#
+ # By default, only validations are run before inserting records. You can
+ # configure this with the :validate or :callbacks options.
+ #
# Options:
# * :copy - set to false if you don't want this definition to be a dup of
# the :default definition
# * :insert - set to false if you don't want to insert this definition
# into the database.
+ # * :validate - set to false if you don't want to validate model data, or run callbacks
+ # * :callbacks - set to true if you want to run callbacks.
def define_models(name = nil, options = {}, &block)
if name.is_a? Hash
options = name
View
9 lib/model_stubbing/definition.rb
@@ -3,11 +3,8 @@ module ModelStubbing
# can set the current time for your tests. You typically create one per test case or
# rspec example.
class Definition
- attr_writer :insert
- attr_writer :current_time
- attr_reader :models
- attr_reader :stubs
- attr_reader :ordered_models
+ attr_writer :insert, :current_time
+ attr_reader :models, :stubs, :ordered_models, :options
# Sets the time that Time.now is mocked to (in UTC)
def time(*args)
@@ -66,8 +63,10 @@ def ==(defn)
# Shortcut methods for each model are generated as well. users(:default) accesses
# the default user stub, and users(:admin) accesses the 'admin' user stub.
def setup_on(base, options = {}, &block)
+ options = {:validate => true, :insert => true}.update(options)
self.insert = false if options[:insert] == false
self.instance_eval(&block) if block
+ @options = options
if base.ancestors.any? { |a| a.to_s == "Test::Unit::TestCase" || a.to_s == "Spec::Example::ExampleGroup" }
unless base.ancestors.include?(ModelStubbing::Extension)
base.send :include, ModelStubbing::Extension
View
12 lib/model_stubbing/model.rb
@@ -1,12 +1,8 @@
module ModelStubbing
# Models hold one or more stubs.
class Model
- attr_reader :definition
- attr_accessor :name
- attr_accessor :plural
- attr_accessor :singular
- attr_reader :stubs
- attr_reader :model_class
+ attr_accessor :name, :plural, :singular
+ attr_reader :definition, :stubs, :model_class
# Creates a stub for this model. A stub with no name is assumed to be the default
# stub. A global key for the definition is also created based on the singular
@@ -85,8 +81,8 @@ def retrieve_record(key, attributes = {})
def stub_method_definition
"def #{@plural}(key, attrs = {}) self.class.definition.models[#{@plural.inspect}].retrieve_record(key, attrs) end\n
- def new_#{@singular}(key = :default, attrs = {}) key, attrs = :default, key if key.is_a?(Hash) ; #{@plural}(key, attrs.merge(:id => :new)) end\n
- def new_#{@singular}!(key = :default, attrs = {}) stub = new_#{@singular}(key, attrs) ; stub.save! ; stub end"
+ def new_#{@singular}(key = :default, attrs = {}) key, attrs = :default, key if key.is_a?(Hash) ; #{@plural}(key, attrs.merge(:id => :new)) end\n
+ def new_#{@singular}!(key = :default, attrs = {}) key, attrs = :default, key if key.is_a?(Hash) ; #{@plural}(key, attrs.merge(:id => :dup)) end"
end
def inspect
View
30 lib/model_stubbing/stub.rb
@@ -4,10 +4,7 @@ module ModelStubbing
# will return the exact same instance. However, custom attributes
# will create unique stub instances.
class Stub
- attr_reader :model
- attr_reader :attributes
- attr_reader :global_key
- attr_accessor :name
+ attr_reader :model, :attributes, :global_key, :name
# Creates a new stub. If it's not the default, it inherits the default
# stub's attributes.
@@ -52,8 +49,14 @@ def inspect
def insert(attributes = {})
@inserting = true
object = record(attributes)
- object.new_record = true # record could have returned one from the cache
- object.save!
+ if model.definition.options[:callbacks]
+ object.new_record = true # record could have returned one from the cache
+ object.save!
+ elsif !model.definition.options[:validate] || object.valid?
+ connection.insert_fixture(object.stubbed_attributes, model.model_class.table_name)
+ else
+ raise "Model data is not valid: #{object.errors.full_messages.to_sentence}"
+ end
@inserting = false
end
@@ -93,20 +96,23 @@ def connection
private
def instantiate(this_record_key, attributes)
- if attributes[:id] == :new
- is_new_record = true
- attributes.delete(:id)
+ case attributes[:id]
+ when :new
+ is_new_record = true
+ attributes.delete(:id)
+ when :dup
+ attributes[:id] = @model.model_class.base_class.mock_id
end
-
+
stubbed_attributes = stubbed_attributes(@attributes.merge(attributes))
-
+
record = @model.model_class.new
meta = class << record
attr_accessor :stubbed_attributes
attr_writer :new_record
self
end
-
+
if is_new_record
record.new_record = true
else
View
31 spec/models.rb
@@ -27,7 +27,7 @@ def quote(value, whatever)
end
class BlankModel
- attr_accessor :id
+ attr_accessor :id, :valid
attr_reader :attributes
def self.base_class
@@ -35,7 +35,11 @@ def self.base_class
end
def new_record?() @new_record end
-
+
+ def valid?
+ !!@valid
+ end
+
def initialize(attributes = {})
@attributes = attributes
attributes.each do |key, value|
@@ -59,7 +63,11 @@ def save
@new_record = false
self.id = db_id if self.id.nil?
end
- alias :save! :save
+
+ def save!
+ raise "Invalid!" unless valid?
+ save
+ end
def method_missing(name, *args)
if name.to_s =~ /(\w+)=$/
@@ -68,7 +76,18 @@ def method_missing(name, *args)
super
end
end
-
+
+ def errors
+ @errors ||= {}.instance_eval do
+ def full_messages
+ inject([]) do |msg, (key, value)|
+ msg << "#{key} #{value}"
+ end
+ end
+ self
+ end
+ end
+
private
def meta_class
@meta_class ||= class << self; self end
@@ -93,6 +112,10 @@ module Foo
Bar = Class.new BlankModel
end
+ def User.table_name
+ "users"
+ end
+
define_models do
time 2007, 6, 1
View
59 spec/stub_spec.rb
@@ -153,10 +153,65 @@ module ModelStubbing
end
end
+ describe Stub, "inserting a new record" do
+ before :all do
+ @defn = ModelStubbing.definitions[:default]
+ @model = @defn.models[:model_stubbing_users]
+ @stub = @model.default
+ @opts = @defn.options.dup
+ end
+
+ before do
+ ModelStubbing.records.clear
+ ModelStubbing.record_ids.clear
+ @defn.options.update(@opts)
+ @conn = mock("Connection")
+ @record = @stub.record
+ @stub.stub!(:record).and_return(@record)
+ @stub.stub!(:connection).and_return(@conn)
+ @inserting_record = lambda { @stub.insert }
+ end
+
+ it "inserts an invalid record without validating" do
+ @defn.options.update(:validate => false, :callbacks => false)
+ @record.valid = false
+ @conn.should_receive(:insert_fixture)
+ @inserting_record.call
+ end
+
+ it "raises error an invalid record with validation" do
+ @defn.options.update(:validate => true, :callbacks => false)
+ @record.valid = false
+ @conn.should_not_receive(:insert_fixture)
+ @inserting_record.should raise_error
+ end
+
+ it "inserts valid record with validation" do
+ @defn.options.update(:validate => true, :callbacks => false)
+ @record.valid = true
+ @conn.should_receive(:insert_fixture)
+ @inserting_record.call
+ end
+
+ it "raises error an invalid record with validation and callbacks" do
+ @defn.options.update(:validate => true, :callbacks => true)
+ @record.valid = false
+ @conn.should_not_receive(:insert_fixture)
+ @inserting_record.should raise_error
+ end
+
+ it "inserts valid record with validation and callbacks" do
+ @defn.options.update(:validate => true, :callbacks => true)
+ @record.valid = true
+ @conn.should_not_receive(:insert_fixture)
+ @inserting_record.call
+ end
+ end
+
describe Stub, "instantiating a new record" do
before :all do
- @model = ModelStubbing.definitions[:default].models[:model_stubbing_users]
- @stub = @model.default
+ @model = ModelStubbing.definitions[:default].models[:model_stubbing_users]
+ @stub = @model.default
end
before do
Please sign in to comment.
Something went wrong with that request. Please try again.