在我的 Rails-6.1.4 应用程序中,我在
routing-filterGem 中引入了
locale
过滤器,以允许 URL 路径中的语言区域设置,如 /en/articles
(对于区域设置“en”)。然后,我发现当给出 params
参数(或任何可选参数)时,路径助手会失败。
这是在模型
article_url()
的 Article
路径助手上测试(最小测试)失败的演示示例,模型实例为 @article
:
# test/controllers/articles_controller_test.rb
patch article_url(@article, params: { article: my_hash })
导致
# 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>'
基本上,路径助手
@article
的主要参数是一个模型实例,似乎被路径助手以某种方式解释为语言环境(!)。
我应该注意到,当未给出 param
(或其他)选项时,我发现路径助手工作正常。因此,只有在给出额外参数时才会出现问题。
对此合适的解决方案是什么?
我的设置如下。
# 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
这里我使用 master 分支的 head (8d1f1da) 作为路由过滤器,因为当前的 RubyGems.org 版本 (0.6.3) 已知不能与 Rails 6.1 一起使用(参见 Issue 75) ).
事实上,我已经设法找到了一个肮脏的解决方法:
article_url(@article, {params: { article: my_hash }}.merge(
ApplicationController.new.default_url_options))
但是,这意味着每次使用带有可选参数的助手时都必须编写这个
Hash#merge
......这在实践中会非常乏味,测试套件中可能有数百或数千个这样的句子,而且它是当然违反 DRY 原则。
有什么解决办法吗?
简而言之,除了在default_url_options
中设置
application_controller.rb
之外,在config/routes.rb
中为test环境设置为
# config/routes.rb
Rails.application.routes.draw do
filter :locale
default_url_options(locale: I18n.locale) if Rails.env.test?
似乎这是在 Rails 6.1 中设置
default_url_options
的方法([编辑] application_controller.rb
中的设置对于 development 环境仍然需要;test 环境需要此设置)。这在测试和开发环境中都可以正常工作除了当在上下文之外调用路径助手时,我的意思是不是从控制器或视图中调用,在这种情况下OP的肮脏的解决方法解决了问题:
Rails.application.routes.url_helpers.article_url(
article,
{only_path: true}.merge(ApplicationController.new.default_url_options)
)
Gem routing-filter 的工作原理是修改路径,就好像路径(头部)中包含的语言环境单词(如“en”)作为 URL 参数(如
?locale=en
)传递,同时删除语言环境部分从路径。因此,设置default_url_options
是正确的策略。
在撰写本文时(Rails-6.1.4),Rail 官方指南中的“从 URL 参数设置区域设置”确实表明了这一点
default_url_options
设置于 app/controllers/application_controller.rb
设置默认区域设置 URL 参数。
但也许描述已经过时了——我的意思是,在 Rails 6.1 中不再准确?
注意,似乎
default_url_options
在 Rails 6 中造成了麻烦;请参阅 通过 Rails 测试的 default_url_options 设置区域设置(Rails 6 及更高版本)。
我在 Rails 7 中找到了解决这个问题的方法,而无需废弃路由文件。通过在
config/environments/test.rb
中设置,可以使控制器和集成测试正常工作:
Rails.application.routes.default_url_options[:locale] = I18n.default_locale
或者将其设置为
test_helper.rb
:
class ActiveSupport::TestCase
setup do
Rails.application.routes.default_url_options[:locale] = I18n.default_locale
end
就我而言,在上下文之外(即不在控制器或视图中)调用路径助手的唯一问题是在邮件程序中。在这种情况下,我只需在
config/environments/test.rb
中的操作邮件程序 default_url_options 中设置区域设置:
config.action_mailer.default_url_options = { host: host, protocol: 'http', locale: :en }
最后,控制器/集成测试的解决方案都不适用于系统测试。为了解决这个问题,我将其添加到
application_system_test_case.rb
:
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
...
setup do
default_url_options[:locale] = I18n.default_locale
end