Class: BloodContracts::Core::Tuple

Inherits:
Refined
  • Object
show all
Defined in:
lib/blood_contracts/core/tuple.rb

Overview

Meta refinement type, represents product of several refinement types

Class Attribute Summary collapse

Instance Attribute Summary collapse

Attributes inherited from Refined

#context, #errors

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Refined

===, and_then, call, #invalid?, match, or_a, #unpack, #valid?

Constructor Details

#initialize(*values, context: {}) ⇒ Tuple

Tuple constructor, builds Tuple from list of data values



125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/blood_contracts/core/tuple.rb', line 125

def initialize(*values, context: {}, **)
  @context = context
  @context[:attributes] ||= {}

  additional_context = values.last if values.last.is_a?(Hash)
  additional_context ||= {}

  @values = parse_values_from_context(context.merge(additional_context))
  @values ||= values

  @errors = []
end

Class Attribute Details

.attributesArray<Refined> (readonly)

List of types in the Tuple



12
13
14
# File 'lib/blood_contracts/core/tuple.rb', line 12

def attributes
  @attributes
end

.failure_klassContractFailure

Accessor to define alternative to ContractFailure for #failure method to use



63
64
65
# File 'lib/blood_contracts/core/tuple.rb', line 63

def failure_klass
  @failure_klass
end

.namesArray<Symbol> (readonly)

Names of attributes



18
19
20
# File 'lib/blood_contracts/core/tuple.rb', line 18

def names
  @names
end

Instance Attribute Details

#valuesArray<Object> (readonly)

List of values in Tuple



118
119
120
# File 'lib/blood_contracts/core/tuple.rb', line 118

def values
  @values
end

Class Method Details

.attribute(name, type = nil, &block) ⇒ Object

Helper which registers attribute in the Tuple, also defines a reader



43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/blood_contracts/core/tuple.rb', line 43

def attribute(name, type = nil, &block)
  if type.nil?
    type = type_from_block(name, &block)
  else
    raise ArgumentError unless type < Refined
  end

  @attributes << type
  @names << name

  define_method(name) do
    match.context.dig(:attributes, name)
  end
end

.inherited(new_klass) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/blood_contracts/core/tuple.rb', line 64

def inherited(new_klass)
  new_klass.instance_variable_set(:@attributes, [])
  new_klass.instance_variable_set(:@names, [])
  new_klass.instance_variable_set(:@finalized, true)
  new_klass.failure_klass ||= TupleContractFailure
  super
end

.new(*args, **kwargs, &block) ⇒ Object

Metaprogramming around constructor Turns input into Tuple meta-class

rubocop:disable Style/SingleLineMethods

Raises:

  • (ArgumentError)


26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/blood_contracts/core/tuple.rb', line 26

def new(*args, **kwargs, &block)
  args = lookup_hash_args(*args, kwargs)

  return super(*args, **kwargs) if @finalized
  names = args.pop.delete(:names) if args.last.is_a?(Hash)

  raise ArgumentError unless args.all? { |type| type < Refined }
  tuple = Class.new(self) { def inspect; super; end }
  tuple.instance_variable_set(:@attributes, args)
  tuple.instance_variable_set(:@names, names.to_a)
  tuple.instance_variable_set(:@finalized, true)
  tuple.class_eval(&block) if block_given?
  tuple
end

Instance Method Details

#attribute_errorsHash<String, ContractFailure>

Subset of attributes which are invalid



191
192
193
# File 'lib/blood_contracts/core/tuple.rb', line 191

def attribute_errors
  {}
end

#attributesHash<String, Refined>

Hash of attributes (name & type pairs)



183
184
185
# File 'lib/blood_contracts/core/tuple.rb', line 183

def attributes
  @context[:attributes]
end

#mappedArray<Object>

Turns match into array of unpacked values



158
159
160
# File 'lib/blood_contracts/core/tuple.rb', line 158

def mapped
  @matches.map(&:unpack)
end

#matchBC::Refined

The type which is the result of data matching process For Tuple it verifies that all the attributes data are valid types



143
144
145
146
147
148
149
150
151
152
# File 'lib/blood_contracts/core/tuple.rb', line 143

def match
  @matches = attributes_enumerator.map do |(type, value), index|
    attribute_name = self.class.names[index]
    attributes.store(
      attribute_name, type.match(value, context: @context.dup)
    )
  end
  return if (failures = @matches.select(&:invalid?)).empty?
  failures.unshift(failure).reduce(:merge!)
end

#unpack_hHash<String, ContractFailure> Also known as: to_hash, to_h, unpack_attributes

Unpacked value in form of a hash per attribute



172
173
174
# File 'lib/blood_contracts/core/tuple.rb', line 172

def unpack_h
  @unpack_h ||= attributes.transform_values(&:unpack)
end