Complex assotiations in FactoryGirl mockup - ruby-on-rails-3

I need to create some complex mockups for my app where I need to reuse one of fields of model. I wanna do it like
FactoryGirl.define do
factory :invoice do
sequence(:name) { |n| "Testowa #{n}" }
full_amount 10_000
pay_date DateTime.now + 7.days
association :clienr
company client.company
end
end
But then I get error undefined methodcompany' for #`. I need it badly to test my app and I couldn't find anything in docs.

I've fixed that by making company lazy evaluated assigment
FactoryGirl.define do
factory :invoice do
sequence(:name) { |n| "Testowa #{n}" }
full_amount 10_000
pay_date DateTime.now + 7.days
association :clienr
company { client.company }
end
end

Related

Many to many relationship with ability to set a state (active)

I have a fully working (for some time now) many-to-many relationship in my Rails application.
Instructors has many Schools (through SchoolsToInstructorsAssociations)
Schools has many Instructors (through SchoolsToInstructorsAssociations)
At this time, I would like the ability to have an "active state" in addition to simply adding or removing an Instructor from a School or a School from an Instructor.
I want an Instructor to be set as inactive before being removed completely at a later point (or reactivated).
My first thought was to add an 'active' boolean to the relationship model (SchoolsToInstructorsAssociations), but there's no simple way to access this attribute to update or query it).
My second thought was to simply create another relationship model with the 'active' attribute, but it's redundant and something extra I have to track.
Maybe a custom many-to-many module? Create a SchoolsToInstructorsAssociations controller?
class Instructor < ActiveRecord::Base
has_many :schools_to_instructors_association
has_many :schools, :through => :schools_to_instructors_association
end
class School < ActiveRecord::Base
has_many :schools_to_instructors_association
has_many :instructors, :through => :schools_to_instructors_association
end
class SchoolsToInstructorsAssociation < ActiveRecord::Base
belongs_to :user
belongs_to :school
end
I also plan to create a history record each time an instructors 'active' state changes or an instructor is removed or added to a school. Not asking how to do this, but wondering if it could be used to track an instructors 'active' state.
class SchoolsController < ApplicationController
def instructors_index
#school = School.find(params[:id])
instructors = find_instructors
#active_instructors = instructors[0]
#inactive_instructors = instructors[1]
respond_to do |format|
format.html # index.html.erb
format.json { render json: #schools }
end
end
private
def find_instructors
active = []; inactive = []
#school.instructors.each do |s|
if SchoolsToInstructorsAssociationRecord.where(user_id: s, school_id: #school)[0].active?
active << s
else
inactive << s
end
return [active, inactive]
end
end
end
class SchoolsToInstructorsAssociationRecord < ActiveRecord::Base
default_scope order('created_at DESC')
attr_accessor :user_id, :school_id, schools_to_instructors_association_id, :active
end
Sounds like you can accomplish what you're trying to do with scopes. Add a boolean column for 'active' as you described for the 'Instructor' class, then you can add scopes for it:
class Instructor < ActiveRecord::Base
...
scope :active, -> { where(active: true) }
scope :inactive, -> { where(active: false) }
...
end
Then for a given School, you can get the active (or inactive) instructors for that school:
#school.instructors.active
=> SELECT "instructors".* FROM "instructors" WHERE "instructors"."school_id" = <id> AND "instructors"."active" = 't'
If you wanted to do some operations on all the inactive instructors (like destroy them, as an example), you could do:
Instructor.inactive.map(&:destroy)
And you can of course write whatever custom methods you want for the Instructor or School classes.

rails 3 - FactoryGirl create associated records

I am trying to create some test data to fill my tables with so that i can test functionality on my site.
The tables in question are: songs, song_arrangements, and song_arrangement_files. The are associated in this way.
Songs:
has_many :song_arrangements, :dependent => :destroy
has_many :song_arrangement_files, :through => :song_arrangements
Song Arrangements:
belongs_to :song
has_many :song_arrangement_files, :dependent => :destroy
Song Arrangement Files:
belongs_to :song
belongs_to :song_arrangement
I have been able to create 25 songs with FactoryGirl with this code:
Code in my spec file:
before { FactoryGirl.create_list(:song, 25) }
Code in the factory:
FactoryGirl.define do
factory :song do |s|
s.sequence(:title) { |n| "Song Title #{n}" }
s.sequence(:artist) { |n| "Song Artist #{n}" }
end
end
Any thoughts on how to create song_arrangements and song_arrangement_files that are correctly assciated with their respective song_arrangement or song record?
I'm guessing i could use after(:create) nested in my factory somehow. I'm very new to FactoryGirl and still fairly new to Rails in general. Any help is much appreciated!
So my other answer helped me do what i needed to do; however, i was needing to basically go one level of associations deeper and i was hitting walls with that solution. I ended re-reading the FactoryGirl documentation for associations and came up with this solution that works in all my cases. It creates songs, song_arrangements, and song_arrangement_files. I'm sure the code isn't pretty, but it works and can be improved upon later. Hope this helps anyone running into the same type of roadblocks.
FactoryGirl.define do
factory :song do |s|
s.sequence(:title) { |n| "Song Title #{n}" }
s.sequence(:artist) { |n| "Song Artist #{n}" }
factory :song_with_song_arrangements do
ignore do
song_arrangements_count 100
end
after(:create) do |song, evaluator|
FactoryGirl.create_list(:song_arrangement, evaluator.song_arrangements_count, song: song)
end
end
end
factory :song_arrangement do |sa|
song
sa.sequence(:title) { |n| "Arrangement #{n}" }
original_key 'A'
bpm 75
sa.sequence(:chart_content) { |n| "This is the chart content for Arrangement #{n}." }
chart_mapping 'V1, C, V2, C, B, C, C'
sa.sequence(:notes) { |n| "These are notes for the Arrangement #{n}." }
factory :song_arrangement_with_song_arrangement_files do
ignore do
song_arrangement_files_count 100
end
after(:create) do |song_arrangement, evaluator|
FactoryGirl.create_list(:song_arrangement_file, evaluator.song_arrangement_files_count, song_arrangement: song_arrangement)
end
end
end
factory :song_arrangement_file do |saf|
song_arrangement
song
saf.sequence(:title) { |n| "Attachment #{n}" }
url 'http://www.google.com'
saf.sequence(:description) { |n| "This is the description of Attachment #{n}." }
end
end
Code used to call these factories:
Songs:
before(:each) { FactoryGirl.create_list(:song, 25) }
Song Arrangements:
before(:each) { FactoryGirl.create(:song_with_song_arrangements) }
Song Arrangement Files:
before(:each) { FactoryGirl.create(:song_arrangement_with_song_arrangement_files) }
For anyone else running in to this issue, here's the solution i came up with. As i said, i am new to FactoryGirl so if there's a better way to do this please share!
FactoryGirl.define do
factory :song do |s|
s.sequence(:title) { |n| "Song Title #{n}" }
s.sequence(:artist) { |n| "Song Artist #{n}" }
before(:create) do |song|
song.song_arrangements << FactoryGirl.build_list(:song_arrangement, 10)
end
end
factory :song_arrangement do |sa|
sa.sequence(:title) { |n| "Arrangement #{n}" }
original_key 'A'
bpm 75
sa.sequence(:chart_content) { |n| "This is the chart content for Arrangement #{n}." }
chart_mapping 'V1, C, V2, C, B, C, C'
sa.sequence(:notes) { |n| "These are notes for the Arrangement #{n}." }
end
end
Giving credit where credit is due, i actually found the answer after a lot of searching from this post: How to user factory girl to create associated lists with a has_many with a validation that requires it on create
It was Blizzo's answer that i pulled the solution from.

Stub associations

I have method and spec.
class Event
def self.renew_subscription(user)
subscription = user.subscription
result = subscription.renew
user.pay(subscription.plan.price_in_cents) if result
result
end
end
let!(:user) { create :user }
describe ".renew_subscription" do
before do
user.subscription.stub!(:renew).and_return(true)
user.subscription.stub!(:plan).
and_return(Struct.new("SP", :price_in_cents).new(699))
end
context "when have to pay" do
it "pays" do
user.should_receive(:pay)
Event.renew_subscription user
end
end
end
There user belongs_to :subscription and subsription belongs_to :plan
Is there the way to stub subscription.renew and subscription.plan (or subscription.plan.price_in_cents)?
I think it's probably safe for you to do something like this:
Subscription.any_instance.stub(:renew).and_return(true)
plan = mock_model(Plan)
Subscription.any_instance.stub(:plan).and_return(plan)
plan.stub(:price_in_cents).and_return(699)
There are probably other ways of doing it too, but I hope that helps.

Trying to create a scope in rails 3 for has habtm relationship

I'm trying to do a basic search on an active record but am having trouble because of the has_and_belongs_to_many relationship. Below are the records and scopes I've created.
Class Section << ActiveRecord::Base
has_and_belongs_to_many :teachers
attr_accessible :department, :class_size
scope :department_scope, lambda { |dept| where("department = ?", dept) }
scope :teacher_fname_scope, lambda { |n| joins(:teachers) & Teacher.has_first_name(n) }
end
Class Teacher << ActiveRecord::Base
has_and_belongs_to_many :sections
attr_accessible :first_name, :last_name
scope :has_first_name, lambda { |fname| where("first_name = ?", fname) }
end
In Rails 3.2 I'm creating a basic search for my Sections model. Suppose I want to do a search for all Sections with a Teacher with a given first_name.
I've tried it using the scopes above, but all I'm getting back from *Section.teacher_fname_scope* is an empty array.
(I'm actually going to have multiple fields, to let the user also search on Section fields like department and class_size so I'll probably create multiple scopes and ultimately chain them together, e.g. the above search by also limited by department and class_size, but my understanding is the scopes are orthogonal so this won't matter to the question above.)
Thanks for any help.
It looks like the winning scope is:
Class Section << ActiveRecord::Base
scope :teacher_fname_scope, lambda { |n| joins(:teachers).("teachers.first_name = ?", n) }
end
This makes sense but I don't see why the original didn't work, given what Ryan Bates talks about in http://railscasts.com/episodes/215-advanced-queries-in-rails-3?view=asciicast
This is an old question, but since I fell here, I'll leave an answer for whoever hits this question:
class Teacher < ActiveRecord::Base
scope :by_name, ->(name) { where(first_name: name) }
end
class Section < ActiveRecord::Base
has_and_belongs_to_many :teachers
scope :by_teacher_name, ->(name) {
joins(:teachers).merge(Teacher.by_name(name))
}
end

Testing a before_save callback with Rspec and Factory Girl

I am pretty sure I am missing something really basic here.
I want to test if a before_save callback does what it is supposed to do, not just that it is called.
I wrote the following test:
it 'should add lecture to question_type' do
#course = Factory :course,
:start_time => Time.now - 1.hour,
:end_time => Time.now
#question = Factory.create(:question,
:course_id => #course.id,
:created_at => Time.now - 10.minutes)
#question.question_type.should == 'lecture'
end
And I have the following factories for course and question:
Factory.define :course do |c|
c.dept_code {"HIST"}
c.course_code { Factory.next(:course_code) }
c.start_time { Time.now - 1.hour }
c.end_time { Time.now }
c.lecture_days { ["Monday", Time.now.strftime('%A'), "Friday"] }
end
Factory.define :question do |q|
q.content {"Why don't I understand this class!?"}
q.association :course
end
And I wrote the following callback in my Question model:
before_save :add_type_to_question
protected
def add_type_to_question
#course = Course.find(self.course_id)
now = Time.now
if (time_now > lecture_start_time && time_now < lecture_end_time ) && #course.lecture_days.map{|d| d.to_i}.include?(Time.now.wday)
self.question_type = "lecture"
end
end
The test keeps failing saying that "got: nil" for question_type instead of 'lecture'
Since I didn't see anything obviously wrong with my implementation code, I tried the callback in my development environment and it actually worked adding 'lecture' to question_type.
This makes me think that there might be something wrong with my test. What am I missing here? Does Factory.create skip callbacks by default?
I would not use Factory.create to trigger the process. FactoryGirl should be used to create the test setup, not to trigger the actual code you want to test. Your test would then look like:
it 'should add lecture to question_type' do
course = Factory(:course, :start_time => Time.now - 1.hour, :end_time => Time.now)
question = Factory.build(:question, :course_id => course.id, :created_at => Time.now - 10.minutes, :question_type => nil)
question.save!
question.reload.question_type.should == 'lecture'
end
If this test still fails, you can start debugging:
Add a puts statement inside add_type_to_question and another one inside the if statement and see what happens.