Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Now able to specify :method => :build in a factory's association.

See issue #64.
  • Loading branch information...
commit 90374818a77e20c768c6b8415bdfdeb59304aa2b 1 parent d22e0d8
Jim Kingdon authored
View
20 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
-----------
View
23 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
View
6 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
View
8 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)
View
31 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
View
27 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
View
64 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
View
24 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
View
2  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 }
View
29 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)

5 comments on commit 9037481

@aaronjensen

Curious why not just use the method used when the Factory was invoked? If I say build, build it and all associations, if I say create, create the entire hierarchy. As it is now I'd have to have separately factories if I want to be able to create and build.

@joshuaclayton

Yeah, this got merged without me taking a look over it. @nzkoz had suggested :method => :build but I think it should be inferred from the way the factory is called (create, build, or build_stubbed). I'm not going to revert this commit, since it's a step in the right direction, but I'll talk to @jferris and try to get a better grasp why it can't be inferred, if that's actually the case.

@cgriego

@joshuaclayton Did you find out anything on if the method can be inferred? The comment added to Proxy::Create in this commit suggests that not overriding the method has no impact, and that may or may not be true in ActiveRecord, but it certainly has an impact in other ORMs like Mongoid.

@gamov

Yes indeed, this doesn't really help because now you have to create two different factories (one for where the association is 'normal' and another one where you state :method => :build).

Still, the foreign key validation problem when you want to save the build model is not solved (validates_presence_of :user_id fails because the associated model user doesn't have an id yet).

Please sign in to comment.
Something went wrong with that request. Please try again.