Class: Railsmith::Pipeline
- Inherits:
-
Object
- Object
- Railsmith::Pipeline
- Defined in:
- lib/railsmith/pipeline.rb,
lib/railsmith/pipeline/errors.rb,
lib/railsmith/pipeline/runner.rb,
lib/railsmith/pipeline/step_definition.rb
Overview
Sequential service composition with fail-fast semantics.
A pipeline declares an ordered list of steps, each backed by a Railsmith::BaseService subclass. On each call, the Runner walks the steps in order, merging the previous step’s result.value (when it is a Hash) into the accumulated params so the next step receives everything seen so far. On the first failure the pipeline halts and returns that failure Result, annotated with :pipeline_name and :pipeline_step in meta.
Basic usage
class CheckoutPipeline < Railsmith::Pipeline
step :validate_cart, service: CartService, action: :validate
step :reserve_inventory, service: InventoryService, action: :reserve,
rollback: :unreserve
step :charge_payment, service: PaymentService, action: :charge,
inputs: { amount: :cart_total }, rollback: :refund
step :send_confirmation, service: NotificationService, action: :send_receipt
end
result = CheckoutPipeline.call(params: { cart_id: 42, user_id: 7 })
result.success? # => true
result.[:pipeline_name] # => "CheckoutPipeline" (on failure only)
Param forwarding
Each step receives accumulated_params, which starts as the original params and grows as each step’s Hash result.value is merged in. Use inputs: to rename keys before they reach a specific step’s service:
step :charge, service: PaymentService, action: :charge,
inputs: { amount: :cart_total }
This renames the :cart_total key to :amount for PaymentService only; subsequent steps still see :cart_total in accumulated params.
Instrumentation
Two events are emitted per run:
"pipeline.step.railsmith" — fired for each step; payload includes
:pipeline, :step, :status, :duration
"pipeline.railsmith" — fired once on completion; payload includes
:pipeline, :status, :duration
Defined Under Namespace
Classes: GuardNotFoundError, ParamCollisionError, ParamMappingError, Runner, StepDefinition
Class Method Summary collapse
-
.call(params: {}, context: UNSET_CONTEXT) ⇒ Object
Run the pipeline and return a Result.
-
.call!(params: {}, context: UNSET_CONTEXT) ⇒ Object
Run the pipeline; raise Railsmith::Failure on the first step failure.
-
.guard(name, &block) ⇒ Object
Register a named guard predicate for use in step
if:/unless:options. -
.guards ⇒ Object
Registered named guard predicates for this pipeline class.
-
.inherited(subclass) ⇒ Object
Subclasses start with private copies of the parent’s step list and guard registry so additions on the subclass do not leak back upward.
-
.pipeline_name ⇒ Object
Human-readable name used in instrumentation payloads.
-
.step(name, service:, action:, inputs: nil, rollback: nil, **options) ⇒ Object
Declare a step in execution order.
-
.step_definitions ⇒ Object
Ordered list of StepDefinition records for this pipeline class.
Class Method Details
.call(params: {}, context: UNSET_CONTEXT) ⇒ Object
Run the pipeline and return a Result.
114 115 116 117 118 119 120 121 122 |
# File 'lib/railsmith/pipeline.rb', line 114 def call(params: {}, context: UNSET_CONTEXT) resolved = if context.equal?(UNSET_CONTEXT) Context.current || Context.build(nil) else Context.build(context) end Runner.new(pipeline_class: self, params: params, context: resolved).run end |
.call!(params: {}, context: UNSET_CONTEXT) ⇒ Object
Run the pipeline; raise Railsmith::Failure on the first step failure.
125 126 127 128 129 130 |
# File 'lib/railsmith/pipeline.rb', line 125 def call!(params: {}, context: UNSET_CONTEXT) result = call(params: params, context: context) raise Railsmith::Failure, result if result.failure? result end |
.guard(name, &block) ⇒ Object
Register a named guard predicate for use in step if:/unless: options.
guard :has_coupon? do |params, ctx|
params.key?(:coupon_code)
end
step :apply_coupon, service: CouponService, action: :apply, if: :has_coupon?
The block receives (accumulated_params, context) and must return a truthy/falsy value.
89 90 91 92 93 |
# File 'lib/railsmith/pipeline.rb', line 89 def guard(name, &block) raise ArgumentError, "guard block is required" if block.nil? guards[name.to_sym] = block end |
.guards ⇒ Object
Registered named guard predicates for this pipeline class.
96 97 98 |
# File 'lib/railsmith/pipeline.rb', line 96 def guards @guards ||= {} end |
.inherited(subclass) ⇒ Object
Subclasses start with private copies of the parent’s step list and guard registry so additions on the subclass do not leak back upward.
134 135 136 137 138 |
# File 'lib/railsmith/pipeline.rb', line 134 def inherited(subclass) super subclass.instance_variable_set(:@step_definitions, step_definitions.dup) subclass.instance_variable_set(:@guards, guards.dup) end |
.pipeline_name ⇒ Object
Human-readable name used in instrumentation payloads.
106 107 108 |
# File 'lib/railsmith/pipeline.rb', line 106 def pipeline_name name || "AnonymousPipeline" end |
.step(name, service:, action:, inputs: nil, rollback: nil, **options) ⇒ Object
Declare a step in execution order.
74 75 76 77 78 |
# File 'lib/railsmith/pipeline.rb', line 74 def step(name, service:, action:, inputs: nil, rollback: nil, **) condition, polarity = extract_step_condition() attrs = { name: name, service: service, action: action, inputs: inputs, rollback: rollback } step_definitions << StepDefinition.new(**step_definition_attributes(attrs, condition, polarity, )) end |
.step_definitions ⇒ Object
Ordered list of StepDefinition records for this pipeline class.
101 102 103 |
# File 'lib/railsmith/pipeline.rb', line 101 def step_definitions @step_definitions ||= [] end |