Search code examples
ruby-on-railsruby-on-rails-5.2

How to set scoped param value in form_with


I'm refactoring my app's routes to address the rule of thumb because my routes are very lengthy.

Imagine that I have the following models

class Company < ApplicationRecord
  has_many :projects
  has_many :users
  ...
end

class Project < ApplicationRecord
  belongs_to :company
  has_many :jobs
  ...
end
...

and my routes look like

Rails.application.routes.draw do
  ...
  resources :companies do
    resources :projects do
      resources :jobs
    end
  end
end

After the user's logged in, I set the @current_user and the companies that he has access to - @current_user.companies. With this approach, I avoid hitting the database when users tempered the URL to access unauthorized resources. For example, when a user edits a job:

/companies/1/projects/30/jobs/20/edit

class JobsController < ApplicationController 
  def edit
    @project = @company.projects.find(params[:project_id]) 
    @job = @project.jobs.find(params[:job_id])
    ...
  end
  ...
end

I followed the example from guides and added scope with a prefixed named parameter to shorten one level of nested resources.

scope ':company_id' do
  resources :projects do
    resources :jobs
  end
end

So far so good, I had successfully shortened my URLs from

/companies/1/projects/30/jobs/20/edit
edit_company_project_job_path(@company, @project, @job)

to

/1/projects/30/jobs/20/edit
edit_project_job_path(@company, @project, @job)

But then I broke my form_with model: [@company, @project, @job] because it fails to infer prefixed named parameter - :compay_id, even when I specify it @company.id.

The only solution I come with was setting the named route back again

scope ':company_id' as: 'company' do
  resources :projects do
    resources :jobs
  end
end

Isn't It the same as before? I just shorted the URL /1/projects/30/jobs/20/edit but the path is back to the original edit_company_project_job_path(@company, @project, @job). Am I missing something? I'd love to know how you do it


Solution

  • They key is to use the :as attribute when specifying the scope. This will make it work as expected with form_with and other helpers that rely on url_for.

    I just submitted a documentation update Pull Request to Rails for this.