Syl
Syl

Reputation: 3829

Should i send a mail from a model callback or from a controller?

I have a model that, when it is in a particular state, need some additional data and then send a mail.

To do so i have a before validation callback that say

"i'm in this state where i need data, do i need more data? No? then i'll change my state and send a mail".

This state is triggered every night by a cron rake, and sometime no data is needed, so it should send the mail ASAP. Later when data is collected, the callback will be triggered and the mail will go it's way.

I have read and been told that mail should be sent from controller only. But here it mean i would need to send mail in my controller and in my rake.

Why is it "bad" to send mail from the callback?

Isn't it a bad idea to go from one place to trigger the mail to two different places?

Upvotes: 3

Views: 1188

Answers (1)

shime
shime

Reputation: 9008

Why would it be bad?

Use ActiveRecord::Observer for that. It's a perfect use case, because mail logic probably doesn't belong neither to your model, nor controller.

class WelcomeEmailObserver < ActiveRecord::Observer
  observe :user

  def after_create(user)
    if user.purchased_membership?
      GreetingMailer.welcome_and_thanks_email(user).deliver
    else
      GreetingMailer.welcome_email(user).deliver
    end
  end
end

# app/models/user.rb
class User
end

# config/application.rb
class Application < Rails::Application
  config.active_record.observers = :welcome_email_observer
end

Example stolen from here.

I would also suggest that you leave your observers stateless, because it's hard to debug them otherwise. Use them only for callbacks with third-party API's, emails or some other stuff that's not very related to your application.

I would suggest you to use no-peeping-toms for testing them. Don't isolate observer test from model tests - that doesn't make much sense, but be sure to test models with and without observers. With this helper gem, you can target an observer you like and switch it on or off.

Enforce single responsibility principle on observers, don't couple them to models. Leave persistence logic away from observers. Use model callbacks for persistence-related concerns.

Also, I would like to warn you to be careful with observers when using something like Sidekiq. It's sometimes faster than ActiveRecord callbacks, so you should use the after_commit callback to avoid conflicts.

Alternatives

ActiveRecord::Observers will be deprecated in Rails 4.0, because they're extracted into a gem. You can still use them, though, but seems like Rails core team enforces extracting everything into classes with single responsibility. Choosing what to use depends on your taste.

Observers make everything less explicit and sure are more complicated to test. They can still be a good OO citizens if you know what you're doing.

Using plain old ruby objects seems more DCI-like to me and that's a big plus because it expresses your intentions more clearly.

Upvotes: 6

Related Questions