Callback for rails 4 delete_all - ruby-on-rails

I have an UnreadEntry model and am using an after_commit callback to send a notification to a pusher service. The problem is event fires just fine when adding records but when a delete_all is sent on the model, neither: after_commit, after_destroy are fired.
How can I add catch delete_all and add a callback to it?
class UnreadEntry < ActiveRecord::Base
belongs_to :user
belongs_to :feed
belongs_to :entry
after_commit :send_pusher_notification, if: PUSHER_ENABLED
validates_uniqueness_of :user_id, scope: :entry_id
def self.create_from_owners(user, entry)
create(user_id: user.id, feed_id: entry.feed_id, entry_id: entry.id, published: entry.published, entry_created_at: entry.created_at)
end
def self.sort_preference(sort)
if sort == 'ASC'
order("published ASC")
else
order("published DESC")
end
end
def send_pusher_notification(user = nil, from = 'UnreadEntry#callback')
if user.nil?
unread_count = UnreadEntry.where(user_id: self.user_id).count
else
unread_count = UnreadEntry.where(user_id: user.id).count
end
Pusher['rssapp'].trigger('unread_count', {
message: unread_count
})
end
end

Simple - don't use delete_all. delete_all and update_all are specifically designed to query the database directly, bypassing the ActiveRecord model logic - including validations and callbacks. If you want callbacks, call destroy on each model instance individually.
my_collection.each(&:destroy)

Related

how to skip_callback while building has_many relationship objects in rails 5

how to skip_callback while building has_many relationship objects in rails 5
Consider below case
class Customer
has_many :resources
end
class Resource
attr_accessor: :skip_callback
belongs_to :customer
after_commit :data_calculation, unless: :skip_callback
def data_calculation
# logic goes here
end
end
customer = Customer.new
customer.resources.build({name: 'abc'})
customer.save
I want skip callback of associated object.
Can we do this while building object?
found solution,
customer = Customer.new
customer.resources.build({name: 'abc', skip_callback: true})
customer.save
passing attr_accessor as params will set callbacks condition value also.

Rails Model - after_destroy never called

I have some trouble to use after_destroy in model
Here is this one:
class Transaction < ActiveRecord::Base
belongs_to :user
delegate :first_name, :last_name, :email, to: :user, prefix: true
belongs_to :project
delegate :name, :thanking_msg, to: :project, prefix: true
validates_presence_of :project_id
after_save :update_collected_amount_in_project
after_update :update_collected_amount_if_disclaimer
after_destroy :update_collected_amount_after_destroy
def currency_symbol
currency = Rails.application.config.supported_currencies.fetch(self.currency)
currency[:symbol]
end
private
def update_collected_amount
new_collected_amount = project.transactions.where(success: true, transaction_type: 'invest').sum(:amount)
project.update_attributes(collected_amount: (new_collected_amount / 100).to_f) # Stored in € not cents inside projects table
end
def update_collected_amount_in_project
update_collected_amount if transaction_type == 'invest' && success == true
end
def update_collected_amount_if_disclaimer
update_collected_amount if transaction_type == 'invest' && self.changes.keys.include?('success') && self.changes.fetch('success', []).fetch(1) == false
end
def update_collected_amount_after_destroy
update_collected_amount
end
end
When I use something like:
Transaction.last.delete
It never go inside my after_destroy, I tried to include some outputs but nothing. I don't know if I'm wrong on how I use this after_destroy, I also tried before_destroy and I have the same problem. after_save and after_update work perfectly.
The after_destroy callbacks aren't called on delete. They are only called if you call destroy, like so:
Transaction.last.destroy
That's actually the only difference between the two methods. Delete bypasses the callbacks.
Delete also won't execute any :dependent association options.
The reason for this is that it never instantiates any of the active record objects that you are deleting, it simply executes an SQL delete statement against the database.
I think you can use rails-observer gem, that can help you in adding after_destroy callback

Save before initial save of an object

When a conversation is created, I want that conversation to have its creator automatically following it:
class Conversation < ActiveRecord::Base
belongs_to :user
has_many :followers
has_many :users, through: :followers
alias_method :user, :creator
before_create { add_follower(self.creator) }
def add_follower(user)
unless self.followers.exists?(user_id: user.id)
self.transaction do
self.update_attributes(follower_count: follower_count + 1)
self.followers.create(user_id: user.id)
end
end
end
end
However, when a user attempts to create a conversation I get a stack level too deep
. I'm creating an infinite loop, and I think this is because the before_create callback is being triggered by the self.update_attributes call.
So how should I efficiently update attributes before creation to stop this loop happening?
Option 1 (preferred)
Rename your column follower_count to followers_count and add:
class Follower
belongs_to :user, counter_cache: true
# you can avoid renaming the column with "counter_cache: :follower_count"
# rest of your code
end
Rails will handle updating followers_count for you.
Then change your add_follower method to:
def add_follower(user)
return if followers.exists?(user_id: user.id)
followers.build(user_id: user.id)
end
Option 2
If you don't want to use counter_cache, use update_column(:follower_count, follower_count + 1). update_column does not trigger any validations or callbacks.
Option 3
Finally you don't need to save anything at this point, just update the values and they will be saved when callback finishes:
def add_follower(user)
return if followers.exists?(user_id: user.id)
followers.build(user_id: user.id)
self.follower_count = follower_count + 1
end

Rails after_remove not getting triggered with destroy

I have venues and venue_managers, with a HABTM relationship, and I am rolling my own counter cache via after_add and after_remove callbacks. Those callbacks on the venue_managers are getting added to the venue model via a mixin. I am using am mixin because there are other models that need this same logic.
If there are 2 venue_managers and I re-assign venue_managers to just have 1, with venue.venue_managers = a_user, then the after_remove callback gets triggered and all is well. If I have 2 venue_managers and I destroy the last one with venue.venue_managers.last.destroy, the after_remove callback is NOT triggered and the counter cache does not update, which is my problem.
class Venue < ActiveRecord::Base
include HABTMCounterCache
has_and_belongs_to_many :venue_managers, :class_name => 'User', :join_table => :venues_venue_managers
end
module HABTMCounterCache
extend ::ActiveSupport::Concern
module ClassMethods
def count_cache_associations
klass = self.name.downcase
self.send("after_add_for_#{klass}_managers") << lambda do |obj, manager|
obj.update_counter_cache(manager)
end
self.send("after_remove_for_#{klass}_managers") << lambda do |obj, manager|
obj.update_counter_cache(manager)
end
end
end
def update_counter_cache(noop)
klass = self.class.name.downcase
self.assign_attributes(:"#{klass}_managers_count" => self.send("#{klass}_managers").count)
end
end

mongoid before_destroy callback on has_and_belongs_to_many association

I have a simple has_and_belongs_to_many relation set up in Mongoid like so:
class Post
...
has_and_belongs_to_many :authors
scope :live, lambda{ published.where(:published_at.lt => Time.now) }
end
class Author
has_and_belongs_to_many :posts
before_save :count_posts
def count_posts
self.post_count = posts.live.length
end
end
When I update the Post model and destroy an Author / Post relationship, how can I do an before_destroy or some other callback on the author to update the post count?
I don't believe there are any features built into Mongoid relationships that would help with this, but what you can do is add a before_destroy callback on the post that tells each author it belongs to that it's being deleted by simply triggering a save on that author so that your count_posts hook is called.
class Post
has_and_belongs_to_many :authors
after_remove :update_author_counts
scope :live, lambda{ published.where(:published_at.lt => Time.now) }
....
protected
def update_author_counts
# Assuming you keep the count_posts callback in
# your author model, all you have to do is trigger a save
authors.each { |a| a.save }
end
end
Reference: http://mongoid.org/en/mongoid/docs/callbacks.html

Resources