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

时间:2017-04-03 13:28:13

标签: ruby-on-rails activerecord routes override

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

我在Rails 5.0.0.1项目上工作。出于实际原因,我们使用命名空间路由,如

namespace :survey do
  resources :questions
  resources :answers
end

我们相应地设置了QuestionAnswer个模型。

documentation告诉我,帮助器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 @questionself.controller_path,在控制器的情况下从另一个视图目录渲染动作,但我不能找到它。

任何帮助都将不胜感激。

修改

经过一些谷歌搜索后,我偶然发现了APIdock中的以下段落(依靠命名路线):

  

传递记录(如Active Record)而不是散列作为options参数将触发该记录的命名路由。查找将在类的名称上进行。因此,传递Workshop对象将尝试使用workshop_path路由。如果你有一个嵌套路线,例如admin_workshop_path,你必须明确地调用它( url_for无法猜出该路线)。

2 个答案:

答案 0 :(得分:2)

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]

答案 1 :(得分:1)

关于你的问题:

  
      
  • link_to的第二个参数使用辅助方法url_for,其中   然后选择适当的(或不是)命名的路线助手 - 但是如何?
  •   

如果你按照你在源代码中提到的#url_for方法,我们将被抛弃,从 ActionView :: RoutingUrlFor ActionDispatch :: Routing :: PolymorphicRoutes

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

ActionDispatch :: Routing :: PolymorphicRoutes#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等属性 - 我不确定这是否与我要查找的内容有关,或者即使它有如何覆盖它。< / LI>   

如上所述,这两个方法被赋予一个lambda,它是该内部类的初始值设定项的第一个参数,它将存储为 @key_strategy 。回到** ActionView#UrlFor“,在我给你看的第一个链接上,你看到了这个电话:

else
  builder.handle_model_call(self, options)

这将传递给Builder方面的一些方法:

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路径。