What is ActionMailer default_url_options? - ruby-on-rails

I must not understand something trivial about email but what does the host in defaul_url_options do? The need for the smtp settings make sense to me to configure how the email will be sent out but how is default_url_options relevant to that?
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
host = '<your heroku app>.herokuapp.com'
config.action_mailer.default_url_options = { host: host }
ActionMailer::Base.smtp_settings = {
:address => 'smtp.sendgrid.net',
:port => '587',
:authentication => :plain,
:user_name => ENV['SENDGRID_USERNAME'],
:password => ENV['SENDGRID_PASSWORD'],
:domain => 'heroku.com',
:enable_starttls_auto => true
}

The default_url_options setting is useful for constructing link URLs in email templates. Usually, the :host, i.e. the fully qualified name of the web server, is needed to be set up with this config option. It has nothing to do with sending emails, it only configures displaying links in the emails.
The need for setting this is nicely documented in the Rails Guides as well ActionMailer::Base sources:
URLs can be generated in mailer views using url_for or named routes. Unlike controllers from Action Pack, the mailer instance doesn't have any context about the incoming request, so you'll need to provide all of the details needed to generate a URL.
When using url_for you'll need to provide the :host, :controller, and :action:
<%= url_for(host: "example.com", controller: "welcome", action: "greeting") %>
When using named routes you only need to supply the :host
<%= users_url(host: "example.com") %>
So, to reword the docs, in web pages, the name of the current web server (to be used in absolute links) is taken from the incoming request info. But you don't have this information available when rendering an email (there's no request), that's why you have to provide it manually, so that links in emails work properly.

Have you ever tried to generate URLs within an ActionMailer template? If you did at least once, then you are probably familiar with the following error:
ActionView::TemplateError (Missing host to link to! Please provide :host parameter or set default_url_options[:host])
This happens because ActionMailer instance doesn't have any context about the incoming request so you'll need to provide the :host, :controller, and :action:. If you use a named route, ActionPack provides controller and action names for you. Otherwise, with the url_for helper you need to pass all the parameters.
<%= message_url %>
<%= url_for :controller => "messages", :action => "index" %>
Regardless your choice, you always need to provide the host option to generate an URL in ActionMailer. As shown by the ActionMailer guide, you basically have two ways to pass the host value to ActionMailer:
1. set a global value
2. pass the option each time you generate an URL
Defining default_url_options is better then passing URL everytime.
That's why we do.

Related

Rails, transactional email and sender validation

I would like to use Sendinblue to send transactional email from my Rails web application.
To configure my application to use Sendinblue I edited config/environments/production.rb as follows (NOTE: fireworks.com is just an example):
Rails.application.configure do
...
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
host = 'fireworks.com'
config.action_mailer.default_url_options = { host: host }
ActionMailer::Base.smtp_settings = {
:address => 'smtp-relay.sendinblue.com',
:port => '587',
:authentication => :plain,
:user_name => ENV['SENDINBLUE_USERNAME'],
:password => ENV['SENDINBLUE_PASSWORD'],
:domain => 'fireworks.com',
:enable_starttls_auto => true
}
...
end
Do I need any gem, like sib-api-v3-sdk?
I suppose this gem is useful only for sending email using the Sendinblue API.
At Sendinblue I authenticated my domain 'fireworks.com' and created a sender, noreply#fireworks.com, which I would use for account activation.
app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
default from: "noreply#fireworks.com"
layout 'mailer'
end
In order for me to use this sender, Sendinblue wants me to verify (or validate) it.
Now the point is that noreply#fireworks.com does not exist. Michael Hartl in his tutorial uses noreply#example.com, I would use noreply#fireworks.com because I own the fireworks.com domain.
Since I would not be able to validate the sender, I thought that the only solution would be creating in my server the noreply user account, configuring Postfix to receive email from Internet and adding an MX record to my DNS zone.
I would maintain this configuration until I receive the validation email.
Is this solution necessary? Do I have any other choice?
Now the point is that noreply#fireworks.com does not exist.
Before continuing, I want to point out that this is ultimately not a good practice. You'll want some address to receive and recognize bounces.
Having said that, I just checked their documentation and it says here that instead of validating individual email addresses, you can just validate the whole domain via DNS entry, file upload or by sending them an email.

Using Sidekiq with Mailer to send asynchronous emails [RAILS]

So I am currently trying to use the Rails gem 'sidekiq' to send a large number of emails asynchronously, so it does not bog down the load time for the next page.
Everything I have works except for this:
FeedbackMailer.invite_contact(current_user, current_business, contact).deliver()
This is my own mailer and a set of paramters which it must take to create the email for the client.
The error that gets thrown by sidekiq is this:
Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true
The default_url_options[:host] is set in the application.rb, but does not seem to get read here. I also tried setting only_path: true and got the same error still.
I can send emails just find when I am outside one of sidekiq's workers, the problem only exists there. Anyone know of a workaround so that emails can be sent from sidekiq's workers?
Also before you answer, using Mailer.delay doesnt really solve my problem, because there is alot of other code around the mailing that must go off during every iteration of the loop.
This error happens when your mail is not configured properly. Check that you have something like this in the relevant environment file (production.rb or staging.rb)
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
:address => 'smtp.something.com',
:port => 587,
:domain => 'yourdomain.com',
:user_name => 'account-admin#yourdomain.com',
:password => 'your secure password',
:authentication => 'plain',
:enable_starttls_auto => true
}

Rails: generate a full URL in an ActionMailer view

I'm using ActionMailer to send a sign up confirmation email. The email needs to contain a link back to the site to verify the user, but I can't persuade Rails to generate a full URL (including the domain etc).
I'm using:
<%= url_for :controller => 'login', :action => 'verify', :guid => #user.new_user.guid, :only_path => false, :host => 'http://plantality.com' %>
in my view
Part b:
In development mode Rails gripes if I don't specify the host explicilty in the link above. But I don't want to do this in production. Any solutions?
To solve the problem to pass a host for generating URLs in ActionMailer, check out this plugin and the reason why I wrote it.
To solve the first issue, use named routes when applicable. Instead of
<%= url_for :controller => 'login', :action => 'verify', :guid => #user.new_user.guid, :only_path => false, :host => 'http://plantality.com' %>
assuming the route is called login, use
<%= login_url(:guid => #user.new_user.guid) %>
Note, I'm using login_url, not login_path.
I'm not sure if it is what you want but in config/environments/development.rb you can specify default options for mailer urls
config.action_mailer.default_url_options = {
:host => "your.host.org",
:port => 3000
}
you can do the same in config/environments/production.rb
I don't know why the previous solutions seem so complicated, but since I'm here why not give my 2 cents...
Go to /config/environments and add:
config.absolute_site_url = 'your site url'
for the respective environment (ie. in development.rb, test.rb, or production.rb). Restart web server.
This allows you to call Rails.application.config.absolute_site_url to get the desired URL. No need for plugins or weird cheat, just store the site url as an application wide variable.
I think its not 100% correct way but this can also be a solution :
See the Using asset hosts section in the documentation. You need to specify an asset_host. You can also construct it dynamically from the request chaining "#{request.protocol}#{request.host_with_port}/login/?guid=#{#user.new_user.guid}"
To generate url, try this
Rails.application.routes.url_helpers.user_url(User.first.id, host: 'yourhost.io')
this will generate url like this:
http://yourhost.io/users/1
As well you can pass some params
expires = Time.now + 2.days
params = {expires: expires}
u = User.first.id
Rails.application.routes.url_helpers.user_url(u, params, host: 'host.com')
will generate:
http://yourhost.io/users/1.expires=2018-08-12+15%253A52%253A15+%252B0300
so you can werifi in action if link is not expired

localhost on rails

I am sending an email that contains a link to my website. I want to be able to test it locally and be able to move the scripts around to different hosts easily.
In my email right now I use the following:
<%= url_for(:host => 'localhost:3000', :controller => "user_activations", :action => "show", :id=>#id, :confirm=>#passcode) %>
This works for when testing locally but will obviously fail for production. Is there an easy way to have rails (or ruby) detect what the current host is? I'm thinking something like $_SERVER of php.
I realize I can use some logic using my environment variable but I would like to avoid this.
Thanks
I define a constant 'HOST' in my environment.rb that sets my host. Alternatively you can use request.host or request.domain.
in environments/development.rb
config.action_mailer.default_url_options = { :host => "localhost", :port => 3000 }
in environments/production.rb
config.action_mailer.default_url_options = { :host => "www.xyu.at" }
and use tests with rspec-email :)

How do I configure the hostname for Rails ActionMailer?

I'm working on a fairly traditional forgot password email - I want to email the user a password change token embedded in a link that they can click on in order to change their password. I'm emailing via the traditional ActionMailer.
If I use a normal link_to tag
<%= link_to "click here", :controller => foo, :action => 'bar', :token => token %>
I get a relative link - rather useless from an email.
If I add in
:only_path => false, then it errors saying I need to set default_url_options[:host]. The ActionController docs imply that you do that by overriding the #default_url_options methods in your controller. Surely there's a configuration option to tell Rails what it's hostname is without adding my own config file, parsing it, etc?
default_url_options is available from config.action_mailer and should be set in your environment's configuration file.
For example, in config/environments/production.rb:
config.action_mailer.default_url_options = {
:host => 'www.yourdomain.com'
}
For local testing, modify config/environments/development.rb:
config.action_mailer.default_url_options = {
:host => '127.0.0.1',
:port => 3000
}
Then, assuming you have a named route called forgot_password_login, you can generate the login link URL in your mailer using something like this:
forgot_password_login_url(:token => 'a7s8q15sk2...')
You probably want to set :protocol => 'https' as well, btw.
config.action_mailer.default_url_options = {
:host => "portal.example.com",
:protocol => 'https'
}
There is another alternative, as described in http://pivotallabs.com/how-i-leaned-to-stop-hating-and-love-action-mailer/
This solution has the advantage that it doesn't require any configuration (so less of a hassle), and works fine as long as you send emails from within controllers.
But if you plan on sending email without going through a controller (e.g. from command line or in response to another email), you need the static configuration.
Setting default_url_options directly is deprecated in Rails 3.1. Use url_for instead.
Add parameter :protocol to override default value (http), :protocol => 'https://'. This will create url starting with "https://..." instead of default "http://"
Interestingly, I had the same issue as you did, but in unit tests (while following Michael Hartl's railstutorial). I had this line in my test.rb file, but that didn't help:
config.action_mailer.default_url_options = { host: 'example.com', protocol: 'http' }
I've also added another line like this to test.rb, and surprisingly this solved the issue
default_url_options = { host: 'example.com', protocol: 'http' }
Setting default_url_options directly is deprecated in Rails 3.1
Use the url_for helper to create it:
<%= link_to "click here", url_for(:controller => foo, :action => 'bar', :token => token, :host => 'www.yourdomain.com') %>
Can you just do
<%="click here", :controller => foo, :action => 'bar', :token => token, :host=>request.host -%>

Resources