Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

after(:build) callback should be triggered prior to save #649

Closed
rymohr opened this issue Apr 22, 2014 · 6 comments
Closed

after(:build) callback should be triggered prior to save #649

rymohr opened this issue Apr 22, 2014 · 6 comments

Comments

@rymohr
Copy link

rymohr commented Apr 22, 2014

Perhaps I'm misusing the callbacks, but shouldn't the after(:build) callback allow you to finalize associations and attributes prior to saving?

I have a block I want to run regardless of whether build or create is used. Here's a simplified example:

factory :account do
  ignore do
    locked false
  end

  after(:build) do |account, options|
    account.user ||= build(:user)
    account.user.status = :unpaid if options.locked
  end
end

If I call FactoryGirl.create(:account, :locked => true) it tries to save the account before the :build callback is ever triggered.

@joshuaclayton
Copy link
Contributor

@rymohr Can you provide any more context? I just wrote up this quick spec:

describe "callbacks" do
  before do
    define_model('User') do
      before_save do |user|
        puts '[ActiveRecord before_save] saving user...'
      end
    end

    FactoryGirl.define do
      factory :user do
        after :build do |user|
          puts '[FactoryGirl after(:build)] saving user...'
        end
      end
    end
  end

  it 'works' do
    FactoryGirl.create :user
  end
end
[ ~/dev/gems/factory_girl master ] ✔ rspec spec/acceptance/create_spec.rb:138                                                                         ☺ 2.1.1
Run options: include {:locations=>{"./spec/acceptance/create_spec.rb"=>[138]}}
[FactoryGirl after(:build)] saving user...
[ActiveRecord before_save] saving user...
.

Finished in 0.14022 seconds
1 example, 0 failures

Which looks to be behaving correctly. Any insights?

@rymohr
Copy link
Author

rymohr commented May 1, 2014

You'll only see this behavior when working with associations.

# spec/acceptance/association_callbacks_spec.rb 
require "spec_helper"

describe "callbacks" do
  before do
    define_model('User') do
      before_save do |user|
        puts 'before_save user'
      end
    end

    define_model('Account', :user_id => :integer) do
      belongs_to :user

      before_save do |account|
        puts 'before_save account'
      end
    end

    FactoryGirl.define do
      factory :user do
        after :build do |user|
          puts 'after(:build) user'
        end
      end

      factory :account do
        user

        after(:build) do |account|
          # account.user has already been saved
          puts "after(:build) account"
        end
      end
    end
  end

  it 'works' do
    FactoryGirl.create :account
  end
end
$ rspec spec/acceptance/association_callbacks_spec.rb 
after(:build) user
before_save user
after(:build) account
before_save account
.

Finished in 0.38016 seconds
1 example, 0 failures

I would have expected this output instead:

after(:build) user
after(:build) account
before_save user
before_save account

@joshuaclayton
Copy link
Contributor

@rymohr so, this is actually due to how factory_girl and ActiveRecord interact. factory_girl, when building objects, saves the associations. Because the association is saved, that's why you're seeing the order of the factory_girl callback on user, then AR (as AR is saving the associated user), and then the callbacks on the factory you're actually creating (the account).

@rymohr
Copy link
Author

rymohr commented May 2, 2014

Is that by design or would you accept a pull that deferred saving associations?

@joshuaclayton
Copy link
Contributor

It does that by design; we'd run into numerous issues over the course of years by not saving associated objects when building the parent. Many stem from interacting with the associations on the built factory.

On May 2, 2014, at 16:37, Ryan Mohr notifications@github.com wrote:

Is that by design or would you accept a pull that deferred saving associations?


Reply to this email directly or view it on GitHub.

@lsantobuono
Copy link

Hi guys sory for bringing this up, facing this same issue.
I need the association to be created before the after_save callbacks are called, because one of those uses the associations to make an operation. This works fine within a controller with nested attributes.
Is there any way to simulate the behaviour while testing?
thanks in advance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants