Traversing associations before create

187 views
Skip to first unread message

Byo Fuel

unread,
Dec 16, 2011, 1:58:58 PM12/16/11
to factor...@googlegroups.com
Hi -- We're trying to use FG to build state that exists across multiple models associated with each other in multiple ways.  Our plan was to do this by building all the models first, then saving the parent model (whose has_many associations will in turn save associated models).  But some models need multiple values populated that represent the IDs of other models (that are not necessarily defined as ActiveRecord Associations).  We're trying to figure out how to duplicate the data integrity rules in FG that are easily enforced with AR.  Here are our models and factories:

Order
  belongs_to :client
  has_many :documents

Document
  belongs_to :order
  has_many :document_uploads

DocumentUpload
  belongs_to :document
  validates_presence_of   :by_client_id

factory :order_uploaded, :class => Order do |o|
    o.association :client, :factory => :client_x
    o.last_name { client.last_name }
    o.after_build do |order|
      order.documents = FactoryGirl.build_list(:document_uploaded, 1)
    end
end

factory :document_uploaded, :class => Document do |d|
    #d.association :order, :factory => :order_uploaded, :method => :build
    ignore do
      order nil
    end
   
    #d.after_build do |doc|
    #  doc.uploads = FactoryGirl.build_list(:document_upload_from_client_x, 1, :document => doc )
    #end
    # Error on the following line: NoMethodError: undefined method `client' for nil:NilClass
    d.uploads {|u| [u.association(:document_upload_from_client_x, :document => d, :by_client_id => u.order.client.id)]}
end

factory :document_upload_from_client_x, :class => DocumentUpload do |du|
    du.association :document
    #Error on the following line: NoMethodError: undefined method `client' for #<FactoryGirl::Declaration::Implicit:0x106499e48>
    after_build { |obj| obj.by_client_id = obj.document.order.client.id }
end

I've verified that as the Order attributes are being evaluated by FG, the client has already been persisted and we can get its ID.  How can we pass that ID down to the DocumentUpload factory?  The challenge is that the DocumentUpload is not valid without that ID, and the codebase will not accept mocking the value.

Thanks much!

Peter Vandenabeele

unread,
Dec 17, 2011, 1:44:26 PM12/17/11
to factor...@googlegroups.com
I think I found a solution. Was a bit of work and essentially a rework.

The main guide here is also to build small build tests along the way,
so you can be sure what exactly happens.

Also, to allow "build" (to not write to the database during tests), use
the belongs_to association (DocumentUpload belongs_to :by_client)
and test the association present. Don't manually play with by_client_id,
since that will require saving or fetching from the database to test.

The main test is this one and it passes:

peterv@ASUS:~/data/backed_up/rails-apps/apps/temp/fg/spec/model/document_upload$ cat factory_spec.rb 
require 'spec_helper'

describe "DocumentUpload" do

  it "builds a document" do
    FactoryGirl.build(:document_upload)
  end

  it "builds a document_upload with a document" do
    du = FactoryGirl.build(:document_upload_with_document)
    du.document.should_not be_nil
  end

  it "builds a document_upload with a document and an order" do
    du = FactoryGirl.build(:document_upload_with_document_and_order)
    du.document.should_not be_nil
    du.document.order.should_not be_nil
  end

  it "builds a document_upload with a document and an order and a client x" do
    du = FactoryGirl.build(:document_upload_with_document_and_order_and_client_x)
    du.document.should_not be_nil
    du.document.order.should_not be_nil
    du.document.order.client.should_not be_nil
    du.document.order.client.last_name.should == "X"
    du.by_client.should_not be_nil
  end

end

The detailed code is here:


HTH,

Peter

--
Peter Vandenabeele
http://twitter.com/peter_v
Reply all
Reply to author
Forward
0 new messages