How can I have Rails automatically include the locale in every link in every email?
I want:
http://www.some-company.com/en/welcome
...instead of just...
http://www.some-company.com/welcome
I just know that for normal views, it would be:
class ApplicationController < ActionController::Base
...
def self.default_url_options(options={})
options.merge({ locale: I18n.locale })
end
...
end
But how is this achieved for email views?
It should work if you put something like self.class.default_url_options[:locale] = my_locale in your mailer methods. You'll have to set that my_locale somehow - maybe pass it as an argument to the mailer method.
Related
I have sub-classed the default form builder to add some additional methods. Sample code is below.
module ApplicationHelper
class AppFormBuilder < ActionView::Helpers::FormBuilder
def coordinates_field(method, options = {})
options[:readonly] = 'true'
....
#template.text_field(#object_name, method, objectify_options(options))
end
end
end
This works well, but to use it I have to change the view code for every form that uses the coordinates_field method, i.e.,
<%= form_for #object, :builder => ApplicationHelper::AppFormBuilder do |f| %>
...
<% end %>
It sounds like it is theoretically possible to change the default form builder globally (config.action_view.default_form_builder), but I can't seem to get this to work. This is what I have tried in /config/application.rb:
module Web
class Application < Rails::Application
...
config.action_view.default_form_builder = "ApplicationHelper::AppFormBuilder"
end
end
Which results in error undefined method 'new' for "ApplicationHelper::AppFormBuilder":String when I hit a view that has a form.
If I instead try this
config.action_view.default_form_builder = ApplicationHelper::AppFormBuilder
I get the error *config.action_view.default_form_builder = ApplicationHelper::AppFormBuilder* when the application starts.
Can anyone provide guidance on how to get this to work?
As mentioned in the official docs, in Rails 5 the correct way is to specify it in a controller. To make it application wide, just set in your ApplicationController:
class ApplicationController < ActionController::Base
default_form_builder AppFormBuilder
end
This makes much more sense. FormHelpers are part of the view layer and not the application config.
ApplicationHelper::AppFormBuilder is not required yet at the time application.rb loads. You can try to put this in a separate initializer file (create one in config\initializers):
module Web
class Application
ActionView::Base.default_form_builder = AppFormBuilder
end
end
I like Max's answer, but (rails n00b here so YMMV) I believe this is equivalent and cleaner, directly in config/application.rb:
config.after_initialize do
ActionView::Base.default_form_builder = MyCustomHelper::MyCustomFormBuilder
end
obviously you replace the names MyCustomHelper and MyCustomFormBuilder.
took me about 48hrs to figure this out, thanks to all that well-structured rails documentation.
You should be able to set this from an initializer as you would for other config options. Create a default_form_builder.rb file under config/initializers/. The syntax should simpler than in #Max's answer.
Rails.application.config.action_view.default_form_builder = AppFormBuilder
I suggest you do not include this in a helper. Add it as separate class inside the /lib directory. You may or may not need to prefix the with the module it's contained within.
Finally, you can set this globally from config/application.rb, but you would have to pass it as a string since the class may not be loaded when rails starts up.
config.action_view.default_form_builder = 'AppFormBuilder'
Rails 5+ 👌
class ApplicationController < ActionController::Base
default_form_builder AppFormBuilder
end
Rails 4 🏖
class ApplicationController < ActionController::Base
ActionView::Base.default_form_builder = AppFormBuilder
end
... or if you prefer an initializer, I'd recommend setting it as a string instead, that way you will not have to restart Rails every time you change something in your form builder.. 😴 (since the class may not be loaded when rails starts up. https://stackoverflow.com/a/27992240/2037928)
module Web
class Application
ActionView::Base.default_form_builder = "AppFormBuilder"
end
end
I am wondering what is a clean and conventional way for setting instance variables in Mailers? Currently, I have re-defined the initialize method in Mailer and subsequently overwrite certain instance variables when needed in any mailers that inherit from Mailer.
class Mailer < ActionMailer::Base
attr_reader :ivar
def initialize
super
#ivar = :blah
...
end
end
This only seems weird to me because new is a private method for mailers. For example, if I were to try to retrieve these in the rails console, I need to do the following:
mailer = Mailer.send(:new)
mailer.ivar
I have also considered adding them to the default hash like so:
class Mailer < ActionMailer::Base
default ivar: :blah,
...
end
The only problem being that I need to create a method like this to retrieve the ivars:
def default_getter(ivar)
self.class.default[ivar]
end
Neither way seems particularly clean to me. I've considered using class variables, but I'm wondering if someone could suggest a cleaner way. Thanks.
Just a little bit late...
You can use before_action callbacks
http://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-callbacks
I have trying to access helper methods from a rails 3 mailer in order to access the current user for the session.
I put the helper :application in my mailer class, which seems to work, except the methods defined therein are not available to my mailer (i get undefined errors). Does anyone know how this is supposed to work?
Here's my class:
class CommentMailer < ActionMailer::Base
default :from => "Andre Fournier <andre#gfournier.com>"
helper :application
end
Thanks,
Sean
To enable you to access application helpers from the ActionMailer views, try adding this:
add_template_helper(ApplicationHelper)
To your ActionMailer (just under your default :from line).
Use helper ApplicationHelper
class NotificationsMailer < ActionMailer::Base
default from: "Community Point <Team#CommunityPoint.ca>"
helper ApplicationHelper
helper NotificationMailerHelper
# ...other code...
NOTE: These helper methods are only available to the Views. They are not available in the mailer class (NotificationMailer in my example).
If you need them in the actual mailer class, use include ApplicationHelper, like so:
class NotificationMailer < ActionMailer::Base
include ApplicationHelper
# ... the rest of your mailer class.
end
From this other SO question.
This is a very old question, but I don't see the full answer, so I will try as I didn't find another resource.
It depends on what you are doing with the methods that have been defined in the helper module. If they are class methods, and everything that's not called on a specific instance seem to be a class methods for 3.2.13, you need to use
extend ApplicationHelper
if an instance methods
include ApplicationHelper
and if you want to use them in a mailer view
helper ApplicationHelper
You could try mixing in the required helper module:
class CommentMailer < ActionMailer::Base
include ApplicationHelper
end
Josh Pinter's answer is correct, but I discovered that it is not necessary.
What is necessary is to name the helper correctly.
NotificationMailerHelper is correct. NotificationMailersHelper (note the s) is not correct.
The class and filename of the helper must match and be correctly spelled.
Rails 3.2.2
include ActionView::Helpers::TextHelper worked for me in the Mailer controller (.rb file). This allowed me to use the pluralize helper in a Mailer controller action (helpers worked fine from the get go in Mailer views). None of the other answers worked, at least not on Rails 4.2
If you want to call helper method from ActionMailer you need to include helper (module) in Mailer file as, if Helper module name is “UserHelper”, then need to write following in Mailer file
class CommentMailer < ActionMailer::Base
default :from => "Andre Fournier <andre#gfournier.com>"
add_template_helper(UserHelper)
end
Or
class CommentMailer < ActionMailer::Base
default :from => "Andre Fournier <andre#gfournier.com>"
include UserHelper
end
Hope this is helpful.
The single method version of promoting a method to being a helper that is available in ApplicationController also works in ActionMailer:
class ApplicationMailer < ActionMailer::Base
helper_method :marketing_host
def marketing_host
"marketing.yoursite.com"
end
end
from there you can call marketing_host from any of your mailer views
I'm not sure exactly what you are doing here, but when I want to access current_user from a mailer, I make a mailer method that I pass the user to as an argument:
class CommentMailer < ActionMailer::Base
default :from => "Andre Fournier <andre#gfournier.com>"
def blog_comment(user)
#recipients = user.email
#from = "andre#gfournier.com"
#sent_on = Time.now
#timestamp = Time.now
#user = user
end
end
With the above, #user, as well as all the other instance variables, are accessible from inside the mailer views ./views/comment_mailer/blog_comment.html.erb and ./views/comment_mailer/blog_comment.text.erb
Separately, you can make a helper called
comment_mailer_helper.rb
and put into that helper any methods that you want to be available to your mailer's views. This seems to me more like what you might want, regarding helpers, because helpers are designed to help views, whereas a mailer is analogous to a controller.
None of the *_path helpers are accessible by default inside of an email. It is necessary instead to use the *_url form of the wanted helper. So, for instance, instead of using user_path(#user) it is necessary to use user_url(#user).
See at Action Mailer basics.
A hackish means of achieving what I wanted is to store the objects I need (current_user.name + current_user.email) in thread attributes, like so: Thread.current[:name] = current_user.name. Then in my mailer I just assigned new instance variables to those values stored in the thread: #name = Thread.current[:name]. This works, but it won't work if using something like delayed job.
I'm writing a plugin that adds a method to controllers and declares it as a helper method. If it were done statically (rather than through the plugin), it would look something like this:
# in RAILS_ROOT/app/controllers/stuffed_animals_controller.rb
class StuffedAnimalsController < ActionController::Base
private
def bear
'Teddy Bear'
end
helper_method :bear
end
# in RAILS_ROOT/app/views/stuffed_animals/index.html.erb:
<%= bear -%>
It works just fine. I want to test that :some_helper_method is actually a helper method, though. I tried this:
def test_declared_bear_as_helper_method
assert StuffedAnimalsController.helper_methods.include?(:bear)
end
Unfortunately, ActionController::Base does not have a :helper_methods class method. Anyone know where I can get the list of things a class exposes via :helper_method?
Got it!
def test_declared_bear_as_helper_method
helper = Object.new
helper.extend StuffedAnimalsController.master_helper_module
assert helper.respond_to?(:bear)
end
I want to use the methods I defined in app/helpers/annotations_helper.rb in my ReportMailer views (app/views/report_mailer/usage_report.text.html.erb). How do I do this?
Based on this guide it seems like the add_template_helper(helper_module) method might do what I want, but I can't figure out how to use it.
(BTW, is there a reason you get access to a different set of helpers in mailer views? This is pretty annoying.)
In the mailer class that you are using to manage your emails:
class ReportMailer < ActionMailer::Base
add_template_helper(AnnotationsHelper)
...
end
In Rails 3, just use the helper method at the top of your ActionMailer class:
helper :mail # loads app/helpers/mail_helper.rb & includes MailHelper
I just passed in a block, since I only need it in the one Mailer:
helper do
def host_url_for(url_path)
root_url.chop + url_path
end
end
(be sure to set config.action_mailer.default_url_options.)
(and if you use url_for, be sure to pass in :only_path => false)
For all mailers in Rails 3 (setting "application" helper):
# config/application.rb:
...
config.to_prepare do
ActionMailer::Base.helper "application"
end
(This is an old question but Rails has evolved so I'm sharing what works for me in Rails 5.2.)
Typically you might want to use a custom view helper in rendering the subject line of an email as well as the HTML. In the case where the view helper is in app/helpers/application_helper.rb as follows:
module ApplicationHelper
def mydate(time, timezone)
time.in_time_zone(timezone).strftime("%A %-d %B %Y")
end
end
I can create a dynamic email subject line and template which both use the helper but I need to tell Rails to use the ApplicationHelper explicitly in apps/mailer/user_mailer.rb in two different ways, as you can see in the second and third lines here:
class UserMailer < ApplicationMailer
include ApplicationHelper # This enables me to use mydate in the subject line
helper :application # This enables me to use mydate in the email template (party_thanks.html.erb)
def party_thanks
#party = params[:party]
mail(to: 'user#domain.com',
subject: "Thanks for coming on #{mydate(#party.created_at, #party.timezone)}")
end
end
I should mention that these two lines work just as well so choose one or the other:
helper :application
add_template_helper(ApplicationHelper)
FWIW, the email template at app/views/user_mailer/party_thanks.html.erb looks like this:
<p>
Thanks for coming on <%= mydate(#party.created_at, #party.timezone) %>
</p>
And the app/controller/party_controller.rb controller looks like this
class PartyController < ApplicationController
...
def create
...
UserMailer.with(party: #party).party_thanks.deliver_later
...
end
end
I have to agree with OP (#Tom Lehman) and #gabeodess that this all feels quite convoluted given https://guides.rubyonrails.org/action_mailer_basics.html#using-action-mailer-helpers so perhaps I am missing something...
For Ruby on Rails 4, I had to do 2 things:
(1) As Duke already said, if the helper you want to add is UsersHelper for example, then add
helper :users
to the derived ActionMailer class (e.g. app/mailers/user_mailer.rb)
(2) After that, I got a new error:
ActionView::Template::Error (Missing host to link to! Please provide the :host
parameter, set default_url_options[:host], or set :only_path to true)
To fix this, add the line
config.action_mailer.default_url_options = { :host => 'localhost' }
to each of the config/environments/*.rb files. For config/environments/production.rb, replace localhost with a more appropriate host for the production helper-generated urls.
Q: For #2, why does the mail view need this information, and the regular views do not?
A: Because the regular views don't need to know the host, since all generated links are served from the host they link to. Links that show up in emails are not served from the same host (unless you are linking to hotmail.com or gmail.com, etc.)
You can just add in your mailer
helper :application
or whatever helper you need
This is what I did in rails 6
class ApplicationMailer < ActionMailer::Base
default from: 'community#example.com'
layout 'mailer'
# Add whatever helper you want
helper :application
end
in my case for Rails4, i do like this:
# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
add_template_helper ApplicationHelper
...
end
and
# app/mailers/user_mailer.rb
class AccountMailer < ApplicationMailer
def some_method(x, y)
end
end
so that you do not have to specify add_template_helper everywhere.