Class: Karafka::Core::Contractable::Contract

Inherits:
Object
  • Object
show all
Extended by:
Karafka::Core::Configurable
Defined in:
lib/karafka/core/contractable/contract.rb

Overview

Note:

This contract does NOT support rules inheritance as it was never needed in Karafka

Base contract for all the contracts that check data format

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Karafka::Core::Configurable

extended, included

Class Attribute Details

.rulesArray<Rule> (readonly)

Returns all the validation rules defined for a given contract.

Returns:

  • (Array<Rule>)

    all the validation rules defined for a given contract



30
31
32
# File 'lib/karafka/core/contractable/contract.rb', line 30

def rules
  @rules
end

Class Method Details

.nested(path) ⇒ Object

Allows for definition of a scope/namespace for nested validations

Examples:

nested(:key) do
  required(:inside) { |inside| inside.is_a?(String) }
end

Parameters:

  • path (Symbol)

    path in the hash for nesting



40
41
42
43
44
45
# File 'lib/karafka/core/contractable/contract.rb', line 40

def nested(path, &)
  init_accu
  @nested << path
  instance_eval(&)
  @nested.pop
end

.optional(*keys, &block) ⇒ Object

Parameters:

  • keys (Array<Symbol>)

    single or full path

  • block (Proc)

    validation rule



59
60
61
62
# File 'lib/karafka/core/contractable/contract.rb', line 59

def optional(*keys, &block)
  init_accu
  @rules << Rule.new(@nested + keys, :optional, block).freeze
end

.required(*keys, &block) ⇒ Object

Defines a rule for a required field (required means, that will automatically create an error if missing)

Parameters:

  • keys (Array<Symbol>)

    single or full path

  • block (Proc)

    validation rule



52
53
54
55
# File 'lib/karafka/core/contractable/contract.rb', line 52

def required(*keys, &block)
  init_accu
  @rules << Rule.new(@nested + keys, :required, block).freeze
end

.virtual(&block) ⇒ Object

Note:

Virtual rules have different result expectations. Please see contracts or specs for details.

Parameters:

  • block (Proc)

    validation rule



68
69
70
71
# File 'lib/karafka/core/contractable/contract.rb', line 68

def virtual(&block)
  init_accu
  @rules << Rule.new([], :virtual, block).freeze
end

Instance Method Details

#call(data, scope: EMPTY_ARRAY) ⇒ Result

Runs the validation

The per-rule handling is inlined instead of dispatching to per-type methods because this runs per rule per validation, including the per-message validations in WaterDrop. Required and optional rules share the whole flow except the missing-key handling. ‘DIG_MISS` is compared via `#equal?` so we never dispatch `#==` to the validated (user-provided) values.

Parameters:

  • data (Hash)

    hash with data we want to validate

  • scope (Array<String>) (defaults to: EMPTY_ARRAY)

    scope of this contract (if any) or empty array if no parent scope is needed if contract starts from root

Returns:

  • (Result)

    validaton result



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
120
121
122
123
124
125
126
# File 'lib/karafka/core/contractable/contract.rb', line 94

def call(data, scope: EMPTY_ARRAY)
  errors = []

  self.class.rules.each do |rule|
    if rule.type == :virtual
      result = rule.validator.call(data, errors, self)

      next if result == true

      result&.each do |sub_result|
        sub_result[0] = scope + sub_result[0]
      end

      errors.push(*result)
    else
      for_checking = dig(data, rule.path)

      if DIG_MISS.equal?(for_checking)
        errors << [scope + rule.path, :missing] if rule.type == :required
      else
        result = rule.validator.call(for_checking, data, errors, self)

        next if result == true

        errors << [scope + rule.path, result || :format]
      end
    end
  end

  return Result.success if errors.empty?

  Result.new(errors, self)
end

#validate!(data, error_class, scope: EMPTY_ARRAY) ⇒ Boolean

Returns true.

Parameters:

  • data (Hash)

    data for validation

  • error_class (Class)

    error class that should be used when validation fails

  • scope (Array<String>) (defaults to: EMPTY_ARRAY)

    scope of this contract (if any) or empty array if no parent scope is needed if contract starts from root

Returns:

  • (Boolean)

    true

Raises:

  • (StandardError)

    any error provided in the error_class that inherits from the standard error



135
136
137
138
139
140
141
# File 'lib/karafka/core/contractable/contract.rb', line 135

def validate!(data, error_class, scope: EMPTY_ARRAY)
  result = call(data, scope: scope)

  return true if result.success?

  raise error_class, result.errors
end