lib/nxt_schema/node/schema.rb in nxt_schema-1.0.0 vs lib/nxt_schema/node/schema.rb in nxt_schema-1.0.1
- old
+ new
@@ -1,21 +1,114 @@
module NxtSchema
module Node
class Schema < Node::Base
- include HasSubNodes
+ def call
+ apply_on_evaluators
+ child_applications # build applications here so we can access them even when invalid
+ return self if maybe_evaluator_applies?
- DEFAULT_TYPE = NxtSchema::Types::Strict::Hash
+ coerce_input
+ return self unless valid?
- def initialize(name:, type: DEFAULT_TYPE, parent_node:, **options, &block)
- super
+ flag_missing_keys
+ apply_additional_keys_strategy
+
+ child_applications.each do |key, child|
+ current_application = child.call
+
+ if !current_application.valid?
+ merge_errors(current_application)
+ else
+ output[key] = current_application.output
+ end
+ end
+
+ transform_keys
+ register_as_coerced_when_no_errors
+ run_validations
+ self
end
- def optional(name, node_or_type_of_node, **options, &block)
- node(name, node_or_type_of_node, **options.merge(optional: true), &block)
+ delegate :[], to: :child_applications
+
+ private
+
+ def transform_keys
+ transformer = node.key_transformer
+ return unless transformer && output.respond_to?(:transform_keys!)
+
+ output.transform_keys!(&transformer)
end
- def omnipresent(name, node_or_type_of_node, **options, &block)
- node(name, node_or_type_of_node, **options.merge(omnipresent: true), &block)
+ def keys
+ @keys ||= node.sub_nodes.reject { |key, _| optional_and_not_given_key?(key) }.keys
+ end
+
+ def additional_keys
+ @additional_keys ||= input.keys - keys
+ end
+
+ def optional_and_not_given_key?(key)
+ node.sub_nodes[key].optional? && !input.key?(key)
+ end
+
+ def additional_keys?
+ additional_keys.any?
+ end
+
+ def missing_keys
+ @missing_keys ||= node.sub_nodes.reject { |_, node| node.omnipresent? || node.optional? }.keys - input.keys
+ end
+
+ def apply_additional_keys_strategy
+ return if allow_additional_keys?
+ return unless additional_keys?
+
+ if restrict_additional_keys?
+ add_schema_error("Additional keys are not allowed: #{additional_keys}")
+ elsif reject_additional_keys?
+ self.output = output.except(*additional_keys)
+ end
+ end
+
+ def flag_missing_keys
+ return if missing_keys.empty?
+
+ add_schema_error("The following keys are missing: #{missing_keys}")
+ end
+
+ def allow_additional_keys?
+ node.additional_keys_strategy == :allow
+ end
+
+ def reject_additional_keys?
+ node.additional_keys_strategy == :reject
+ end
+
+ def restrict_additional_keys?
+ node.additional_keys_strategy == :restrict
+ end
+
+ def child_applications
+ @child_applications ||= begin
+ keys.inject({}) do |acc, key|
+ child_application = build_child_application(key)
+ acc[key] = child_application if child_application.present?
+ acc
+ end
+ end
+ end
+
+ def build_child_application(key)
+ sub_node = node.sub_nodes[key]
+ return unless sub_node.present?
+
+ value = input_has_key?(input, key) ? input[key] : MissingInput.new
+ sub_node.build_application(input: value, context: context, parent: self)
+ end
+
+ def input_has_key?(input, key)
+ input.respond_to?(:key?) && input.key?(key)
end
end
end
end