Welcome to Giant Robots Smashing Into Other Giant Robots — a weblog about development, business, design and technology — written by thoughtbot.
it's the wiz and nooobody beats it
We all love wizards.
Here’s a common pattern I use for wizards.
It sucks and the code’s ugly but it works.
We’re going to create a 3-step wizard for creating aUser.
1 2 3 4 5 6 |
class User < ActiveRecord::Base validates_presence_of :email, :password validates_confirmation_of :password end |
schema:
users (email, password, name, bio, created)
So in the first step we’ll collect all the required User information: email and password.
UsersController.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class UsersController < ApplicationController def new @user = User.new end def create @user = User.new params[:user] if @user.save redirect_to edit_user_path(:id => @user, :step => 'b') else render :action => :new end end end |
But instead of redirecting to #show after a POST to #create we redirect to #edit. I also pass in the fact that I’m now on step ‘b’ (the second step) of this wizard.
Here’s #edit:1 2 3 4 |
def edit @step = params[:step] @user = User.find params[:id] end |
<%= render :partial => @step, :locals => { :user => @user } %>
That’s going to look for a partial named ‘_b.rhtml’ when going to the second step in the wizard. This way we avoid conditional logic in this view.
app/views/users/_b.rhtml
<h2>Step Two</h2>
<%= error_messages_for :user %>
<% form_for :user,
:url => user_path(:id => @user,
:step => @step),
:html => { :method => :put } do |form| -%>
<p>
<label for="user_name">Name</label>
<%= form.text_field :name %>
</p>
<p>
<%= submit_tag 'Submit' %>
</p>
<% end -%>
Here we collect some optional User information in a form that PUTs to #update, passing along the current step in the wizard.
Here’s #update:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
def update @step = params[:step] @user = User.find params[:id] if last_step? if @user.update_attributes params[:user].merge(:created => true) redirect_to user_path(@user) else render :action => :edit end else if @user.update_attributes params[:user] redirect_to edit_user_path(:id => @user, :step => @step.succ) else render :action => :edit end end end private def last_step? params[:step] == 'c' end |
Now #update says “if its the last step update it and redirect to #show, else update it and redirect back to edit and increase the step by 1”.
So you say “What if I don’t want aUser to be shown on my site that hasn’t fully completed the wizard?” That is the purpose of the created boolean in the users table. The following block of code from #update sets created to true if its the last step in the wizard:
1 2 3 4 5 6 7 |
if last_step? if @user.update_attributes params[:user].merge(:created => true) redirect_to user_path(@user) else render :action => :edit end else |
User queries will have to include the boolean:
1 2 |
User.find :all, :conditions => 'created = true' |
Here’s the last step in the wizard:
app/views/users/_c.rhtml
<h2>Step 3 (last step)</h2>
<%= error_messages_for :user %>
<% form_for :user,
:url => user_path(:id => @user,
:step => @step),
:html => { :method => :put } do |form| -%>
<p>
<label for="user_bio">Bio</label>
<%= form.text_area :bio %>
</p>
<p>
<%= submit_tag 'Submit' %>
</p>
<% end -%>
It collects more optional User in a form that PUTs to #update just like step ‘b’ (2) did.
You might be wondering why I chose letters instead of numbers for my wizard stages. Rails complains if you try to have a partial named 1.rhtml or 2.rhtml for some reason.
About this entry
You're reading an entry on GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS, the company weblog of thoughtbot, inc.
- Author:
- Jared Carroll
- Published:
- August 6th 03:17 PM
- Updated:
- September 30th 09:57 AM
- Sections:
- Development
thoughtbot is hiring
We are hiring web developers and web designers in both Boston and New York, NY.
What are we up to?
We built Shoulda, an eclectic set of additions to Test::Unit; Paperclip to manage uploaded files without hassle; Jester, a REST/ActiveResource client library written in Javascript, and Squirrel, an enhancement for ActiveRecord's find syntax; — amongst some other projects.

Chad (President) and Jon (CTO) co-authored a technical book titled Pro Active Record: Databases with Ruby and Rails, which explores the ins and outs of the ActiveRecord ruby library. You can buy it today at Amazon.com.
About thoughtbot, inc.
We are a small web application development consulting business, with offices in Boston, MA and New York, NY. If you're looking to find a team for your next web development project or your new web application — get in touch.
8 comments
Jump to comment form