A clean and elegant approach to partial object validation with Rails + Wicked wizards (using session to store the partial object)
class PostWizardController < ApplicationController | |
include Wicked::Wizard | |
steps '1_title', '2_body', '3_price' | |
# Define partial validation in a clear tabular format | |
VALIDATIONS = { | |
'1_title' => [:title], | |
'2_body' => [:title, :body], | |
'3_price' => [:title, :body, :price], | |
}.freeze | |
# Here PartialPost is a private property of this controller not shared in any | |
# other part of the code so we make it a nested class. | |
class PartialPost < Post | |
attr_accessor :step | |
# For message passing only | |
attr_accessor :session | |
def self.model_name | |
Post.model_name | |
end | |
def save | |
# In the last step, actually persist to database | |
if self.step == VALIDATIONS.keys.last | |
rv = super | |
session.delete(:post_wizard) if rv | |
self.session = nil | |
return rv | |
end | |
# Clear out the session because we can't save session within session | |
self.session = nil | |
# Trigger ActiveRecord's validation | |
self.valid? | |
(self.errors.messages.keys - VALIDATIONS[self.step]).each do |k| | |
self.errors.messages.delete(k) | |
end | |
# Insert complicated custom validation manipulation here | |
self.errors.empty? | |
end | |
end | |
def index | |
session[:post_wizard] ||= {} | |
session[:post_wizard][:post] = PartialPost.new | |
redirect_to wizard_path(steps.first) | |
end | |
def show | |
if session[:post_wizard].try(:[], :post).present? | |
@post = session[:post_wizard][:post] | |
end | |
render_wizard | |
end | |
def update | |
@post = session[:post_wizard][:post] | |
@post.attributes = params[:post] | |
@post.step = step | |
@post.session = session | |
render_wizard(@post) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment