Rails mailer views in separated directory - ruby-on-rails

I have small organizatoric issue, in my application I have 3 mailer User_mailer, prduct_mailer, some_other_mailer and all of them store their views in app/views/user_mailer ...
I will want to have a subdirectory in /app/views/ called mailers and put all in the folders user_mailer, product_mailer and some_other_mailer.
Thanks,

You should really create an ApplicationMailer class with your defaults and inherit from that in your mailers:
# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
append_view_path Rails.root.join('app', 'views', 'mailers')
default from: "Whatever HQ <hq#whatever.com>"
end
# app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer
def say_hi(user)
# ...
end
end
# app/views/mailers/user_mailer/say_hi.html.erb
<b>Hi #user.name!</b>
This lovely pattern uses the same inheritance scheme as controllers (e.g. ApplicationController < ActionController::Base).

I so agree with this organization strategy!
And from Nobita's example, I achieved it by doing:
class UserMailer < ActionMailer::Base
default :from => "whatever#whatever.com"
default :template_path => '**your_path**'
def whatever_email(user)
#user = user
#url = "http://whatever.com"
mail(:to => user.email,
:subject => "Welcome to Whatever",
)
end
end
It is Mailer-specific but not too bad!

I had some luck with this in 3.1
class UserMailer < ActionMailer::Base
...
append_view_path("#{Rails.root}/app/views/mailers")
...
end
Got deprecation warnings on template_root and RAILS_ROOT

If you happen to need something really flexible, inheritance can help you.
class ApplicationMailer < ActionMailer::Base
def self.inherited(subclass)
subclass.default template_path: "mailers/#{subclass.name.to_s.underscore}"
end
end

You can put the templates wherever you want, but you will have to specify it in the mailer. Something like this:
class UserMailer < ActionMailer::Base
default :from => "whatever#whatever.com"
def whatever_email(user)
#user = user
#url = "http://whatever.com"
mail(:to => user.email,
:subject => "Welcome to Whatever",
:template_path => '**your_path**',
)
end
end
Take a look at 2.4 Mailer Views for more info.

Easy Solution: Specify Path in ApplicationMailer
class ApplicationMailer < ActionMailer::Base
layout 'mailer'
prepend_view_path "app/views/mailers" # <---- dump your views here
end

Related

How to test instance variables in an ActionMailer object?

There is a before_action callback method in my ActionMailer object which is responsible for setting some instance variables.
class TestMailer < ApplicationMailer
before_action :set_params
def send_test_mail
mail(to: #email, subject: subject)
end
def set_params
#account = account.email
#date = some_action(account.updated_at)
end
end
The question is How one can test these variables in a rspec test?
some thing like:
describe TestMailer do
describe '#set_params' do
described_class.with(account: account, subject: subject).send_test_mail.deliver_now
expect(#date).to eq(Date.today)
end
end
any clue would be highly appreciated.
I think that instead of testing the instance variables, it would be better to test the email body, for example:
expect(mail.body.encoded).to include(account.updated_at)
you could setup a spy inside a mock method instance_variable_set, then validate that spy
class TestMailer < ApplicationMailer
attr_accessor :day
# ...
end
describe TestMailer do
let(:freeze_today) { Time.now.utc }
it '#set_params' do
# freeze today
allow_any_instance_of(TestMailer).to receive(:some_action)
.with(account.updated_at)
.and_return(freeze_today)
# spy
#spy = nil
allow_any_instance_of(TestMailer).to receive(:day=) do |time|
#spy = time
end
described_class.with(account: account, subject: subject)
.send_test_mail
.deliver_now
expect(#spy).to eq(freeze_today)
# or just simple like this
expect_any_instance_of(TestMailer).to receive(:day=).with(freeze_today)
end
end

Include application_helper.rb in ActionMailer

I'm trying to get access to some of my application_helper methods within my mailer, but nothing seems to be working from these SO posts:
View Helpers in Mailers
Access Helpers from mailer
In app/helpers/application_helper.rb I have the following:
module ApplicationHelper
def get_network_hosts
.. stuff get get a #network_hosts object
end
end
In my mailer at app/mailers/user_notifier.rb I have the following:
class UserMailer < ActionMailer::Base
default from: "Support <support#me.co>"
add_template_helper(ApplicationHelper)
def trial_notifier(user)
get_network_hosts
#user = user
#total = user.company.subscription.per_device_charge * #network_hosts.count
if #total < 501
#message = "You'd pay #{#total}/month if you converted to full-access now!"
else
#message = "You'd pay #{#total}/month if you converted to full-access now, but we have a better deal for you!"
end
#url = edit_user_registration_url
mail(to: #user.email, subject: 'What's up?')
end
end
In my mailer I've tried all of the suggestions in the above SO posts, but I'm still getting this error:
`NameError: undefined local variable or method `get_network_hosts' for #<UserMailer:0x007fe756c67c18`>
I'm currently using Rails 4.1.7.
So what do I have to actually do to be able to use my application_helper methods within a mailer?
You can try to do this as following:
In your mailer at app/mailers/user_notifier.rb:
class UserMailer < ActionMailer::Base
default from: "Support <support#me.co>"
helper :application
or you can try this:
helper ApplicationHelper

Rails mailer alternate from address

I can set a default from address in a rails like so;
class UserMailer < ActionMailer::Base
default :from => "\"Company\" <company#example.com>"
def custom_address(user)
# I want to set the from address here
mail(to: user.email, subject: 'Custom from address')
end
end
but how do I set a custom address for a different method? I can't see it listed anywhere in the docs
I may be wrong, but I believe you can just override the from within the mail method.
class UserMailer < ActionMailer::Base
default :from => "\"Company\" <company#example.com>"
def custom_address(user)
# I want to set the from address here
mail(to: user.email, subject: 'Custom from address', from: 'asdf#other.com')
end
end

How To Tell The Difference Between Ruby Class and Instance Methods

Here is some code in a recent Railscast:
class UserMailer < ActionMailer::Base
default from: "from#example.com"
def password_reset(user)
#user = user
mail :to => user.email, :subject => "Password Reset"
end
end
and this is in a controller
def create
user = User.find_by_email(params[:email])
UserMailer.password_reset(user).deliver
redirect_to :root, :notice => "Email sent with password reset instructions."
end
The password_reset method looks like an instance method to me, yet it looks like it's being called like a class method. Is it an instance or a class method, or is there something special about this UserMailer class?
Looking in the source (https://github.com/rails/rails/blob/master/actionmailer/lib/action_mailer/base.rb), Rails uses method_missing to create a new instance of the ActionMailer. Here's the relevant part from the source:
def method_missing(method_name, *args) # :nodoc:
if respond_to?(method_name)
new(method_name, *args).message
else
super
end
end

Override ActionMailer subject in staging environment

I would like all the emails sent from our staging server to have the phrase "[STAGING] " prefaced in the subject. Is there an elegant way to do this in Rails 3.2 using ActionMailer?
Here's an elegant solution I found using ActionMailer Interceptor based on an existing answer.
# config/initializers/change_staging_email_subject.rb
if Rails.env.staging?
class ChangeStagingEmailSubject
def self.delivering_email(mail)
mail.subject = "[STAGING] " + mail.subject
end
end
ActionMailer::Base.register_interceptor(ChangeStagingEmailSubject)
end
This works for Rails 4.x
class UserMailer < ActionMailer::Base
after_action do
mail.subject.prepend('[Staging] ') if Rails.env.staging?
end
(...)
end
Not really, inheritance is about as elegant as it gets.
class OurNewMailer < ActionMailer::Base
default :from => 'no-reply#example.com',
:return_path => 'system#example.com'
def subjectify subject
return "[STAGING] #{subject}" if Rails.env.staging?
subject
end
end
Then you can inherit from each of your mailers.
# modified from the rails guides
class Notifier < OurNewMailer
def welcome(recipient)
#account = recipient
mail(:to => recipient.email_address_with_name, :subject => subjectify("Important Message"))
end
end
I don't think this is as clean as what you were hopeing for, but this will dry it up a bit.

Resources