module Attr::Gather::Workflow::DSL
DSL
for configuring a workflow
@api public
Constants
- Undefined
@api private
Public Instance Methods
Configures the result aggregator
Aggregators
make is possible to build custom logic about how results should be “merged” together. For example, yuo could build and aggregator that prioritizes the values of some tasks over others.
@example
class EnhanceUserProfile include Attr::Gather::Workflow aggregator :deep_merge end
@example
class EnhanceUserProfile include Attr::Gather::Workflow aggregator MyCustomAggregator end
@param agg [#call] the aggregator to use
@api public
# File lib/attr/gather/workflow/dsl.rb, line 147 def aggregator(agg = nil, opts = EMPTY_HASH) @aggregator = if agg.nil? && !defined?(@aggregator) Aggregators.default elsif agg.respond_to?(:new) agg.new(filter: filter, **opts) elsif agg Aggregators.resolve(agg, filter: filter, **opts) else @aggregator end end
Defines a container for task dependencies
Using a container makes it easy to re-use workflows with different data sources. Say one workflow was required to use a legacy DB, and one wanted to use a new DB. Using a container makes it easy to configure that dependency.
@example
LegacySystem = Dry::Container.new.tap do |c| c.register(:database) { Sequel.connect('sqlite://legacy.db') end class EnhanceUserProfile include Attr::Gather::Workflow container LegacySystem end
@param cont [Dry::Container] the Dry::Container to use
@note For more information, check out {dry-rb.org/gems/dry-container}
@api public
# File lib/attr/gather/workflow/dsl.rb, line 118 def container(cont = nil) @container = cont if cont @container end
Defines a task with name and options
@param task_name [Symbol] the name of the task
@example
class EnhanceUserProfile include Attr::Gather::Workflow # ... fetch :user_info do |t| t.depends_on = [:fetch_gravatar_info] end end
Calling `fetch` will yield a task object which you can configure like a PORO. Tasks will be registered for execution in the workflow.
@yield [Attr::Gather::Workflow::Task] A task to configure
@api public
# File lib/attr/gather/workflow/dsl.rb, line 66 def fetch(task_name, opts = EMPTY_HASH) task(task_name, opts) end
Defines a filter for filtering out invalid values
When aggregating data from many sources, it is hard to reason about all the ways invalid data will be returned. For example, if you are pulling data from a spreadsheet, there will often be typos, etc.
Defining a filter allows you to declaratively state what is valid. attr-gather will use this definition to automatically filter out invalid values, so they never make it into your system.
Filtering happens during each step of the workflow, which means that every Task
will receive validated input that you can rely on.
@example
class UserContract < Dry::Validation::Contract do params do optional(:id).filled(:integer) optional(:email).filled(:str?, format?: /@/) end end class EnhanceUserProfile include Attr::Gather::Workflow # Any of the key/value pairs that had validation errors will be # filtered from the output. filter :contract, UserContract.new end
@param filt [Symbol] the name filter to use @param args [Array<Object>] arguments for initializing the filter
@api public
# File lib/attr/gather/workflow/dsl.rb, line 192 def filter(filt = nil, *args, **opts) @filter = if filt.nil? && !defined?(@filter) Filters.default elsif filt Filters.resolve(filt, *args, **opts) else @filter end aggregator.filter = @filter @filter end
Defines a filter for filtering invalid values with an inline contract
This serves as a convenience method for defining a contract filter.
@example
class EnhanceUserProfile include Attr::Gather::Workflow # Any of the key/value pairs that had validation errors will be # filtered from the output. filter_with_contract do params do required(:name).filled(:string) required(:age).value(:integer) end rule(:age) do key.failure('must be greater than 18') if value < 18 end end end
@return [Dry::Validation::Contract,NilClass] @see dry-rb.org/gems/dry-validation
@api public
# File lib/attr/gather/workflow/dsl.rb, line 233 def filter_with_contract(arg = nil, &blk) contract = blk ? build_inline_contract_filter(&blk) : arg filter(:contract, contract) end
Defines a task with name and options
@param task_name [Symbol] the name of the task
@example
class EnhanceUserProfile include Attr::Gather::Workflow # ... step :fetch_user_info do |t| t.depends_on = [:email_info] end end
Calling `step` will yield a task object which you can configure like a PORO. Tasks will be registered for execution in the workflow.
@yield [Attr::Gather::Workflow::Task] A task to configure
@api public
# File lib/attr/gather/workflow/dsl.rb, line 91 def step(task_name, opts = EMPTY_HASH) task(task_name, opts) end
Defines a task with name and options
@param task_name [Symbol] the name of the task
@example
class EnhanceUserProfile include Attr::Gather::Workflow # ... task :fetch_database_info do |t| t.depends_on = [] end task :fetch_avatar_info do |t| t.depends_on = [:fetch_gravatar_info] end end
Calling `task` will yield a task object which you can configure like a PORO. Tasks will be registered for execution in the workflow.
@yield [Attr::Gather::Workflow::Task] A task to configure
@api public
# File lib/attr/gather/workflow/dsl.rb, line 38 def task(task_name, opts = EMPTY_HASH) conf = OpenStruct.new yield conf tasks << ({ name: task_name, **opts, **conf.to_h }) self end
Private Instance Methods
# File lib/attr/gather/workflow/dsl.rb, line 240 def build_inline_contract_filter(&blk) contract_klass = Class.new(Dry::Validation::Contract, &blk) contract_klass.new end