README.md in nxt_pipeline-0.2.8 vs README.md in nxt_pipeline-0.3.0

- old
+ new

@@ -30,30 +30,35 @@ by the step yielded to the constructor. ```ruby pipeline = NxtPipeline::Pipeline.new do |p| # Add a named constructor that will be used to execute your steps later - # All options that you pass in your step will be available through accessors in your constructor - p.constructor(:service, default: true) do |step, arg| - step.service_class.new(options: arg).call + # All options that you pass in your step will be available through accessors in your constructor + # You can call :to_s on a step to set it by default. You can later overwrite at execution for each step if needed. + p.constructor(:service, default: true) do |step, arg:| + step.to_s = step.service_class.to_s + result = step.service_class.new(options: arg).call + result && { arg: result } end - p.constructor(:job) do |step, arg| - step.job_class.perform_later(*arg) && arg + p.constructor(:job) do |step, arg:| + step.job_class.perform_later(*arg) && { arg: arg } end end # Once a pipeline was created you can still configure it -pipeline.constructor(:call) do |step, arg| - step.caller.new(arg).call +pipeline.constructor(:call) do |step, arg:| + result = step.caller.new(arg).call + result && { arg: result } end # same with block syntax # You can use this to split up execution from configuration pipeline.configure do |p| - p.constructor(:call) do |step, arg| - step.caller.new(arg).call + p.constructor(:call) do |step, arg:| + result = step.caller.new(arg).call + result && { arg: result } end end ``` ### Defining steps @@ -65,15 +70,15 @@ pipeline.step service_class: MyOtherServiceClass, to_s: 'Second step' # ^ Since service is the default step you don't have to specify it the step type each time pipeline.step :job, job_class: MyJobClass # to_s is optional pipeline.step :job, job_class: MyOtherJobClass -pipeline.step :step_name_for_better_log do |_, arg| +pipeline.step :step_name_for_better_log do |_, arg:| # ... end -pipeline.step to_s: 'This is the same as above' do |step, arg| +pipeline.step to_s: 'This is the same as above' do |step, arg:| # ... step.to_s => 'This is the same as above' end ``` You can also define inline steps, meaning the block will be executed. When you do not provide a :to_s option, type @@ -100,12 +105,12 @@ You can also directly execute a pipeline with: ```ruby NxtPipeline::Pipeline.execute('initial argument') do |p| - p.step do |_, arg| - arg.upcase + p.step do |_, arg:| + { arg: arg.upcase } end end ``` You can query the steps of your pipeline simply by calling `pipeline.steps`. A NxtPipeline::Step will provide you with @@ -131,11 +136,11 @@ You can also define guard clauses that take a proc to prevent the execution of a step. When the guard takes an argument the step argument is yielded. ```ruby pipeline.execute('initial argument') do |p| - p.step :service, service_class: MyServiceClass, if: -> (arg) { arg == 'initial argument' } + p.step :service, service_class: MyServiceClass, if: -> (arg:) { arg == 'initial argument' } p.step :service, service_class: MyOtherServiceClass, unless: -> { false } end ``` @@ -143,28 +148,28 @@ Apart from defining constructors and steps you can also define error callbacks. ```ruby NxtPipeline::Pipeline.new do |p| - p.step do |_, arg| - arg.upcase + p.step do |_, arg:| + { arg: arg.upcase } end - p.on_error MyCustomError do |step, arg, error| + p.on_error MyCustomError do |step, opts, error| # First matching error callback will be executed! end - p.on_errors ArgumentError, KeyError do |step, arg, error| + p.on_errors ArgumentError, KeyError do |step, opts, error| # First matching error callback will be executed! end - p.on_errors YetAnotherError, halt_on_error: false do |step, arg, error| + p.on_errors YetAnotherError, halt_on_error: false do |step, opts, error| # After executing the callback the pipeline will not halt but continue to # execute the next steps. end - p.on_errors do |step, arg, error| + p.on_errors do |step, opts, error| # This will match all errors inheriting from StandardError end end ``` @@ -172,22 +177,75 @@ You can also define callbacks that run before and after the `#execute` action. Both callback blocks get the pipeline instance (to access stuff like the `log`) and the argument of the pipeline yielded. ```ruby NxtPipeline::Pipeline.new do |p| - p.before_execute do |pipeline, arg| + p.before_execute do |pipeline, arg:| # Will be called from within #execute before entering the first step # After any configure block though! end - p.after_execute do |pipeline, arg| + p.after_execute do |pipeline, arg:| # Will be called from within #execute after executing last step end end ``` Note that the `after_execute` callback will not be called, when an error is raised in one of the steps. See the previous section (_Error callbacks_) for how to define callbacks that run in case of errors. +### DSL + +The gem also comes with an easy DSL to make pipeline handling in your code more convenient. +Simply include NxtPipeline::Dsl in your class: + +```ruby +class MyAwesomeClass + include NxtPipeline::Dsl + + # register a pipeline with a name and a block + pipeline :validation do |p| + pipeline.constructor(:validate) do |step, arg:| + result = step.validator.call(arg: arg) + result && { arg: result } + end + + pipeline.step :validate, validator: NameValidator + pipeline.step :validate, validator: AdressValidator + pipeline.step :validate, validator: BankAccountValidator + pipeline.step :validate, validator: PhoneNumberValidator + + p.on_error ValidationError do |step, opts, error| + # ... + end + end + + pipeline :execution do |p| + p.step do |_, arg:| + { arg: arg.upcase } + end + + p.on_error MyCustomError do |step, opts, error| + # nesting pipelines also works + pipeline(:error).execute(error) + end + end + + pipeline :error do |p| + p.step do |_, error| + error # do something here + end + end + + def call(arg) + # execute a pipeline simply by fetching it and calling execute on it as you would normally + pipeline(:execution).execute(arg: arg) + end +end +``` + +## Topics +- Step orchestration (chainable steps) +- Constructors should take arg as first and step as second arg ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.