Declare in:/out: explicitly on method steps

  • Declare in:/out: on a method step explicitly - do not skip them just because a method step is a regular Ruby method with access to the whole instance, and could technically read or write instance state directly instead.

  • This keeps the data flow of the whole step chain visible from the step declarations alone. One glance at the list of steps tells you what each step consumes and produces, without opening its method body, regardless of whether that step is a method step or a service step.

    class ParseCents
      include ConvenientService::Standard::Config
    
      attr_reader :amount
    
      step :NormalizeAmount,
        in: :amount,
        out: :normalized_amount
    
      step :AssertValidAmount,
        in: :normalized_amount
    
      step :result,
        in: :normalized_amount,
        out: :cents
    
      def initialize(amount:)
        @amount = amount
      end
    
      def result
        success(cents: (normalized_amount.to_f * 100).round)
      end
    
      private
    
      def NormalizeAmount
        success(normalized_amount: amount.to_s.strip)
      end
    
      def AssertValidAmount
        return failure("Amount `#{normalized_amount}` is not a valid positive number") unless normalized_amount.match?(/\A\d+(\.\d{1,2})?\z/)
    
        success
      end
    end
  • A method step's in: does not even have to match its method's parameter list - see what is method step loose call?

See also

Sources