【问题标题】:Is it possible to dynamically remove a level of rails resource nesting?是否可以动态删除一定级别的 Rails 资源嵌套?
【发布时间】:2011-12-08 00:33:43
【问题描述】:

我需要路由以适用于托管和白标版本的 rails 应用程序。两个版本都在相同的代码库和相同的服务器上运行,因此路由需要弄清楚,但我不清楚如何实现这一点。

我正在建立一个工作委员会。每个注册的公司都可以在网站上创建自己的公司资料。如果他们获得付费的高级版本,他们可以使用他们自己的 CNAME 的 URL 并从他们的子域之一提供工作板。都是很标准的东西。

哪些路线需要在主站点上显示

http://jobsrus.com/companies/company-name
# e.g.
http://jobsrus.com/companies/microsoft

这会导致诸如

之类的路线
http://jobsrus.com/companies/microsoft/jobs/
http://jobsrus.com/companies/microsoft/newest

什么路线需要在白标网站上显示

公司还可以为工作板贴上白标签,使其看起来像这样:

http://jobs.microsoft.com/jobs
http://jobs.microsoft.com/newest

澄清差异

澄清一下,相同的controller#action 将由双方提供:

http://company-domain/jobs
# and
http://jobsrus.com/companies/company-name/jobs

丑陋的路由:

最简单的路由是:

routes.rb

resources :companies do
  ...
  resources :jobs do
    ...
  end
end

给出:

http://jobsrus.com/companies/microsoft/jobs

# but also

http://jobs.microsoft.com/companies/microsoft/jobs

而我们希望后者是:

http://jobs.microsoft.com/jobs

如何从路由中删除第一层嵌套?

我的问题很简单。如何从路由中删除 companies/company-name 嵌套级别?白标站点唯一需要的路由是:

routes.rb

resources :jobs do
  ...
end

如何在路由中动态包含或排除嵌套级别?我可以使用request.host 变量来触发开关,但我不知道如何最好地激活或取消激活该嵌套层。

----- 编辑(和部分解决方案)-----------

使用@m_x 的答案,我使用约束来创建路由。为了更好地说明问题,我还使用了一些额外的路线:

(简化为仅显示 :show 和 :index 方法)

def company_resources    
  resources :jobs, only: [:index, :show] do
    resource :applicants, only: [:index, :show] do
      resource :messages, only: [:index, :show]
    end
  end
end

 constraints host: /^(?!jobsrus\.com)/ do
   company_resources
 end

 resources :companies, only: [:index, :show] do
   company_resources
 end

这在匹配传入请求方面效果很好,我们可以看到rake routes 产生了我们正在寻找的匹配项:

        job_applicants_messages GET /jobs/:job_id/applicants/messages(.:format)                       {:host=>/^(?!jobsrus\.com)/, :action=>"show", :controller=>"messages"}
                 job_applicants GET /jobs/:job_id/applicants(.:format)                                {:host=>/^(?!jobsrus\.com)/, :action=>"show", :controller=>"applicants"}
                           jobs GET /jobs(.:format)                                                   {:host=>/^(?!jobsrus\.com)/, :action=>"index", :controller=>"jobs"}
                            job GET /jobs/:id(.:format)                                               {:host=>/^(?!jobsrus\.com)/, :action=>"show", :controller=>"jobs"}
company_job_applicants_messages GET /companies/:company_id/jobs/:job_id/applicants/messages(.:format) {:action=>"show", :controller=>"messages"}
         company_job_applicants GET /companies/:company_id/jobs/:job_id/applicants(.:format)          {:action=>"show", :controller=>"applicants"}
                   company_jobs GET /companies/:company_id/jobs(.:format)                             {:action=>"index", :controller=>"jobs"}
                    company_job GET /companies/:company_id/jobs/:id(.:format)                         {:action=>"show", :controller=>"jobs"}
                      companies GET /companies(.:format)                                              {:action=>"index", :controller=>"companies"}
                        company GET /companies/:id(.:format)                                          {:action=>"show", :controller=>"companies"}

但是,现在不再有任何规范的方法来生成路由。如果我们想为特定公司的工作索引创建路线,我们必须使用不同的方法,具体取决于我们是在白标公司还是jobsrus.com 公司:

# path generator for jobs page on a whitelabel company
jobs_path
# => 'microsoft.com/jobs'

# path generator for jobs page on a company on the main site
company_jobs_path @company
# => 'jobsrus.com/companies/microsoft/jobs'

# what is actually required
company_jobs_path @company
# => 'jobsrus.com/companies/microsoft/jobs' (when on main site)
# => 'microsoft.com/jobs' (when on whitelabel)

我可以覆盖路径方法并定义一些根据host 变量切换的方法。不过,如果rails way 这样做会很好。支持吗?

【问题讨论】:

    标签: ruby-on-rails routing


    【解决方案1】:

    有趣的问题。我认为可以使用request-based constraints 来做到这一点。

    在初始化器中,定义一个常量:

     YOUR_HOST = 'jobsrus.com'.freeze
    

    然后在 routes.rb 中:

     constraints :host => /!#{YOUR_HOST}/ do
       resources :jobs
     end
    
     resources :companies do
       resources :jobs
     end
    

    这里的顺序很重要:如果request.host 与您的主机名不匹配,则第一组路由可用并在请求到达第二组之前捕获请求。

    但是现在,您需要对控制器进行更改,以便它可以检索公司并相应地确定作业资源的范围(没有尝试过,谨慎使用):

    class JobsController < ApplicationController
      before_filter :resolve_whitelabel
    
      def resolve_whitelabel
        if request.host != YOUR_HOST
          # not safe as is, just demonstrates the idea
          @client      = Company.find_by_host( request.host ) 
          @scoped_jobs = Job.where( company_id: @client.id ) 
        else
          @scoped_jobs = Job
        end    
      end
    
    
      def scoped_jobs
        @scoped_jobs
      end
    
      def index
        # just an example
        @jobs = scoped_jobs.recent 
      end
    end 
    

    您只需要记住始终使用scoped_jobs

    编辑

    您可以在Proc 中“存储”一个块:

    routes = Proc.new do
               resources :jobs
             end
    

    ...然后您应该能够使用 &amp; 运算符将此 Proc 转换回一个块:

    constraints( :host => /!#{YOUR_HOST}/, &routes )
    resources( :companies, &routes )
    

    这需要测试,我从未在这种情况下使用它。请特别注意,Proc 充当闭包:它在创建时捕获其上下文(此范围内可用的变量等。这称为它的“绑定”)(就像块一样)。这可能会导致意外行为(尽管我认为在这种情况下这并不重要,因为您的 Proc 的范围与原始块的范围相同)。

    【讨论】:

    • 嗨,m_x。这是一个非常好的答案,是我正在考虑使用的一种变体,它只是一个基于host 的简单“if”语句。因为实际上我在公司jobs &gt; applicants &gt; messages 等下实际上有一些深层嵌套。我想避免重复使用嵌套块。我想这更像是一个 Ruby 问题,但是可以存储块然后将其反刍到正确的巢中吗?
    • 我实际上编辑了我的问题以包含您的答案,并且我使用 Proc 进行了测试(实际上我只是使用一种方法进行了测试)。我将您的问题标记为正确,因为它非常有帮助,但是该方法确实引发了后续路径帮助程序模棱两可的问题(请参阅我的编辑)。您对如何处理这些问题有什么想法吗?
    • 不知道这对你有没有用,但我发现this doc关于浅层路线
    • 感谢@m_x - 非常感谢您的意见。不幸的是,浅层路线并没有给我想要的东西。
    • 为什么不将company_jobs_path 覆盖为application helper
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-01
    • 1970-01-01
    • 2011-08-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多