Open-source project
zombocom/wicked avatar
zombocom/wicked

Wicked: Step-by-Step Rails Controllers Without a Separate Workflow Engine

Use wicked to turn your controller into a wizard

2,850 stars189 forksRubyMIT

At a glance

What is it?
Wicked is an MIT-licensed Ruby gem that adds ordered, multi-step flows to Rails controllers. It fits onboarding and similar forms where each screen belongs to one controller, but its `:id` handling and save behavior require deliberate design.
Who is it for?
Adopt Wicked when the complete flow belongs naturally in one Rails controller and ordered rendering is enough to represent your process. Do not adopt it if you need durable resumable workflow state, independently managed transitions, or unrestricted record identifiers without route changes.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 32 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why Wicked Fits Rails Onboarding Controllers

Wicked addresses a specific Rails problem: presenting a process as a sequence of pages while retaining ordinary controller actions, views, routes, and forms. The README presents it as a way to build an after-signup wizard with password confirmation, profile confirmation, and friend discovery. That makes it relevant to developers implementing account setup, questionnaires, document collection, or similar interfaces whose stages can share one controller.

The library describes itself as a "RESTful(ish) state machine," but its public model is comparatively small. A controller declares a list of steps, Wicked derives the current stage from the request, and helpers generate URLs for the current, previous, next, or named step. Views remain in the normal Rails view hierarchy. This is a practical fit when each stage is mostly presentation around a resource the application is already editing.

Wicked is not presented as a general workflow engine. The documentation does not describe persisted wizard progress, background transitions, approval processes, scheduled steps, or recovery after arbitrary branches. Its state is request-oriented and expressed through controller step handling. Applications needing those capabilities would need to provide more application-level infrastructure than the README shows.

Wicked's Request Flow and Step Rendering

The central mechanism is the `steps` declaration. In the example, `steps :confirm_password, :confirm_profile, :find_friends` establishes the intended order for the `show` action. Wicked exposes the current stage through `step`, allowing the controller to prepare stage-specific data with a `case` statement before calling `render_wizard`.

Rendering follows Rails conventions. A step named `:confirm_password` in `AfterSignupController` maps to `/views/after_signup/confirm_password.html.erb`. The controller still owns the request, while the gem selects the template associated with the declared step. URL helpers connect views to stages: `next_wizard_path` advances, `previous_wizard_path` returns, `wizard_path(:find_friends)` targets a named stage, and the corresponding `*_url` helpers return fully qualified URLs.

A direct link such as `after_signup_path(:confirm_password)` enters the first documented step. The helper design makes it possible to build ordinary links and forms without creating a separate controller for every screen. However, the README does not document server-enforced restrictions against jumping directly to a later URL. If stages have ordering and authorization requirements, controller logic must not treat URL generation alone as sufficient access control.

Installing Wicked and Creating the First Controller

Wicked is installed as a Ruby gem in an existing Rails application. The README gives one Gemfile entry and one Bundler command. No separate initializer or generator is documented.

Add the dependency to `Gemfile`:

ruby
gem 'wicked'

Install the bundle:

bash
bundle install

The README says the application is ready after `bundle install`. The gem's public behavior then comes through the `Wicked::Wizard` module, routes, a controller, and views. The repository contains `wicked.gemspec`, so it is packaged as a Ruby gem, but the README does not provide a standalone command-line application or a documented `rails generate wicked` command.

The first Rails generator shown in the README creates the controller:

bash
rails g controller after_signup

This creates the normal Rails controller structure. The generator name and wording should be preserved because the rest of the example assumes `AfterSignupController` and the `after_signup` resource path. Wicked is then included in that controller rather than replacing the controller's Rails responsibilities.

Defining the After Signup Flow and Its Views

The README adds a resource route in `config/routes.rb`:

ruby
resources :after_signup

The controller can include the module and declare its steps:

ruby
class AfterSignupController < ApplicationController
  include Wicked::Wizard

  steps :confirm_password, :confirm_profile, :find_friends
end

The documentation also shows the older inheritance style:

ruby
class AfterSignupController < Wicked::WizardController

  steps :confirm_password, :confirm_profile, :find_friends
end

The included module is the more direct example because it keeps the application controller as the base class. The older inheritance form is documented as an alternative, so an application should choose one style rather than combining both without a specific reason.

A step must have a view whose name matches it. For the `:confirm_password` step, the README identifies `/views/after_signup/confirm_password.html.erb` as the rendered template. The view can link to the next stage with:

erb
<%= link_to 'skip', next_wizard_path %>

That link advances through the declared sequence. A named destination can be generated with:

erb
<%= link_to 'skip', wizard_path(:find_friends) %>

These helpers make the view concise, but the example does not say that `skip` is a conditional branch. It is simply the label of a link to the next wizard path. The controller still decides what data each step needs and whether a condition should call `skip_step`.

Handling Updates, Saves, and Callback Side Effects

Sequential navigation is only one half of the documented flow. Wicked also supports an `update` action for form submissions. The controller sets `@user`, performs an update for the matching step, and passes the object to `render_wizard`. According to the README, a successful save shows the next step, while a failed save re-renders the previous view.

The update form sends PUT to `wizard_path`:

erb
<%= form_for @user, url: wizard_path, method: :put do |f| %>
  <%= f.password_field :password %>
  <%= f.password_field :password_confirmation %>
  <%= f.submit "Change Password" %>
<% end %>

The form method is deliberate. The README warns that omitting it can produce a warning about a missing create action or a failure to find a POST route. The controller can then use strong parameters and `render_wizard`:

ruby
def update
  @user = current_user
  case step
  when :confirm_password
    @user.update(user_params)
  end
  render_wizard @user
end

There is a sharp edge here: `render_wizard` attempts to save a passed object. Since the example has already called `@user.update(user_params)`, the object can be saved twice, causing callbacks to run twice. The README suggests `assign_attributes` as a possible alternative when duplicate persistence is undesirable. That is not automatically equivalent to normal update behavior in every model, so validation, authentication, and callback expectations should be checked before choosing the pattern.

Strong parameters remain application code. The example permits `:email` and `:current_password`, then notes that more fields may be added. Wicked controls step presentation but does not replace Rails parameter authorization or model validation.

Skipping Steps and Avoiding the `:id` Collision

Wicked provides `skip_step` for conditional stages. In the documented friend-discovery example, the controller checks `current_user.has_facebook_access_token?`. If the token is absent, it calls `skip_step` instead of loading friends. This lets the flow respond to an account capability without moving the entire wizard into a different controller.

The example shows where responsibility remains with the application. The controller decides whether a condition warrants skipping, while Wicked handles movement within the declared flow. The documentation does not claim that skipped-step validation is automatic. If a skipped profile or preference step contains required model attributes, the application must assign defaults or otherwise ensure the final object remains valid.

The `:id` parameter imposes a route constraint. Wicked uses `:id` to control the flow, so an application that needs `:id` for its own object should use a nested route and a prefixed identifier such as `:product_id`. This avoids using one parameter for both the wizard step and the resource identifier. The README does not document a configuration key that changes this parameter convention, so nested routes are the documented solution.

A direct URL may still be constructed by a client or bookmark. The README documents path helpers and sequential views, but it does not state how Wicked handles unauthorized jumps between steps. Applications with permissions, prerequisites, or non-linear requirements should enforce those rules in controller and model logic rather than relying only on the link sequence.

Maintenance, Releases, and MIT Upgrade Costs

The repository is not archived, and its last push was on 2026-09-21. The README does not state supported Rails or Ruby version ranges, so compatibility cannot be inferred from the project description alone. The repository includes `wicked.gemspec`, `gemfiles/`, and `Appraisals`, which indicate dependency configuration is part of the project, but the documentation does not give a current compatibility matrix.

There are no GitHub releases, so there is no release entry documenting a migration between named versions. An adopter should inspect the dependency versions resolved by Bundler and review `CHANGELOG.md` before selecting an upgrade. Because `bundle install` can update resolved dependencies according to the application lockfile, the resulting `Gemfile.lock` changes should be reviewed rather than assuming the gem installation is isolated.

The project uses the MIT license. That is a permissive license, generally allowing modification and redistribution subject to its notice and conditions, but this review does not provide legal advice. Upgrade cost appears concentrated in controller tests: changes to rendering, navigation, strong parameters, form verbs, and save behavior can affect several actions. The repository includes `test/` and a `Rakefile`, but the README does not specify a project-specific test command, so no such command is presented here.

The Rails-Native Alternative to Separate Wizard Controllers

The practical alternative is to implement the same flow without Wicked: create a controller action per screen, define explicit routes, and store progress in the session, database, or resource itself. That approach requires more routing and transition code, but it leaves URL parameters, authorization, persistence, branching, and form verbs under direct application control.

Wicked takes a narrower approach. It keeps a declared sequence inside one controller and generates step-aware paths. That reduces controller duplication for a straightforward ordered flow. The trade-off is acceptance of Wicked's conventions, especially the `:id` parameter and the behavior of `render_wizard`. A separately managed workflow engine would be more appropriate if the process has durable state, non-linear branches, scheduled work, or actors who resume work independently, although the README does not evaluate any particular engine.

For a short onboarding sequence, the library's approach aligns with Rails. For a regulated approval process or long-lived application, the absence of README documentation for persisted transition history is a material adoption boundary.

Editorial conclusion

Adopt Wicked when the complete flow belongs naturally in one Rails controller and ordered rendering is enough to represent your process. Do not adopt it if you need durable resumable workflow state, independently managed transitions, or unrestricted record identifiers without route changes. Verify first that every update form sends PUT to `wizard_path`, every skipped step leaves the required data valid, and your callbacks tolerate `render_wizard` saving the object a second time.

Frequently asked questions

How do I install Wicked in a Rails application?

Add `gem 'wicked'` to the Gemfile and run `bundle install`. The README does not specify a version constraint or a separate post-installation command.

How does Wicked choose which step view to render?

Wicked renders a view named after the current step. For `:confirm_password` in `AfterSignupController`, the README maps that step to `/views/after_signup/confirm_password.html.erb`.

Why should a Wicked form use PUT?

The documented update flow submits to the existing wizard URL and expects the controller's `update` action. Without PUT, the README says you may encounter a warning about a missing create action or a failure to find a POST route.

Official sources

  1. Issues
  2. License: MIT
  3. Project website
  4. README
  5. zombocom/wicked on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/zombocom-wicked.svg)](https://hysenlabs.com/projects/zombocom-wicked)

Was this useful?