class NxtSchema::Template::Base

Attributes

additional_keys_strategy[RW]
configuration[R]
context[RW]
key_transformer[R]
maybe_evaluators[RW]
meta[RW]
name[RW]
on_evaluators[RW]
options[RW]
parent_node[RW]
path[RW]
root_node[RW]
type[RW]
type_system[R]
validations[R]

Public Class Methods

new(name:, type:, parent_node:, **options, &block) click to toggle source
# File lib/nxt_schema/template/base.rb, line 4
def initialize(name:, type:, parent_node:, **options, &block)
  resolve_name(name)

  @parent_node = parent_node
  @options = options
  @is_root_node = parent_node.nil?
  @root_node = parent_node.nil? ? self : parent_node.root_node
  @path = resolve_path
  @on_evaluators = []
  @maybe_evaluators = []
  @validations = []
  @configuration = block

  resolve_key_transformer
  resolve_context
  resolve_optional_option
  resolve_omnipresent_option
  resolve_type_system
  resolve_type(type)
  resolve_additional_keys_strategy
  node_class # memoize
  configure(&block) if block_given?
end

Public Instance Methods

apply(input: Undefined.new, context: self.context, parent: nil, error_key: nil) click to toggle source
# File lib/nxt_schema/template/base.rb, line 45
def apply(input: Undefined.new, context: self.context, parent: nil, error_key: nil)
  build_node(input: input, context: context, parent: parent, error_key: error_key).call
end
apply!(input: Undefined.new, context: self.context, parent: nil, error_key: nil) click to toggle source
# File lib/nxt_schema/template/base.rb, line 49
def apply!(input: Undefined.new, context: self.context, parent: nil, error_key: nil)
  result = build_node(input: input, context: context, parent: parent, error_key: error_key).call
  return result if parent

  raise NxtSchema::Errors::Invalid.new(result) if result.errors.any?

  result.output
end
build_node(input: Undefined.new, context: self.context, parent: nil, error_key: nil) click to toggle source
# File lib/nxt_schema/template/base.rb, line 58
def build_node(input: Undefined.new, context: self.context, parent: nil, error_key: nil)
  node_class.new(
    node: self,
    input: input,
    parent: parent,
    context: context,
    error_key: error_key
  )
end
default(value = NxtSchema::Undefined.new, &block) click to toggle source
# File lib/nxt_schema/template/base.rb, line 80
def default(value = NxtSchema::Undefined.new, &block)
  value = missing_input?(value) ? block : value
  condition = ->(input) { missing_input?(input) || input.nil? }
  on(condition, value)

  self
end
maybe(value = NxtSchema::Undefined.new, &block) click to toggle source
# File lib/nxt_schema/template/base.rb, line 95
def maybe(value = NxtSchema::Undefined.new, &block)
  value = missing_input?(value) ? block : value
  maybe_evaluators << MaybeEvaluator.new(value: value)

  self
end
omnipresent?() click to toggle source
# File lib/nxt_schema/template/base.rb, line 76
def omnipresent?
  @omnipresent
end
on(condition, value = NxtSchema::Undefined.new, &block) click to toggle source
# File lib/nxt_schema/template/base.rb, line 88
def on(condition, value = NxtSchema::Undefined.new, &block)
  value = missing_input?(value) ? block : value
  on_evaluators << OnEvaluator.new(condition: condition, value: value)

  self
end
optional?() click to toggle source
# File lib/nxt_schema/template/base.rb, line 72
def optional?
  @optional
end
root_node?() click to toggle source
# File lib/nxt_schema/template/base.rb, line 68
def root_node?
  @is_root_node
end
validate(key = NxtSchema::Undefined.new, *args, &block) click to toggle source
# File lib/nxt_schema/template/base.rb, line 102
def validate(key = NxtSchema::Undefined.new, *args, &block)
  # TODO: This does not really work with all kinds of chaining combinations yet!

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

  register_validator(validator)

  self
end
validate_with(&block) click to toggle source
# File lib/nxt_schema/template/base.rb, line 124
def validate_with(&block)
  proxy = ->(node) { NxtSchema::Validator::ValidateWithProxy.new(node).validate(&block) }
  register_validator(proxy)
end

Private Instance Methods

configure(&block) click to toggle source
# File lib/nxt_schema/template/base.rb, line 159
def configure(&block)
  if block.arity == 1
    block.call(self)
  else
    instance_exec(&block)
  end
end
missing_input?(value) click to toggle source
# File lib/nxt_schema/template/base.rb, line 205
def missing_input?(value)
  value.is_a? Undefined
end
node_class() click to toggle source
# File lib/nxt_schema/template/base.rb, line 155
def node_class
  @node_class ||= "NxtSchema::Node::#{self.class.name.demodulize}".constantize
end
register_validator(validator) click to toggle source
# File lib/nxt_schema/template/base.rb, line 137
def register_validator(validator)
  validations << validator
end
resolve_additional_keys_strategy() click to toggle source
# File lib/nxt_schema/template/base.rb, line 167
def resolve_additional_keys_strategy
  @additional_keys_strategy = options.fetch(:additional_keys) do
    parent_node&.send(:additional_keys_strategy) || :allow
  end
end
resolve_context() click to toggle source
# File lib/nxt_schema/template/base.rb, line 201
def resolve_context
  self.context = options.fetch(:context) { parent_node&.send(:context) }
end
resolve_key_transformer() click to toggle source
# File lib/nxt_schema/template/base.rb, line 209
def resolve_key_transformer
  @key_transformer = options.fetch(:transform_keys) { parent_node&.key_transformer || ->(key) { key.to_sym } }
end
resolve_name(name) click to toggle source
# File lib/nxt_schema/template/base.rb, line 213
def resolve_name(name)
  raise ArgumentError, 'Name can either be a symbol or an integer' unless name.class.in?([Symbol, Integer])

  @name = name
end
resolve_omnipresent_option() click to toggle source
# File lib/nxt_schema/template/base.rb, line 189
def resolve_omnipresent_option
  omnipresent = options.fetch(:omnipresent, false)
  raise Errors::InvalidOptions, 'Omnipresent nodes are only available within schemas' if omnipresent && !parent_node.is_a?(Schema)
  raise Errors::InvalidOptions, "Can't make omnipresent node optional" if optional? && omnipresent

  @omnipresent = omnipresent
end
resolve_optional_option() click to toggle source
# File lib/nxt_schema/template/base.rb, line 173
def resolve_optional_option
  optional = options.fetch(:optional, false)
  raise Errors::InvalidOptions, 'Optional nodes are only available within schemas' if optional && !parent_node.is_a?(Schema)
  raise Errors::InvalidOptions, "Can't make omnipresent node optional" if optional && omnipresent?

  if optional.respond_to?(:call)
    # When a node is conditionally optional we make it optional and add a validator to the parent to check
    # that it's there when the option does not apply.
    optional_node_validator = validator(:optional_node, optional, name)
    parent_node.send(:register_validator, optional_node_validator)
    @optional = true
  else
    @optional = optional
  end
end
resolve_path() click to toggle source
# File lib/nxt_schema/template/base.rb, line 197
def resolve_path
  self.path = root_node? ? name : "#{parent_node.path}.#{name}"
end
resolve_type(name_or_type) click to toggle source
# File lib/nxt_schema/template/base.rb, line 141
def resolve_type(name_or_type)
  @type = root_node.send(:type_resolver).resolve(type_system, name_or_type)
end
resolve_type_system() click to toggle source
# File lib/nxt_schema/template/base.rb, line 145
def resolve_type_system
  @type_system = TypeSystemResolver.new(node: self).call
end
type_resolver() click to toggle source
# File lib/nxt_schema/template/base.rb, line 149
def type_resolver
  @type_resolver ||= begin
    root_node? ? TypeResolver.new : (raise NoMethodError, 'type_resolver is only available on root node')
  end
end
validator(key, *args) click to toggle source
# File lib/nxt_schema/template/base.rb, line 133
def validator(key, *args)
  Validators::REGISTRY.resolve!(key).new(*args).build
end