Rails Routes/Controller/Directory Structure design question

I have a design question where I would appreciate a thoughtful response.

Let's say you have a simple application (for example's sake) that has a User, Company and Theme model. A Company `has_one` Theme and `has_many` Users.

Administrators (a User) can fully manage Companies and Themes - the whole REST stack, in addition to a few other actions too. Administrators are expected to do things to Companies and themes that other user roles cannot do.

We also have a Company role. This role can edit their own Company, as well as select a Theme from the ones the admin-user added as nice defaults, or they can just make their own theme.

Now, here we have some non-trivial design choices, and I would like to know what the best-practice is.

**PART 1**

In Rails, it makes sense to have `resources :users, :companies, :themes` for the administrators and probably `resource :company, :theme, :users` for the Company users.

But of course, we run into some naming conflicts here - both singular and plural - so we might want to try something like `resource :my_company, :my_theme, :my_users` to separate them? Or is there a better solution?

Furthermore, a theme is just a component of a company, so maybe we want to nest them?

    :resource :my_company do       :resource :theme       :resources :users     end

This works okay, but it could be confusing as to which UsersController we are referring to... no? This is really sticky and I would love to know how to deal with this. Do you have 1 controller, or 2? What do you name them?

But then I look at the url, and it's kind of silly:

    应用宝官网-全网最新最热手机应用游戏下载

I guess it could be worse.

Company users also might want the list of themes via ajax, so is it correct for them to call:

    应用宝官网-全网最新最热手机应用游戏下载

?

Is this how to approach this situation, or is there a better way?

**PART 2**

Also, what should your directory structure look? Should you have controllers separated by user role?

    /app/controllers/admin/companies_controller.rb     /app/controllers/admin/themes_controller.rb     /app/controllers/admin/users_controller.rb     /app/controllers/company/my_company_controller.rb     /app/controllers/company/theme_controller.rb     /app/controllers/company/users_controller.rb

Or is there better ways to handle this?

I would really appreciate a thoughtful response on this. Thanks!

Oh, I also want to add that Admins will manage Users too, not just Companies and Themes. So there is a conflict where Companies can create/edit/delete Users and use/not use the same controller. There is also potential route conflicts unless you use nested resources.

I also want to add that Admins can do some things to users, but Company users can do other things. It's not just a subset - but the views and actions and forms will be somewhat different.

That is why I am having difficulty figuring out the routes, what the controllers should be, and what to generalize and what to keep separate.

I have a design question where I would appreciate a thoughtful

response.

Let’s say you have a simple application (for example’s sake) that has

a User, Company and Theme model. A Company has_one Theme and

has_many Users.

Administrators (a User) can fully manage Companies and Themes - the

whole REST stack, in addition to a few other actions too.

Administrators are expected to do things to Companies and themes that

other user roles cannot do.

We also have a Company role. This role can edit their own Company, as

well as select a Theme from the ones the admin-user added as nice

defaults, or they can just make their own theme.

Now, here we have some non-trivial design choices, and I would like to

know what the best-practice is.

PART 1

In Rails, it makes sense to have

resources :users, :companies, :themes for the administrators and

probably resource :company, :theme, :users for the Company users.

But of course, we run into some naming conflicts here - both singular

and plural - so we might want to try something like

resource :my_company, :my_theme, :my_users to separate them? Or is

there a better solution?

Furthermore, a theme is just a component of a company, so maybe we

want to nest them?

:resource :my_company do

  :resource :theme

  :resources :users

end

This works okay, but it could be confusing as to which UsersController

we are referring to… no? This is really sticky and I would love to

know how to deal with this. Do you have 1 controller, or 2? What do

you name them?

But then I look at the url, and it’s kind of silly:

[http://myapp.com/my_company/my_theme/edit](http://myapp.com/my_company/my_theme/edit)

I guess it could be worse.

Company users also might want the list of themes via ajax, so is it

correct for them to call:

[http://myapp.com/themes.json](http://myapp.com/themes.json)

?

Is this how to approach this situation, or is there a better way?

PART 2

Also, what should your directory structure look? Should you have

controllers separated by user role?

/app/controllers/admin/companies_controller.rb

/app/controllers/admin/themes_controller.rb

/app/controllers/admin/users_controller.rb

/app/controllers/company/my_company_controller.rb

/app/controllers/company/theme_controller.rb

/app/controllers/company/users_controller.rb

Or is there better ways to handle this?

Look into namespacing your routes. So you could end up with the directory structure above, and have it be clean like:

resources :company resources :theme

resources :users

namespace :admin resources :company … end

Oh, I also want to add that Admins will manage Users too, not just

Companies and Themes. So there is a conflict where Companies can

create/edit/delete Users and use/not use the same controller. There is

also potential route conflicts unless you use nested resources.

I also want to add that Admins can do some things to users, but

Company users can do other things. It’s not just a subset - but the

views and actions and forms will be somewhat different.

That is why I am having difficulty figuring out the routes, what the

controllers should be, and what to generalize and what to keep

separate.

That is a challenge and there is no right answer, only what is cleaner. What is nice about the namespacing way of going is that you have a more clear delineation as far as rights for the admin. It might mean you dup some code between the controllers. But that is certainly cleaner than having special cases in a single controller that sooner or later could become problematic.

Interesting... I forgot about namespaces. So you suggest namespacing the admin, but nest "my_company"?

Also, how is my proposed directory structure? Sound good? Is there a better way?

How do you deal with form_for's when the routes are nested?

For example, let's say you have a Admin::CompaniesController in your :admin namespace. The model is obviously Company. I get an error for new forms:

    <%= simple_form_for(@company, :url => admin_company_path(@company)) do |f| %>

Here's the error message:

    ActionView::Template::Error: No route matches {:action=>"show", :controller=>"admin/companies", :id=>#<Company id: nil, name: nil, phone_number: nil, address: nil, postal_code: nil, is_enabled: true, courses_created: 0, province_id: nil, theme_id: nil, payment_plan_id: nil, created_at: nil, updated_at: nil>}

How can I get rails to play nice? I have no idea why it's even trying to use the show action at all...

How do you deal with form_for’s when the routes are nested?

For example, let’s say you have a Admin::CompaniesController in

your :admin namespace. The model is obviously Company. I get an error

for new forms:

<%= simple_form_for(@company, :url =>

admin_company_path(@company)) do |f| %>

Here’s the error message:

ActionView::Template::Error: No route matches

{:action=>“show”, :controller=>“admin/companies”, :id=>#<Company id:

nil, name: nil, phone_number: nil, address: nil, postal_code: nil,

is_enabled: true, courses_created: 0, province_id: nil, theme_id: nil,

payment_plan_id: nil, created_at: nil, updated_at: nil>}

How can I get rails to play nice? I have no idea why it’s even trying

to use the show action at all…

Try running rake routes and see what you get… then either use what you have available or modify so you have the route you want.

Try running rake routes and see what you get.... then either use what you have available or modify so you have the route you want.

Well, that was the problem - it's not about the routes being - it was about rails doing the wrong thing.

The solution was to put:

[:admin, @company]

And then rails would generate the proper :post and :put routes given this. It tripped me because there's no way I would have thought of this...

I mean, to replace a parameter of a model to an array is just strange for me. It's elegant, but you have to know this in advance - it's hardly intuitive. Someone would have to tell you or you'd have to look at the source.