-
-
Notifications
You must be signed in to change notification settings - Fork 243
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
220 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,4 +7,5 @@ gem "json", :platforms => :mri_18 | |
group :development do | ||
gem "rake" | ||
gem "minitest", '~> 5.0' | ||
gem "pry" | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
require 'open-uri' | ||
require 'addressable/uri' | ||
require 'pathname' | ||
|
||
module JSON | ||
class Schema | ||
# Raised by {JSON::Schema::Loader} when one of its settings indicate | ||
# a schema should not be loaded. | ||
class LoadRefused < 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("Load of #{type == :uri ? 'URI' : type} at #{location} refused!") | ||
end | ||
end | ||
|
||
# When an unregistered schema is encountered, the {JSON::Schema::Loader} is | ||
# used to fetch its contents and register it with the {JSON::Validator}. | ||
# | ||
# This default loader will load schemas from the filesystem or from a URI. | ||
class Loader | ||
# The behavior of the schema loader can be controlled by providing | ||
# callbacks to determine whether to permit loading 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 load 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_loader = JSON::Schema::Loader.new( | ||
# :accept_uri => false, | ||
# :accept_file => false | ||
# ) | ||
# | ||
# @example Only permit URIs from certain hosts | ||
# JSON::Validator.schema_loader = JSON::Schema::Loader.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 load the schema | ||
# @return [JSON::Schema] | ||
# @raise [JSON::Schema::LoadRefused] if +accept_uri+ or +accept_file+ | ||
# indicated the schema should not be loaded | ||
# @raise [JSON::ParserError] if the schema was not a valid JSON object | ||
def load(location) | ||
uri = Addressable::URI.parse(location.to_s) | ||
body = if uri.scheme.nil? || uri.scheme == 'file' | ||
uri = Addressable::URI.convert_path(uri.path) | ||
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) | ||
open(uri.to_s).read | ||
else | ||
raise JSON::Schema::LoadRefused.new(uri.to_s, :uri) | ||
end | ||
end | ||
|
||
def read_file(pathname) | ||
if accept_file?(pathname) | ||
pathname.read | ||
else | ||
raise JSON::Schema::LoadRefused.new(pathname.to_s, :file) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"description": "An Address following the convention of http://microformats.org/wiki/hcard", | ||
"type": "object", | ||
"properties": { | ||
"post-office-box": { "type": "string" }, | ||
"extended-address": { "type": "string" }, | ||
"street-address": { "type": "string" }, | ||
"locality":{ "type": "string" }, | ||
"region": { "type": "string" }, | ||
"postal-code": { "type": "string" }, | ||
"country-name": { "type": "string"} | ||
}, | ||
"required": ["locality", "region", "country-name"], | ||
"dependencies": { | ||
"post-office-box": "street-address", | ||
"extended-address": "street-address" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
require File.expand_path('../test_helper', __FILE__) | ||
require 'webmock' | ||
|
||
class TestSchemaLoader < Minitest::Test | ||
ADDRESS_SCHEMA_URI = 'http://json-schema.org/address' | ||
ADDRESS_SCHEMA_PATH = File.expand_path('../schemas/address_microformat.json', __FILE__) | ||
|
||
include WebMock::API | ||
|
||
def setup | ||
WebMock.enable! | ||
WebMock.disable_net_connect! | ||
end | ||
|
||
def teardown | ||
WebMock.reset! | ||
WebMock.disable! | ||
end | ||
|
||
def stub_address_request(body = File.read(ADDRESS_SCHEMA_PATH)) | ||
stub_request(:get, ADDRESS_SCHEMA_URI). | ||
to_return(:body => body, :status => 200) | ||
end | ||
|
||
def test_accept_all_uris | ||
stub_address_request | ||
|
||
loader = JSON::Schema::Loader.new | ||
schema = loader.load(ADDRESS_SCHEMA_URI) | ||
|
||
assert_equal schema.uri, Addressable::URI.parse("#{ADDRESS_SCHEMA_URI}#") | ||
end | ||
|
||
def test_accept_all_files | ||
loader = JSON::Schema::Loader.new | ||
schema = loader.load(ADDRESS_SCHEMA_PATH) | ||
|
||
assert_equal schema.uri, Addressable::URI.convert_path(ADDRESS_SCHEMA_PATH + '#') | ||
end | ||
|
||
def test_refuse_all_uris | ||
loader = JSON::Schema::Loader.new(:accept_uri => false) | ||
refute loader.accept_uri?(Addressable::URI.parse('http://foo.com')) | ||
end | ||
|
||
def test_refuse_all_files | ||
loader = JSON::Schema::Loader.new(:accept_file => false) | ||
refute loader.accept_file?(Pathname.new('/foo/bar/baz')) | ||
end | ||
|
||
def test_accept_uri_proc | ||
loader = JSON::Schema::Loader.new( | ||
:accept_uri => proc { |uri| uri.host == 'json-schema.org' } | ||
) | ||
|
||
assert loader.accept_uri?(Addressable::URI.parse('http://json-schema.org/address')) | ||
refute loader.accept_uri?(Addressable::URI.parse('http://sub.json-schema.org/address')) | ||
end | ||
|
||
def test_accept_file_proc | ||
test_root = Pathname.new(__FILE__).expand_path.dirname | ||
|
||
loader = JSON::Schema::Loader.new( | ||
:accept_file => proc { |path| path.to_s.start_with?(test_root.to_s) } | ||
) | ||
|
||
assert loader.accept_file?(test_root.join('anything.json')) | ||
refute loader.accept_file?(test_root.join('..', 'anything.json')) | ||
end | ||
|
||
def test_file_scheme | ||
loader = JSON::Schema::Loader.new(:accept_uri => true, :accept_file => false) | ||
assert_raises(JSON::Schema::LoadRefused) do | ||
loader.load('file://' + ADDRESS_SCHEMA_PATH) | ||
end | ||
end | ||
|
||
def test_parse_error | ||
stub_address_request('this is totally not valid JSON!') | ||
|
||
loader = JSON::Schema::Loader.new | ||
assert_raises(JSON::ParserError) do | ||
loader.load(ADDRESS_SCHEMA_URI) | ||
end | ||
end | ||
end |