class BloodContracts::Core::AndThen
Meta refinement type, represents pipe of several refinement types
Attributes
List of data transformation step names
@return [Array<Symbol>]
List of data transformation step
@return [Array<Refined>]
Public Class Methods
rubocop:enable Style/CaseEquality Style/SingleLineMethods
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
# File lib/blood_contracts/core/pipe.rb, line 24 def inspect; super; end
rubocop:disable Style/SingleLineMethods
# 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
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
# 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
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
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
@private
# File lib/blood_contracts/core/pipe.rb, line 137 def current_step @context[:steps].last end
@private
# File lib/blood_contracts/core/pipe.rb, line 157 def inspect "#<pipe #{self.class.name} = #{steps_with_names.join(' > ')}"\ " (value=#{@value})>" end
@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
@private
# File lib/blood_contracts/core/pipe.rb, line 142 def step_name(index) self.class.names[index] || index end
@private
# File lib/blood_contracts/core/pipe.rb, line 132 def steps @context[:steps] end
@private
# File lib/blood_contracts/core/pipe.rb, line 121 def steps_enumerator self.class.steps.each_with_index end
@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
@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