Module: MCPClient::SchemaValidator::Composition

Included in:
MCPClient::SchemaValidator
Defined in:
lib/mcp_client/schema_validator/composition.rb

Overview

How complete a verdict on one value is. Extended into SchemaValidator, so the methods are its own; Evaluation applies the composition keywords themselves and reads the answers here.

A composition branch is evaluated speculatively (its errors are a verdict, not output). anyOf and allOf are monotonic, so a branch that passes as far as the validator can evaluate it is accepted; not, oneOf and if are not, so a branch that still holds an unevaluated assertion applying to the instance is :undecided and never a match, while one decided by its evaluated keywords (or carrying only annotations) is a full verdict.

Every standard assertion whose verdict this validator can reach is evaluated (Instances#validate_object, Instances#validate_array, Scalars#validate_number, and the unevaluated keywords from the annotations Evaluation collects), so what is left here is only what genuinely cannot be decided: a dynamic reference whose target the dynamic scope could re-bind, and format where it asserts.

Instance Method Summary collapse

Instance Method Details

#contains_max(schema, dialect) ⇒ Numeric?

Returns the number of matching items contains allows, when the dialect defines the companion and it is a number.

Returns:

  • (Numeric, nil) —

    the number of matching items contains allows, when the dialect defines the companion and it is a number



53
54
55
56
# File 'lib/mcp_client/schema_validator/composition.rb', line 53

def contains_max(schema, dialect)
  max = schema['maxContains'] if keyword_known?('maxContains', dialect)
  max if max.is_a?(Numeric)
end

#contains_min(schema, dialect) ⇒ Numeric

The number of matching items contains requires: its companion where the dialect defines one and gives it a number, else the default of 1 (JSON Schema 2020-12 Validation Section 6.4.4).

Returns:

  • (Numeric)


46
47
48
49
# File 'lib/mcp_client/schema_validator/composition.rb', line 46

def contains_min(schema, dialect)
  min = schema['minContains'] if keyword_known?('minContains', dialect)
  min.is_a?(Numeric) ? min : 1
end

#partial_keywords?(schema, dialect, data, _ctx) ⇒ Boolean

Whether a schema object carries an assertion the validator does not evaluate (in the dialect in force) that applies to this instance, so its verdict is only partial. Annotations (format in 2019-09 and 2020-12, contentSchema) decide nothing and leave the verdict whole; draft-07 format asserts (Validation Section 7.2), and the validator does not evaluate formats, so a string branch carrying one is undecided there. Dynamic references are evaluated, against the dynamic scope, and leave no verdict partial.

Parameters:

  • schema (Hash) —

    the schema object

  • dialect (String, nil) —

    the dialect in force at it

  • data (Object) —

    the instance

  • _ctx (Context) —

    the validation context

Returns:

  • (Boolean)


38
39
40
# File 'lib/mcp_client/schema_validator/composition.rb', line 38

def partial_keywords?(schema, dialect, data, _ctx)
  dialect == DRAFT_07 && data.is_a?(String) && schema.key?('format')
end

#pattern_matches?(pattern, name, deadline = nil) ⇒ Boolean

Match a server-supplied pattern against a property name. Both come from the peer, so — exactly like validate_pattern — the match runs under the validation-wide deadline: a backtracking expression here must not be able to hold the calling thread.

Parameters:

  • deadline (Float, nil) (defaults to: nil) —

    monotonic deadline for the whole validation

Returns:

  • (Boolean)

Raises:

  • (Aborted) —

    when the budget is exhausted



74
75
76
77
78
79
80
81
82
83
# File 'lib/mcp_client/schema_validator/composition.rb', line 74

def pattern_matches?(pattern, name, deadline = nil)
  remaining = pattern_budget_remaining(deadline)
  raise Aborted, "validation time budget exhausted before pattern #{clip(pattern.inspect)}" if remaining.zero?

  ecma_regexp(pattern, remaining, deadline).match?(name)
rescue Regexp::TimeoutError
  raise Aborted, "pattern #{clip(pattern.inspect)} exceeded the #{PATTERN_MATCH_TIMEOUT}s matching budget"
rescue RegexpError, TypeError
  false
end

#property_present?(data, name) ⇒ Boolean

Returns whether the instance carries the property in either key form.

Parameters:

  • data (Hash) —

    the instance

  • name (Object) —

    the property name a schema keyword names

Returns:

  • (Boolean) —

    whether the instance carries the property in either key form



62
63
64
65
# File 'lib/mcp_client/schema_validator/composition.rb', line 62

def property_present?(data, name)
  name = name.to_s
  data.key?(name) || data.key?(name.to_sym)
end