Demistifying Rails default_url_options
The way default_url_options work is not very intuitive, here are my findings for verions 7.0 and above.
url_options vs default_url_options
You should only deal with default_url_options. url_options is used internally and you should not touch it.
A URL is essentialy built by merging
explicit args > default_url_options > request-derived values
What can you put in there?
┌─────────────────┬───────────────────────────────────────────────────────────────────────┐
│ :host │ The hostname (required for _url helpers unless derived from request) │
│ :port │ Port number, e.g. 3000 │
│ :protocol │ e.g. "http" or "https" │
│ :subdomain │ Subdomain portion; false strips all subdomains │
│ :domain │ Domain portion, used with :tld_length │
│ :tld_length │ Number of TLD labels, defaults to 1 (used with :subdomain/:domain) │
│ :anchor │ Fragment appended to URL, e.g. "section-1" │
│ :params │ Query string parameters hash │
│ :path_params │ Parameters only used for named dynamic segments; extras are discarded │
│ :trailing_slash │ If true, adds a trailing slash │
│ :script_name │ Path prefix relative to domain root (for mounted engines) │
│ :only_path │ If true, returns relative URL (no host/protocol) │
└─────────────────┴───────────────────────────────────────────────────────────────────────┘
Plus any route segment parameters (like :locale) which are merged into the path if the route defines them, or appended as query params if it doesn’t.
Relationships between the various items
Rails.application.default_url_options is an alias for Rails.application.routes.default_url_options, both reading and writing.
ActionMailer::Base.default_url_options is a separate object. It is populated dynamically from config.action_mailer.default_url_options when the railtie is initialized.
The various contexts
Keeping present that explicit args always have the priority, this is where default_url_options come from
Controllers
By default the controller will add to the url_options (not default!) host, port and proto.
You can define adefault_url_options controller instance method, and this becomes the priority chain:
explicit args
> controller's default_url_options
> request-derived values (host, port, protocol)
> Rails.application.routes.default_url_options
The options are merged, not replaced.
Mailers
Mailers can have their own separate default_url_options. You define them in the environment file as config.action_mailer.default_url_options and they are copied by the railtie to aActionMailer::Base.default_url_options class attribute. If you try to set a value after the initialization is complete you must act directly on ActionMailer::Base.default_url_options.
explicit args
> specific mailer's default_url_options (instance method, if defined)
> ActionMailer::Base.default_url_options (class attribute, via config.action_mailer.default_url_options)
> Rails.application.routes.default_url_options
The instance method will override the class attribute (if defined) unless it calls super.
Direct access
When you do something like Rails.application.routes.url_helpers.posts_path then the dynamicdefault_url_options will be empty, but it still picks up Rails.application.routes.default_url_options.
If you have dynamic bits you will need to pass them explicitly, or include the method and define your default_url_options (see below).
Including the url helpers
When in your class you include Rails.application.routes.url_helpers then you can define a default_url_options method in your class