class BloodContracts::Core::AndThen

Meta refinement type, represents pipe of several refinement types

Attributes

names[R]

List of data transformation step names

@return [Array<Symbol>]

steps[R]

List of data transformation step

@return [Array<Refined>]

Public Class Methods

>(other_type, **kwargs)

rubocop:enable Style/CaseEquality Style/SingleLineMethods

Alias for: and_then
and_then(other_type, **kwargs) click to toggle source

Compose types in a Pipe check Pipe passes data from type to type sequentially

@return [BC::Pipe]

rubocop:disable Style/CaseEquality

# File lib/blood_contracts/core/pipe.rb, line 36
def and_then(other_type, **kwargs)
  raise ArgumentError unless Class === other_type
  pipe = Class.new(self) { def inspect; super; end }
  finalize!(pipe, [self, other_type], kwargs[:names].to_a)
  pipe
end
Also aliased as: >
inspect() click to toggle source
Calls superclass method
# File lib/blood_contracts/core/pipe.rb, line 24
def inspect; super; end
new(*args, **kwargs, &block) click to toggle source

rubocop:disable Style/SingleLineMethods

Calls superclass method
# File lib/blood_contracts/core/pipe.rb, line 18
def new(*args, **kwargs, &block)
  return super(*args, **kwargs) if @finalized
  names = kwargs.delete(:names) unless kwargs.empty?
  names ||= []

  raise ArgumentError unless args.all? { |type| type < Refined }
  pipe = Class.new(self) { def inspect; super; end }
  finalize!(pipe, args, names)
  pipe.class_eval(&block) if block_given?
  pipe
end
new(*) click to toggle source

Constructs a Pipe using the given value (for Pipe steps are also stored in the context)

@return [Pipe]

Calls superclass method
# File lib/blood_contracts/core/pipe.rb, line 80
def initialize(*)
  super
  @context[:steps] = @context[:steps].to_a
  @context[:steps_values] = {}
end
step(name, type) click to toggle source

Helper which registers step in validation pipe, also defines a reader

@param [Symbol] name of the matching step @param [Refined] type of the matching step

# File lib/blood_contracts/core/pipe.rb, line 50
def step(name, type)
  raise ArgumentError unless type < Refined
  @steps << type
  @names << name
  define_method(name) do
    match.context.dig(:steps_values, name)
  end
end

Private Class Methods

finalize!(new_class, steps, names) click to toggle source
# File lib/blood_contracts/core/pipe.rb, line 68
        def finalize!(new_class, steps, names)
  new_class.instance_variable_set(:@steps, steps)
  new_class.instance_variable_set(:@names, names)
  new_class.instance_variable_set(:@finalized, true)
end

Public Instance Methods

errors() click to toggle source

List of errors per type during the matching

@return [Array<Hash<Refined, String>>]

# File lib/blood_contracts/core/pipe.rb, line 108
def errors
  @context[:errors]
end
match() { |match| ... } click to toggle source

The type which is the result of data matching process For PIpe it verifies that data is valid through all data transformation steps

@return [BC::Refined]

# File lib/blood_contracts/core/pipe.rb, line 92
def match
  steps_enumerator.reduce(value) do |next_value, (step, index)|
    match = next_step_value_match!(step, next_value, index)

    break match if match.invalid?
    next match unless block_given?
    next refine_value(yield(match)) if index < self.class.steps.size - 1

    match
  end
end

Private Instance Methods

current_step() click to toggle source

@private

# File lib/blood_contracts/core/pipe.rb, line 137
        def current_step
  @context[:steps].last
end
inspect() click to toggle source

@private

# File lib/blood_contracts/core/pipe.rb, line 157
        def inspect
  "#<pipe #{self.class.name} = #{steps_with_names.join(' > ')}"\
  " (value=#{@value})>"
end
next_step_value_match!(step, value, index) click to toggle source

@private

# File lib/blood_contracts/core/pipe.rb, line 113
        def next_step_value_match!(step, value, index)
  unpacked_value = unpack_refined(value)
  match = step.match(unpacked_value, context: @context)
  track_steps!(index, unpacked_value, match.class.name)
  match
end
step_name(index) click to toggle source

@private

# File lib/blood_contracts/core/pipe.rb, line 142
        def step_name(index)
  self.class.names[index] || index
end
steps() click to toggle source

@private

# File lib/blood_contracts/core/pipe.rb, line 132
        def steps
  @context[:steps]
end
steps_enumerator() click to toggle source

@private

# File lib/blood_contracts/core/pipe.rb, line 121
        def steps_enumerator
  self.class.steps.each_with_index
end
steps_with_names() click to toggle source

@private

# File lib/blood_contracts/core/pipe.rb, line 147
        def steps_with_names
  steps = self.class.steps
  if self.class.names.empty?
    steps.map(&:inspect)
  else
    steps.zip(self.class.names).map { |k, n| "#{k.inspect}(#{n})" }
  end
end
track_steps!(index, unpacked_value, match_class_name) click to toggle source

@private

# File lib/blood_contracts/core/pipe.rb, line 126
        def track_steps!(index, unpacked_value, match_class_name)
  @context[:steps_values][step_name(index)] = unpacked_value
  steps << match_class_name unless current_step.eql?(match_class_name)
end