module SteppedService

Service class based on steps chain (see steps), which can be successfull even with errors. If execution of step is successful, next step will be executed. Step can be marked as failed by calling fail!. That will stop executing steps chain and return as failed?. During processing, errors can be added, but they will not stop the execution.

Example usage

class TeaMaker < SteppedService::Base
  attr_reader :tea_type, :bags_into_kettle
  def initialize(tea_type:, bags_into_kettle:)
    @tea_type = tea_type
    @bags_into_kettle = bags_into_kettle
  end

  def steps
    %i[get_water_into_kettle
      make_it_boil
      put_tea_bags_into_kettle
      wait_3mins
      pour_tea_into_cups
      add_sugar_to_caps]
  end

  private

  attr_accessor :teabags, :kettle, :cups

  def get_water_into_kettle
    self.kettle = find_kettle
    unless kettle
      errors.add(:kettle, "There is no kettle in kitchen!")
      fail!
    end
    fill_water_into(kettle)
  end

  def make_it_boil
    ...
  end

  def put_tea_bags_into_kettle
    self.teabags = get_teabags(tea_type, bags_into_kettle)
    if teabags.empty?
      errors.add(:teabags, "There is no #{bags_into_kettle} #{tea_type} tea teabags in storage," \
                           "  using fruit tea teabags.")
      self.teabags = get_teabags(:fruit, bags_into_kettle)
      if teabags.empty?
        errors.add(:teabags, "There is no #{bags_into_kettle} fruit tea teabags in storage either!")
        fail!
      end
    end
    insert(teabags, to: kettle)
  end

  ...

  def add_sugar_to_caps # last step should fill @result
     cups.each{ |c| c.insert(sugar_cube) }
     @result = cups
  end
end

calling

teamaker = TeaMaker.call(:black, 2)

will return instance of with: