module Rworkflow class Lifecycle attr_reader :states attr_accessor :initial, :default, :state_class, :state_options CARDINALITY_ALL_STARTED = :all_started # Indicates a cardinality equal to the jobs pushed at the start of the workflow DEFAULT_CARDINALITY = State::DEFAULT_CARDINALITY STATE_POLICY_NO_WAIT = State::STATE_POLICY_NO_WAIT DEFAULT_STATE_OPTIONS = { cardinality: self::DEFAULT_CARDINALITY, policy: self::STATE_POLICY_NO_WAIT }.freeze def initialize(state_class: State, state_options: {}) @state_options = DEFAULT_STATE_OPTIONS.merge(state_options) @state_class = state_class @states = {} @default = nil yield(self) if block_given? end def state(name, options = {}) options = @state_options.merge(options) new_state = @state_class.new(**options) yield(new_state) if block_given? @states[name] = new_state end def transition(from, name) from_state = @states[from] raise(StateError, from) if from_state.nil? return from_state.perform(name, @default) end def concat!(from, name, lifecycle, &state_merge_handler) state_merge_handler ||= lambda do |_, original_state, concat_state| original_state.merge(concat_state) end @states.merge!(lifecycle.states, &state_merge_handler) next_state = lifecycle.initial @states[from].transition(name, next_state) return self end def rename_state(old_state_name, new_state_name) old_state = @states[old_state_name] @states[new_state_name] = old_state @states.delete(old_state) @initial = new_state_name if @initial == old_state_name end def to_h return { initial: @initial, default: @default, state_class: @state_class, state_options: @state_options, states: Hash[@states.map { |name, state| [name, state.to_h] }] } end def to_graph states = @states || [] return digraph do states.each do |from, state| state.transitions.each do |transition, to| edge(from.to_s, to.to_s).label(transition.to_s) end end end end def serialize return self.class.serialize(self) end class << self def serialize(lifecycle) return lifecycle.to_h end def unserialize(hash) return self.new do |lf| lf.initial = hash[:initial] lf.default = hash[:default] lf.state_options = hash[:state_options] lf.state_class = hash[:state_class] hash[:states].each do |name, state_hash| lf.states[name] = lf.state_class.unserialize(state_hash) end end end end end end