views:

44

answers:

3

I have an app with the following models: User, Task, and Assignment. Each Assignment belongs_to a User and a Task (or in other words, a Task is assigned to a User via an Assignment).

Once a User completes a Task, the Assignment is marked as complete, and the app immediately creates a new Assignment (or in other words, assigns the task to someone else).

Immediately after creating this new Assignment, I want to send an email to the new assignee. I know I can do this one of three ways:

  1. Explicitly send the email in my controller.
  2. Send the email in a callback on the Assignment model.
  3. Create an observer on the Assignment model and send the email in after_create.

Which of these options do people think is best, and why? #1 seems bad to me, because I don't want to have to remember to send it in every action that might complete an Assignment. I've heard a couple people say that Rails observers are bad and should be avoided, but I'm not sure if they're people I should trust or not. Any other opinions?

+1  A: 

i would go for observers as they reduce clutter in your model / controller code and i can think of no downside in using them ...

iirc sending an email after save email is even an example in the active record observers documentation

roman
Yeah I saw it, and that's one of the reasons I asked. However, a few people told me to avoid Observers, so I just wanted to get a few more opinions.
jboxer
if there are any problems with observers i would be interested too!
roman
The reason they gave was that nothing directly references the observers, so new people looking at the code may not notice them. It seems like a somewhat weak reason to me, and more indicative of a hole in a developer's Rails knowledge than a bad code smell, but I still want to hear the opinions of those more experienced than me.
jboxer
to be sure, the observer.rb lies right next to the model.rb in the models directory.
Eric Hill
Yeah, that's what I was thinking.
jboxer
A: 

You can also do a combination of things. You could use observers for one action, and if there is just a single email for one other action you could use option #1 for it.

Have you heard of acts_as_state_machine, or any other similar solutions?

http://github.com/rubyist/aasm

They allow you to define a state of each object and different things that can happen with state changes.

This allows you to have as much logic as you need about when things are sent, if you need this much. Can be overkill, but can be really handy. I suggest because you want an email sent when a task is 'completed' which sounds like it may be a type of state or status column in your Task model.

spotman
This looks cool, and I'll definitely keep it in mind. For the moment though, it's definitely overkill. Thanks though.
jboxer
+3  A: 

You're right, the first way isn't a good approach. Observers are my preferred way to go, for a couple reasons.

First, if you use TDD (test-driven development) you can shut off observers to more purely test the model without every creation firing off a mailer creation. Then you can unit test the mailer and observer separately.

Second, the idea of separating callbacks creates cleaner code. Callbacks aren't really part of your model, they are events. Your model contains the functions and attributes necessary to run itself, and the callbacks (implemented with observers) are separate event handlers.

That said, I don't think your second option is "bad" or less professional. Either way works as long as it's at the model level, instead of controllers or (even worse) views.

Jaime Bellmyer
The TDD thing is a fantastic reason to use Observers. Unless someone makes a more compelling point, I'll check you off soon.
jboxer