class SmashTheState::Operation::Sequence
Attributes
middleware_class_block[RW]
run_options[R]
steps[R]
Public Class Methods
new()
click to toggle source
# File lib/smash_the_state/operation/sequence.rb, line 10 def initialize @steps = [] @run_options = { dry: false } end
Public Instance Methods
add_error_handler_for_step(step_name, &block)
click to toggle source
# File lib/smash_the_state/operation/sequence.rb, line 87 def add_error_handler_for_step(step_name, &block) step = @steps.find { |s| s.name == step_name } # should we raise an exception instead? return if step.nil? step.error_handler = block end
add_middleware_step(step_name, options = {})
click to toggle source
rubocop:enable Lint/ShadowedException
# File lib/smash_the_state/operation/sequence.rb, line 104 def add_middleware_step(step_name, options = {}) step = Operation::Step.new step_name, options do |state, original_state| if middleware_class(state, original_state).nil? # no-op state else middleware_class(state, original_state).send(step_name, state, original_state) end end @steps << step end
add_step(step_name, options = {}, &block)
click to toggle source
# File lib/smash_the_state/operation/sequence.rb, line 46 def add_step(step_name, options = {}, &block) # steps need to be unique unless steps_for_name(step_name).empty? raise( StepConflict, "an operation step named #{step_name.inspect} already exists" ) end @steps << Step.new(step_name, options, &block) end
add_validation_step(options = {}, &block)
click to toggle source
# File lib/smash_the_state/operation/sequence.rb, line 58 def add_validation_step(options = {}, &block) step = steps_for_name(:validate).first || SmashTheState::Operation::ValidationStep.new(options) step.add_implementation(&block) @steps |= [step] end
call(state)
click to toggle source
# File lib/smash_the_state/operation/sequence.rb, line 15 def call(state) run_steps(@steps, state) end
dynamic_schema?()
click to toggle source
# File lib/smash_the_state/operation/sequence.rb, line 117 def dynamic_schema? dynamic_schema_step.nil? == false end
dynamic_schema_step()
click to toggle source
# File lib/smash_the_state/operation/sequence.rb, line 121 def dynamic_schema_step steps_for_name(:_dynamic_schema).first end
mark_as_side_effect_free!()
click to toggle source
marks all the the currently defined steps as free of side-effects
# File lib/smash_the_state/operation/sequence.rb, line 42 def mark_as_side_effect_free! steps.each { |s| s.options[:side_effect_free] = true } end
middleware_class(state, original_state = nil)
click to toggle source
rubocop:disable Lint/ShadowedException
# File lib/smash_the_state/operation/sequence.rb, line 97 def middleware_class(state, original_state = nil) middleware_class_block.call(state, original_state).constantize rescue NameError, NoMethodError nil end
override_step(step_name, options = {}, &block)
click to toggle source
# File lib/smash_the_state/operation/sequence.rb, line 66 def override_step(step_name, options = {}, &block) step = steps_for_name(step_name).first if step.nil? raise( BadOverride, "overriding step #{step_name.inspect} failed because it does " \ "not exist" ) end @steps[@steps.index(step)] = Step.new(step_name, options, &block) end
side_effect_free()
click to toggle source
return a copy without the steps that produce side-effects
# File lib/smash_the_state/operation/sequence.rb, line 33 def side_effect_free dup.tap do |seq| seq.instance_eval do @steps = seq.steps.select(&:side_effect_free?) end end end
slice(start, count)
click to toggle source
# File lib/smash_the_state/operation/sequence.rb, line 19 def slice(start, count) # slice should return a copy of the object being sliced dup.tap do |seq| # we're going to slice the steps, which is really the meat of a sequence, but we # need to evaluate in the copy context so that we can swap out the steps for a # new copy of steps (because note - even though we've copied the sequence # already, the steps of the copy still refer to the steps of the original!) seq.instance_eval do @steps = seq.steps.slice(start, count) end end end
steps_for_name(name)
click to toggle source
returns steps named the specified name. it's generally bad form to have mulitple steps with the same name, but it can happen in some reasonable cases (the most common being :validate)
# File lib/smash_the_state/operation/sequence.rb, line 83 def steps_for_name(name) steps.select { |s| s.name == name } end
Private Instance Methods
make_original_state(state)
click to toggle source
# File lib/smash_the_state/operation/sequence.rb, line 127 def make_original_state(state) return dynamic_schema_step.implementation.call(state, state, run_options) if dynamic_schema? state.dup end
run_steps(steps_to_run, state)
click to toggle source
# File lib/smash_the_state/operation/sequence.rb, line 133 def run_steps(steps_to_run, state) # retain a copy of the original state so that we can refer to it for posterity as # the operation state gets mutated over time original_state = make_original_state(state) current_step = nil steps_to_run.reduce(state) do |memo, s| current_step = s # we're gonna pass the state from the previous step into the implementation as # 'memo', but for convenience, we'll also always pass the original state into # the implementation as 'original_state' so that no matter what you can get to # your original input if s.name == :validate s.validate!(memo) else s.implementation.call(memo, original_state, run_options) end end rescue Operation::State::Invalid => e e.state rescue Operation::Error => e raise e if current_step.error_handler.nil? current_step.error_handler.call(e.state, original_state) end