more on naming and the CRUD

— July 7, 2006 at 11:54 PDT

Sebastian Delmont blogged recently about a flexible approach to handling permalinks that is an interesting compromise between numeric IDs and semantically rich textual slugs. Coincidentally, Aristotle Pagaltzis described the same thing last December.

To summarize: Combine the ID and the textual slug into a URL. Your app will resolve the URL based only on the ID part. The text is there to make users comfortable and the SEO-obsessed happy. So /users/13, /users/13-josh, and /users/13-joshua would resolve to the same entity. Sebastian claims this happens in the database, but I'll note that "13-josh".to_i returns the integer 13 so your Ruby code will be happy too.

I think that combining this naming scheme with a 301 permanent redirect to update stale names sounds fairly workable. You still have the problem of your bookmarks not being equated, but if this technique becomes common could notice the 301s and merge rankings of different URLs for the same thing. Maybe they even do that already - I don't use any bookmarking sites so I wouldn't know. I'm assuming the overhead for the slug comparison and redirect won't be an excessive burden to an app.

On another note, Dan Peterson put together a nice CRUD scaffolding generator. It creates controllers with the 7 CRUD actions (index, new, create, show, edit, update, destroy) and compatible view templates. If you're looking to get started with CRUD, using simply_restful with Dan's generator is a good way to go. Unfortunately there is barely any documentation on how to structure RESTful controllers. Note the opportunity for someone who wants to write some much needed docs!

21 commentsplugins, rails, sightings

  1. ph2006-07-07 12:09:03

    Hello, here at work we are using the following users/ph-50/

    we add the number at the end for everything, i heard the word closer of the beginning of the url is more important for the search engine. (From a SEO consultant..)

  2. Twylo2006-07-07 13:23:36

    Thanks for this. I've been struggling with balancing CRUD with a user's demand for a little while. I've been writing a Rails app that serves images uploaded by users, and one of the big design criteria was that the name of the image file appear in the URL. This raised my CRUD-hackles, but eventually I consented. I will float this idea as a possible compromise!

  3. jay donnell2006-07-07 14:03:51

    Don't you love it when you do something and then a little while later you see someone else explain why it makes sense :)

    I did this exact thing for my current project, It's a blog site and the links for posts and tags are both done in this manner.

    There is one part where I think I differed from your approach a bit. If your seo obsessed then you do NOT want multiple urls going to the same place without a redirect. i.e. you do not want /users/13 and /users/13-josh both being a 200 for the same post. I may have misunderstood you, but I think you are implying that they both "resolve" as in no redirect.

    What I do in my controller is grab the id and get the post from the database. Then I url'ify the title of the post object and compare it to the actual url. If they match I continue processing. If not I redirect to the proper url. I.e. there is only ONE proper url. Everything else gets a redirect or a "no post found".

  4. Josh Susser2006-07-07 14:22:08

    @jay: That's just what I had in mind. If there is no match, do a 301 permanent redirect to the resolved page with the permalink URL.

    I looked at your site. I see you did the links with a slash not a dash. I think the dash works better for composing things like /posts/42/comments/5. /posts/42-random/comments/5 is easier to parse/route than /posts/42/random/comments/5

  5. jay donnell2006-07-07 15:26:38

    why is it easier? I use a route that is something like this:

    map.connect 'post/show/:id/:title', :controller => 'post', :action => 'show'

    parsing is done for me. In the controller I simply do something like:

    if params[:title] != @post.url_title #redirect code

  6. Josh Susser2006-07-07 15:32:27

    @jay: Yes, that works for the straightforward case. But what if you have something on the URL that trails the :title? That seems to be where RESTful routes are headed.

  7. peter2006-07-08 08:46:33

    Dan's CRUD scaffolding looks good. I'm going to use a couple of those ideas.

    However, I've had a problem with scaffolding for a while because it is not DRY and I think that it is misleading to folks just learning Rails. Scaffolding leads to so many controllers with similar code. If I make a change to one then I must propagate the change to all the scaffolded controllers. I think pushing common code down to the application controller is a much better idea. Then in controllers requiring special code, override methods and use calls to super where appropriate.

    See: art of mission

  8. Josh Susser2006-07-08 09:31:33

    @peter: Scaffolding isn't meant to be DRY. It's also not meant to be good enough code to use in your product. It's just a temporary crutch to help you get going. I definitely agree that refactoring common code up into the superclass will make for DRYer controllers and smaller subclasses, and you're approach looks like a good start. I just don't think it's worth doing that for scaffolding. But it's interesting to think about creating a RestfulController superclass to make it easy to write typical CRUD type controllers that work with the SimplyRestful plugin.

  9. peter2006-07-08 10:18:25

    If a RestfulController superclass is used, and a scaffold generator is designed knowing that controller is there, then teh scaffolded code would be much more likely to survive into production.

    With the upcoming changes in Rails, will the SimplyRestful plugin be obsolete? I haven't used this plugin but am preparing for the changes in Rails due to DHH's "World of Resources" slides.

  10. Josh Susser2006-07-08 10:36:53

    @peter: True, but the core team's philosophy about scaffolding is that it shouldn't survive into production. That's not saying everyone agrees with that. Streamlined looks very interesting as a configurable console to replace scaffolding for typical admin crud tasks. Personally, I think it would be useful to have a good superclass that would make building REST/CRUD controllers easier.

    DHH has said that SimplyRestful will be rolled into core for the 1.2 release, though I wouldn't be surprised to see a few changes to it before then.

  11. john2006-07-08 17:59:29

    I just don't see the point. Is it crazy of me to have a 'uri' column in my tables. I'm having to make a

    find(:all, :conditions => ["uri REGEXP ?", generated_uri])

    every time I do 'beforevalidationon_create' on a uri-ed model (handled by a simple plugin I cooked up) but man, its so simple. And Im not worried about the performance of creating new records as much as Im worried about accessing them semantically.

    What's the point. I say if you want to do something right then do it. This 'article title with a primary key latching onto it' talk is silly IMO.

    But then again I am not driven by google entirely. I want the UI to make sense. And the URL is a big part of the UI.

    After all these years I still think of the URL as the most fundamental part of a web interface. I hate seeing it meaningless just like I would hate to see ajax in scaffolding. We need to build stuff with a solid foundation and semantics and then add sugar. What we don't need is a bunch of sugar mixed in with some SEO and then sprinkle it with complicated code.

    I know there are already a handful of plugins that do all this stuff. I rolled my own because I have my own priorities. The point is that so many apps do it already and its so easy. Which leads me to my question.

    What is it that is so hard is all I want to ask. Maybe Im just out of my league here. Im probably missing something. But I got to ask, what is the difficult part about this topic. Am I missing something about the up-and-coming ActiveResource? Why is this a hard thing to solve? I guess its because everyone wants to do this stuff without changing the schema of a database and still have a word in their urls (and avoid diving too deep into AR with plugins). But help me out please Josh. What am I missing?

    lots of love and a huge appreciation for this blog, john

  12. john2006-07-08 18:13:37

    oh and Im drunk. Germany is number 3!

    I expect this all to make more sense in the morning.

    If you think Im bad now, just wait until I comment here in four years when we are number one.


  13. peter2006-07-08 19:56:22

    just like I would hate to see ajax in scaffolding

    I was just thinking today that AJAX should make it's way into scaffolding. Maybe not core Rails scaffolding but someones scaffolding plugin. It certainly works well in my controller superclass for CRUD controllers. Since respond_to makes it so easy to cater to those with and without JavaScript, why not have some AJAX automatically in the mix?

  14. Andreas2006-07-14 15:25:39

    The big problem with the /entity/integer type scheme is, they're not necessarily permanent. As soon as you compact your db sequence fields (like when optimizing), or transport/merge the database records to another DB, things are messed up badly. I think the auto-inc pks should be seen as a app internal thing that can only be trusted within a "unit of work" or transaction. They should not go out to the world as permanent handle. UUIDs would be better in this case, but they're not so user-friendly. The date-based approach is good IMHO ( but it only makes sense for CMS content.

  15. rick2006-07-15 05:59:17

    Primary key IDs will be the default for simply_restful and ActiveResource. Though as fate would have it, my first project using ActiveResource uses a token id, so I'm having to add custom primary key support.

    Also, are commas allowed? articles/about-my-cat,53

  16. Josh Susser2006-07-15 08:15:20

    @rick: Commas seem to be allowed, same as semi-colons: RFC3986 in section 3.3, last paragraph.

    @Andreas: Philosophically, I'll agree that eposing an internal aspect of the database implementation as an external identifier on the URL seems like a bad idea. Practically, I'm not sure it will ever matter. Creating and managing UIDs can bring another set of problems, especially in a highly parallel system. But I do think getting PKs out of URLs sounds like a good idea. Is a UID-human-friendly-words approach acceptable?

  17. Mike Perham2006-07-17 07:18:02

    Scaffolding generators are a terrible idea in practice, great for 2 minute getting started videos, terrible for actual product development. Take a look at Django's admin CRUD interface for their model objects. Not a single line of code in your app - it's all auto-generated on the fly. I'm hoping AjaxScaffold uses this approach in their move to a plugin/engine, rather than a generator.

  18. john2006-07-18 12:32:59

    hi josh, I just went though my current app and changed everything to fit the id-meaningful-identifier, and got rid of my plugin. The more I thought about it the more I got frustrated with my previous approach. And at the end of the day, who the hell cares if there is a number in the url. 'PK identifiers' is what rails is doing and rails is much smarter than I am. And if we can have our cake and eat it too...

    my previous approach was to have a uri column in tables that I wanted to give meaningful urls to. Then I would simply leave that column locked or possibly add a table dedicated to finding lost souls.

    Why I think Im not so smart for doing things this way: -There is no way to tell AR that the uri column is unique. That means find() and then find_by_uri() will have to make separate queries. -Making a url_graveyard table to find records that match old titles and stuff is a bummer when your 301 responses sound super easy and maintainable. -If I were to have said the hell with the url_graveyard and just kept the uri attribute unchangeable, well, what the hell is the point of being able to make your urls meaningful if you cant change them when the content of your record changes, or you come up with a better way to name the thing. -All in all, at the end of the day, the whole reason I have gotten so far into rails is because I know that there are much smarter and more experienced people developing this framework than I will ever be able to compare myself to. The conventions are what brought me here. Im just a jerk who loves this stuff. I look up to you and the rest of the community. And if rails says that my life will be easier using PKs as identifiers, Id be crazy to write my own hack to make things work my way.

    Anyway Im happy with my decision. I think its a great approach. Flexible and easy as pie to impliment. It will always work. SEO friendly. And I cant see anything wrong with it at all besides a number in every url (but those are the cards we have been dealt).

    Ive been doing a lot of work lately on my app so it conforms better to the rails-way and less to my hair-brained ideas that mostly have their roots in procedural programming. Im forever happier.

    Anyway thanks for the kick in the head. have a nice day.

  19. peter2006-07-18 14:21:12

    I've been doing a lot of work lately on my app so it conforms better to the rails-way and less to my hair-brained ideas.... Im forever happier.

    Likewise. Sometimes is difficult to see where I am going against the Rails-grain vs just writing the custom code a Rails app needs to do it's particular job. Once I do things the Rails way I am much happier!

  20. Mark2006-07-18 14:47:46

    Has anyone had the chance to look at Trestle?

  21. Jim Kane2006-12-27 02:51:17

    I read an article on this very same topic around the middle of the year and quickly integrated it into the blog directory I was building. It ends up being about 3 lines of Ruby code (probably two if I cared to go back and optimize it). Highly recommended idiom -- I hope this gets more press as it makes URLs that are friendly to man and machine!

Sorry, comments for this article are closed.