Piper.rb

Installation

Add this line to your application's Gemfile:

gem "piper-rb"

Usage

super basic

The most basic element of Piper is a pipe. Just think of it as a condition step. If no keywords are used, then (in accordance to Ruby conventions) the last line of code is taken as the step's condition:

require "piper-rb"

class YourSuperbService < PiperService
  attribute :input, Types::Any

  pipe "input is Numeric" do
    input.is_a? Numeric
  end

  pipe "input is greater than 100?" do
    false

    200 == 100

    input > 100
  end

  pipe "input is less than 1000?" do
    input < 1000
  end
end

YourSuperbService.new(input: 200).call
#=> Success(true)

YourSuperbService.new(input: "asd").call
#=> Failure({:service=>#<YourSuperbService input="asd">, :object=>false, :message=>nil})

keywords for more control

If you want to specify the condition, object to be returned and the error message, there are keywords for this:

  • condition (aliase: cond)
  • result_object (aliases: object, rslt)
  • message (aliases: fail_message, mssg)
require "piper-rb"

class YourFineService < PiperService
  attribute :input, Types::Any

  pipe "input is Numeric" do
    message { "The input must be `Numeric`." }

    input.is_a? Numeric
  end

  pipe "input is greater than 100?" do
    cond { input > 100 }

    200 == 100
  end

  pipe "input is less than 1000?" do
    object { input }

    input < 1000
  end
end

YourFineService.new(input: 200).call
#=> Success(200)

YourFineService.new(input: "asd").call
#=> Failure({:service=>(...), :object=>false, :message=>"The input must be `Numeric`."})

you can access last step's object with last_result:

require "piper-rb"

class YourGreatService < PiperService
  attribute :input, Types::Any

  pipe "this step`s result should be passed to..." do
    input * 30
  end

  pipe "...to this step" do
    cond    { last_result == input * 30 }
    object  { last_result }
  end
end

YourGreatService.new(input: 20).call
#=> Success(600)

you can nest services, calling them in a pipe passes their result:

require "piper-rb"

class YourMajesticService < PiperService
  attribute :input, Types::Any

  pipe :nothing_to_see_here do
    true
  end

  pipe :calling_nested_service do
    YourFlamboyantService.new(nested_input: input * 20).call
  end
end

class YourFlamboyantService < PiperService
  attribute :nested_input, Types::Any

  pipe :simple_step do
    message { "I am the one who knocks!" }
    nested_input >= 300
  end

end

YourMajesticService.new(input: 20).call
#=> Success(true)

YourMajesticService.new(input: 2).call
#=> Failure({:service=>#<YourFlamboyantService nested_input=40>, :object=>false, :message=>"I am the one who knocks!"})