module Eco module API module UseCases class UseCaseChain < UseCase MAX_CHAINS = 70 @@num_chains = 0 def initialize(name, type:, root:, options: {}, &block) super(name, type: type, root: root, options: options, &block) @chains = [] @resolved_chains = nil end def root=(value) raise "You cannot change root UseGroup once the chains have been resolved" if @resolved_chains super(value) end def use(preserve_chains: false, recursive: false) UseCase.new(@name, type: @type, root: @root, options: @options, &@case).tap do |newcase| if preserve_chains chain_use = {preserve_chains: recursive, recursive: recursive} @chains = @chains.map do |usecase| if usecase.respond_to? :call Proc.new do |usegroup| usecase = usecase.call(usegroup) usecase.use(chain_use).chain_to(newcase) usecase end elsif usecase.is_a?(UseCase) usecase.use(chain_use).chain_to(newcase) usecase end end end end end def launch(input: nil, people: nil, session:, options: {}) data_model = super(input: input, people: people, session: session, options: options) launch_chain(data_model) end def chain(usecase = nil) @@num_chains += 1 raise "Reached maximum number of chained use cases (#{MAX_CHAINS}). Looks like a recursive cyclic chain 'use'" if @@num_chains >= MAX_CHAINS raise "A UseCase can only be chained with another UseCase" if usecase && !usecase.is_a?(UseCase) raise "Missuse. Please use either parameter or block but not both" if block_given? && usecase usecase = block_given?? Proc.new : usecase @chains.push(usecase) self end protected def chain_to(usecase) raise "A UseCase can only be chained with another UseCase" if usecase && !usecase.is_a?(UseCase) usecase.chain(self) end def resolved_chains(use_group = nil) return @resolved_chains if @resolved_chains raise "Only UseGroup object can resolve chains. Given: #{use_group} " if use_group && !use_group.is_a?(UseGroup) use_group = use_group || @root @resolved_chains = @chains.map do |usecase| usecase = usecase.call(use_group) if usecase.respond_to? :call raise "A UseCase can only be chained with another UseCase" if usecase && !usecase.is_a?(UseCase) usecase.resolved_chains(use_group) usecase end end private def launch_chain(data_model) return data_model if resolved_chains.empty? data_model.tap do |dm| dm[self][:chains] ||= {} resolved_chains.each do |usecase| # chained cases use same params as parent case (out of simplicity) data_chain = dm[self][:io].chain(usecase: usecase) keyed_data = data_chain.params(keyed: true) dm[self][:chains].merge(usecase.launch(keyed_data)) end end end end end end end