Integrating Design Drafts Into Your Rails App

F59E60E8-6EDF-44CD-8B30-989643692CBC.jpgThis one might be a little half-baked, I'd expect the implementation and features to change a bit over time. (Image from ArtCulture)

Nearly every project we work on at Pathfinder involves coordination between the developers and the designers. Over time, we've found that the more the design and development work can be integrated, the better everybody's work is. To that end, we like to have our designers work in CSS and HTML where possible, and to keep their designs in the same Subversion repository as the rest of the code. Being able to run a development environment helps the designers see their changes, and gives both sides immediate feedback on changes.

Here's a Rails hack that takes this one step further, allowing developers and designers to go back and forth between the current developer version and the design.

This is actually pretty simple, it turns out. I started with a new controller: WireframesController. The assumption here is that the view directory for this controller will mimic the view structure of the rest of the application. In other words, the design for the user profile page would be stored in app/views/wireframes/users/show.erb and the edit page would be in app/views/wireframes/users/edit.erb.

The first step is to enable the wireframes controller to respond to a URL that has both a controller and action in the query string, such as: wireframes?c=users&a=show

class WireframesController < ApplicationController

  def index
    template_name = "wireframes/#{params[:c]}/#{params[:a]}"
    render :template => template_name
  end

end

That's pretty simple, it just builds up the location of the template file from the controller and action, and renders the template. Big deal.

What I'd really like it to do is respond to any url that ends in .wire by showing the wire frame. This could be done using respond_to, but then you'd have to explicitly note which actions have wireframes, and that's more of a pain then I want this to be.

Instead, I put the following in the ApplicationController:

  before_filter :redirect_wireframe
  def redirect_wireframe
    if request.request_uri.ends_with? ".wire" &&
          params[:controller] != "wireframes" &&
          RAILS_ENV == "development"
      redirect_to :controller => "wireframes", :action => "index",
          :c => params[:controller], :a => params[:action]
    end
  end

The before_filter means this code will be called on any request. If it's development mode, and the request ends with .wire, and it isn't already a request for a wireframe then the code redirects to the Wireframe controller and displays the appropriate design. So, users/show/2.wire, will redirect to the users/show wireframe.

UPDATE: A commenter pointed out that this can be done as a Rails route, so if the before_filter isn't working, or if you prefer the route version, place the following in the routes.rb file. This must be before any RESTful resource route declarations.

  map.connect ':c/:a.wire',
      :controller => "wireframes", :action => "index"
  map.connect ':c/:a/:id.wire',
      :controller => "wireframes", :action => "index"

These routes will set the c and a parameters as the above index method expects. It needs to be before any RESTful route because otherwise the RESTful route will try to take the action, which probably won't do what you want.

Also, at the bottom of the application layout file, I put a link to the associated wireframe:

<% if RAILS_ENV == "development" %>
  <%= link_to "Wireframe for this action",
	 :controller => "wireframes",
      :action => "index", :c => params[:controller],
      :a => params[:action] %>
<% end %>

The design files are ERb, which is fine if the designer is just comfortable with HTML, but also allows some dynamic content if the designer wants (the Ruby Faker gem works great with this).

So, this isn't perfect, or very battle-tested, yet. It'd be nice if it could detect if a design file existed, or detect if you are in a design view and link back to the development view. Once I get a better sense of how we're going to use this, I'll package it as a plugin to make this a bit cleaner.

Related posts:

  1. I’m Cranky Because I’m Not Getting Enough REST
  2. DRYing up Rails Controllers: Polymorphic and Super Controllers
  3. Roles Testing For Security
  4. Catching Ajax Errors with Prototype and Rails
  5. Bridging the Gap Between Rails Developers and HTML Designers

Topics:

Comments: 8 so far

  1. I’m probably missing something but couldn’t you just use a route namespace to accomplish the same thing? Maybe that doesn’t give you enough control?

    Comment by Andy Amiri, Friday, August 1, 2008 @ 4:06 pm

  2. Similar thoughts?

    http://locomotivation.com/2008/06/28/redesign-your-site-in-place-using-rails-custom-mime-types

    Comment by David Mathers, Saturday, August 2, 2008 @ 4:54 am

  3. 1. You probably could do most or all of this with a route, but I find that hard to debug when things go wrong.

    2. Yes, that’s very similar — I wanted to be able to manage my solution without having to touch any respond_to block in the code, but I like the way yours looks too.

    Noel

    Comment by Noel Rappin, Sunday, August 3, 2008 @ 11:53 am

  4. Thanks for the idea!
    The routes.rb solution didn’t work for me (nested controllers).

    You can even simplify it – render the template directly from the before_filter – it will halt the filter chain.

    And you can use params[:format] to extract the format.

    Comment by Ɓukasz Adamczak, Tuesday, August 12, 2008 @ 6:07 am

  5. [...] project that included integrated design drafts, named-scope based reporting, and aggressive markup in the helpers had it’s post-mortem this week. [...]

    Pingback by Pathfinder Development » A Look Back At Past Posts, Friday, October 3, 2008 @ 12:02 pm

  6. [...] Alice’s post about wireframes, and Noel’s post about how you can allow a designer to write the HTML within a rails [...]

    Pingback by Pathfinder Development » What makes a good requirement document for an agile project, Thursday, November 6, 2008 @ 2:22 pm

  7. I absolutely *love* this approach … however, if you’re running Rails version 2, wouldn’t this be a bit more elegant with a custom mime-type?

    First setup the mime-type:

    # config/initializers/mime_types.rb
    Mime::Type.register_alias “text/html”, :wire

    Then modify your controllers as needed

    # app/controllers/people_controller.rb
    # GET /people/1
    # GET /people/1.wire
    def show
    @person = Person.find params[:id]

    respond_to do |wants|
    wants.html # renders app/views/people/show.html.erb
    wants.wire # renders app/views/people/show.wire.erb
    end
    end

    You could even do any of the “wants” lines with a block if you require special logic. For instance, if you don’t want to mess with the database for your first few iterations:

    # app/controllers/people_controller.rb
    # GET /people/1
    # GET /people/1.wire
    def show
    respond_to do |wants|
    wants.html do
    @person = Person.find params[:id]
    end
    wants.wire # renders app/views/people/show.wire.erb
    end
    end

    Thoughts?

    Comment by Anthony Navarre, Saturday, December 13, 2008 @ 11:17 am

  8. Ah, apologies if my previous post just reiterates info that commenter #2 posted – the link in that comment currently goes to a 404 error. Even if my comment is redundant for the author, hopefully the code is useful to a few readers, given the 404.

    Comment by Anthony Navarre, Saturday, December 13, 2008 @ 11:23 am

Leave a comment

Powered by WP Hashcash

Launch: Pathfinder Newsletter

    Get a monthly update on best practices for delivering successful software.

    Subscribe via email


    Subscribe via RSS      RSS icon

Topics

Search

WordPress

Comments about this site: info@pathf.com