ruby-on-railsinternationalizationlocaleruby-on-rails-6rails-i18n

Rails default_url_options conflicts with params in path/url-helpers


In my Rails-6.1.4 application, I have introduced the locale filter in the routing-filter Gem to allow a language-locale in URL-paths like /en/articles (for locale "en"). Then, I find path helpers fail when params parameter (or any optional parameters) is given.

Here is a demonstrative example of failure in testing (minitest) at article_url() path helper for the model Article with a model instance of @article:

 # test/controllers/articles_controller_test.rb
patch article_url(@article, params: { article: my_hash })

resulting in

# Error output
ArticlesControllerTest#test_should_update:
DRb::DRbRemoteError: No route matches {:action=>"show", :controller=>"articles",
 :locale=>#<Article id: 805413029, ...>}, missing required keys: [:id]
 (ActionController::UrlGenerationError)
    test/controllers/articles_controller_test.rb:58:in `block in <class:ArticlesControllerTest>'

Basically, the main argument for the path helper @article, which is a model instance, seems to be somehow interpreted as a locale(!) by the path helper. I should note I find path helpers work fine when the param (or whatever) option is not given. So, it causes a problem only when an extra parameter is given.

What is the appropriate solution for this?


My setting is as follows.

 # test_helper.rb  (as recommended in the reference of routing-filter)
RoutingFilter.active = false

 # application_controller.rb
def default_url_options
  Rails.application.default_url_options = Rails.application.routes.default_url_options =
   { locale: I18n.locale }
end

 # config/routes.rb
Rails.application.routes.draw do
  filter :locale
  
  scope "(:locale)", locale: /en|ja|fr/ do # [EDIT] This scope should be removed;
                                           # It is unnecessary and is sometimes harmful 
                                           # as the locale in the path may be doubled. 
    resources :articles
  end
end

Here I am using the head (8d1f1da) of the master branch for routing-filter because the current RubyGems.org version (0.6.3) for it is known not to work with Rails 6.1 (see Issue 75).


In fact, I have managed to find a dirty workaround:

article_url(@article, {params: { article: my_hash }}.merge(
  ApplicationController.new.default_url_options))

However, this means this Hash#merge has to be written every time a helper is used with an optional parameter… That would be extremely tedious in practice, where there can be hundreds or thousands of such sentences in the test suite, and it is of course against the DRY principle.

Any solution?


Solution

  • I found a way to solve this in Rails 7 without junking up the routes file. Controller and Integration tests can be made to work correctly by setting it in config/environments/test.rb:

    Rails.application.routes.default_url_options[:locale] = I18n.default_locale
    

    Or by setting it in test_helper.rb:

    class ActiveSupport::TestCase
      setup do
        Rails.application.routes.default_url_options[:locale] = I18n.default_locale
      end
    

    In my case, the only issue with calling path helpers out of context (i.e. not in a Controller or View) was in mailers. In this case I simply set the locale in the action mailer default_url_options in config/environments/test.rb:

    config.action_mailer.default_url_options = { host: host, protocol: 'http', locale: :en }
    

    Finally, neither solution for controller/integration tests worked for system tests. To address that, I added this into application_system_test_case.rb:

    class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
      ...
    
      setup do
        default_url_options[:locale] = I18n.default_locale
      end