lib/trailblazer/operation/pipetree.rb in trailblazer-operation-0.0.5 vs lib/trailblazer/operation/pipetree.rb in trailblazer-operation-0.0.6

- old
+ new

@@ -2,11 +2,11 @@ require "pipetree/flow" require "trailblazer/operation/result" require "uber/option" class Trailblazer::Operation - New = ->(klass, options) { klass.new(options) } # returns operation instance. + New = ->(klass, options) { klass.new(options) } # returns operation instance. # Implements the API to populate the operation's pipetree and # `Operation::call` to invoke the latter. # http://trailblazer.to/gems/operation/2.0/pipetree.html module Pipetree @@ -45,62 +45,110 @@ def >>(*args); _insert(:>>, *args) end def >(*args); _insert(:>, *args) end def &(*args); _insert(:&, *args) end def <(*args); _insert(:<, *args) end + # self.| ->(*) { }, before: "operation.new" + # self.| :some_method + def |(cfg, user_options={}) + DSL.import(self, self["pipetree"], cfg, user_options) && + heritage.record(:|, cfg, user_options) + end + + alias_method :step, :| + alias_method :consider, :& + alias_method :failure, :< + alias_method :success, :> + # :private: - # High-level user step API that allows ->(options) procs. - def _insert(operator, proc, options={}) - heritage.record(:_insert, operator, proc, options) + module Option + def self.call(proc, &block) + type = :proc + option = + if proc.is_a? Symbol + type = :symbol + ->(input, *_options) { input.send(proc, *_options) } + elsif proc.is_a? Proc + # ->(input, options) { proc.(**options) } + ->(input, *_options) { proc.(*_options) } + elsif proc.is_a? Uber::Callable + type = :callable + ->(input, *_options) { proc.(*_options) } + end + + yield type if block_given? + option + end + end + + # :public: + # Wrap the step into a proc that only passes `options` to the step. + # This is pure convenience for the developer and will be the default + # API for steps. ATM, we also automatically generate a step `:name`. + def self.insert(pipe, operator, proc, options={}, kws={}) # TODO: definer_name is a hack for debugging, only. # proc = Uber::Option[proc] _proc = if options[:wrap] == false proc - elsif proc.is_a? Symbol - options[:name] ||= proc - ->(input, _options) { input.send(proc, _options) } - elsif proc.is_a? Proc - options[:name] ||= "#{self.name}:#{proc.source_location.last}" if proc.is_a? Proc - # ->(input, options) { proc.(**options) } - ->(input, _options) { proc.(_options) } - elsif proc.is_a? Uber::Callable - options[:name] ||= proc.class - ->(input, _options) { proc.(_options) } + else + Option.(proc) do |type| + options[:name] ||= proc if type == :symbol + options[:name] ||= "#{kws[:definer_name]}:#{proc.source_location.last}" if proc.is_a? Proc if type == :proc + options[:name] ||= proc.class if type == :callable + end end - self["pipetree"].send(operator, _proc, options) # ex: pipetree.> Validate, after: Model::Build + pipe.send(operator, _proc, options) # ex: pipetree.> Validate, after: Model::Build end - def ~(cfg) - heritage.record(:~, cfg) + def self.import(operation, pipe, cfg, user_options={}) + if cfg.is_a?(Array) # e.g. from Contract::Validate + mod, args, block = cfg - self.|(cfg, inheriting: true) # FIXME: not sure if this is the final API. - end + import = Import.new(pipe, user_options) # API object. - def |(cfg, user_options={}) # sorry for the magic here, but still playing with the DSL. - if cfg.is_a?(Macro) # e.g. Contract::Validate - import = Import.new(self, user_options) + return mod.import!(operation, import, *args, &block) + end - return cfg.import!(self, import) && - heritage.record(:|, cfg, user_options) + insert(pipe, :>, cfg, user_options, {}) # DOEES NOOOT calls heritage.record + end + + Macros = Module.new + def self.macro!(name, constant) + Macros.send :define_method, name do |*args, &block| + [constant, args, block] end + end - self.> cfg, user_options # calls heritage.record + # :private: + # High-level user step API that allows ->(options) procs. + def _insert(operator, proc, options={}) + heritage.record(:_insert, operator, proc, options) + + DSL.insert(self["pipetree"], operator, proc, options, definer_name: self.name) end + def ~(cfg) + heritage.record(:~, cfg) + + self.|(cfg, inheriting: true) # FIXME: not sure if this is the final API. + end + # Try to abstract as much as possible from the imported module. This is for # forward-compatibility. # Note that Import#call will push the step directly on the pipetree which gives it the # low-level (input, options) interface. - Import = Struct.new(:operation, :user_options) do + Import = Struct.new(:pipetree, :user_options) do def call(operator, step, options) - operation["pipetree"].send operator, step, options.merge(user_options) + pipetree.send operator, step, options.merge(user_options) end def inheriting? user_options[:inheriting] end end end end + + extend Pipetree::DSL::Macros end