Rails 3 Factory Girl + Many to Many Relationships - ruby-on-rails-3

There aren't currently any up to date answers for this using Factory Girl 4.1 (that I could find) - how do you setup a many to many relationship inside of a factory?
For instance I have Students and Classrooms which are in a many to many relationship using a join table, so far I had the following setup:
factory :classroom do
name "Foo Class"
...
end
factory :student do
name "John Doe"
...
end
factory :student_with_classroom, :parent => :student do
after(:build) {|student| student.classrooms << classroom}
end
However this results in:
NameError:
undefined local variable or method `classroom' for #<FactoryGirl::SyntaxRunner>
My attempt was guesswork for the most part as I had no luck finding any non-deprecated syntax to accomplish this.

Actually I managed to find the answer I was looking for buried under a slew of other answers in this SO: How to create has_and_belongs_to_many associations in Factory girl
factory :classroom do
name "Foo Class"
...
end
factory :student do
name "John Doe"
...
end
factory :student_with_classroom, :parent => :student do
classrooms {[FactoryGirl.create(:classroom)]}
end

Check out this SO post: How to set up factory in FactoryGirl with has_many association. It will point you to https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md; search for has_many.

Related

How to get the child factories from a parent factory

I am using RSpec and FactoryGirl to test my Ruby-on-Rails-3 application. I am using a hierarchy of factories:
FactoryGirl.define do
factory :local_airport do
... attributes generic to a local airport
factory :heathrow do
name "London Heathrow"
iata "LHR"
end
factory :stansted do
name "Stansted"
iata "STN"
end
... more local airports
end
end
In my RSpec I sometimes want to be able to create all the child factories by specifying the parent factory. Ideally, something like:
describe Flight do
before( :each ) do
# Create the standard airports
FactoryGirl.find( :local_airport ).create_child_factories
end
end
Many thanks in advance.
You can't really tell a factory to build all it's child factories, as being a child factory just means that it inherits the attributes from the parent. But what you could do is add a trait, for example :with_child_factories. Then your factory would look like the following:
FactoryGirl.define do
factory :local_airport do
... attributes generic to a local airport
factory :heathrow do
name "London Heathrow"
iata "LHR"
end
factory :stansted do
name "Stansted"
iata "STN"
end
... more local airports
trait :with_child_factories do
after(:create) do
FactoryGirl.create(:heathrow)
FactoryGirl.create(:stansted)
...
end
end
end
end
Then you could call it in your test with
FactoryGirl.create(:local_airport, :with_child_factories)
Well after a few hours of delving in the FactoryGirl code I have found a solution. Interestingly FactoryGirl only stores a reference to the parent in the factory, and not from parents to children.
In spec/factories/factory_helper.rb:
module FactoryGirl
def self.create_child_factories( parent_factory )
FactoryGirl.factories.each do |f|
parent = f.send( :parent )
if !parent.is_a?(FactoryGirl::NullFactory) && parent.name == parent_factory
child_factory = FactoryGirl.create( f.name )
end
end
end
end
In my RSpec I can now write:
require 'spec_helper'
describe Flight do
before( :each ) do
# Create the standard airports
FactoryGirl.create_child_factories( :local_airport )
end
...
One gotcha I found is that it is best to have a factory hierarchy that is simple (i.e. two levels). I started with a three layer hierarchy and found I was generating 'abstract' factories that only existed as part of the hierarchy. I have simplified the hierarchy to two levels using traits.

Applying FactoryGirl trait to Mongoid embedded objects

I am building a Rails 3.2.11 application with Mongoid. I test with Cucumber and create test objects with FactoryGirl. I have embedded objects. I want to be able to use FactoryGirl traits with both my parent and embedded objects to make lots of permutations and keep things DRY.
The problem: I can't seem to get traits to apply to the embedded objects.
model.rb (actually, separate model files)
class User
include Mongoid::Document
#Fields and stuff
embeds_one :car
end
class Car
include Mongoid::Document
#Fields and stuff
embedded_in :user
end
spec/factories.rb
FactoryGirl.define do
factory :user do
status 'active' # shared attribute
trait :bob do
name 'Bob'
email 'bob#bob.com'
end
trait :carol do
name 'Carol'
email 'carol#carol.com'
end
car { [FactoryGirl.build(:car)] }
end
factory :car do
number_of_tires 4 # shared attribute
trait :red_ford do
make 'Ford'
color 'Red'
end
trait :blue_chevy do
make 'Chevy'
color 'Blue'
end
end
end
features/step_definitions/user_steps.rb (not working right)
Given /^There is a user Bob with a Blue Chevy$/ do
FactoryGirl.create(:user, :bob, :car => [:car => :blue_chevy])
end
I can factory create the user object just fine if I leave off the embedded object trait. Any way to get the embedded object to be built with the desired trait applied? Thanks!
The fix
Joshua Clayton at Thoughtbot http://joshuaclayton.me says:
There's no shorthand syntax for applying traits to associations;
basically, you'd have to do something like this:
cars = [FactoryGirl.build(:car, :blue_chevy)]
FactoryGirl.create(:user, :bob, cars: cars)
Because it is a 1 to 1 relationship, you do not need the array when building the car object in spec/factories.rb
car { FactoryGirl.build(:car) }
This applies in the cucumber step as well. Also, as I understand, traits are used as attributes, so your step code should look like
FactoryGirl.create(:user, :bob, :car, :blue_chevy)

Rails Form with has_many through - Which model to choose?

I have the following models:
Student has_many :subjects, :through => :classes
Subject has_many :students, :through => :classes
Class belongs_to :subject
belongs_to :student
The model class has an extra attribute (among the foreign keys to subject and students table) called level.
Basically I want to be able to have a form that will let the student to choose a subject and relate that subject to its record. So, I have this:
ClassesController < ApplicationController
def new
#list_of_subjects = Subject.all
# What should I do here?
end
My question is: How should I create the object for the form? From which model it should be, subject, student or class? I want to be able to create a record in the class table that would relate the student and the subject that the student has chosen, but I don't know if I am doing it wrong.
Thanks
I didn't think you could create a model called Class since it's a keyword, but that's neither here nor there...
First I think your controller and view should be using Student since it's the student that's selecting these things. Next, I think what you want to do is to add accepts_nested_attributes_for :class in your Student model which allows you to create an instance of the Class connector model from Student.
What you're trying to do sounds a little like something I tried to do. I have my full code there.
Using nested attributes to easily select associations in a form
I later refined it a bit in this question too to make the code less hideous:
Rails: How do I prepend or insert an association with build?
I know it's late, but I hope that helps.

Creating a factory as a different class in Ruby with FactoryGirl

I have some ActiveRecord superclass Product and a subclass DiscountedProduct that share the same table and I have some factories for the superclass that I want to use with the subclass.
Factory(:product).class #=> Product
What I am trying to find is a shorthand for:
DiscountedProduct.create(Factory.build(:product).attributes)
NOTICE:
I don't use Factory.attributes_for so that the needed associations get built.
FactoryGirl v2.3.2
Rails 3.0.4
Well, seems factory_girl supports quite well the inheritance.
You can define your factory either way :
As a nested definition
factory :product do
name 'Product name'
factory :discounted_product do
discounted true
end
end
or as a linked definition
factory :discounted_product :parent => :product do
approved true
end

Rails ActiveRecord has_one association together with Object method override

I came across a peculiar problem with has_one association in combination with an Object method override. Can somebody explain to me what is going on?
Here is how it goes:
I have a has_one relationship between Supplier and Account, like in the example of the has_one example used in Rails Guides.
Supplier:
class Supplier < ActiveRecord::Base
validates :name, :presence => true
has_one :account
nilify_blanks
end
Account:
class Account < ActiveRecord::Base
belongs_to :supplier
validates :supplier_id, :presence => true
nilify_blanks
def foo
puts 'in account'
end
def to_s
puts 'in account'
end
end
I also have a method foo on Object as follows:
class Object
def foo
puts 'in object'
end
end
When I call:
Supplier#account#to_s
I get 'in account'
When I call:
Supplier#account#foo
I get 'in object'
whereas I would expect it to print 'in account'
Does anybody have any clue why does this happen? Is this a bug in Rails ActiveRecord?
Thanks in advance
P.S. If you want, you can get a full fledged application that demonstrates the problem from here:
https://github.com/pmatsinopoulos/test_association_and_object_method_override.git
After doing some investingation with one of my friends, got the way assoiciation works.
when we do Supplier.account it will give you object of AssociationProxy not an object of account.
AssociationProxy delegates all methods to associated object if its definition not present in itself(it also delegates methods like class, inspect etc. so you can get the actual class name).
Now, when we add foo in Object class its available in AssociationProxy and when you say Supplier.account it invokes foo from AssociationProxy not from account.
if you want to invoke foo from account use target method to get actual account object like
Supplier.account.target.foo #=> foo from account