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:
- Make them generate .haml instead of .erb
- Make them layout my forms using an html <dl> list.
Check back soon if your interested. I'll continue documenting my process.