module Eco module API # Autoloading children is done on init and each time usecases it's queried class UseCases class UnknownCase < StandardError def initialize(msg = nil, case_name: nil, type: nil) msg ||= "Unknown case" msg += ". Case name '#{case_name}'" if case_name msg += ". Case type '#{type}'" if type super(msg) end end class AmbiguousCaseReference < StandardError def initialize(msg = nil, case_name: nil) msg ||= "You must specify type when there are multiple cases with same name" msg += ". Case name '#{case_name}'" if case_name super(msg) end end extend Eco::API::Common::ClassAutoLoader include Enumerable autoloads_children_of "Eco::API::Common::Loaders::UseCase" autoload_namespace_ignore "Eco::API" def initialize() @usecases = {} @cache_init = false @cases_by_name = {} init_caches end def length count end def empty? count == 0 end def each(&block) return to_enum(:each) unless block items.each(&block) end def items to_h.values end def defined?(name, type: nil) return to_h.key?(key(name, type)) if type name?(name) end def name?(name) !!by_name[name] end # @return [Eco::API::UseCases::UseCase] the `name` usecase of type `type` with the defined `block`. def define(name, type:, &block) Eco::API::UseCases::UseCase.new(name, type: type, root: self, &block).tap do |usecase| add(usecase) end end # Adds `usecase` to the _Collection_. Before it tries to auto-load pending classes of `auto_loaded_class`. # @param usecase [Eco::API::UseCases::UseCase] the usecase to be added. def add(usecase) raise "Expected Eco::API::UseCases::UseCase object. Given: #{usecase}" if !usecase || !usecase.is_a?(Eco::API::UseCases::UseCase) name = usecase.name type = usecase.type puts "Warning: overriding '#{type.to_s}' case #{name}" if self.defined?(name, type: type) to_h[key(name, type)] = usecase @cache_init = false usecase end # @return [Eco::API::UseCases] a copy of instance object of `self`. def dup init_caches self.class.new.merge(self) end # Merges cases overriding self for exisint parsers # @param cases [Eco::API::UseCases] the source set of cases to be merged. # @return [Eco::API::UseCases] `self`. def merge(cases) return self if !cases raise "Expected a Eco::API::UseCases object. Given #{cases.class}" if !cases.is_a?(Eco::API::UseCases) to_h.merge!(cases.to_h).transform_values do |usecase| usecase.root = self end @cache_init = false self end def names by_name.keys end def types(name) return nil if !name?(name) by_name[name].map { |usecase| usecase.type } end def case(name, type: nil) if type && target_case = to_h[key(name, type)] return target_case elsif type raise UseCases::UnknownCase.new(case_name: name, type: type) end raise UseCases::UnknownCase.new(case_name: name, type: type) unless cases = by_name[name] raise UseCases::AmbiguousCaseReference.new(case_name: name) if cases.length > 1 cases.first end protected def to_h init_caches @usecases end private def by_name init_caches @by_name end def init_caches # first, make sure it loads pending `:custom_children` children to be loaded re_init = self.class.autoload_children(self) return true if @cache_init && !re_init @by_name = @usecases.values.group_by { |usecase| usecase.name } @cache_init = true end def key(name, type) name.to_s + type.to_s end def name(key) key.to_s.split(":").first end def type key.to_s.split(":").last&.to_sym end end end end require_relative 'usecases/base_case' require_relative 'usecases/use_case' require_relative 'usecases/use_case_chain' require_relative 'usecases/base_io' require_relative 'usecases/use_case_io' require_relative 'usecases/cli' require_relative 'usecases/default_cases' require_relative 'usecases/graphql' require_relative 'usecases/ooze_samples' require_relative 'usecases/ooze_cases' require_relative 'usecases/default'