Confusion Abounds with FactoryGirl: has_many through - ruby-on-rails

I am having a hard time figuring out what this error means exactly:
An error occurred in a `before(:suite)` hook.
Failure/Error: FactoryGirl.lint
FactoryGirl::InvalidFactoryError:
The following factories are invalid:
* question - Validation failed: Option must
exist, Question must exist (ActiveRecord::RecordInvalid)
These are the factories:
# question has many options through quiz
FactoryGirl.define do
factory :question, class: 'Question' do
option "What color are your eyes"
end
end
# option has many questions through quiz
FactoryGirl.define do
factory :option, class: 'Option' do
option "blue"
end
end
# JoinTable
FactoryGirl.define do
factory :quiz, class: 'Quiz' do
option nil
question nil
end
end
My guess is that the nil right next to the associations in Quiz factory has something to do with the error. I tried to read through FactoryGirl docs to see how to create the right associations but I do not understand certain things. For example:
am I supposed to create the associations in the factory for the join table (Question) only?
more importantly, since I want to first create a question and then tell the question what options it has, how do I achieve this in the Factory?
Thank you!
Edit:
Option Model:
class Option < ApplicationRecord
has_many :quizzes
has_many :questions, through: :quizzes
end
Question Model
class Question < ApplicationRecord
has_many :quizzes
has_many :options, through: :quizzes
end
Quiz Model
class Some::QuizQuestion < ApplicationRecord
belongs_to :questions
belongs_to :options
end

I think you need to add the class_name option to your relationships, I am pretty sure Rails doesn't deal with the Some namespace the way you're expecting it to.
For example:
class Some::QuizQuestion < ApplicationRecord
belongs_to :some_questions, class_name: 'Some::Question'
belongs_to :some_questions, through: :some_quiz_questions
end
Also, that second belongs_to doesn't make sense afaict, you are redefining some_questions right after defining it.

Forgot to post the solution to this question.
Main issue/question was:
why was the validation for the join table failing/returning error below:
* question - Validation failed: Option must
exist, Question must exist (ActiveRecord::RecordInvalid)
As I suspected, it was because of the nil value next to both of the associations in QuizQuestion factory:
FactoryGirl.define do
factory :quiz, class: 'Question' do
option nil
question nil
end
end
Solution was to get rid of the nil value.

Related

"uninitialized constant" error with a a different name of the class while accessing has_many through fields

For this piece of code at my partial:
<% if build.optical_drives.blank? %>
I am getting this error:
uninitialized constant Build::OpticalDrife
Although I didn't mistype OpticalDrife anywhere.
I have this following relationship:
class OpticalDrive < ActiveRecord::Base
has_many :optical_drive_builds
has_many :optical_drives, through: :optical_drive_builds
end
class OpticalDriveBuild < ActiveRecord::Base
has_many :optical_drives
# has_many :builds
end
class Build < ActiveRecord::Base
has_many :optical_drive_builds
has_many :optical_drives, :through => :optical_drive_builds
end
We have a option called :class_name :
Specify the class name of the association. Use it only if that name can’t be inferred from the association name. So has_many :products will by default be linked to the Product class, but if the real class name is SpecialProduct, you’ll have to specify it with this option.
has_many :optical_drives, :through => :optical_drive_builds, class_name: 'OpticalDrive'
It happened due to the singularization and pluralization error of rails. As word like knives has a singular form knife, rails mistakenly singularized drives as drife.
I have fixed this problem by adding following in my config/initializers/inflection.rb:
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.irregular 'drive', 'drives'
end
More at: http://www.sadafnoor.com/blog/interesting-problem-fixed-with-rails-inflections/
Inflections
To give context as to why this error appears, the clue is in the name:
Build::OpticalDrife
This would only happen if Rails is taking drives and trying to turn it singular.
Although this works well in most cases, the fact remains that it can sometimes get it wrong.
The workaround is to define the class_name explicitly:
has_many :optical_drives, through: :optical_drive_builds, class_name: "OpticalDrive"
This sets the class_name explicitly, which will save the interpolation issue.
However, the real way to fix it is to define the interpolation in your app (sadaf2605's answer) and this:
#config/locales/en.yml
en:
activerecord:
models:
optical_drive: OpticalDrive

Can't validate children of a has_many through relation directly

I have the following setup:
class Round < ActiveRecord::Base
has_many :cards_rounds
has_many :cards, through: :cards_rounds
accepts_nested_attributes_for :cards_rounds
validate :round_validations
private
def round_validations
// pry
unless cards.map(&:id).uniq.size == 3
errors.add(:round, "Must have 3 unique cards")
end
unless cards.map(&:quality).uniq.size == 1
errors.add(:round, "Cards must be of the same rarity")
end
end
end
class CardsRound < ActiveRecord::Base
belongs_to :card
belongs_to :round
end
class Card < ActiveRecord::Base
has_many :cards_rounds
has_many :rounds, through: :cards_rounds
end
Round always fails to validate on creation. When I step in using pry, I can see that cards is nil, but cards_rounds is populated and I can call cards_rounds[0].card (for example).
Is this the expected behaviour? It seems odd to me that I can reference the cards through cards_rounds but not directly as a collection.
Rails version is 4.0.1
Yeah I feel your pain. I'm not sure if this is the expected behavior but I've came into this same problem some times.
The best think you can do is to open an issue on https://github.com/rails/rails
Not sure where the problem is, I did the following gist which the tests pass: https://gist.github.com/arthurnn/9607180. See that I changed the validation. The problem is that the validation cannot rely on mapping the ids from cards, as they dont have an id yet.

Testing Rails Deep Nested Attributes with RSPEC and FactoryGirl

I need some help getting my factory_girl settings correct on this has one through many with nested attributes. Here's the three models for reference.
location.rb
class Location < ActiveRecord::Base
has_many :person_locations
has_many :people, through: :person_locations
end
person_location.rb
class PersonLocation < ActiveRecord::Base
belongs_to :person
belongs_to :location
accepts_nested_attributes_for :location, reject_if: :all_blank
end
person.rb
class Person < ActiveRecord::Base
has_many :person_locations
has_many :locations, through: :person_locations
accepts_nested_attributes_for :person_locations, reject_if: :all_blank
end
Notice that locations is nested under the person record, but it needs to go through two models to be nested. I can get the tests working like this:
it "creates the objects and can be called via rails syntax" do
Location.all.count.should == 0
#person = FactoryGirl.create(:person)
#location = FactoryGirl.create(:location)
#person_location = FactoryGirl.create(:person_location, person: #person, location: #location)
#person.locations.count.should == 1
#location.people.count.should == 1
Location.all.count.should == 1
end
I should be able to create all three of these records within one line but haven't figured out how to yet. Here's the structure I would like to have work correctly :
factory :person do
...
trait :location_1 do
person_locations_attributes { location_attributes { FactoryGirl.attributes_for(:location, :location_1) } }
end
end
I have other models which are able to create via a similar syntax, but it has only one nested attribute versus this deeper nesting.
As entered above, I get the following error:
FactoryGirl.create(:person, :location_1)
undefined method `location_attributes' for #<FactoryGirl::SyntaxRunner:0x007fd65102a380>
Furthermore, I want to be able to test properly my controller setup for creating a new user with nested location. It will be tough to do this if I can't get the call down to one line.
Thanks for your help!! Hopefully I provided enough above to help others as well when they are creating a has many through relationship with nested attributes.
A couple of days later I figured it out after reading blog 1 and blog 2. In the process of refactoring all of my FactoryGirl code now.
FactoryGirl should look as follows:
factory :person do
...
trait :location_1 do
after(:create) do |person, evaluator|
create(:person_location, :location_1, person: person)
end
end
end
The person_location factory should be pretty straight forward then following the above code. You can either do the location_attributes which is in the original question or create a similar block to this answer to handle it there.

FactoryGirl has_many association with validation

I have a standard has_many relationship (Booking has many Orders) with validation that a Booking does not get saved without at least one Order. I'm trying to replicate this with my FactoryGirl factories but the validation is preventing me from doing so.
class Booking < ActiveRecord::Base
has_many :orders
validates :orders, presence: true
end
class Order < ActiveRecord::Base
belongs_to :booking
end
Here are my FactoyGirl factory specifications for each model as followed from FactoryGirl's GitHub wiki page.
FactoryGirl.define do
factory :booking do
factory :booking_with_orders do
ignore do
orders_count 1
end
before(:create) do |booking, evaluator|
FactoryGirl.create_list(:order, evaluator.orders_count, booking: booking)
end
end
end
factory :order do
booking
end
end
When I try to run FactoryGirl.create(:booking_with_orders) from my spec, I get:
Failure/Error: #booking = FactoryGirl.create(:booking_with_orders)
ActiveRecord::RecordInvalid:
Validation failed: Orders can't be blank
It seems like the check for the validation is running even before before(:create) [...] which would theoretically create the Orders for the Booking.
This post recommends not adding has_many relationships to your factories but I would like to solve this anyways if there is a good way to do it.
Thanks in advance.
Wat? Impossible? Not at all.
Just change your code to something like this:
after :build do |booking, evaluator|
booking.orders << FactoryGirl.build_list(:order, evaluator.orders_count, booking: nil)
end
Taking off from #jassa's answer, if you just need to add a single (required) associated record with a specific attribute, this pattern worked for me:
factory :booking do
ignore do
order_name "name"
end
after :build do |factory, evaluator|
factory.orders << FactoryGirl.build(:order, name: evaluator.order_name, booking: nil)
end
end
This seems like an overly simplistic observation but what you're trying to do is in effect make sure that the Order exists before the Booking, which is impossible, as the Order cannot exist without its booking_id (meaning that the Booking needs to be created first).
There's nothing wrong with a has_many relationship in your factories, it's your validation that is a problem. Does this currently work in your application? How do you save your records in that case? What is the flow for creating Orders and Bookings?
Even the infamous accepts_nested_attributes_for won't help you here.
My advice is to rethink your record saving and validation strategy so that it is a little more sane.

Problem with nested associations in Rails3

I'm having a problem with some associations in Rails3.
I have the following associations
InformationCategory :has_many => Informations :has_many => InformationValues
I'm able to succesfully do the following:
Information.first.information_values
=> [#InformationValue..]
I'm also able to do the following:
InformationCategory.first.informations
=> [#Information...]
However, for some reason, this fails:
InformationCategory.first.informations.first.information_values
=> NoMethodError: undefined method `information_values' for #<Information:0x000001053321c8>
Why can't I use 'nested associations' in Rails? The error message clearly states that InformationCategory.first.informations.first returns an instance of Information
Am I doing something wrong?
Thanks in advance!
You don't define all the nested descendants on outermost model: each model defines what it "directly" has_many of, or the model it belongs_to. Because your question is wrong, I can only guess without seeing more specifically how your models are supposed to be related.
This might be a start:
class InformationCategory < ActiveRecord::Base
has_many :informations
end
class Information < ActiveRecord::Base
belongs_to :information_category
has_many :information_values
end
class InformationValue < ActiveRecord::Base
belongs_to :information
end
However, you might be trying to do a has_many :through, but I can't tell from your question.

Resources