lib/plumb/steppable.rb in plumb-0.0.1 vs lib/plumb/steppable.rb in plumb-0.0.2

- old
+ new

@@ -17,10 +17,11 @@ BLANK_STRING = '' BLANK_ARRAY = [].freeze BLANK_HASH = {}.freeze BLANK_RESULT = Result.wrap(Undefined) + NOOP = ->(result) { result } module Callable def metadata MetadataVisitor.call(self) end @@ -44,24 +45,26 @@ module Steppable include Callable def self.included(base) nname = base.name.split('::').last - nname.gsub!(/([a-z\d])([A-Z])/,'\1_\2') + nname.gsub!(/([a-z\d])([A-Z])/, '\1_\2') nname.downcase! nname.gsub!(/_class$/, '') nname = nname.to_sym base.define_method(:node_name) { nname } end def self.wrap(callable) if callable.is_a?(Steppable) callable + elsif callable.is_a?(::Hash) + HashClass.new(schema: callable) elsif callable.respond_to?(:call) Step.new(callable) else - StaticClass.new(callable) + MatchClass.new(callable) end end attr_reader :name @@ -106,15 +109,11 @@ def transform(target_type, callable = nil, &block) self >> Transform.new(target_type, callable || block) end def check(errors = 'did not pass the check', &block) - a_check = lambda { |result| - block.call(result.value) ? result : result.invalid(errors:) - } - - self >> a_check + self >> MatchClass.new(block, error: errors) end def meta(data = {}) self >> Metadata.new(data) end @@ -143,14 +142,14 @@ end end def default(val = Undefined, &block) val_type = if val == Undefined - DefaultProc.call(block) - else - Types::Static[val] - end + DefaultProc.call(block) + else + Types::Static[val] + end self | (Types::Undefined >> val_type) end class Node @@ -184,55 +183,58 @@ rule(included_in: opts) end def rule(*args) specs = case args - in [::Symbol => rule_name, value] - { rule_name => value } - in [::Hash => rules] - rules - else - raise ArgumentError, "expected 1 or 2 arguments, but got #{args.size}" - end + in [::Symbol => rule_name, value] + { rule_name => value } + in [::Hash => rules] + rules + else + raise ArgumentError, "expected 1 or 2 arguments, but got #{args.size}" + end self >> Rules.new(specs, metadata[:type]) end - def is_a(klass) - rule(is_a: klass) - end - def ===(other) case other when Steppable other == self else resolve(other).valid? end end - def coerce(type, coercion = nil, &block) - coercion ||= block - step = lambda { |result| - if type === result.value - result.valid(coercion.call(result.value)) - else - result.invalid(errors: "%s can't be coerced" % result.value.inspect) - end - } - self >> step - end - def build(cns, factory_method = :new, &block) self >> Build.new(cns, factory_method:, &block) end def pipeline(&block) Pipeline.new(self, &block) end def to_s inspect + end + + # Build a step that will invoke onr or more methods on the value. + # Ex 1: Types::String.invoke(:downcase) + # Ex 2: Types::Array.invoke(:[], 1) + # Ex 3 chain of methods: Types::String.invoke([:downcase, :to_sym]) + # @return [Step] + def invoke(*args, &block) + case args + in [::Symbol => method_name, *rest] + self >> Step.new( + ->(result) { result.valid(result.value.public_send(method_name, *rest, &block)) }, + [method_name.inspect, rest.inspect].join(' ') + ) + in [Array => methods] if methods.all? { |m| m.is_a?(Symbol) } + methods.reduce(self) { |step, method| step.invoke(method) } + else + raise ArgumentError, "expected a symbol or array of symbols, got #{args.inspect}" + end end end end require 'plumb/deferred'