Daniel Fone

Ruby/Rails Engineer

Prototyping Web Applications in Rails 4

October 2013

From a presentation I gave at the at the @chchruby meetup on Thursday, 17th Oct, 2013. It was aimed at folks who were unfamiliar with Rails and bear in mind, this was a 15 minute lightning talk. I’ve linked to specific commits below, but you can view the finished repo on GitHub.

Background

Tonight I want share a development style that Rails is very good at, but one that doesn’t get a lot of love in the community.

When I first discovered Ruby on Rails in my days of writing spaghetti PHP, I was amazed with how fast you could write basic web applications. However, as we become better developers and learn how to do things properly, we spend more and more time thinking about testing and decoupling and encapsulation and what to name our variables. In other words: no matter how good your tools are, doing things well is hard and takes time.

So tonight I want to go back to the good old days of not knowing any better, and show you how little effort it takes to throw together a prototype web application using generators and scaffolds and lots of bad hacks.

And although I’d never want to see this kind of code in a production application, I’m increasingly using this technique professionally to develop very fast, cost-effective prototypes for demonstration purposes. After all, it’s much easier discussing the merit of an idea when everyone’s looking at it on the screen, rather than everyone imagining something slightly different in their head.

The Idea

form.png

We’re going to prototype a simple reimbursement system. We want to manage expense claims which consist of:

  • A simple reference
  • A “payee” to reimburse — usually an employee
  • A series of lines detailing the expenses, amount, expense codes etc
  • Any comments

The Application Skeleton

$ rails new reimburser-wizard -T -m ~/Development/rails-templates/default-prototype.rb
$ cd reimburser-wizard

The rails new command is pretty self-explanatory. It creates a new Rails application in a directory of the same name. The only two options I’m specifying are:

-T, [--skip-test-unit]         # Skip Test::Unit files
-m, [--template=TEMPLATE]      # Path to some application template (can be a filesystem path or URL)

You can check out my prototyping template on Github, but here’s the basic rundown:

  • Disable test generators
  • Disable strong parameter protection
  • Include the twittter-bootstrap-rails gem and run the generators
  • Remove production and test configuration
  • Add a partial for rendering error messages

Next we’ll copy in a simple layout (app/views/layouts/application.html.erb) and some bootstrap overrides (app/assets/stylesheets/bootstrap_and_overrides.css).

The Scaffolding

Here’s where we really start building our prototype. Most of the work is done by Rails’ scaffold generators. In this simple prototype, we’ll model our domain with just two resources, a Reimbursement and an Employee.

$ rails generate scaffold Reimbursement reference:string payee:references comments:text data:text
$ rails generate scaffold Employee name:string
$ rake db:migrate

Our Reimbursement model is very simple: we’ve got three regular attributes (reference, payee and comments), and a data field we’ll use as a meta-attribute for serializing whatever other attributes we need later. For the purposes of the demo, our Employee simply has a name attribute. The db:migrate rake task applies the generated migrations to the sqlite database. As you can see in the commit, the scaffold generators create a lot of code for us.

Now we’ll run some bootstrap helpers to replace the default rails views with bootstrap themed views.

$ rm app/assets/stylesheets/scaffolds.css.scss
$ rails generate bootstrap:themed Reimbursements
$ rails generate bootstrap:themed Employees

That’s it for our scaffolding! The basic foundation of the prototype is in place.

Demo

Now’s an excellent time to play with the application. Let’s start the web server:

$ rails server

Because we haven’t set up our root route, you’ll need to navigate to http://localhost:3000/reimbursements .

We’ve barely written any code but we’ve got a fully functional web application. Admittedly it’s not very useful yet, but it’s a powerful base to add functionality on to our prototype. Be sure to create a few Employees for our next step.

Making It Work

From this point, we’ll write code rather than use generators. I’ll link to diffs on GitHub. If you’re new to Ruby on Rails, check out any unfamiliar methods in the Ruby on Rails API docs.

Change payee field to a select box diff

Naturally, we want our users to be able to select employees to reimburse. We’ll use Rails’ collection_select helper for this. Check out the comments on the commit for more info.

Change comment field to be multiline diff

A simple switch from a text_field to a text_area. Confession: after 5 years of professional Rails development, I didn’t know about simple_format until I was preparing this talk.

Add LineItem table to Reimbursement view diff

The line items are the most complex part of this simple app. We’ll start by adding a table on the Reimbursement view to present the line items. Since they don’t exist yet, we’ll “stub” the method on the Reimbursement model to return an empty array.

Add LineItem fields to Reimbursement form diff

To create the line item fields on the reimbursement form, we’ll use the magic of fields_for. This is a tremendously flexible helper, to the point that I’ve never quite understood how it works despite using it in almost every application I write.

We’ll also setup our LineItem model, with a few attributes and some validations.

Allow LineItems to be persisted diff

Finally, we’ll inform our Reimbursement model how to persist the line items. We’re going to serialise the attributes as JSON in our data meta-attibute. We need to do a simple transformation on the form data, but the rest is fairly straight forward.

Finished!

In just 15 minutes, we’ve built a very simple prototype that contains some non-trivial functionality.

form.png

comments powered by Disqus