how to create factory that passes override through associations - ruby

class User < ActiveRecord::Base
belongs_to :account
end
class Account < ActiveRecord::Base
has_many :users
has_many :reminders
end
class Reminder < ActiveRecord::Base
belongs_to :account
belongs_to :user
end
factory :user do
account
end
factory :reminder do
account
user
end
How do I modify the :reminder factory so that I can pass in an account and that account will be used for both the Reminder as well as the User?
r = FactoryBot.create(:reminder, account: Account.last)
r.account.id == r.user.account.id

You can pass a block to factory attribute definitions if the default does not suit you. In the current case, you want to make sure that the account and user match.
factory :reminder do
account
user { build(:user, account: account) }
end
This will ensure that the same account is used for the user if the factory needs to build the user. This will cover your requirement. However, there is another case that is not covered. If you pass in a user, but no account, then a new account will be built and the accounts will not match. Checking for a given user can mitigate this.
#overrides has the attributes that are passed in.
factory :reminder do
account { #overrides[:user]&.account || build(:account) }
user { build(:user, account: account) }
end
Now if only user or only account is passed into the factory, then the resulting reminder will have accounts matching.

Try FactoryBot.build it will build models, with defined associations, but those should be replaced later when you assign them to account on the last line:
r = FactoryBot.build(:reminder)
u = FactoryBot.build(:user)
a = FactoryBot.create(:account, users: [u], reminders: [r])

Related

How to make assciation of different kind of roles(agent and client)

I am using devise as a user model with rolify for roles
A user has one role called 'client'
A user has one role called 'agent'
I want to make a association like this
class User < ApplicationRecord
rolify
has_many :agents # if user role is client will get all agents
has_many :clients # if user role is agent will get all clients
end
How to make above associations. Please help me.
Thanks
I solved the above problem with following associations. Let me know if I am wrong
class User < ApplicationRecord
rolify
has_many :user_agents
has_many :agents, through: :user_agents # if user role is client will get all agents
has_many :user_clients, class_name: 'UserAgent', foreign_key: :agent_id
has_many :clients, source: :user, through: :user_clients # if user role is agent will get all clients
end
# field: user_id and agent_id
class UserAgent < ApplicationRecord
belongs_to :user
belongs_to :agent, class_name: 'User'
end

Creating multiple unique entries with transient FactoryGirl

I have a FactoryGirl Object that creates a Category in my case (it is associated with an image)
class Image < ActiveRecord::Base
has_many :image_categories, dependent: :destroy
has_many :categories, through: :image_categories
validates :categories, presence: { message: 'Choose At Least 1 Category' }
end
class Category < ActiveRecord::Base
has_many :image_categories
has_many :images, through: :image_categories
validates :name, presence: { message: "Don't forget to add a Category" }
validates_uniqueness_of :name, message: 'Category name %{value} already exists'
end
FactoryGirl.define do
factory :category do
name 'My Category'
end
end
FactoryGirl.define do
factory :image do
title 'Test Title'
description 'Test Description'
transient do
categories_count 1
end
categories { build_list(:category, categories_count) }
end
end
When creating an image with 1 category everything is fine, but if i try and save with 2 categories the second entry gets saved as nil, I guess thats because of my validation of unique names.
So my question is how can I use transients to create a list of 2 unique categories
Hope this makes sense
Thanks
Direct from Factory Girl Documentation: http://www.rubydoc.info/gems/factory_girl/file/GETTING_STARTED.md#Transient_Attributes
FactoryGirl.define do
# post factory with a `belongs_to` association for the user
factory :post do
title "Through the Looking Glass"
user
end
# user factory without associated posts
factory :user do
name "John Doe"
# user_with_posts will create post data after the user has been created
factory :user_with_posts do
# posts_count is declared as a transient attribute and available in
# attributes on the factory, as well as the callback via the evaluator
transient do
posts_count 2
end
# the after(:create) yields two values; the user instance itself and the
# evaluator, which stores all values from the factory, including transient
# attributes; `create_list`'s second argument is the number of records
# to create and we make sure the user is associated properly to the post
after(:create) do |user, evaluator|
create_list(:post, evaluator.posts_count, user: user)
end
end
end
end

How to prevent ActiveRecord::Observer from logging deletion of dependent records if parent record is destroyed?

I have a model User that has_one Company. A Company can have many Taxes and AdditionalFees. I am using ActiveRecord::Observer to log any activity done by the user on their Company data for the admin. For that purpose i am observing User, Company, Tax and AdditionalFee models. For any change made to the attributes in these models by the user i am logging these activities by creating a record in another ActivityLog model.
The associations among my models are -
class User < ActiveRecord::Base
has_one: :company, dependent: :destroy
class Company < ActiveRecord::Base
belongs_to: :user
has_many: :taxes, dependent: :destroy
has_many: :additional_fees, dependent: :destroy
class Tax < ActiveRecord::Base
belongs_to: :company
class AdditionalFee < ActiveRecord::Base
belongs_to: :company
My Observer is as follows:
class ActivityObserver < ActiveRecord::Observer
observe :user, :company, :tax, :additional_fee
The problem i am facing is when the user or company is deleted.
I create a record in the AcitivityLog model in the after_destroy callback in my observer.
def after_destroy(obj)
ActivityLog.create(performer: 'user_name_here', target: 'record_destroyed(serialised object)', action: "destroy", description: "#user_name_here has deleted their company #company_name_here", change: "lot_variant")
end
I create a record in the ActivityLog this way for each record in the observed model that is deleted.
Because the observer is observing all the associated models, it is creating a new entry for each associated record when the user or company is deleted.
Is there a way to not prevent the observer from logging dependent destroyed records when the parent is destroyed? I do not wish to create a new entry in the ActivityLog model for the dependent records destroyed but want to record only for the parent record(company or user) that is destroyed.
You can check obj object type if it is Company or User you can go ahead and create ActivityLog.
def after_destroy(obj)
case obj
when Company, User then create_activity_log!
end
end

How do I "unscope" an edit path and/or action in Rails 3?

Currently I'm working on a simple Rails 3 web application with devise. When users sign up an Account is created. This the essence of the Account model:
class Account < ActiveRecord::Base
include Authority::UserAbilities
# Callbacks
after_create do |account|
account.create_person
account.add_role :owner, account.person
end
has_one :person, dependent: :destroy
end
And the essence of the Person model:
class Person < ActiveRecord::Base
belongs_to :account
default_scope where(published: true)
end
As you can see a person (which basically is a user profile) is created after the creation of an account, the default value of published is false. After signing up the user is signed in and redirected to the home page, which contains edit_person_path(current_account.person).
After setting the default_scope for Person a Routing Error: No route matches {:action=>"edit", :controller=>"people", :id=>nil} was thrown because of edit_person_path(current_account.person).
My solution to this was to change edit_person_path(current_account.person) into edit_person_path(Person.unscoped { current_account.person} ).
The issue that I'm having now is that although the page is rendering fine, when I click on the edit link the following exception is raised:
ActiveRecord::RecordNotFound in PeopleController#edit
Couldn't find Person with id=126 [WHERE "people"."published" = 't']
What's the best way to temporarily unscope the edit action, should I do this in PeopleController#edit?

In factory definition, assign attribute using attribute of other factory

I have two factory_girl factories, contact and user. Contact has an attribute, dest_user_id, which is a foreign key to user, but can be NULL. For that attribute, I want to create a new user using the user factory, and then assign its id to dest_user_id. Is there a way to do that?
Having a foreign key of dest_user_id shouldn't change the approach, provided the relationship remains the same as the model name. Assuming that contact belongs_to user and a user either has_one or has_many contacts, you can achieve what you want as follows:
First create the factories:
FactoryGirl.define do
factory :user do
Field 1
Field 2
# etc
end
factory :contact do
# Add only the minimum fields required to create a contact, but not the association
Field 1
Field 2
factory :contact_with_user do
# then you add the association for this inherited factory
# we can use 'user' here as the factory is the same as the association name
user
end
end
end
With this setup you create a contact without a user, and therefore dest_user_id is NULL when you user FactoryGirl.create(:contact) within your test/spec.
To create a user and assign the id into the dest_user_id field on the contact you would use the following:
#user = FactoryGirl.create(:user)
#contact = FactoryGirl.create(:contact_with_user, :user => #user)
This approach maximises flexibility as you can create a contact with or without a user, and you can have the user.id passed into the contact model only if you need it for a particular test. If you call FactoryGirl.create(:contact_with_user) then both a contact and a user will automatically be created but you wouldn't have the control over the assignment of dest_user_id that you have when you use FactoryGirl.create(:contact_with_user, :user => #user)

Resources