Wizard-ify Your Rails Controllers with Wicked
28 Feb 2012If insanity is doing the same thing and expecting different results, I was going crazy writing step-by-step wizards. I was never happy with the end result, they did what I wanted, but were messy and had too many moving parts. I wanted a simple & re-usable way to create restful-ish controllers. Thats when I decided to rip out all that scary controller logic and bake it into in a Gem I call Wicked.
Wizards can be used for a number of things, they appear frequently after signing up for a service. They typically ask for additional information, or they give the user a tour of a service. Before I started working for Heroku, I helped out a bit on the peer learning site, Hourschool. Since students are most interested in the courses that are close to them, Hourschool asks users for their zip code. When a student signs up via Facebook, the zipcode isn’t directly exposed via the API. So to get that extra input, we decided to add an after register wizard to ask for this information and more. If you want to add similar functionality to your Rails site, you can watch the screencast, download an example, browse the source, or check out the getting started guide below.
Get Started
First install the gem, then inherit from Wicked::WizardController
and you can specify a set of steps. Here we have a controller called AfterRegisterController
with existing routes.
class AfterRegisterController < Wicked::WizardController
steps :add_zip, :add_twitter, :add_friends
# ...
Create a Show and Update action, calling render_wizard
at the end of each which allows us to show the appropriate view for the step.
class AfterRegisterController < Wicked::WizardController
steps :add_zip, :add_twitter, :add_friends
def show
@user = current_user
render_wizard
end
def update
@user = current_user
render_wizard
end
end
By default the wizard will render a view with the same name as the step. So you need to create view files for each step, in this case views/after_register/add_zip.html.erb
& views/after_register/add_twitter.html.erb
. In those views we can use wizard helpers to create links to the next step.
<%= link_to 'skip', next_wizard_path %>
Or you can manually specify which wizard action you want to link to by using the wizard_path helper.
<%= link_to 'skip', wizard_path(:find_friends) %>
Our :add_zip
action can have a form that uses the wizard path and calls the update action. To get to this update action, you simply need to submit a form that PUT’s (or PATCH’s) to the same url:
<p>Please Fill in your zip code!</p>
<%= form_for(@user, :url => wizard_path, :method => :put) do |f| %>
<%= f.text_field :zip, :placeholder => "zip code" %>
<%= f.submit 'Next', :class => 'btn btn-primary' %>
<% end %>
<%= link_to 'skip', next_wizard_path %>
We then need to make sure our Signup wizard’s update action updates the current_user’s attributes. After modifying the user we can pass it into render_wizard
, which will show the next step if the object saves or re-render the previous view if the user has validation errors.
def update
@user = current_user
@user.update_attributes(params[:user])
render_wizard @user
end
If you want to skip showing a step under certain conditions you can do it by using the step
method which returns a symbol of the current step you’re on, and skip_step
if you don’t want to render the current step. So if we wanted to skip asking our users to authenticate with twitter if they skipped the add_zip step, we can do it like this:
def show
@user = current_user
case step
when :add_twitter
skip_step if @user.zip.blank?
end
render_wizard
end
Redirect the user here after they are created in your system, and you have a fully baked after registration wizard. Thats all there is to it, just create a new view file every time you add a new step and you’re good to go. Please give this a try and message me at @schneems if you find any other really killer ‘wizard’ applications