Konnor's Blog

How do default_url_options work in Rails?

April 03, 2021

`default_url_options`

I’m sure you’ve all been bitten by the default_url_options API in Rails. You either couldnt get it to work, or just moved on. Or, you just threw everything and the wall and saw what stuck.

How it works

Currently, there are 4 different config options for default_url_options as far as I can tell. This doesnt include defining default_url_options as a method inside of your routes file, controller, or mailer.

Rails.application.default_url_options
Rails.application.routes.default_url_options
Rails.application.config.action_controller.default_url_options
Rails.application.config.action_mailer.default_url_options

So? Whats the difference?

Well the first two options are the same thing. They reference the same variable.

Example:

bundle exec rails console
Rails.application.default_url_options
# => {}
Rails.application.routes.default_url_options
# => {}

When one is updated, the other is updated.

bundle exec rails console
Rails.application.default_url_options[:host] = "0.0.0.0"
# => {host: "0.0.0.0"}
Rails.application.routes.default_url_options
# => {:host => "0.0.0.0"}
Rails.application.routes.default_url_options[:host] = "1.2.3.4"
# => {:host => "1.2.3.4"}
Rails.application.default_url_options
# => {:host => "1.2.3.4"}

So, to recap, Rails.application.default_url_options is the same as Rails.application.routes.default_url_options

By default, they are both hashes that can be modified in place. Easy.

Action Controller / Action Mailbox

Alright heres where things get weird.

Lets just open up a console and see what happens.

bundle exec rails console
Rails.application.config.action_mailer
# => ActiveSupport::OrderedOptions
Rails.application.config.action_controller
# => ActiveSupport::OrderedOptions
Rails.application.config.action_mailer.default_url_options
# => nil
Rails.application.config.action_controller.default_url_options
# => nil

So by default Rails.application.config.action_mailer and Rails.application.config.action_controller return nil if the environments havent been set for them. Okay…cool.

The reason is that ActiveSupport::OrderedOptions is essentially a mixed hash / instance variable configuration object. This means if the value isnt set, itll return nil.

When setting your config, you want to give action_(mailer|controller).default_url_options a full hash if it hasnt been set. If it has been set already, you can modify it in place like a normal Hash.

Example:

bundle exec rails console
Rails.application.config.action_controller.default_url_options = {host:
"0.0.0.0", port: 4569}
# => {:host => "0.0.0.0", :port => 4569}
Rails.application.config.action_mailer.default_url_options = {}
Rails.application.config.action_mailer.default_url_options[:host] =
"0.0.0.0"
# => {:host => "0.0.0.0"}

Bringing it all together

Alright, now for the meat and potatoes now that we’ve gotten definitions out of the way.

Setting Rails.application.default_url_options will only affect calls made from the Rails router.

Example:

bundle exec rails console
Rails.application.default_url_options = {host: "0.0.0.0"}
Rails.application.routes.url_helpers.url_for(User.first)
# => http://0.0.0.0/users/1

However, if we were to go into our actual running app and click on a button that uses: users_url(User.first) the hostname will not be what we set. The same goes for out ActionMailer, the hostname wont be set properly. To remedy this, we have to set those as well.

Rails.application.config.action_controller = {host: "0.0.0.0"}
Rails.application.config.action_mailer = {host: "0.0.0.0"}

Now, your action_controller and action_mailer will both have the new hostname.

To put it in simpler terms, heres roughly what were doing:

Rails.application.default_url_options affects the Rails router. You will use this when interacting directly with the router. IE: url_for()

Rails.application.config.action_(mailer|controller) affects the actual browser route generations when you use either a Controller or Mailer. IE: users_url(Path.first)

Honestly, I couldnt find anything about this in the Rails docs. I found a few open / closed issues like this one:

https://github.com/rspec/rspec-rails/issues/1275

But for the most part this was all done in the Rails console.

I’m sure I may have gotten something wrong or missed something. But, this is my understand of the default_url_options configuration. Good luck, and hopefully you dont have to waste as much time as I did hunting down bugs with this.


Written by Konnor Rogers who currently works at VeueLive as a full time developer. You should follow him on Twitter