46
Ultra Light & Maintainable Wizards in Rails Andy Maleh VP of Engineering BIG ASTRONAUT

Ultra Light and Maintainable Rails Wizards at RailsConf 2014

Embed Size (px)

Citation preview

Ultra Light & Maintainable

Wizards in RailsAndy Maleh

VP of Engineering

BIG ASTRONAUT

Overview• Why Use A Wizard?

• Wizard Example

• Wizard Implementation Goals

• 1001 Wizard Implementations

• Ultra Light & Maintainable Wizard

Why Use A Wizard?Avoid overwhelming user with a huge form

Why Use A Wizard?Simplify a workflow into multiple steps

Why Use A Wizard?Help the user by providing guidance

Wizard ExampleEarlyShares Project Idea Submission

Wizard ExampleStep 1 – Basic Info

Wizard ExampleStep 2 – Details

Wizard ExampleStep 3 – Document Content

Wizard ExampleStep 4 – Preview

Wizard ExampleSteps Done – Project Landing Page

Wizard Implementation Goals

• Rails Server must persist progress on every stepo JS Client-Side Persistence is Out Of Scope

• REST

• MVC

• OO

• Non-Functional Requirements:o Productivity

o Maintainability

o Performance

o Security

1001 Wizard Implementations

1. One Controller Per Wizard Step

• Create a REST resource per wizard step• One ActiveRecord with conditional validations

• Multiple Controllers

• Multiple sets of Views and Helpers

• Have each wizard step controller redirect to the

next one on create or update

1001 Wizard Implementations

1. One Controller Per Wizard Step

Step 1Controller

Step 2Controller

Step 3Controller

Step 4Controller

Create & Redirect to New

Create & Redirect to New

Create & Redirect to New

Step 1ActiveRecord

1001 Wizard Implementations

1. One Controller Per Wizard Stepo Critique

• Redundant code across controllers

o Repetitive redirect logic

o Redundant authentication logic

o Similar model loading logic

o Similar REST actions

• Tying database tables to presentation details

1001 Wizard Implementations

2. One Action/Presenter Per Wizard Step

• Create one ActiveRecord

• Create one controller with a different new and create

action variation per wizard step• e.g. new_step1, create_step1, new_step2, create_step2, etc…

• Create a different ActiveModel presenter per wizard step

• Have each ActiveModel presenter manage its own step

validation logic

• Bind every wizard step form to corresponding

ActiveModel presenter

• Have each wizard step action redirect to the next one

on create

1001 Wizard Implementations

2. One Action Per Wizard Step

Controller

Step 1 NewStep 1 CreateStep 2 New

Step 2 CreateStep 3 New

Step 3 CreateStep 4 New

Step 4 Create

Step 1Presenter

Validation/PersistanceAdapter

Step 2Presenter

Validation/PersistanceAdapter

Step 3Presenter

Validation/PersistanceAdapter

Step 4Presenter

Validation/Persistance Adapter

ActiveRecord

Create Step & Redirect to New Step

1001 Wizard Implementations

2. One Action Per Wizard Stepo Critique

• Not RESTful

• Redundant code across actions

o Repetitive redirect logic

o Repetitive update logic

• Too much presenter management code

1001 Wizard Implementations

3. Session Accumulationo Create one ActiveRecord

o Have multiple Controllers or Actions accumulate wizard step data in the

session

o Have ActiveRecord in-memory conditional validations run on every step

o On the final step, create ActiveRecord running all validations

Step 1

Step 2

Step 3

Step 4

Accumulate in Session

Accumulate in Session

Accumulate in Session

Create ActiveRecord

1001 Wizard Implementations

3. Session Accumulationo Critique

• Reliance on session has implications on scalability

• Controller code more complex due to managing session data

storage

• Validations defined twice, once per ActiveModel presenters used for

form validation and once in the actual ActiveRecord

1001 Wizard Implementations

4. Hidden Value Accumulationo Same as session value accumulation except the controller manages data

coming from a request parameter

o Same pros and cons as Session Value Accumulation except that it has no

scalability implications

o NOTE: hidden value must be encrypted for security

1001 Wizard Implementations

5. State Machineo Create one ActiveRecord

o Make ActiveRecord a state machine managing steps:

• adding a step column

• add current_step, next_step, and prev_step methods

o Different view per step

o Have single ActiveRecord manage each step validations by relying on

conditional validations. For example:

validate :phone,

presence: true,

if: lambda {current_step==“shipping”}

1001 Wizard Implementations

5. State Machineo Critique

• Puts presentation concerns in Model (breaks MVC)

o The state machine wizard must be a layer on top of the Model. It

has nothing to do with the business model.

• Stores an extra column in the database for purely presentation-

related reasons

o Can be avoided with session storage of state, opening a different

can of worms (controller complexity)

• More complexity in declaring validations due to conditions and

potentially overloading the Model

1001 Wizard Implementations

1001. Gems

• Wizardry: state machine in model

• Wicked: clean state machine in controller (better)

but no validation support beyond conditional

validation

• Rails-Wizard-Generator: XML XML XML

• Stepper: Nice support for steps in model and

controller

Wizard Implementation Goals Review

• Rails Server must persist progress on every stepo JS Client-Side Persistence is Out Of Scope

• REST

• MVC

• OO

• Non-Functional Requirements:o Productivity

o Maintainability

o Performance

o Security

Ultra Light & Maintainable Wizard

• Philosophy:o A wizard is simply a builder of a model

Ultra Light & Maintainable Wizard

• Philosophy:o Every step is simply a partial data-view of the model

Ultra Light & Maintainable Wizard

• Philosophy:o REST resources are the model and model parts edited during a step.

Ultra Light & Maintainable Wizard

• Philosophy:o Models must manage validations without conditions by relying on step-

polymorphism

Ultra Light & Maintainable Wizard

• Philosophy:o Step view forms are maintained independently with no conditionals as

well

step1.html.erb

step2.html.erb

step3.html.erb

step4.html.erb

Ultra Light & Maintainable Wizard

ModelController

CreateShow

Step 1 PresenterValidations/Step Logic

Step 2 PresenterValidations/Step Logic

Step 3 PresenterValidations/Step Logic

ModelActiveRecord

Core Validations & Shared Logic

Step 4 PresenterValidations/Step Logic

Update & Redirect to Edit

Model PartController

EditUpdate

Ultra Light & Maintainable Wizard

• In a Nutshell:o Model resource

o Nested model part resource

(e.g. /projects/project1/project_parts/basic_info)

• Step name serves as ID

• Contains validations and before/after hooks for stepping

o Controller for the model resource:

• Begins wizard by creating model ActiveRecord

• Shows produced model at the end of the wizard

o Controller for the model part resource:

• Every step represents an Edit action of a model part

• Every step submission represents an Update action of a model part

o View for model resource show page

o View for every model part presenter edit page

Ultra Light & Maintainable Wizard

Routes

Ultra Light & Maintainable Wizard

Model (main REST resource)

Ultra Light & Maintainable Wizard

Step Sub-Models aka Model Parts

Ultra Light & Maintainable Wizard

Project::BasicInfo Step Sub-Model

Ultra Light & Maintainable Wizard

Project::BasicInfo Step Sub-Model (cont’d)

Ultra Light & Maintainable Wizard

Project::Detail Step Sub-Model

Ultra Light & Maintainable Wizard

ProjectsController

ProjectPartsController

Ultra Light & Maintainable Wizard

ProjectPartsController (cont’d)

Ultra Light & Maintainable Wizard

Views

Ultra Light & Maintainable Wizard

View form template

Ultra Light & Maintainable Wizard

Step View Template Example

Review• Why Use A Wizard?

• Wizard Example

• Wizard Implementation Goals

• 1001 Wizard Implementations

• Ultra Light & Maintainable Wizard

github.com/AndyMaleh/ultra_light_wizard

Andy Maleh – VP of Engineering – Big Astronaut

• WEBSITE: http://www.bigastronaut.com

• BLOG: http://andymaleh.blogspot.com

• TWITTER: @AndyMaleh