Good observation about stack traces and abstractions.
Re. your question, the pattern itself is no different than, say, Rack middleware, so you'd see similar cost and benefits. In essence you're running one callable object after the other.
A pipeline is essentially this
steps = [
->(r) { r },
->(r) { r },
->(r) { r },
]
Wrap initial data in a common Value object
initial = Result.new(some_data)
Run the Result through the steps, in order
result = steps.reduce(initial) { |r, step| step.call(r) }
That's the pattern, really. A reduce operation.
Re. stack trace, it can add noise because you're iterating over steps instead of calling them procedurally one by one, and you may want to decorate steps (put steps inside steps) for encapsulation, caching, etc. but again no different than Rack.
A pipeline is essentially this
steps = [ ->(r) { r }, ->(r) { r }, ->(r) { r }, ]
Wrap initial data in a common Value object
initial = Result.new(some_data)
Run the Result through the steps, in order
result = steps.reduce(initial) { |r, step| step.call(r) }
That's the pattern, really. A reduce operation.
Re. stack trace, it can add noise because you're iterating over steps instead of calling them procedurally one by one, and you may want to decorate steps (put steps inside steps) for encapsulation, caching, etc. but again no different than Rack.