before_destroy callback on rails - ruby-on-rails

I want to use before_destroy callback in my rails app. I want to use it because I use it on notification. But how can I use before_destroy?
I have my code here for before_save and after_save but I don't know what will I do for before_destroy.
class Record < ActiveRecord::Base
# Callbacks
before_save {
#is_new_record = self.new_record? if self.new_record?
}
after_save {
action = #is_new_record ? 'created' : 'updated'
Notification.publish_notification(self, action)
}
...
#Some stuffs here
UPDATE
Actually, I want to save logs for my table Notification just like when creating new record.
Like,
User One created new Record.
So I want to save on notification before I destroy a record. Just like.
User One destroy a record.
Just like that.
Please help me.

I prefer to use methods instead of the anonymous blocks. I would handle this as follows:
class Record < ActiveRecord::Base
before_save :remember_new_record
after_save :write_save_notification
before_destroy :write_destroy_notification
# ... the rest of your class ...
private
def remember_new_record
#is_new_record = self.new_record?
end
def write_save_notification
action = #is_new_record ? 'created' : 'updated'
Notification.publish_notification(self, action)
end
def write_destroy_notification
Notification.publish_notification(self, 'destroy')
end
end
So it is simple as that, unless you ment something else?

Related

Inline `after_commit` Callbacks in Rails

I've got a sidekiq job that needs to be run after the commit, but only in some situations and not all, in order to avoid a common race condition.
For example, the below after_commit will always fire but the code inside will only execute if the flag is true (previously set in the verify method).
class User < ActiveRecord::Base
...
after_commit do |user|
if #enqueue_some_job
SomeJob.new(user).enqueue
#enqueue_some_job = nil
end
end
def verify
#enqueue_some_job = ...
...
save!
end
end
The code is a bit ugly. I'd much rather be able to somehow wrap the callback inline like this:
class User < ActiveRecord::Base
def verify
if ...
run_after_commit do |user|
SomeJob.new(user).enqueue
end
end
...
save!
end
end
Does anything built into Rails exist to support a syntax like this (that doesn't rely on setting a temporary instance variable)? Or do any libraries exist that extend Rails to add a syntax like this?
Found a solution using a via a concern. The snippet gets reused enough that it is probably a better option to abstract the instance variable and form a reusable pattern. It doesn't handle returns (not sure which are supported via after_commit since no transaction is present to roll back.
app/models/concerns/callbackable.rb
module Callbackable
extend ActiveSupport::Concern
included do
after_commit do |resource|
if #_execute_after_commit
#_execute_after_commit.each do |callback|
callback.call(resource)
end
#_execute_after_commit = nil
end
end
end
def execute_after_commit(&callback)
if callback
#_execute_after_commit ||= []
#_execute_after_commit << callback
end
end
end
app/models/user.rb
class User < ActiveRecord::Base
include Callbackable
def verify
if ...
execute_after_commit do |user|
SomeJob.new(user).enqueue
end
end
...
save!
end
end
You can use a method name instead of a block when declaring callbacks:
class User < ActiveRecord::Base
after_commit :do_something!
def do_something!
end
end
To set a condition on the callback you can use the if and unless options. Note that these are just hash options - not keywords.
You can use a method name or a lambda:
class User < ActiveRecord::Base
after_commit :do_something!, if: -> { self.some_value > 2 }
after_commit :do_something!, unless: :something?
def do_something!
end
def something?
true || false
end
end
Assuming that you need to verify a user after create.
after_commit :run_sidekiq_job, on: :create
after_commit :run_sidekiq_job, on: [:create, :update] // if you want on update as well.
This will ensure that your job will run only after a commit to db.
Then define your job that has to be performed.
def run_sidekiq_job
---------------
---------------
end
Hope it helps you :)

cannot update a new record on after_create callback

class Model < ActiveRecord::Base
after_create :set_slug
def set_slug
update_column(:slug, to_slug)
end
def to_slug
#code to create slug
end
end
Why does this return 'ActiveRecord::ActiveRecordError: cannot update a new record' if the callback is an after_create? The issue is with "update_column"
Your problem lies in the fact that update_columns doesn't work on new records.
Why not use update_attributes` instead
after_create do
self.update_attributes(slug: to_slug)
end
if you want then you can also try following approach
if new_record?
return
else
update_column(:slug, to_slug)
end
Also check the model side validations. That may also cause the problems.
There is ActiveRecord::Persistence::ClassMethods#update_columns method which contains line
raise ActiveRecordError, "cannot update a new record" if new_record?
therefore you should use update_all, for example:
def set_slug
self.class.where(id: id).update_all(slug: to_slug) if id
end
Hope it will help

How to detect if a paperclip attachment was changed in after_save callback?

It looks like Paperclip doesn't honor the ActiveRecord dirty model. How do I detect the change in after_save callback.
class User
has_attachment :avatar
after_save :do_something
def do_something
if name_changed?
#
end
# How to determine avatar was changed?
#if avatar_changed?
# #
#end
end
end
Note
I know I can detect the change in before_save callback using avatar.dirty? call, but the dirty flag is set to false after save.
I can add a processor, but I need to perform my actions after the model data is saved.
You could try accessing the _changed? method for one of the attributes:
if avatar_updated_at_changed?
# do something
end
When I need access to this data after save, I typically take this approach:
class Foo
has_attachment :avatar
before_save :check_for_avatar_changes
after_save :do_something
def do_something
if #avatar_has_changes
#
end
end
def check_for_avatar_changes
#avatar_has_changes = self.avatar.dirty?
end
end

after_save callback to set the updated_by column to the current_user

I would like to use an after_save callback to set the updated_by column to the current_user. But the current_user isn't available in the model. How should I do this?
You need to handle it in the controller. First execute the save on the model, then if successful update the record field.
Example
class MyController < ActionController::Base
def index
if record.save
record.update_attribute :updated_by, current_user.id
end
end
end
Another alternative (I prefer this one) is to create a custom method in your model that wraps the logic. For example
class Record < ActiveRecord::Base
def save_by(user)
self.updated_by = user.id
self.save
end
end
class MyController < ActionController::Base
def index
...
record.save_by(current_user)
end
end
I have implemented this monkeypatch based on Simone Carletti's advice, as far as I could tell touch only does timestamps, not the users id. Is there anything wrong with this? This is designed to work with a devise current_user.
class ActiveRecord::Base
def save_with_user(user)
self.updated_by_user = user unless user.blank?
save
end
def update_attributes_with_user(attributes, user)
self.updated_by_user = user unless user.blank?
update_attributes(attributes)
end
end
And then the create and update methods call these like so:
#foo.save_with_user(current_user)
#foo.update_attributes_with_user(params[:foo], current_user)

How to get the current_user in a model observer?

Given the following models:
Room (id, title)
RoomMembers (id, room_id)
RoomFeed, also an observer
When a Room title is updated, I want to create a RoomFeed item, showing who the user is who made the update.
#room.update_attributes(:title => "This is my new title")
Problem is in my observer for RoomFeed:
def after_update(record)
# record is the Room object
end
The is no way for me to get the user.id of the person who just made the update. How do I go about doing that? is there a better way to do the update so I get the current_user?
I think what you are looking for is, room.updated_by inside your observer. If you don't want to persist the updated_by, just declare it as an attr_accessor. Before you push the update, make sure you assign the current_user to updated_by, may be from you controller.
This is a typical "separation of concern" issue.
The current_user lives in the controller and the Room model should know nothing about it. Maybe a RoomManager model could take care of who's changing the name on the doors...
Meanwhile a quick & dirty solution would be to throw a (non persistant) attribute at Room.rb to handle the current_user....
# room.rb
class Room
attr_accessor :room_tagger_id
end
and pass your current_user in the params when updating #room.
That way you've got the culprit! :
def after_update(record)
# record is the Room object
current_user = record.room_tagger_id
end
Create the following
class ApplicationController
before_filter :set_current_user
private
def set_current_user
User.current_user = #however you get the current user in your controllers
end
end
class User
...
def self.current_user
##current_user
end
def self.current_user= c
##current_user = c
end
...
end
Then use...
User.current_user wherever you need to know who is logged in.
Remember that the value isn't guaranteed to be set when your class is called from non-web requests, like rake tasks, so you should check for .nil?
I guess this is a better approach
http://rails-bestpractices.com/posts/47-fetch-current-user-in-models
Update user.rb
class User < ActiveRecord::Base
cattr_accessor :current
end
Update application_controller.rb
class ApplicationController
before_filter :set_current_user
private
def set_current_user
User.current = current_user
end
end
Then you can get logged user by User.current anywhere. I'm using this approach to access user exactly in observers.

Resources