-
-
Notifications
You must be signed in to change notification settings - Fork 240
/
reader.rb
140 lines (125 loc) · 4.43 KB
/
reader.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
require '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)
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