Class: Ree::Contracts::ArgContracts::Ksplat

Inherits:
Object
  • Object
show all
Includes:
Truncatable
Defined in:
lib/ree/contracts/arg_contracts/ksplat.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Truncatable

#truncate

Constructor Details

#initialize(**contracts) ⇒ Ksplat

Returns a new instance of Ksplat.



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/ree/contracts/arg_contracts/ksplat.rb', line 22

def initialize(**contracts)
  @contracts = contracts
  @opt_dict = Set.new

  @validators = contracts
    .transform_values { Validators.fetch_for(_1) }
    .transform_keys { |key|
      next key unless key.is_a?(String) || key.is_a?(Symbol)

      key_str = key.to_s
      next key unless key_str.end_with?('?') && key_str.length > 1

      opt_key = key_str[0..-2]
      opt_key = opt_key.to_sym if key.is_a? Symbol
      @opt_dict << opt_key

      opt_key
    }
  
  @rest_validator = @validators[RestKeys]
  
  if @rest_validator
    @validators.default = @rest_validator
  end
end

Instance Attribute Details

#rest_validatorObject (readonly)

Returns the value of attribute rest_validator.



12
13
14
# File 'lib/ree/contracts/arg_contracts/ksplat.rb', line 12

def rest_validator
  @rest_validator
end

#validatorsObject (readonly)

Returns the value of attribute validators.



12
13
14
# File 'lib/ree/contracts/arg_contracts/ksplat.rb', line 12

def validators
  @validators
end

Class Method Details

.[](**contracts) ⇒ Object



14
15
16
17
18
19
20
# File 'lib/ree/contracts/arg_contracts/ksplat.rb', line 14

def self.[](**contracts)
  if contracts.empty?
    raise BadContractError, 'Ksplat contract should accept at least one contract'
  end

  new(**contracts)
end

Instance Method Details

#message(value, name, lvl = 1) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/ree/contracts/arg_contracts/ksplat.rb', line 77

def message(value, name, lvl = 1)
  unless value.is_a?(Hash)
    return "expected Hash, got #{value.class} => #{truncate(value.inspect)}"
  end

  if value.has_key?(RestKeys)
    return "RestKeys is a reserved key for Ksplat contract"
  end
  
  errors = []
  sps = "  " * lvl
  all_keys = (@validators.keys - [RestKeys] + value.keys).uniq

  all_keys.each do |key|
    validator = @validators[key]

    if !validator
      errors << "\n\t#{sps} - #{name}[#{key.inspect}]: unexpected"
      next
    end

    if !value.has_key?(key)
      if !optional?(key)
        errors << "\n\t#{sps} - #{name}[#{key.inspect}]: missing"
      else
        next
      end
    end
  
    val = value[key]
    next if validator.call(val)
  
    msg = validator.message(val, "#{name}[#{key.inspect}]", lvl + 1)
    errors << "\n\t#{sps} - #{name}[#{key.inspect}]: #{msg}"

    if errors.size > 3
      errors << "\n\t#{sps} - ..."
      break
    end
  end
  
  errors.join
end

#to_sObject



73
74
75
# File 'lib/ree/contracts/arg_contracts/ksplat.rb', line 73

def to_s
  "Ksplat[#{validators.map { |k, v| "#{key_to_s(k)} => #{v.to_s}" }.join(', ')}]"
end

#valid?(value) ⇒ Boolean

Returns:

  • (Boolean)


48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/ree/contracts/arg_contracts/ksplat.rb', line 48

def valid?(value)
  return false unless value.is_a?(Hash)
  return false if value.has_key?(RestKeys)
  
  is_valid = value.all? do |key, v|
    if @validators.has_key?(key)
      @validators[key].call(v)
    elsif @rest_validator
      @rest_validator.call(v)
    else
      false
    end
  end

  return false if !is_valid

  return false if @validators.detect do |key, validator|
    next if key == RestKeys || optional?(key)
    next if value.has_key?(key)
    true
  end

  true
end