Skip to content

Instantly share code, notes, and snippets.

@ilake
Created February 21, 2010 13:34
Show Gist options
  • Save ilake/310314 to your computer and use it in GitHub Desktop.
Save ilake/310314 to your computer and use it in GitHub Desktop.
#<a href="http://ow.ly/18z1T">RoR Multi-step forms</a>
private
def get_partial_user_from_session
unless @session['partial_user'].nil?
@user = @session['partial_user']
else
@user = User.new
end
end
def save_partial_user_in_session
unless @user.nil?
@session['partial_user'] = @user
end
end
# Might be a good addition to AR::Base
def valid_for_attributes( model, attributes )
unless model.valid?
errors = model.errors
our_errors = Array.new
errors.each { |attr,error|
if attributes.include? attr
our_errors << [attr,error]
end
}
errors.clear
our_errors.each { |attr,error| errors.add(attr,error) }
return false unless errors.empty?
end
return true
end
#To ensure the errors are cleared out properly on each request add a before_filter with the following:
def clear_stage_errors
unless @session['partial_user'].nil?
@session['partial_user'].errors.clear
end
end
=begin
You can use
get_partial_user_from_session
and
save_partial_user_in_session
as before and after filters, respectively, so you only have to reference @user in your multi-step controller.
=end
#A clean way to arrange the view is to have each step in its own partial, and set a variable such as
@current_stage
#in your controller, then in your view just do something like:
<%= start_form_tag({:action=> "signup"} , { :name => 'signupform' }) %>
<%= hidden_field_tag "changeCountry" %>
<%= hidden_field_tag "current_stage", "#{@current_stage}"%>
<h2>Registration Process</h2>
<%= @flash['notice'] %>
<%= render_partial "#{@current_stage}" %>
<%= end_form_tag %>
#Your signup controller action could something similar to this:
def signup
case @request.method
when :post
@current_stage = @params['current_stage']
if @current_stage == "stage1"
@user.attributes = @params['user']
@current_stage = "stage2" if valid_for_attributes(@user,["login","attribute2"])
end
elsif @current_stage == "stage2"
@user.attributes = @params['user']
@current_stage = "stage3" if valid_for_attributes(@user,["attribute3","attribute4"])
elsif @current_stage == "stage3"
@user.attributes = @params['user']
if @user.save
@session[:user] = User.authenticate(@user.login, @params['user']['password'])
flash['notice'] = "Signup successful"
redirect_to :action => "home"
end
end
when :get
@current_stage = "stage1"
end
end
#==========================================
=begin
Comments on this post
bmarini posts on May 19, 2008 at 12:56
I took your partial validation idea and included it in AR Base
=end
module ActiveRecord
module Validations
module Partial
def valid_for_attributes?( *attr_names )
return validate_for_attributes(attr_names)
end
def validate_for_attributes( *attr_names )
attr_names.map! {|a| a.to_s }
unless valid?
our_errors = Array.new
errors.each { |attr,error|
if attr_names.include? attr
our_errors << [attr,error]
end
}
errors.clear
our_errors.each { |attr,error| errors.add(attr,error) }
return false unless errors.empty?
end
return true
end
end
end
end
ActiveRecord::Base.class_eval do
include ActiveRecord::Validations::Partial
end
=begin
cgunther posts on Jul 02, 2009 at 11:40
bmarini, I think there is a bug in your code.
When I called:
=end
@order.valid_for_attributes? 'first_name', 'last_name'
=begin
It always passed validation. The error was the asterisk in front of attr_names on validate_for_attributes. It was being passed an array as the first attribute and when you run map! on it, it was converting the entire array to a single string (first_namelast_name).
Here's the code that works for me:
=end
module ActiveRecord
module Validations
module Partial
def valid_for_attributes?( *attr_names )
return validate_for_attributes(attr_names)
end
def validate_for_attributes( attr_names )
attr_names.map! {|a| a.to_s }
unless valid?
our_errors = Array.new
errors.each { |attr,error|
if attr_names.include? attr
our_errors << [attr,error]
end
}
errors.clear
our_errors.each { |attr,error| errors.add(attr,error) }
return false unless errors.empty?
end
return true
end
end
end
end
ActiveRecord::Base.class_eval do
include ActiveRecord::Validations::Partial
end
#The main difference is that if you call validate_for_attributes directly, you have to pass it an array.
#Chris Gunther
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment