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
Related
I have a helper function to generate email for user as below
module ApplicationHelper
# Generate unique email for user based on their first and only cat id
def unique_email_for_user(user)
"category#{user.cats.first.id}#mywebsite.com"
end
end
I can call this unique_email_for_user function in my controllers, but not in my Mailer as below
class UserMailer < ApplicationMailer
# Call application_helpers
helper :application
# match the name to views/mailer/send_reminder.html.erb
def send_reminder(user)
#user = user
# Send email to user
mail(to: #user.email, reply_to: unique_email_for_user(#user), subject: "My email subject")
end
end
I get the error as below. I thought adding helper :application to my Mailer would make the necessary connections, No?
This worked
include ApplicationHelper
instead of
helper :application
I want to pass a method from the application controller to a mailer to send shopping cart contents to email.
Method in the application_controller.rb:
def current_order
if session[:order_id].present?
Order.find(session[:order_id])
else
Order.new
end
end
Mailer:
class CartMailer < ApplicationMailer
default from: "from#example.com"
def send_cart_contents
#order = current_order
mail(to: "to#example.com", subject: 'Order from the site')
end
end
And the view:
Order from the site
<% #order.order_items.each do |oi| %>
<%= oi.product.name %>
<% end %>
I'm getting an error: undefined local variable or method 'current_order'.
What am I doing wrong? Thank you.
UPDATE
If I'm passing it as a parameter:
# Preview all emails at http://localhost:3000/rails/mailers/cart_mailer
class CartMailerPreview < ActionMailer::Preview
def cart_mailer_preview
CartMailer.send_cart_contents(current_order)
end
end
I'm also getting NameError.
UPDATE 2
CartMailerPreview don't have access to the current_order, so to test it just pass an id with the parameter. When you use it normally all works well.
The CartMailer is not going to have visibility to the current_order defined in application_controller.rb. This is a good thing.
Best practices is to have the send_cart_contents method accept the order so that it can mail it out:
class CartMailer < ApplicationMailer
default from: "from#example.com"
def send_cart_contents(order)
#order = order
mail(to: "to#example.com", subject: 'Order from the site')
end
end
This way you can mail out a cart from a background job and isolates your mailers from your controllers. Relying on a global current_order is not a good practice.
You should pass current_order to the mailer as a parameter.
I've got a Rails 3 mailer that works fine.
class Notifier < ActionMailer::Base
def cool_email(user_id)
#user = User.find_by_id(user_id)
mail(to: #user.email,
from: 'admin#example.com',
subject: 'hi'
)
end
end
The view for this will render the #user instance variable correctly and the email is sent without any problem.
However, when I namespace the mailer, everything breaks. With the mailer structured like this.
class Foo::Notifier < ::ActionMailer::Base
def cool_email(user_id)
#user = User.find_by_id(user_id)
mail(to: #user.email,
from: 'admin#example.com',
subject: 'hi'
)
end
end
And the view inside app/view/foo, Rails is unable to find the html template. The email sends, but there is nothing inside the body.
What am I doing wrong?
The views should be stored in app/view/foo/notifier, specifically app/view/foo/notifier/cool_email.EXTENSION.
FYI, it's always a good practice to append Mailer to the name of a mailer.
class Foo::NotifierMailer < ::ActionMailer::Base
or
class Foo::NotificationMailer < ::ActionMailer::Base
It prevents conflicts and makes possible to immediately understand the scope of the class.
class UserMailer < ActionMailer::Base
default from: 'notifications#example.com'
def welcome_email(user)
#user = user
#url = 'http://example.com/login'
mail(to: #user.email, subject: 'Welcome to My Awesome Site')
end
end
To send the email I write UserMailer.welcome_email(#user).deliver so my question is: are the methods declared in the Mailer Controller static? Becausei I call welcome_email on a class, so I am cunfused
Not really, but in practice it works as if they were. You have the answer here: How can you call class methods on mailers when they're not defined as such? .
Basically, the Mailer has a method_missing defined that if the method called doesn't exist, it will create an instance of the mailer and call the method on it.
I'm trying to access request.host (well, ideally host_with_port) from a Mailer in Rails. The actually call to request.host is in a Helper:
#/app/helpers/confirmations_helper
module ConfirmationsHelper
def email_confirm_url(token)
"http://#{request.host_with_port}/confirm/#{token}" # failure: undefined method
end
end
#/app/mailers/user_mailer
class UserMailer < ActionMailer::Base
default from: "email#domain.com"
add_template_helper(ConfirmationsHelper) #get access to helpers/confirmations_helper.rb
def email_confirmation(user)
#user = user
#url = "http://www.domain.com/"
mail(to: user.email, subject: "Email Confirmation")
end
end
#config/environments/development.rb
...
config.action_mailer.default_url_options = { :host => "localhost:3000" }
Error I'm getting is:
ActionView::Template::Error:
undefined method `host' for nil:NilClass
Use
ActionMailer::Base.default_url_options[:host]
in your Mailer to access the configured host in in config/environments/
I believe, this is the easiest way.
ActionView::Template::Error:
undefined method `host' for nil:NilClass
This is telling you that request is nil. This is because outside of the scope of your controller (ie. in a class extending ActionMailer::Base) request doesn't exist.
You need to pass the request object or just the part you need (request.host_with_port) to the mailer like you do other data like user in your email_confirmation.
So you have a create method with something like this
def create
#user = User.new
#user.assign_attributes(params[:user])
#user.save
#user.send_email_confirmation
end
Inside your User model you have a send_email_confirmation method like this
class User < ActiveRecord::Base
def send_email_confirmation
UserMailer.email_confirmation(self).deliver
end
Your mailer's email_confirmation looks like
def email_confirmation(user)
#user = user
#url = "http://www.domain.com/"
mail(to: user.email, subject: "Email Confirmation")
end
Making the request to the mailer from your model is not the best idea; you should keep a cleaner separation of concerns. This is part of your problem and why you are finding unwanted complexity when trying to pass something like request from your controller action into the mailer template.
What I might suggest is creating a worker class. Here I explain how to setup classes in lib/ - the same concept can be applied to something like a lib/your_app/workers/user.rb.
You could have the following in this class
module YourApp
module Workers
module User
extend self
def create!(params, options{})
options.reverse_merge! host: ""
user = User.new
user.assign_attributes(params)
user.save
UserMailer.email_confirmation(user, host).deliver
user
end
end
end
end
Your controller action could then simply be
def create
#user = ::YourApp::Worker::User.create!(params[:user], host: request.host_with_port)
end
Your mailer method can now look like
def email_confirmation(user, host)
#user = user
token = "" # define token somehow
#url = "#{host}/confirm/#{token}"
mail(to: user.email, subject: "Email Confirmation")
end
Finally, you can remove send_email_confirmation from your model as well as the email_confirm_url method from your helper since they're no longer used. Two things to note
my example above doesn't include anything in the way of validations/error-checks
my example makes an assumtion about where token is being defined and used
As you can see, by introducing this 'worker' class, there is a clean separation of functionality without duplication.