Wizard-ify Your Rails Controllers with Wicked28 Feb 2012
If 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.
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_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) %>
: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