Do not autosave belongs-to association - ruby

I have a model Subscription with a belongs-to association to Participant.
The subscription form uses fields_for to build the associated participant fields.
Also in the form is a radio button called called 'other_person'.
What I want is to NOT save the associated participant table (so also not validate) when the other_person field is set to false.

I will assume other_person is a field of the Subscription model on the following example:
class Subscription < ActiveRecord::Base
before_save :remove_empty_participant
belongs_to :participant
private
def remove_empty_participant
self.participant = nil unless self.other_person
end
end
If it's not a field of your Subscription model, you will have to remove the attributes in the controller's action:
class SubscriptionsController < ActionController
def create
params[:subscription].delete(:participant) unless params[:other_person]
# Save the subscription with your current params...
end
end
Hope it helps.

Related

temporarily replace content of has_many relationship

when my model is in a particular state, I need to render it with hiding some specific data.
my model knows the data I need to hide through a has_many relationship.
my idea is to retrieve the model, replace the content of the has_many relationship with a dummy, non persisted object, and then render it, without saving the model.
So that when rendering the data shown will be from the dummy object.
here's my code:
the model:
class Car < ActiveRecord::Base
....
has_many :owners
....
end
in the controller:
#car.owners = [ Owner.new(name: "", phone: "") ] if hide_owner?
it actually attempts to do the update on the DB and fails with this error:
*** ActiveRecord::RecordNotSaved Exception: Failed to replace owners because one or more of the new records could not be saved.
It feels like this would be easier if you were accessing a Decorator class instead of the model directly.
Then in the decorator you could define:
def owners
if hide_owner?
[ Owner.new(name: "", phone: "") ]
else
object.owners
end
end
... where object is the instance of Car.
Maybe look at the Draper gem or others.

Set a custom serializer in the include file with rails 5

I have this code that brings one vacancy from my model Vacancy and then render in json the attributes according to the serializer VacancyDetailSerializer:
Controller
vacancy = Vacancy.find(params[:id])
render json: vacancy, serializer: VacancyDetailSerializer,
include: [:restaurant]
The thing here is that in the include: [:restaurant] I want to specify a custom serializer the way I did with vacancy, because right now is taking the serializer of RestaurantSerializer, but I don't want to take that file, is there a way to do it with the include? Maybe is here in the controller, or maybe in the serializer?
If you have belongs_to :restaurant association in the VacancyDetailSerializer, then serializer for this association can be specified:
class VacancyDetailSerializer < ActiveModel::Serializer
belongs_to :restaurant, serializer: AnotherRestaurantSerializer
end
Or it can be overridden by providing a block:
class VacancyDetailSerializer < ActiveModel::Serializer
belongs_to :restaurant do
AnotherRestaurantSerializer.new(object.restaurant)
end
end
Or a custom association serializer lookup can be implemented.

ruby on rails "before_save" in parent model to check attribut of child

I have a model 'place_detail' that has many a child 'emails'
has_many :emails, :dependent => :destroy
and in the email model:
belongs_to :place_detail
Now in the place_detail i want to make sur i added a email to check a attribut 'has_email'
so i added:
before_save :check_if_has_email
...
def check_if_has_email
if emails.count >0
self.has_email = true;
else
self.has_email = false;
end
end
the problem is that the attribute has_email does not check event if i created a email child. what i understand is that the parent is saved before the child
how can i get my has_email checked in place_detail when i create a child email?
EDIT:
I could simply put the has_email boolean in a method like
def has_email?
if self.emails.count >0
return true
..
but i prefer the boolean in the attribute because i use it in many scope and it would be a lot a change in the app
This will ensure that your model has at least one email (place it in your model file place_detail.rb)
has_many :emails, :dependent => :destroy
validates :emails, :length => { :minimum => 1 }
EDIT:
One suggestion would be just to check the trait place_detail.emails count when you need it. If you examine such data multiple times a request you can store it like so
def has_email?
(result ||= self.emails.count) > 0
end
That way it will only check your database once
If you're forced to use the 'has_email' attribute within the place_detail model, you could simply save the place_detail in the Create method of the Email controller.
#email = Email.new(email_params)
#email.place_detail.save

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

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