Rails methods called during a database change - ruby-on-rails

Basically i want to update a set of caches when data is created/removed/amended from a table
what would the most pragmatic way of doing this?

ActiveRecord has a set of callbacks (more info).
I would use the after_commit callback to invalidate your caches.
class MyClass < ActiveRecord::Base
after_commit :invalidate_cache
private
def invalidate_cache
#some logic
end
end

Related

How do I get the Callbacks for an ActiveRecord object?

Is there a way to be able to see what callbacks an ActiveRecord object has?
Say if I have:
class MyModel < ActiveRecord::Base
after_save :my_after_save_function
end
How would you see the what the after_save callback points to?
e.g. MyModel.callbacks
I want to be able to test that my models have after_save callbacks that point to a particular function or callback class
Just found it.
You can use:
MyModel._save_callbacks.map(&:filter)
This article helped me.

Rails: Save data in two tables

I have a user table and a setting table with 1-1 relationship. I want to insert some default setting for the newly created user. I am thinking to use after_create callback of user. However, I am not sure if this will be transactional. What is the best approach for this condition?
You may find after_initialize callback useful for building the setting object for user and assigning default setting. Example:
class User < ActiveRecord::Base
has_one :setting
after_initialize :init_user_setting
private
def init_user_setting
# Assign default setting or build
self.setting = ...
end
end
With this you'll have your complete parent user including child setting. When you call user.save both user and setting are saved and both happen inside a transaction.
You can use after_create callback or observer both. Both will be okay. But You should also set your default values for settings in a initialize method for attribute assignment. Or you can also use user's create method to do same after save call. But it's not a good way. So prefer either call_back or observer.
--UPDATE--
As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model's validations, the registered callbacks, and the database operation to be executed.
The whole callback chain is wrapped in a transaction. If any before callback method returns exactly false or raises an exception, the execution chain gets halted and a ROLLBACK is issued; after callbacks can only accomplish that by raising an exception.
After setting an attribute to false at the end of a before_save callback, I could not for the life of me figure out why the object would never save! Before callbacks must return truthy values or it will rollback.
--Previous--
You need to do something like this. This is my default approach for anything short of dealing with currency.
class User < ActiveRecord::Base
has_one :setting
after_create :setup_user
private
def setup_user
user_settings = self.setting.new
user_settings.attr1 = foo1
user_settings.attr2 = foo2
user_settings.save
end
end
class Setting < ActiveRecord::Base
belongs_to :user
end
Ideally you would put validations on the user and make it so that if a user is valid a setting is also valid. But if you don't do that, you need to use
if !user_settings.save
self.destroy
end
For where to put the default values,if the setting default values depend on the user, stick them in the setup_user method. If the setting default values doesn't care about the user, stick them in a before_save or before_validation method on the setting. With regards to the user, you need to use an after_create method for the case where a user doesn't validate, you want those callbacks to only fire if a user is successfully created. You don't want to user after_validation because the user would not have been created and if the setting contains a foreign id to the user that is not yet created, for an instant your database will be inconsistent.
Transaction based approach
class User < ActiveRecord::Base
has_one :setting
after_commit :setup_user, on: [:create]
after_rollback :undo_user, on: [:create]
private
def setup_user
user_settings = self.setting.new
user_settings.attr1 = foo1
user_settings.attr2 = foo2
user_settings.save!
end
def undo_user
#The users settings didn't save so roll back the user
self.destroy
end
end
class Setting < ActiveRecord::Base
belongs_to :user
end
To put it in the same transaction you need to use after_commit and after_rollback. Using .save! will throw an exception and trigger after_rollback. You will not have a user without the setting.

What is a call back method in rails?

What the heck is a call back method in rails? I see this term being used everywhere while I learn about controllers and models. Can someone provide examples please?
Ref ActiveRecord::Callbacks for the Callbacks w.r.to Activerecord
Callbacks are hooks into the lifecycle of an Active Record object that allow you
to trigger logic before or after an alteration of the object state. This can be
used to make sure that associated and dependent objects are deleted when destroy
is called (by overwriting before_destroy) or to massage attributes before they‘re
validated (by overwriting before_validation). As an example of the callbacks
initiated, consider the Base#save call for a new record
Take an example you have a Subscription model and you have a column signed_up_on which will contains the date at which subscription is created. For this w/o Callbacks you can do something like following in your controller.
#subscription.save
#subscription.update_attribute('signed_up_on', Date.today)
Which will perfectly fine but if suppose you have 3-4 methods in your application where subscription is get create. So to achieve it you have repeat the code in all the places which is redundant.
To avoid this you can use Callbacks and before_create Callback here. So whenever your object of subscription is get create it will assign today's date to signed_up_on
class Subscription < ActiveRecord::Base
before_create :record_signup
private
def record_signup
self.signed_up_on = Date.today
end
end
Following is the list of all the Callbacks
after_create
after_destroy
after_save
after_update
after_validation
after_validation_on_create
after_validation_on_update
before_create
before_destroy
before_save
before_update
before_validation
before_validation_on_create
before_validation_on_update

ActiveRecord Always Run Some Code After Retrieval

I'm trying to always run some code after .find or .where or whatever is used to retrieve objects.
For example, the following describes what I want, but does not work
Class Person < ActiveRecord::BA
#mortality=true
end
I want #mortality=true to run whenever a Person object is created
And based on my current understanding of ORM/ActiveRecord, a new object is created whenever retrieval is done. Hopefully that is correct.
You want to do this in the after_initialize method:
class Person < ActiveRecord::Base
def after_initialize
#mortality = true
end
end
Note that this is something you should avoid doing if possible because it happens on every object, even when you retrieve enormous result sets.
In this (albeit simple) case, you can do the assignment lazily by overriding the getter:
class Person < ActiveRecord::Base
def mortality
#mortality.nil? ? true : #mortality
end
end
(you can't use the nil gate ||= here because it filters false values as well)
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
Look for after_find and after_initialize callbacks.

How to invoke a method every time database is modified

I am writing a Ruby on Rails app and I want a method to be called every time the database is modified. Is it possible to do this without inserting a method call in every location where the database is modified?
I like KandadaBooggu's answer but if you did not want to monkey with AR you might be able to do this with an Observer.
class AllObserver < ActiveRecord::Observer
observe :model_a, :model_b
def after_save(record)
logger.info("CREATED #{record.class}")
end
def after_update(record)
logger.info("UPDATED #{record.class}")
end
end
Just add the models that you want to observer. In this example it will log updates to ModelA and ModelB
Depends on the database. Many databases have very powerful stored procedure languages that can, among other things, invoke web services.
You could have a trigger on the important database tables call a ruby web service that calls your method.
Or you can have triggers that update an event table, and then have a process that watches for changes on that table and then fires the method.
There's likely some meta-programming magic that you might be able to use to tweak your ruby code to invoke the change as well.
All sorts of options.
If you want to log all models:
Monkey patch the ActiveRecord::Base class.
class ActiveRecord::Base
after_save :log_something
after_destroy :log_something
private
def log_something
end
end
For a specific model:
class User < ActiveRecord::Base
after_save :log_something
after_destroy :log_something
private
def log_something
end
end
Have you considered using: after_update or before_update in ActiveRecord:
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

Resources