json-schema-6.0.0/0000755000004100000410000000000015050634712013757 5ustar www-datawww-datajson-schema-6.0.0/lib/0000755000004100000410000000000015050634712014525 5ustar www-datawww-datajson-schema-6.0.0/lib/json-schema.rb0000644000004100000410000000135715050634712017267 0ustar www-datawww-datarequire 'rubygems' if Gem::Specification.find_all_by_name('multi_json').any? require 'multi_json' # Force MultiJson to load an engine before we define the JSON constant here; otherwise, # it looks for things that are under the JSON namespace that aren't there (since we have defined it here) MultiJson.respond_to?(:adapter) ? MultiJson.adapter : MultiJson.engine end require 'json-schema/util/array_set' require 'json-schema/util/uri' require 'json-schema/schema' require 'json-schema/schema/reader' require 'json-schema/validator' Dir[File.join(File.dirname(__FILE__), 'json-schema/attributes/**/*.rb')].each { |file| require file } Dir[File.join(File.dirname(__FILE__), 'json-schema/validators/*.rb')].sort!.each { |file| require file } json-schema-6.0.0/lib/json-schema/0000755000004100000410000000000015050634712016734 5ustar www-datawww-datajson-schema-6.0.0/lib/json-schema/validator.rb0000644000004100000410000004706715050634712021264 0ustar www-datawww-datarequire 'open-uri' require 'pathname' require 'bigdecimal' require 'digest/sha1' require 'date' require 'timeout' require 'stringio' require 'yaml' require 'delegate' require 'json-schema/schema/reader' require 'json-schema/errors/schema_error' require 'json-schema/errors/schema_parse_error' require 'json-schema/errors/json_load_error' require 'json-schema/errors/json_parse_error' require 'json-schema/util/uri' module JSON class Validator @@schemas = {} @@cache_schemas = true @@default_opts = { list: false, version: nil, validate_schema: false, record_errors: false, errors_as_objects: false, insert_defaults: false, clear_cache: false, strict: false, allPropertiesRequired: false, noAdditionalProperties: false, parse_data: true, parse_integer: true, } @@validators = {} @@default_validator = nil @@available_json_backends = [] @@json_backend = nil @@serializer = nil @@mutex = Mutex.new def initialize(schema_data, opts = {}) @options = @@default_opts.clone.merge(opts) configured_validator = self.class.validator_for_name(@options[:version]) @options[:schema_reader] ||= self.class.schema_reader @validation_options = @options[:record_errors] ? { record_errors: true } : {} @validation_options[:insert_defaults] = true if @options[:insert_defaults] if @options[:strict] == true @validation_options[:allPropertiesRequired] = true @validation_options[:noAdditionalProperties] = true else @validation_options[:allPropertiesRequired] = true if @options[:allPropertiesRequired] @validation_options[:noAdditionalProperties] = true if @options[:noAdditionalProperties] end @validation_options[:clear_cache] = true if !@@cache_schemas || @options[:clear_cache] @@mutex.synchronize { @base_schema = initialize_schema(schema_data, configured_validator) } @@mutex.synchronize { build_schemas(@base_schema) } # validate the schema, if requested if @options[:validate_schema] # Don't clear the cache during metaschema validation! meta_validator = self.class.new(@base_schema.validator.metaschema, { clear_cache: false }) meta_validator.validate(@base_schema.schema) end # If the :fragment option is set, try and validate against the fragment if opts[:fragment] @base_schema = schema_from_fragment(@base_schema, opts[:fragment]) end end def schema_from_fragment(base_schema, fragment) schema_uri = base_schema.uri fragments = fragment.split('/').map { |f| f.gsub('~0', '~').gsub('~1', '/') } # ensure the first element was a hash, per the fragment spec if fragments.shift != '#' raise JSON::Schema::SchemaError, 'Invalid fragment syntax in :fragment option' end schema_fragment = base_schema.schema fragments.each do |f| case schema_fragment when Hash schema_fragment = schema_fragment[f] when Array schema_fragment = schema_fragment[f.to_i] end end unless schema_fragment.is_a?(Hash) raise JSON::Schema::SchemaError, 'Invalid fragment resolution for :fragment option' end schema = JSON::Schema.new(schema_fragment, schema_uri, base_schema.validator) if @options[:list] schema.to_array_schema elsif schema.is_a?(Hash) JSON::Schema.new(schema, schema_uri, @options[:version]) else schema end end class ErrorRecorder < SimpleDelegator def initialize(sub) @errors = [] super end def validation_error(error) @errors.push(error) end def validation_errors @errors end def with_errors self end end def with_errors ErrorRecorder.new(self) end # Run a simple true/false validation of data against a schema def validate(data) original_data = data data = initialize_data(data) error_recorder = with_errors @base_schema.validate(data, [], error_recorder, @validation_options) if @options[:record_errors] if @options[:errors_as_objects] error_recorder.validation_errors.map { |e| e.to_hash } else error_recorder.validation_errors.map { |e| e.to_string } end else true end ensure if @validation_options[:clear_cache] == true self.class.clear_cache end if @validation_options[:insert_defaults] self.class.merge_missing_values(data, original_data) end end def load_ref_schema(parent_schema, ref) schema_uri = JSON::Util::URI.absolutize_ref(ref, parent_schema.uri) return true if self.class.schema_loaded?(schema_uri) validator = self.class.validator_for_uri(schema_uri, false) schema_uri = JSON::Util::URI.file_uri(validator.metaschema) if validator schema = @options[:schema_reader].read(schema_uri) self.class.add_schema(schema) build_schemas(schema) end # Build all schemas with IDs, mapping out the namespace def build_schemas(parent_schema) schema = parent_schema.schema # Build ref schemas if they exist if schema['$ref'] load_ref_schema(parent_schema, schema['$ref']) end case schema['extends'] when String load_ref_schema(parent_schema, schema['extends']) when Array schema['extends'].each do |type| handle_schema(parent_schema, type) end end # Check for schemas in union types %w[type disallow].each do |key| next unless schema[key].is_a?(Array) schema[key].each do |type| if type.is_a?(Hash) handle_schema(parent_schema, type) end end end # Schema properties whose values are objects, the values of which # are themselves schemas. %w[definitions properties patternProperties].each do |key| next unless value = schema[key] value.each do |_k, inner_schema| handle_schema(parent_schema, inner_schema) end end # Schema properties whose values are themselves schemas. %w[additionalProperties additionalItems dependencies extends].each do |key| next unless schema[key].is_a?(Hash) handle_schema(parent_schema, schema[key]) end # Schema properties whose values may be an array of schemas. %w[allOf anyOf oneOf not].each do |key| next unless value = schema[key] Array(value).each do |inner_schema| handle_schema(parent_schema, inner_schema) end end # Items are always schemas if schema['items'] items = schema['items'].clone items = [items] unless items.is_a?(Array) items.each do |item| handle_schema(parent_schema, item) end end # Convert enum to a ArraySet if schema['enum'].is_a?(Array) schema['enum'] = ArraySet.new(schema['enum']) end end # Either load a reference schema or create a new schema def handle_schema(parent_schema, obj) if obj.is_a?(Hash) schema_uri = parent_schema.uri.dup schema = JSON::Schema.new(obj, schema_uri, parent_schema.validator) if obj['id'] self.class.add_schema(schema) end build_schemas(schema) end end class << self def validate(schema, data, opts = {}) validate!(schema, data, opts) rescue JSON::Schema::ValidationError, JSON::Schema::SchemaError false end def validate_json(schema, data, opts = {}) validate(schema, data, opts.merge(json: true)) end def validate_uri(schema, data, opts = {}) validate(schema, data, opts.merge(uri: true)) end def validate!(schema, data, opts = {}) validator = new(schema, opts) validator.validate(data) end def validate2(schema, data, opts = {}) warn '[DEPRECATION NOTICE] JSON::Validator#validate2 has been replaced by JSON::Validator#validate! and will be removed in version >= 3. Please use the #validate! method instead.' validate!(schema, data, opts) end def validate_json!(schema, data, opts = {}) validate!(schema, data, opts.merge(json: true)) end def validate_uri!(schema, data, opts = {}) validate!(schema, data, opts.merge(uri: true)) end def fully_validate(schema, data, opts = {}) validate!(schema, data, opts.merge(record_errors: true)) end def fully_validate_schema(schema, opts = {}) data = schema schema = validator_for_name(opts[:version]).metaschema fully_validate(schema, data, opts) end def fully_validate_json(schema, data, opts = {}) fully_validate(schema, data, opts.merge(json: true)) end def fully_validate_uri(schema, data, opts = {}) fully_validate(schema, data, opts.merge(uri: true)) end def schema_reader @@schema_reader ||= JSON::Schema::Reader.new end def schema_reader=(reader) @@schema_reader = reader end def clear_cache @@schemas = {} end def schemas @@schemas end def add_schema(schema) @@schemas[schema_key_for(schema.uri)] ||= schema end def schema_for_uri(uri) # We only store normalized uris terminated with fragment #, so we can try whether # normalization can be skipped @@schemas[uri] || @@schemas[schema_key_for(uri)] end def schema_loaded?(schema_uri) !schema_for_uri(schema_uri).nil? end def schema_key_for(uri) key = Util::URI.normalized_uri(uri).to_s key.end_with?('#') ? key : "#{key}#" end def cache_schemas=(val) warn '[DEPRECATION NOTICE] Schema caching is now a validation option. Schemas will still be cached if this is set to true, but this method will be removed in version >= 3. Please use the :clear_cache validation option instead.' @@cache_schemas = val == true end def validators @@validators end def default_validator @@default_validator end def validator_for_uri(schema_uri, raise_not_found = true) return default_validator unless schema_uri u = JSON::Util::URI.parse(schema_uri) validator = validators["#{u.scheme}://#{u.host}#{u.path}"] if validator.nil? && raise_not_found raise JSON::Schema::SchemaError, "Schema not found: #{schema_uri}" else validator end end def validator_for_name(schema_name, raise_not_found = true) return default_validator unless schema_name schema_name = schema_name.to_s validator = validators.values.detect do |v| Array(v.names).include?(schema_name) end if validator.nil? && raise_not_found raise JSON::Schema::SchemaError, 'The requested JSON schema version is not supported' else validator end end def validator_for(schema_uri) warn '[DEPRECATION NOTICE] JSON::Validator#validator_for has been replaced by JSON::Validator#validator_for_uri and will be removed in version >= 3. Please use the #validator_for_uri method instead.' validator_for_uri(schema_uri) end def register_validator(v) @@validators["#{v.uri.scheme}://#{v.uri.host}#{v.uri.path}"] = v end def register_default_validator(v) @@default_validator = v end def register_format_validator(format, validation_proc, versions = (@@validators.flat_map { |_k, v| v.names.first } + [nil])) custom_format_validator = JSON::Schema::CustomFormat.new(validation_proc) versions.each do |version| validator = validator_for_name(version) validator.formats[format.to_s] = custom_format_validator end end def deregister_format_validator(format, versions = (@@validators.flat_map { |_k, v| v.names.first } + [nil])) versions.each do |version| validator = validator_for_name(version) validator.formats[format.to_s] = validator.default_formats[format.to_s] end end def restore_default_formats(versions = (@@validators.flat_map { |_k, v| v.names.first } + [nil])) versions.each do |version| validator = validator_for_name(version) validator.formats = validator.default_formats.clone end end def json_backend if defined?(MultiJson) MultiJson.respond_to?(:adapter) ? MultiJson.adapter : MultiJson.engine else @@json_backend end end def json_backend=(backend) if defined?(MultiJson) backend = 'json_gem' if backend == 'json' MultiJson.respond_to?(:use) ? MultiJson.use(backend) : MultiJson.engine = backend else backend = backend.to_s if @@available_json_backends.include?(backend) @@json_backend = backend else raise JSON::Schema::JsonParseError, "The JSON backend '#{backend}' could not be found." end end end def parse(s) if defined?(MultiJson) begin MultiJson.respond_to?(:adapter) ? MultiJson.load(s) : MultiJson.decode(s) rescue MultiJson::ParseError => e raise JSON::Schema::JsonParseError, e.message end else case @@json_backend.to_s when 'json' begin JSON.parse(s, quirks_mode: true) rescue JSON::ParserError => e raise JSON::Schema::JsonParseError, e.message end when 'yajl' begin json = StringIO.new(s) parser = Yajl::Parser.new parser.parse(json) or raise(JSON::Schema::JsonParseError, 'The JSON could not be parsed by yajl') rescue Yajl::ParseError => e raise JSON::Schema::JsonParseError, e.message end else raise JSON::Schema::JsonParseError, "No supported JSON parsers found. The following parsers are suported:\n * yajl-ruby\n * json" end end end def merge_missing_values(source, destination) case destination when Hash source.each do |key, source_value| destination_value = destination[key] || destination[key.to_sym] if destination_value.nil? destination[key] = source_value else merge_missing_values(source_value, destination_value) end end when Array source.each_with_index do |source_value, i| destination_value = destination[i] merge_missing_values(source_value, destination_value) end end end unless defined?(MultiJson) if Gem::Specification.find_all_by_name('json').any? require 'json' @@available_json_backends << 'json' @@json_backend = 'json' else # Try force-loading json for rubies > 1.9.2 begin require 'json' @@available_json_backends << 'json' @@json_backend = 'json' rescue LoadError end end if Gem::Specification.find_all_by_name('yajl-ruby').any? require 'yajl' @@available_json_backends << 'yajl' @@json_backend = 'yajl' end @@serializer = if @@json_backend == 'yajl' ->(o) { Yajl::Encoder.encode(o) } elsif @@json_backend == 'json' ->(o) { JSON.dump(o) } else ->(o) { YAML.dump(o) } end end end private if Gem::Specification.find_all_by_name('uuidtools').any? require 'uuidtools' @@fake_uuid_generator = ->(s) { UUIDTools::UUID.sha1_create(UUIDTools::UUID_URL_NAMESPACE, s).to_s } else require 'json-schema/util/uuid' @@fake_uuid_generator = ->(s) { JSON::Util::UUID.create_v5(s, JSON::Util::UUID::Nil).to_s } end def serialize(schema) if defined?(MultiJson) MultiJson.respond_to?(:dump) ? MultiJson.dump(schema) : MultiJson.encode(schema) else @@serializer.call(schema) end end def fake_uuid(schema) @@fake_uuid_generator.call(schema) end def initialize_schema(schema, default_validator) if schema.is_a?(String) begin # Build a fake URI for this schema_uri = JSON::Util::URI.parse(fake_uuid(schema)) schema = JSON::Schema.new(JSON::Validator.parse(schema), schema_uri, default_validator) if @options[:list] && @options[:fragment].nil? schema = schema.to_array_schema end self.class.add_schema(schema) rescue JSON::Schema::JsonParseError # Build a uri for it schema_uri = Util::URI.normalized_uri(schema) if self.class.schema_loaded?(schema_uri) schema = self.class.schema_for_uri(schema_uri) if @options[:list] && @options[:fragment].nil? schema = schema.to_array_schema schema.uri = JSON::Util::URI.parse(fake_uuid(serialize(schema.schema))) self.class.add_schema(schema) end schema else schema = @options[:schema_reader].read(schema_uri) schema = JSON::Schema.stringify(schema) if @options[:list] && @options[:fragment].nil? schema = schema.to_array_schema end self.class.add_schema(schema) end end elsif schema.is_a?(Hash) schema_uri = JSON::Util::URI.parse(fake_uuid(serialize(schema))) schema = JSON::Schema.stringify(schema) schema = JSON::Schema.new(schema, schema_uri, default_validator) if @options[:list] && @options[:fragment].nil? schema = schema.to_array_schema end self.class.add_schema(schema) else raise JSON::Schema::SchemaParseError, 'Invalid schema - must be either a string or a hash' end schema end def initialize_data(data) if @options[:parse_data] if @options[:json] data = self.class.parse(data) elsif @options[:uri] json_uri = Util::URI.normalized_uri(data) data = self.class.parse(custom_open(json_uri)) elsif data.is_a?(String) begin # Check if the string is valid integer strict_convert = data.match?(/\A[+-]?\d+\z/) && !@options[:parse_integer] data = self.class.parse(data) unless strict_convert rescue JSON::Schema::JsonParseError begin json_uri = Util::URI.normalized_uri(data) data = self.class.parse(custom_open(json_uri)) rescue JSON::Schema::JsonLoadError, JSON::Schema::UriError # Silently discard the error - use the data as-is end end end end JSON::Schema.stringify(data) end def custom_open(uri) uri = Util::URI.normalized_uri(uri) if uri.is_a?(String) if uri.absolute? && Util::URI::SUPPORTED_PROTOCOLS.include?(uri.scheme) begin URI.open(uri.to_s).read rescue OpenURI::HTTPError, Timeout::Error => e raise JSON::Schema::JsonLoadError, e.message end else begin File.read(JSON::Util::URI.unescaped_path(uri)) rescue SystemCallError => e raise JSON::Schema::JsonLoadError, e.message end end end end end json-schema-6.0.0/lib/json-schema/schema.rb0000644000004100000410000000332415050634712020523 0ustar www-datawww-datarequire 'pathname' module JSON class Schema attr_accessor :schema, :uri, :validator def initialize(schema, uri, parent_validator = nil) @schema = schema @uri = uri # If there is an ID on this schema, use it to generate the URI if @schema['id'].is_a?(String) temp_uri = JSON::Util::URI.parse(@schema['id']) if temp_uri.relative? temp_uri = uri.join(temp_uri) end @uri = temp_uri end @uri = JSON::Util::URI.strip_fragment(@uri) # If there is a $schema on this schema, use it to determine which validator to use @validator = if @schema['$schema'] JSON::Validator.validator_for_uri(@schema['$schema']) elsif parent_validator parent_validator else JSON::Validator.default_validator end end def validate(data, fragments, processor, options = {}) @validator.validate(self, data, fragments, processor, options) end def self.stringify(schema) case schema when Hash schema.map { |key, _value| [key.to_s, stringify(schema[key])] }.to_h when Array schema.map do |schema_item| stringify(schema_item) end when Symbol schema.to_s else schema end end # @return [JSON::Schema] a new schema matching an array whose items all match this schema. def to_array_schema array_schema = { 'type' => 'array', 'items' => schema } array_schema['$schema'] = schema['$schema'] unless schema['$schema'].nil? self.class.new(array_schema, uri, validator) end def to_s @schema.to_json end end end json-schema-6.0.0/lib/json-schema/schema/0000755000004100000410000000000015050634712020174 5ustar www-datawww-datajson-schema-6.0.0/lib/json-schema/schema/reader.rb0000644000004100000410000001070115050634712021762 0ustar www-datawww-datarequire 'open-uri' require 'pathname' module JSON class Schema # Base for any reading exceptions encountered by {JSON::Schema::Reader} class ReadError < StandardError # @return [String] the requested schema location which was refused attr_reader :location # @return [Symbol] either +:uri+ or +:file+ attr_reader :type def initialize(location, type) @location = location @type = type super(error_message) end private def type_string (type == :uri) ? 'URI' : type.to_s end end # Raised by {JSON::Schema::Reader} when one of its settings indicate # a schema should not be read. class ReadRefused < ReadError private def error_message "Read of #{type_string} at #{location} refused" end end # Raised by {JSON::Schema::Reader} when an attempt to read a schema fails class ReadFailed < ReadError private def error_message "Read of #{type_string} at #{location} failed" end end # When an unregistered schema is encountered, the {JSON::Schema::Reader} is # used to fetch its contents and register it with the {JSON::Validator}. # # This default reader will read schemas from the filesystem or from a URI. class Reader # The behavior of the schema reader can be controlled by providing # callbacks to determine whether to permit reading referenced schemas. # The options +accept_uri+ and +accept_file+ should be procs which # accept a +URI+ or +Pathname+ object, and return a boolean value # indicating whether to read the referenced schema. # # URIs using the +file+ scheme will be normalized into +Pathname+ objects # and passed to the +accept_file+ callback. # # @param options [Hash] # @option options [Boolean, #call] accept_uri (true) # @option options [Boolean, #call] accept_file (true) # # @example Reject all unregistered schemas # JSON::Validator.schema_reader = JSON::Schema::Reader.new( # :accept_uri => false, # :accept_file => false # ) # # @example Only permit URIs from certain hosts # JSON::Validator.schema_reader = JSON::Schema::Reader.new( # :accept_file => false, # :accept_uri => proc { |uri| ['mycompany.com', 'json-schema.org'].include?(uri.host) } # ) def initialize(options = {}) @accept_uri = options.fetch(:accept_uri, true) @accept_file = options.fetch(:accept_file, true) end # @param location [#to_s] The location from which to read the schema # @return [JSON::Schema] # @raise [JSON::Schema::ReadRefused] if +accept_uri+ or +accept_file+ # indicated the schema could not be read # @raise [JSON::Schema::ParseError] if the schema was not a valid JSON object # @raise [JSON::Schema::ReadFailed] if reading the location was acceptable but the # attempt to retrieve it failed def read(location) uri = JSON::Util::URI.parse(location.to_s) body = if uri.scheme.nil? || uri.scheme == 'file' uri = JSON::Util::URI.file_uri(uri) read_file(Pathname.new(uri.path).expand_path) else read_uri(uri) end JSON::Schema.new(JSON::Validator.parse(body), uri) end # @param uri [Addressable::URI] # @return [Boolean] def accept_uri?(uri) if @accept_uri.respond_to?(:call) @accept_uri.call(uri) else @accept_uri end end # @param pathname [Pathname] # @return [Boolean] def accept_file?(pathname) if @accept_file.respond_to?(:call) @accept_file.call(pathname) else @accept_file end end private def read_uri(uri) if accept_uri?(uri) URI.open(uri.to_s).read else raise JSON::Schema::ReadRefused.new(uri.to_s, :uri) end rescue OpenURI::HTTPError, SocketError raise JSON::Schema::ReadFailed.new(uri.to_s, :uri) end def read_file(pathname) if accept_file?(pathname) File.read(JSON::Util::URI.unescaped_path(pathname.to_s)) else raise JSON::Schema::ReadRefused.new(pathname.to_s, :file) end rescue Errno::ENOENT raise JSON::Schema::ReadFailed.new(pathname.to_s, :file) end end end end json-schema-6.0.0/lib/json-schema/schema/validator.rb0000644000004100000410000000223415050634712022507 0ustar www-datawww-datamodule JSON class Schema class Validator attr_accessor :attributes, :formats, :uri, :names attr_reader :default_formats def initialize @attributes = {} @formats = {} @default_formats = {} @uri = nil @names = [] @metaschema_name = '' end def extend_schema_definition(schema_uri) warn '[DEPRECATION NOTICE] The preferred way to extend a Validator is by subclassing, rather than #extend_schema_definition. This method will be removed in version >= 3.' validator = JSON::Validator.validator_for_uri(schema_uri) @attributes.merge!(validator.attributes) end def validate(current_schema, data, fragments, processor, options = {}) current_schema.schema.each do |attr_name, _attribute| if @attributes.has_key?(attr_name.to_s) @attributes[attr_name.to_s].validate(current_schema, data, fragments, processor, self, options) end end data end def metaschema resources = File.expand_path('../../../resources', __dir__) File.join(resources, @metaschema_name) end end end end json-schema-6.0.0/lib/json-schema/util/0000755000004100000410000000000015050634712017711 5ustar www-datawww-datajson-schema-6.0.0/lib/json-schema/util/array_set.rb0000644000004100000410000000077015050634712022233 0ustar www-datawww-data# This is a hack that I don't want to ever use anywhere else or repeat EVER, but we need enums to be # an Array to pass schema validation. But we also want fast lookup! class ArraySet < Array def include?(obj) unless defined? @values @values = Set.new each { |x| @values << convert_to_float_if_numeric(x) } end @values.include?(convert_to_float_if_numeric(obj)) end private def convert_to_float_if_numeric(value) value.is_a?(Numeric) ? value.to_f : value end end json-schema-6.0.0/lib/json-schema/util/uuid.rb0000755000004100000410000002036415050634712021214 0ustar www-datawww-data#!/usr/bin/env ruby ### http://mput.dip.jp/mput/uuid.txt # Copyright(c) 2005 URABE, Shyouhei. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this code, to deal in the code without restriction, including without # limitation the rights to use, copy, modify, merge, publish, distribute, # sublicense, and/or sell copies of the code, and to permit persons to whom the # code is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the code. # # THE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE CODE OR THE USE OR OTHER DEALINGS IN THE # CODE. # # 2009-02-20: Modified by Pablo Lorenzoni to correctly # include the version in the raw_bytes. require 'digest/md5' require 'digest/sha1' require 'tmpdir' module JSON module Util # Pure ruby UUID generator, which is compatible with RFC4122 UUID = Struct.new :raw_bytes class UUID private_class_method :new class << self def mask(v, str) nstr = str.bytes.to_a version = [0, 16, 32, 48, 64, 80][v] nstr[6] &= 0b00001111 nstr[6] |= version # nstr[7] &= 0b00001111 # nstr[7] |= 0b01010000 nstr[8] &= 0b00111111 nstr[8] |= 0b10000000 str = +'' nstr.each { |s| str << s.chr } str end private :mask # UUID generation using SHA1. Recommended over create_md5. # Namespace object is another UUID, some of them are pre-defined below. def create_sha1(str, namespace) sha1 = Digest::SHA1.new sha1.update namespace.raw_bytes sha1.update str sum = sha1.digest raw = mask 5, sum[0..15] ret = new raw ret.freeze ret end alias create_v5 create_sha1 # UUID generation using MD5 (for backward compat.) def create_md5(str, namespace) md5 = Digest::MD5.new md5.update namespace.raw_bytes md5.update str sum = md5.digest raw = mask 3, sum[0..16] ret = new raw ret.freeze ret end alias create_v3 create_md5 # UUID generation using random-number generator. From it's random # nature, there's no warranty that the created ID is really universaly # unique. def create_random rnd = [ rand(0x100000000), rand(0x100000000), rand(0x100000000), rand(0x100000000), ].pack 'N4' raw = mask 4, rnd ret = new raw ret.freeze ret end alias create_v4 create_random def read_state(fp) # :nodoc: fp.rewind Marshal.load fp.read end def write_state(fp, c, m) # :nodoc: fp.rewind str = Marshal.dump [c, m] fp.write str end private :read_state, :write_state STATE_FILE = 'ruby-uuid' # create the "version 1" UUID with current system clock, current UTC # timestamp, and the IEEE 802 address (so-called MAC address). # # Speed notice: it's slow. It writes some data into hard drive on every # invokation. If you want to speed this up, try remounting tmpdir with a # memory based filesystem (such as tmpfs). STILL slow? then no way but # rewrite it with c :) def create(clock = nil, time = nil, mac_addr = nil) c = t = m = nil Dir.chdir Dir.tmpdir do unless FileTest.exist? STATE_FILE # Generate a pseudo MAC address because we have no pure-ruby way # to know the MAC address of the NIC this system uses. Note # that cheating with pseudo arresses here is completely legal: # see Section 4.5 of RFC4122 for details. sha1 = Digest::SHA1.new 256.times do r = [rand(0x100000000)].pack 'N' sha1.update r end str = sha1.digest r = rand 14 # 20-6 node = str[r, 6] || str nnode = node.bytes.to_a nnode[0] |= 0x01 node = '' nnode.each { |s| node << s.chr } k = rand 0x40000 open STATE_FILE, 'w' do |fp| fp.flock IO::LOCK_EX write_state fp, k, node fp.chmod 0o777 # must be world writable end end open STATE_FILE, 'r+' do |fp| fp.flock IO::LOCK_EX c, m = read_state fp c = clock % 0x4000 if clock m = mac_addr if mac_addr t = time if t.nil? # UUID epoch is 1582/Oct/15 tt = Time.now t = (tt.to_i * 10_000_000) + (tt.tv_usec * 10) + 0x01B21DD213814000 end c = c.succ # important; increment here write_state fp, c, m end end tl = t & 0xFFFF_FFFF tm = t >> 32 tm &= 0xFFFF th = t >> 48 th &= 0x0FFF th |= 0x1000 cl = c & 0xFF ch = c & 0x3F00 ch >>= 8 ch |= 0x80 pack tl, tm, th, cl, ch, m end alias create_v1 create # A simple GUID parser: just ignores unknown characters and convert # hexadecimal dump into 16-octet object. def parse(obj) str = obj.to_s.sub(/\Aurn:uuid:/, '') str.gsub!(/[^0-9A-Fa-f]/, '') raw = str[0..31].lines.to_a.pack 'H*' ret = new raw ret.freeze ret end # The 'primitive constructor' of this class # Note UUID.pack(uuid.unpack) == uuid def pack(tl, tm, th, ch, cl, n) raw = [tl, tm, th, ch, cl, n].pack 'NnnCCa6' ret = new raw ret.freeze ret end end # The 'primitive deconstructor', or the dual to pack. # Note UUID.pack(uuid.unpack) == uuid def unpack raw_bytes.unpack 'NnnCCa6' end # Generate the string representation (a.k.a GUID) of this UUID def to_s a = unpack tmp = a[-1].unpack 'C*' a[-1] = format '%02x%02x%02x%02x%02x%02x', *tmp '%08x-%04x-%04x-%02x%02x-%s' % a end alias guid to_s # Convert into a RFC4122-comforming URN representation def to_uri 'urn:uuid:' + to_s end alias urn to_uri # Convert into 128-bit unsigned integer def to_int tmp = raw_bytes.unpack 'C*' tmp.inject do |r, i| (r * 256) | i end end alias to_i to_int # Gets the version of this UUID # returns nil if bad version def version a = unpack v = (a[2] & 0xF000).to_s(16)[0].chr.to_i return v if (1..5).include? v nil end # Two UUIDs are said to be equal if and only if their (byte-order # canonicalized) integer representations are equivalent. Refer RFC4122 for # details. def ==(other) to_i == other.to_i end include Comparable # UUIDs are comparable (don't know what benefits are there, though). def <=>(other) to_s <=> other.to_s end # Pre-defined UUID Namespaces described in RFC4122 Appendix C. NameSpace_DNS = parse '6ba7b810-9dad-11d1-80b4-00c04fd430c8' NameSpace_URL = parse '6ba7b811-9dad-11d1-80b4-00c04fd430c8' NameSpace_OID = parse '6ba7b812-9dad-11d1-80b4-00c04fd430c8' NameSpace_X500 = parse '6ba7b814-9dad-11d1-80b4-00c04fd430c8' # The Nil UUID in RFC4122 Section 4.1.7 Nil = parse '00000000-0000-0000-0000-000000000000' end end end json-schema-6.0.0/lib/json-schema/util/uri.rb0000644000004100000410000000714715050634712021046 0ustar www-datawww-data# frozen_string_literal: true require 'addressable/uri' module JSON module Util # @api private class URI < Addressable::URI SUPPORTED_PROTOCOLS = %w[http https ftp tftp sftp ssh svn+ssh telnet nntp gopher wais ldap prospero] class << self alias unescape_uri unescape # @param uri [String, Addressable::URI] # @return [Addressable::URI, nil] def parse(uri) super rescue Addressable::URI::InvalidURIError => e raise JSON::Schema::UriError, e.message end # @param uri [String, Addressable::URI] # @return [Addressable::URI, nil] def file_uri(uri) convert_path(parse(uri).path) end # @param uri [String, Addressable::URI # @return [String] def unescaped_path(uri) parse(uri).unescaped_path end # Strips the fragment from the URI. # @param uri [String, Addressable::URI] # @return [Addressable::URI] def strip_fragment(uri) parse(uri).strip_fragment end # @param uri [String, Addressable::URI] # @return [Addressable::URI] def normalized_uri(uri, base_path = Dir.pwd) parse(uri).normalized_uri(base_path) end # Normalizes the reference URI based on the provided base URI # # @param ref [String, Addressable::URI] # @param base [String, Addressable::URI] # @return [Addressable::URI] def normalize_ref(ref, base) parse(ref).normalize_ref(base) end def absolutize_ref(ref, base) parse(ref).absolutize_ref(base) end end # Unencodes any percent encoded characters within a path component. # # @return [String] def unescaped_path self.class.unescape_component(path) end # Strips the fragment from the URI. # @return [Addressable::URI] a new instance of URI without a fragment def strip_fragment if fragment.nil? || fragment.empty? self else merge(fragment: '') end end # Normalizes the URI based on the provided base path. # # @param base_path [String] the base path to use for relative URIs. Defaults to the current working directory. # @return [Addressable::URI] the normalized URI or nil def normalized_uri(base_path = Dir.pwd) if relative? if path[0, 1] == '/' self.class.file_uri(self) else self.class.file_uri(File.join(base_path, self)) end else self end end # @param base [Addressable::URI, String] # @return [Addressable::URI] def normalize_ref(base) base_uri = self.class.parse(base) defer_validation do if relative? # Check for absolute path path, fragment = to_s.split('#') merge!(base_uri) if path.nil? || path == '' self.path = base_uri.path elsif path[0, 1] == '/' self.path = Pathname.new(path).cleanpath.to_s else join!(path) end self.fragment = fragment end self.fragment = '' if self.fragment.nil? || self.fragment.empty? end self end # @param base [Addressable::URI, String] # @return [Addressable::URI] def absolutize_ref(base) ref = strip_fragment if ref.absolute? ref else self.class.strip_fragment(base).join(ref.path).normalized_uri end end end end end json-schema-6.0.0/lib/json-schema/validators/0000755000004100000410000000000015050634712021104 5ustar www-datawww-datajson-schema-6.0.0/lib/json-schema/validators/hyper-draft6.rb0000644000004100000410000000037615050634712023752 0ustar www-datawww-datamodule JSON class Schema class HyperDraft6 < Draft6 def initialize super @uri = JSON::Util::URI.parse('http://json-schema.org/draft-06/hyper-schema#') end JSON::Validator.register_validator(new) end end end json-schema-6.0.0/lib/json-schema/validators/draft3.rb0000644000004100000410000000364415050634712022623 0ustar www-datawww-datarequire 'json-schema/schema/validator' module JSON class Schema class Draft3 < Validator def initialize super @attributes = { 'type' => JSON::Schema::TypeAttribute, 'disallow' => JSON::Schema::DisallowAttribute, 'format' => JSON::Schema::FormatAttribute, 'maximum' => JSON::Schema::MaximumAttribute, 'minimum' => JSON::Schema::MinimumAttribute, 'minItems' => JSON::Schema::MinItemsAttribute, 'maxItems' => JSON::Schema::MaxItemsAttribute, 'uniqueItems' => JSON::Schema::UniqueItemsAttribute, 'minLength' => JSON::Schema::MinLengthAttribute, 'maxLength' => JSON::Schema::MaxLengthAttribute, 'divisibleBy' => JSON::Schema::DivisibleByAttribute, 'enum' => JSON::Schema::EnumAttribute, 'properties' => JSON::Schema::PropertiesAttribute, 'pattern' => JSON::Schema::PatternAttribute, 'patternProperties' => JSON::Schema::PatternPropertiesAttribute, 'additionalProperties' => JSON::Schema::AdditionalPropertiesAttribute, 'items' => JSON::Schema::ItemsAttribute, 'additionalItems' => JSON::Schema::AdditionalItemsAttribute, 'dependencies' => JSON::Schema::DependenciesAttribute, 'extends' => JSON::Schema::ExtendsAttribute, '$ref' => JSON::Schema::RefAttribute, } @default_formats = { 'date-time' => DateTimeFormat, 'date' => DateFormat, 'ip-address' => IP4Format, 'ipv6' => IP6Format, 'time' => TimeFormat, 'uri' => UriFormat, } @formats = @default_formats.clone @uri = JSON::Util::URI.parse('http://json-schema.org/draft-03/schema#') @names = ['draft3', 'http://json-schema.org/draft-03/schema#'] @metaschema_name = 'draft-03.json' end JSON::Validator.register_validator(new) end end end json-schema-6.0.0/lib/json-schema/validators/hyper-draft1.rb0000644000004100000410000000037615050634712023745 0ustar www-datawww-datamodule JSON class Schema class HyperDraft1 < Draft1 def initialize super @uri = JSON::Util::URI.parse('http://json-schema.org/draft-01/hyper-schema#') end JSON::Validator.register_validator(new) end end end json-schema-6.0.0/lib/json-schema/validators/hyper-draft2.rb0000644000004100000410000000037615050634712023746 0ustar www-datawww-datamodule JSON class Schema class HyperDraft2 < Draft2 def initialize super @uri = JSON::Util::URI.parse('http://json-schema.org/draft-02/hyper-schema#') end JSON::Validator.register_validator(new) end end end json-schema-6.0.0/lib/json-schema/validators/draft4.rb0000644000004100000410000000444115050634712022620 0ustar www-datawww-datarequire 'json-schema/schema/validator' module JSON class Schema class Draft4 < Validator def initialize super @attributes = { 'type' => JSON::Schema::TypeV4Attribute, 'allOf' => JSON::Schema::AllOfAttribute, 'anyOf' => JSON::Schema::AnyOfAttribute, 'oneOf' => JSON::Schema::OneOfAttribute, 'not' => JSON::Schema::NotAttribute, 'disallow' => JSON::Schema::DisallowAttribute, 'format' => JSON::Schema::FormatAttribute, 'maximum' => JSON::Schema::MaximumAttribute, 'minimum' => JSON::Schema::MinimumAttribute, 'minItems' => JSON::Schema::MinItemsAttribute, 'maxItems' => JSON::Schema::MaxItemsAttribute, 'minProperties' => JSON::Schema::MinPropertiesAttribute, 'maxProperties' => JSON::Schema::MaxPropertiesAttribute, 'uniqueItems' => JSON::Schema::UniqueItemsAttribute, 'minLength' => JSON::Schema::MinLengthAttribute, 'maxLength' => JSON::Schema::MaxLengthAttribute, 'multipleOf' => JSON::Schema::MultipleOfAttribute, 'enum' => JSON::Schema::EnumAttribute, 'properties' => JSON::Schema::PropertiesV4Attribute, 'required' => JSON::Schema::RequiredAttribute, 'pattern' => JSON::Schema::PatternAttribute, 'patternProperties' => JSON::Schema::PatternPropertiesAttribute, 'additionalProperties' => JSON::Schema::AdditionalPropertiesAttribute, 'items' => JSON::Schema::ItemsAttribute, 'additionalItems' => JSON::Schema::AdditionalItemsAttribute, 'dependencies' => JSON::Schema::DependenciesV4Attribute, 'extends' => JSON::Schema::ExtendsAttribute, '$ref' => JSON::Schema::RefAttribute, } @default_formats = { 'date-time' => DateTimeV4Format, 'ipv4' => IP4Format, 'ipv6' => IP6Format, 'uri' => UriFormat, } @formats = @default_formats.clone @uri = JSON::Util::URI.parse('http://json-schema.org/draft-04/schema#') @names = ['draft4', 'http://json-schema.org/draft-04/schema#'] @metaschema_name = 'draft-04.json' end JSON::Validator.register_validator(new) JSON::Validator.register_default_validator(new) end end end json-schema-6.0.0/lib/json-schema/validators/draft2.rb0000644000004100000410000000322015050634712022610 0ustar www-datawww-datarequire 'json-schema/schema/validator' module JSON class Schema class Draft2 < Validator def initialize super @attributes = { 'type' => JSON::Schema::TypeAttribute, 'disallow' => JSON::Schema::DisallowAttribute, 'format' => JSON::Schema::FormatAttribute, 'maximum' => JSON::Schema::MaximumInclusiveAttribute, 'minimum' => JSON::Schema::MinimumInclusiveAttribute, 'minItems' => JSON::Schema::MinItemsAttribute, 'maxItems' => JSON::Schema::MaxItemsAttribute, 'uniqueItems' => JSON::Schema::UniqueItemsAttribute, 'minLength' => JSON::Schema::MinLengthAttribute, 'maxLength' => JSON::Schema::MaxLengthAttribute, 'divisibleBy' => JSON::Schema::DivisibleByAttribute, 'enum' => JSON::Schema::EnumAttribute, 'properties' => JSON::Schema::PropertiesOptionalAttribute, 'pattern' => JSON::Schema::PatternAttribute, 'additionalProperties' => JSON::Schema::AdditionalPropertiesAttribute, 'items' => JSON::Schema::ItemsAttribute, 'extends' => JSON::Schema::ExtendsAttribute, } @default_formats = { 'date-time' => DateTimeFormat, 'date' => DateFormat, 'time' => TimeFormat, 'ip-address' => IP4Format, 'ipv6' => IP6Format, 'uri' => UriFormat, } @formats = @default_formats.clone @uri = JSON::Util::URI.parse('http://json-schema.org/draft-02/schema#') @names = ['draft2'] @metaschema_name = 'draft-02.json' end JSON::Validator.register_validator(new) end end end json-schema-6.0.0/lib/json-schema/validators/draft6.rb0000644000004100000410000000461615050634712022626 0ustar www-datawww-datarequire 'json-schema/schema/validator' module JSON class Schema class Draft6 < Validator def initialize super @attributes = { 'type' => JSON::Schema::TypeV4Attribute, 'allOf' => JSON::Schema::AllOfAttribute, 'anyOf' => JSON::Schema::AnyOfAttribute, 'oneOf' => JSON::Schema::OneOfAttribute, 'not' => JSON::Schema::NotAttribute, 'disallow' => JSON::Schema::DisallowAttribute, 'format' => JSON::Schema::FormatAttribute, 'maximum' => JSON::Schema::MaximumAttribute, 'minimum' => JSON::Schema::MinimumAttribute, 'minItems' => JSON::Schema::MinItemsAttribute, 'maxItems' => JSON::Schema::MaxItemsAttribute, 'minProperties' => JSON::Schema::MinPropertiesAttribute, 'maxProperties' => JSON::Schema::MaxPropertiesAttribute, 'uniqueItems' => JSON::Schema::UniqueItemsAttribute, 'minLength' => JSON::Schema::MinLengthAttribute, 'maxLength' => JSON::Schema::MaxLengthAttribute, 'multipleOf' => JSON::Schema::MultipleOfAttribute, 'enum' => JSON::Schema::EnumAttribute, 'properties' => JSON::Schema::PropertiesV4Attribute, 'required' => JSON::Schema::RequiredAttribute, 'pattern' => JSON::Schema::PatternAttribute, 'patternProperties' => JSON::Schema::PatternPropertiesAttribute, 'additionalProperties' => JSON::Schema::AdditionalPropertiesAttribute, 'items' => JSON::Schema::ItemsAttribute, 'additionalItems' => JSON::Schema::AdditionalItemsAttribute, 'dependencies' => JSON::Schema::DependenciesV4Attribute, 'extends' => JSON::Schema::ExtendsAttribute, 'const' => JSON::Schema::ConstAttribute, '$ref' => JSON::Schema::RefAttribute, 'propertyNames' => JSON::Schema::PropertyNames, } @default_formats = { 'date-time' => DateTimeV4Format, 'ipv4' => IP4Format, 'ipv6' => IP6Format, 'uri' => UriFormat, } @formats = @default_formats.clone @uri = JSON::Util::URI.parse('http://json-schema.org/draft-06/schema#') @names = ['draft6', 'http://json-schema.org/draft-06/schema#'] @metaschema_name = 'draft-06.json' end JSON::Validator.register_validator(new) JSON::Validator.register_default_validator(new) end end end json-schema-6.0.0/lib/json-schema/validators/hyper-draft4.rb0000644000004100000410000000037615050634712023750 0ustar www-datawww-datamodule JSON class Schema class HyperDraft4 < Draft4 def initialize super @uri = JSON::Util::URI.parse('http://json-schema.org/draft-04/hyper-schema#') end JSON::Validator.register_validator(new) end end end json-schema-6.0.0/lib/json-schema/validators/hyper-draft3.rb0000644000004100000410000000037615050634712023747 0ustar www-datawww-datamodule JSON class Schema class HyperDraft3 < Draft3 def initialize super @uri = JSON::Util::URI.parse('http://json-schema.org/draft-03/hyper-schema#') end JSON::Validator.register_validator(new) end end end json-schema-6.0.0/lib/json-schema/validators/draft1.rb0000644000004100000410000000311715050634712022614 0ustar www-datawww-datarequire 'json-schema/schema/validator' module JSON class Schema class Draft1 < Validator def initialize super @attributes = { 'type' => JSON::Schema::TypeAttribute, 'disallow' => JSON::Schema::DisallowAttribute, 'format' => JSON::Schema::FormatAttribute, 'maximum' => JSON::Schema::MaximumInclusiveAttribute, 'minimum' => JSON::Schema::MinimumInclusiveAttribute, 'minItems' => JSON::Schema::MinItemsAttribute, 'maxItems' => JSON::Schema::MaxItemsAttribute, 'minLength' => JSON::Schema::MinLengthAttribute, 'maxLength' => JSON::Schema::MaxLengthAttribute, 'maxDecimal' => JSON::Schema::MaxDecimalAttribute, 'enum' => JSON::Schema::EnumAttribute, 'properties' => JSON::Schema::PropertiesOptionalAttribute, 'pattern' => JSON::Schema::PatternAttribute, 'additionalProperties' => JSON::Schema::AdditionalPropertiesAttribute, 'items' => JSON::Schema::ItemsAttribute, 'extends' => JSON::Schema::ExtendsAttribute, } @default_formats = { 'date-time' => DateTimeFormat, 'date' => DateFormat, 'time' => TimeFormat, 'ip-address' => IP4Format, 'ipv6' => IP6Format, 'uri' => UriFormat, } @formats = @default_formats.clone @uri = JSON::Util::URI.parse('http://json-schema.org/draft-01/schema#') @names = ['draft1'] @metaschema_name = 'draft-01.json' end JSON::Validator.register_validator(new) end end end json-schema-6.0.0/lib/json-schema/attributes/0000755000004100000410000000000015050634712021122 5ustar www-datawww-datajson-schema-6.0.0/lib/json-schema/attributes/not.rb0000644000004100000410000000216215050634712022250 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class NotAttribute < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) schema = JSON::Schema.new(current_schema.schema['not'], current_schema.uri, validator) failed = true errors_copy = processor.validation_errors.clone begin schema.validate(data, fragments, processor, options) # If we're recording errors, we don't throw an exception. Instead, check the errors array length if options[:record_errors] && errors_copy.length != processor.validation_errors.length processor.validation_errors.replace(errors_copy) else message = "The property '#{build_fragment(fragments)}' of type #{type_of_data(data)} matched the disallowed schema" failed = false end rescue ValidationError # Yay, we failed validation. end unless failed validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end end end end end json-schema-6.0.0/lib/json-schema/attributes/limits/0000755000004100000410000000000015050634712022423 5ustar www-datawww-datajson-schema-6.0.0/lib/json-schema/attributes/limits/max_length.rb0000644000004100000410000000047215050634712025101 0ustar www-datawww-datarequire 'json-schema/attributes/limits/length' module JSON class Schema class MaxLengthAttribute < LengthLimitAttribute def self.limit_name 'maxLength' end def self.error_message(schema) "was not of a maximum string length of #{limit(schema)}" end end end end json-schema-6.0.0/lib/json-schema/attributes/limits/maximum_inclusive.rb0000644000004100000410000000035415050634712026510 0ustar www-datawww-datarequire 'json-schema/attributes/limits/maximum' module JSON class Schema class MaximumInclusiveAttribute < MaximumAttribute def self.exclusive?(schema) schema['maximumCanEqual'] == false end end end end json-schema-6.0.0/lib/json-schema/attributes/limits/max_properties.rb0000644000004100000410000000051115050634712026006 0ustar www-datawww-datarequire 'json-schema/attributes/limits/properties' module JSON class Schema class MaxPropertiesAttribute < PropertiesLimitAttribute def self.limit_name 'maxProperties' end def self.error_message(schema) "had more properties than the allowed #{limit(schema)}" end end end end json-schema-6.0.0/lib/json-schema/attributes/limits/min_items.rb0000644000004100000410000000047215050634712024737 0ustar www-datawww-datarequire 'json-schema/attributes/limits/items' module JSON class Schema class MinItemsAttribute < ItemsLimitAttribute def self.limit_name 'minItems' end def self.error_message(schema) "did not contain a minimum number of items #{limit(schema)}" end end end end json-schema-6.0.0/lib/json-schema/attributes/limits/length.rb0000644000004100000410000000036715050634712024237 0ustar www-datawww-datarequire 'json-schema/attributes/limit' module JSON class Schema class LengthLimitAttribute < LimitAttribute def self.acceptable_type String end def self.value(data) data.length end end end end json-schema-6.0.0/lib/json-schema/attributes/limits/minimum.rb0000644000004100000410000000042715050634712024426 0ustar www-datawww-datarequire 'json-schema/attributes/limits/numeric' module JSON class Schema class MinimumAttribute < NumericLimitAttribute def self.limit_name 'minimum' end def self.exclusive?(schema) schema['exclusiveMinimum'] end end end end json-schema-6.0.0/lib/json-schema/attributes/limits/maximum.rb0000644000004100000410000000042715050634712024430 0ustar www-datawww-datarequire 'json-schema/attributes/limits/numeric' module JSON class Schema class MaximumAttribute < NumericLimitAttribute def self.limit_name 'maximum' end def self.exclusive?(schema) schema['exclusiveMaximum'] end end end end json-schema-6.0.0/lib/json-schema/attributes/limits/items.rb0000644000004100000410000000036515050634712024075 0ustar www-datawww-datarequire 'json-schema/attributes/limit' module JSON class Schema class ItemsLimitAttribute < LimitAttribute def self.acceptable_type Array end def self.value(data) data.length end end end end json-schema-6.0.0/lib/json-schema/attributes/limits/min_length.rb0000644000004100000410000000047215050634712025077 0ustar www-datawww-datarequire 'json-schema/attributes/limits/length' module JSON class Schema class MinLengthAttribute < LengthLimitAttribute def self.limit_name 'minLength' end def self.error_message(schema) "was not of a minimum string length of #{limit(schema)}" end end end end json-schema-6.0.0/lib/json-schema/attributes/limits/minimum_inclusive.rb0000644000004100000410000000035415050634712026506 0ustar www-datawww-datarequire 'json-schema/attributes/limits/minimum' module JSON class Schema class MinimumInclusiveAttribute < MinimumAttribute def self.exclusive?(schema) schema['minimumCanEqual'] == false end end end end json-schema-6.0.0/lib/json-schema/attributes/limits/min_properties.rb0000644000004100000410000000052315050634712026007 0ustar www-datawww-datarequire 'json-schema/attributes/limits/properties' module JSON class Schema class MinPropertiesAttribute < PropertiesLimitAttribute def self.limit_name 'minProperties' end def self.error_message(schema) "did not contain a minimum number of properties #{limit(schema)}" end end end end json-schema-6.0.0/lib/json-schema/attributes/limits/max_items.rb0000644000004100000410000000046015050634712024736 0ustar www-datawww-datarequire 'json-schema/attributes/limits/items' module JSON class Schema class MaxItemsAttribute < ItemsLimitAttribute def self.limit_name 'maxItems' end def self.error_message(schema) "had more items than the allowed #{limit(schema)}" end end end end json-schema-6.0.0/lib/json-schema/attributes/limits/properties.rb0000644000004100000410000000036715050634712025152 0ustar www-datawww-datarequire 'json-schema/attributes/limit' module JSON class Schema class PropertiesLimitAttribute < LimitAttribute def self.acceptable_type Hash end def self.value(data) data.size end end end end json-schema-6.0.0/lib/json-schema/attributes/limits/numeric.rb0000644000004100000410000000062415050634712024414 0ustar www-datawww-datarequire 'json-schema/attributes/limit' module JSON class Schema class NumericLimitAttribute < LimitAttribute def self.acceptable_type Numeric end def self.error_message(schema) exclusivity = exclusive?(schema) ? 'exclusively' : 'inclusively' format('did not have a %s value of %s, %s', limit_name, limit(schema), exclusivity) end end end end json-schema-6.0.0/lib/json-schema/attributes/pattern.rb0000644000004100000410000000116415050634712023126 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class PatternAttribute < Attribute def self.validate(current_schema, data, fragments, processor, _validator, options = {}) return unless data.is_a?(String) pattern = current_schema.schema['pattern'] regexp = Regexp.new(pattern) unless regexp.match(data) message = "The property '#{build_fragment(fragments)}' value #{data.inspect} did not match the regex '#{pattern}'" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end end end end end json-schema-6.0.0/lib/json-schema/attributes/dependencies_v4.rb0000644000004100000410000000036415050634712024511 0ustar www-datawww-datarequire 'json-schema/attributes/dependencies' module JSON class Schema class DependenciesV4Attribute < DependenciesAttribute def self.accept_value?(value) value.is_a?(Array) || value.is_a?(Hash) end end end end json-schema-6.0.0/lib/json-schema/attributes/limit.rb0000644000004100000410000000242715050634712022572 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class LimitAttribute < Attribute def self.validate(current_schema, data, fragments, processor, _validator, options = {}) schema = current_schema.schema return unless data.is_a?(acceptable_type) && invalid?(schema, value(data)) property = build_fragment(fragments) description = error_message(schema) message = format("The property '%s' %s", property, description) validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end def self.invalid?(schema, data) exclusive = exclusive?(schema) limit = limit(schema) if limit_name.start_with?('max') exclusive ? data >= limit : data > limit else exclusive ? data <= limit : data < limit end end def self.limit(schema) schema[limit_name] end def self.exclusive?(_schema) false end def self.value(data) data end def self.acceptable_type raise NotImplementedError end def self.error_message(schema) raise NotImplementedError end def self.limit_name raise NotImplementedError end end end end json-schema-6.0.0/lib/json-schema/attributes/propertynames.rb0000644000004100000410000000155015050634712024360 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class PropertyNames < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) return unless data.is_a?(Hash) propnames = current_schema.schema['propertyNames'] if propnames.is_a?(Hash) schema = JSON::Schema.new(propnames, current_schema.uri, validator) data.each_key do |key| schema.validate(key, fragments + [key], processor, options) end elsif propnames == false && data.any? message = "The property '#{build_fragment(fragments)}' contains additional properties #{data.keys.inspect} outside of the schema when none are allowed" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end end end end end json-schema-6.0.0/lib/json-schema/attributes/ref.rb0000644000004100000410000000446715050634712022236 0ustar www-datawww-datarequire 'json-schema/attribute' require 'json-schema/errors/schema_error' require 'json-schema/util/uri' module JSON class Schema class RefAttribute < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) uri, schema = get_referenced_uri_and_schema(current_schema.schema, current_schema, validator) if schema schema.validate(data, fragments, processor, options) elsif uri message = "The referenced schema '#{uri}' cannot be found" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) else message = "The property '#{build_fragment(fragments)}' was not a valid schema" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end end def self.get_referenced_uri_and_schema(s, current_schema, validator) uri = nil schema = nil temp_uri = JSON::Util::URI.normalize_ref(s['$ref'], current_schema.uri) # Grab the parent schema from the schema list schema_key = temp_uri.to_s.split('#')[0] + '#' ref_schema = JSON::Validator.schema_for_uri(schema_key) if ref_schema # Perform fragment resolution to retrieve the appropriate level for the schema target_schema = ref_schema.schema fragments = JSON::Util::URI.parse(JSON::Util::URI.unescape_uri(temp_uri)).fragment.split('/') fragment_path = '' fragments.each do |fragment| next unless fragment && fragment != '' fragment = fragment.gsub('~0', '~').gsub('~1', '/') target_schema = if target_schema.is_a?(Array) target_schema[fragment.to_i] else target_schema[fragment] end fragment_path += "/#{fragment}" if target_schema.nil? raise SchemaError, "The fragment '#{fragment_path}' does not exist on schema #{ref_schema.uri}" end end # We have the schema finally, build it and validate! uri = temp_uri schema = JSON::Schema.new(target_schema, temp_uri, validator) end [uri, schema] end end end end json-schema-6.0.0/lib/json-schema/attributes/required.rb0000644000004100000410000000203615050634712023270 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class RequiredAttribute < Attribute def self.validate(current_schema, data, fragments, processor, _validator, options = {}) return unless data.is_a?(Hash) schema = current_schema.schema defined_properties = schema['properties'] schema['required'].each do |property, _property_schema| next if data.has_key?(property.to_s) prop_defaults = options[:insert_defaults] && defined_properties && defined_properties[property] && !defined_properties[property]['default'].nil? && !defined_properties[property]['readonly'] unless prop_defaults message = "The property '#{build_fragment(fragments)}' did not contain a required property of '#{property}'" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end end end end end end json-schema-6.0.0/lib/json-schema/attributes/const.rb0000644000004100000410000000105015050634712022571 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class ConstAttribute < Attribute def self.validate(current_schema, data, fragments, processor, _validator, options = {}) const_value = current_schema.schema['const'] unless const_value == data message = "The property '#{build_fragment(fragments)}' value #{data.inspect} did not match constant '#{const_value}'" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end end end end end json-schema-6.0.0/lib/json-schema/attributes/allof.rb0000644000004100000410000000560615050634712022553 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class AllOfAttribute < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) # Create an hash to hold errors that are generated during validation errors = Hash.new { |hsh, k| hsh[k] = [] } valid = true message = nil current_schema.schema['allOf'].each_with_index do |element, schema_index| schema = JSON::Schema.new(element, current_schema.uri, validator) # We're going to add a little cruft here to try and maintain any validation errors that occur in the allOf # We'll handle this by keeping an error count before and after validation, extracting those errors and pushing them onto an error array pre_validation_error_count = validation_errors(processor).count begin # Cannot raise if noAdditionalProperties is true, we need to # evaluate each sub schema within the allOf, before raising. if options[:noAdditionalProperties] == true schema.validate(data, fragments, processor, options.merge(record_errors: true)) else schema.validate(data, fragments, processor, options) end rescue ValidationError => e valid = false message = e.message end diff = validation_errors(processor).count - pre_validation_error_count while diff > 0 diff -= 1 errors["allOf ##{schema_index}"].push(validation_errors(processor).pop) end end # Find any properties that are missing across all subschemas. common_missing_properties = {} if options[:noAdditionalProperties] == true && !errors.empty? all_property_errors = errors.values.flatten.map(&:properties) common_missing_properties = (all_property_errors.first || []).to_set all_property_errors[1..].each do |curr_property_errors| common_missing_properties &= curr_property_errors.to_set end end # PropertiesV4Attribute represents errors that would indicate an # additional property was detected. If we filter these out, we should # be left with errors that are not dependent on any other sub schema. non_missing_property_errors = errors.values.flatten.reject do |error| error.failed_attribute == JSON::Schema::PropertiesV4Attribute end if !valid || !non_missing_property_errors.empty? || !common_missing_properties.empty? message ||= "The property '#{build_fragment(fragments)}' of type #{type_of_data(data)} did not match all of the required schemas" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) validation_errors(processor).last.sub_errors = errors end end end end end json-schema-6.0.0/lib/json-schema/attributes/multipleof.rb0000644000004100000410000000030615050634712023626 0ustar www-datawww-datarequire 'json-schema/attributes/divisibleby' module JSON class Schema class MultipleOfAttribute < DivisibleByAttribute def self.keyword 'multipleOf' end end end end json-schema-6.0.0/lib/json-schema/attributes/properties_v4.rb0000644000004100000410000000060415050634712024254 0ustar www-datawww-datarequire 'json-schema/attributes/properties' module JSON class Schema class PropertiesV4Attribute < PropertiesAttribute # draft4 relies on its own RequiredAttribute validation at a higher level, rather than # as an attribute of individual properties. def self.required?(_schema, options) options[:allPropertiesRequired] == true end end end end json-schema-6.0.0/lib/json-schema/attributes/dependencies.rb0000644000004100000410000000277115050634712024104 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class DependenciesAttribute < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) return unless data.is_a?(Hash) current_schema.schema['dependencies'].each do |property, dependency_value| next unless data.has_key?(property.to_s) next unless accept_value?(dependency_value) case dependency_value when String validate_dependency(current_schema, data, property, dependency_value, fragments, processor, self, options) when Array dependency_value.each do |value| validate_dependency(current_schema, data, property, value, fragments, processor, self, options) end else schema = JSON::Schema.new(dependency_value, current_schema.uri, validator) schema.validate(data, fragments, processor, options) end end end def self.validate_dependency(schema, data, property, value, fragments, processor, attribute, options) return if data.key?(value.to_s) message = "The property '#{build_fragment(fragments)}' has a property '#{property}' that depends on a missing property '#{value}'" validation_error(processor, message, fragments, schema, attribute, options[:record_errors]) end def self.accept_value?(value) value.is_a?(String) || value.is_a?(Array) || value.is_a?(Hash) end end end end json-schema-6.0.0/lib/json-schema/attributes/enum.rb0000644000004100000410000000137215050634712022416 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class EnumAttribute < Attribute def self.validate(current_schema, data, fragments, processor, _validator, options = {}) enum = current_schema.schema['enum'] return if enum.include?(data) values = enum.map do |val| case val when nil then 'null' when Array then 'array' when Hash then 'object' else val.to_s end end.join(', ') message = "The property '#{build_fragment(fragments)}' value #{data.inspect} did not match one of the following values: #{values}" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end end end end json-schema-6.0.0/lib/json-schema/attributes/extends.rb0000644000004100000410000000332415050634712023123 0ustar www-datawww-datarequire 'json-schema/attribute' require 'json-schema/attributes/ref' module JSON class Schema class ExtendsAttribute < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) schemas = current_schema.schema['extends'] schemas = [schemas] unless schemas.is_a?(Array) schemas.each do |s| uri, schema = get_extended_uri_and_schema(s, current_schema, validator) if schema schema.validate(data, fragments, processor, options) elsif uri message = "The extended schema '#{uri}' cannot be found" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) else message = "The property '#{build_fragment(fragments)}' was not a valid schema" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end end end def self.get_extended_uri_and_schema(s, current_schema, validator) uri = nil schema = nil if s.is_a?(Hash) uri = current_schema.uri if s['$ref'] ref_uri, ref_schema = JSON::Schema::RefAttribute.get_referenced_uri_and_schema(s, current_schema, validator) if ref_schema if s.size == 1 # Check if anything else apart from $ref uri = ref_uri schema = ref_schema else s = s.dup s.delete '$ref' s = ref_schema.schema.merge(s) end end end schema ||= JSON::Schema.new(s, uri, validator) end [uri, schema] end end end end json-schema-6.0.0/lib/json-schema/attributes/additionalitems.rb0000644000004100000410000000206415050634712024623 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class AdditionalItemsAttribute < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) return unless data.is_a?(Array) schema = current_schema.schema return unless schema['items'].is_a?(Array) case schema['additionalItems'] when false if schema['items'].length < data.length message = "The property '#{build_fragment(fragments)}' contains additional array elements outside of the schema when none are allowed" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end when Hash additional_items_schema = JSON::Schema.new(schema['additionalItems'], current_schema.uri, validator) data.each_with_index do |item, i| next if i < schema['items'].length additional_items_schema.validate(item, fragments + [i.to_s], processor, options) end end end end end end json-schema-6.0.0/lib/json-schema/attributes/anyof.rb0000644000004100000410000000350115050634712022562 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class AnyOfAttribute < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) # Create a hash to hold errors that are generated during validation errors = Hash.new { |hsh, k| hsh[k] = [] } valid = false original_data = data.is_a?(Hash) ? data.clone : data current_schema.schema['anyOf'].each_with_index do |element, schema_index| schema = JSON::Schema.new(element, current_schema.uri, validator) # We're going to add a little cruft here to try and maintain any validation errors that occur in the anyOf # We'll handle this by keeping an error count before and after validation, extracting those errors and pushing them onto a union error pre_validation_error_count = validation_errors(processor).count begin schema.validate(data, fragments, processor, options) valid = true rescue ValidationError # We don't care that these schemas don't validate - we only care that one validated end diff = validation_errors(processor).count - pre_validation_error_count valid = false if diff > 0 while diff > 0 diff -= 1 errors["anyOf ##{schema_index}"].push(validation_errors(processor).pop) end break if valid data = original_data end unless valid message = "The property '#{build_fragment(fragments)}' of type #{type_of_data(data)} did not match one or more of the required schemas" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) validation_errors(processor).last.sub_errors = errors end end end end end json-schema-6.0.0/lib/json-schema/attributes/uniqueitems.rb0000644000004100000410000000077715050634712024032 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class UniqueItemsAttribute < Attribute def self.validate(current_schema, data, fragments, processor, _validator, options = {}) return unless data.is_a?(Array) if data.clone.uniq! message = "The property '#{build_fragment(fragments)}' contained duplicated array values" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end end end end end json-schema-6.0.0/lib/json-schema/attributes/oneof.rb0000644000004100000410000000365415050634712022565 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class OneOfAttribute < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) errors = Hash.new { |hsh, k| hsh[k] = [] } validation_error_count = 0 one_of = current_schema.schema['oneOf'] original_data = data.is_a?(Hash) ? data.clone : data success_data = nil valid = false one_of.each_with_index do |element, schema_index| schema = JSON::Schema.new(element, current_schema.uri, validator) pre_validation_error_count = validation_errors(processor).count begin schema.validate(data, fragments, processor, options) success_data = data.is_a?(Hash) ? data.clone : data valid = true rescue ValidationError valid = false end diff = validation_errors(processor).count - pre_validation_error_count valid = false if diff > 0 validation_error_count += 1 unless valid while diff > 0 diff -= 1 errors["oneOf ##{schema_index}"].push(validation_errors(processor).pop) end data = original_data end if validation_error_count == one_of.length - 1 data = success_data return end message = if validation_error_count == one_of.length "The property '#{build_fragment(fragments)}' of type #{type_of_data(data)} did not match any of the required schemas" else "The property '#{build_fragment(fragments)}' of type #{type_of_data(data)} matched more than one of the required schemas" end validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) if message validation_errors(processor).last.sub_errors = errors if message end end end end json-schema-6.0.0/lib/json-schema/attributes/type_v4.rb0000644000004100000410000000161715050634712023046 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class TypeV4Attribute < Attribute def self.validate(current_schema, data, fragments, processor, _validator, options = {}) union = true types = current_schema.schema['type'] unless types.is_a?(Array) types = [types] union = false end return if types.any? { |type| data_valid_for_type?(data, type) } types = types.map { |type| type.is_a?(String) ? type : '(schema)' }.join(', ') message = format( "The property '%s' of type %s did not match %s: %s", build_fragment(fragments), type_of_data(data), union ? 'one or more of the following types' : 'the following type', types, ) validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end end end end json-schema-6.0.0/lib/json-schema/attributes/disallow.rb0000644000004100000410000000060415050634712023265 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class DisallowAttribute < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) return unless type = validator.attributes['type'] type.validate(current_schema, data, fragments, processor, validator, options.merge(disallow: true)) end end end end json-schema-6.0.0/lib/json-schema/attributes/formats/0000755000004100000410000000000015050634712022575 5ustar www-datawww-datajson-schema-6.0.0/lib/json-schema/attributes/formats/date.rb0000644000004100000410000000155415050634712024044 0ustar www-datawww-datarequire 'json-schema/attributes/format' module JSON class Schema class DateFormat < FormatAttribute REGEXP = /\A\d{4}-\d{2}-\d{2}\z/ def self.validate(current_schema, data, fragments, processor, _validator, options = {}) if data.is_a?(String) error_message = "The property '#{build_fragment(fragments)}' must be a date in the format of YYYY-MM-DD" if REGEXP.match(data) begin Date.parse(data) rescue ArgumentError => e raise e unless e.message == 'invalid date' validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) end else validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) end end end end end end json-schema-6.0.0/lib/json-schema/attributes/formats/ip.rb0000644000004100000410000000203615050634712023533 0ustar www-datawww-datarequire 'json-schema/attributes/format' require 'ipaddr' require 'socket' module JSON class Schema class IPFormat < FormatAttribute def self.validate(current_schema, data, fragments, processor, _validator, options = {}) return unless data.is_a?(String) begin ip = IPAddr.new(data) rescue ArgumentError => e raise e unless e.message.start_with?('invalid address') end family = (ip_version == 6) ? Socket::AF_INET6 : Socket::AF_INET unless ip && ip.family == family error_message = "The property '#{build_fragment(fragments)}' must be a valid IPv#{ip_version} address" validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) end end def self.ip_version raise NotImplementedError end end class IP4Format < IPFormat def self.ip_version 4 end end class IP6Format < IPFormat def self.ip_version 6 end end end end json-schema-6.0.0/lib/json-schema/attributes/formats/date_time_v4.rb0000644000004100000410000000105315050634712025465 0ustar www-datawww-datarequire 'json-schema/attributes/format' module JSON class Schema class DateTimeV4Format < FormatAttribute def self.validate(current_schema, data, fragments, processor, _validator, options = {}) return unless data.is_a?(String) DateTime.rfc3339(data) rescue ArgumentError error_message = "The property '#{build_fragment(fragments)}' must be a valid RFC3339 date/time string" validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) end end end end json-schema-6.0.0/lib/json-schema/attributes/formats/time.rb0000644000004100000410000000201415050634712024055 0ustar www-datawww-datarequire 'json-schema/attributes/format' module JSON class Schema class TimeFormat < FormatAttribute REGEXP = /\A(\d{2}):(\d{2}):(\d{2})\z/ def self.validate(current_schema, data, fragments, processor, _validator, options = {}) if data.is_a?(String) error_message = "The property '#{build_fragment(fragments)}' must be a time in the format of hh:mm:ss" if (m = REGEXP.match(data)) validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[1].to_i > 23 validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[2].to_i > 59 validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[3].to_i > 59 else validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) end end end end end end json-schema-6.0.0/lib/json-schema/attributes/formats/custom.rb0000644000004100000410000000122415050634712024433 0ustar www-datawww-datarequire 'json-schema/attributes/format' require 'json-schema/errors/custom_format_error' module JSON class Schema class CustomFormat < FormatAttribute def initialize(validation_proc) @validation_proc = validation_proc end def validate(current_schema, data, fragments, processor, _validator, options = {}) @validation_proc.call data rescue JSON::Schema::CustomFormatError => e message = "The property '#{self.class.build_fragment(fragments)}' #{e.message}" self.class.validation_error(processor, message, fragments, current_schema, self.class, options[:record_errors]) end end end end json-schema-6.0.0/lib/json-schema/attributes/formats/date_time.rb0000644000004100000410000000331015050634712025052 0ustar www-datawww-datarequire 'json-schema/attributes/format' module JSON class Schema class DateTimeFormat < FormatAttribute REGEXP = /\A\d{4}-\d{2}-\d{2}T(\d{2}):(\d{2}):(\d{2})([\.,]\d+)?(Z|[+-](\d{2})(:?\d{2})?)?\z/ def self.validate(current_schema, data, fragments, processor, _validator, options = {}) # Timestamp in restricted ISO-8601 YYYY-MM-DDThh:mm:ssZ with optional decimal fraction of the second if data.is_a?(String) error_message = "The property '#{build_fragment(fragments)}' must be a date/time in the ISO-8601 format of YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss.ssZ" if (m = REGEXP.match(data)) parts = data.split('T') begin Date.parse(parts[0]) rescue ArgumentError => e raise e unless e.message == 'invalid date' validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) return end validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m.length < 4 validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[1].to_i > 23 validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[2].to_i > 59 validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[3].to_i > 59 else validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) end end end end end end json-schema-6.0.0/lib/json-schema/attributes/formats/uri.rb0000644000004100000410000000114415050634712023721 0ustar www-datawww-datarequire 'json-schema/attributes/format' require 'json-schema/errors/uri_error' module JSON class Schema class UriFormat < FormatAttribute def self.validate(current_schema, data, fragments, processor, _validator, options = {}) return unless data.is_a?(String) error_message = "The property '#{build_fragment(fragments)}' must be a valid URI" begin JSON::Util::URI.parse(data) rescue JSON::Schema::UriError validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) end end end end end json-schema-6.0.0/lib/json-schema/attributes/items.rb0000644000004100000410000000152115050634712022567 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class ItemsAttribute < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) return unless data.is_a?(Array) items = current_schema.schema['items'] case items when Hash schema = JSON::Schema.new(items, current_schema.uri, validator) data.each_with_index do |item, i| schema.validate(item, fragments + [i.to_s], processor, options) end when Array items.each_with_index do |item_schema, i| break if i >= data.length schema = JSON::Schema.new(item_schema, current_schema.uri, validator) schema.validate(data[i], fragments + [i.to_s], processor, options) end end end end end end json-schema-6.0.0/lib/json-schema/attributes/additionalproperties.rb0000644000004100000410000000433415050634712025700 0ustar www-datawww-datarequire 'json-schema/attribute' require 'json-schema/attributes/extends' module JSON class Schema class AdditionalPropertiesAttribute < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) schema = current_schema.schema return unless data.is_a?(Hash) && (schema['type'].nil? || schema['type'] == 'object') extra_properties = remove_valid_properties(data.keys, current_schema, validator) addprop = schema['additionalProperties'] if addprop.is_a?(Hash) matching_properties = extra_properties # & addprop.keys matching_properties.each do |key| additional_property_schema = JSON::Schema.new(addprop, current_schema.uri, validator) additional_property_schema.validate(data[key], fragments + [key], processor, options) end extra_properties -= matching_properties end if extra_properties.any? && (addprop == false || (addprop.is_a?(Hash) && !addprop.empty?)) message = "The property '#{build_fragment(fragments)}' contains additional properties #{extra_properties.inspect} outside of the schema when none are allowed" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end end def self.remove_valid_properties(extra_properties, current_schema, validator) schema = current_schema.schema if schema['properties'] extra_properties -= schema['properties'].keys end schema['patternProperties']&.each_key do |key| regexp = Regexp.new(key) extra_properties.reject! { |prop| regexp.match(prop) } end if extended_schemas = schema['extends'] extended_schemas = [extended_schemas] unless extended_schemas.is_a?(Array) extended_schemas.each do |schema_value| _, extended_schema = JSON::Schema::ExtendsAttribute.get_extended_uri_and_schema(schema_value, current_schema, validator) if extended_schema extra_properties = remove_valid_properties(extra_properties, extended_schema, validator) end end end extra_properties end end end end json-schema-6.0.0/lib/json-schema/attributes/maxdecimal.rb0000644000004100000410000000122415050634712023552 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class MaxDecimalAttribute < Attribute def self.validate(current_schema, data, fragments, processor, _validator, options = {}) return unless data.is_a?(Numeric) max_decimal_places = current_schema.schema['maxDecimal'] s = data.to_s.split('.')[1] if s && s.length > max_decimal_places message = "The property '#{build_fragment(fragments)}' had more decimal places than the allowed #{max_decimal_places}" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end end end end end json-schema-6.0.0/lib/json-schema/attributes/patternproperties.rb0000644000004100000410000000134715050634712025246 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class PatternPropertiesAttribute < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) return unless data.is_a?(Hash) current_schema.schema['patternProperties'].each do |property, property_schema| regexp = Regexp.new(property) # Check each key in the data hash to see if it matches the regex data.each do |key, _value| next unless regexp.match(key) schema = JSON::Schema.new(property_schema, current_schema.uri, validator) schema.validate(data[key], fragments + [key], processor, options) end end end end end end json-schema-6.0.0/lib/json-schema/attributes/properties.rb0000644000004100000410000000461615050634712023652 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class PropertiesAttribute < Attribute def self.required?(schema, options) schema.fetch('required') { options[:allPropertiesRequired] } end def self.validate(current_schema, data, fragments, processor, validator, options = {}) return unless data.is_a?(Hash) schema = current_schema.schema schema['properties'].each do |property, property_schema| property = property.to_s if !data.key?(property) && options[:insert_defaults] && property_schema.has_key?('default') && !property_schema['readonly'] default = property_schema['default'] data[property] = default.is_a?(Hash) ? default.clone : default end if required?(property_schema, options) && !data.has_key?(property) message = "The property '#{build_fragment(fragments)}' did not contain a required property of '#{property}'" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end if data.has_key?(property) expected_schema = JSON::Schema.new(property_schema, current_schema.uri, validator) expected_schema.validate(data[property], fragments + [property], processor, options) end end # When noAdditionalProperties is true, ensure no undefined properties exist in the data return unless options[:noAdditionalProperties] == true && !schema.key?('additionalProperties') diff = data.select do |k, _v| k = k.to_s if schema.has_key?('patternProperties') match = false schema['patternProperties'].each do |property, _property_schema| regexp = Regexp.new(property) if regexp.match(k) match = true break end end !schema['properties'].has_key?(k) && !match else !schema['properties'].has_key?(k) end end unless diff.empty? properties = diff.keys.join(', ') message = "The property '#{build_fragment(fragments)}' contained undefined properties: '#{properties}'" validation_error(processor, message, fragments, current_schema, self, options[:record_errors], diff.keys) end end end end end json-schema-6.0.0/lib/json-schema/attributes/divisibleby.rb0000644000004100000410000000126215050634712023755 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class DivisibleByAttribute < Attribute def self.keyword 'divisibleBy' end def self.validate(current_schema, data, fragments, processor, _validator, options = {}) return unless data.is_a?(Numeric) factor = current_schema.schema[keyword] if factor == 0 || factor == 0.0 || (BigDecimal(data.to_s) % BigDecimal(factor.to_s)).to_f != 0 message = "The property '#{build_fragment(fragments)}' was not divisible by #{factor}" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end end end end end json-schema-6.0.0/lib/json-schema/attributes/properties_optional.rb0000644000004100000410000000171715050634712025556 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class PropertiesOptionalAttribute < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) return unless data.is_a?(Hash) schema = current_schema.schema schema['properties'].each do |property, property_schema| property = property.to_s if !property_schema['optional'] && !data.key?(property) message = "The property '#{build_fragment(fragments)}' did not contain a required property of '#{property}'" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end if data.has_key?(property) expected_schema = JSON::Schema.new(property_schema, current_schema.uri, validator) expected_schema.validate(data[property], fragments + [property], processor, options) end end end end end end json-schema-6.0.0/lib/json-schema/attributes/type.rb0000644000004100000410000000574015050634712022436 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class TypeAttribute < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) union = true types = if options[:disallow] current_schema.schema['disallow'] else current_schema.schema['type'] end unless types.is_a?(Array) types = [types] union = false end valid = false # Create a hash to hold errors that are generated during union validation union_errors = Hash.new { |hsh, k| hsh[k] = [] } types.each_with_index do |type, type_index| if type.is_a?(String) valid = data_valid_for_type?(data, type) elsif type.is_a?(Hash) && union # Validate as a schema schema = JSON::Schema.new(type, current_schema.uri, validator) # We're going to add a little cruft here to try and maintain any validation errors that occur in this union type # We'll handle this by keeping an error count before and after validation, extracting those errors and pushing them onto a union error pre_validation_error_count = validation_errors(processor).count begin schema.validate(data, fragments, processor, options.merge(disallow: false)) valid = true rescue ValidationError # We don't care that these schemas don't validate - we only care that one validated end diff = validation_errors(processor).count - pre_validation_error_count valid = false if diff > 0 while diff > 0 diff -= 1 union_errors["type ##{type_index}"].push(validation_errors(processor).pop) end end break if valid end if options[:disallow] return unless valid message = "The property '#{build_fragment(fragments)}' matched one or more of the following types: #{list_types(types)}" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) elsif !valid if union message = "The property '#{build_fragment(fragments)}' of type #{type_of_data(data)} did not match one or more of the following types: #{list_types(types)}" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) validation_errors(processor).last.sub_errors = union_errors else message = "The property '#{build_fragment(fragments)}' of type #{type_of_data(data)} did not match the following type: #{list_types(types)}" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end end end def self.list_types(types) types.map { |type| type.is_a?(String) ? type : '(schema)' }.join(', ') end end end end json-schema-6.0.0/lib/json-schema/attributes/format.rb0000644000004100000410000000075415050634712022745 0ustar www-datawww-datarequire 'json-schema/attribute' module JSON class Schema class FormatAttribute < Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) return unless data_valid_for_type?(data, current_schema.schema['type']) format = current_schema.schema['format'].to_s validator = validator.formats[format] validator&.validate(current_schema, data, fragments, processor, validator, options) end end end end json-schema-6.0.0/lib/json-schema/errors/0000755000004100000410000000000015050634712020250 5ustar www-datawww-datajson-schema-6.0.0/lib/json-schema/errors/uri_error.rb0000644000004100000410000000012015050634712022576 0ustar www-datawww-datamodule JSON class Schema class UriError < StandardError end end end json-schema-6.0.0/lib/json-schema/errors/json_parse_error.rb0000644000004100000410000000012615050634712024150 0ustar www-datawww-datamodule JSON class Schema class JsonParseError < StandardError end end end json-schema-6.0.0/lib/json-schema/errors/validation_error.rb0000644000004100000410000000322715050634712024144 0ustar www-datawww-datamodule JSON class Schema class ValidationError < StandardError INDENT = ' ' attr_accessor :fragments, :schema, :failed_attribute, :sub_errors, :message, :properties def initialize(message, fragments, failed_attribute, schema, properties = []) @fragments = fragments.clone @schema = schema @sub_errors = {} @failed_attribute = failed_attribute @message = message @properties = properties super(message_with_schema) end def to_string(subschema_level = 0) if @sub_errors.empty? (subschema_level == 0) ? message_with_schema : message else messages = ["#{message}. The schema specific errors were:\n"] @sub_errors.each do |subschema, errors| messages.push "- #{subschema}:" messages.concat(Array(errors).map { |e| "#{INDENT}- #{e.to_string(subschema_level + 1)}" }) end messages.map { |m| (INDENT * subschema_level) + m }.join("\n") end end def to_hash base = { schema: @schema.uri, fragment: ::JSON::Schema::Attribute.build_fragment(fragments), message: message_with_schema, failed_attribute: @failed_attribute.to_s.split(':').last.split('Attribute').first } unless @sub_errors.empty? base[:errors] = @sub_errors.each_with_object({}) do |(subschema, errors), hsh| subschema_sym = subschema.downcase.gsub(/\W+/, '_').to_sym hsh[subschema_sym] = Array(errors).map { |e| e.to_hash } end end base end def message_with_schema "#{message} in schema #{schema.uri}" end end end end json-schema-6.0.0/lib/json-schema/errors/schema_error.rb0000644000004100000410000000012315050634712023242 0ustar www-datawww-datamodule JSON class Schema class SchemaError < StandardError end end end json-schema-6.0.0/lib/json-schema/errors/schema_parse_error.rb0000644000004100000410000000016315050634712024440 0ustar www-datawww-datarequire 'json/common' module JSON class Schema class SchemaParseError < JSON::ParserError end end end json-schema-6.0.0/lib/json-schema/errors/custom_format_error.rb0000644000004100000410000000013115050634712024663 0ustar www-datawww-datamodule JSON class Schema class CustomFormatError < StandardError end end end json-schema-6.0.0/lib/json-schema/errors/json_load_error.rb0000644000004100000410000000012515050634712023754 0ustar www-datawww-datamodule JSON class Schema class JsonLoadError < StandardError end end end json-schema-6.0.0/lib/json-schema/attribute.rb0000644000004100000410000000302115050634712021260 0ustar www-datawww-datarequire 'json-schema/errors/validation_error' module JSON class Schema class Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}); end def self.build_fragment(fragments) "#/#{fragments.join('/')}" end def self.validation_error(processor, message, fragments, current_schema, failed_attribute, record_errors, properties = []) error = ValidationError.new(message, fragments, failed_attribute, current_schema, properties) if record_errors processor.validation_error(error) else raise error end end def self.validation_errors(validator) validator.validation_errors end TYPE_CLASS_MAPPINGS = { 'string' => String, 'number' => Numeric, 'integer' => Integer, 'boolean' => [TrueClass, FalseClass], 'object' => Hash, 'array' => Array, 'null' => NilClass, 'any' => Object, } def self.data_valid_for_type?(data, type) valid_classes = TYPE_CLASS_MAPPINGS.fetch(type) { return true } Array(valid_classes).any? { |c| data.is_a?(c) } end # Lookup Schema type of given class instance def self.type_of_data(data) type, = TYPE_CLASS_MAPPINGS.map { |k, v| [k, v] }.sort_by do |(_, v)| -Array(v).map { |klass| klass.ancestors.size }.max end.find do |(_, v)| Array(v).any? { |klass| data.is_a?(klass) } end type end end end end json-schema-6.0.0/LICENSE.md0000644000004100000410000000206515050634712015366 0ustar www-datawww-dataCopyright (c) 2010-2011, Lookingglass Cyber Solutions Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.json-schema-6.0.0/json-schema.gemspec0000644000004100000410000001411715050634712017537 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: json-schema 6.0.0 ruby lib Gem::Specification.new do |s| s.name = "json-schema".freeze s.version = "6.0.0" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.metadata = { "bug_tracker_uri" => "https://github.com/voxpupuli/json-schema//issues", "changelog_uri" => "https://github.com/voxpupuli/json-schema//blob/master/CHANGELOG.md", "funding_uri" => "https://github.com/sponsors/voxpupuli", "source_code_uri" => "https://github.com/voxpupuli/json-schema/" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Kenny Hoxworth".freeze, "Vox Pupuli".freeze] s.date = "1980-01-02" s.email = "voxpupuli@groups.io".freeze s.extra_rdoc_files = ["LICENSE.md".freeze, "README.md".freeze] s.files = ["LICENSE.md".freeze, "README.md".freeze, "lib/json-schema.rb".freeze, "lib/json-schema/attribute.rb".freeze, "lib/json-schema/attributes/additionalitems.rb".freeze, "lib/json-schema/attributes/additionalproperties.rb".freeze, "lib/json-schema/attributes/allof.rb".freeze, "lib/json-schema/attributes/anyof.rb".freeze, "lib/json-schema/attributes/const.rb".freeze, "lib/json-schema/attributes/dependencies.rb".freeze, "lib/json-schema/attributes/dependencies_v4.rb".freeze, "lib/json-schema/attributes/disallow.rb".freeze, "lib/json-schema/attributes/divisibleby.rb".freeze, "lib/json-schema/attributes/enum.rb".freeze, "lib/json-schema/attributes/extends.rb".freeze, "lib/json-schema/attributes/format.rb".freeze, "lib/json-schema/attributes/formats/custom.rb".freeze, "lib/json-schema/attributes/formats/date.rb".freeze, "lib/json-schema/attributes/formats/date_time.rb".freeze, "lib/json-schema/attributes/formats/date_time_v4.rb".freeze, "lib/json-schema/attributes/formats/ip.rb".freeze, "lib/json-schema/attributes/formats/time.rb".freeze, "lib/json-schema/attributes/formats/uri.rb".freeze, "lib/json-schema/attributes/items.rb".freeze, "lib/json-schema/attributes/limit.rb".freeze, "lib/json-schema/attributes/limits/items.rb".freeze, "lib/json-schema/attributes/limits/length.rb".freeze, "lib/json-schema/attributes/limits/max_items.rb".freeze, "lib/json-schema/attributes/limits/max_length.rb".freeze, "lib/json-schema/attributes/limits/max_properties.rb".freeze, "lib/json-schema/attributes/limits/maximum.rb".freeze, "lib/json-schema/attributes/limits/maximum_inclusive.rb".freeze, "lib/json-schema/attributes/limits/min_items.rb".freeze, "lib/json-schema/attributes/limits/min_length.rb".freeze, "lib/json-schema/attributes/limits/min_properties.rb".freeze, "lib/json-schema/attributes/limits/minimum.rb".freeze, "lib/json-schema/attributes/limits/minimum_inclusive.rb".freeze, "lib/json-schema/attributes/limits/numeric.rb".freeze, "lib/json-schema/attributes/limits/properties.rb".freeze, "lib/json-schema/attributes/maxdecimal.rb".freeze, "lib/json-schema/attributes/multipleof.rb".freeze, "lib/json-schema/attributes/not.rb".freeze, "lib/json-schema/attributes/oneof.rb".freeze, "lib/json-schema/attributes/pattern.rb".freeze, "lib/json-schema/attributes/patternproperties.rb".freeze, "lib/json-schema/attributes/properties.rb".freeze, "lib/json-schema/attributes/properties_optional.rb".freeze, "lib/json-schema/attributes/properties_v4.rb".freeze, "lib/json-schema/attributes/propertynames.rb".freeze, "lib/json-schema/attributes/ref.rb".freeze, "lib/json-schema/attributes/required.rb".freeze, "lib/json-schema/attributes/type.rb".freeze, "lib/json-schema/attributes/type_v4.rb".freeze, "lib/json-schema/attributes/uniqueitems.rb".freeze, "lib/json-schema/errors/custom_format_error.rb".freeze, "lib/json-schema/errors/json_load_error.rb".freeze, "lib/json-schema/errors/json_parse_error.rb".freeze, "lib/json-schema/errors/schema_error.rb".freeze, "lib/json-schema/errors/schema_parse_error.rb".freeze, "lib/json-schema/errors/uri_error.rb".freeze, "lib/json-schema/errors/validation_error.rb".freeze, "lib/json-schema/schema.rb".freeze, "lib/json-schema/schema/reader.rb".freeze, "lib/json-schema/schema/validator.rb".freeze, "lib/json-schema/util/array_set.rb".freeze, "lib/json-schema/util/uri.rb".freeze, "lib/json-schema/util/uuid.rb".freeze, "lib/json-schema/validator.rb".freeze, "lib/json-schema/validators/draft1.rb".freeze, "lib/json-schema/validators/draft2.rb".freeze, "lib/json-schema/validators/draft3.rb".freeze, "lib/json-schema/validators/draft4.rb".freeze, "lib/json-schema/validators/draft6.rb".freeze, "lib/json-schema/validators/hyper-draft1.rb".freeze, "lib/json-schema/validators/hyper-draft2.rb".freeze, "lib/json-schema/validators/hyper-draft3.rb".freeze, "lib/json-schema/validators/hyper-draft4.rb".freeze, "lib/json-schema/validators/hyper-draft6.rb".freeze, "resources/draft-01.json".freeze, "resources/draft-02.json".freeze, "resources/draft-03.json".freeze, "resources/draft-04.json".freeze, "resources/draft-06.json".freeze] s.homepage = "https://github.com/voxpupuli/json-schema/".freeze s.licenses = ["MIT".freeze] s.required_ruby_version = Gem::Requirement.new(">= 3.2".freeze) s.rubygems_version = "3.3.15".freeze s.summary = "Ruby JSON Schema Validator".freeze if s.respond_to? :specification_version then s.specification_version = 4 end if s.respond_to? :add_runtime_dependency then s.add_runtime_dependency(%q.freeze, ["~> 2.8"]) s.add_runtime_dependency(%q.freeze, ["~> 3.1"]) s.add_development_dependency(%q.freeze, ["~> 5.0"]) s.add_development_dependency(%q.freeze, ["~> 13.0"]) s.add_development_dependency(%q.freeze, ["~> 4.2.0"]) s.add_development_dependency(%q.freeze, ["~> 3.23"]) else s.add_dependency(%q.freeze, ["~> 2.8"]) s.add_dependency(%q.freeze, ["~> 3.1"]) s.add_dependency(%q.freeze, ["~> 5.0"]) s.add_dependency(%q.freeze, ["~> 13.0"]) s.add_dependency(%q.freeze, ["~> 4.2.0"]) s.add_dependency(%q.freeze, ["~> 3.23"]) end end json-schema-6.0.0/resources/0000755000004100000410000000000015050634712015771 5ustar www-datawww-datajson-schema-6.0.0/resources/draft-03.json0000644000004100000410000000742715050634712020216 0ustar www-datawww-data{ "$schema": "http://json-schema.org/draft-03/schema#", "id": "http://json-schema.org/draft-03/schema#", "type": "object", "properties": { "type": { "type": [ "string", "array" ], "items": { "type": [ "string", { "$ref": "#" } ] }, "uniqueItems": true, "default": "any" }, "properties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "patternProperties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "additionalProperties": { "type": [ { "$ref": "#" }, "boolean" ], "default": {} }, "items": { "type": [ { "$ref": "#" }, "array" ], "items": { "$ref": "#" }, "default": {} }, "additionalItems": { "type": [ { "$ref": "#" }, "boolean" ], "default": {} }, "required": { "type": "boolean", "default": false }, "dependencies": { "type": "object", "additionalProperties": { "type": [ "string", "array", { "$ref": "#" } ], "items": { "type": "string" } }, "default": {} }, "minimum": { "type": "number" }, "maximum": { "type": "number" }, "exclusiveMinimum": { "type": "boolean", "default": false }, "exclusiveMaximum": { "type": "boolean", "default": false }, "minItems": { "type": "integer", "minimum": 0, "default": 0 }, "maxItems": { "type": "integer", "minimum": 0 }, "uniqueItems": { "type": "boolean", "default": false }, "pattern": { "type": "string", "format": "regex" }, "minLength": { "type": "integer", "minimum": 0, "default": 0 }, "maxLength": { "type": "integer" }, "enum": { "type": "array", "minItems": 1, "uniqueItems": true }, "default": { "type": "any" }, "title": { "type": "string" }, "description": { "type": "string" }, "format": { "type": "string" }, "divisibleBy": { "type": "number", "minimum": 0, "exclusiveMinimum": true, "default": 1 }, "disallow": { "type": [ "string", "array" ], "items": { "type": [ "string", { "$ref": "#" } ] }, "uniqueItems": true }, "extends": { "type": [ { "$ref": "#" }, "array" ], "items": { "$ref": "#" }, "default": {} }, "id": { "type": "string", "format": "uri" }, "$ref": { "type": "string", "format": "uri" }, "$schema": { "type": "string", "format": "uri" } }, "dependencies": { "exclusiveMinimum": "minimum", "exclusiveMaximum": "maximum" }, "default": {} } json-schema-6.0.0/resources/draft-04.json0000644000004100000410000001042715050634712020211 0ustar www-datawww-data{ "id": "http://json-schema.org/draft-04/schema#", "$schema": "http://json-schema.org/draft-04/schema#", "description": "Core schema meta-schema", "definitions": { "schemaArray": { "type": "array", "minItems": 1, "items": { "$ref": "#" } }, "positiveInteger": { "type": "integer", "minimum": 0 }, "positiveIntegerDefault0": { "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ] }, "simpleTypes": { "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] }, "stringArray": { "type": "array", "items": { "type": "string" }, "minItems": 1, "uniqueItems": true } }, "type": "object", "properties": { "id": { "type": "string", "format": "uri" }, "$schema": { "type": "string", "format": "uri" }, "title": { "type": "string" }, "description": { "type": "string" }, "default": {}, "multipleOf": { "type": "number", "minimum": 0, "exclusiveMinimum": true }, "maximum": { "type": "number" }, "exclusiveMaximum": { "type": "boolean", "default": false }, "minimum": { "type": "number" }, "exclusiveMinimum": { "type": "boolean", "default": false }, "maxLength": { "$ref": "#/definitions/positiveInteger" }, "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" }, "pattern": { "type": "string", "format": "regex" }, "additionalItems": { "anyOf": [ { "type": "boolean" }, { "$ref": "#" } ], "default": {} }, "items": { "anyOf": [ { "$ref": "#" }, { "$ref": "#/definitions/schemaArray" } ], "default": {} }, "maxItems": { "$ref": "#/definitions/positiveInteger" }, "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" }, "uniqueItems": { "type": "boolean", "default": false }, "maxProperties": { "$ref": "#/definitions/positiveInteger" }, "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" }, "required": { "$ref": "#/definitions/stringArray" }, "additionalProperties": { "anyOf": [ { "type": "boolean" }, { "$ref": "#" } ], "default": {} }, "definitions": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "properties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "patternProperties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "dependencies": { "type": "object", "additionalProperties": { "anyOf": [ { "$ref": "#" }, { "$ref": "#/definitions/stringArray" } ] } }, "enum": { "type": "array", "minItems": 1, "uniqueItems": true }, "type": { "anyOf": [ { "$ref": "#/definitions/simpleTypes" }, { "type": "array", "items": { "$ref": "#/definitions/simpleTypes" }, "minItems": 1, "uniqueItems": true } ] }, "allOf": { "$ref": "#/definitions/schemaArray" }, "anyOf": { "$ref": "#/definitions/schemaArray" }, "oneOf": { "$ref": "#/definitions/schemaArray" }, "not": { "$ref": "#" } }, "dependencies": { "exclusiveMaximum": [ "maximum" ], "exclusiveMinimum": [ "minimum" ] }, "default": {} } json-schema-6.0.0/resources/draft-02.json0000644000004100000410000000524415050634712020210 0ustar www-datawww-data{ "$schema" : "http://json-schema.org/draft-02/hyper-schema#", "id" : "http://json-schema.org/draft-02/schema#", "type" : "object", "properties" : { "type" : { "type" : ["string", "array"], "items" : { "type" : ["string", {"$ref" : "#"}] }, "optional" : true, "uniqueItems" : true, "default" : "any" }, "properties" : { "type" : "object", "additionalProperties" : {"$ref" : "#"}, "optional" : true, "default" : {} }, "items" : { "type" : [{"$ref" : "#"}, "array"], "items" : {"$ref" : "#"}, "optional" : true, "default" : {} }, "optional" : { "type" : "boolean", "optional" : true, "default" : false }, "additionalProperties" : { "type" : [{"$ref" : "#"}, "boolean"], "optional" : true, "default" : {} }, "requires" : { "type" : ["string", {"$ref" : "#"}], "optional" : true }, "minimum" : { "type" : "number", "optional" : true }, "maximum" : { "type" : "number", "optional" : true }, "minimumCanEqual" : { "type" : "boolean", "optional" : true, "requires" : "minimum", "default" : true }, "maximumCanEqual" : { "type" : "boolean", "optional" : true, "requires" : "maximum", "default" : true }, "minItems" : { "type" : "integer", "optional" : true, "minimum" : 0, "default" : 0 }, "maxItems" : { "type" : "integer", "optional" : true, "minimum" : 0 }, "uniqueItems" : { "type" : "boolean", "optional" : true, "default" : false }, "pattern" : { "type" : "string", "optional" : true, "format" : "regex" }, "minLength" : { "type" : "integer", "optional" : true, "minimum" : 0, "default" : 0 }, "maxLength" : { "type" : "integer", "optional" : true }, "enum" : { "type" : "array", "optional" : true, "minItems" : 1, "uniqueItems" : true }, "title" : { "type" : "string", "optional" : true }, "description" : { "type" : "string", "optional" : true }, "format" : { "type" : "string", "optional" : true }, "contentEncoding" : { "type" : "string", "optional" : true }, "default" : { "type" : "any", "optional" : true }, "divisibleBy" : { "type" : "number", "minimum" : 0, "minimumCanEqual" : false, "optional" : true, "default" : 1 }, "disallow" : { "type" : ["string", "array"], "items" : {"type" : "string"}, "optional" : true, "uniqueItems" : true }, "extends" : { "type" : [{"$ref" : "#"}, "array"], "items" : {"$ref" : "#"}, "optional" : true, "default" : {} } }, "optional" : true, "default" : {} }json-schema-6.0.0/resources/draft-06.json0000644000004100000410000001040715050634712020211 0ustar www-datawww-data{ "$schema": "http://json-schema.org/draft-06/schema#", "$id": "http://json-schema.org/draft-06/schema#", "title": "Core schema meta-schema", "definitions": { "schemaArray": { "type": "array", "minItems": 1, "items": { "$ref": "#" } }, "nonNegativeInteger": { "type": "integer", "minimum": 0 }, "nonNegativeIntegerDefault0": { "allOf": [ { "$ref": "#/definitions/nonNegativeInteger" }, { "default": 0 } ] }, "simpleTypes": { "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] }, "stringArray": { "type": "array", "items": { "type": "string" }, "uniqueItems": true, "default": [] } }, "type": ["object", "boolean"], "properties": { "$id": { "type": "string", "format": "uri-reference" }, "$schema": { "type": "string", "format": "uri" }, "$ref": { "type": "string", "format": "uri-reference" }, "title": { "type": "string" }, "description": { "type": "string" }, "default": {}, "multipleOf": { "type": "number", "exclusiveMinimum": 0 }, "maximum": { "type": "number" }, "exclusiveMaximum": { "type": "number" }, "minimum": { "type": "number" }, "exclusiveMinimum": { "type": "number" }, "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, "pattern": { "type": "string", "format": "regex" }, "additionalItems": { "$ref": "#" }, "items": { "anyOf": [ { "$ref": "#" }, { "$ref": "#/definitions/schemaArray" } ], "default": {} }, "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, "uniqueItems": { "type": "boolean", "default": false }, "contains": { "$ref": "#" }, "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, "required": { "$ref": "#/definitions/stringArray" }, "additionalProperties": { "$ref": "#" }, "definitions": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "properties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "patternProperties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "dependencies": { "type": "object", "additionalProperties": { "anyOf": [ { "$ref": "#" }, { "$ref": "#/definitions/stringArray" } ] } }, "propertyNames": { "$ref": "#" }, "const": {}, "enum": { "type": "array", "minItems": 1, "uniqueItems": true }, "type": { "anyOf": [ { "$ref": "#/definitions/simpleTypes" }, { "type": "array", "items": { "$ref": "#/definitions/simpleTypes" }, "minItems": 1, "uniqueItems": true } ] }, "format": { "type": "string" }, "allOf": { "$ref": "#/definitions/schemaArray" }, "anyOf": { "$ref": "#/definitions/schemaArray" }, "oneOf": { "$ref": "#/definitions/schemaArray" }, "not": { "$ref": "#" } }, "default": {} } json-schema-6.0.0/resources/draft-01.json0000644000004100000410000000471315050634712020207 0ustar www-datawww-data{ "$schema" : "http://json-schema.org/draft-01/hyper-schema#", "id" : "http://json-schema.org/draft-01/schema#", "type" : "object", "properties" : { "type" : { "type" : ["string", "array"], "items" : { "type" : ["string", {"$ref" : "#"}] }, "optional" : true, "default" : "any" }, "properties" : { "type" : "object", "additionalProperties" : {"$ref" : "#"}, "optional" : true, "default" : {} }, "items" : { "type" : [{"$ref" : "#"}, "array"], "items" : {"$ref" : "#"}, "optional" : true, "default" : {} }, "optional" : { "type" : "boolean", "optional" : true, "default" : false }, "additionalProperties" : { "type" : [{"$ref" : "#"}, "boolean"], "optional" : true, "default" : {} }, "requires" : { "type" : ["string", {"$ref" : "#"}], "optional" : true }, "minimum" : { "type" : "number", "optional" : true }, "maximum" : { "type" : "number", "optional" : true }, "minimumCanEqual" : { "type" : "boolean", "optional" : true, "requires" : "minimum", "default" : true }, "maximumCanEqual" : { "type" : "boolean", "optional" : true, "requires" : "maximum", "default" : true }, "minItems" : { "type" : "integer", "optional" : true, "minimum" : 0, "default" : 0 }, "maxItems" : { "type" : "integer", "optional" : true, "minimum" : 0 }, "pattern" : { "type" : "string", "optional" : true, "format" : "regex" }, "minLength" : { "type" : "integer", "optional" : true, "minimum" : 0, "default" : 0 }, "maxLength" : { "type" : "integer", "optional" : true }, "enum" : { "type" : "array", "optional" : true, "minItems" : 1 }, "title" : { "type" : "string", "optional" : true }, "description" : { "type" : "string", "optional" : true }, "format" : { "type" : "string", "optional" : true }, "contentEncoding" : { "type" : "string", "optional" : true }, "default" : { "type" : "any", "optional" : true }, "maxDecimal" : { "type" : "integer", "optional" : true, "minimum" : 0 }, "disallow" : { "type" : ["string", "array"], "items" : {"type" : "string"}, "optional" : true }, "extends" : { "type" : [{"$ref" : "#"}, "array"], "items" : {"$ref" : "#"}, "optional" : true, "default" : {} } }, "optional" : true, "default" : {} }json-schema-6.0.0/README.md0000644000004100000410000004021415050634712015237 0ustar www-datawww-data# Ruby JSON Schema Validator [![License](https://img.shields.io/github/license/voxpupuli/json-schema.svg)](https://github.com/voxpupuli/json-schema/blob/master/LICENSE.md) [![Test](https://github.com/voxpupuli/json-schema/actions/workflows/test.yml/badge.svg)](https://github.com/voxpupuli/json-schema/actions/workflows/test.yml) [![Release](https://github.com/voxpupuli/json-schema/actions/workflows/release.yml/badge.svg)](https://github.com/voxpupuli/json-schema/actions/workflows/release.yml) [![RubyGem Version](https://img.shields.io/gem/v/json-schema.svg)](https://rubygems.org/gems/json-schema) [![RubyGem Downloads](https://img.shields.io/gem/dt/json-schema.svg)](https://rubygems.org/gems/json-schema) [![Donated by Iain Beeston](https://img.shields.io/badge/donated%20by-Iain%20Beeston-fb7047.svg)](#transfer-notice) This library is intended to provide Ruby with an interface for validating JSON objects against a JSON schema conforming to [JSON Schema Draft 6](https://tools.ietf.org/html/draft-wright-json-schema-01). Legacy support for [JSON Schema Draft 4](http://tools.ietf.org/html/draft-zyp-json-schema-04), [JSON Schema Draft 3](http://tools.ietf.org/html/draft-zyp-json-schema-03), [JSON Schema Draft 2](http://tools.ietf.org/html/draft-zyp-json-schema-02), and [JSON Schema Draft 1](http://tools.ietf.org/html/draft-zyp-json-schema-01) is also included. Additional Resources -------------------- - [Google Groups](https://groups.google.com/forum/#!forum/ruby-json-schema) - #voxpupuli on irc.libera.chat Version 2.0.0 Upgrade Notes --------------------------- Please be aware that the upgrade to version 2.0.0 will use Draft-04 **by default**, so schemas that do not declare a validator using the `$schema` keyword will use Draft-04 now instead of Draft-03. This is the reason for the major version upgrade. Version 3.0.0 Upgrade Notes --------------------------- All individual changes are documented in the CHANGELOG.md. The biggest change is that the new version only supports Ruby 2.5 and newer. Take a look into the gemspec file to see the currently supported Ruby version and also `.github/workflows/test.yml` to see the Ruby versions we test on. Installation ------------ From rubygems.org: ```sh gem install json-schema ``` From the git repo: ```sh gem build json-schema.gemspec gem install json-schema-*.gem ``` Validation ----- Three base validation methods exist: 1. `validate`: returns a boolean on whether a validation attempt passes 2. `validate!`: throws a `JSON::Schema::ValidationError` with an appropriate message/trace on where the validation failed 3. `fully_validate`: builds an array of validation errors return when validation is complete All methods take two arguments, which can be either a JSON string, a file containing JSON, or a Ruby object representing JSON data. The first argument to these methods is always the schema, the second is always the data to validate. An optional third options argument is also accepted; available options are used in the examples below. By default, the validator uses the [JSON Schema Draft 4](http://tools.ietf.org/html/draft-zyp-json-schema-04) specification for validation; however, the user is free to specify additional specifications or extend existing ones. Legacy support for Draft 1, Draft 2, and Draft 3 is included by either passing an optional `:version` parameter to the `validate` method (set either as `:draft1` or `draft2`), or by declaring the `$schema` attribute in the schema and referencing the appropriate specification URI. Note that the `$schema` attribute takes precedence over the `:version` option during parsing and validation. For further information on json schema itself refer to Understanding JSON Schema. Basic Usage -------------- ```ruby require "json-schema" schema = { "type" => "object", "required" => ["a"], "properties" => { "a" => {"type" => "integer"} } } # # validate ruby objects against a ruby schema # # => true JSON::Validator.validate(schema, { "a" => 5 }) # => false JSON::Validator.validate(schema, {}) # # validate a json string against a json schema file # require "json" File.write("schema.json", JSON.dump(schema)) # => true JSON::Validator.validate('schema.json', '{ "a": 5 }') # # raise an error when validation fails # # => "The property '#/a' of type String did not match the following type: integer" begin JSON::Validator.validate!(schema, { "a" => "taco" }) rescue JSON::Schema::ValidationError => e e.message end # # return an array of error messages when validation fails # # => ["The property '#/a' of type String did not match the following type: integer in schema 18a1ffbb-4681-5b00-bd15-2c76aee4b28f"] JSON::Validator.fully_validate(schema, { "a" => "taco" }) ``` Advanced Options ----------------- ```ruby require "json-schema" schema = { "type"=>"object", "required" => ["a"], "properties" => { "a" => { "type" => "integer", "default" => 42 }, "b" => { "type" => "object", "properties" => { "x" => { "type" => "integer" } } } } } # # with the `:list` option, a list can be validated against a schema that represents the individual objects # # => true JSON::Validator.validate(schema, [{"a" => 1}, {"a" => 2}, {"a" => 3}], :list => true) # => false JSON::Validator.validate(schema, [{"a" => 1}, {"a" => 2}, {"a" => 3}]) # # with the `:errors_as_objects` option, `#fully_validate` returns errors as hashes instead of strings # # => [{:schema=>#, :fragment=>"#/a", :message=>"The property '#/a' of type String did not match the following type: integer in schema 18a1ffbb-4681-5b00-bd15-2c76aee4b28f", :failed_attribute=>"TypeV4"}] JSON::Validator.fully_validate(schema, { "a" => "taco" }, :errors_as_objects => true) # # with the `:strict` option, all properties are considered to have `"required": true` and all objects `"additionalProperties": false` # # => true JSON::Validator.validate(schema, { "a" => 1, "b" => { "x" => 2 } }, :strict => true) # => false JSON::Validator.validate(schema, { "a" => 1, "b" => { "x" => 2 }, "c" => 3 }, :strict => true) # => false JSON::Validator.validate(schema, { "a" => 1 }, :strict => true) # # with the `:fragment` option, only a fragment of the schema is used for validation # # => true JSON::Validator.validate(schema, { "x" => 1 }, :fragment => "#/properties/b") # => false JSON::Validator.validate(schema, { "x" => 1 }) # # with the `:validate_schema` option, the schema is validated (against the json schema spec) before the json is validated (against the specified schema) # # => true JSON::Validator.validate(schema, { "a" => 1 }, :validate_schema => true) # => false JSON::Validator.validate({ "required" => true }, { "a" => 1 }, :validate_schema => true) # # with the `:insert_defaults` option, any undefined values in the json that have a default in the schema are replaced with the default before validation # # => true JSON::Validator.validate(schema, {}, :insert_defaults => true) # => false JSON::Validator.validate(schema, {}) # # with the `:version` option, schemas conforming to older drafts of the json schema spec can be used # v2_schema = { "type" => "object", "properties" => { "a" => { "type" => "integer" } } } # => false JSON::Validator.validate(v2_schema, {}, :version => :draft2) # => true JSON::Validator.validate(v2_schema, {}) # # with the `:parse_data` option set to false, the json must be a parsed ruby object (not a json text, a uri or a file path) # # => true JSON::Validator.validate(schema, { "a" => 1 }, :parse_data => false) # => false JSON::Validator.validate(schema, '{ "a": 1 }', :parse_data => false) # # with the `:parse_integer` option set to false, the integer value given as string will not be parsed. # # => true JSON::Validator.validate({type: "integer"}, "23") # => false JSON::Validator.validate({type: "integer"}, "23", parse_integer: false) # => true JSON::Validator.validate({type: "string"}, "123", parse_integer: false) # => false JSON::Validator.validate({type: "string"}, "123") # # with the `:json` option, the json must be an unparsed json text (not a hash, a uri or a file path) # # => true JSON::Validator.validate(schema, '{ "a": 1 }', :json => true) # => "no implicit conversion of Hash into String" begin JSON::Validator.validate(schema, { "a" => 1 }, :json => true) rescue TypeError => e e.message end # # with the `:uri` option, the json must be a uri or file path (not a hash or a json text) # File.write("data.json", '{ "a": 1 }') # => true JSON::Validator.validate(schema, "data.json", :uri => true) # => "Can't convert Hash into String." begin JSON::Validator.validate(schema, { "a" => 1 }, :uri => true) rescue TypeError => e e.message end # # with the `:clear_cache` option set to true, the internal cache of schemas is # cleared after validation (otherwise schemas are cached for efficiency) # File.write("schema.json", v2_schema.to_json) # => true JSON::Validator.validate("schema.json", {}) File.write("schema.json", schema.to_json) # => true JSON::Validator.validate("schema.json", {}, :clear_cache => true) # => false JSON::Validator.validate("schema.json", {}) ``` Extending Schemas ----------------- For this example, we are going to extend the [JSON Schema Draft 3](http://tools.ietf.org/html/draft-zyp-json-schema-03) specification by adding a 'bitwise-and' property for validation. ```ruby require "json-schema" class BitwiseAndAttribute < JSON::Schema::Attribute def self.validate(current_schema, data, fragments, processor, validator, options = {}) if data.is_a?(Integer) && data & current_schema.schema['bitwise-and'].to_i == 0 message = "The property '#{build_fragment(fragments)}' did not evaluate to true when bitwise-AND'd with #{current_schema.schema['bitwise-or']}" validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) end end end class ExtendedSchema < JSON::Schema::Draft3 def initialize super @attributes["bitwise-and"] = BitwiseAndAttribute @uri = JSON::Util::URI.parse("http://test.com/test.json") @names = ["http://test.com/test.json"] end JSON::Validator.register_validator(self.new) end schema = { "$schema" => "http://test.com/test.json", "properties" => { "a" => { "bitwise-and" => 1 }, "b" => { "type" => "string" } } } data = { "a" => 0 } data = {"a" => 1, "b" => "taco"} JSON::Validator.validate(schema,data) # => true data = {"a" => 1, "b" => 5} JSON::Validator.validate(schema,data) # => false data = {"a" => 0, "b" => "taco"} JSON::Validator.validate(schema,data) # => false ``` Custom format validation ------------------------ The JSON schema standard allows custom formats in schema definitions which should be ignored by validators that do not support them. JSON::Schema allows registering procs as custom format validators which receive the value to be checked as parameter and must raise a `JSON::Schema::CustomFormatError` to indicate a format violation. The error message will be prepended by the property name, e.g. [The property '#a']() ```ruby require "json-schema" format_proc = -> value { raise JSON::Schema::CustomFormatError.new("must be 42") unless value == "42" } # register the proc for format 'the-answer' for draft4 schema JSON::Validator.register_format_validator("the-answer", format_proc, ["draft4"]) # omitting the version parameter uses ["draft1", "draft2", "draft3", "draft4"] as default JSON::Validator.register_format_validator("the-answer", format_proc) # deregistering the custom validator # (also ["draft1", "draft2", "draft3", "draft4"] as default version) JSON::Validator.deregister_format_validator('the-answer', ["draft4"]) # shortcut to restore the default formats for validators (same default as before) JSON::Validator.restore_default_formats(["draft4"]) # with the validator registered as above, the following results in # ["The property '#a' must be 42"] as returned errors schema = { "$schema" => "http://json-schema.org/draft-04/schema#", "properties" => { "a" => { "type" => "string", "format" => "the-answer", } } } errors = JSON::Validator.fully_validate(schema, {"a" => "23"}) ``` Validating a JSON Schema ------------------------ To validate that a JSON Schema conforms to the JSON Schema standard, you need to validate your schema against the metaschema for the appropriate JSON Schema Draft. All of the normal validation methods can be used for this. First retrieve the appropriate metaschema from the internal cache (using `JSON::Validator.validator_for_name()` or `JSON::Validator.validator_for_uri()`) and then simply validate your schema against it. ```ruby require "json-schema" schema = { "type" => "object", "properties" => { "a" => {"type" => "integer"} } } metaschema = JSON::Validator.validator_for_name("draft4").metaschema # => true JSON::Validator.validate(metaschema, schema) ``` Controlling Remote Schema Reading --------------------------------- In some cases, you may wish to prevent the JSON Schema library from making HTTP calls or reading local files in order to resolve `$ref` schemas. If you fully control all schemas which should be used by validation, this could be accomplished by registering all referenced schemas with the validator in advance: ```ruby schema = JSON::Schema.new(some_schema_definition, Addressable::URI.parse('http://example.com/my-schema')) JSON::Validator.add_schema(schema) ``` If more extensive control is necessary, the `JSON::Schema::Reader` instance used can be configured in a few ways: ```ruby # Change the default schema reader used JSON::Validator.schema_reader = JSON::Schema::Reader.new(:accept_uri => true, :accept_file => false) # For this validation call, use a reader which only accepts URIs from my-website.com schema_reader = JSON::Schema::Reader.new( :accept_uri => proc { |uri| uri.host == 'my-website.com' } ) JSON::Validator.validate(some_schema, some_object, :schema_reader => schema_reader) ``` The `JSON::Schema::Reader` interface requires only an object which responds to `read(string)` and returns a `JSON::Schema` instance. See the [API documentation](http://www.rubydoc.info/github/ruby-json-schema/json-schema/master/JSON/Schema/Reader) for more information. JSON Backends ------------- The JSON Schema library currently supports the `json` and `yajl-ruby` backend JSON parsers. If either of these libraries are installed, they will be automatically loaded and used to parse any JSON strings supplied by the user. If more than one of the supported JSON backends are installed, the `yajl-ruby` parser is used by default. This can be changed by issuing the following before validation: ```ruby JSON::Validator.json_backend = :json ``` Optionally, the JSON Schema library supports using the MultiJSON library for selecting JSON backends. If the MultiJSON library is installed, it will be autoloaded. Notes ----- The 'format' attribute is only validated for the following values: - date-time - date - time - ip-address (IPv4 address in draft1, draft2 and draft3) - ipv4 (IPv4 address in draft4) - ipv6 - uri All other 'format' attribute values are simply checked to ensure the instance value is of the correct datatype (e.g., an instance value is validated to be an integer or a float in the case of 'utc-millisec'). Additionally, JSON::Validator does not handle any json hyperschema attributes. # Transfer Notice This plugin was originally authored by [Iain Beeston](https://github.com/iainbeeston). The maintainer preferred that [Vox Pupuli](https://voxpupuli.org/) take ownership of the module for future improvement and maintenance. Existing pull requests and issues were transferred, please fork and continue to contribute [here](https://github.com/voxpupuli/json-schema). # License This gem is licensed under the [MIT license](LICENSE.md). ## Release information To make a new release, please do: * update the version in VERSION.yml * Install gems with `bundle install --with release --path .vendor` * generate the changelog with `bundle exec rake changelog` * Check if the new version matches the closed issues/PRs in the changelog * Create a PR with it * After it got merged, push a tag. GitHub actions will do the actual release to rubygems and GitHub Packages