On 10/21/07, Don Brown <[EMAIL PROTECTED]> wrote: > On 10/22/07, Martin Cooper <[EMAIL PROTECTED]> wrote: > > On 10/21/07, Don Brown <[EMAIL PROTECTED]> wrote: > > > > > > [...] > > > Which reminds me, this plugin is targeted towards HTML-based web apps > > > that want to expose their information in machine-readable ways (XML > > > and JSON), > > > > > > I presume you mean DOM-based apps; some of us don't use HTML any more. ;-) > > HTML = text format that you want to manually generate using jsp, > velocity, or freemarker
What happens when different clients want different representations? This is one of the places where Rails does a pretty nice job, using the respond_to method to let the controller choose which representation to produce, based on some combination of (a) what did the client ask for (via a content type header or a format suffix on the URL), and (b) what representations are available in the app. On the other hand, when combined with a plugin like make_resourceful[1] , a Rails controller supporting simple things like CRUD operations, with multiple representations, can be incredibly concise. (Rails lets you define multiple related actions in a single controller, so this is all in one class.) The domain of interest in this example is the posts on a blog system. class PostController << ApplicationController make_resourceful do actions :all publish :xml, :json, :yaml, attributes => [ :id, :title, :body, { :comments => [ :id, :author, :body ] } ] end; end; That's the whole controller (without make_resourceful it'd be about 100 lines of code) ... but it allows you to select any of XML, JSON, or YAML (a text format for hierarchical data commonly used in Rails config files, but not restricted to Ruby). Adding HTML as an output option is one more entry on the publish line (plus writing the corresponding views in your favorite template language). And this controller automatically maps *all* of the following (think of them as context relative) URLs to the specified controller actions, which perform the specified tasks: # POST /posts -- create a new post and return status 201 plus a Location header with the # appropriate URL to access the newly created post instance def create ... end # DELETE /posts/xxx -- delete the post row with key xxx def delete ... end # GET /posts/xxx/edit -- create an HTML entry form for the post with key xxx; the # resulting PUT will call the update() action. Only used for HTML representation # (in earlier edge rails, this was ";edit" instead of "/edit") def edit ... end # GET /posts -- return an HTML table or a data structure in the requested format def index ... end # GET /posts/new -- create an HTML entry form for a new post instance; the resulting # POST will trigger the create() action. Only used for HTML representation # (prior to edge rails, this was ";new" instead of "/new") def new ... end # GET /posts/xxx -- return HTML details or data representation of post with key xxx def show ... end # PUT /posts/xxx -- update the post row with key xxx from parameters in this request def update ... end Several notes are important to understand if you're not familiar with Rails REST support: * With make_resourceful, you don't actually write the above methods at all -- they are generated at runtime. The source code above is what the developer writes (plus any HTML views). Can you write a complete CRUD web service with Struts2, supporting multiple representations, in nine lines of code (minus HTML views)? * Without make_resourceful, there's a Rails generator that will actually generate all this code -- but you end up with ~100 lines per controller, with a pattern that is repeated in every controller with only a few details being different. That's not optimal, which is why a plugin like this got created. It won't be part of Rails 2.0 core (the Rails core developers have a similar allergy to a bloated core that the Struts2 developers have), but it's readily available. * You can add additional actions that are related, and this is not uncommon for special purpose requests that don't strictly conform to CRUD operations but share the same requirements for data acquisition. * For index and show, the client requests the representation they want, either via an accept header specifying the content type, or adding ".xml", ".json", or ".yml" to the URL. * A client of a RESTful web service will never need the "edit" or "new" actions ... it will simply interact directly with "update" or "create" appropriately. * The Rails helper methods for form based HTML input have workarounds for the fact that browsers do not actually know how to do a PUT or DELETE (except in an Ajax callback). This kind of thing could easily be implemented in the server side view representation, but it'll be specific to whatever templating technology you like. * Note that the controller's create/delete/update methods do not care at all whether the client is a browser or a RESTful web service client. The same is basically true for index and show -- the "publish" capability maps to "respond_to" methods in the generated actions. This is one of the core values of REST as a paradigm -- URIs are "nouns" that describe resources that can be retrieved in multiple formats, rather than treating them as "verbs" that invoke a particular action method, which is typical of most action oriented frameworks. Why the long winded diatribe? Because while I'd *love* to see life made simpler for Java web developers, and a lot of the things happening in Struts2 are going that way -- it won't be me doing it. I've gone over to the dark side :-) and much prefer to develop in Rails -- for the conciseness mentioned above, but also because I don't ever have to do a "build" or "deploy" step during my development cycle any more. But you guys and gals need to be reminded that *this* is the kind of thing you are competing against if you expect to attract Rails developers ... or to avoid even more "previously Java web developer" defectors like me :-). Craig [1] http://nex-3.xom/posts/7 (but google for the make_resourceful google group for more current info) --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]