【问题标题】:What's a DRY solution for routes and controller with a polymorphic resource attached to both of a pair of nested resources?多态资源连接到一对嵌套资源的路由和控制器的 DRY 解决方案是什么?
【发布时间】:2012-01-31 18:44:03
【问题描述】:

免责声明:使用 Rails 开发的第一个月,但我已经阅读了我能找到的所有内容。

编辑:不知何故,我错过了 this very similar question 并给出了类似的最终答案。

我有多态标志:

Class Flag...
  belongs_to :flaggable, :polymorphic => true
  ...
end

我有嵌套资源,这些资源具有适当的 has_many :flags, :as => :flaggable 语句。

resources :posts do
  resources :comments
end

我希望帖子和 cmets 以及将来网站上的其他内容都可以标记。 关于路由和控制器的 DRY/标准方式(我使用的是 Rails 3.1)是什么?


我为路线做了什么:

主要基于 this rails cast,我将标志作为帖子和 cmets 的嵌套资源。已经,我认为我走错了路,因为它似乎是在重新陈述模型中的多态关系以及违反"Resources should never be nested more than 1 level deep." 的指导方针

resources :posts do
  resources :flags
  resources :comments do
    resources :flags
  end
end

另外,我想单独实现可标记的路由,如下所示。但同样,这似乎并不 DRY,并且还为 cmets 生成了不想要的独立路由。

resources :posts do
  resources :flags
end
resources :comments do
  resources :flags
end

最后,我想知道是否可以为可标记物制作通用资源。我找不到任何方法来实现这一点,并且它与之前为通用可标记类型提供通用路由的方法存在相同的问题。

resources :flaggable do
  resources :flags
end

我为上面嵌套资源的控制器做了什么:

我实现了 find_flaggable,但意识到对于嵌套资源,转换为可标记类的参数可能是 Post 或 Comment,因为两者都以参数(post_id 和 comment_id)结尾。我可以使用当前设置的 id 优先级列表来解决以下问题,但这不是一个通用的解决方案,它甚至比现在的 DRY 还要少。

def find_flaggable
  params.each do |name, value|
    if name =~ /(.+)_id$/
      return $1.classify.constantize.find(value)
    end
  end
  nil
end

所以这就是我停下来的地方(实际上只为帖子和评论实施了一个有限的解决方案)并意识到我不知道一个令人满意的方法来实现这一点。有人可以帮忙吗?

【问题讨论】:

    标签: resources ruby-on-rails-3.1 routes polymorphism


    【解决方案1】:

    感谢您的回答。这是我最终使用的,尽管我对它并不完全满意。这似乎是半成品多态性,因为多态资源的父级实际上需要直接“键入”沿线的某处。在控制器内的杰克解决方案中。在 Azolo 中,通过将标志路由到各种控制器中。我曾希望找到一种方法让 Rails 进行打字并简化应用程序代码。

    要回答我关于路由的第一个问题,似乎没有人会担心由于将例如 cmets 放在带有标志作为子资源的基本级别而导致未使用的路由(如 Jake 的回答中所示)。所以我把杰克的回答作为设置路线的方法。多态路径助手也不错。

    [:posts, :comments, :yet_another_resource].each do |resource_type|
      resources resource_type do
        resources :flags
      end
    end
    

    这将导致使用一个标志控制器,该控制器接受各种多态路径助手,例如我的问题中的 railscast 中的 find_flaggable。

    def find_flaggable
      params.each do |name, value|
        if name =~ /(.+)_id$/
          return $1.classify.constantize.find(value)
        end
      end
      nil
    end
    

    再次感谢您的帮助。

    【讨论】:

      【解决方案2】:

      只是为了解决在你的路由中干掉代码的问题,一个简单的方法是使用

      [:posts, :comments, :yet_another_resource].each do |resource_type|
        resources resource_type do
          resources :flags
        end
      end
      

      而不是

      resources :posts do
        resources :flags
      end
      resources :comments do
        resources :flags
      end
      

      当您在嵌套资源下有很多操作时,这开始变得非常干净和有用。有点像为您的路线重构的“提取方法”的简单版本。

      编辑

      如果你错过了我的评论,我认为polymorphic_url 是你正在寻找的正确的东西!!

      【讨论】:

      • 这是一个不错的技术。谢谢你。我有两个问题要问你。 1)这样做我认为与我的第二种路由方法有相同的问题:它从一些我不想成为的资源中制作顶级资源/路由。是否有一些选项可以禁用这些路由并仅启用子路由? 2)您对控制器有什么建议以及处理可能嵌套或不嵌套的标记资源的一般方法吗?还是其他方法?
      • 嗯,说实话,我觉得理解你的完整问题有点困难。但我认为您可能正在寻找的是 polymorphic_url 尝试类似 described here 的东西,因为您使用的是多态关系。
      • 抱歉这个问题可能不是很好,因为我是 Rails 新手,但我想知道哪一部分不清楚。多态路径很好。它到达控制器。在标志控制器中,我认为仍然存在在没有先验基础资源知识的情况下创建标志的问题。
      【解决方案3】:

      由于您只是尝试标记文章而不是操作标记资源集合,因此我只需在控制器中针对您可能要标记的资源创建一个 flag 方法。

      从那里您可以从资源本身构建标志。

      class PostsController < ApllicationController
        ...
      
        def flag
          @post = Post.find(params[:id])
          flag = @post.flags.build(params[:flag])
      
          if flag.save
            flash[:notice] = "Post flagged"
          else
            flash[:notice] = "Unable to flag post"
          end
          render @post
        end
      end
      

      routes.rb:

       resources :posts do
         post 'flag', :on => member
         resources :comments do
           post 'flag', :on => member
         end
       end
      

      这样的事情应该可以工作,这不是最干燥的方法。但这可能是我将如何实现它的方式。

      【讨论】:

      • 感谢详细的解决方案。我相信它类似于我提出的第一个路由解决方案,但将显式(非多态)决策从标志控制器移到每个可标记控制器中,其中 id 的优先级本质上是显而易见的。好主意。
      • 我要再等一会儿,看看是否有人有更通用的解决方案,如果没有,则将您的标记为解决方案。再次感谢。
      • 没问题,但我会有点厌倦将标记动作移动到另一个控制器,如果你这样做,你会将参数传递给另一个控制器。看起来标记应该由您要标记的对象处理。只是我的 2 美分。
      • 为了将 Azolo 的 cmets 放在上下文中,我删除了 :only => :create 选项以使标志成为通用资源,因此希望答案可以更通用。
      猜你喜欢
      • 1970-01-01
      • 2013-01-23
      • 2014-08-15
      • 2013-02-03
      • 2021-10-19
      • 1970-01-01
      • 2013-01-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多