Rails 4 before_destroy does not prevent destruction - ruby-on-rails

Here's a simple Message model that i've set up.
class Message < ActiveRecord::Base
validates_presence_of :user_id, :head, :text
belongs_to :user
has_many :receivers, class_name: MessageReceiver
has_many :users, through: :receivers
before_destroy :check_receivers
private
def check_receivers
if self.receivers.empty?
true
else
self.update(deleted: 1)
false
end
end
end
The :check_receivers method works as expected when called directly (I remove it from the private section for that ofcource), but somehow it does not prevent a message from being destroyed.
Am I doing something wrong here?

Remove
before_destroy :check_receivers
and try
has_many :receivers, class_name: MessageReceiver, dependent: :restrict_with_exception

Related

How to call an attribute from a class array

I'm trying to make a method that gets the date and adds onto it an amount of days specified.
At present I cannot call the days specified.
I have a Plant Class that has_many DaysTillSellables, The Period class has_many DaysTillSellable also.
When the user creates a plant they can add a DaysTillSellables and then select a period and then enter an amount of days.
I first need to check to see which period the date is in, then return that period. Currently attempting like so
def current_period
return unless completed_at_week.between?(period.start_week, period.finish_week)
index_days_till_sellables_on_period_id
end
Then Find the days till sellable that is connected to that period and finally call the days from that
Below is the code for the class I'm trying to call it in
class Potting < ApplicationRecord
belongs_to :batch, inverse_of: :pottings
validates :plant_count, :completed_at, presence: true
enum germination_result: { light: 0, medium: 1, full: 2 }
def pottings_completed_at
"Week #{completed_at.strftime('%U').to_i}/#{completed_at.strftime('%Y').to_i}"
end
def completed_at
super || Time.zone.today
end
def completed_at_week
completed_at.strftime('%U')
end
def current_period
return unless completed_at_week.between?(period.start_week, period.finish_week)
index_days_till_sellables_on_period_id
end
def period_days
plant.days_till_sellables.find_by(period: :current_period).&current_period.days
end
def ready_for_sale
completed_at + period_days
end
end
I've added more Code below to give better context for classes
class DaysTillSellable < ApplicationRecord
belongs_to :period, inverse_of: :days_till_sellables, foreign_key: :period_id
belongs_to :plant, inverse_of: :days_till_sellables, foreign_key: :plant_id
validates :days, presence: true
end
.
class Period < ApplicationRecord
has_many :days_till_sellables, dependent: :destroy, inverse_of: :period
belongs_to :organization
.
class Plant < ApplicationRecord
has_many :batches, dependent: :destroy
has_many :goals, dependent: :destroy
has_many :days_till_sellables, dependent: :destroy, inverse_of: :plant
belongs_to :organization
accepts_nested_attributes_for :days_till_sellables, allow_destroy: true
validates :genus, :species, :period_id, presence: true
end
I think you are looking for:
class Potting
belongs_to :plant, through: :batch
...
end

Rails - accepts_nested_attributes_for and presence validation

In my application I have a simple relationship where a user that has many events:
class User < ApplicationRecord
has_many :events, foreign_key: 'created_by', dependent: :destroy
accepts_nested_attributes_for :events
end
class Event < ApplicationRecord
belongs_to :user, foreign_key: 'created_by', inverse_of: :events
validates :created_by, presence: true
end
When I am trying to create a user alongside with some events I am getting a validation error "Companies.created by can't be blank".
My params hash looks like that:
{"user"=>{"events_attributes"=>[{"name"=>"disney show"}]}}
When I remove validates :created_by, presence: true everything works as expected. Any help would be appreciated!
Try to specify explicitly bi-directional associations in your User model using inverse_of: :
class User < ApplicationRecord
has_many :events, foreign_key: 'created_by', inverse_of: :user, dependent: :destroy
accepts_nested_attributes_for :events
end
And your Event record will be created with present foreign key pointing to User.

Rails - Remove has_many relation when the parent is destroyed

I have 3 models
HiringTag
class HiringTag < ActiveRecord::Base
attr_accessible :name, :staffroom_id
validates :name, presence: true
has_many :hiring_tag_applications
has_many :job_applications, through: :hiring_tag_applications
after_destroy { |application|
HiringTagApplication.destroy(application.job_applications.pluck(:job_application_id))
end
HiringTagApplication
class HiringTagApplication < ActiveRecord::Base
belongs_to :hiring_tag, dependent: :destroy
belongs_to :job_application
end
JobApplication
class JobApplication < ActiveRecord::Base
has_many :hiring_tag_applications
has_many :hiring_tags, through: :hiring_tag_applications
end
What I am trying to do: is when I destroy the HiringTag or JobApplication, I want the related data to be deleted in HiringTagApplication as you will notice I have a after_destroy inside HiringTag that call back does get executed but the error I get is:
Couldn't find HiringTagApplication with id=96453
96453 is not the id of HiringTagApplication but it is job_application_id
How can I correct this so the record can be deleted?
No need of after_destroy, just use dependent: :destroy correctly like:
class HiringTag < ActiveRecord::Base
has_many :hiring_tag_applications, dependent: :destroy
end
And
class JobApplication < ActiveRecord::Base
has_many :hiring_tag_applications, dependent: :destroy
end
& remove it from associated table
class HiringTagApplication < ActiveRecord::Base
belongs_to :hiring_tag
belongs_to :job_application
end
Now, whenever HiringTag or JobApplication gets deleted its associated HiringTagApplication will also be deleted

Reject nested assosciation creation rails

I have 2 models as below,
Updated based on suggestions
class User < ActiveRecord::Base
has_many :company_users, dependent: :destroy, inverse_of: :user
accepts_nested_attributes_for :company_users, allow_destroy: true
has_many :companies, through: :company_users
has_many :roles, through: :company_users
end
and
class CompanyUser < ActiveRecord::Base
belongs_to :company
belongs_to :role
belongs_to :user, inverse_of: :company_users
validates :user, uniqueness: {scope: [:company, :role]}
end
I find the uniqueness validation is working only on the update request. On create request validation is not functioning and it simply bypasses it.
I want to enable the same validation to reject if a user has same company & role assigned more than once.
If you want a ensure the uniqueness of user on unique pair of :company and :role, then you can try following. By default, the validations run for both create and update. You don't need :on => [ :create, :update ]. So it should be just:
validates :user, uniqueness: {scope: [:company, :role]}
Solved this issue with the below validation,
class User < ActiveRecord::Base
has_many :company_users, dependent: :destroy, inverse_of: :user
accepts_nested_attributes_for :company_users, allow_destroy: true
has_many :companies, through: :company_users
has_many :roles, through: :company_users
validate :company_users, :uniqueness_of_company_users
end
private
def uniqueness_of_company_users
errors.add(:company_users, 'error in role creation') if company_users.map{|x| "#{x.company_id} #{x.role_id}"}.uniq.size != company_users.size
end
This is the additional validation required to solve the issue
Thanks Rich Peck for this https://railscoding.wordpress.com/2015/04/27/uniqueness-gotcha/
From the docs
The :on option takes one of the values :create or :update
--
A validation is only run on create or update anyway, right?
find doesn't manipulate the db, destroy gets rid of the record & new just invokes a new instance of the object. You have literally zero other reasons to validate.
So, really, you should have:
validates :user, uniqueness: {scope: [:company_id, :role_id]}
This will look up against the values in company_id and role_id, which is probably going to be more efficient than calling the company and role objects themselves.
I could be wrong, but I really think if you used the above, it should work.
--
You may also wish to clean up your models:
class User < ActiveRecord::Base
has_many :company_users, dependent: :destroy, inverse_of: :user
accepts_nested_attributes_for :company_users, allow_destroy: true
has_many :companies, through: :company_users
has_many :roles, through: :company_users
end
class CompanyUser < ActiveRecord::Base
belongs_to :company
belongs_to :role
belongs_to :user, inverse_of: :company_users
validates :user, uniqueness: {scope: [:company_id, :role_id]}
end

AcriveRecord relationship validation between two or more belongs_to associations

I'm wondering if there is a cleaner way to validate multiple relationships in rails. I don't mean validating an association, rather ensuring relationship integrity between two or more belongs_to associations. Here is some code as an example:
class User < ActiveRecord::Base
has_many :products, inverse_of: :user
has_many :purchases, inverse_of: :user
end
class Purchase < ActiveRecord::Base
belongs_to :user, inverse_of: :purchases
has_many :products, inverse_of: :purchase
end
class Product < ActiveRecord::Base
belongs_to :user, inverse_of: :products
belongs_to :purchase, inverse_of: :products
validates :user, :purchase, presence: true
validate :purchase_user
private
def purchase_user
errors.add(:purchase, 'purchase user does not match user') if user != purchase.user
end
end
The purchase_user validation method here checks that the user is the same as purchase.user and adds an error if they are not.
I could change it to use :inclusion like so:
validates :purchase, inclusion: { in: proc { |record| record.user.purchases } }
But that seems even more inefficient, any suggestions?

Resources