REST Style

Rimantas Liubertas said the following on 06/01/08 01:42 PM:

If putting the names of the controllers in the URL is not a REST thing, then why do the people proposing REST keep on doing it? Its like they're obsessed with it. Ever last example I've see, in this thread and on the 'Net, has it.

REST thing is URIs pointing at resources. It is not about the "names of controllers" it is about resources, "nouns".

Then you're just reinforcing my question. Why do all the examples put the names of the controllers in the URL?

I can understand it as a 'teaching example', to emphasize the nesting structure by labelling it, but are the 'labels' mandatory? Can't we move past the 'learning examples'? ARE the labels mandatory?

As I say, why does every example keep doing it?

Asked and answered: it's the way Rails does it by default.

Michael Schuerig said the following on 06/01/08 01:44 PM: >> The point I was trying to make is that this need not be written >> >> http://example.com/web/HouseRenovation/topic/GetBuildingPermit >> ^^^ ^^^^^ >> >> I make this point because all the examples I've given of 'nested >> resources' have the names of the controllers in the URL. >> >> If putting the names of the controllers in the URL is not a REST >> thing, then why do the people proposing REST keep on doing it? >> Its like they're obsessed with it. Ever last example I've see, in >> this thread and on the 'Net, has it. > > Because it is easy, you can get it automatically, and often it > fits. In Rails it's what map.resources gives you for free. If it > doesn't suit your needs, well, feel free to customize all the way > you want. It'll be a bit more work, but can be RESTful all the > same. > > For example, I'm using some fairly generic code to audit changes to > resources. Display of audit logs is handled by a single controller > across all resources and I can get logs for everything, a > particular resource type, or a single resource; all of this in > various formats. Rails routing doesn't provide the URLs I want > automatically, of course, but I can easily persuade it to do my > bidding > > map.with_options(:controller => 'audits', :action => 'show') do > >a> a.audits '/audits', :format => 'html' > a.audits '/audits.:format' > a.audits ':resources/audits', :format => 'html' > a.audits ':resources/audits.:format' > a.audits ':resources/:id/audits.:format' > a.audits ':resources/:id/audits', :format => 'html' > end

Have you deliberately chosen a bad example? You've still got the name of the controller in each URL. Which is what I'm trying to address in this part of the thread.

Feel free to rename the controller to 'something_else_entirely'. I don't know why you would do that, though, because it comes quite naturally that a specific resource or a specific representation format is handled by a single controller.

And yes, Understand "its a design decision" you made. But how could you have made it differently?

See above, I could easily have used a special controller on each of the routes above and I could have given those controllers some weird names, but why should I?

Michael

Then you're just reinforcing my question. Why do all the examples put the names of the controllers in the URL?

If you talking about the default routes provided by Rails then you're completely missing something. It's not the names of the controllers that are in the URI, they are the names of the resources. The Rails convention happens to name the controllers after the resource names. This works well because of the same reasons that naming ActiveRecord attributes after their column in the database makes things much easier. This removes the need for complex object relational mapping configuration files (convention over configuration).

What Rails is attempting to do with REST is similar. Just like with ActiveRecord, if you decide to develop you own conventions then it's just going to make your life, and the life of consumer of your service, more difficult. There's not anything particularly wrong with that, it comes down to choice. Do it the Rails way and it's easy and well understood. Do it your way and put in the extra effort to work around the conventions.

Every decision that remains with the developer adds complexity to the system. When those decisions are made for you by the framework it serves to simplify your design. That's the goal of the REST implementation in Rails. Whether they have achieved that goal is another story altogether.

Michael D. Ivey said the following on 06/01/08 03:33 PM:

Robert Walker said the following on 06/01/08 04:50 PM:

Then you're just reinforcing my question. Why do all the examples put the names of the controllers in the URL?

If you talking about the default routes provided by Rails then you're completely missing something. It's not the names of the controllers that are in the URI, they are the names of the resources. The Rails convention happens to name the controllers after the resource names. This works well because of the same reasons that naming ActiveRecord attributes after their column in the database makes things much easier. This removes the need for complex object relational mapping configuration files (convention over configuration).

True, but it neither addresses my question nor helps me figure out how I can make use of this in my applications.

Back to my "HOW" set of questions.

And as a note, some of the examples have had quote involved routes files and generated routes for all the extra methods. Which really is 'configuration'.

Michael Schuerig said the following on 06/01/08 04:39 PM:

Michael Schuerig said the following on 06/01/08 01:44 PM:

The point I was trying to make is that this need not be written

  http://example.com/web/HouseRenovation/topic/GetBuildingPermit                      ^^^ ^^^^^

I make this point because all the examples I've given of 'nested resources' have the names of the controllers in the URL.

If putting the names of the controllers in the URL is not a REST thing, then why do the people proposing REST keep on doing it? Its like they're obsessed with it. Ever last example I've see, in this thread and on the 'Net, has it.

Because it is easy, you can get it automatically, and often it fits. In Rails it's what map.resources gives you for free. If it doesn't suit your needs, well, feel free to customize all the way you want. It'll be a bit more work, but can be RESTful all the same.

For example, I'm using some fairly generic code to audit changes to resources. Display of audit logs is handled by a single controller across all resources and I can get logs for everything, a particular resource type, or a single resource; all of this in various formats. Rails routing doesn't provide the URLs I want automatically, of course, but I can easily persuade it to do my bidding

  map.with_options(:controller => 'audits', :action => 'show') do >a> a.audits '/audits', :format => 'html' a.audits '/audits.:format'     a.audits ':resources/audits', :format => 'html'     a.audits ':resources/audits.:format'     a.audits ':resources/:id/audits.:format'     a.audits ':resources/:id/audits', :format => 'html'   end

Have you deliberately chosen a bad example? You've still got the name of the controller in each URL. Which is what I'm trying to address in this part of the thread.

Feel free to rename the controller to 'something_else_entirely'. I don't know why you would do that, though, because it comes quite naturally that a specific resource or a specific representation format is handled by a single controller.

And yes, Understand "its a design decision" you made. But how could you have made it differently?

See above, I could easily have used a special controller on each of the routes above and I could have given those controllers some weird names, but why should I?

??? I'm not asking for 'weird names'. I'm not asking to rename the controller. Go back to my 'projects' example.

  http://example.com/web/HouseRenovation/topic/SandAndvarnishFloor vs   http://example.com/HouseRenovation/SandAndvarnishFloor

I'm asking WHY do you keep on putting that FIXED STRING that identifies the controller (even indirectly via the 'with options'). As I say, its chronic for nested controllers/resources.

I can understand it for the operations like /login and /signup that are 'out of band' of the normal flow of the application (though I know many CAs who believe that auditing is part of the normal flow of operations of a business, and a lot of recent legislation seems to support that view), but why do people keep putting it into the normal flow?

I'm asking if its mandatory. If its not mandatory then why do it?

Please don't say 'its easier' or 'less work' or something along those lines. I've experimented with (non REST) routing in my wiki and the difference between

       http://example.com/wiki/System/Homepage and        http://example.com/System/Homepage

is just a few characters in routes.rb. Its about the same 'work' either way. And I do even have a way to make the 'login' and 'signup' "in band" when I choose to implement it.

How can I tell that you're no making a 'virtue out of a necessity'?

I'm asking WHY do you keep on putting that FIXED STRING that identifies the controller (even indirectly via the 'with options'). As I say, its chronic for nested controllers/resources.

I'm not sure I'm able to follow you. I need that fixed string, because in some way requests have to be mapped to controllers. If I use map.resources, Rails does the mapping behind the scenes; if I use plain map.connect, I have to do the mapping myself.

http://example.com/web/HouseRenovation/topic/SandAndvarnishFloor vs http://example.com/HouseRenovation/SandAndvarnishFloor

map.connect '/*categories',   :controller => 'products', :action => 'index'

class ProductsController < ActionController::Base   def index     @items = Product.find_by_categories(params[categories])     ...   end end

class Product < ActiveRecord::Base   def self.find_by_categories(categories)     # find all products assigned to the most specific category   end end

Michael

I'm not asking for 'weird names'. I'm not asking to rename the controller. Go back to my 'projects' example.

  http://example.com/web/HouseRenovation/topic/SandAndvarnishFloor vs   http://example.com/HouseRenovation/SandAndvarnishFloor

I'm asking WHY do you keep on putting that FIXED STRING that identifies the controller (even indirectly via the 'with options'). As I say, its chronic for nested controllers/resources.

<...>

I am about to give up :frowning: If there is fixed string it usually identifies resource (which is core for REST), but of course it may be that controller has the same name. It's controller named after resource not the other way around, so you have names of the resources in your URI and you may have controllers with the same names.

Let's take another bad example blog. You may have:

http://blog.example.com/posts/ -> this way you can get list of resources, blog postings.

http://blog.exampe.com/posts/1 -> there you identify single resource, a blog post. You may choose different way to go, let's say http://blog.example.com/posts/the-first-post or even http://blog.example.com/the-first-post – it doesn't really matter, except that you get the first for free and another options will require some additional work.

Now, there come comments. http://blog.example.com/posts/1/comments - a list of comments for the current entry. You may as well have http://blog.example.com/posts/the-first-post/comments or http://blog.example.com/the-first-post/comments/

Going down the nested resources road you will have

http://blog.example.com/posts/1/comments/2 http://blog.example.com/posts/the-first-post/comments/2 http://blog.example.com/the-first-post/comments/2

If you want you can event get rid of /comments/ and implement http://blog.example.com/post-id-or-name/comment-id identification schema – it does not really matter.

As for http://blog.example.com/posts/1/comments/2 it is not putting the names of the controllers into URI, it's still about identification of resources: /posts/ identifies collection of posts /posts/1 identifies specific post from that collection /posts/1/comments identifies collection of comments for that specific post /posts/1/comments/2 identifies specific comment.

Of course, you can implement your own identification schema, making all more obscure, but it can soon make life more difficult. This may make sense if your site provides quite limited set of resources. You can sure map your blog posts and comments to http://blog.example.com/post-id-or-name/comment-id but then you will have to think, how to do with categories, tags, etc. http://blog.example.com/rest-way - is that post, or is that name of the category? http://blog.example.com/categories/rest-way – now it is clear.

Basicaly having full path to resource in URI makes life easier. Just don't think about it as about controllers. Think resources.

Regards, Rimantas

Rimantas Liubertas said the following on 06/01/08 07:09 PM:

[snip - clarification of resoruce vs controller acceped and skipped]

If you want you can event get rid of /comments/ and implement http://blog.example.com/post-id-or-name/comment-id identification schema – it does not really matter.

Yes, "I want". How?

map.resources :webs do |webs|     webs.resources :pages end

map.page "/:web_name/:page_name", :controller => "pages", :action => "show"

However, most apps DON'T have just one resource type. Most have lots of Things: users, accounts, places, pages, monkeys ... etc.

So be sure to put your extra resource route last.

Is that what you're asking for?

Well, it's /possible/, but you do get confusion. If you add this snippet to the top of routes.rb (There should be a better place to put it, but I was trying to do this quickly)

module ActionController   module Resources     class Resource       def path         @path ||= "#{path_prefix}"         # this is normally @path ||= "#{path_prefix}/#{plural}"       end     end   end end

If you then do a 'map.resources :projects' you will get the full array of REST routes, (GET, POST, PUT, DELETE) linked up as appropriate. 'rake routes' produces the following:

projects GET / {:controller=>"projects", :action=>"index"}          formatted_projects GET /.:format {:controller=>"projects", :action=>"index"}                             POST / {:controller=>"projects", :action=>"create"}                             POST /.:format {:controller=>"projects", :action=>"create"}                 new_project GET /new {:controller=>"projects", :action=>"new"}       formatted_new_project GET /new.:format {:controller=>"projects", :action=>"new"}                edit_project GET /:id/edit {:controller=>"projects", :action=>"edit"}      formatted_edit_project GET /:id/edit.:format {:controller=>"projects", :action=>"edit"}                     project GET /:id {:controller=>"projects", :action=>"show"}           formatted_project GET /:id.:format {:controller=>"projects", :action=>"show"}                             PUT /:id {:controller=>"projects", :action=>"update"}                             PUT /:id.:format {:controller=>"projects", :action=>"update"}                             DELETE /:id {:controller=>"projects", :action=>"destroy"}                             DELETE /:id.:format {:controller=>"projects", :action=>"destroy"}

Which is as you wanted, routing-wise. Using

def to_param   self.name end

In our project model means that the name is used instead of the id number for any generated links, and a similar change in the controller from 'find_by_id(params[:id])' to 'find_by_name(params[:id])' works similarly. Obviously all the project names should be unique, and in addition, you should disallow a project named 'new'.

However, the problem comes when we decide that each project should have tasks.

map.resources :projects, :has_many => :tasks

I won't post the full routing list, as it is rather long, but I will pull out two lines from it:

... project GET /:id {:controller=>"projects", :action=>"show"} ... project_tasks GET /:project_id {:controller=>"tasks", :action=>"index"} ...

As you can see, the two routes, for two different resources (the project and it's subsidiary tasks) are indentical. Rails has a 'first declared, first served' policy for routing, so it's impossible to get a list of all the tasks of a project. (Well, not impossible, but requires some thought to get around - see later) It's also not REST, which is based around the idea of each url pointing to one (and only one) resource.

That is actually the only conflict you get in the case of a project with many tasks, but if a project also has notes, then suddenly you're also posting to the same place to create several seperate resources. And obviously, in addition to notes and tasks needing unique names within themselves, you can't have a note and a task with the same name. ('name' used here as a placeholder for whatever id parameter is used to find projects by)

Of course, with more hacking of the Resources module, you could probably make it such that the controller name was used for the index action. So /projects gave you a list of all the projects /:project_name was an individual project /:project_name/tasks was all the tasks associated with a project /:project_name/:task_name was an individual task etc.

Obviously, you'd need further constrains on allowed names to stop people using projects, tasks, etc. as names of projects or tasks, by accident or with malicious intent.

Or if monkeying around in the actionpack source isn't what you want, hook up all your routes manually in routes.rb. The full REST routing for a single resource is 14 routes, once you take into account the format and edit and new actions, but none of them are especially complicated to write. You start with something like

map.projects '/', :controller => 'projects', :action => 'index', :conditions => { :method => :get } map.project '/:id', :controller => 'projects', :action => 'show', :conditions => { :method => :get } and so on, changing the action and condition as appropriate.

So, that's how you /can/ have URLs in the form you desire, but it is not a trivial task, at least without some thought on how you will handle such things as route generation and the overlaps in URLs I mentioned earlier.

I hope this helps, however, Jonathan.

namelessjon said the following on 07/01/08 05:56 AM:

[.. snip ..]

If you then do a 'map.resources :projects' you will get the full array of REST routes, (GET, POST, PUT, DELETE) linked up as appropriate. 'rake routes' produces the following:

Could you repost that formatted differently, please. It seems to have wrapped in a confusing manner.

Thanks.

end

In our project model means that the name is used instead of the id number for any generated links,

Which I strongly advocate for both security reasons (see other thread) and user intelligibility.

However, the problem comes when we decide that each project should have tasks.

I don't see why its a problem. The reason I asked about this was that in my Wiki is a real short piece of routing. Two lines in a "map.with_options" wrapper.

map.resources :projects, :has_many => :tasks

I won't post the full routing list, as it is rather long, but I will pull out two lines from it:

Perhaps you could explain WHY it has to be long. As I said, my 'old' way doesn't make the routing long.

... project GET /:id {:controller=>"projects", :action=>"show"} ... project_tasks GET /:project_id {:controller=>"tasks", :action=>"index"} ...

As you can see, the two routes, for two different resources (the project and it's subsidiary tasks) are indentical. Rails has a 'first declared, first served' policy for routing, so it's impossible to get a list of all the tasks of a project.

??? Only a problem if the spec requires that the UI has that. Things like generating html select lists can still be one 'internally'. If its not a requirement its not a problem, and for my design its not a requirement. Lists of topics in a web are handled differently since they have to have metadata, links and access control applied. Its a design difference.

Yes, I'm making a 'virtue out of a necessity'.

(Well, not impossible, but requires some thought to get around - see later) It's also not REST, which is based around the idea of each url pointing to one (and only one) resource.

That is actually the only conflict you get in the case of a project with many tasks, but if a project also has notes, then suddenly you're also posting to the same place to create several seperate resources. And obviously, in addition to notes and tasks needing unique names within themselves, you can't have a note and a task with the same name. ('name' used here as a placeholder for whatever id parameter is used to find projects by)

So the 'old way' is superior for this class of problem. I can keep nesting it with not much effort.

Heck,   http://Webname/Topicname/edit from        /:web/:topic/:action

could equally well be

  http://Webname/Topicname?action=edit

I've got a lot of flexibility.

Of course, with more hacking of the Resources module, you could probably make it such that the controller name was used for the index action. So /projects gave you a list of all the projects /:project_name was an individual project /:project_name/tasks was all the tasks associated with a project /:project_name/:task_name was an individual task etc.

Indeed: effectively taking out-of-band of the normal 'address space'.

In effect, /login, /signup are out of band and its not that difficult to think of /web/:webname a being an administrative 'out of band' interface.

Some Wikis are obsessive about having EVERYTHING as a wiki page, login, signup, creating new webs, changing access control. Personally I see those as 'out of band' of the working with the wiki pages. So for me, its not a problem.

And slotting /web/:webname in as an admin function is a easy way for me to segue into using REST.

Obviously, you'd need further constrains on allowed names to stop people using projects, tasks, etc. as names of projects or tasks, by accident or with malicious intent.

:slight_smile: I was just going to ask about that. :slight_smile:

In my present setup I have

     :requirements => {                       :web => /[A-Z][a-z]+/,                       :topic => /([A-Z][a-z]+){2,}/,

That is, the topic has to be a CamelWord. (that RE probably needs refinement in due course) and the web has only a leading uppercase. I also have

           :action => /[a-z][a-z_]{3,}/

for where its appropriate.

Can these requirements be re-mapped?

Or if monkeying around in the actionpack source isn't what you want,

At my level of experience with it, it feels a bit daunting.

hook up all your routes manually in routes.rb. The full REST routing for a single resource is 14 routes,

OUCH!

once you take into account the format and edit and new actions, but none of them are especially complicated to write. You start with something like

map.projects '/', :controller => 'projects', :action => 'index', :conditions => { :method => :get } map.project '/:id', :controller => 'projects', :action => 'show', :conditions => { :method => :get } and so on, changing the action and condition as appropriate.

ARRGH! Wrapping confusion! Again.

So, that's how you /can/ have URLs in the form you desire, but it is not a trivial task, at least without some thought on how you will handle such things as route generation and the overlaps in URLs I mentioned earlier.

One of the benefits being touted for REST is that it makes things simpler. But what I'm seeing is that for what I'm doing it makes it more complicated.

I hope this helps, however, Jonathan.

Yes.

namelessjon said the following on 07/01/08 05:56 AM:

> [.. snip ..]

> If you then do a 'map.resources :projects' you will get the full > array of REST routes, (GET, POST, PUT, DELETE) linked up as > appropriate. 'rake routes' produces the following:

Could you repost that formatted differently, please. It seems to have wrapped in a confusing manner.

Thanks.

There, a pastie should avoid the confusion and formatting:

http://pastie.caboo.se/136143

> end

> In our project model means that the name is used instead of the id > number for any generated links,

Which I strongly advocate for both security reasons (see other thread) and user intelligibility.

Yes, it does aid in the readability, and I generally do use 'name'- type URLs when I make applications, REST non-withstanding. The security aspect I am less sold on. Not, I should hasten to add that I think security is bad, just that in many cases, the resources/pages/ whatever you want to call them already have a publically avaiable indexes and so it's rather trivial to iterate over all of them anyway.

> However, the problem comes when we decide that each project should > have tasks.

I don't see why its a problem. The reason I asked about this was that in my Wiki is a real short piece of routing. Two lines in a "map.with_options" wrapper.

> map.resources :projects, :has_many => :tasks

> I won't post the full routing list, as it is rather long, but I will > pull out two lines from it:

Perhaps you could explain WHY it has to be long. As I said, my 'old' way doesn't make the routing long.

Well, it's the REST that makes it so long (essentially) Or more specifically, connecting things such that the four HTTP verbs are mapped to appropriate URLs and Controller methods, e.g. GET matched to show, DELETE mapped to destroy, etc. If you used your old method, but still wanted to REST, then you would need a lot of "if request.post?" "if request.put?" "if request.get?" etc. in your controller methods. In a RESTful controller, you have a more complex routing and several small methods.

> ... project GET /:id {:controller=>"projects", :action=>"show"} ... > project_tasks GET /:project_id {:controller=>"tasks", > :action=>"index"} ...

> As you can see, the two routes, for two different resources (the > project and it's subsidiary tasks) are indentical. Rails has a > 'first declared, first served' policy for routing, so it's impossible > to get a list of all the tasks of a project.

??? Only a problem if the spec requires that the UI has that. Things like generating html select lists can still be one 'internally'. If its not a requirement its not a problem, and for my design its not a requirement. Lists of topics in a web are handled differently since they have to have metadata, links and access control applied. Its a design difference.

Yes, I'm making a 'virtue out of a necessity'.

Well, when I said 'impossible' I was talking within the constraints of REST, at least as I understand it, rather than 'impossible in the grand scheme of things'. In REST, the project is not the list of its subtasks, they are separate entities, and so should be 'GETable' separately, hence they should have individual URLs.

[SNIP]

Some Wikis are obsessive about having EVERYTHING as a wiki page, login, signup, creating new webs, changing access control. Personally I see those as 'out of band' of the working with the wiki pages. So for me, its not a problem.

And slotting /web/:webname in as an admin function is a easy way for me to segue into using REST.

See the end of the email for comments on that, I orginally missed it up here.

> Obviously, you'd need further constrains on allowed names to stop > people using projects, tasks, etc. as names of projects or tasks, by > accident or with malicious intent.

:slight_smile: I was just going to ask about that. :slight_smile:

In my present setup I have

     :requirements => {                       :web => /[A-Z][a-z]+/,                       :topic => /([A-Z][a-z]+){2,}/,

That is, the topic has to be a CamelWord. (that RE probably needs refinement in due course) and the web has only a leading uppercase. I also have

           :action => /[a-z][a-z_]{3,}/

for where its appropriate.

Can these requirements be re-mapped?

Well, keeping them within the routing is probably a good thing, but you probably also want to add them as validations to the Web / Topic model With:

def Topic < ActiveRecord::Base

  validates_format_of :name, :with => /([A-Z][a-z]+){2,}/

  validates_uniqueness_of :name

end

Or similar, so that when people create invalid names, they can't, rather than creating a page they subsequently can't access.

> Or if monkeying around in the actionpack source isn't what you want,

At my level of experience with it, it feels a bit daunting.

That's no excuse. :wink: Looking for that routing code was the first time I really have tried hacking at the Rails source.

> hook up all your routes manually in routes.rb. The full REST routing > for a single resource is 14 routes,

OUCH!

ARRGH! Wrapping confusion! Again.

I am most sorry about that. It seems that if you go over the line limit, it's all treated as a paragraph and wrapped together.

map.projects '/', :controller => 'projects',      :action => 'index', :conditions => { :method => :get }

map.project '/:id', :controller => 'projects',       :action => 'show', :conditions => { :method => :get }

and so on, changing the action and condition as appropriate.

I should note here that it is actually 7 'normal' routes, and then 7 'formatted' routes, for providing xml, yaml, etc. representations of the data.

One of the benefits being touted for REST is that it makes things simpler. But what I'm seeing is that for what I'm doing it makes it more complicated.

Well, it depends. It makes it simple to logically extract information from a collection of resources, nested or otherwise. It makes it simple represent data in a variety of ways. It does make it harder to have arbitrary URL schema, due to the nature of the system, and the need for everything to have a unique URL for performing operations on.

So for a wiki with URL requirements such as yours, being non-RESTful probably makes more sense. Although, if you did still want to be RESTful, a RESTful backend, with the full and unambiguous, though perhaps wasteful, REST routing of "topics/:topic_id/pages/:page_id". This could be under an '/api/' namespace, or something of that nature. The public requests pages with the /:topic/:page/:action routing, but the forms all POST/PUT/DELETE to the appropriate REST controller. There would be the need for some small logic used to correctly find the page depending on which route was used, but it wouldn't be complicated.

Which, reading your email more closely up, you considered.

Or just not use REST for this application. It isn't for everything.

Regards, Jonathan

namelessjon said the following on 07/01/08 11:16 AM:

...

There, a pastie should avoid the confusion and formatting:

Parked at Loopia

Thank you. Much appreciated. That's the rake output? What's the input?

In our project model means that the name is used instead of the id number for any generated links,

Which I strongly advocate for both security reasons (see other thread) and user intelligibility.

Yes, it does aid in the readability, and I generally do use 'name'- type URLs when I make applications, REST non-withstanding. The security aspect I am less sold on. Not, I should hasten to add that I think security is bad, just that in many cases, the resources/pages/ whatever you want to call them already have a publically avaiable indexes and so it's rather trivial to iterate over all of them anyway.

Which is why I "don't do that, then".

However, the problem comes when we decide that each project should have tasks.

I don't see why its a problem. The reason I asked about this was that in my Wiki is a real short piece of routing. Two lines in a "map.with_options" wrapper.

map.resources :projects, :has_many => :tasks I won't post the full routing list, as it is rather long, but I will pull out two lines from it:

Perhaps you could explain WHY it has to be long. As I said, my 'old' way doesn't make the routing long.

Well, it's the REST that makes it so long (essentially) Or more specifically, connecting things such that the four HTTP verbs are mapped to appropriate URLs and Controller methods, e.g. GET matched to show, DELETE mapped to destroy, etc. If you used your old method, but still wanted to REST, then you would need a lot of "if request.post?" "if request.put?" "if request.get?" etc. in your controller methods. In a RESTful controller, you have a more complex routing and several small methods.

Thanks for clearing that up. So it's about trade-offs.

... project GET /:id {:controller=>"projects", :action=>"show"} ... project_tasks GET /:project_id {:controller=>"tasks", :action=>"index"} ... As you can see, the two routes, for two different resources (the project and it's subsidiary tasks) are indentical. Rails has a 'first declared, first served' policy for routing, so it's impossible to get a list of all the tasks of a project.

??? Only a problem if the spec requires that the UI has that. Things like generating html select lists can still be one 'internally'. If its not a requirement its not a problem, and for my design its not a requirement. Lists of topics in a web are handled differently since they have to have metadata, links and access control applied. Its a design difference.

Yes, I'm making a 'virtue out of a necessity'.

Well, when I said 'impossible' I was talking within the constraints of REST, at least as I understand it, rather than 'impossible in the grand scheme of things'. In REST, the project is not the list of its subtasks, they are separate entities, and so should be 'GETable' separately, hence they should have individual URLs.

[SNIP]

Some Wikis are obsessive about having EVERYTHING as a wiki page, login, signup, creating new webs, changing access control. Personally I see those as 'out of band' of the working with the wiki pages. So for me, its not a problem.

And slotting /web/:webname in as an admin function is a easy way for me to segue into using REST.

See the end of the email for comments on that, I orginally missed it up here.

Obviously, you'd need further constrains on allowed names to stop people using projects, tasks, etc. as names of projects or tasks, by accident or with malicious intent.

:slight_smile: I was just going to ask about that. :slight_smile:

In my present setup I have

     :requirements => {                       :web => /[A-Z][a-z]+/,                       :topic => /([A-Z][a-z]+){2,}/,

That is, the topic has to be a CamelWord. (that RE probably needs refinement in due course) and the web has only a leading uppercase. I also have

           :action => /[a-z][a-z_]{3,}/

for where its appropriate.

Can these requirements be re-mapped?

Well, keeping them within the routing is probably a good thing, but you probably also want to add them as validations to the Web / Topic model

I told you I was paranoid. Guess what I do?

The purpose of the routing is send it to the right controller & method.

I could have just use

                 *url

and done all the work in the controller. I chose not to. Trade-offs.

With:

def Topic < ActiveRecord::Base

  validates_format_of :name, :with => /([A-Z][a-z]+){2,}/

  validates_uniqueness_of :name

end

Or similar, so that when people create invalid names, they can't, rather than creating a page they subsequently can't access.

Yes, that validates the creation. But validating the routing counts too, so that invalid URLs get routed:

   http://CamelName/Noncamelname    http://nonCamelName/Noncamelname    http://Noncamelname/nonname

and they don't even touch the controller.

I see this as pushing error checking back as early in the process as possible.

Or if monkeying around in the actionpack source isn't what you want,

At my level of experience with it, it feels a bit daunting.

That's no excuse. :wink:

I don't carry a 50mm machine gun for the same reason. Are you suggesting I should? :slight_smile: Let's think what else I don't think I'm inexperienced enough to be doing? Do you need brain surgery or an organ transplant?:slight_smile:

Looking for that routing code was the first time I really have tried hacking at the Rails source.

I'll stick to OS and compilers :slight_smile:

hook up all your routes manually in routes.rb. The full REST routing for a single resource is 14 routes,

OUCH!

ARRGH! Wrapping confusion! Again.

I am most sorry about that. It seems that if you go over the line limit, it's all treated as a paragraph and wrapped together.

map.projects '/', :controller => 'projects',      :action => 'index', :conditions => { :method => :get }

map.project '/:id', :controller => 'projects',       :action => 'show', :conditions => { :method => :get }

and so on, changing the action and condition as appropriate.

Thanks you. I see now. Is there some equivalent to

  :requirements => { :web => /[A-Z][a-z]+/ }

for this? That the id-as-name has to meet some pattern or other constraint?

Side question: Do these "requirements" have to be REs ?

I should note here that it is actually 7 'normal' routes, and then 7 'formatted' routes, for providing xml, yaml, etc. representations of the data.

One of the benefits being touted for REST is that it makes things simpler. But what I'm seeing is that for what I'm doing it makes it more complicated.

Well, it depends. It makes it simple to logically extract information from a collection of resources, nested or otherwise. It makes it simple represent data in a variety of ways. It does make it harder to have arbitrary URL schema, due to the nature of the system, and the need for everything to have a unique URL for performing operations on.

Trade-offs :-/

So for a wiki with URL requirements such as yours, being non-RESTful probably makes more sense. Although, if you did still want to be RESTful, a RESTful backend, with the full and unambiguous, though perhaps wasteful, REST routing of "topics/:topic_id/pages/:page_id". This could be under an '/api/' namespace, or something of that nature. The public requests pages with the /:topic/:page/:action routing, but the forms all POST/PUT/DELETE to the appropriate REST controller. There would be the need for some small logic used to correctly find the page depending on which route was used, but it wouldn't be complicated.

I'll put that on the queue for a later revision.

Which, reading your email more closely up, you considered.

Or just not use REST for this application. It isn't for everything.

You've been patient and helpful. I hope others have benefited from this thread.

Hi guys,

I was rewatching the Railsconf 2006 keynote from David and its all about REST. He talks about the implementation of REST in combination with has_many :through. I think its a very good talk if you want to know more about REST. Even without the slides you can follow a long quite well. It gives a clear insight on how he was thinking at the time.

Its up for grabs here. http://blog.scribestudio.com/pages/rails/

You want the David Heinemeier Hansson download. And do yourself a favor and watch Martin Fowler.

Now don't go hammer the server all at once :slight_smile:

Regards.

Peter Dierx http://www.railstation.eu/blog

PS I will post this in 'REST Style' and 'WHY Rest'

Thank you. Much appreciated. That's the rake output? What's the input?

I was sure I said, but anyway

That is the output of 'rake routes' (which you will need to be using rails 2.0 to use - I think that's the first gem it appeared in, anyway) when my routes.rb consisted of:

map.resources :projects

And I had done the bit of monkey patching I pasted into an earlier email. ( reproduced in this pastie for future reference, and syntax highlighting. Parked at Loopia ).

I would be grateful if someone more experienced with rails could suggest a better place to put this than the top of routes.rb

[Massive snipping]

> Well, keeping them within the routing is probably a good thing, but > you probably also want to add them as validations to the Web / Topic > model

I told you I was paranoid. Guess what I do?

The purpose of the routing is send it to the right controller & method.

I could have just use

                 *url

and done all the work in the controller. I chose not to. Trade-offs.

> With:

> def Topic < ActiveRecord::Base

> validates_format_of :name, :with => /([A-Z][a-z]+){2,}/

> validates_uniqueness_of :name

> end

> Or similar, so that when people create invalid names, they can't, > rather than creating a page they subsequently can't access.

Yes, that validates the creation. But validating the routing counts too, so that invalid URLs get routed:

   http://CamelName/Noncamelname    http://nonCamelName/Noncamelname    http://Noncamelname/nonname

and they don't even touch the controller.

I see this as pushing error checking back as early in the process as possible.

Oh, no. Sorry, I should have been more positive in my agreement/ endorsement, rather than 'is probably a good thing'. I fully agree, keeping them in the routing is a good thing, and catching the errors earlier rather than later is a good thing. Though I think that catching them later would allow you to be more specific with an error, should that be your desire.

I was just pointing out the importance of also having some model validations, so that people couldn't make pages they can't access (and because of that, probably can't delete).

>>> Or if monkeying around in the actionpack source isn't what you want, >> At my level of experience with it, it feels a bit daunting.

> That's no excuse. :wink:

I don't carry a 50mm machine gun for the same reason. Are you suggesting I should? :slight_smile: Let's think what else I don't think I'm inexperienced enough to be doing? Do you need brain surgery or an organ transplant?:slight_smile:

Funny you should mention it, but ... no. :wink: Though messing in the rails source is /probably/ less dangerous than brain surgery.

> Looking for that routing code was the first time I really have tried > hacking at the Rails source.

I'll stick to OS and compilers :slight_smile:

I think I will leave that to you. :slight_smile:

>>> hook up all your routes manually in routes.rb. The full REST routing >>> for a single resource is 14 routes, >> OUCH!

>> ARRGH! Wrapping confusion! Again.

> I am most sorry about that. It seems that if you go over the line > limit, it's all treated as a paragraph and wrapped together.

> map.projects '/', :controller => 'projects', > :action => 'index', :conditions => { :method => :get }

> map.project '/:id', :controller => 'projects', > :action => 'show', :conditions => { :method => :get }

> and so on, changing the action and condition as appropriate.

Thanks you. I see now. Is there some equivalent to

        :requirements => { :web => /[A-Z][a-z]+/ }

for this? That the id-as-name has to meet some pattern or other constraint?

Well, according to the docs, yes. It's :requirements , the same as for normal routes.

map.project '/:id', :controller => 'projects',   :action => 'show', :conditions => { :method => :get },   :requirements => { :id => /[A-Z][a-z]+/ }

Side question: Do these "requirements" have to be REs ?

And, yes, they do. So far as I know, anyway.

[--snip--]

> Well, it depends. > It makes it simple to logically extract information from a collection > of resources, nested or otherwise. > It makes it simple represent data in a variety of ways. > It does make it harder to have arbitrary URL schema, due to the nature > of the system, and the need for everything to have a unique URL for > performing operations on.

Trade-offs :-/

Most things are in web design, no? I mean, if there weren't there wouldn't be so many jobs going round, as people would be much better placed to get it right all the time.

[--snip--]

> Or just not use REST for this application. It isn't for everything.

You've been patient and helpful. I hope others have benefited from this thread.

Well, I'm glad I could help you at least.

Regards, Jonathan