Rails 如何决定将哪个命名路由帮助程序用于 ActiveRecord 实例?



有谁知道它是如何工作的,最重要的是如何覆盖它?

我从事一个 Rails 5.0.0.1 项目。出于实际原因,我们使用命名空间路由,例如

namespace :survey do
resources :questions
resources :answers
end

我们相应地QuestionAnswer模型。

文档告诉我们,帮助程序link_to可以简单地将 ActiveRecord 实例作为第二个参数传递,这意味着在我的情况下只需在视图 (haml) 中编写以下内容:

= link_to 'Show question', @question

如果没有命名空间,这工作正常,但适用于命名的帮助程序方法question_path即使路由是命名空间的 - 而不是survey_question_path。关键是我想从 ActiveRecord 实例中决定使用哪个命名路由帮助程序

有趣的是,Rails 知道 Question 实例将有一个命名路由,类似question_path,我只是不知道怎么做。

到目前为止,我发现了什么:

  • link_to的第二个参数使用一个辅助方法url_for,然后选择适当的(或不)命名的路由帮助程序 - 但是如何呢?
  • ActiveRecord 实例具有方法model_name,其中包含route_key(复数)和singular_route_key等属性 - 我不确定这是否与我正在寻找的内容有关,或者即使它有如何覆盖它。

我认为有一个简单的方法可以在模型中覆盖,例如to_partial_path用于render @question,或者在控制器的情况下self.controller_path从另一个视图目录渲染操作,但我找不到它。

任何帮助将不胜感激。

编辑

经过更多的谷歌搜索,我偶然发现了APIdock中的以下段落(依赖于命名路由):

传递记录(如Active Record)而不是哈希作为选项参数将触发该记录的命名路由。查找将在类的名称上进行。因此,传递创意工坊对象将尝试使用workshop_path路由。如果你有一个嵌套的路由,比如admin_workshop_path你必须显式调用它(url_for不可能猜到那个路由)。

Rails 使用模型的model_name,就是这样,你不应该覆盖该方法。如果你想把一个Question变成对survey_question_path的调用,那么你需要定义你自己的question_path,它返回survey_question_path......

def question_path(question)
survey_question_path(question)
end

。或者只是自己使用完整的方法名称。

您还可以使用符号/模型数组,这些数组直接传递给url_for

= link_to 'Show question', [:survey, @question]

至于你的问题:

  • link_to 的第二个参数使用帮助程序方法url_for,它 然后选择适当(或不)命名的路由助手 - 但是如何?

如果你遵循你在源代码周围提到的#url_for方法,我们就会被扔掉,从ActionView::RoutingUrlForActionDispatch::Routing::P olymorphicRoutes

(资料来源:https://github.com/rails/rails/blob/870dde4710f1492c83233620f343ec414a07a950/actionview/lib/action_view/routing_url_for.rb#L115)。

ActionDispatch::Routing::P olymorphicRoutes#polymorphic_url方法中,它使用内部类并调用HelperMethodBuilder.polymorphic_method,将所有给定的选项(包括您的模型类作为"record_or_hash_or_array"传递(是的,我知道...))。

下面几行,在这个方法的声明中,你会更多地使用小方法,最终得到这样的方法:

def self.build(action, type)
prefix = action ? "#{action}_" : ""
suffix = type
if action.to_s == "new"
HelperMethodBuilder.singular prefix, suffix
else
HelperMethodBuilder.plural prefix, suffix
end
end
def self.singular(prefix, suffix)
new(->(name) { name.singular_route_key }, prefix, suffix)
end
def self.plural(prefix, suffix)
new(->(name) { name.route_key }, prefix, suffix)
end

(来源:https://github.com/rails/rails/blob/3f2b7d60a52ffb2ad2d4fcf889c06b631db1946b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb)

如您所见,它将决定是否是#new操作,并调用#singular#plural,这将反过来回答您的第二个问题:

  • ActiveRecord 实例具有方法 model_name,其中包含route_key(复数)和 singular_route_key 等属性 - 我不确定这是否与我正在寻找的内容有关,或者即使它有如何覆盖它。

正如我们在上面看到的,这两个方法被分配给一个 lambda,该 lambda 是这个内部类的初始值设定项的第一个参数,它将存储为@key_strategy。回到**ActionView#UrlFor"中,在我向您展示的第一个链接上,您会看到以下调用:

else
builder.handle_model_call(self, options)

这将传递给构建器端的几个方法:

def handle_model(record)
args  = []
model = record.to_model
named_route = if model.persisted?
args << model
get_method_for_string model.model_name.singular_route_key
else
get_method_for_class model
end
[named_route, args]
end
def handle_model_call(target, model)
method, args = handle_model model
target.send(method, *args)
end
private
def get_method_for_class(klass)
name = @key_strategy.call klass.model_name
get_method_for_string name
end
def get_method_for_string(str)
"#{prefix}#{str}_#{suffix}"
end

而且,如果我没有错过任何内容,最后一种方法将返回您的 URL 路径。

最新更新