Last active
April 28, 2020 15:45
-
-
Save jebw/c156a092dec8d2fac4a6ec4be4b005a3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Rails.application.routes.draw do | |
resources :registration_steps, only: %i(edit update) | |
end | |
class Step | |
attr_reader :name, :attributes, :condition | |
def initialize(name, attributes, &condition) | |
@name, @attributes = name, attributes | |
@condition = condition || true | |
end | |
end | |
class Journey | |
STEPS = [] | |
class << self | |
def get(step_name) | |
raise UnknownStep unless STEPS[step_name] | |
STEPS[step_name] | |
end | |
def steps | |
STEPS.values | |
end | |
def step_names | |
STEPS.keys | |
end | |
private | |
def step(name, attributes, &condition) | |
STEPS[name] = Step.new(name, attributes, condition) | |
end | |
end | |
delegate :get, :steps, :step_names, to: :class | |
step :identity, %i(firstname lastname email) | |
step :contact_details, %i(address1 postcode country) do | |
firstname.present? || lastname.present? || email.present? | |
end | |
def new(step_name) | |
raise UnknownStep unless step_names.include?(step_name) | |
@step_name = step_name | |
end | |
def current | |
get(@step_name) || raise(UnknownStep) | |
end | |
def next_step(name = @step_name) | |
step = STEPS[steps.index_of(name) + 1] | |
return unless step | |
if instance_eval(step.condition) | |
step | |
else | |
next_step(step.name) | |
end | |
end | |
end | |
module RedisStore | |
include ActiveSupport::Concern | |
included do | |
delegate :redis, :key_for_id, to: :class | |
end | |
def save | |
Redis.current.set key, serialized_data | |
end | |
def key | |
self.id ||= SecureRandom.uuid | |
key_for_id(id) | |
end | |
module ClassMethods | |
def redis | |
Redis.current | |
end | |
def key_for_id(id) | |
"registration/#{id}" | |
end | |
def find(id) | |
serialized_data = redis.get key_for_id(id) | |
self.new deserialize_data(data) | |
end | |
end | |
end | |
class StepValidation | |
include ActiveSupport::Concern | |
def included | |
before_validation :clean_unneeded_fields | |
end | |
module ClassMethods | |
def validate_step(step_name, &block) | |
step = Journey.get(step_name) | |
with_options(on: [step.name, :complete], if: step.condition, &block) | |
end | |
def clean_unneeded_fields | |
Journey.steps.each do |step| | |
unless instance_eval(step.condition) | |
step.attributes.each { |attr| self.attr = nil } | |
end | |
end | |
end | |
end | |
end | |
class Registration | |
include ActiveModel::Model | |
include ActiveModel::Attributes | |
include RedisStore | |
include StepValidation | |
validate_step :identity do | |
validates :firstname, presence: true | |
validates :lastname, presence: true | |
validates :email, presence: true, email_format: true | |
end | |
validate_step :contact_details do | |
validates :address1, presence: true | |
validates :postcode, presence: true, format: { with: POSTCODE_FORMAT } | |
validates :country, presence: true, inclusion: { in: COUNTRY_CODES } | |
end | |
validate_step :t_and_cs do | |
validates :t_and_cs, presence: true, acceptance: true | |
end | |
end | |
class RegistrationStepsController < ApplicationController | |
def edit | |
@registration = current_registration | |
end | |
def update | |
@registration = current_registration | |
@registration.attributes = step_params | |
if @registration.valid?(on: current_step) | |
@registration.save! | |
redirect_to next_step_path | |
else | |
render 'edit' | |
end | |
end | |
private | |
def current_registration | |
if session[:registration_id] | |
@registration ||= Registration.find(session[:registration_id]) | |
else | |
@registration ||= Registration.new | |
end | |
end | |
def current_step | |
@current_step ||= Journey.new(params[:id]) | |
end | |
def step_params | |
params.require(:registration).permit(*current_step[:attributes]) | |
end | |
def next_step_path | |
if next_step = current_step.next_step | |
registration_steps_path current_step.next_step | |
else | |
finished_path | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment