Wednesday, April 23, 2008

Kick Starting A Rails Application

I recently got started on a new rails pet project. When working on your own project you wear all the hats, call all the shots, and do all the work - for better or for worse. No matter where it falls in the end, you learn so much in the process.

In that spirit, my goal is to treat this project like my own personal startup weekend session, with a focus on momentum and forward motion.

Working Toward A Frankenstein - Mocking Up My Models & UI

To move my idea from vapor-ware to Frankenstein in good time I start right in by sketching out a quick and dirty mockup of the Models needed to get the gears turning. After a little pencil chewing and eraser dust I've got something rough to work with. My next step, since I'm historically a UI developer, is to throw together a basic clickable wireframe to compliment my rough Model sketch.

I am truly just going for a Frankenstein at this point - exposed stitches and bolts and all. Lets get this beast lurching forward.

For the mockup I use the haml driven staticmatic (highly recommend for mockups) and all goes quickly. Nice! It's like hoisting a lightning rod...and the beast begins to stir.

Refactoring My Frankenstein - Employing Imaginary Business Logic

I've skipped mocking up the obvious form pages but I do make a point to create Views for the areas of functionality that are not standard, or at least not plainly familiar to me. For instance I have an image Model that I want the user to be able to crop and size. Since I've never built an online image cropper I decided to mock it up and see what kind of question marks I encounter in the process.

With my plain-jane-greyscale UI in place I click through my pages to imagine the business logic I'll need to make my Views and Models work together. During the click-through process I continue to redefine my Models with needed attributes id's of other related Models. Sticky parts rear their ugly head but since I have no business logic written yet, changing the Models & Views is simple. Common patterns come to light and two Models get abstracted into one, etc. The more back and forth I do, the more simple and clearly defined my solution becomes.

Likewise, my Views mature as I whittle them down to exactly what needs to be displayed and gathered to support both my imagined business logic as well as my imagined end user expectations.

At this stage, my Model mockups now take on a less sketchy format so I can easily converted them to a script/generate rspec_scaffold later:

  Pirate
    name:string
    is_one_eyed:bool
  
  PirateType
    name:string
  
  Jewel
    name:string
    description:text
    is_cursed:bool
  
  JewelsPirates
    pirate_id:integer
    jewel_id:integer

Teasing Out Enums and States

As I continue to refine my Models, there are two types I keep a special look out for. First I take note of all the enumerations I run into. These are the Models that usually show up in drop down lists, that only have a name and id, and that lend themselves to a naming convention of xyz_type.

I like to manage all my enumerations from one place using Trevor Squire's acts as enumerated plugin. With that in mind, when I find an enumeration I write down all the values it needs to contain.

The second Model type I look for is any Model that uses (or could use) state. For example, if I notice a Model has many boolean attributes (is_reviewed, is_archived) that could be better represented by a single string attribute called "state" (state = pending_review, reviewed, archived) then I'll swap in the single "state" attribute for the booleans and write down the states the Model can be in. Later I will apply Scot Barron's excellent acts_as_state_machine plugin to manage those states.

Initial Migrations

So I've teased out my Enumerations, States and a number of additional Models. Excellent. Now my Model mockups look something like this.

  Pirate
    name:string
    is_one_eyed:boolean
  
  PirateType
    name:string
  # PIRATETYPES = good, bad, undead
  
  Jewel
    name:string 
    description:text
    state:string 
  # JEWEL STATES = un_stolen, stolen, cursed, buried
  
  JewelsPirates
    pirate_id:integer
    jewel_id:integer

Before I start generating database migrations, I write two initializer migrations by hand.

  001_create_initial_enums
  002_create_initial_joins

The "enums" migration creates all my enum Model tables and writes the values I've noted to the database. The "joins" migration creates all my join tables, sans any id attribute.

Generating The Beginnings Of My Rails App

Now I'm ready to dive forward and turn my mockups into a living Rails App. With the shorthand I've used for my Model mockups I can easily create a series of script/generate calls...

  
    script/generate rspec_scaffold Pirate name:string is_one_eyed:boolean
    script/generate rspec_scaffold Jewel name:string description:text state:string
  

Ahh, generators...it's like having a hindu god in your pocket. But the form Views getting generated are not even close to something I'll use for my actual forms. I know, I know - the scaffold Views are repudiated for only being there to impress clients and the uninitiated, but because I have a pretty solid mockup and a clear idea of the hamlized html I'll be using for my forms, I may as well generate something I can work with. So before I start generating, I'm going to modify the default RubyOnRails scaffold templates in two ways:

  1. Make them generate .haml instead of .erb
  2. Make them layout my forms using an html <dl> list.

Check back soon if your interested. I'll continue documenting my process.