Skip to content

Commit

Permalink
Extract ClassMethods constant into autoloadable file
Browse files Browse the repository at this point in the history
Otherwise, patching it does not work.
  • Loading branch information
mamhoff committed Jan 15, 2025
1 parent 123ebdf commit d2680fc
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 208 deletions.
208 changes: 0 additions & 208 deletions core/app/models/spree/core/state_machines/order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,214 +8,6 @@ def self.included(klass)
klass.extend ClassMethods
end

module ClassMethods
attr_accessor :previous_states
attr_writer :next_event_transitions, :checkout_steps, :removed_transitions

def checkout_flow(&block)
if block_given?
@checkout_flow = block
define_state_machine!
else
@checkout_flow
end
end

def define_state_machine!
self.checkout_steps = {}
self.next_event_transitions = []
self.previous_states = [:cart]
self.removed_transitions = []

# Build the checkout flow using the checkout_flow defined either
# within the Order class, or a decorator for that class.
#
# This method may be called multiple times depending on if the
# checkout_flow is re-defined in a decorator or not.
instance_eval(&checkout_flow)

klass = self

# To avoid multiple occurrences of the same transition being defined
# On first definition, state_machines will not be defined
state_machines.clear if respond_to?(:state_machines)
state_machine :state, initial: :cart, use_transactions: false do
klass.next_event_transitions.each { |state| transition(state.merge(on: :next)) }

# Persist the state on the order
after_transition do |order, transition|
# Hard to say if this is really necessary, it was introduced in this commit:
# https://github.com/mamhoff/solidus/commit/fa1d66c42e4c04ee7cd1c20d87e4cdb74a226d3d
# But it seems to be harmless, so we'll keep it for now.
order.state = order.state # rubocop:disable Lint/SelfAssignment

order.state_changes.create(
previous_state: transition.from,
next_state: transition.to,
name: 'order',
user_id: order.user_id
)
order.save
end

event :cancel do
transition to: :canceled, if: :allow_cancel?, from: :complete
end

event :return do
transition to: :returned, from: [:complete, :awaiting_return], if: :all_inventory_units_returned?
end

event :resume do
transition to: :resumed, from: :canceled, if: :canceled?
end

event :authorize_return do
transition to: :awaiting_return, from: :complete
end

event :complete do
transition to: :complete, from: klass.checkout_steps.keys.last
end

if states[:payment]
event :payment_failed do
transition to: :payment, from: :confirm
end

after_transition to: :complete, do: :add_payment_sources_to_wallet
before_transition to: :payment, do: :add_default_payment_from_wallet
before_transition to: :payment, do: :ensure_billing_address

before_transition to: :confirm, do: :add_store_credit_payments

# see also process_payments_before_complete below which needs to
# be added in the correct sequence.
end

before_transition from: :cart, do: :ensure_line_items_present

if states[:address]
before_transition to: :address, do: :assign_default_user_addresses
before_transition from: :address, do: :persist_user_address!
end

if states[:delivery]
before_transition to: :delivery, do: :ensure_shipping_address
before_transition to: :delivery, do: :create_proposed_shipments
before_transition to: :delivery, do: :ensure_available_shipping_rates
end

before_transition to: :resumed, do: :ensure_line_item_variants_are_not_deleted
before_transition to: :resumed, do: :validate_line_item_availability

# Sequence of before_transition to: :complete
# calls matter so that we do not process payments
# until validations have passed
before_transition to: :complete, do: :validate_line_item_availability
before_transition to: :complete, do: :ensure_promotions_eligible
before_transition to: :complete, do: :ensure_line_item_variants_are_not_deleted
before_transition to: :complete, do: :ensure_inventory_units
if states[:payment]
before_transition to: :complete, do: :process_payments_before_complete
end

after_transition to: :complete, do: :finalize
after_transition to: :resumed, do: :after_resume
after_transition to: :canceled, do: :after_cancel

after_transition from: any - :cart, to: any - [:confirm, :complete] do |order|
order.recalculate
end

after_transition do |order, transition|
order.logger.debug "Order #{order.number} transitioned from #{transition.from} to #{transition.to} via #{transition.event}"
end

after_failure do |order, transition|
order.logger.debug "Order #{order.number} halted transition on event #{transition.event} state #{transition.from}: #{order.errors.full_messages.join}"
end
end
end

def go_to_state(name, options = {})
checkout_steps[name] = options
previous_states.each do |state|
add_transition({ from: state, to: name }.merge(options))
end
if options[:if]
previous_states << name
else
self.previous_states = [name]
end
end

def insert_checkout_step(name, options = {})
before = options.delete(:before)
after = options.delete(:after) unless before
after = checkout_steps.keys.last unless before || after

cloned_steps = checkout_steps.clone
cloned_removed_transitions = removed_transitions.clone
checkout_flow do
cloned_steps.each_pair do |key, value|
go_to_state(name, options) if key == before
go_to_state(key, value)
go_to_state(name, options) if key == after
end
cloned_removed_transitions.each do |transition|
remove_transition(transition)
end
end
end

def remove_checkout_step(name)
cloned_steps = checkout_steps.clone
cloned_removed_transitions = removed_transitions.clone
checkout_flow do
cloned_steps.each_pair do |key, value|
go_to_state(key, value) unless key == name
end
cloned_removed_transitions.each do |transition|
remove_transition(transition)
end
end
end

def remove_transition(options = {})
removed_transitions << options
next_event_transitions.delete(find_transition(options))
end

def find_transition(options = {})
return nil if options.nil? || !options.include?(:from) || !options.include?(:to)

next_event_transitions.detect do |transition|
transition[options[:from].to_sym] == options[:to].to_sym
end
end

def next_event_transitions
@next_event_transitions ||= []
end

def checkout_steps
@checkout_steps ||= {}
end

def checkout_step_names
checkout_steps.keys
end

def add_transition(options)
next_event_transitions << { options.delete(:from) => options.delete(:to) }.merge(options)
end

def removed_transitions
@removed_transitions ||= []
end
end

def checkout_steps
steps = self.class.checkout_steps.each_with_object([]) { |(step, options), checkout_steps|
next if options.include?(:if) && !options[:if].call(self)
Expand Down
Loading

0 comments on commit d2680fc

Please sign in to comment.