Edge Rails namespaced routing

Hi,

I was wondering if anyone came across a similar scenario when working with namespaced routes with edge rails.

Consider an application with controllers customers and products that also have other resources.

map.resources :customers do |customer|   customer.resources :notes   customer.resources :tags end

map.resources :products do |product|   customer.resources :notes   customer.resources :tags end

Now of course this poses a problem as it stands because the notes and tags controllers will conflict. I know there are polymorphic routes/ controllers (http://revolutiononrails.blogspot.com/2007/05/drying-up- polymorphic-controllers.html) however this doesnt make sense if the controllers have little shared logic/behaviour; only a common name. I know I can just use a different name, but with the addition of namespaces this should no longer be a concern with a proper design.

So.. I'm thinking something like:

/app/controllers/customers/root_controller.rb /app/controllers/customers/tags_controller.rb /app/controllers/customers/notes_controller.rb /app/controllers/products/root_controller.rb /app/controllers/products/tags_controller.rb /app/controllers/products/notes_controller.rb

with routes like:

map.namespace(:customers) do |customers|   customers.resources :root, :has_many => [:tags, :notes] end map.namespace(:products) do |products|   products.resources :root, :has_many => [:tags, :notes] end

creating routes like: /customers/root/#{root_id} /customers/root/#{root_id}/tags/#{id} /customers/root/#{root_id}/notes/#{id /products/root/#{root_id} /products/root/#{root_id}/tags/#{id} /products/root/#{root_id}/notes/#{id

This will work. But we have an unnecessary /root/ in the route. I suppose the solution would be adding a flag to the namespace function to pass a root controller with a default name of "root_controller" so the root of the namespace can itself be a resource. Can this be done? What do you guys think about it as an addition to rails?

This leads me to another scenario. Namespaced models.... quite simply as it stands you can in /app/models/ make a folder called /customer/ there add tag.rb and note.rb. With models Customer::Tag and Customer::Note. One thing is the table_name will remain "tags" and "notes", which in this case should be prefixed by the namespace name like "customer_tags" and "customer_notes". Also with the ability to have a root model like Customer in /customer/root.rb.

I'd love to hear that this can already be done as rails stands. I can make the patches and submit them, however I don't want to divulge from other design considerations.

Regards,

Peter

This leads me to another scenario. Namespaced models.... quite simply as it stands you can in /app/models/ make a folder called /customer/ there add tag.rb and note.rb. With models Customer::Tag and Customer::Note. One thing is the table_name will remain "tags" and "notes", which in this case should be prefixed by the namespace name like "customer_tags" and "customer_notes". Also with the ability to have a root model like Customer in /customer/root.rb.

On a totally unrelated note, I'm completely against the idea of namespaced models. Putting them in a separate directory is perfect, but not naming them like Customer::Tag. Why would u need to do that ?

-Pratik

Hey,

so you're saying that you are using the same resource names (notes,
tags) but you want those routes to go to different controllers?

map.resources :customers do |customer|    customer.resources :notes, :controller => 'customer_notes'    customer.resources :tags, :controller => 'customer_tags' end

map.resources :products do |product|    customer.resources :notes, :controller => 'product_notes'    customer.resources :tags, :controller => 'product_tags' end

In edge-rails your url helpers will automatically be name-prefixed so:

customer_notes_path #=> /customer/3/notes, controller ==
CustomerNotesController

etc etc

HTH, Trevor

To not have naming collisions. You can't have two classes named Tag in the same namespace (and not using one is still in a namespace.. the default one). Pretty standard namespace stuff.

+1 for namespaced models! :slight_smile:

So what will the table be for those namespaced models ?

I don't like namespaced models either, but its pretty easy to do. Just add the folder to the load path and all the models in the folder will be loaded. As for the table name, I think you end up with "customer_tag" or maybe just "tag". Either way you could set your own table name on the model.

The only good use I found for named spaced models is for third party code.

Exactly. config.load_paths was my point when I said "Putting them in a separate directory is perfect,".

Also, if you're gonna namespace your models as Customer::Tag - and put them inside models/customer/tag.rb - you better name your model CustomerTag and keep it as models/customer/customer_tag.rb

-Pratik

I have read over everyone's comments and I agree to a point. I'll go over each proposed solution and add my comments.

1. Use :controller => 'customer_notes' when specifying resources to have unique controller names 2. Use longer model names like CustomerNote instead of Customer::Note 3. Use load_paths to locate classes grouped by directory names

The first word that comes to mind is, "elegance". None of these produce an optimal solution. They are hacks to achieve the same functionality that namespaces could offer.

Comments ...

1. The same reason that rails core named their classes ActiveRecord::Base, not ActiveRecordBase, or ActonController::Caching::Actions, not ActionControllerCachingActions, etc., is its poor style/design/foresight to use CustomerNotesController instead of Customers::NotesController. We should be leveraging the ruby language, not shaping ruby to fit rails. What happens if we have to refactor? In addition, having to configure the routes by hand to use the correct controller name is not the rails-way.

2. A lot of the same reasons I wrote in #1. What about an enterprise- level web application with 80 models. If we prefix them with another name anyways, why not make a namespace out of it?

3. So for every module I write, I should have an entry in my environment listing each of them so it can load the paths properly? Thats rhetorical.

The bottom line is, convention over configuration is the rails motto but it doesn't seem to be in these cases. It is short sighted to think that each application designed should only serve the purposes within that application. The concept of modules and components are necessary in a proper software design. How often do people reuse their controllers, models, etc.? My biggest question is, what is the resistance? Why not have more specific directory structures that organize our controllers/models without the worry of conflicting names. I can't think of a single reason why not. It's starting to begin with the new map.namespace functionality for edge rails' routing.

Thanks for all the feedback everyone. Rails is a phenomenal framework and its the communal effort that made it this way.

Regards,

Peter

1. The same reason that rails core named their classes ActiveRecord::Base, not ActiveRecordBase, or ActonController::Caching::Actions, not ActionControllerCachingActions, etc., is its poor style/design/foresight to use CustomerNotesController instead of Customers::NotesController. We should be leveraging the ruby language, not shaping ruby to fit rails. What happens if we have to refactor? In addition, having to configure the routes by hand to use the correct controller name is not the rails-way.

Reason why it's AR::Base and not ActiveRecordBase is because AR is a module that has lot more stuff than just the Base class.

Customers::NotesControlle is poor to use instead of NotesController < CommonController. Or you can just do something like

class NotesController < AC   include CustomerBehavior end

Please note that you can have any directory structure you want, keeping sane controller names. Also, you can have any url you need as well.

2. A lot of the same reasons I wrote in #1. What about an enterprise- level web application with 80 models. If we prefix them with another name anyways, why not make a namespace out of it?

My current app has like 40 models so far. 40 more would fit in just perfect.

3. So for every module I write, I should have an entry in my environment listing each of them so it can load the paths properly? Thats rhetorical.

No. Just the directory. root/models/customer/

Thanks, Pratik

Peter,

When you invoke "convention over configuration" you have to bear in mind that *your* convention is a product of the problems you're trying to solve - and those problems may not match the majority's problem-space.

There's nothing in your proposal that I can see which would make any of my applications better structured - but I recognize that my own sample isn't necessarily representative of the whole.

The best way to show us how much better things would be is to take your ideas and implement them as a plugin - that way everyone can plainly see the benefits and how much the convention buys us.

Regards, Trevor

This leads me to another scenario. Namespaced models.... quite simply as it stands you can in /app/models/ make a folder called /customer/ there add tag.rb and note.rb. With models Customer::Tag and Customer::Note. One thing is the table_name will remain "tags" and "notes", which in this case should be prefixed by the namespace name like "customer_tags" and "customer_notes". Also with the ability to have a root model like Customer in /customer/root.rb.

I'd love to hear that this can already be done as rails stands. I can make the patches and submit them, however I don't want to divulge from other design considerations.

class Customer < ActiveRecord::Base; end

=> nil

Customer.table_name

=> "customers"

class Customer::Tag < ActiveRecord::Base; end

=> nil

Customer::Tag.table_name

=> "customer_tags"

It's already working in edge, did you try it out? It's smart enough to prefix the table name if the parent is an active record model, not a regular module. Try this layout:

app/models/customer.rb app/models/customer/tag.rb

I appreciate everyone sharing their own perspective. It's insightful.

Trevor, I absolutely agree with you in saying that "convention over configuration" applies to a universal solution. But how does what I'm proposing take away from that? Namespaces should by no means be an enforced axiom, but it should be one that exists for a particular scenario, which in my opinion can work for most.

Rick, that sounds great! Thats exactly what I'm talking about here. So it looks like there is already some overlap. What would the class definitions for customer.rb and tag.rb look like in edge?

... I'm assuming:

class Customer < ActiveRecord::Base end # table => "customers"

class Customer::Tag < ActiveRecord::Base end # table => "customer_tags"

Regards,

Peter

To add to that.. Rick, that type of parent/child functionality in models is exactly what I have envisioned but with controllers. So if that means instead of putting everything in the /app/models/customer/ directory, there exists a parent model and then child models under the directory of the parent class name, I'm all for that. To revise my first posting then:

routes:

map.resources :customers, :namespace => true do |customers|   customers.resources :tags   customers.resources :notes end

or

map.resources :customers, :namespace => :customers do |customers|   customers.resources :tags   customers.resources :notes end

or

map.namespace :customers, :resource => :customers do |customers|   customers.resources :tags   customers.resources :notes end

controllers:

/app/controllers/customers_controller.rb /app/controllers/customers/notes_controller.rb /app/controllers/customers/tags_controller.rb

..something to that effect.

Regards,

Peter

However,

as it stands in edge it might work that:

map.resources :customers map.namespace(:customers) do |customers|   customers.resources :tags   customers.resources :notes end

This would _almost_ work. Except that there wouldnt be a reference (at least not apparent through this routing) that tags and notes are children of customers. So you can have routes like /customers, / customers/2, /customers/:action, /customers/notes, /customers/notes/ new, /customers/2/notes/4, etc.

Peter

Peter,

your original ideas don't "take-away" from anything. They just don't
speak to me and they wouldn't make my apps better organized. Note
that I'm talking strictly about the controllers issue - the
namespaced models thing... I use that all over the place.

But as far as interactions between routes.rb and controllers - I have
never felt that there should be a strict correlation between the
hierarchy of my URLs and my class structure. Your original proposal
seemed to push very strongly in that direction.

Trev

I didn't mean for my explaination enforce a strict mapping between controllers and routes. Only to the same extent that it already is now. This is already becoming apparent with the map.namespace route. I'd just like to see as I said a parent/child resource relationship in a namespace.

Peter

Hello again,

For information purposes I wanted to follow up on this discussion with an already existing solution in edge.

map.resources :customers do |ns|   ns.namespace(:customers) do |customers|     customers.resources :tags   end end

GET /customers/ {:action=>"index", :controller=>"customers"} POST /customers.:format/ {:action=>"create", :controller=>"customers"} GET /customers/new/ {:action=>"new", :controller=>"customers"} GET /customers/new.:format/ {:action=>"new", :controller=>"customers"} GET /customers/:id/edit/ {:action=>"edit", :controller=>"customers"} GET /customers/:id/edit.:format/ {:action=>"edit", :controller=>"customers"} GET /customers/:id/ {:action=>"show", :controller=>"customers"} GET /customers/:id.:format/ {:action=>"show", :controller=>"customers"} PUT /customers/:id/ {:action=>"update", :controller=>"customers"} PUT /customers/:id.:format/ {:action=>"update", :controller=>"customers"} DELETE /customers/:id/ {:action=>"destroy", :controller=>"customers"} DELETE /customers/:id.:format/ {:action=>"destroy", :controller=>"customers"} GET /customers/:customer_id/tags/ {:action=>"index", :controller=>"customers/tags"} GET /customers/:customer_id/tags.:format/ {:action=>"index", :controller=>"customers/tags"} POST /customers/:customer_id/tags/ {:action=>"create", :controller=>"customers/tags"} POST /customers/:customer_id/tags.:format/ {:action=>"create", :controller=>"customers/tags"} GET /customers/:customer_id/tags/new/ {:action=>"new", :controller=>"customers/tags"} GET /customers/:customer_id/tags/new/ {:action=>"new", :controller=>"customers/tags"} GET /customers/:customer_id/tags/new.:format/ {:action=>"new", :controller=>"customers/tags"} GET /customers/:customer_id/tags/new.:format/ {:action=>"new", :controller=>"customers/tags"} GET /customers/:customer_id/tags/:id/edit/ {:action=>"edit", :controller=>"customers/tags"} GET /customers/:customer_id/tags/:id/edit/ {:action=>"edit", :controller=>"customers/tags"} GET /customers/:customer_id/tags/:id/edit.:format/ {:action=>"edit", :controller=>"customers/tags"} GET /customers/:customer_id/tags/:id/edit.:format/ {:action=>"edit", :controller=>"customers/tags"} GET /customers/:customer_id/tags/:id/ {:action=>"show", :controller=>"customers/tags"} GET /customers/:customer_id/tags/:id.:format/ {:action=>"show", :controller=>"customers/tags"} PUT /customers/:customer_id/tags/:id/ {:action=>"update", :controller=>"customers/tags"} PUT /customers/:customer_id/tags/:id.:format/ {:action=>"update", :controller=>"customers/tags"} DELETE /customers/:customer_id/tags/:id/ {:action=>"destroy", :controller=>"customers/tags"} DELETE /customers/:customer_id/tags/:id.:format/ {:action=>"destroy", :controller=>"customers/tags"}

Now your controllers can be organized in a namespaced structure:

/app/controllers/customers_controller.rb => CustomersController /app/controllers/customers/tags_controller.rb => Customers::TagsController

Regards,

Peter