Skip to content

Commit

Permalink
Merge pull request #131 from RST-J/extract_formats
Browse files Browse the repository at this point in the history
Extract formats into separate classes
  • Loading branch information
hoxworth committed Oct 26, 2014
2 parents 024a63d + 5488f37 commit 3adabea
Show file tree
Hide file tree
Showing 17 changed files with 241 additions and 162 deletions.
1 change: 1 addition & 0 deletions lib/json-schema.rb
Expand Up @@ -20,5 +20,6 @@
require 'json-schema/schema'
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/attributes/formats/*.rb")].each {|file| require file }
Dir[File.join(File.dirname(__FILE__), "json-schema/validators/*.rb")].sort!.each {|file| require file }
require 'json-schema/uri/file'
116 changes: 3 additions & 113 deletions lib/json-schema/attributes/format.rb
Expand Up @@ -4,120 +4,10 @@
module JSON
class Schema
class FormatAttribute < Attribute
def self.validate(current_schema, data, fragments, processor, validator, options = {})
case current_schema.schema['format']

# Timestamp in restricted ISO-8601 YYYY-MM-DDThh:mm:ssZ with optional decimal fraction of the second
when 'date-time'
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"
r = Regexp.new('^\d\d\d\d-\d\d-\d\dT(\d\d):(\d\d):(\d\d)([\.,]\d+)?(Z|[+-](\d\d)(:?\d\d)?)?$')
if (m = r.match(data))
parts = data.split("T")
begin
Date.parse(parts[0])
rescue Exception
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
return
end
begin
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
rescue Exception
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
return
end
else
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
return
end
end
# Date in the format of YYYY-MM-DD
when 'date'
if data.is_a?(String)
error_message = "The property '#{build_fragment(fragments)}' must be a date in the format of YYYY-MM-DD"
r = Regexp.new('^\d\d\d\d-\d\d-\d\d$')
if (m = r.match(data))
begin
Date.parse(data)
rescue Exception
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
return
end
else
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
return
end
end

# Time in the format of HH:MM:SS
when 'time'
if data.is_a?(String)
error_message = "The property '#{build_fragment(fragments)}' must be a time in the format of hh:mm:ss"
r = Regexp.new('^(\d\d):(\d\d):(\d\d)$')
if (m = r.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])
return
end
end

# IPv4 in dotted-quad format
when 'ip-address', 'ipv4'
if data.is_a?(String)
error_message = "The property '#{build_fragment(fragments)}' must be a valid IPv4 address"
r = Regexp.new('^(\d+){1,3}\.(\d+){1,3}\.(\d+){1,3}\.(\d+){1,3}$')
if (m = r.match(data))
1.upto(4) do |x|
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[x].to_i > 255
end
else
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
return
end
end

# IPv6 in standard format (including abbreviations)
when 'ipv6'
if data.is_a?(String)
error_message = "The property '#{build_fragment(fragments)}' must be a valid IPv6 address"
r = Regexp.new('^[a-f0-9:]+$')
if (m = r.match(data))
# All characters are valid, now validate structure
parts = data.split(":")
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if parts.length > 8
condensed_zeros = false
parts.each do |part|
if part.length == 0
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if condensed_zeros
condensed_zeros = true
end
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if part.length > 4
end
else
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
return
end
end

when 'uri'
if data.is_a?(String)
error_message = "The property '#{build_fragment(fragments)}' must be a valid URI"
begin
URI.parse(URI.escape(data))
rescue URI::InvalidURIError
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
end
end

when 'hostname'

when 'email'
end
def self.validate(current_schema, data, fragments, processor, validator, options = {})
validator = validator.formats[current_schema.schema['format'].to_s]
validator.validate(current_schema, data, fragments, processor, validator, options = {}) unless validator.nil?
end
end
end
Expand Down
39 changes: 39 additions & 0 deletions lib/json-schema/attributes/formats/date.rb
@@ -0,0 +1,39 @@
require 'json-schema/attribute'
require 'uri'
module JSON
class Schema
class DateTimeFormat < FormatAttribute
def self.validate(current_schema, data, fragments, processor, validator, options = {})
case current_schema.schema['format']

# Timestamp in restricted ISO-8601 YYYY-MM-DDThh:mm:ssZ with optional decimal fraction of the second
when 'date-time'
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"
r = Regexp.new('^\d\d\d\d-\d\d-\d\dT(\d\d):(\d\d):(\d\d)([\.,]\d+)?(Z|[+-](\d\d)(:?\d\d)?)?$')
if (m = r.match(data))
parts = data.split("T")
begin
Date.parse(parts[0])
rescue Exception
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
return
end
begin
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
rescue Exception
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
return
end
else
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
return
end
end
end
end
end
end
end
25 changes: 25 additions & 0 deletions lib/json-schema/attributes/formats/date_time.rb
@@ -0,0 +1,25 @@
require 'json-schema/attribute'
require 'uri'
module JSON
class Schema
class DateFormat < FormatAttribute
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"
r = Regexp.new('^\d\d\d\d-\d\d-\d\d$')
if (r.match(data))
begin
Date.parse(data)
rescue Exception
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
return
end
else
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
return
end
end
end
end
end
end
22 changes: 22 additions & 0 deletions lib/json-schema/attributes/formats/ip4.rb
@@ -0,0 +1,22 @@
require 'json-schema/attribute'
require 'uri'
module JSON
class Schema
class IP4Format < FormatAttribute
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 valid IPv4 address"
r = Regexp.new('^(\d+){1,3}\.(\d+){1,3}\.(\d+){1,3}\.(\d+){1,3}$')
if (m = r.match(data))
1.upto(4) do |x|
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[x].to_i > 255
end
else
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
return
end
end
end
end
end
end
30 changes: 30 additions & 0 deletions lib/json-schema/attributes/formats/ip6.rb
@@ -0,0 +1,30 @@
require 'json-schema/attribute'
require 'uri'
module JSON
class Schema
class IP6Format < FormatAttribute
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 valid IPv6 address"
r = Regexp.new('^[a-f0-9:]+$')
if (r.match(data))
# All characters are valid, now validate structure
parts = data.split(":")
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if parts.length > 8
condensed_zeros = false
parts.each do |part|
if part.length == 0
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if condensed_zeros
condensed_zeros = true
end
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if part.length > 4
end
else
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
return
end
end
end
end
end
end
22 changes: 22 additions & 0 deletions lib/json-schema/attributes/formats/time.rb
@@ -0,0 +1,22 @@
require 'json-schema/attribute'
require 'uri'
module JSON
class Schema
class TimeFormat < FormatAttribute
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"
r = Regexp.new('^(\d\d):(\d\d):(\d\d)$')
if (m = r.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])
return
end
end
end
end
end
end
18 changes: 18 additions & 0 deletions lib/json-schema/attributes/formats/uri.rb
@@ -0,0 +1,18 @@
require 'json-schema/attribute'
require 'uri'
module JSON
class Schema
class UriFormat < FormatAttribute
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 valid URI"
begin
URI.parse(URI.escape(data))
rescue URI::InvalidURIError
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
end
end
end
end
end
end
2 changes: 1 addition & 1 deletion lib/json-schema/schema/validator.rb
@@ -1,7 +1,7 @@
module JSON
class Schema
class Validator
attr_accessor :attributes, :uri, :names, :metaschema
attr_accessor :attributes, :formats, :uri, :names, :metaschema

def initialize()
@attributes = {}
Expand Down
8 changes: 8 additions & 0 deletions lib/json-schema/validators/draft1.rb
Expand Up @@ -24,6 +24,14 @@ def initialize
"items" => JSON::Schema::ItemsAttribute,
"extends" => JSON::Schema::ExtendsAttribute
}
@formats = {
'date-time' => DateTimeFormat,
'date' => DateFormat,
'time' => TimeFormat,
'ip-address' => IP4Format,
'ipv6' => IP6Format,
'uri' => UriFormat
}
@uri = URI.parse("http://json-schema.org/draft-01/schema#")
@names = ["draft1"]
@metaschema = File.join("resources", "draft-01.json")
Expand Down
8 changes: 8 additions & 0 deletions lib/json-schema/validators/draft2.rb
Expand Up @@ -25,6 +25,14 @@ def initialize
"items" => JSON::Schema::ItemsAttribute,
"extends" => JSON::Schema::ExtendsAttribute
}
@formats = {
'date-time' => DateTimeFormat,
'date' => DateFormat,
'time' => TimeFormat,
'ip-address' => IP4Format,
'ipv6' => IP6Format,
'uri' => UriFormat
}
@uri = URI.parse("http://json-schema.org/draft-02/schema#")
@names = ["draft2"]
@metaschema = File.join("resources", "draft-02.json")
Expand Down
8 changes: 8 additions & 0 deletions lib/json-schema/validators/draft3.rb
Expand Up @@ -29,6 +29,14 @@ def initialize
"extends" => JSON::Schema::ExtendsAttribute,
"$ref" => JSON::Schema::RefAttribute
}
@formats = {
'date-time' => DateTimeFormat,
'date' => DateFormat,
'ip-address' => IP4Format,
'ipv6' => IP6Format,
'time' => TimeFormat,
'uri' => UriFormat
}
@uri = URI.parse("http://json-schema.org/draft-03/schema#")
@names = ["draft3", "http://json-schema.org/draft-03/schema#"]
@metaschema = File.join("resources", "draft-03.json")
Expand Down
6 changes: 6 additions & 0 deletions lib/json-schema/validators/draft4.rb
Expand Up @@ -36,6 +36,12 @@ def initialize
"extends" => JSON::Schema::ExtendsAttribute,
"$ref" => JSON::Schema::RefAttribute
}
@formats = {
'date-time' => DateTimeFormat,
'ipv4' => IP4Format,
'ipv6' => IP6Format,
'uri' => UriFormat
}
@uri = URI.parse("http://json-schema.org/draft-04/schema#")
@names = ["draft4", "http://json-schema.org/draft-04/schema#"]
@metaschema = File.join("resources", "draft-04.json")
Expand Down
12 changes: 12 additions & 0 deletions test/test_jsonschema_draft1.rb
Expand Up @@ -684,6 +684,18 @@ def test_format_datetime
assert(!JSON::Validator.validate(schema,data,:version => :draft1))
end

def test_format_unknown
schema = {
"type" => "object",
"properties" => { "a" => {"type" => "string", "format" => "unknown"}}
}

data = {"a" => "I can write what I want here"}
assert(JSON::Validator.validate(schema,data,:version => :draft1))
data = {"a" => ""}
assert(JSON::Validator.validate(schema,data,:version => :draft1))
end


def test_format_union
data1 = {"a" => "boo"}
Expand Down
12 changes: 12 additions & 0 deletions test/test_jsonschema_draft2.rb
Expand Up @@ -756,6 +756,18 @@ def test_format_datetime
assert(!JSON::Validator.validate(schema,data,:version => :draft2))
end

def test_format_unknown
schema = {
"type" => "object",
"properties" => { "a" => {"type" => "string", "format" => "unknown"}}
}

data = {"a" => "I can write what I want here"}
assert(JSON::Validator.validate(schema,data,:version => :draft2))
data = {"a" => ""}
assert(JSON::Validator.validate(schema,data,:version => :draft2))
end


def test_format_union
data1 = {"a" => "boo"}
Expand Down

0 comments on commit 3adabea

Please sign in to comment.