【问题标题】:How Rails decides which named route helper to use for ActiveRecord instance?Rails 如何决定 ActiveRecord 实例使用哪个命名路由助手?
【发布时间】:2017-04-03 13:28:12
【问题描述】:

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

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

namespace :survey do
  resources :questions
  resources :answers
end

我们有相应的Question 和Answer 模型。

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

= link_to 'Show question', @question

如果没有命名空间,这可以正常工作,但适用于命名辅助方法 question_path,即使路由是命名空间的 - 而不是 survey_question_path。关键是我想从一个名为 route helper 的 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 对象将尝试使用 workshop_path 路由。如果您有嵌套路由,例如 admin_workshop_path,则必须显式调用它(url_for 无法猜测该路由)。

【问题讨论】:

    标签: ruby-on-rails activerecord routes overriding


    【解决方案1】:

    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]
    

    【讨论】:

    • 是的,至于帮手,我想到了,只是不喜欢这个主意……(我的坏:-))。另一方面,您的第二个建议,即数组,似乎解决了我的问题,THX。 (至于model_name,我并没有打算覆盖它,只有route_key属性,但我认为你是对的......)
    【解决方案2】:

    至于你的问题:

    • 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(复数)和 single_route_key 之类的属性 - 我不确定这是否与我正在寻找的内容有关,或者即使它与如何覆盖它有关。

    正如我们在上面看到的,这两个方法被分配给一个 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 路径。

    【讨论】:

    • 哇!壮观...我尝试了同样的方法-这就是我到达@question.model_name.singular_route_key的方式,我猜-只是迷失在了方法的丛林中(例如,至少有5种url_for方法,我不知道哪个一个以进一步调查...)。我只是想知道是否有办法覆盖singular_route_key,但我想没有它我可以应付。无论如何,谢谢:-)
    猜你喜欢
    • 1970-01-01
    • 2015-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-24
    相关资源
    最近更新 更多