How to design mutually dependent models with callbacks - ruby-on-rails

So I have 2 models A and B.
When A is saved or destroyed, B needs to be updated based on what happened with A.
When B is saved or destroyed, A needs to be updated based on what happened with B.
I can't just put 2 callbacks, it will lead to models having callbacks recursively. I don't need the callback to fire when model A is saved as result of firing B callback and vice versa.
What is the rails way to do this?

I had a similar situation, the best thing you can do is using a flag in your callbacks
class A
attr_accessor :is_updated_by_callback
after_commit :update_b
def update_b
return if is_updated_by_callback
b.is_updated_by_callback = true
b.update
end
end
and vice-versa

You can use update_columns in your callbacks:
e.g:
model A was saved. In the callback, you have something like this:
recordB.update_columns(:name => '')
update_column will not run any callback.
note: It will also not run validation, or update the updated_at field.

Related

What happens during each of the ActiveRecord model lifecycle stages?

I cannot find documentation describing what happens during each of the ActiveRecord lifecycle stages. The Guide and API list the available callbacks.
How would I know which callback is appropriate if I don't know the state of the model, or what took place earlier in the lifecycle?
For example, when does the model get persisted, and gain an id? The guide lists callbacks:
3.1 Creating an Object
before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
after_commit/after_rollback
So, I'm pretty sure that the model hasn't been persisted, and doesn't have and id, before before_save is called. I would expect the model to have and id before after_save is called. Unfortunately, I have no idea where, between those 2 calls the model was persisted, and gained its id.
EDIT
Again, this is purely one example. I have updated the question to clarify: "What happens during each of the ActiveRecord model lifecycle stages?"
Actually I can't provide you a link where all this stuff is explained.
But if I were you, I'd implement a method that will be invoked on each of these callbacks, and it could help to find out, when model gaines its id.
The "save" action is when the query to insert the record into the database is executed, and it is generally this action that will assign an id.
I can't say for sure that there aren't exceptions, but id's are usually assigned by the database during the insert process. The assigned id can be passed back to the application as part of the insert statement.

Independent observation of model events in Rails

What is the best way in rails to implement the situation where when Model A is created, Model B can observe this and change.
One solution is some kind of filter such as an after_create in Model A. However having some Model B code in Model A seems like a bad practise.
Another solution is to have some code in Model B's controller to handle this which also seems like a bad idea.
Ideally Model B or some kind of independent observer class should be able to observe the creation of all Model A's and then act as required.
Update:
Thanks to OP for pointing out that this was for Rails4 as the question was originally tagged which I had missed to notice. Rails 4 alternative to Observers question here at SO has several great answers.
Original Answer:
This can be done using Observer. Say you want a ModelAObserver, where you'd define the operations required on ModelB, you can create a new file app/models/model_a_observer.rb manually or use the generator rails g observer ModelA.
Then define the required callbacks in the ModelAObserver:
# app/models/model_a_observer.rb
class ModelAObserver < ActiveRecord::Observer
observe :model_a
def after_create(new_model_a_record)
...
# Operations on ModelB
...
end
end

Safe to override save method in ActiveRecord?

In a particular class that extends ActiveRecord::Base, I need to update a different ActiveRecord object whenever this one is created. Is it safe to override the save method and do the following in the save?
def save
super
other = self.other
other.name = self.name
other.save!
end
I'm worried about potential transaction related issues. I assume this would all be 1 transaction, if any part fails, everything is rolled back?
Is there a particular reason why you wouldn't use the hooks provided for this purpose?
You've got after_create, after_save, after_update, before_create, before_save, before_update - and a bunch of others. Wouldn't one of those be suitable?
In fact, given what you've said, it sounds like before_save is what you want, as you can catch any errors that occur whilst saving the second model and prevent the first one from being saved (by returning false from the before_save call).

Rails: create or update in model saving

I have a model (Bookmark) with two fields, call them A and B.
When creating a new model i have to perform a rather complex check on B and, if true update a Bookmark in the database (setting A to the one being passed) and abort the creation, if false just keep it on and create a new Bookmark.
So I set a before_create filter. The problem with the function called is that if I perform an update_attributes on a retrieved object from it and then return false (in order to abort the saving), the update is not performed (for some reason I don't know).
update_attributes returns true and there are no errors, but in the log i only see
SQL (0.2ms)
If i don't return false, the record is correctly updated, but it also inserts a new record (and I don't want this). Any help?
Canceling callbacks
If a before_* callback returns false, all the later callbacks and the associated action are cancelled. If an after_* callback returns false, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks defined as methods on the model, which are called last.
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
When using callbacks, any call to update_attribute from within the before_create callback will bork it.
I was bitten unexpectedly by this as well. I think the source for this came from the rails api, in a one liner, or a blog post that escapes me right now, sorry.
This complicated call isn't a validation though?
Or maybe you can pull out the update_attribute and use it after the complicated logic?

Observers vs. Callbacks

i thought about using observers or callbacks.
What and when you should use an observer?
F.e. you could do following:
# User-model
class User << AR
after_create :send_greeting!
def send_greeting!
UserNotifier.deliver_greeting_message(self)
end
end
#observer
class UserNotifier << AR
def greeting_message(user)
...
end
end
or you could create an observer and let it watch when users becomes created...
What dou you recommened?
One really important distinction to keep in mind, which is related to Milan Novota's answer, is that callbacks on an ActiveRecord have the ability to cancel the action being called and all subsequent callbacks, where as observers do not.
class Model < ActiveRecord::Base
before_update :disallow_bob
def disallow_bob
return false if model.name == "bob"
end
end
class ModelObserver < ActiveRecord::Observer
def before_update(model)
return false if model.name == "mary"
end
end
m = Model.create(:name => "whatever")
m.update_attributes(:name => "bob")
=> false -- name will still be "whatever" in database
m.update_attributes(:name => "mary")
=> true -- name will be "mary" in database
Observers may only observe, they may not intervene.
You can use observers as a means of decoupling or distribution of responsibility. In the basic sense - if your model code gets too messy start to think about using observers for some unessential behavior. The real power (at least as I see it) of observers lies in their ability to serve as a connection point between your models and some other subsystem whose functionality is used by all (or some) of the other classes. Let's say you decide to add an IM notification to your application - say you want to be notified about some (or all) of the CRUD actions of some (or all) of the models in your system. In this case using observers would be ideal - your notification subsystem will stay perfectly separated from your business logic and your models won't be cluttered with behavior which is not of their business. Another good use case for observers would be an auditing subsystem.
A callback is more short lived: You pass it into a function to be called once. It's part of the API in that you usually can't call the function without also passing a callback. This concept is tightly coupled with what the function does. Usually, you can only pass a single callback..
Example: Running a thread and giving a callback that is called when the thread terminates.
An observer lives longer and it can be attached/detached at any time. There can be many observers for the same thing and they can have different lifetimes.
Example: Showing values from a model in a UI and updating the model from user input.

Resources