Scott Raymond on Refactoring to REST

Posted by josh July 20, 2006 @ 11:00 AM

Gonzo Rails developer Scott Raymond has written an analysis of how he refactored IconBuffet.com using RESTful routes. It's the most cogent and compelling description of the benefits of a CRUDdy/RESTful design that I've seen. He reduced the number of actions in the application by 25%, and reduced the size of his routes file from 16 lines to 3. Isn't small beautiful?

Posted in Sightings | 26 comments

Comments

  1. Ken Barker on 21 Jul 12:12:

    Ahhh! The missing content from DHH’s keynote.

    Much thanks for posting this.

    Ken

  2. James on 21 Jul 17:15:

    I’ve been wondering if I should start refactoring using the SimplyRestful plugin, or if I should wait for David’s ActiveResource?

  3. josh on 21 Jul 18:08:

    James, you can do CRUDdy controllers and RESTful routes without ever using ActiveResource. That approach is useful for vanilla web apps without an API. You can start with SimplyRestful right now if you are on edge Rails, or wait for the 1.2 release when it should be rolled into core.

  4. john on 21 Jul 18:24:

    Thats great, I have been working on similar re-organizing. And yup, taking CRUD more seriously has done amazing things for me.

    One thing I really love about all this in regards to where it is taking Rails is the uniformity of actions. Once we really start simplifying and recognizing the patterns in our work like this, it really opens interesting windows for further extraction and cool new ways to make even more of the not so fun stuff disappear.

    And of course all the obvious stuff is true too: less code, that is easier to organize.

    I sure am glad to have all of your guidance… and Im so excited! The future looks bright.

    Cheers to CRUD. And thanks, community, for being so good to me.

  5. Jamie Hill on 21 Jul 23:06:

    Just been playing with this stuff and noticed a rather large oversight in that you cannot delete items unless you have javascript on.

    I usually have a non-javascript fallback which does a get request on a confirm_delete action and do a post from there however I can’t see a way of doing this with the resource way.

  6. Alex on 22 Jul 00:12:

    Does anyone know if there are plans by the core team to refactor scaffolding for 1.2 to bring it in-line with the new SimplyRestful and ActiveResource way of doing things?

    The scaffold generated code is probably the first code that the typical new Rails users starts to play with so it makes sense.

    I am guesing however that refactoring scaffolding may not be the dream job that a core memeber wants to tackle and may be left for someone else to submit the work as a patch – if there is even a demand for a refactored scaffolding?

  7. Jose on 22 Jul 01:00:

    The Trestle Generator is pretty CRUD-ish scaffolding: http://trestle.rubyforge.org/

  8. rick on 22 Jul 03:12:

    Jamie: That is true. Right now the only default way to delete an item is with a form or an ajax call. You can technically use a URL like /foo/1?_method=delete, but then you run the risk of web accelerators deleting your stuff. Here’s what I’ve been doing if I really need a delete confirmation page:

    
    # sets a route like /projects/1;destroy
    map.resource :project, :member => { :destroy => :get }
    
    # the action.  On GET it just shows a simple confirmation message with a form button to destroy.
      def destroy
        if request.delete?
          @project.destroy
          redirect_to projects_url
        end
      end
    

    Alex: The core team doesn’t use scaffolding. Personally, I think plugins are just fine for scaffold generators. There are already a few out there now. Personally, I’ve been using Dan’s CRUD Generator. Hopefully he’ll take some of my patches for it too.

  9. Alex on 22 Jul 18:18:

    Jose: I just knew someone would post a comment about Trestle in reference to a question about scaffold. It does NOT generate code in-line with current Rails trends like those talked about in DHH’s most recent talk.

    Rick: Thanks. I will go and play with Dan’s CRUD Generator. Out of interest, who is Dan?

    I could guess that the core team did not make use of scaffold however my concern was for new raisl users who start the learning process using scaffold. It would be great for them to start learning the “right” way of doing things.

  10. Alex on 22 Jul 18:20:

    Ignore my “who is Dan” question. I found references in google to “Dan Peterson”.

  11. Joe on 22 Jul 19:25:

    You know, this CRUD/REST stuff doesn’t get me that excited. Seems like it just offers a bit of simplification. But…

    I did have a further idea for simplification – as I’ve written the standard boilerplate code for a new controller and its views yet again, I wished there was a way to just create a generic controller and views where basically the only variables are the model and titles and fields in the view templates. Perhaps there’s an easy way? Seems right up the CRUD’ers alley. ;)

  12. rick on 22 Jul 19:44:

    Alex: You don’t know Dan Awesomeson?

  13. DGM on 25 Jul 15:39:

    Joe: Something like the scaffold generator?

  14. Joe on 25 Jul 21:05:

    More so – single controller and single view directory instead of multiple models and controllers. Then you have vars for whatever model, fetched objects, etc.

  15. Jamie Hill on 26 Jul 00:54:

    The Javascript-only deletes have been bugging me so I have come up with a method to make it a non-issue and generally make Simply RESTful feel whole.

    I’d like to hear peoples comments on my approach http://www.thelucid.com/articles/2006/07/26/simply-restful-the-missing-action

  16. Bosko on 26 Jul 04:47:

    How do you folks typically deal with sticking to strictly CRUD in an application with a rather complex entity and associated update views to edit said entity, when said view is more involved than a single atomic “update”?

    For example, say you have some Foo (with associated controller FoosController), and Foo has_many Bar and has_many Baz. Suppose that your application allows for editing of “Foo” itself, but also allows you to associate existing Bar and Baz entities in a piece-by-piece fashion (think list of existing Bar entities with checkboxes allowing you to check on ones you wanted to associate with the Foo you’re editing).

    The obvious approach here is to just add POST-gated associate_bar, deassociate_bar, associate_baz, deassociate_baz actions to FoosController itself. But this is not CRUD-friendly.

    Yet another approach is to come up with a crazy way of passing the association “state” to the update action inside FoosController. Yet another, arguably more CRUDy, is to invent a first class entity, say, FooBarAssociationState, and then CRUD/update that. I would argue, though, that in this particular case the first (i.e., “obvious”) approach is most intuitive. Do you agree?

  17. Joe on 26 Jul 06:22:

    Bosko – Looks like you need to watch DHH’s keynote, specifically where he talks about memberships.

  18. Bosko on 26 Jul 13:24:

    To add to my comment (comment#16), before someone else suggests this: in the scenario I describe, why is an update on BarsController not appropriate for associating the Bar entity with the Foo entity (as opposed to calling FoosController’s associate_bar, or adopting one of the two other approaches I mention)?

    The reason is that BarsController’s update action is already called from a different context and is designed to render a completely unrelated view.

    Still, the suggestion to just call update inside BarsController can still be made, as long as the update is made aware of different calling contexts, and for this the following plugin, provided by Bruce Williams, can be used:

    http://codefluency.com/articles/2006/07/01/rails-views-getting-in-context/

    (it’s called “context”)

    This actually begs the question: with a CRUD-centric approach, we’re bound to end up [re]using actions from different contexts more than before (whereas before the typical approach was to just add another action to one of our controllers); therefore, we will need something like the “context” plugin more and more often; shouldn’t the “context” plugin (or similar) be made part of Rails?

  19. Anthony Eden on 26 Jul 15:41:

    I’ve posted two articles on using the SimplyRESTful plugin to create RESTful rails apps.

    http://anthonyeden.com/articles/2006/07/25/playing-with-simply-restful-plugin http://anthonyeden.com/articles/2006/07/26/getting-restful-on-rails-part-2

    Hopefully these will be helpful to people who are trying to get their head around RESTful Rails.

  20. Chris on 26 Jul 20:46:

    I’d love to see DHH’s response to Bosko.

    What he has said has been playing on my mind also.

    “The reason is that BarsController’s update action is already called from a different context and is designed to render a completely unrelated view.” How do you get around that? Surely having a context plugin would make it ugly?

  21. Dell on 26 Jul 21:29:

    CRUD-ifying everything basically amounts to making your controllers shallower and making more of them. From what I’ve seen, it works well for relatively simple applications, but erodes as the applications become more complex and need to deal with increasing complexity in associating data.

    Ultimately, you need to make a decision to determine if lots of simple controllers is more effective than fewer more complicated ones.

  22. Chris on 27 Jul 08:33:

    “CRUD-ifying everything basically amounts to making your controllers shallower and making more of them”

    ..so our controllers end up matching our models 1:1?

  23. Bosko on 27 Jul 13:52:

    Re: Comment #17

    “Bosko – Looks like you need to watch DHH’s keynote, specifically where he talks about memberships.”

    I’ve seen the keynote and read the slides as soon as they were available. The scenario I describe is different from the membership part of the talk you refer to. This is not a many-many relationship where you can create a middle entity identifiable by an id. This is a one-to-many relationship (Foo has_many :bars), where you’d like to be able to add one or more Bars to Foo, one at a time, from an interface separate to Foo’s and Bar’s “edit”.

    I describe the issue in a more general sense (i.e., the tight coupling between controller action and view) in my blog:

    http://www.crowdedweb.com/articles/2006/07/26/on-crudifying-rails-applications-with-complex-view-structures

    I maintain that we’ll probably need something like the context plugin more and more often.

  24. jakehow on 27 Jul 17:14:

    Re: Joe

    I am using a generic CRUDController and all of my other controllers inherit from it.

    I applied this to one app and cut about 75% of the controller code out of it!

    Anywhere I need custom actions I just override them in that specific controller.

    Check out my full write-up for the code:

    http://geekonomics.blogspot.com/2006/07/crud-and-shared-controllers.html

  25. Jamie on 30 Jul 13:53:

    Can anyone help out with this? http://thelucid.com/articles/2006/07/30/problem-link_to_remote-with-method-delete-in-safari

  26. Glenn on 15 Aug 03:21:

    If you are interested in creating CRUD/simply_restful/Edge Rails compliant model/views/controllers/tests you might want to check out my update to Dan Peterson’s crud_generator.

    http://code.google.com/p/crudgenerator2/

    Here is a bit of info about it:

    CRUD Generator 2 : Generates a CRUD and simply_restful compliant model, controller, and simple unstyled but fully functional views. A full set of unit and functional tests are also generated which run all CRUD actions on the model and controller.

    Plugin is based on the original ‘crud_generator’ plugin written by Dan Peterson ( http://svn.dpiddy.net/plugins/crud_generator/ )

    Enhancements to ‘crud_generator2’ include - Fully functional CRUD/simply_restful/Edge Rails compliant controller/views - Designed to be a base upon which you can build your code. Not a scaffold to be ripped out and replaced later. - Updated controller supports display of flash messages and extra error handling. - All generated code is instantly usable out of the box. - Full set of unit and functional tests that you can build upon which will run all CRUD actions on the model and controllers generated.

    Enjoy! And please let me know if you have feedback or file a bug on the code.google.com page if you find any issues. This is my first published enhancement to a plugin so please go easy on me. :-)