ActionMailer best practices: Call method in the model or the controller?

余生颓废 提交于 2019-12-03 23:51:14

Late answer, but I want to rationalize on the subject:

Usually, in a web app, you want to send emails either as a direct reaction to a client. Or as a background task, in case we're talking about a newsletter/notification mail sort of thing.

The model is basically a data storage mapper. Its logic should encapsulate data-handling/communication with data storage handling. Therefore, inserting logic which does not relate to it is a bit tricky, and in most cases wrong. Let us take the example: User registers an account and should receive a confirmation email. In this case one could say, the confirmation email is a direct effect of the creation of a new account. Now, instead of doing it in the web app, try to create a user in the console. Sounds wrong to trigger a callback in that case, right? So, callback option scratched. Should we still write the method in the model? Well, if it's a direct effect of a user action/input, then it should stay in that workflow. I would write it in the controller after the user was successfully created. Directly. Replicating this logic in the model to be called in the controller anyways adds unnecessary modularity, and dependency of an Active Record model from Action Mailer. Try to consider sharing the model over many apps, in which some of them don't want Action Mailer for it. For the stated reasons, I'm of the opinion that the mailer calls should be where they make sense, and usually the model is not that place. Try to give me examples where it does make.

Well, depends.

I've used all of those options and your point about 'why should I put this where?' is good.

If it's something I want to happen every time a model is updated in a certain way, then I put it in the model. Maybe even in a callback in the model.

Sometimes you're just firing off a report; there's no updating of anything. In that case, I've normally got a resource with an index action that sends the report.

If the mailer isn't really related to the model that's being changed, I could see putting it in a callback. I don't do that very often. I'd be more likely to still encapsulate it in the model. I've done it, just not very often.

Augustin Riedinger

I'm aware it's been a while but best practices never die, right? :)

Email is by definition asynchronous communication (except for confirmation email, but even this one it should be a best practice to leave a delay before having to confirm).

Hence in my opinion, the most logical way to send it is :

  • in a background action (using Sidekiq or delayed_job)
  • in a callback method : "hey this action is successfully done, maybe we can tell the world now?"

Problem in Rails is that it is not too many callbacks (as in JS for instance): I personnaly find it dirty to have code like:

after_save :callback

def callback
  if test_that_is_true_once_in_the_objects_life
    Mailer.send_email()
  end
end

So, if you really want to think like a programmer, the idea would be to set up some custom callback system in your app.

Eg.

def run_with_callback(action, callback_name)
  if send(action)
    delay.send(callback_name)
  end
end

Or even creating an event system in your app would be a decent solution.

But in the end those solutions are pretty expensive in time so people end-up writing it inline after the action

def activate
  [...]
  user.save
  Mailer.send_mail
  respond_to 
  [...]
end

which is the closest fashion to callback in synchronous programming and results having Mailers call everywhere (in Model and in Controller).

There's several reasons why controllers are a good place for the mailers:

  • Emails that have nothing to do with a model.
  • If your emails depend on several models that dont know about each other.
  • Extracting models to an API should not mean reimplementing mailers.
  • Mailer content determined by request variables that you dont want to pass to the model.
  • If your business model requires a lot of diferent emails, model callbacks can stack.
  • If the email does not depend on the result of model computations.
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!