Class: JSONSchemer::Schema
- Inherits:
-
Object
- Object
- JSONSchemer::Schema
- Includes:
- Output
- Defined in:
- lib/json_schemer/schema.rb
Defined Under Namespace
Classes: Context
Constant Summary collapse
- SCHEMA_KEYWORD_CLASS =
Draft202012::Vocab::Core::Schema
- VOCABULARY_KEYWORD_CLASS =
Draft202012::Vocab::Core::Vocabulary
- ID_KEYWORD_CLASS =
Draft202012::Vocab::Core::Id
- UNKNOWN_KEYWORD_CLASS =
Draft202012::Vocab::Core::UnknownKeyword
- NOT_KEYWORD_CLASS =
Draft202012::Vocab::Applicator::Not
- PROPERTIES_KEYWORD_CLASS =
Draft202012::Vocab::Applicator::Properties
- NET_HTTP_REF_RESOLVER =
proc { |uri| JSON.parse(Net::HTTP.get(uri)) }
- RUBY_REGEXP_RESOLVER =
proc { |pattern| Regexp.new(pattern) }
- ECMA_REGEXP_RESOLVER =
proc { |pattern| Regexp.new(EcmaRegexp.ruby_equivalent(pattern)) }
- DEFAULT_PROPERTY_DEFAULT_RESOLVER =
proc do |instance, property, results_with_tree_validity| results_with_tree_validity = results_with_tree_validity.select(&:last) unless results_with_tree_validity.size == 1 annotations = results_with_tree_validity.to_set { |result, _tree_valid| result.annotation } if annotations.size == 1 instance[property] = annotations.first.clone true else false end end
- SYMBOL_PROPERTY_DEFAULT_RESOLVER =
proc do |instance, property, results_with_tree_validity| DEFAULT_PROPERTY_DEFAULT_RESOLVER.call(instance, property.to_sym, results_with_tree_validity) end
Constants included from Output
Instance Attribute Summary collapse
-
#after_property_validation ⇒ Object
readonly
Returns the value of attribute after_property_validation.
-
#base_uri ⇒ Object
Returns the value of attribute base_uri.
-
#before_property_validation ⇒ Object
readonly
Returns the value of attribute before_property_validation.
-
#configuration ⇒ Object
readonly
Returns the value of attribute configuration.
-
#content_encodings ⇒ Object
readonly
Returns the value of attribute content_encodings.
-
#content_media_types ⇒ Object
readonly
Returns the value of attribute content_media_types.
-
#custom_keywords ⇒ Object
readonly
Returns the value of attribute custom_keywords.
-
#format ⇒ Object
readonly
Returns the value of attribute format.
-
#formats ⇒ Object
readonly
Returns the value of attribute formats.
-
#insert_property_defaults ⇒ Object
readonly
Returns the value of attribute insert_property_defaults.
-
#keyword_order ⇒ Object
Returns the value of attribute keyword_order.
-
#keywords ⇒ Object
Returns the value of attribute keywords.
-
#meta_schema ⇒ Object
Returns the value of attribute meta_schema.
-
#parent ⇒ Object
readonly
Returns the value of attribute parent.
-
#parsed ⇒ Object
readonly
Returns the value of attribute parsed.
-
#root ⇒ Object
readonly
Returns the value of attribute root.
-
#value ⇒ Object
readonly
Returns the value of attribute value.
-
#vocabulary ⇒ Object
readonly
Returns the value of attribute vocabulary.
Attributes included from Output
Instance Method Summary collapse
- #absolute_keyword_location ⇒ Object
- #bundle ⇒ Object
- #defs_keyword ⇒ Object
- #error(formatted_instance_location:, **options) ⇒ Object
- #error_key ⇒ Object
- #fetch(key) ⇒ Object
- #fetch_content_encoding(content_encoding, *args, &block) ⇒ Object
- #fetch_content_media_type(content_media_type, *args, &block) ⇒ Object
- #fetch_format(format, *args, &block) ⇒ Object
- #id_keyword ⇒ Object
-
#initialize(value, parent = nil, root = self, keyword = nil, configuration: JSONSchemer.configuration, base_uri: configuration.base_uri, meta_schema: configuration.meta_schema, vocabulary: configuration.vocabulary, format: configuration.format, formats: configuration.formats, content_encodings: configuration.content_encodings, content_media_types: configuration.content_media_types, keywords: configuration.keywords, before_property_validation: configuration.before_property_validation, after_property_validation: configuration.after_property_validation, insert_property_defaults: configuration.insert_property_defaults, property_default_resolver: configuration.property_default_resolver, ref_resolver: configuration.ref_resolver, regexp_resolver: configuration.regexp_resolver, output_format: configuration.output_format, resolve_enumerators: configuration.resolve_enumerators, access_mode: configuration.access_mode) ⇒ Schema
constructor
A new instance of Schema.
- #inspect ⇒ Object
- #ref(value) ⇒ Object
- #ref_resolver ⇒ Object
- #regexp_resolver ⇒ Object
- #resolve_ref(uri) ⇒ Object
- #resolve_regexp(pattern) ⇒ Object
- #resources ⇒ Object
- #schema_pointer ⇒ Object
- #valid?(instance, **options) ⇒ Boolean
- #valid_schema?(**options) ⇒ Boolean
- #validate(instance, output_format: @output_format, resolve_enumerators: @resolve_enumerators, access_mode: @access_mode) ⇒ Object
- #validate_instance(instance, instance_location, keyword_location, context) ⇒ Object
- #validate_schema(**options) ⇒ Object
Methods included from Output
Constructor Details
#initialize(value, parent = nil, root = self, keyword = nil, configuration: JSONSchemer.configuration, base_uri: configuration.base_uri, meta_schema: configuration.meta_schema, vocabulary: configuration.vocabulary, format: configuration.format, formats: configuration.formats, content_encodings: configuration.content_encodings, content_media_types: configuration.content_media_types, keywords: configuration.keywords, before_property_validation: configuration.before_property_validation, after_property_validation: configuration.after_property_validation, insert_property_defaults: configuration.insert_property_defaults, property_default_resolver: configuration.property_default_resolver, ref_resolver: configuration.ref_resolver, regexp_resolver: configuration.regexp_resolver, output_format: configuration.output_format, resolve_enumerators: configuration.resolve_enumerators, access_mode: configuration.access_mode) ⇒ Schema
Returns a new instance of Schema.
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/json_schemer/schema.rb', line 49 def initialize( value, parent = nil, root = self, keyword = nil, configuration: JSONSchemer.configuration, base_uri: configuration.base_uri, meta_schema: configuration., vocabulary: configuration.vocabulary, format: configuration.format, formats: configuration.formats, content_encodings: configuration.content_encodings, content_media_types: configuration.content_media_types, keywords: configuration.keywords, before_property_validation: configuration.before_property_validation, after_property_validation: configuration.after_property_validation, insert_property_defaults: configuration.insert_property_defaults, property_default_resolver: configuration.property_default_resolver, ref_resolver: configuration.ref_resolver, regexp_resolver: configuration.regexp_resolver, output_format: configuration.output_format, resolve_enumerators: configuration.resolve_enumerators, access_mode: configuration.access_mode ) @value = deep_stringify_keys(value) @parent = parent @root = root @keyword = keyword @schema = self @configuration = configuration @base_uri = base_uri @meta_schema = @vocabulary = vocabulary @format = format @formats = formats @content_encodings = content_encodings @content_media_types = content_media_types @custom_keywords = keywords @before_property_validation = Array(before_property_validation) @after_property_validation = Array(after_property_validation) @insert_property_defaults = insert_property_defaults @property_default_resolver = property_default_resolver @original_ref_resolver = ref_resolver @original_regexp_resolver = regexp_resolver @output_format = output_format @resolve_enumerators = resolve_enumerators @access_mode = access_mode @parsed = parse end |
Instance Attribute Details
#after_property_validation ⇒ Object (readonly)
Returns the value of attribute after_property_validation.
47 48 49 |
# File 'lib/json_schemer/schema.rb', line 47 def after_property_validation @after_property_validation end |
#base_uri ⇒ Object
Returns the value of attribute base_uri.
45 46 47 |
# File 'lib/json_schemer/schema.rb', line 45 def base_uri @base_uri end |
#before_property_validation ⇒ Object (readonly)
Returns the value of attribute before_property_validation.
47 48 49 |
# File 'lib/json_schemer/schema.rb', line 47 def before_property_validation @before_property_validation end |
#configuration ⇒ Object (readonly)
Returns the value of attribute configuration.
46 47 48 |
# File 'lib/json_schemer/schema.rb', line 46 def configuration @configuration end |
#content_encodings ⇒ Object (readonly)
Returns the value of attribute content_encodings.
47 48 49 |
# File 'lib/json_schemer/schema.rb', line 47 def content_encodings @content_encodings end |
#content_media_types ⇒ Object (readonly)
Returns the value of attribute content_media_types.
47 48 49 |
# File 'lib/json_schemer/schema.rb', line 47 def content_media_types @content_media_types end |
#custom_keywords ⇒ Object (readonly)
Returns the value of attribute custom_keywords.
47 48 49 |
# File 'lib/json_schemer/schema.rb', line 47 def custom_keywords @custom_keywords end |
#format ⇒ Object (readonly)
Returns the value of attribute format.
47 48 49 |
# File 'lib/json_schemer/schema.rb', line 47 def format @format end |
#formats ⇒ Object (readonly)
Returns the value of attribute formats.
47 48 49 |
# File 'lib/json_schemer/schema.rb', line 47 def formats @formats end |
#insert_property_defaults ⇒ Object (readonly)
Returns the value of attribute insert_property_defaults.
47 48 49 |
# File 'lib/json_schemer/schema.rb', line 47 def insert_property_defaults @insert_property_defaults end |
#keyword_order ⇒ Object
Returns the value of attribute keyword_order.
45 46 47 |
# File 'lib/json_schemer/schema.rb', line 45 def keyword_order @keyword_order end |
#keywords ⇒ Object
Returns the value of attribute keywords.
45 46 47 |
# File 'lib/json_schemer/schema.rb', line 45 def keywords @keywords end |
#meta_schema ⇒ Object
Returns the value of attribute meta_schema.
45 46 47 |
# File 'lib/json_schemer/schema.rb', line 45 def @meta_schema end |
#parent ⇒ Object (readonly)
Returns the value of attribute parent.
46 47 48 |
# File 'lib/json_schemer/schema.rb', line 46 def parent @parent end |
#parsed ⇒ Object (readonly)
Returns the value of attribute parsed.
46 47 48 |
# File 'lib/json_schemer/schema.rb', line 46 def parsed @parsed end |
#root ⇒ Object (readonly)
Returns the value of attribute root.
46 47 48 |
# File 'lib/json_schemer/schema.rb', line 46 def root @root end |
#value ⇒ Object (readonly)
Returns the value of attribute value.
46 47 48 |
# File 'lib/json_schemer/schema.rb', line 46 def value @value end |
#vocabulary ⇒ Object (readonly)
Returns the value of attribute vocabulary.
47 48 49 |
# File 'lib/json_schemer/schema.rb', line 47 def vocabulary @vocabulary end |
Instance Method Details
#absolute_keyword_location ⇒ Object
267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/json_schemer/schema.rb', line 267 def absolute_keyword_location # using `equal?` because `URI::Generic#==` is slow @absolute_keyword_location ||= if !parent || (!parent.schema.base_uri.equal?(base_uri) && (base_uri.fragment.nil? || base_uri.fragment.empty?)) absolute_keyword_location_uri = base_uri.dup absolute_keyword_location_uri.fragment = '' absolute_keyword_location_uri.to_s elsif keyword "#{parent.absolute_keyword_location}/#{fragment_encode(escaped_keyword)}" else parent.absolute_keyword_location end end |
#bundle ⇒ Object
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
# File 'lib/json_schemer/schema.rb', line 218 def bundle return value unless value.is_a?(Hash) id_keyword = .id_keyword defs_keyword = .defs_keyword compound_document = value.dup compound_document[id_keyword] = base_uri.to_s compound_document['$schema'] = .base_uri.to_s = compound_document[defs_keyword] = (compound_document[defs_keyword]&.dup || {}) if compound_document.key?('$ref') && .keywords.fetch('$ref').exclusive? compound_document['allOf'] = (compound_document['allOf']&.dup || []) compound_document['allOf'] << { '$ref' => compound_document.delete('$ref') } end values = [self] while value = values.shift case value when Schema values << value.parsed when Keyword if value.respond_to?(:ref_uri) && value.respond_to?(:ref_schema) ref_uri = value.ref_uri.dup ref_uri.fragment = nil ref_id = ref_uri.to_s ref_schema = value.ref_schema.root next if ref_schema == root || .key?(ref_id) = ref_schema.value.dup [id_keyword] = ref_id ['$schema'] = ref_schema..base_uri.to_s [ref_id] = values << ref_schema else values << value.parsed end when Hash values.concat(value.values) when Array values.concat(value) end end compound_document end |
#defs_keyword ⇒ Object
326 327 328 |
# File 'lib/json_schemer/schema.rb', line 326 def defs_keyword @defs_keyword ||= (keywords.key?('$defs') ? '$defs' : 'definitions') end |
#error(formatted_instance_location:, **options) ⇒ Object
334 335 336 337 338 339 340 |
# File 'lib/json_schemer/schema.rb', line 334 def error(formatted_instance_location:, **) if value == false && parent&.respond_to?(:false_schema_error) parent.false_schema_error(:formatted_instance_location => formatted_instance_location, **) else "value at #{formatted_instance_location} does not match schema" end end |
#error_key ⇒ Object
290 291 292 |
# File 'lib/json_schemer/schema.rb', line 290 def error_key '^' end |
#fetch(key) ⇒ Object
294 295 296 |
# File 'lib/json_schemer/schema.rb', line 294 def fetch(key) parsed.fetch(key) end |
#fetch_content_encoding(content_encoding, *args, &block) ⇒ Object
306 307 308 309 310 311 312 |
# File 'lib/json_schemer/schema.rb', line 306 def fetch_content_encoding(content_encoding, *args, &block) if == self content_encodings.fetch(content_encoding, *args, &block) else content_encodings.fetch(content_encoding) { .fetch_content_encoding(content_encoding, *args, &block) } end end |
#fetch_content_media_type(content_media_type, *args, &block) ⇒ Object
314 315 316 317 318 319 320 |
# File 'lib/json_schemer/schema.rb', line 314 def fetch_content_media_type(content_media_type, *args, &block) if == self content_media_types.fetch(content_media_type, *args, &block) else content_media_types.fetch(content_media_type) { .fetch_content_media_type(content_media_type, *args, &block) } end end |
#fetch_format(format, *args, &block) ⇒ Object
298 299 300 301 302 303 304 |
# File 'lib/json_schemer/schema.rb', line 298 def fetch_format(format, *args, &block) if == self formats.fetch(format, *args, &block) else formats.fetch(format) { .fetch_format(format, *args, &block) } end end |
#id_keyword ⇒ Object
322 323 324 |
# File 'lib/json_schemer/schema.rb', line 322 def id_keyword @id_keyword ||= (keywords.key?('$id') ? '$id' : 'id') end |
#inspect ⇒ Object
357 358 359 |
# File 'lib/json_schemer/schema.rb', line 357 def inspect "#<#{self.class.name} @value=#{@value.inspect} @parent=#{@parent.inspect} @keyword=#{@keyword.inspect}>" end |
#ref(value) ⇒ Object
123 124 125 |
# File 'lib/json_schemer/schema.rb', line 123 def ref(value) root.resolve_ref(URI.join(base_uri, value)) end |
#ref_resolver ⇒ Object
342 343 344 |
# File 'lib/json_schemer/schema.rb', line 342 def ref_resolver @ref_resolver ||= @original_ref_resolver == 'net/http' ? CachedResolver.new(&NET_HTTP_REF_RESOLVER) : @original_ref_resolver end |
#regexp_resolver ⇒ Object
346 347 348 349 350 351 352 353 354 355 |
# File 'lib/json_schemer/schema.rb', line 346 def regexp_resolver @regexp_resolver ||= case @original_regexp_resolver when 'ecma' CachedResolver.new(&ECMA_REGEXP_RESOLVER) when 'ruby' CachedResolver.new(&RUBY_REGEXP_RESOLVER) else @original_regexp_resolver end end |
#resolve_ref(uri) ⇒ Object
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/json_schemer/schema.rb', line 170 def resolve_ref(uri) pointer = '' if Format.valid_json_pointer?(uri.fragment) pointer = URI.decode_www_form_component(uri.fragment) uri.fragment = nil end lexical_resources = resources.fetch(:lexical) schema = lexical_resources[uri] if !schema && uri.fragment.nil? empty_fragment_uri = uri.dup empty_fragment_uri.fragment = '' schema = lexical_resources[empty_fragment_uri] end unless schema location_independent_identifier = uri.fragment uri.fragment = nil remote_schema = JSONSchemer.schema( ref_resolver.call(uri) || raise(InvalidRefResolution, uri.to_s), :configuration => configuration, :base_uri => uri, :meta_schema => , :ref_resolver => ref_resolver, :regexp_resolver => regexp_resolver ) remote_uri = remote_schema.base_uri.dup remote_uri.fragment = location_independent_identifier if location_independent_identifier schema = remote_schema.resources.fetch(:lexical).fetch(remote_uri) end schema = Hana::Pointer.parse(pointer).reduce(schema) do |obj, token| obj.fetch(token) rescue IndexError raise InvalidRefPointer, pointer end schema = schema.parsed_schema if schema.is_a?(Keyword) raise InvalidRefPointer, pointer unless schema.is_a?(Schema) schema end |
#resolve_regexp(pattern) ⇒ Object
214 215 216 |
# File 'lib/json_schemer/schema.rb', line 214 def resolve_regexp(pattern) regexp_resolver.call(pattern) || raise(InvalidRegexpResolution, pattern) end |
#resources ⇒ Object
330 331 332 |
# File 'lib/json_schemer/schema.rb', line 330 def resources @resources ||= { :lexical => {}, :dynamic => {} } end |
#schema_pointer ⇒ Object
280 281 282 283 284 285 286 287 288 |
# File 'lib/json_schemer/schema.rb', line 280 def schema_pointer @schema_pointer ||= if !parent '' elsif keyword "#{parent.schema_pointer}/#{escaped_keyword}" else parent.schema_pointer end end |
#valid?(instance, **options) ⇒ Boolean
99 100 101 |
# File 'lib/json_schemer/schema.rb', line 99 def valid?(instance, **) validate(instance, :output_format => 'flag', **).fetch('valid') end |
#valid_schema?(**options) ⇒ Boolean
115 116 117 |
# File 'lib/json_schemer/schema.rb', line 115 def valid_schema?(**) .valid?(value, **) end |
#validate(instance, output_format: @output_format, resolve_enumerators: @resolve_enumerators, access_mode: @access_mode) ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/json_schemer/schema.rb', line 103 def validate(instance, output_format: @output_format, resolve_enumerators: @resolve_enumerators, access_mode: @access_mode) instance_location = Location.root context = Context.new(instance, [], nil, (!insert_property_defaults && output_format == 'flag'), access_mode) result = validate_instance(deep_stringify_keys(instance), instance_location, root_keyword_location, context) if insert_property_defaults && result.insert_property_defaults(context, &property_default_resolver) result = validate_instance(deep_stringify_keys(instance), instance_location, root_keyword_location, context) end output = result.output(output_format) resolve_enumerators!(output) if resolve_enumerators output end |
#validate_instance(instance, instance_location, keyword_location, context) ⇒ Object
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 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 168 |
# File 'lib/json_schemer/schema.rb', line 127 def validate_instance(instance, instance_location, keyword_location, context) context.dynamic_scope.push(self) original_adjacent_results = context.adjacent_results adjacent_results = context.adjacent_results = {} short_circuit = context.short_circuit begin return result(instance, instance_location, keyword_location, false) if value == false return result(instance, instance_location, keyword_location, true) if value == true || value.empty? valid = true nested = [] parsed.each do |keyword, keyword_instance| next unless keyword_result = keyword_instance.validate(instance, instance_location, join_location(keyword_location, keyword), context) valid &&= keyword_result.valid return result(instance, instance_location, keyword_location, false) if short_circuit && !valid nested << keyword_result adjacent_results[keyword_instance.class] = keyword_result end if root.custom_keywords.any? resolved_instance_location = Location.resolve(instance_location) root.custom_keywords.each do |custom_keyword, callable| if value.key?(custom_keyword) [*callable.call(instance, value, resolved_instance_location)].each do |custom_keyword_result| custom_keyword_valid = custom_keyword_result == true valid &&= custom_keyword_valid type = custom_keyword_result.is_a?(String) ? custom_keyword_result : custom_keyword details = { 'keyword' => custom_keyword, 'result' => custom_keyword_result } nested << result(instance, instance_location, keyword_location, custom_keyword_valid, :type => type, :details => details) end end end end result(instance, instance_location, keyword_location, valid, nested) ensure context.dynamic_scope.pop context.adjacent_results = original_adjacent_results end end |
#validate_schema(**options) ⇒ Object
119 120 121 |
# File 'lib/json_schemer/schema.rb', line 119 def validate_schema(**) .validate(value, **) end |