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'