diff --git a/GETTING_STARTED.md b/GETTING_STARTED.md index 8e92b2b0c..480d6992d 100644 --- a/GETTING_STARTED.md +++ b/GETTING_STARTED.md @@ -194,13 +194,25 @@ The behavior of the association method varies depending on the build strategy us # Builds and saves a User and a Post post = FactoryGirl.create(:post) - post.new_record? # => false - post.author.new_record # => false + post.new_record? # => false + post.author.new_record? # => false # Builds and saves a User, and then builds but does not save a Post post = FactoryGirl.build(:post) - post.new_record? # => true - post.author.new_record # => false + post.new_record? # => true + post.author.new_record? # => false + +To not save the associated object, specify :method => :build in the factory: + + factory :post do + # ... + association :author, :factory => :user, :method => :build + end + + # Builds a User, and then builds a Post, but does not save either + post = FactoryGirl.build(:post) + post.new_record? # => true + post.author.new_record? # => true Inheritance ----------- diff --git a/lib/factory_girl/proxy/build.rb b/lib/factory_girl/proxy/build.rb index 25aea5825..55d4a1063 100644 --- a/lib/factory_girl/proxy/build.rb +++ b/lib/factory_girl/proxy/build.rb @@ -23,19 +23,38 @@ def set(attribute, value, ignored = false) end def associate(name, factory_name, overrides) + method = get_method(overrides[:method]) factory = FactoryGirl.factory_by_name(factory_name) - set(name, factory.run(Proxy::Create, overrides)) + set(name, factory.run(method, remove_method(overrides))) end def association(factory_name, overrides = {}) + method = get_method(overrides[:method]) factory = FactoryGirl.factory_by_name(factory_name) - factory.run(Proxy::Create, overrides) + factory.run(method, remove_method(overrides)) + end + + def remove_method(overrides) + overrides.dup.delete_if {|key, value| key == :method} end def result(to_create) run_callbacks(:after_build) @instance end + + def parse_method(method) + method ||= :create + if :build == method + return Proxy::Build + elsif :create == method + return Proxy::Create + else + raise "unrecognized method #{method}" + end + end + + alias_method :get_method, :parse_method end end end diff --git a/lib/factory_girl/proxy/create.rb b/lib/factory_girl/proxy/create.rb index 1e8ddab3c..d049e1e4e 100644 --- a/lib/factory_girl/proxy/create.rb +++ b/lib/factory_girl/proxy/create.rb @@ -11,6 +11,12 @@ def result(to_create) run_callbacks(:after_create) @instance end + + def get_method(method_string) + # Leaving this as Proxy::Build in the :method => :build case + # is a bit strange, but does it have any user-visible behaviors? + parse_method(method_string) + end end end end diff --git a/lib/factory_girl/proxy/stub.rb b/lib/factory_girl/proxy/stub.rb index 73563c960..26aba5972 100644 --- a/lib/factory_girl/proxy/stub.rb +++ b/lib/factory_girl/proxy/stub.rb @@ -60,12 +60,16 @@ def set(attribute, value, ignored = false) def associate(name, factory_name, overrides) factory = FactoryGirl.factory_by_name(factory_name) - set(name, factory.run(Proxy::Stub, overrides)) + set(name, factory.run(Proxy::Stub, remove_method(overrides))) end def association(factory_name, overrides = {}) factory = FactoryGirl.factory_by_name(factory_name) - factory.run(Proxy::Stub, overrides) + factory.run(Proxy::Stub, remove_method(overrides)) + end + + def remove_method(overrides) + overrides.dup.delete_if {|key, value| key == :method} end def result(to_create) diff --git a/spec/acceptance/build_spec.rb b/spec/acceptance/build_spec.rb index 8acfd4c69..ac95a35e3 100644 --- a/spec/acceptance/build_spec.rb +++ b/spec/acceptance/build_spec.rb @@ -31,3 +31,34 @@ end end +describe "a built instance with :method => :build" do + include FactoryGirl::Syntax::Methods + + before do + define_model('User') + + define_model('Post', :user_id => :integer) do + belongs_to :user + end + + FactoryGirl.define do + factory :user + + factory :post do + association(:user, :method => :build) + end + end + end + + subject { build(:post) } + + it "isn't saved" do + should be_new_record + end + + it "assigns but does not save associations" do + subject.user.should be_kind_of(User) + subject.user.should be_new_record + end + +end diff --git a/spec/acceptance/create_spec.rb b/spec/acceptance/create_spec.rb index c3cb9c880..e7a881142 100644 --- a/spec/acceptance/create_spec.rb +++ b/spec/acceptance/create_spec.rb @@ -31,6 +31,33 @@ end end +describe "a created instance, specifying :method => build" do + include FactoryGirl::Syntax::Methods + + before do + define_model('User') + + define_model('Post', :user_id => :integer) do + belongs_to :user + end + + FactoryGirl.define do + factory :user + + factory :post do + association(:user, :method => :build) + end + end + end + + subject { create('post') } + + it "still saves associations (:method => :build only affects build, not create)" do + subject.user.should be_kind_of(User) + subject.user.should_not be_new_record + end +end + describe "a custom create" do include FactoryGirl::Syntax::Methods diff --git a/spec/acceptance/stub_spec.rb b/spec/acceptance/stub_spec.rb new file mode 100644 index 000000000..2efa96137 --- /dev/null +++ b/spec/acceptance/stub_spec.rb @@ -0,0 +1,64 @@ +require 'spec_helper' + +describe "a stubbed instance" do + include FactoryGirl::Syntax::Methods + + before do + define_model('User') + + define_model('Post', :user_id => :integer) do + belongs_to :user + end + + FactoryGirl.define do + factory :user + + factory :post do + user + end + end + end + + subject { build_stubbed(:post) } + + it "acts as if it came from the database" do + should_not be_new_record + end + + it "assigns associations and acts as if it is saved" do + subject.user.should be_kind_of(User) + subject.user.should_not be_new_record + end +end + +describe "a stubbed instance with :method => :build" do + include FactoryGirl::Syntax::Methods + + before do + define_model('User') + + define_model('Post', :user_id => :integer) do + belongs_to :user + end + + FactoryGirl.define do + factory :user + + factory :post do + association(:user, :method => :build) + end + end + end + + subject { build_stubbed(:post) } + + it "acts as if it is saved in the database" do + should_not be_new_record + end + + it "assigns associations and acts as if it is saved" do + subject.user.should be_kind_of(User) + subject.user.should_not be_new_record + end + +end diff --git a/spec/factory_girl/proxy/build_spec.rb b/spec/factory_girl/proxy/build_spec.rb index 18bcb0fa9..a81f6cb40 100644 --- a/spec/factory_girl/proxy/build_spec.rb +++ b/spec/factory_girl/proxy/build_spec.rb @@ -9,4 +9,28 @@ it_should_behave_like "proxy with association support", FactoryGirl::Proxy::Create it_should_behave_like "proxy with standard getters and setters", :attribute_name, "attribute value!" it_should_behave_like "proxy with callbacks", :after_build + it_should_behave_like "proxy with :method => :build", + FactoryGirl::Proxy::Build + + describe "specifying method" do + it "defaults to create" do + subject.send(:get_method, nil).should == FactoryGirl::Proxy::Create + end + + it "can specify create explicitly" do + subject.send(:get_method, :create).should == + FactoryGirl::Proxy::Create + end + + it "can specify build explicitly" do + subject.send(:get_method, :build).should == + FactoryGirl::Proxy::Build + end + + it "complains if method is unrecognized" do + lambda { subject.send(:get_method, :froboznicate) }. + should raise_error("unrecognized method froboznicate") + end + end + end diff --git a/spec/factory_girl/proxy/stub_spec.rb b/spec/factory_girl/proxy/stub_spec.rb index f23d121bf..77ad68f50 100644 --- a/spec/factory_girl/proxy/stub_spec.rb +++ b/spec/factory_girl/proxy/stub_spec.rb @@ -9,6 +9,8 @@ it_should_behave_like "proxy with association support", FactoryGirl::Proxy::Stub it_should_behave_like "proxy with standard getters and setters", :attribute_name, "attribute value!" it_should_behave_like "proxy with callbacks", :after_stub + it_should_behave_like "proxy with :method => :build", + FactoryGirl::Proxy::Stub context "asking for a result" do it { subject.result(nil).should_not be_new_record } diff --git a/spec/support/shared_examples/proxy.rb b/spec/support/shared_examples/proxy.rb index 73325e3fe..2094f66b4 100644 --- a/spec/support/shared_examples/proxy.rb +++ b/spec/support/shared_examples/proxy.rb @@ -49,6 +49,35 @@ end end +shared_examples_for "proxy with :method => :build" do |factory_girl_proxy_class| + let(:factory_name) { :user } + let(:association_name) { :owner } + let(:factory) { stub("associate_factory") } + let(:overrides) { { :method => :build } } + + before do + FactoryGirl.stubs(:factory_by_name => factory) + instance.stubs(association_name => factory_name) + factory.stubs(:run => factory_name) + subject.stubs(:set) + end + + it "sets a value for the association" do + subject.associate(association_name, factory_name, overrides) + subject.result(nil).send(association_name).should == factory_name + end + + it "sets the association attribute as the factory" do + subject.associate(association_name, factory_name, overrides) + subject.should have_received(:set).with(association_name, factory_name) + end + + it "runs the factory with the correct proxy class" do + subject.associate(association_name, factory_name, overrides) + factory.should have_received(:run).with(factory_girl_proxy_class, {}) + end +end + shared_examples_for "proxy with standard getters and setters" do |attribute, value| before do instance.stubs(:"#{attribute}=" => value, :"#{attribute}" => value)