Class: NxtSchema::Node::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/nxt_schema/node/base.rb

Direct Known Subclasses

Collection, Leaf, Schema

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name: name_from_index, type:, parent_node:, **options, &block) ⇒ Base

Returns a new instance of Base.



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/nxt_schema/node/base.rb', line 4

def initialize(name: name_from_index, type:, parent_node:, **options, &block)
  @name = name
  @parent_node = parent_node
  @options = options
  @type_system = resolve_type_system
  @additional_keys_strategy = resolve_additional_keys_strategy
  @type = type
  @schema_errors_key = options.fetch(:schema_errors_key, :itself)
  @validations = []
  @level = parent_node ? parent_node.level + 1 : 0
  @all_nodes = parent_node ? (parent_node.all_nodes || {}) : {}
  @is_root = parent_node.nil?
  @root = parent_node.nil? ? self : parent_node.root
  @errors = {}
  @context = nil
  @applied = false
  @input = nil
  @value = NxtSchema::Undefined.new
  @locale = options.fetch(:locale) { parent_node&.locale || 'en' }

  # Note that it is not possible to use present? on an instance of NxtSchema::Schema since it inherits from Hash
  evaluate_block(block) if block_given?
end

Instance Attribute Details

#additional_keys_strategyObject

Returns the value of attribute additional_keys_strategy.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def additional_keys_strategy
  @additional_keys_strategy
end

#all_nodesObject

Returns the value of attribute all_nodes.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def all_nodes
  @all_nodes
end

#appliedObject

Returns the value of attribute applied.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def applied
  @applied
end

#contextObject

Returns the value of attribute context.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def context
  @context
end

#errorsObject

Returns the value of attribute errors.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def errors
  @errors
end

#inputObject

Returns the value of attribute input.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def input
  @input
end

#levelObject

Returns the value of attribute level.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def level
  @level
end

#localeObject

Returns the value of attribute locale.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def locale
  @locale
end

#nameObject

Returns the value of attribute name.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def name
  @name
end

#namespaceObject

Returns the value of attribute namespace.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def namespace
  @namespace
end

#optionsObject

Returns the value of attribute options.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def options
  @options
end

#parent_nodeObject

Returns the value of attribute parent_node.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def parent_node
  @parent_node
end

#rootObject

Returns the value of attribute root.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def root
  @root
end

#schema_errorsObject

Returns the value of attribute schema_errors.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def schema_errors
  @schema_errors
end

#schema_errors_keyObject

Returns the value of attribute schema_errors_key.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def schema_errors_key
  @schema_errors_key
end

#typeObject

Returns the value of attribute type.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def type
  @type
end

#type_systemObject Also known as: types

Returns the value of attribute type_system.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def type_system
  @type_system
end

#validation_errorsObject

Returns the value of attribute validation_errors.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def validation_errors
  @validation_errors
end

#validationsObject

Returns the value of attribute validations.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def validations
  @validations
end

#valueObject

Returns the value of attribute value.



28
29
30
# File 'lib/nxt_schema/node/base.rb', line 28

def value
  @value
end

Instance Method Details

#add_error(error, index = schema_errors_key) ⇒ Object



127
128
129
130
131
# File 'lib/nxt_schema/node/base.rb', line 127

def add_error(error, index = schema_errors_key)
  validation_errors[index] ||= []
  validation_errors[index] << error
  false
end

#add_validators(validator) ⇒ Object



198
199
200
201
202
# File 'lib/nxt_schema/node/base.rb', line 198

def add_validators(validator)
  options[:validate] ||= []
  options[:validate] = Array(options.fetch(:validate, []))
  options[:validate] << validator
end

#apply_validationsObject



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/nxt_schema/node/base.rb', line 142

def apply_validations
  # We don't run validations in case there are schema errors
  # to avoid weird errors
  # First reject empty schema_errors
  schema_errors.reject! { |_, v| v.empty? }

  # TODO: Is this correct? - Do not apply validations when maybe criteria applies?
  unless schema_errors[schema_errors_key]&.any? && !maybe_criteria_applies?(value)
    build_validations

    validations.each do |validation|
      args = [self, value]
      validation.call(*args.take(validation.arity))
    end
  end

  if self.is_a?(NxtSchema::Node::Collection) && value.respond_to?(:each)
    value.each_with_index do |item, index|
      validation_errors[index]&.reject! { |_, v| v.empty? }
    end
  end

  validation_errors.reject! { |_, v| v.empty? }

  self
end

#build_validationsObject



169
170
171
172
# File 'lib/nxt_schema/node/base.rb', line 169

def build_validations
  validations_from_options = Array(options.fetch(:validate, []))
  self.validations = validations_from_options
end

#default(default_value, &block) ⇒ Object



58
59
60
61
62
# File 'lib/nxt_schema/node/base.rb', line 58

def default(default_value, &block)
  options.merge!(default: default_value)
  evaluate_block(block) if block_given?
  self
end

#leaf?Boolean

Returns:

  • (Boolean)


188
189
190
# File 'lib/nxt_schema/node/base.rb', line 188

def leaf?
  false
end

#maybe(maybe_value, &block) ⇒ Object



81
82
83
84
85
# File 'lib/nxt_schema/node/base.rb', line 81

def maybe(maybe_value, &block)
  options.merge!(maybe: maybe_value)
  evaluate_block(block) if block_given?
  self
end

#meta(value = NxtSchema::Undefined.new) ⇒ Object



64
65
66
67
68
69
70
71
# File 'lib/nxt_schema/node/base.rb', line 64

def meta(value = NxtSchema::Undefined.new)
  if value.is_a?(NxtSchema::Undefined)
    @meta
  else
    @meta = value
    self
  end
end

#optional(optional_value = nil, &block) ⇒ Object

Raises:

  • (ArgumentError)


87
88
89
90
91
92
# File 'lib/nxt_schema/node/base.rb', line 87

def optional(optional_value = nil, &block)
  raise ArgumentError, 'Optional nodes can only exist within schemas' unless parent.is_a?(NxtSchema::Node::Schema)

  options.merge!(optional: optional_value || block)
  self
end

#parent(level = 1) ⇒ Object Also known as: up



52
53
54
# File 'lib/nxt_schema/node/base.rb', line 52

def parent(level = 1)
  level.times.inject(self) { |acc| acc.parent_node }
end

#presence(presence_value = nil, &block) ⇒ Object

Raises:

  • (ArgumentError)


94
95
96
97
98
99
# File 'lib/nxt_schema/node/base.rb', line 94

def presence(presence_value = nil, &block)
  raise ArgumentError, 'Present nodes can only exist within schemas' unless parent.is_a?(NxtSchema::Node::Schema)

  options.merge!(presence: presence_value || block)
  self
end

#presence?Boolean

Returns:

  • (Boolean)


101
102
103
104
105
106
107
108
109
110
111
# File 'lib/nxt_schema/node/base.rb', line 101

def presence?
  @presence ||= begin
    presence_option = options[:presence]

    options[:presence] = if presence_option.respond_to?(:call)
      Callable.new(presence_option).call(self, value)
    else
      presence_option
    end
  end
end

#root?Boolean

Returns:

  • (Boolean)


184
185
186
# File 'lib/nxt_schema/node/base.rb', line 184

def root?
  @is_root
end

#schema_errors?Boolean

Returns:

  • (Boolean)


174
175
176
177
# File 'lib/nxt_schema/node/base.rb', line 174

def schema_errors?
  schema_errors.reject! { |_, v| v.empty? }
  schema_errors.any?
end

#valid?Boolean

Returns:

  • (Boolean)

Raises:

  • (SchemaNotAppliedError)


192
193
194
195
196
# File 'lib/nxt_schema/node/base.rb', line 192

def valid?
  raise SchemaNotAppliedError, 'Schema was not applied yet' unless applied?

  validation_errors.empty?
end

#validate(key, *args, &block) ⇒ Object



113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/nxt_schema/node/base.rb', line 113

def validate(key, *args, &block)
  if key.is_a?(Symbol)
    validator = validator(key, *args)
  elsif key.respond_to?(:call)
    validator = key
  else
    raise ArgumentError, "Don't know how to resolve validator from: #{key}"
  end

  add_validators(validator)
  evaluate_block(block) if block_given?
  self
end

#validate_all_nodesObject



133
134
135
136
137
138
139
140
# File 'lib/nxt_schema/node/base.rb', line 133

def validate_all_nodes
  sorted_nodes = all_nodes.values.sort do |node, other_node|
    [node.level, (!node.leaf?).to_s] <=> [other_node.level, (!other_node.leaf?).to_s]
  end

  # we have to start from the bottom, leafs before others on the same level
  sorted_nodes.reverse_each(&:apply_validations)
end

#validate_with(&block) ⇒ Object



208
209
210
211
212
# File 'lib/nxt_schema/node/base.rb', line 208

def validate_with(&block)
  add_validators(
    ->(node) { NxtSchema::Node::ValidateWithProxy.new(node).validate(&block) }
  )
end

#validation_errors?Boolean

Returns:

  • (Boolean)


179
180
181
182
# File 'lib/nxt_schema/node/base.rb', line 179

def validation_errors?
  validation_errors.reject! { |_, v| v.empty? }
  validation_errors.any?
end

#validator(key, *args) ⇒ Object



204
205
206
# File 'lib/nxt_schema/node/base.rb', line 204

def validator(key, *args)
  Validators::Registry::VALIDATORS.resolve(key).new(*args).build
end

#value_or_default_value(value) ⇒ Object



73
74
75
76
77
78
79
# File 'lib/nxt_schema/node/base.rb', line 73

def value_or_default_value(value)
  if !value && options.key?(:default)
    DefaultValueEvaluator.new(self, options.fetch(:default)).call
  else
    value
  end
end