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.
Related
I have a rails app with the models below. I have both assigned_tasks and executed_tasks for a given user. I would like to know which option is better for getting all the tasks (executed and assigned as well) for that given user.
task.rb
belongs_to :assigner, class_name: "User"
belongs_to :executor, class_name: "User"
user.rb
has_many :assigned_tasks, class_name: "Task", foreign_key: "assigner_id", dependent: :destroy
has_many :executed_tasks, class_name: "Task", foreign_key: "executor_id", dependent: :destroy
Solution 1:
task.rb
scope :completed, -> { where.not(completed_at: nil) }
scope :uncompleted, -> { where(completed_at: nil) }
user.rb
def tasks_uncompleted
tasks_uncompleted = assigned_tasks.uncompleted.order("deadline DESC")
tasks_uncompleted += executed_tasks.uncompleted.order("deadline DESC")
tasks_uncompleted.sort_by { |h| h[:deadline] }.reverse!
end
tasks_controller:
#tasks = current_user.tasks_uncompleted.paginate(page: params[:page], per_page: 12)
Solution 2:
task.rb
scope :completed, -> { where.not(completed_at: nil) }
scope :uncompleted, -> { where(completed_at: nil) }
scope :alltasks, -> (u) { where('executor_id = ? OR assigner_id = ?', u.id, u.id) }
tasks_controller
#tasks = Task.alltasks(current_user).uncompleted.order("deadline DESC").paginate(page: params[:page], per_page: 12)
You should define an association on User that will return all of the Tasks associated by either executor_id or assigner_id:
class User < ActiveRecord::Base
has_many :assigned_and_executed_tasks,
->(user) { where('executor_id = ? OR assigner_id = ?', user, user) },
class_name: 'Task',
source: :tasks
end
user = User.find(123)
user.assigned_and_executed_tasks
# => SELECT tasks.* FROM tasks WHERE executor_id = 123 OR assigner_id = 123;
Then you can do as you do in "Solution 2," but instead of the unfortunate Task.alltasks(current_user) you can just do current_user.assigned_and_executed_tasks (of course you could give it a shorter name, but descriptive names are better than short ones):
#tasks = current_user.assigned_and_executed_tasks
.uncompleted
.order("deadline DESC")
.paginate(page: params[:page], per_page: 12)
Solution 2 will be the more efficient way of retrieving the records from your database. In most Rails apps, calls to the database are a frequent cause of bottlenecks, and in solution 2 you make one call to the database to retrieve all the records, but in solution 1 you make two calls to the database to retrieve the same information.
Personally, I also think this solution is much more readable, easily testable, and maintainable, so solution 2 is better in many ways beyond speed!
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.
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
My factory:
FactoryGirl.define do
factory :comment do
content 'bla bla bla bla bla'
user
end
factory :user do
sequence(:username) { |n| "johnsmith#{n}" }
password '123'
factory :user_with_comments do
ignore do
comments_count 5
end
after(:create) do |user, evaluator|
FactoryGirl.create_list(:comment, evaluator.comments_count, user: user)
end
end
end
end
My spec:
require 'spec_helper'
describe Comment do
let(:comment) { Factory.create :comment }
describe "Attributes" do
it { should have_db_column(:content).of_type(:text) }
it { should have_db_column(:user_id).of_type(:integer) }
it { should have_db_column(:profile_id).of_type(:integer) }
end
describe "Relationships" do
it { should belong_to(:profile) }
it { should belong_to(:user) }
end
describe "Methods" do
describe "#user_name" do
it "Should return the comment creater username" do
user = Factory.create :user
binding.pry
comment.user = user
binding.pry
comment.user_username.should == user.username
end
end
end
end
On the first binding.pry, User.count returns 1 as expected. But on the second binding.pry, User.count returns 2. My question is, why the comment.user = user assignment creates a new user record ? Thanks in advance.
The reason is that your let for comment calls Factory.create :comment. In the factory for comment, it calls the association for user.
So when you use your let, it makes a comment object and a user object and connects them. Then, you override that user when you set comment.user=user.
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