Class: Hubbado::Sequence::Pipeline

Inherits:
Object
  • Object
show all
Defined in:
lib/hubbado/sequence/pipeline.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ctx, dispatcher: nil) ⇒ Pipeline

Returns a new instance of Pipeline.



27
28
29
30
31
32
# File 'lib/hubbado/sequence/pipeline.rb', line 27

def initialize(ctx, dispatcher: nil)
  @ctx = ctx
  @trail = []
  @failed_result = nil
  @dispatcher = dispatcher
end

Class Method Details

.call(ctx = nil, **kwargs, &block) ⇒ Object

‘Pipeline.(ctx) { |p| … }` is the block form: yields the pipeline, runs the block (so steps can be added in statement form), and returns the final Result. The non-block form returns the Pipeline so chained `.step(…)…result` calls still work.



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/hubbado/sequence/pipeline.rb', line 8

def self.call(ctx = nil, **kwargs, &block)
  if ctx.nil?
    ctx = Ctx.build(kwargs)
  elsif !kwargs.empty?
    raise ArgumentError, "Pipeline.() takes either a Ctx or keyword arguments, not both"
  elsif !ctx.is_a?(Ctx)
    ctx = Ctx.build(ctx)
  end

  pipe = new(ctx)

  if block
    block.call(pipe)
    pipe.result
  else
    pipe
  end
end

Instance Method Details

#invoke(name, *args, **kwargs) ⇒ Object

‘invoke(:name, *args, **kwargs)` calls a declared dependency on the sequencer: gets the dependency via `dispatcher.send(name)` (the reader), then invokes it with `(ctx, *args, **kwargs)`. Same trail recording, failure short-circuiting, and lenient return convention as `step`.

Use this for any declared dependency — macros (‘Macros::Model::Find`) and nested sequencers (`Seqs::Present`) alike. Use `step` for local instance methods like `def deserialize_contract(ctx)`.



67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/hubbado/sequence/pipeline.rb', line 67

def invoke(name, *args, **kwargs)
  return self if @failed_result

  return_value = invoke_dependency(name, args, kwargs)

  if return_value.is_a?(Result) && return_value.failure?
    @failed_result = tag_failure(return_value, name)
  else
    @trail << name
  end

  self
end

#resultObject



96
97
98
99
100
101
102
# File 'lib/hubbado/sequence/pipeline.rb', line 96

def result
  if @failed_result
    @failed_result
  else
    Result.ok(@ctx, trail: @trail.dup)
  end
end

#step(name, &block) ⇒ Object

‘step(:name) { |ctx| … }` runs the block. `step(:name)` with no block dispatches to `dispatcher.send(name, ctx)` on the sequencer that built this pipeline (via the mixin’s ‘pipeline(ctx)` helper). Block beats dispatch when both are available; raises if neither.

Lenient return convention: a step is treated as successful unless it explicitly returns a failed ‘Result`. Any other return value (nil, false, a model, a hash, `Result.ok(…)`) is taken as success and the pipeline continues with the same `@ctx`. Only `Result.fail(…)` / `failure(ctx, code: …)` short-circuits the pipeline.



44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/hubbado/sequence/pipeline.rb', line 44

def step(name, &block)
  return self if @failed_result

  return_value = invoke_step(name, block)

  if return_value.is_a?(Result) && return_value.failure?
    @failed_result = tag_failure(return_value, name)
  else
    @trail << name
  end

  self
end

#transaction(&block) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/hubbado/sequence/pipeline.rb', line 81

def transaction(&block)
  return self if @failed_result

  if defined?(::ActiveRecord::Base)
    ::ActiveRecord::Base.transaction do
      yield(self)
      raise ::ActiveRecord::Rollback if @failed_result
    end
  else
    yield(self)
  end

  self
end