是否可以动态删除一定级别的rails资源嵌套?

时间:2011-12-08 00:33:43

标签: ruby-on-rails routing

我需要路由才能使用rails app的托管和whitelabel版本。这两个版本都在相同的代码库和相同的服务器上运行,所以路由需要解决问题,但我不清楚如何实现这一点。

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

在主网站上需要查看哪些路线

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

在whitelabel网站上需要查看哪些路线

该公司还可以对工作板进行白色标记,使其看起来像这样:

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这样做会很好。这支持了吗?

1 个答案:

答案 0 :(得分: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

...然后您应该可以使用Proc运算符将此&转换回块:

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

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