3

Given Rails 4.1, two controllers CRM::CompaniesController and CRM::ContactPeople, two models CRM::Company and CRM::ContactPerson and the following routes:

namespace :crm do
  resources :companies do
    resources :contact_people
  end
end

The generated URL helpers contain the CRM namespace only once, which is exactly what I want:

crm_company_contact_people      GET  /crm/companies/:company_id/contact_people(.:format)
new_crm_company_contact_person  GET  /crm/companies/:company_id/contact_people/new(.:format) 
# ...

However, using the Array approach for URL helpers

= form_for([@crm_company, @crm_contact_person]) do |f|

tries to generate a URL with each resource namespaced:

undefined method `crm_company_crm_contact_people_path' for #<#<Class...

I would like to have "crm" in my paths only once at the beginning (unless it breaks a common Rails approach) and it would be ugly to add the URL to each form explicitly. Is there something I can do (perhaps in the routes, the model or the first form_for argument) so that Rails knows how to build the correct path? Or is there a more Rails-like way to generate this kind of structure so that Rails knows automatically how to build the paths?

Sebastian vom Meer
  • 5,005
  • 2
  • 28
  • 36
  • refer this http://stackoverflow.com/questions/2718059/nested-resources-in-namespace-form-for – amtest Apr 29 '15 at 08:42
  • Thanks, the problem is the same. But it seems the OP was looking for specific solution for form_for (in this case `form_for(@foo, url: ...`). I try to find a Rails-Way to avoid this workaround, so more general changes route structure or other elements are welcome. – Sebastian vom Meer Apr 29 '15 at 08:56

2 Answers2

1

You need to include the namespace in your call to form_for:

= form_for([:crm, @crm_company, @crm_contact_person]) do |f|

Also, since you're using namespaced models, you need to define the following class method on them (from rails/rails issue #10705) to ignore the namespace:

def self.use_relative_model_naming?
  true
end
fylooi
  • 3,840
  • 14
  • 24
  • Unfortunately this adds the namespace at the beginning and keeps the namespaces of the models: `crm_crm_company_crm_contact_person_path` – Sebastian vom Meer Apr 30 '15 at 11:11
  • Ah, forgot about the namespaced models. Edited to take care of that. – fylooi Apr 30 '15 at 18:25
  • Thanks. After debugging `url_for` I came across ActiveModel::Naming and `.use_relative_model_naming?`, too. I still think about ways to it without adding something to `form_for`. However, this seems to be the right approach. – Sebastian vom Meer May 02 '15 at 17:40
0

I tried several approaches to find the least annoying approach for me. I startet with changing the CRM namespace to use relative model naming:

module CRM
  def self.use_relative_model_naming?
    true
  end
end

However, this removes the namespace from each object, including company. So the following doesn't work anymore:

= url_for @crm_company
# undefined method `company_path'

To avoid this I tried to unnamespace only specific models by modifying the model name (please note that this has more side effects than URL generation):

class CRM::ContactPerson < ActiveRecord::Base
  def self.model_name
    ActiveModel::Name.new(self, CRM)
  end
end

Now the following URLs work fine:

= url_for @crm_company
= url_for [@crm_company, @crm_contact_person]

But if CRM::ContactPerson is shallow nested and appears as child of CRM::Company AND as child of CRM directly the problem from above reappears:

= url_for @crm_contact_person
# undefined method `contact_person_path'

In this case we could split up the nested and unnested part into CRM::Companies::ContactPerson and CRM::ContactPerson and use relative model naming for the CRM::Companies module. However, this seems to be a lot of overhead and could be even worse than using explicit URL helpers.

So for now I cannot find an all-embracing solution, but at least the one for nested-only resources works fine.

Sebastian vom Meer
  • 5,005
  • 2
  • 28
  • 36