Rails FactoryGirl trait association with model aft

2019-03-01 10:24发布

问题:

In my model, I have an after_create callback that triggers the method:

class Job < ActiveRecord::Base
  after_create :update_vanity_url

  private

  def update_vanity_url
    self.vanity_url = '/jobs/' + company.slug + '/' + slug + '/' + id.to_s + '/'
  end
end

This sets up a custom url for my jobs, however, when I try to use this in my coupon factory it is not being saved. The coupon is created without a job assigned. Only when the coupon is used is it paired with one job. I referred to that as executed:

FactoryGirl.define do
  factory :coupon do
    code { rand(25**25) }
    percent_discount { rand(100**1) }
    start_at { Time.now }
    end_at { 30.day.from_now }

    trait :executed do |c|
      association :job, factory: [:job, :purchased] 
      c.executed_at { Time.now }
    end
  end
end

Ideally, I would like to be able to call FactoryGirl.create(:coupon, :executed) which works but the after_create is never called... Thoughts?

More details of this setup are covered here Rails FactoryGirl Factory with optional model association

Per issue comments below, I have added my routes section and updates:

Routes

  resources :jobs, only: [:new] do
    collection do
      post 'new', to: 'jobs#create'
    end
    get '/review', to: 'reviews#new'
    patch '/review', to: 'reviews#update'
    get '/payment', to: 'payments#new'
    patch '/payment', to: 'payments#update'
  end
  match '/jobs/:company_slug/:job_slug/:id', via: :get, to: 'jobs#show'

回答1:

I researched this for hours, and this is what I found to work. It turns out it was not FactoryGirl, rather the model itself... I think...

  def update_vanity_url
    self.vanity_url = '/jobs/' + company.slug + '/' + slug + '/' + id.to_s + '/'
    save
  end


回答2:

Your actual question

The problem you're asking about is more a symptom or side effect of how you're doing your lookup.

tl;dr: The vanity_url attribute is not being saved because update_vanity_url is set on an after_create callback when the record has already been written to the database. It is setting it in memory which is why your tests might've been tripped up by this.

That's why putting save in the callback fixed your issue. It's simply saving a Job a second time after it's created. This is necessary because you wouldn't have the id before it was written to the database.

Maybe a better solution?

Are you doing anything with the vanity_url attribute besides routing to it? I ask because unless you're denormalizing this for speed you may not need to store this on the model at all.

The only thing I came up with is that you might be using those params to construct the vanity_url and doing something like @job = Job.where(vanity_url: vanity_url).first or something.