Module: JSI::Schema::Elements
- Defined in:
- lib/jsi/schema/elements/numeric.rb,
lib/jsi/schema/elements.rb,
lib/jsi/schema/elements.rb,
lib/jsi/schema/elements/id.rb,
lib/jsi/schema/elements/not.rb,
lib/jsi/schema/elements/ref.rb,
lib/jsi/schema/elements/enum.rb,
lib/jsi/schema/elements/self.rb,
lib/jsi/schema/elements/type.rb,
lib/jsi/schema/elements/const.rb,
lib/jsi/schema/elements/items.rb,
lib/jsi/schema/elements/anchor.rb,
lib/jsi/schema/elements/format.rb,
lib/jsi/schema/elements/comment.rb,
lib/jsi/schema/elements/default.rb,
lib/jsi/schema/elements/numeric.rb,
lib/jsi/schema/elements/numeric.rb,
lib/jsi/schema/elements/numeric.rb,
lib/jsi/schema/elements/numeric.rb,
lib/jsi/schema/elements/pattern.rb,
lib/jsi/schema/elements/some_of.rb,
lib/jsi/schema/elements/some_of.rb,
lib/jsi/schema/elements/some_of.rb,
lib/jsi/schema/elements/xschema.rb,
lib/jsi/schema/elements/contains.rb,
lib/jsi/schema/elements/examples.rb,
lib/jsi/schema/elements/required.rb,
lib/jsi/schema/elements/info_bool.rb,
lib/jsi/schema/elements/properties.rb,
lib/jsi/schema/elements/definitions.rb,
lib/jsi/schema/elements/dynamic_ref.rb,
lib/jsi/schema/elements/info_string.rb,
lib/jsi/schema/elements/xvocabulary.rb,
lib/jsi/schema/elements/dependencies.rb,
lib/jsi/schema/elements/if_then_else.rb,
lib/jsi/schema/elements/content_schema.rb,
lib/jsi/schema/elements/items_prefixed.rb,
lib/jsi/schema/elements/property_names.rb,
lib/jsi/schema/elements/contains_minmax.rb,
lib/jsi/schema/elements/numeric_draft04.rb,
lib/jsi/schema/elements/numeric_draft04.rb,
lib/jsi/schema/elements/array_validation.rb,
lib/jsi/schema/elements/array_validation.rb,
lib/jsi/schema/elements/array_validation.rb,
lib/jsi/schema/elements/content_encoding.rb,
lib/jsi/schema/elements/dependent_schemas.rb,
lib/jsi/schema/elements/object_validation.rb,
lib/jsi/schema/elements/object_validation.rb,
lib/jsi/schema/elements/string_validation.rb,
lib/jsi/schema/elements/string_validation.rb,
lib/jsi/schema/elements/unevaluated_items.rb,
lib/jsi/schema/elements/content_media_type.rb,
lib/jsi/schema/elements/dependent_required.rb,
lib/jsi/schema/elements/unevaluated_properties.rb
Overview
module Schema::Elements
Constant Summary collapse
- ID =
element_map do |keyword: , fragment_is_anchor: | Schema::Element.new(keyword: keyword) do |element| element.add_action(:id) { cxt_yield(schema_content[keyword]) if keyword_value_str?(keyword) } element.add_action(:id_without_fragment) do next if !keyword_value_str?(keyword) id_without_fragment = Util.uri(schema_content[keyword]).merge(fragment: nil) if !id_without_fragment.empty? cxt_yield(id_without_fragment) end end if fragment_is_anchor element.add_action(:anchor) do next if !keyword_value_str?(keyword) id_fragment = Util.uri(schema_content[keyword]).fragment if id_fragment && !id_fragment.empty? cxt_yield(id_fragment) end end end end end
- NOT =
element_map do Schema::Element.new(keyword: 'not') do |element| element.add_action(:subschema) do if keyword?('not') #> This keyword's value MUST be a valid JSON Schema. cxt_yield(['not']) end end # element.add_action(:subschema) element.add_action(:validate) do if keyword?('not') # This keyword's value MUST be a valid JSON Schema. # An instance is valid against this keyword if it fails to validate successfully against the schema # defined by this keyword. not_valid = inplace_subschema_validate(['not']).valid? validate( !not_valid, 'validation.keyword.not.valid', "instance is valid against `not` schema", keyword: 'not', ) end end # element.add_action(:validate) end # Schema::Element.new end
- REF =
exclusive [Boolean]: whether to abort invocation of subsequent actions when a $ref is evaluated
element_map do |exclusive: | Schema::Element.new(keyword: '$ref') do |element| if exclusive # $ref must come before all other elements to abort evaluation element.required_before_elements { |_| true } actions_to_abort = [ :id, :id_without_fragment, :anchor, ] actions_to_abort.each do |action| element.add_action(action) { self.abort = true if keyword?('$ref') } end end resolve_ref = proc do next if !keyword_value_str?('$ref') ref = schema.schema_ref(schema_content['$ref']) resolved_schema = ref.resolve.with_dynamic_scope_from(schema) [resolved_schema, ref] end element.add_action(:inplace_applicate) do resolved_schema, ref = *instance_exec(&resolve_ref) || next inplace_schema_applicate(resolved_schema, ref: ref) if exclusive self.abort = true end end # element.add_action(:inplace_applicate) element.add_action(:validate) do resolved_schema, schema_ref = *instance_exec(&resolve_ref) || next ref_result = resolved_schema.internal_validate_instance( instance_ptr, instance_document, validate_only: validate_only, visited_refs: Util.add_visited_ref(visited_refs, schema_ref), ) inplace_results_validate( ref_result.valid?, 'validation.keyword.$ref.invalid', "instance is not valid against the schema referenced by `$ref`", keyword: '$ref', results: [ref_result], ) if exclusive self.abort = true end end # element.add_action(:validate) end # Schema::Element.new end
- ENUM =
element_map do Schema::Element.new(keyword: 'enum') do |element| element.add_action(:validate) do if keyword?('enum') value = schema_content['enum'] #> The value of this keyword MUST be an array. if value.respond_to?(:to_ary) # An instance validates successfully against this keyword if its value is equal to one of the # elements in this keyword's array value. validate( value.include?(instance), 'validation.keyword.enum.none_equal', "instance is not equal to any `enum` item", keyword: 'enum', ) end end end # element.add_action(:validate) end # Schema::Element.new end
- SELF =
element_map do Schema::Element.new do |element| element.add_action(:inplace_applicate) do inplace_schema_applicate(schema) end element.add_action(:validate) do #> boolean schemas are equivalent to the following behaviors: #> #> true: Always passes validation, as if the empty schema {} #> #> false: Always fails validation, as if the schema { "not":{} } if schema_content == false validate(false, 'validation.false_schema', "instance is not valid against `false` schema") end end end end
- TYPE =
element_map do Schema::Element.new(keyword: 'type') do |element| element.add_action(:validate) do next if !keyword?('type') value = schema_content['type'] #> The value of this keyword MUST be either a string or an array. If it is an array, elements of #> the array MUST be strings and MUST be unique. types = value.respond_to?(:to_ary) ? value : [value] matched_type = types.any? do |type| if instance_types.key?(type) instance_exec(&instance_types[type]) else false end end validate( matched_type, 'validation.keyword.type.not_match', 'instance type does not match `type` value', keyword: 'type', ) end # element.add_action(:validate) end # Schema::Element.new end
- CONST =
element_map do Schema::Element.new(keyword: 'const') do |element| element.add_action(:validate) do if keyword?('const') value = schema_content['const'] # The value of this keyword MAY be of any type, including null. # An instance validates successfully against this keyword if its value is equal to the value of # the keyword. validate( instance == value, 'validation.keyword.const.not_equal', 'instance is not equal to `const` value', keyword: 'const', ) end end # element.add_action(:validate) end # Schema::Element.new end
- ITEMS =
element_map do Schema::Element.new(keywords: %w(items additionalItems)) do |element| element.add_action(:subschema) do if keyword?('items') #> The value of "items" MUST be either a valid JSON Schema or an array of valid JSON Schemas. if keyword_value_ary?('items') schema_content['items'].each_index do |i| cxt_yield(['items', i]) end else cxt_yield(['items']) end end if keyword?('additionalItems') #> The value of "additionalItems" MUST be a valid JSON Schema. cxt_yield(['additionalItems']) end end # element.add_action(:subschema) element.add_action(:child_applicate) do if instance.respond_to?(:to_ary) if keyword?('items') && schema_content['items'].respond_to?(:to_ary) if schema_content['items'].size > token child_subschema_applicate(['items', token]) elsif keyword?('additionalItems') child_subschema_applicate(['additionalItems']) end elsif keyword?('items') child_subschema_applicate(['items']) end end # if instance.respond_to?(:to_ary) end # element.add_action(:child_applicate) element.add_action(:validate) do if instance.respond_to?(:to_ary) if keyword?('items') #> The value of "items" MUST be either a valid JSON Schema or an array of valid JSON Schemas. if schema_content['items'].respond_to?(:to_ary) #> If "items" is an array of schemas, validation succeeds if each element of the instance #> validates against the schema at the same position, if any. items_results = {} additionalItems_results = {} instance.each_index do |i| if i < schema_content['items'].size items_results[i] = child_subschema_validate(i, ['items', i]) elsif keyword?('additionalItems') additionalItems_results[i] = child_subschema_validate(i, ['additionalItems']) end end child_results_validate( items_results.each_value.all?(&:valid?), 'validation.keyword.items.array.invalid', "instance array items are not all valid against corresponding `items` schemas", keyword: 'items', child_results: items_results, instance_indexes_valid: items_results.inject({}) { |h, (i, r)| h.update({i => r.valid?}) }.freeze, ) child_results_validate( additionalItems_results.each_value.all?(&:valid?), 'validation.keyword.additionalItems.invalid', "instance array items after `items` schemas are not all valid against `additionalItems` schema", keyword: 'additionalItems', child_results: additionalItems_results, instance_indexes_valid: additionalItems_results.inject({}) { |h, (i, r)| h.update({i => r.valid?}) }.freeze, ) else #> If "items" is a schema, validation succeeds if all elements in the array successfully #> validate against that schema. items_results = {} instance.each_index do |i| items_results[i] = child_subschema_validate(i, ['items']) end child_results_validate( items_results.each_value.all?(&:valid?), 'validation.keyword.items.schema.invalid', "instance array items are not all valid against `items` schema", keyword: 'items', child_results: items_results, instance_indexes_valid: items_results.inject({}) { |h, (i, r)| h.update({i => r.valid?}) }.freeze, ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- ANCHOR =
element_map do |keyword: , actions: | Schema::Element.new(keyword: keyword) do |element| actions.each do |action| element.add_action(action) { cxt_yield(schema_content[keyword]) if keyword_value_str?(keyword) } end end end
- FORMAT =
element_map do Schema::Element.new(keyword: 'format') do |element| end # Schema::Element.new end
- COMMENT =
element_map do Schema::Element.new(keyword: '$comment') do |element| end # Schema::Element.new end
- DEFAULT =
element_map do Schema::Element.new(keyword: 'default') do |element| element.add_action(:default) { cxt_yield(schema_content['default']) if keyword?('default') } end # Schema::Element.new end
- MULTIPLE_OF =
element_map do Schema::Element.new(keyword: 'multipleOf') do |element| element.add_action(:validate) do if keyword?('multipleOf') value = schema_content['multipleOf'] # The value of "multipleOf" MUST be a number, strictly greater than 0. if value.is_a?(Numeric) && value > 0 # A numeric instance is valid only if division by this keyword's value results in an integer. if instance.is_a?(Numeric) if instance.is_a?(Integer) && value.is_a?(Integer) valid = instance % value == 0 else quotient = instance / value if quotient.finite? valid = quotient % 1.0 == 0.0 else valid = BigDecimal(instance, Float::DIG) % BigDecimal(value, Float::DIG) == 0 end end validate( valid, 'validation.keyword.multipleOf.not_multiple', 'instance is not a multiple of `multipleOf` value', keyword: 'multipleOf', ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- MAXIMUM =
element_map do Schema::Element.new(keyword: 'maximum') do |element| element.add_action(:validate) do if keyword?('maximum') value = schema_content['maximum'] # The value of "maximum" MUST be a number, representing an inclusive upper limit for a numeric instance. if value.is_a?(Numeric) # If the instance is a number, then this keyword validates only if the instance is less than or # exactly equal to "maximum". if instance.is_a?(Numeric) validate( instance <= value, 'validation.keyword.maximum.greater', "instance is greater than `maximum` value", keyword: 'maximum', ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- EXCLUSIVE_MAXIMUM =
element_map do Schema::Element.new(keyword: 'exclusiveMaximum') do |element| element.add_action(:validate) do if keyword?('exclusiveMaximum') value = schema_content['exclusiveMaximum'] # The value of "exclusiveMaximum" MUST be number, representing an exclusive upper limit for a numeric instance. if value.is_a?(Numeric) # If the instance is a number, then the instance is valid only if it has a value strictly less than # (not equal to) "exclusiveMaximum". if instance.is_a?(Numeric) validate( instance < value, 'validation.keyword.exclusiveMaximum.greater_or_equal', "instance is greater than or equal to `exclusiveMaximum` value", keyword: 'exclusiveMaximum', ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- MINIMUM =
element_map do Schema::Element.new(keyword: 'minimum') do |element| element.add_action(:validate) do if keyword?('minimum') value = schema_content['minimum'] # The value of "minimum" MUST be a number, representing an inclusive lower limit for a numeric instance. if value.is_a?(Numeric) # If the instance is a number, then this keyword validates only if the instance is greater than or # exactly equal to "minimum". if instance.is_a?(Numeric) validate( instance >= value, 'validation.keyword.minimum.less', "instance is less than `minimum` value", keyword: 'minimum', ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- EXCLUSIVE_MINIMUM =
element_map do Schema::Element.new(keyword: 'exclusiveMinimum') do |element| element.add_action(:validate) do if keyword?('exclusiveMinimum') value = schema_content['exclusiveMinimum'] # The value of "exclusiveMinimum" MUST be number, representing an exclusive lower limit for a numeric instance. if value.is_a?(Numeric) # If the instance is a number, then the instance is valid only if it has a value strictly greater # than (not equal to) "exclusiveMinimum". if instance.is_a?(Numeric) validate( instance > value, 'validation.keyword.exclusiveMaximum.less_or_equal', "instance is less than or equal to `exclusiveMinimum` value", keyword: 'exclusiveMinimum', ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- PATTERN =
element_map do Schema::Element.new(keyword: 'pattern') do |element| element.add_action(:validate) do if keyword?('pattern') value = schema_content['pattern'] # The value of this keyword MUST be a string. if value.respond_to?(:to_str) if instance.respond_to?(:to_str) begin # This string SHOULD be a valid regular expression, according to the ECMA 262 regular expression # dialect. # TODO regexp = Regexp.new(value) #> A string instance is considered valid if the regular expression matches the instance successfully. validate( regexp.match(instance), 'validation.keyword.pattern.not_match', 'instance string does not match `pattern` regular expression value', keyword: 'pattern', ) rescue RegexpError # cannot validate end end end end end # element.add_action(:validate) end # Schema::Element.new end
- ALL_OF =
element_map do Schema::Element.new(keyword: 'allOf') do |element| element.add_action(:subschema) do #> This keyword's value MUST be a non-empty array. if keyword_value_ary?('allOf') schema_content['allOf'].each_index do |i| #> Each item of the array MUST be a valid JSON Schema. cxt_yield(['allOf', i]) end end end # element.add_action(:subschema) element.add_action(:inplace_applicate) do if keyword?('allOf') && schema_content['allOf'].respond_to?(:to_ary) schema_content['allOf'].each_index do |i| inplace_subschema_applicate(['allOf', i]) end end end # element.add_action(:inplace_applicate) element.add_action(:validate) do if keyword?('allOf') value = schema_content['allOf'] # This keyword's value MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema. if value.respond_to?(:to_ary) # An instance validates successfully against this keyword if it validates successfully against all # schemas defined by this keyword's value. allOf_results = value.each_index.map do |i| inplace_subschema_validate(['allOf', i]) end inplace_results_validate( allOf_results.all?(&:valid?), 'validation.keyword.allOf.not_all_valid', "instance is not valid against all `allOf` schemas", keyword: 'allOf', results: allOf_results, allOf_indexes_valid: allOf_results.each_with_index.inject({}) { |h, (r, i)| h.update({i => r.valid?}) }.freeze, ) end end end # element.add_action(:validate) end # Schema::Element.new end
- ANY_OF =
element_map do Schema::Element.new(keyword: 'anyOf') do |element| element.add_action(:subschema) do #> This keyword's value MUST be a non-empty array. if keyword_value_ary?('anyOf') schema_content['anyOf'].each_index do |i| #> Each item of the array MUST be a valid JSON Schema. cxt_yield(['anyOf', i]) end end end # element.add_action(:subschema) element.add_action(:inplace_application_requires_instance) { cxt_yield(true) if keyword?('anyOf') } element.add_action(:inplace_applicate) do if keyword?('anyOf') && schema_content['anyOf'].respond_to?(:to_ary) anyOf = schema_content['anyOf'].each_index.map { |i| subschema(['anyOf', i]) } validOf = anyOf.select { |schema| schema.instance_valid?(instance) } if !validOf.empty? applicators = validOf else # invalid application: if none of the anyOf were valid, we apply them all applicators = anyOf end applicators.each do |applicator| inplace_schema_applicate(applicator) end end end # element.add_action(:inplace_applicate) element.add_action(:validate) do if keyword?('anyOf') value = schema_content['anyOf'] # This keyword's value MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema. if value.respond_to?(:to_ary) # An instance validates successfully against this keyword if it validates successfully against at # least one schema defined by this keyword's value. # Note that when annotations are being collected, all subschemas MUST be examined so that # annotations are collected from each subschema that validates successfully. anyOf_results = value.each_index.map do |i| inplace_subschema_validate(['anyOf', i]) end inplace_results_validate( anyOf_results.any?(&:valid?), 'validation.keyword.anyOf.not_any_valid', "instance is not valid against any `anyOf` schema", keyword: 'anyOf', results: anyOf_results, # when invalid these are all false, but included for consistency with allOf/oneOf anyOf_indexes_valid: anyOf_results.each_with_index.inject({}) { |h, (r, i)| h.update({i => r.valid?}) }.freeze, ) end end end # element.add_action(:validate) end # Schema::Element.new end
- ONE_OF =
element_map do Schema::Element.new(keyword: 'oneOf') do |element| element.add_action(:subschema) do #> This keyword's value MUST be a non-empty array. if keyword_value_ary?('oneOf') schema_content['oneOf'].each_index do |i| #> Each item of the array MUST be a valid JSON Schema. cxt_yield(['oneOf', i]) end end end # element.add_action(:subschema) element.add_action(:inplace_application_requires_instance) { cxt_yield(true) if keyword?('oneOf') } element.add_action(:inplace_applicate) do if keyword?('oneOf') && schema_content['oneOf'].respond_to?(:to_ary) oneOf_idxs = schema_content['oneOf'].each_index subschema_idx_valid = Hash.new { |h, i| h[i] = subschema(['oneOf', i]).instance_valid?(instance) } # count up to 2 `oneOf` subschemas which `instance` validates against nvalid = oneOf_idxs.inject(0) { |n, i| n <= 1 && subschema_idx_valid[i] ? n + 1 : n } if nvalid != 0 applicator_idxs = oneOf_idxs.select { |i| subschema_idx_valid[i] } else # invalid application: if none of the oneOf were valid, we apply them all. # note: invalid application does not apply when multiple oneOfs validate. applicator_idxs = oneOf_idxs end applicator_idxs.each do |i| inplace_subschema_applicate(['oneOf', i]) end end end # element.add_action(:inplace_applicate) element.add_action(:validate) do if keyword?('oneOf') value = schema_content['oneOf'] # This keyword's value MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema. if value.respond_to?(:to_ary) # An instance validates successfully against this keyword if it validates successfully against # exactly one schema defined by this keyword's value. oneOf_results = value.each_index.map do |i| inplace_subschema_validate(['oneOf', i]) end if oneOf_results.none?(&:valid?) inplace_results_validate( false, 'validation.keyword.oneOf.not_any_valid', "instance is not valid against any `oneOf` schema", keyword: 'oneOf', results: oneOf_results, oneOf_indexes_valid: oneOf_results.each_with_index.inject({}) { |h, (r, i)| h.update({i => r.valid?}) }.freeze, ) else inplace_results_validate( oneOf_results.select(&:valid?).size == 1, 'validation.keyword.oneOf.multiple_valid', "instance is valid against multiple `oneOf` schemas", keyword: 'oneOf', results: oneOf_results, oneOf_indexes_valid: oneOf_results.each_with_index.inject({}) { |h, (r, i)| h.update({i => r.valid?}) }.freeze, ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- XSCHEMA =
element_map do Schema::Element.new(keyword: '$schema') do |element| end # Schema::Element.new end
- CONTAINS =
element_map do Schema::Element.new(keyword: 'contains') do |element| element.add_action(:subschema) do if keyword?('contains') #> The value of this keyword MUST be a valid JSON Schema. cxt_yield(['contains']) end end # element.add_action(:subschema) element.add_action(:child_applicate) do if instance.respond_to?(:to_ary) if keyword?('contains') contains_schema = subschema(['contains']) child_idx_valid = Hash.new { |h, i| h[i] = contains_schema.instance_valid?(instance[i]) } if child_idx_valid[token] child_schema_applicate(contains_schema) else instance_valid = instance.each_index.any? { |i| child_idx_valid[i] } unless instance_valid # invalid application: if contains_schema does not validate against any child, it applies to every child child_schema_applicate(contains_schema) end end end end # if instance.respond_to?(:to_ary) end # element.add_action(:child_applicate) element.add_action(:validate) do if keyword?('contains') # An array instance is valid against "contains" if at least one of its elements is valid against # the given schema. if instance.respond_to?(:to_ary) results = {} instance.each_index do |i| results[i] = child_subschema_validate(i, ['contains']) end child_results_validate( results.each_value.any?(&:valid?), 'validation.keyword.contains.none', "instance array does not contain any items valid against `contains` schema", keyword: 'contains', child_results: results, # when invalid these are all false, but included for consistency with `contains` with min/max instance_indexes_valid: results.inject({}) { |h, (i, r)| h.update({i => r.valid?}) }.freeze, ) end end end # element.add_action(:validate) end # Schema::Element.new end
- EXAMPLES =
element_map do Schema::Element.new(keyword: 'examples') do |element| end # Schema::Element.new end
- REQUIRED =
element_map do Schema::Element.new(keyword: 'required') do |element| element.add_action(:described_object_property_names) do next if !keyword_value_ary?('required') schema_content['required'].each(&block) end element.add_action(:validate) do if keyword?('required') value = schema_content['required'] # The value of this keyword MUST be an array. Elements of this array, if any, MUST be strings, and MUST be unique. if value.respond_to?(:to_ary) if instance.respond_to?(:to_hash) # An object instance is valid against this keyword if every item in the array is the name of a property in the instance. missing_required = value.reject { |property_name| instance.key?(property_name) }.freeze validate( missing_required.empty?, 'validation.keyword.required.missing_property_names', 'instance object does not contain all property names specified by `required` value', keyword: 'required', missing_required_property_names: missing_required, ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- INFO_BOOL =
element_map do |keyword: | Schema::Element.new(keyword: keyword) do |element| end # Schema::Element.new end
- PROPERTIES =
element_map do Schema::Element.new(keywords: %w(properties patternProperties additionalProperties)) do |element| element.add_action(:subschema) do #> The value of "properties" MUST be an object. if keyword_value_hash?('properties') schema_content['properties'].each_key do |property_name| #> Each value of this object MUST be a valid JSON Schema. cxt_yield(['properties', property_name]) end end #> The value of "patternProperties" MUST be an object. if keyword_value_hash?('patternProperties') schema_content['patternProperties'].each_key do |pattern| #> Each property value of this object MUST be a valid JSON Schema. cxt_yield(['patternProperties', pattern]) end end if keyword?('additionalProperties') #> The value of "additionalProperties" MUST be a valid JSON Schema. cxt_yield(['additionalProperties']) end end # element.add_action(:subschema) element.add_action(:described_object_property_names) do next if !keyword_value_hash?('properties') schema_content['properties'].each_key(&block) end element.add_action(:child_applicate) do if instance.respond_to?(:to_hash) apply_additional = true if keyword?('properties') && schema_content['properties'].respond_to?(:to_hash) && schema_content['properties'].key?(token) apply_additional = false child_subschema_applicate(['properties', token]) end if keyword?('patternProperties') && schema_content['patternProperties'].respond_to?(:to_hash) schema_content['patternProperties'].each_key do |pattern| if pattern.respond_to?(:to_str) && token.to_s =~ Regexp.new(pattern) # TODO map pattern to ruby syntax apply_additional = false child_subschema_applicate(['patternProperties', pattern]) end end end if apply_additional && keyword?('additionalProperties') child_subschema_applicate(['additionalProperties']) end end # if instance.respond_to?(:to_hash) end # element.add_action(:child_applicate) element.add_action(:validate) do evaluated_property_names = Set[] if keyword?('properties') value = schema_content['properties'] # The value of "properties" MUST be an object. Each value of this object MUST be a valid JSON Schema. if value.respond_to?(:to_hash) # Validation succeeds if, for each name that appears in both the instance and as a name within this # keyword's value, the child instance for that name successfully validates against the corresponding # schema. if instance.respond_to?(:to_hash) results = {} instance.each_key do |property_name| if value.key?(property_name) evaluated_property_names << property_name results[property_name] = child_subschema_validate(property_name, ['properties', property_name]) end end child_results_validate( results.each_value.all?(&:valid?), 'validation.keyword.properties.invalid', "instance object properties are not all valid against corresponding `properties` schemas", keyword: 'properties', child_results: results, instance_properties_valid: results.inject({}) { |h, (k, r)| h.update({k => r.valid?}) }.freeze, ) end end end if keyword?('patternProperties') value = schema_content['patternProperties'] # The value of "patternProperties" MUST be an object. Each property name of this object SHOULD be a # valid regular expression, according to the ECMA 262 regular expression dialect. Each property value # of this object MUST be a valid JSON Schema. if value.respond_to?(:to_hash) # Validation succeeds if, for each instance name that matches any regular expressions that appear as # a property name in this keyword's value, the child instance for that name successfully validates # against each schema that corresponds to a matching regular expression. if instance.respond_to?(:to_hash) results = {} instance.each_key do |property_name| value.each_key do |value_property_pattern| begin # TODO ECMA 262 if value_property_pattern.respond_to?(:to_str) && Regexp.new(value_property_pattern).match(property_name.to_s) evaluated_property_names << property_name results[property_name] = child_subschema_validate(property_name, ['patternProperties', value_property_pattern]) end rescue ::RegexpError # cannot validate end end end child_results_validate( results.each_value.all?(&:valid?), 'validation.keyword.patternProperties.invalid', "instance object properties are not all valid against matching `patternProperties` schemas", keyword: 'patternProperties', child_results: results, instance_properties_valid: results.inject({}) { |h, (k, r)| h.update({k => r.valid?}) }.freeze, ) end end end if keyword?('additionalProperties') # The value of "additionalProperties" MUST be a valid JSON Schema. if instance.respond_to?(:to_hash) results = {} instance.each_key do |property_name| if !evaluated_property_names.include?(property_name) results[property_name] = child_subschema_validate(property_name, ['additionalProperties']) end end child_results_validate( results.each_value.all?(&:valid?), 'validation.keyword.additionalProperties.invalid', "instance object additional properties are not all valid against `additionalProperties` schema", keyword: 'additionalProperties', child_results: results, instance_properties_valid: results.inject({}) { |h, (k, r)| h.update({k => r.valid?}) }.freeze, ) end end end # element.add_action(:validate) end # Schema::Element.new end
- DEFINITIONS =
element_map do |keyword: | Schema::Element.new(keyword: keyword) do |element| element.add_action(:subschema) do #> This keyword's value MUST be an object. if keyword_value_hash?(keyword) schema_content[keyword].each_key do |property_name| #> Each member value of this object MUST be a valid JSON Schema. cxt_yield([keyword, property_name]) end end end # element.add_action(:subschema) end end
- DYNAMIC_REF =
element_map do Schema::Element.new(keyword: '$dynamicRef') do |element| resolve_dynamicRef = proc do next unless keyword_value_str?('$dynamicRef') dynamic_anchor_map = schema.jsi_next_schema_dynamic_anchor_map #> Resolved against the current URI base, it produces the URI used as the starting point for runtime resolution. #> This initial resolution is safe to perform on schema load. ref = schema.schema_ref(schema_content['$dynamicRef']) initial_resolution = ref.resolve #> If the initially resolved starting point URI includes a #> fragment that was created by the "$dynamicAnchor" keyword, resolve_dynamically = \ # did resolution resolve a fragment? ref.ref_uri.fragment && # does the fragment correspond to a dynamicAnchor? (not a regular anchor, not a pointer) initial_resolution.dialect_invoke_each(:dynamicAnchor).include?(ref.ref_uri.fragment) && # is the anchor in our dynamic_anchor_map? dynamic_anchor_map.key?(ref.ref_uri.fragment) if resolve_dynamically #> the initial URI MUST be replaced by the URI (including the fragment) #> for the outermost schema resource in the dynamic scope (Section 7.1) #> that defines an identically named fragment with "$dynamicAnchor". # our dynamic resolution doesn't use a stack of dynamic scope URIs. # we replace the initially resolved resource with the resource from dynamic_anchor_map. scope_schema, subptrs = dynamic_anchor_map[ref.ref_uri.fragment] resolved_schema = subptrs.inject(scope_schema, &:subschema) else #> Otherwise, its behavior is identical to "$ref", and no runtime resolution is needed. resolved_schema = initial_resolution end [resolved_schema.with_dynamic_scope_from(schema), ref] end element.add_action(:inplace_applicate) do resolved_schema, ref = *instance_exec(&resolve_dynamicRef) || next inplace_schema_applicate(resolved_schema, ref: ref) end element.add_action(:validate) do resolved_schema, ref = *instance_exec(&resolve_dynamicRef) || next ref_result = resolved_schema.internal_validate_instance( instance_ptr, instance_document, validate_only: validate_only, visited_refs: Util.add_visited_ref(visited_refs, ref), ) inplace_results_validate( ref_result.valid?, 'validation.keyword.$dynamicRef.invalid', "instance is not valid against the schema referenced by `$dynamicRef`", keyword: '$dynamicRef', results: [ref_result], ) end end end
- INFO_STRING =
element_map do |keyword: | Schema::Element.new(keyword: keyword) do |element| end # Schema::Element.new end
- XVOCABULARY =
element_map do Schema::Element.new(keyword: '$vocabulary') do |element| end end
- DEPENDENCIES =
element_map do Schema::Element.new(keyword: 'dependencies') do |element| element.add_action(:subschema) do #> This keyword's value MUST be an object. if keyword_value_hash?('dependencies') schema_content['dependencies'].each_pair do |property_name, dependency| #> Each property specifies a dependency. #> Each dependency value MUST be an array or a valid JSON Schema. if !dependency.respond_to?(:to_ary) cxt_yield(['dependencies', property_name]) end end end end # element.add_action(:subschema) element.add_action(:described_object_property_names) do next if !keyword_value_hash?('dependencies') schema_content['dependencies'].each do |property_name, dependency| cxt_yield(property_name) if dependency.respond_to?(:to_ary) dependency.each(&block) end end end element.add_action(:inplace_application_requires_instance) do next if !keyword_value_hash?('dependencies') next if schema_content['dependencies'].each_value.all? { |dependency| dependency.respond_to?(:to_ary) } cxt_yield(true) end element.add_action(:inplace_applicate) do next if !keyword_value_hash?('dependencies') next if schema_content['dependencies'].each_value.all? { |dependency| dependency.respond_to?(:to_ary) } next if !instance.respond_to?(:to_hash) #> This keyword's value MUST be an object. Each property specifies a dependency. Each dependency #> value MUST be an array or a valid JSON Schema. schema_content['dependencies'].each_pair do |property_name, dependency| if dependency.respond_to?(:to_ary) # noop: array-form dependencies has no in-place applicator schema else # If the dependency value is a subschema, and the dependency key is a # property in the instance, the entire instance must validate against # the dependency value. if instance.key?(property_name) inplace_subschema_applicate(['dependencies', property_name]) end end end end # element.add_action(:inplace_applicate) element.add_action(:validate) do #> This keyword's value MUST be an object. Each property specifies a dependency. Each dependency #> value MUST be an array or a valid JSON Schema. next if !keyword_value_hash?('dependencies') next if !instance.respond_to?(:to_hash) schema_content['dependencies'].each_pair do |property_name, dependency| if dependency.respond_to?(:to_ary) # If the dependency value is an array, each element in the array, if # any, MUST be a string, and MUST be unique. If the dependency key is # a property in the instance, each of the items in the dependency value # must be a property that exists in the instance. if instance.respond_to?(:to_hash) && instance.key?(property_name) missing_required = dependency.reject { |name| instance.key?(name) }.freeze validate( missing_required.empty?, 'validation.keyword.dependencies.dependent_required.missing_property_names', 'instance object does not contain all dependent required property names specified by `dependencies` value', keyword: 'dependencies', property_name: property_name, missing_dependent_required_property_names: missing_required, ) end else # If the dependency value is a subschema, and the dependency key is a # property in the instance, the entire instance must validate against # the dependency value. if instance.key?(property_name) dependency_result = inplace_subschema_validate(['dependencies', property_name]) inplace_results_validate( dependency_result.valid?, 'validation.keyword.dependencies.dependent_schema.invalid', 'instance object is not valid against the schema corresponding to a matched property name specified by `dependencies` value', keyword: 'dependencies', results: [dependency_result], property_name: property_name, ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- IF_THEN_ELSE =
element_map do Schema::Element.new(keywords: %w(if then else)) do |element| element.add_action(:subschema) do if keyword?('if') #> This keyword's value MUST be a valid JSON Schema. cxt_yield(['if']) end if keyword?('then') #> This keyword's value MUST be a valid JSON Schema. cxt_yield(['then']) end if keyword?('else') #> This keyword's value MUST be a valid JSON Schema. cxt_yield(['else']) end end # element.add_action(:subschema) element.add_action(:inplace_application_requires_instance) { cxt_yield(true) if keyword?('if') } element.add_action(:inplace_applicate) do if keyword?('if') if subschema(['if']).instance_valid?(instance) if collect_evaluated inplace_subschema_applicate(['if'], applicate: false) end if keyword?('then') inplace_subschema_applicate(['then']) end else if keyword?('else') inplace_subschema_applicate(['else']) end end end end # element.add_action(:inplace_applicate) element.add_action(:validate) do if keyword?('if') # This keyword's value MUST be a valid JSON Schema. # This validation outcome of this keyword's subschema has no direct effect on the overall validation # result. Rather, it controls which of the "then" or "else" keywords are evaluated. if_result = inplace_subschema_validate(['if']) if if_result.valid? result.evaluated_tokens.merge(if_result.evaluated_tokens) end if if_result.valid? if keyword?('then') then_result = inplace_subschema_validate(['then']) inplace_results_validate( then_result.valid?, 'validation.keyword.then.invalid', "instance is not valid against `then` schema after validating against `if` schema", keyword: 'if', results: [then_result], ) end else if keyword?('else') else_result = inplace_subschema_validate(['else']) inplace_results_validate( else_result.valid?, 'validation.keyword.else.invalid', "instance is not valid against `else` schema after not validating against `if` schema", keyword: 'if', results: [else_result], ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- CONTENT_SCHEMA =
element_map do Schema::Element.new(keyword: 'contentSchema') do |element| element.add_action(:subschema) do if keyword?('contentSchema') #> The value of this property MUST be a valid JSON schema. cxt_yield(['contentSchema']) end end # element.add_action(:subschema) end # Schema::Element.new end
- ITEMS_PREFIXED =
element_map do Schema::Element.new(keywords: %w(items prefixItems)) do |element| element.add_action(:subschema) do # https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-01#name-prefixitems #> The value of "prefixItems" MUST be a non-empty array of valid JSON Schemas. if keyword_value_ary?('prefixItems') schema_content['prefixItems'].each_index do |i| cxt_yield(['prefixItems', i]) end end # https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-01#name-items #> The value of "items" MUST be a valid JSON Schema. if keyword?('items') cxt_yield(['items']) end end element.add_action(:child_applicate) do next if !instance.respond_to?(:to_ary) if keyword_value_ary?('prefixItems') && schema_content['prefixItems'].size > token child_subschema_applicate(['prefixItems', token]) elsif keyword?('items') child_subschema_applicate(['items']) end end element.add_action(:validate) do next if !instance.respond_to?(:to_ary) i = 0 if keyword_value_ary?('prefixItems') prefixItems_results = {} while i < schema_content['prefixItems'].size && i < instance.size prefixItems_results[i] = child_subschema_validate(i, ['prefixItems', i]) i += 1 end #> Validation succeeds if each element of the instance validates #> against the schema at the same position, if any. #> This keyword does not constrain the length of the array. #> If the array is longer than this keyword's value, this keyword #> validates only the prefix of matching length. child_results_validate( prefixItems_results.each_value.all?(&:valid?), 'validation.keyword.prefixItems.invalid', "instance array items are not all valid against corresponding `prefixItems` schemas", keyword: 'prefixItems', child_results: prefixItems_results, instance_indexes_valid: prefixItems_results.inject({}) { |h, (ri, r)| h.update({ri => r.valid?}) }.freeze, ) end if keyword?('items') items_results = {} while i < instance.size #> This keyword applies its subschema to all instance elements at indexes #> greater than the length of the "prefixItems" array in the same schema #> object, as reported by the annotation result of that "prefixItems" keyword. #> If no such annotation result exists, "items" applies its subschema to all instance array elements. #> Note that the behavior of "items" without "prefixItems" is identical #> to that of the schema form of "items" in prior drafts. #> When "prefixItems" is present, the behavior of "items" is identical #> to the former "additionalItems" keyword. items_results[i] = child_subschema_validate(i, ['items']) i += 1 end if keyword_value_ary?('prefixItems') error_key = 'validation.keyword.items.after_prefixItems.invalid' error_msg = "instance array items after `prefixItems` are not all valid against `items` schema" else error_key = 'validation.keyword.items.invalid' error_msg = "instance array items are not all valid against `items` schema" end child_results_validate( items_results.each_value.all?(&:valid?), error_key, error_msg, keyword: 'items', child_results: items_results, instance_indexes_valid: items_results.inject({}) { |h, (ri, r)| h.update({ri => r.valid?}) }.freeze, ) end end end end
- PROPERTY_NAMES =
element_map do Schema::Element.new(keyword: 'propertyNames') do |element| element.add_action(:subschema) do if keyword?('propertyNames') #> The value of "propertyNames" MUST be a valid JSON Schema. cxt_yield(['propertyNames']) end end # element.add_action(:subschema) element.add_action(:propertyNames) do cxt_yield(subschema(['propertyNames'])) if keyword?('propertyNames') end element.add_action(:validate) do if keyword?('propertyNames') # The value of "propertyNames" MUST be a valid JSON Schema. # # If the instance is an object, this keyword validates if every property name in the instance # validates against the provided schema. Note the property name that the schema is testing will # always be a string. if instance.respond_to?(:to_hash) results = {} instance.each_key do |property_name| results[property_name] = subschema(['propertyNames']).internal_validate_instance( Ptr[], property_name, validate_only: validate_only, ) end validate( results.each_value.all?(&:valid?), 'validation.keyword.propertyNames.invalid', "instance object property names are not all valid against `propertyNames` schema", keyword: 'propertyNames', results: results.each_value, instance_property_names_valid: results.inject({}) { |h, (k, r)| h.update({k => r.valid?}) }.freeze, ) end end end # element.add_action(:validate) end # Schema::Element.new end
- CONTAINS_MINMAX =
element_map do Schema::Element.new(keywords: ['contains', 'minContains', 'maxContains']) do |element| element.add_action(:subschema) do if keyword?('contains') #> The value of this keyword MUST be a valid JSON Schema. cxt_yield(['contains']) end end # element.add_action(:subschema) element.add_action(:child_applicate) do if instance.respond_to?(:to_ary) if keyword?('contains') contains_schema = subschema(['contains']) child_idx_valid = Hash.new { |h, i| h[i] = contains_schema.instance_valid?(instance[i]) } if child_idx_valid[token] child_schema_applicate(contains_schema) else minContains = keyword_value_numeric?('minContains') ? schema_content['minContains'] : 1 child_valid_count = instance.each_index.inject(0) { |n, i| n < minContains && child_idx_valid[i] ? n + 1 : n } if child_valid_count < minContains # invalid application: if contains_schema does not validate against `minContains` children, # it applies to every child. # note: this does not consider maxContains; a validation error from maxContains is not from any # validation error of child application (which invalid application is intended to preserve), but # rather from too few failures in child application. children that don't validate against contains # are correctly not described by contains when contains validation failure comes from maxContains. child_schema_applicate(contains_schema) end end end end # if instance.respond_to?(:to_ary) end # element.add_action(:child_applicate) element.add_action(:validate) do if keyword?('contains') # https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-01#name-contains #> An array instance is valid against "contains" if at least one of its elements is valid against #> the given schema, except when "minContains" is present and has a value of 0, #> in which case an array instance MUST be considered valid against the "contains" keyword, #> even if none of its elements is valid against the given schema. if instance.respond_to?(:to_ary) results = {} instance.each_index do |i| results[i] = child_subschema_validate(i, ['contains']) end child_valid_count = instance.each_index.count { |i| results[i].valid? } minContains = keyword_value_numeric?('minContains') ? schema_content['minContains'] : nil if minContains # https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#name-mincontains child_results_validate( child_valid_count >= minContains, 'validation.keyword.contains.fewer_than_minContains', "instance array contains fewer items that are valid against `contains` schema than `minContains` value", keyword: 'contains', child_results: results, ) else child_results_validate( child_valid_count > 0, 'validation.keyword.contains.none', "instance array does not contain any items that are valid against `contains` schema", keyword: 'contains', child_results: results, ) end maxContains = keyword_value_numeric?('maxContains') ? schema_content['maxContains'] : nil if maxContains # https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#name-maxcontains validate( child_valid_count <= maxContains, 'validation.keyword.maxContains.more_than_maxContains', "instance array contains more items that are valid against `contains` schema than `maxContains` value", keyword: 'maxContains', ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- MAXIMUM_BOOLEAN_EXCLUSIVE =
element_map do Schema::Element.new(keywords: %w(maximum exclusiveMaximum)) do |element| element.add_action(:validate) do if keyword?('maximum') value = schema_content['maximum'] # The value of "maximum" MUST be a JSON number. if value.is_a?(Numeric) if instance.is_a?(Numeric) # Successful validation depends on the presence and value of "exclusiveMaximum": if schema_content['exclusiveMaximum'] == true # if "exclusiveMaximum" has boolean value true, the instance is valid if it is strictly lower # than the value of "maximum". validate( instance < value, 'validation.keyword.maximum.with_exclusiveMaximum.greater_or_equal', "instance is greater than or equal to `maximum` value with `exclusiveMaximum` = true", keyword: 'maximum', ) else # if "exclusiveMaximum" is not present, or has boolean value false, then the instance is # valid if it is lower than, or equal to, the value of "maximum" validate( instance <= value, 'validation.keyword.maximum.greater', "instance is greater than `maximum` value", keyword: 'maximum', ) end end end end end # element.add_action(:validate) end # Schema::Element.new end
- MINIMUM_BOOLEAN_EXCLUSIVE =
element_map do Schema::Element.new(keywords: %w(minimum exclusiveMinimum)) do |element| element.add_action(:validate) do if keyword?('minimum') value = schema_content['minimum'] # The value of "minimum" MUST be a JSON number. if value.is_a?(Numeric) if instance.is_a?(Numeric) # Successful validation depends on the presence and value of "exclusiveMinimum": if schema_content['exclusiveMinimum'] == true # if "exclusiveMinimum" is present and has boolean value true, the instance is valid if it is # strictly greater than the value of "minimum". validate( instance > value, 'validation.keyword.minimum.with_exclusiveMinimum.less_or_equal', "instance is less than or equal to `minimum` value with `exclusiveMinimum` = true", keyword: 'minimum', ) else # if "exclusiveMinimum" is not present, or has boolean value false, then the instance is # valid if it is greater than, or equal to, the value of "minimum" validate( instance >= value, 'validation.keyword.minimum.less', "instance is less than `minimum` value", keyword: 'minimum', ) end end end end end # element.add_action(:validate) end # Schema::Element.new end
- MAX_ITEMS =
element_map do Schema::Element.new(keyword: 'maxItems') do |element| element.add_action(:validate) do if keyword?('maxItems') value = schema_content['maxItems'] # The value of this keyword MUST be a non-negative integer. if internal_integer?(value) && value >= 0 if instance.respond_to?(:to_ary) # An array instance is valid against "maxItems" if its size is less than, or equal to, the value of this keyword. size = instance.to_ary.size validate( size <= value, 'validation.keyword.maxItems.size_greater', 'instance array size is greater than `maxItems` value', keyword: 'maxItems', instance_size: size, ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- MIN_ITEMS =
element_map do Schema::Element.new(keyword: 'minItems') do |element| element.add_action(:validate) do if keyword?('minItems') value = schema_content['minItems'] # The value of this keyword MUST be a non-negative integer. if internal_integer?(value) && value >= 0 if instance.respond_to?(:to_ary) # An array instance is valid against "minItems" if its size is greater than, or equal to, the value of this keyword. size = instance.to_ary.size validate( size >= value, 'validation.keyword.minItems.size_less', 'instance array size is less than `minItems` value', keyword: 'minItems', instance_size: size, ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- UNIQUE_ITEMS =
element_map do Schema::Element.new(keyword: 'uniqueItems') do |element| element.add_action(:validate) do if keyword?('uniqueItems') value = schema_content['uniqueItems'] # The value of this keyword MUST be a boolean. if value == true if instance.respond_to?(:to_ary) # If it has boolean value true, the instance validates successfully if all of its elements are unique. # TODO instance.tally.select { |_, count| count > 1 }.keys.freeze when all supported Hash.method_defined?(:tally) duplicate_items = instance.group_by(&:itself).select { |_, v| v.size > 1 }.keys.freeze validate( duplicate_items.empty?, 'validation.keyword.uniqueItems.not_unique', "instance array items are not unique with `uniqueItems` = true", keyword: 'uniqueItems', duplicate_items: duplicate_items, ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- CONTENT_ENCODING =
element_map do Schema::Element.new(keyword: 'contentEncoding') do |element| end # Schema::Element.new end
- DEPENDENT_SCHEMAS =
element_map do Schema::Element.new(keyword: 'dependentSchemas') do |element| element.add_action(:subschema) do #> This keyword's value MUST be an object. next if !keyword_value_hash?('dependentSchemas') #> Each value in the object MUST be a valid JSON Schema. schema_content['dependentSchemas'].each_key do |property_name| cxt_yield(['dependentSchemas', property_name]) end end element.add_action(:described_object_property_names) do next if !keyword_value_hash?('dependentSchemas') schema_content['dependentSchemas'].each_key(&block) end element.add_action(:inplace_application_requires_instance) { cxt_yield(true) if keyword?('dependentSchemas') } element.add_action(:inplace_applicate) do #> This keyword's value MUST be an object. next if !keyword_value_hash?('dependentSchemas') next if !instance.respond_to?(:to_hash) #> This keyword specifies subschemas that are evaluated if the #> instance is an object and contains a certain property. # #> If the object key is a property in the instance, the entire instance must validate #> against the subschema. Its use is dependent on the presence of the property. schema_content['dependentSchemas'].each_key do |property_name| if instance.key?(property_name) inplace_subschema_applicate(['dependentSchemas', property_name]) end end end element.add_action(:validate) do #> This keyword's value MUST be an object. next if !keyword_value_hash?('dependentSchemas') next if !instance.respond_to?(:to_hash) #> This keyword specifies subschemas that are evaluated if the #> instance is an object and contains a certain property. # #> If the object key is a property in the instance, the entire instance must validate #> against the subschema. Its use is dependent on the presence of the property. results = {} schema_content['dependentSchemas'].each_key do |property_name| if instance.key?(property_name) results[property_name] = inplace_subschema_validate(['dependentSchemas', property_name]) end end inplace_results_validate( results.each_value.all?(&:valid?), 'validation.keyword.dependentSchemas.invalid', "instance object is not valid against all schemas corresponding to matched property names specified by `dependentSchemas`", keyword: 'dependentSchemas', results: results.each_value, dependentSchemas_properties_valid: results.inject({}) { |h, (k, r)| h.update({k => r.valid?}) }.freeze, ) end end end
- MAX_PROPERTIES =
element_map do Schema::Element.new(keyword: 'maxProperties') do |element| element.add_action(:validate) do if keyword?('maxProperties') value = schema_content['maxProperties'] # The value of this keyword MUST be a non-negative integer. if internal_integer?(value) && value >= 0 if instance.respond_to?(:to_hash) # An object instance is valid against "maxProperties" if its number of properties is less than, or equal to, the value of this keyword. size = instance.size validate( size <= value, 'validation.keyword.maxProperties.properties_count_greater', "instance object properties count is greater than `maxProperties` value", keyword: 'maxProperties', instance_properties_count: size, ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- MIN_PROPERTIES =
element_map do Schema::Element.new(keyword: 'minProperties') do |element| element.add_action(:validate) do if keyword?('minProperties') value = schema_content['minProperties'] # The value of this keyword MUST be a non-negative integer. if internal_integer?(value) && value >= 0 if instance.respond_to?(:to_hash) # An object instance is valid against "minProperties" if its number of properties is greater than, or equal to, the value of this keyword. size = instance.size validate( size >= value, 'validation.keyword.minProperties.properties_count_less', "instance object properties count is less than `minProperties` value", keyword: 'minProperties', instance_properties_count: size, ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- MAX_LENGTH =
element_map do Schema::Element.new(keyword: 'maxLength') do |element| element.add_action(:validate) do if keyword?('maxLength') value = schema_content['maxLength'] # The value of this keyword MUST be a non-negative integer. if internal_integer?(value) && value >= 0 if instance.respond_to?(:to_str) # A string instance is valid against this keyword if its length is less than, or equal to, the # value of this keyword. length = instance.to_str.length validate( length <= value, 'validation.keyword.maxLength.length_greater', "instance string length is greater than `maxLength` value", keyword: 'maxLength', instance_length: length, ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- MIN_LENGTH =
element_map do Schema::Element.new(keyword: 'minLength') do |element| element.add_action(:validate) do if keyword?('minLength') value = schema_content['minLength'] # The value of this keyword MUST be a non-negative integer. if internal_integer?(value) && value >= 0 if instance.respond_to?(:to_str) # A string instance is valid against this keyword if its length is greater than, or equal to, the # value of this keyword. length = instance.to_str.length validate( length >= value, 'validation.keyword.minLength.length_less', "instance string length is less than `minLength` value", keyword: 'minLength', instance_length: length, ) end end end end # element.add_action(:validate) end # Schema::Element.new end
- UNEVALUATED_ITEMS =
element_map do Schema::Element.new(keyword: 'unevaluatedItems') do |element| element.depends_on_elements do |other_element| other_element.invokes?(:inplace_applicate) || (other_element.invokes?(:child_applicate) && !other_element.invokes?(:application_requires_evaluated)) end element.add_action(:application_requires_evaluated) { cxt_yield(true) if keyword?('unevaluatedItems') } element.add_action(:subschema) do #> The value of "unevaluatedItems" MUST be a valid JSON Schema. if keyword?('unevaluatedItems') cxt_yield(['unevaluatedItems']) end end element.add_action(:child_applicate) do if instance.respond_to?(:to_ary) if keyword?('unevaluatedItems') if !evaluated child_subschema_applicate(['unevaluatedItems']) end end end end element.add_action(:validate) do next if !keyword?('unevaluatedItems') next if !instance.respond_to?(:to_ary) results = {} instance.each_index do |i| if !result.evaluated_tokens.include?(i) results[i] = child_subschema_validate(i, ['unevaluatedItems']) end end child_results_validate( results.each_value.all?(&:valid?), 'validation.keyword.unevaluatedItems.invalid', "instance array unevaluated items are not all valid against `unevaluatedItems` schema", keyword: 'unevaluatedItems', child_results: results, instance_indexes_valid: results.inject({}) { |h, (i, r)| h.update({i => r.valid?}) }.freeze, ) end end end
- CONTENT_MEDIA_TYPE =
element_map do Schema::Element.new(keyword: 'contentMediaType') do |element| end # Schema::Element.new end
- DEPENDENT_REQUIRED =
element_map do Schema::Element.new(keyword: 'dependentRequired') do |element| element.add_action(:described_object_property_names) do next if !keyword_value_hash?('dependentRequired') schema_content['dependentRequired'].each do |property_name, dependent_property_names| cxt_yield(property_name) next if !dependent_property_names.respond_to?(:to_ary) dependent_property_names.each(&block) end end element.add_action(:validate) do #> The value of this keyword MUST be an object. next if !keyword_value_hash?('dependentRequired') next if !instance.respond_to?(:to_hash) #> This keyword specifies properties that are required if a specific other property is #> present. Their requirement is dependent on the presence of the other property. # #> Validation succeeds if, for each name that appears in both the instance and as a name #> within this keyword's value, every item in the corresponding array is also the name of #> a property in the instance. missing_dependent_required = {} schema_content['dependentRequired'].each do |property_name, dependent_property_names| #> Properties in this object, if any, MUST be arrays. next if !dependent_property_names.respond_to?(:to_ary) if instance.key?(property_name) missing_required = dependent_property_names.reject { |name| instance.key?(name) } unless missing_required.empty? missing_dependent_required[property_name] = missing_required end end end validate( missing_dependent_required.empty?, 'validation.keyword.dependentRequired.missing_property_names', "instance object does not contain all dependent required property names specified by `dependentRequired`", keyword: 'dependentRequired', missing_dependent_required: missing_dependent_required, ) end end end
- UNEVALUATED_PROPERTIES =
element_map do Schema::Element.new(keyword: 'unevaluatedProperties') do |element| element.depends_on_elements do |other_element| other_element.invokes?(:inplace_applicate) || (other_element.invokes?(:child_applicate) && !other_element.invokes?(:application_requires_evaluated)) end element.add_action(:application_requires_evaluated) { cxt_yield(true) if keyword?('unevaluatedProperties') } element.add_action(:subschema) do #> The value of "unevaluatedProperties" MUST be a valid JSON Schema. if keyword?('unevaluatedProperties') cxt_yield(['unevaluatedProperties']) end end element.add_action(:child_applicate) do if instance.respond_to?(:to_hash) if keyword?('unevaluatedProperties') if !evaluated child_subschema_applicate(['unevaluatedProperties']) end end end end element.add_action(:validate) do next if !keyword?('unevaluatedProperties') next if !instance.respond_to?(:to_hash) results = {} instance.each_key do |property_name| if !result.evaluated_tokens.include?(property_name) results[property_name] = child_subschema_validate(property_name, ['unevaluatedProperties']) end end child_results_validate( results.each_value.all?(&:valid?), 'validation.keyword.unevaluatedProperties.invalid', "instance object unevaluated properties are not all valid against `unevaluatedProperties` schema", keyword: 'unevaluatedProperties', child_results: results, instance_properties_valid: results.inject({}) { |h, (k, r)| h.update({k => r.valid?}) }.freeze, ) end end end
Class Method Summary collapse
Class Method Details
.element_map(&block) ⇒ Object
5 6 7 |
# File 'lib/jsi/schema/elements.rb', line 5 def self.element_map(&block) Util::MemoMap::Immutable.new(&block) end |