I have an after_destroy model callback that regenerates cache after the model instance has been destroyed. It does this by calling open("http://domain.com/page-to-cache") for as many pages as need to be re-cached.
The problem is that the model instance apparently isn't fully destroyed yet at this time, because those open url requests still register its presence, and the regenerated cache looks exactly like the pre-destroy cache. How can I run those calls after the model instance has been actually destroyed?
You may be able to use an after_commit callback to do something after the entire transaction has gone through to the database. This is different depending on the version of Rails you're using (2.3.x versus 3.x.x), but is essentially something like the following:
# model_observer.rb
class ModelObserver < ActiveRecord::Observer
def after_commit(instance)
do_something if instance.destroyed?
end
end
You can read some documentation about the Rails 3 after_commit callback here. If your version of Rails doesn't have an after_commit hook, you can try using this gem which will provide the functionality.
You could try adding an after_save callback like:
after_save :my_after_save_callback
def my_after_save_callback
do_something if destroyed?
end
Related
I am trying to update a rails record if a duplicate exists as follow:
class Rating < ActiveRecord::Base
before_create :update_rating_if_already_exists
def update_rating_if_already_exists
original_rating = Rating.where(user: self.user, article: self.article)
if original_rating.blank?
true
else
original_rating[0].update_attribute(:score, self.score)
false
end
end
end
the problem however is that when I use after_create as above, this will not work for normal usage from controller action, as the controller will first build a new instance of the class with given params and then save (not create) the new object.
However, if I change the above to an after_save, it takes care of the controller problem, but then leads to another problem:
original_rating[0].update_attribute(:score, self.score)
will try to update the original record with a save method, which will also trigger the before_save...and on, and on... and this leads to a SystemStackError: stack level too deep error.
This is the dilemma now and my question is how could I go about this?
Thanks for all contributions. :)
You can use before_save with a new_record? condition to avoid executing the callback on an update_attribute call.
before_save :update_rating_if_already_exists, if: :new_record?
def update_rating_if_already_exists
# method code
end
For Mongoid 3+, is there a diagram/description of the various callbacks?
http://mongoid.org/en/mongoid/v3/callbacks.html
For example, what's the difference between before_upsert vs. before_save. Isn't a save caused by an insert or update call? Or does save also get called by destroy?
Also, what's difference between before_xxx and around_xxx?
Cheers,
With before_xxx the code is executed before the action and with around_xxx you have the option to execute code before and after the action itself.
For example, imagine you want to update all the user belongings after destroy a user project (User has_many :proyects and Project belongs_to User) :
class ProjectsController < ApplicationController
around_destroy :destroy_belongings
def destroy_belongings
old_user = self.user
...
# Here the before_destroy ends.
yield # Here the destroy is performed itself.
# Here the after_destroy starts. It's needed to do this operation after destroy the project because, imagine, the update_belongings method calculates something related to the current number of proyects. And a simple after_destroy is not useful as we would have lost the project owner.
old_user.update_belongings
end
end
You can also see related answers here and here. Moreover this other article could be useful for you.
In Ruby, you can create a class (Model) like this:
class User < ActiveRecord::Base
before_save :do_something
def save
# do something will get called here magically
...
end
def do_something
end
end
Now say in scala I want to do the same thing, is this possible?
Basically whenever any method is called in a scala class, do_something will get called before it.
Note: You could add more methods to get called like:
before_save :do_something, do_something_else
Take a look at the callbacks hooks of the Scala ActiveRecord library.
Scala ActiveRecord provides callback hooks for executing actions
before or after the model is saved, deleted, or validated. You can
override callback methods and implement logic, if necessary. Nothing
is done by default.
I have the following classes:
class Vigil < ActiveRecord::Base
after_update :do_something_cool
private
def do_something_cool
# Sweet code here
end
end
class NewsFeedObserver < ActionController::Caching::Sweeper
observe Vigil
def after_update
# Create a news feed entry
end
end
Everything works as expected; however, the after_update in the sweeper requires that the do_something_cool method in the model has finished before it can run properly. The problem is that the after_update in the sweeper is being called before (or perhaps at the same time as) the do_something_cool callback and it's causing problems.
Does anyone know how to force the after_update in the sweeper to fire after the model callback? Is there better way to achieve this?
Update/Fix: As it turns out, unlike the answer below states, the observer callbacks actually ARE firing in the correct order (after the model callbacks). When I discovered this, I realized something else must be wrong.
The do_something_cool method destroys all of a vigil's slots, and replaces them with the correct number of slots with the correct times. The observer relies on the number of slots to figure out how long the vigil should last. So, the underlying problem was that all of the vigil's slots were being destroyed, and that data was cached, so when I called vigil.slots from the observer, it was using the cached (destroyed slots) data. The solution: simply call vigil.slots(true) at the end of do_something_cool to reload/recache the newly created slots!
It's not going to be running at the same time but you're right, it looks like the Sweeper callback is being run before the Model one.
This post might be helpful : http://upstre.am/2007/10/27/using-and-testing-activerecordrails-observers/
About halfway down (search for 'callback :after_read') they have tried to create custom callbacks for their observers. You could use this to create a after_something_cool ARObserver method that gets called when the Model is done being cool e.g.
class Vigil < ActiveRecord::Base
after_update :do_something_cool
private
def do_something_cool
# Sweet code here
callback :after_something_cool
end
end
class NewsFeedObserver < ActionController::Caching::Sweeper
observe Vigil
def after_something_cool
# Create a news feed entry
end
end
Disclaimer: I've never done this and I've always found sweepers to be temperamental between rails versions so what worked for them might not work for you :(
I have a record that needs to be validated before doing some action. Am I required to use a "valid?" method if I'm doing it with after_create?
For example, I have in my User model:
def after_create
if valid?
...
end
end
I thought it wasn't necessary to put in the valid method, but my application is telling me otherwise. Any idea?
You do not need the if valid? declaration there because after_create gets called after the record has already been validated (and created).
What do you mean your application is telling you otherwise?
Also, for the callback methods, you should use something like:
after_create :call_my_method
private
def call_my_method
# Do cool stuff
end