In an application I have, there are 2 models: Comment and User
I want for example to be able to call a method in User when Comment is updated; as an example send_email. Why not just write send_email in Comment? Because then I'd be contradicting encapsulation.
My question then is, what is the right way to call a callback method on another model. Example:
after_update :user.send_email
Yes you are right, after_update can be used for the callback.Assuming the comment and user mappings generically
#comment.rb
after_update :send_email_to_user
private
def send_email_to_user
user.send_email
end
#user.rb
def send_email
end
after_update takes either the name of a method. In your example :user.send_email is not a valid method name. One way is to create a method that calls send_email on the user object, and then register this as the callback. See Rails Guides for full documentation on Active Record Callbacks.
after_update :send_email
def send_email
user.send_email
end
Related
Seems like i’ve gone back to basics and am missing something… ..
I have two models User and Lists. User has many lists and lists belongs to User… When we remove the user from the list, i.e. the lists user_id == nil, we can’t seem to catch the update through a callback like after_update or after_commit.
class List < ApplicationRecord
after_update :check_user
belongs_to :user
private
def check_user
binding.pry
if user_id.blank?
end
end
end
we even tried before_save but it doesn’t seem like the model sees the change. Are we missing something obvious??
What method are you using to update the record? Because there are methods that DO NOT trigger callbacks like update_column, update_attribute and update_all
How would I create a permalink with an id for a new model?
E.g
animal = Animal.create(name: 'cool dog') #creates animal with id of 1 and name of dog
animal.permalink => "1-cool-dog"
How do you add the proper callback so that id is inserted? before_save or after_save doesn't work
after_save :update_permalink #or before_save
def update_permalink
self.permalink = "#{id} #{name}".parameterize
end
What ends up happening is I get "cool-dog" instead of "1-cool-dog"
And I get why. It's setting an attribute without saving it on after_save. But doesn't work on before_save either because id hasn't been created on a new record.
According to http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
You should use after_commit instead of after_save
Both save and destroy come wrapped in a transaction that ensures that
whatever you do in validations or callbacks will happen under its
protected cover. So you can use validations to check for values that
the transaction depends on or you can raise exceptions in the
callbacks to rollback, including after_* callbacks.
As a consequence changes to the database are not seen outside your
connection until the operation is complete. For example, if you try to
update the index of a search engine in after_save the indexer won’t
see the updated record. The after_commit callback is the only one that
is triggered once the update is committed. See below.
As I commented above you may want to simply override the to_param method of your Animal Model like this.
def to_param
"#{id}-#{name.parameterize}"
end
This will make all of your urls automatically like the permalink you are trying to create and you can still use Animal.find(params[:id])
Perhaps you don't need to save the permalink to the database at all.
def permalink
"#{self.id} #{self.name}"
end
This approach would add a permalink to the model by concatenating the id and name each time the permalink is read.
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
I have a class People and class User (from Devise).
When someone signs up a user row(object) gets created in the User class(table).
I would also like the user.rb model to create a row(object) in the People class(table).
(The user.rb also has "has_one :person" in it.)
I tried the following without success:
after_create :create_person
protected
def create_person
self.create_person email: self.email
end
How could I code this?
after_create :create_person
protected
def create_person
Person.create(self.attributes)
end
But take care, if you want to update the person record when the corresponding user record is updated use after_save and Person.find_or_create_by_email(self.email)
The code you show should work,
the only reason why it might not work would be validations.
Doing create_person will do a save not a save!.
If you have validation on the Person model, it may be failing.
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