Class: Spree::Checkout::Registry

Inherits:
Object
  • Object
show all
Defined in:
app/models/spree/checkout/registry.rb

Overview

Global registry for custom checkout steps and requirements.

Provides a composable extension point so developers can add, remove, or reorder checkout steps and attach extra requirements to existing steps —all without subclassing or monkey-patching.

Registered steps and requirements are evaluated by Requirements at serialization time to produce the requirements array on the Cart API.

Examples:

Add a custom step

Spree::Checkout::Registry.register_step(
  name: :loyalty,
  before: :payment,
  satisfied: ->(order) { order.loyalty_verified? },
  requirements: ->(order) { [{ step: 'loyalty', field: 'loyalty_number', message: 'Required' }] }
)

Add a requirement to an existing step

Spree::Checkout::Registry.add_requirement(
  step: :payment,
  field: :po_number,
  message: 'PO number is required',
  satisfied: ->(order) { order.po_number.present? }
)

Remove a step

Spree::Checkout::Registry.remove_step(:loyalty)

Class Method Summary collapse

Class Method Details

.add_requirement(step:, field:, message:, satisfied:, **options) ⇒ Array<Requirement>

Add an extra requirement to an existing checkout step.

Parameters:

  • step (String, Symbol)

    checkout step this requirement belongs to

  • field (String, Symbol)

    field identifier

  • message (String)

    human-readable validation message

  • satisfied (Proc)

    lambda accepting an order, returns true when met

  • options (Hash)

    additional options forwarded to Spree::Checkout::Requirement (:applicable)

Returns:

  • (Array<Requirement>)

    the updated requirements list



51
52
53
# File 'app/models/spree/checkout/registry.rb', line 51

def add_requirement(step:, field:, message:, satisfied:, **options)
  requirements << Requirement.new(step: step, field: field, message: message, satisfied: satisfied, **options)
end

.ordered_stepsArray<Step>

Returns steps sorted by before/after constraints relative to the checkout flow.

The sort order is derived from Order.checkout_step_names so it stays in sync with any customizations to the checkout state machine. Steps with before:/after: anchors are ordered by the anchor’s position; steps without constraints are appended at the end.

Returns:

  • (Array<Step>)

    steps in display order



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'app/models/spree/checkout/registry.rb', line 80

def ordered_steps
  return steps if steps.empty?

  step_order = Spree::Order.checkout_step_names.map(&:to_s)
  positioned, unpositioned = steps.partition { |s| s.before || s.after }

  sorted = positioned.sort_by do |s|
    anchor = s.before || s.after
    idx = step_order.index(anchor)
    # before: inserts just before the anchor, after: just after
    idx ? (s.before ? idx - 0.5 : idx + 0.5) : Float::INFINITY
  end

  sorted + unpositioned
end

.register_step(name:, satisfied:, requirements:, **options) ⇒ Array<Step>

Register a new custom checkout step.

Parameters:

  • name (String, Symbol)

    unique step identifier

  • satisfied (Proc)

    lambda accepting an order, returns true when step is complete

  • requirements (Proc)

    lambda accepting an order, returns Array of requirement hashes

  • options (Hash)

    additional options forwarded to Step (:before, :after, :applicable)

Returns:

  • (Array<Step>)

    the updated steps list



39
40
41
# File 'app/models/spree/checkout/registry.rb', line 39

def register_step(name:, satisfied:, requirements:, **options)
  steps << Step.new(name: name, satisfied: satisfied, requirements: requirements, **options)
end

.remove_requirement(step:, field:) ⇒ Array<Requirement>

Remove a previously registered requirement by step and field.

Parameters:

  • step (String, Symbol)

    checkout step the requirement belongs to

  • field (String, Symbol)

    field identifier

Returns:

  • (Array<Requirement>)

    the updated requirements list



68
69
70
# File 'app/models/spree/checkout/registry.rb', line 68

def remove_requirement(step:, field:)
  requirements.reject! { |r| r.step == step.to_s && r.field == field.to_s }
end

.remove_step(name) ⇒ Array<Step>

Remove a previously registered step by name.

Parameters:

  • name (String, Symbol)

    step name to remove

Returns:

  • (Array<Step>)

    the updated steps list



59
60
61
# File 'app/models/spree/checkout/registry.rb', line 59

def remove_step(name)
  steps.reject! { |s| s.name == name.to_s }
end

.requirementsArray<Requirement>

Returns all registered requirements.

Returns:



100
# File 'app/models/spree/checkout/registry.rb', line 100

def requirements = (@requirements ||= [])

.reset!void

This method returns an undefined value.

Clear all registered steps and requirements. Intended for testing.



105
106
107
108
# File 'app/models/spree/checkout/registry.rb', line 105

def reset!
  @steps = []
  @requirements = []
end

.stepsArray<Step>

Returns all registered steps.

Returns:

  • (Array<Step>)

    all registered steps



97
# File 'app/models/spree/checkout/registry.rb', line 97

def steps = (@steps ||= [])