Rails: Indicating that a model has an observer - ruby-on-rails

A model I've implemented needs to escape/format one of the fields into an html-friendly version of the entered text (for the sake of argument, lets say it's a blue/redcloth stlye thing).
I implemented this in an observer with the before_save callback, all working, no complaints. I am learning rails as I code and it struck me that from just looking at the model there is no indication that it has an observer.
Is there a neat way of indicating (for other programmers who may view/edit my code) that a model has an observer or should I just do this in a comment (or not at all).

As far as I know, you do not need to specify on the target class/model that it has an observer all you need to do is make sure that the observer lists which models it is observing.
class ContentObserver < ActiveRecord::Observer
observe :answer, :audio_clip, :document
#This right here ^
def after_update(record)
record.recent_activity.save!
end
end

Have a spec/test file for the observer. If, in the future, someone changes the model in a way that upsets the observer then the spec/test will fail which will alert them.

Related

Default creation of has_one dependency in rails

I'd like to understand best practices for creating a dependency of a model in rails. The scenario is simple. The two models are:
class Main < ActiveRecord::Base
has_one :dependent
validates :dependent, presence: true
end
class Dependent < ActiveRecord::Base
end
(Note that I want to validate to ensure the dependent always exists)
Whenever a Main object is created I want a Dependent object to be created and "default initialized". I come from a background of C++ hence I view this problem as one of constructing a member variable whose type is some class which has a default constructor.
There are a bunch of ways I can solve this.
Put logic in before_validation to create a Dependent.
This feels very "un-railsy". I wanted to do this in before_create but validations are done before that callback. Doing it in before_validation is ugly as this callback is called both on create and on update, which makes the logic tricky/messy.
Put logic in .new
This feels very very "un-railsy" and is probably conceptually wrong. I'd see new as performing ActiveRecord construction which happens before the model is built.
Make the caller do the work
Whenever a Main object is created it must be done via new-save rather than create. The calling code then has to create Dependent itself, albeit with default values, e.g.
Main.new do |m|
m.dependent = Dependent.create
end
This is annoyingly burdensome on callers and causes a lot of duplicate code. I could pack this into a factory type method but the same problem exists that calling code needs to do some legwork.
Is there a canonical solution to this?
You should try using after_create callback and create Dependent instance in this method.
To answer my own question, I found a callback I hadn't see listed before: after_initialize. I can do what I want here.
A note for others:
My particular case is quite straightforward as I always want my dependent class to be default initialized and the user doesn't ever need to set anything. However, in a more complex situation this wouldn't work and initializing dependents may require:
Explicit initialization at the site of creation
UI for user to initialize the dependent using #accepts_nested_attributes_for

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

How can I see the request in an AR Observer for audit purposes?

I have a simple Observer that is watching certain models and writing the model to_json to a table along with the model name. I want to also track who performed the change, but I can't figure out how to access the current user.
Perhaps there is a simple gem or bit of code I can put in ApplicationController or something like that that could help?
There's no clean way to do this that I can think of. You could try using the User.current pattern but I'm not a fan of that method and I'm not sure it would communicate to observers properly.
In cases like these where I want to know who (last) changed the object, I usually add a 'changed_by' and 'changed_at' type of attribute to the model itself, and set those in the controller. That would make it much easier to leave an audit trail as well. You could even use attr_accessor if you don't actually want to store the values in the database.

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