Monday, 18 October 2010

Customizing Devise to a pseudo multi-stage Signup

The Requirements!
On our new site we want to have a kind of multi-stage signup. The first page the user chooses a type of subscription, the second page they create their account, with address information, and the third page they enter their billing info. If they don't do the third stage that's ok as we will warn them that their account is incomplete (and they won't get any services until they give us the billing info)

We are doing this new site in Rails3 and I thought I'd use Devise as the authentication engine. In our old site we use Restful Authentication, which has been awesome. But Devise seems to be the thing all the kids are into today!

So here's how we did it:

Step 1:
Create a New Controller
We created a new controller to handle the first stage of the signup. It's very simple. It only is a "new" method (for now) as we do no saving or updating of the subscription.

rails g controller subscriptions new

create app/controllers/subscriptions_controller.rb
route get "subscriptions/new"
invoke erb
create app/views/subscriptions
create app/views/subscriptions/new.html.erb
invoke rspec
create spec/controllers/subscriptions_controller_spec.rb
create spec/views/subscriptions
create spec/views/subscriptions/new.html.erb_spec.rb
invoke helper
create app/helpers/subscriptions_helper.rb
invoke rspec
create spec/helpers/subscriptions_helper_spec.rb

The controller looks like this:
class SubscriptionsController < Devise::RegistrationsController
def new
@subscription_plans = SubscriptionPlan.visible

Pretty basic stuff!

The view is:
<% title "Sign up" %>

<div>Pick a subscription</div>
<% semantic_form_for :subscription, :url => users_sign_up2_path, :html => { :method => :get } do |form| %>
<%= form.input :plan_id, :as => :radio, :collection =>{|sp| ["#{sp.description}",]} %>
<% form.buttons do %>
<%= form.commit_button "Continue" %>
<% end %>
<% end %>

Step 2:
We needed to add some custom routes to the users block to let Devise know what was going on:
  devise_for :users do
get "/users/sign_up" => "subscriptions#new"
get "/users/sign_up2" => "registrations#new"

The first line says use the subscriptions controller as the first stage of the sign up. That way we don't need to change any helpers and can do things in the standard Devise way. The second adds the second stage - the actual user new-ing and creation:

Step 3:
Override the Registrations Controller.
This is where 'the good stuff' happens! Note that we don't need to override the create method. Devise has a hook in it for building the model, so we override that instead.
It should be noted that this only works if the model can't be saved - i.e. your validations are complete. In our case we are creating 3 models in 1: subscription, address and user. The subscription and address will get saved if the user gets saved. So, we have validations in the user requiring a subscription and address. Then validations on the address to make sure it is good. That way if the validation of the address fails the validation of the user fails and we get chucked back out the the new user form.
The code might say it better!

class RegistrationsController < Devise::RegistrationsController
def new
@address =
@subscription_plan = SubscriptionPlan.find(params[:subscription][:plan_id])
rescue Exception => e
redirect_to users_sign_up_path and return
@user =


# Build a devise resource passing in the session. Useful to move
# temporary session data to the newly created user.
def build_resource(hash=nil)
address_info = params[:user].delete(:address) rescue {}
sub_info = params[:user].delete(:subscription_plan)
@subscription_plan = SubscriptionPlan.find sub_info["id"]
rescue Exception => e
redirect_to users_sign_up_path and return

subscription = =>

@address = => "US"))

@user =[:user].merge(:first_name => @address.first_name, :last_name => @address.last_name))
@user.subscription = subscription
@user.current_shipping_address = @address

def after_sign_up_path_for(resource)


Also note that we override 'after_sign_up_path_for'. That way we move the user onto the third stage of the process - the billing info - if they sign up.

This is still in early stages, and not yet live, but the process seems to work.

Thursday, 14 October 2010

testing Iridium gateway and active merchant with Rspec

Recently I rewrote the billing section of a website. The site uses Iridium as the gateway.
I thought people might be interested in seeing how I spec'd this:

context "iridium" do
before do
BillingDetail.gateway =
:login => "Casdasdasd",
:password => "asdasdasd",
:enable_3d_secure => true)

it "should work" do
bd =


credit_card =
address =
bd.address = address

bd.authorize(100, credit_card)

it "should fail" do
bd =


credit_card =
address =
bd.address = address

lambda {
bd.authorize(100, credit_card)
}.should raise_error(TGR::GatewayError)

I use FactoryGirl, so I set up some factories:

Factory.define :iridium_good_no_3ds, :class => ActiveMerchant::Billing::CreditCard do |cc|
cc.number "4976000000003436"
cc.last_name "Watson"
cc.first_name "John"
cc.verification_value "452"
cc.month "12"
cc.year "2012"

Factory.define :iridium_good_no_3ds_address, :class => Address do |address|
address.last_name "Watson"
address.first_name "John"
address.address_1 "32 Edward Street" "Camborne"
address.state "Cornwall"
address.zipcode "TR14 8PA" "GB"

Factory.define :iridium_card_declined, :class => ActiveMerchant::Billing::CreditCard do |cc|
cc.number "4921810000009076"
cc.last_name "Lewis"
cc.first_name "Jack"
cc.verification_value "875"
cc.month "12"
cc.year "2012"

Factory.define :iridium_card_declined_address, :class => Address do |address|
address.last_name "Lewis"
address.first_name "Jack"
address.address_1 "4 Wing Road" "Leighton Buzzard"
address.state "Bedfordshire"
address.zipcode "LU7 0JB" "GB"

This seems to work a treat. I can do all my other tests using the bogus gateway, but to make sure I am actually providing the right info for Iridium this context, with its before block to set the right gateway, does the job nicely!