DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world
RoR Multi-step Forms
Here's a controller code snippet for doing partial validations on Rails model objects:
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
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. 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






Comments
Snippets Manager replied on Thu, 2009/07/02 - 11:31am
@order.valid_for_attributes? 'first_name', 'last_name'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: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 endThe main difference is that if you call validate_for_attributes directly, you have to pass it an array. Chris GuntherSnippets Manager replied on Tue, 2006/12/05 - 10:38pm
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