json-schema 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Michael Bleigh, Josh Kalderimis, Erik Michaels-Ober, and Intridea, Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/LICENSE.md ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010-2011, Lookingglass Cyber Solutions
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.textile CHANGED
@@ -18,7 +18,7 @@ From the git repo:
18
18
 
19
19
  <pre>
20
20
  $ gem build json-schema.gemspec
21
- $ gem install json-schema-1.0.1.gem
21
+ $ gem install json-schema-1.0.2.gem
22
22
  </pre>
23
23
 
24
24
 
data/lib/json-schema.rb CHANGED
@@ -1,3 +1,26 @@
1
+ require 'rubygems'
2
+
3
+ def load_gem gemname
4
+ if begin
5
+ Gem::Specification::find_by_name(gemname)
6
+ rescue Gem::LoadError
7
+ false
8
+ rescue
9
+ Gem.available?(gemname)
10
+ end
11
+ require gemname
12
+ end
13
+ end
14
+
15
+ load_gem('yajl')
16
+ load_gem('json')
17
+
18
+ require File.join(File.dirname(__FILE__),"json-schema/lib/multi_json/multi_json.rb")
19
+
20
+ # Force MultiJson to load an engine before we define the JSON constant here; otherwise,
21
+ # it looks for things that are under the JSON namespace that aren't there (since we have defined it here)
22
+ MultiJson.engine
23
+
1
24
  $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/json-schema"
2
25
 
3
26
  require 'rubygems'
@@ -5,4 +28,4 @@ require 'schema'
5
28
  require 'validator'
6
29
  Dir[File.join(File.dirname(__FILE__), "json-schema/attributes/*.rb")].each {|file| require file }
7
30
  Dir[File.join(File.dirname(__FILE__), "json-schema/validators/*.rb")].each {|file| require file }
8
- require 'uri/file'
31
+ require 'uri/file'
@@ -0,0 +1,85 @@
1
+ module MultiJson
2
+ class DecodeError < StandardError
3
+ attr_reader :data
4
+ def initialize(message, backtrace, data)
5
+ super(message)
6
+ self.set_backtrace(backtrace)
7
+ @data = data
8
+ end
9
+ end
10
+
11
+ module_function
12
+
13
+ @engine = nil
14
+
15
+ # Get the current engine class.
16
+ def engine
17
+ return @engine if @engine
18
+ self.engine = self.default_engine
19
+ @engine
20
+ end
21
+
22
+ REQUIREMENT_MAP = [
23
+ ["yajl", :yajl],
24
+ ["json", :json_gem],
25
+ ["json/pure", :json_pure]
26
+ ]
27
+
28
+ DEFAULT_ENGINE_WARNING = 'Warning: multi_json is using default ok_json engine. Suggested action: require and load an appropriate JSON library.'
29
+
30
+ # The default engine based on what you currently
31
+ # have loaded and installed. First checks to see
32
+ # if any engines are already loaded, then checks
33
+ # to see which are installed if none are loaded.
34
+ def default_engine
35
+ return :yajl if defined?(::Yajl)
36
+ return :json_gem if defined?(::JSON)
37
+
38
+ REQUIREMENT_MAP.each do |(library, engine)|
39
+ begin
40
+ require library
41
+ return engine
42
+ rescue LoadError
43
+ next
44
+ end
45
+ end
46
+
47
+ Kernel.warn DEFAULT_ENGINE_WARNING
48
+ :ok_json
49
+ end
50
+
51
+ # Set the JSON parser utilizing a symbol, string, or class.
52
+ # Supported by default are:
53
+ #
54
+ # * <tt>:json_gem</tt>
55
+ # * <tt>:json_pure</tt>
56
+ # * <tt>:ok_json</tt>
57
+ # * <tt>:yajl</tt>
58
+ def engine=(new_engine)
59
+ case new_engine
60
+ when String, Symbol
61
+ require File.join(File.dirname(__FILE__),"multi_json/engines/#{new_engine}")
62
+ @engine = MultiJson::Engines.const_get("#{new_engine.to_s.split('_').map{|s| s.capitalize}.join('')}")
63
+ when Class
64
+ @engine = new_engine
65
+ else
66
+ raise "Did not recognize your engine specification. Please specify either a symbol or a class."
67
+ end
68
+ end
69
+
70
+ # Decode a JSON string into Ruby.
71
+ #
72
+ # <b>Options</b>
73
+ #
74
+ # <tt>:symbolize_keys</tt> :: If true, will use symbols instead of strings for the keys.
75
+ def decode(string, options = {})
76
+ engine.decode(string, options)
77
+ rescue engine::ParseError => exception
78
+ raise DecodeError.new(exception.message, exception.backtrace, string)
79
+ end
80
+
81
+ # Encodes a Ruby object as JSON.
82
+ def encode(object, options = {})
83
+ engine.encode(object, options)
84
+ end
85
+ end
@@ -0,0 +1,27 @@
1
+ module MultiJson
2
+ module Engines
3
+ module JsonCommon
4
+
5
+ def decode(string, options = {})
6
+ opts = {}
7
+ opts[:symbolize_names] = options[:symbolize_keys]
8
+ string = string.read if string.respond_to?(:read)
9
+ ::JSON.parse(string, opts)
10
+ end
11
+
12
+ def encode(object, options = {})
13
+ object.to_json(process_options(options))
14
+ end
15
+
16
+ protected
17
+
18
+ def process_options(options={})
19
+ return options if options.empty?
20
+ opts = {}
21
+ opts.merge!(JSON::PRETTY_STATE_PROTOTYPE.to_h) if options.delete(:pretty)
22
+ opts.merge! options
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,12 @@
1
+ require 'json' unless defined?(::JSON)
2
+ require File.join(File.dirname(__FILE__),'../engines/json_common')
3
+
4
+ module MultiJson
5
+ module Engines
6
+ # Use the JSON gem to encode/decode.
7
+ class JsonGem
8
+ ParseError = ::JSON::ParserError
9
+ extend JsonCommon
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'json/pure' unless defined?(::JSON)
2
+ require File.join(File.dirname(__FILE__),'../engines/json_common')
3
+
4
+ module MultiJson
5
+ module Engines
6
+ # Use JSON pure to encode/decode.
7
+ class JsonPure
8
+ ParseError = ::JSON::ParserError
9
+ extend JsonCommon
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,48 @@
1
+ require File.join(File.dirname(__FILE__), "../vendor/ok_json")
2
+
3
+ module MultiJson
4
+ module Engines
5
+ class OkJson
6
+ ParseError = ::MultiJson::OkJson::Error
7
+
8
+ def self.decode(string, options = {}) #:nodoc:
9
+ string = string.read if string.respond_to?(:read)
10
+ result = ::MultiJson::OkJson.decode(string)
11
+ options[:symbolize_keys] ? symbolize_keys(result) : result
12
+ end
13
+
14
+ def self.encode(object, options = {}) #:nodoc:
15
+ ::MultiJson::OkJson.valenc(stringify_keys(object))
16
+ end
17
+
18
+ def self.symbolize_keys(object) #:nodoc:
19
+ modify_keys(object) do |key|
20
+ key.is_a?(String) ? key.to_sym : key
21
+ end
22
+ end
23
+
24
+ def self.stringify_keys(object) #:nodoc:
25
+ modify_keys(object) do |key|
26
+ key.is_a?(Symbol) ? key.to_s : key
27
+ end
28
+ end
29
+
30
+ def self.modify_keys(object, &modifier) #:nodoc:
31
+ case object
32
+ when Array
33
+ object.map do |value|
34
+ modify_keys(value, &modifier)
35
+ end
36
+ when Hash
37
+ object.inject({}) do |result, (key, value)|
38
+ new_key = modifier.call(key)
39
+ new_value = modify_keys(value, &modifier)
40
+ result.merge! new_key => new_value
41
+ end
42
+ else
43
+ object
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,18 @@
1
+ require 'yajl' unless defined?(Yajl)
2
+
3
+ module MultiJson
4
+ module Engines
5
+ # Use the Yajl-Ruby library to encode/decode.
6
+ class Yajl
7
+ ParseError = ::Yajl::ParseError
8
+
9
+ def self.decode(string, options = {}) #:nodoc:
10
+ ::Yajl::Parser.new(:symbolize_keys => options[:symbolize_keys]).parse(string)
11
+ end
12
+
13
+ def self.encode(object, options = {}) #:nodoc:
14
+ ::Yajl::Encoder.encode(object, options)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,587 @@
1
+ # Copyright 2011 Keith Rarick
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ # See https://github.com/kr/okjson for updates.
22
+
23
+ require 'stringio'
24
+
25
+ # Some parts adapted from
26
+ # http://golang.org/src/pkg/json/decode.go and
27
+ # http://golang.org/src/pkg/utf8/utf8.go
28
+ module MultiJson
29
+ module OkJson
30
+ extend self
31
+
32
+
33
+ # Decodes a json document in string s and
34
+ # returns the corresponding ruby value.
35
+ # String s must be valid UTF-8. If you have
36
+ # a string in some other encoding, convert
37
+ # it first.
38
+ #
39
+ # String values in the resulting structure
40
+ # will be UTF-8.
41
+ def decode(s)
42
+ ts = lex(s)
43
+ v, ts = textparse(ts)
44
+ if ts.length > 0
45
+ raise Error, 'trailing garbage'
46
+ end
47
+ v
48
+ end
49
+
50
+
51
+ # Parses a "json text" in the sense of RFC 4627.
52
+ # Returns the parsed value and any trailing tokens.
53
+ # Note: this is almost the same as valparse,
54
+ # except that it does not accept atomic values.
55
+ def textparse(ts)
56
+ if ts.length < 0
57
+ raise Error, 'empty'
58
+ end
59
+
60
+ typ, _, val = ts[0]
61
+ case typ
62
+ when '{' then objparse(ts)
63
+ when '[' then arrparse(ts)
64
+ else
65
+ raise Error, "unexpected #{val.inspect}"
66
+ end
67
+ end
68
+
69
+
70
+ # Parses a "value" in the sense of RFC 4627.
71
+ # Returns the parsed value and any trailing tokens.
72
+ def valparse(ts)
73
+ if ts.length < 0
74
+ raise Error, 'empty'
75
+ end
76
+
77
+ typ, _, val = ts[0]
78
+ case typ
79
+ when '{' then objparse(ts)
80
+ when '[' then arrparse(ts)
81
+ when :val,:str then [val, ts[1..-1]]
82
+ else
83
+ raise Error, "unexpected #{val.inspect}"
84
+ end
85
+ end
86
+
87
+
88
+ # Parses an "object" in the sense of RFC 4627.
89
+ # Returns the parsed value and any trailing tokens.
90
+ def objparse(ts)
91
+ ts = eat('{', ts)
92
+ obj = {}
93
+
94
+ if ts[0][0] == '}'
95
+ return obj, ts[1..-1]
96
+ end
97
+
98
+ k, v, ts = pairparse(ts)
99
+ obj[k] = v
100
+
101
+ if ts[0][0] == '}'
102
+ return obj, ts[1..-1]
103
+ end
104
+
105
+ loop do
106
+ ts = eat(',', ts)
107
+
108
+ k, v, ts = pairparse(ts)
109
+ obj[k] = v
110
+
111
+ if ts[0][0] == '}'
112
+ return obj, ts[1..-1]
113
+ end
114
+ end
115
+ end
116
+
117
+
118
+ # Parses a "member" in the sense of RFC 4627.
119
+ # Returns the parsed values and any trailing tokens.
120
+ def pairparse(ts)
121
+ (typ, _, k), ts = ts[0], ts[1..-1]
122
+ if typ != :str
123
+ raise Error, "unexpected #{k.inspect}"
124
+ end
125
+ ts = eat(':', ts)
126
+ v, ts = valparse(ts)
127
+ [k, v, ts]
128
+ end
129
+
130
+
131
+ # Parses an "array" in the sense of RFC 4627.
132
+ # Returns the parsed value and any trailing tokens.
133
+ def arrparse(ts)
134
+ ts = eat('[', ts)
135
+ arr = []
136
+
137
+ if ts[0][0] == ']'
138
+ return arr, ts[1..-1]
139
+ end
140
+
141
+ v, ts = valparse(ts)
142
+ arr << v
143
+
144
+ if ts[0][0] == ']'
145
+ return arr, ts[1..-1]
146
+ end
147
+
148
+ loop do
149
+ ts = eat(',', ts)
150
+
151
+ v, ts = valparse(ts)
152
+ arr << v
153
+
154
+ if ts[0][0] == ']'
155
+ return arr, ts[1..-1]
156
+ end
157
+ end
158
+ end
159
+
160
+
161
+ def eat(typ, ts)
162
+ if ts[0][0] != typ
163
+ raise Error, "expected #{typ} (got #{ts[0].inspect})"
164
+ end
165
+ ts[1..-1]
166
+ end
167
+
168
+
169
+ # Sans s and returns a list of json tokens,
170
+ # excluding white space (as defined in RFC 4627).
171
+ def lex(s)
172
+ ts = []
173
+ while s.length > 0
174
+ typ, lexeme, val = tok(s)
175
+ if typ == nil
176
+ raise Error, "invalid character at #{s[0,10].inspect}"
177
+ end
178
+ if typ != :space
179
+ ts << [typ, lexeme, val]
180
+ end
181
+ s = s[lexeme.length..-1]
182
+ end
183
+ ts
184
+ end
185
+
186
+
187
+ # Scans the first token in s and
188
+ # returns a 3-element list, or nil
189
+ # if no such token exists.
190
+ #
191
+ # The first list element is one of
192
+ # '{', '}', ':', ',', '[', ']',
193
+ # :val, :str, and :space.
194
+ #
195
+ # The second element is the lexeme.
196
+ #
197
+ # The third element is the value of the
198
+ # token for :val and :str, otherwise
199
+ # it is the lexeme.
200
+ def tok(s)
201
+ case s[0]
202
+ when ?{ then ['{', s[0,1], s[0,1]]
203
+ when ?} then ['}', s[0,1], s[0,1]]
204
+ when ?: then [':', s[0,1], s[0,1]]
205
+ when ?, then [',', s[0,1], s[0,1]]
206
+ when ?[ then ['[', s[0,1], s[0,1]]
207
+ when ?] then [']', s[0,1], s[0,1]]
208
+ when ?n then nulltok(s)
209
+ when ?t then truetok(s)
210
+ when ?f then falsetok(s)
211
+ when ?" then strtok(s)
212
+ when Spc then [:space, s[0,1], s[0,1]]
213
+ when ?\t then [:space, s[0,1], s[0,1]]
214
+ when ?\n then [:space, s[0,1], s[0,1]]
215
+ when ?\r then [:space, s[0,1], s[0,1]]
216
+ else numtok(s)
217
+ end
218
+ end
219
+
220
+
221
+ def nulltok(s); s[0,4] == 'null' && [:val, 'null', nil] end
222
+ def truetok(s); s[0,4] == 'true' && [:val, 'true', true] end
223
+ def falsetok(s); s[0,5] == 'false' && [:val, 'false', false] end
224
+
225
+
226
+ def numtok(s)
227
+ m = /-?([1-9][0-9]+|[0-9])([.][0-9]+)?([eE][+-]?[0-9]+)?/.match(s)
228
+ if m && m.begin(0) == 0
229
+ if m[3] && !m[2]
230
+ [:val, m[0], Integer(m[1])*(10**Integer(m[3][1..-1]))]
231
+ elsif m[2]
232
+ [:val, m[0], Float(m[0])]
233
+ else
234
+ [:val, m[0], Integer(m[0])]
235
+ end
236
+ end
237
+ end
238
+
239
+
240
+ def strtok(s)
241
+ m = /"([^"\\]|\\["\/\\bfnrt]|\\u[0-9a-fA-F]{4})*"/.match(s)
242
+ if ! m
243
+ raise Error, "invalid string literal at #{abbrev(s)}"
244
+ end
245
+ [:str, m[0], unquote(m[0])]
246
+ end
247
+
248
+
249
+ def abbrev(s)
250
+ t = s[0,10]
251
+ p = t['`']
252
+ t = t[0,p] if p
253
+ t = t + '...' if t.length < s.length
254
+ '`' + t + '`'
255
+ end
256
+
257
+
258
+ # Converts a quoted json string literal q into a UTF-8-encoded string.
259
+ # The rules are different than for Ruby, so we cannot use eval.
260
+ # Unquote will raise an error if q contains control characters.
261
+ def unquote(q)
262
+ q = q[1...-1]
263
+ a = q.dup # allocate a big enough string
264
+ r, w = 0, 0
265
+ while r < q.length
266
+ c = q[r]
267
+ case true
268
+ when c == ?\\
269
+ r += 1
270
+ if r >= q.length
271
+ raise Error, "string literal ends with a \"\\\": \"#{q}\""
272
+ end
273
+
274
+ case q[r]
275
+ when ?",?\\,?/,?'
276
+ a[w] = q[r]
277
+ r += 1
278
+ w += 1
279
+ when ?b,?f,?n,?r,?t
280
+ a[w] = Unesc[q[r]]
281
+ r += 1
282
+ w += 1
283
+ when ?u
284
+ r += 1
285
+ uchar = begin
286
+ hexdec4(q[r,4])
287
+ rescue RuntimeError => e
288
+ raise Error, "invalid escape sequence \\u#{q[r,4]}: #{e}"
289
+ end
290
+ r += 4
291
+ if surrogate? uchar
292
+ if q.length >= r+6
293
+ uchar1 = hexdec4(q[r+2,4])
294
+ uchar = subst(uchar, uchar1)
295
+ if uchar != Ucharerr
296
+ # A valid pair; consume.
297
+ r += 6
298
+ end
299
+ end
300
+ end
301
+ w += ucharenc(a, w, uchar)
302
+ else
303
+ raise Error, "invalid escape char #{q[r]} in \"#{q}\""
304
+ end
305
+ when c == ?", c < Spc
306
+ raise Error, "invalid character in string literal \"#{q}\""
307
+ else
308
+ # Copy anything else byte-for-byte.
309
+ # Valid UTF-8 will remain valid UTF-8.
310
+ # Invalid UTF-8 will remain invalid UTF-8.
311
+ a[w] = c
312
+ r += 1
313
+ w += 1
314
+ end
315
+ end
316
+ a[0,w]
317
+ end
318
+
319
+
320
+ # Encodes unicode character u as UTF-8
321
+ # bytes in string a at position i.
322
+ # Returns the number of bytes written.
323
+ def ucharenc(a, i, u)
324
+ case true
325
+ when u <= Uchar1max
326
+ a[i] = (u & 0xff).chr
327
+ 1
328
+ when u <= Uchar2max
329
+ a[i+0] = (Utag2 | ((u>>6)&0xff)).chr
330
+ a[i+1] = (Utagx | (u&Umaskx)).chr
331
+ 2
332
+ when u <= Uchar3max
333
+ a[i+0] = (Utag3 | ((u>>12)&0xff)).chr
334
+ a[i+1] = (Utagx | ((u>>6)&Umaskx)).chr
335
+ a[i+2] = (Utagx | (u&Umaskx)).chr
336
+ 3
337
+ else
338
+ a[i+0] = (Utag4 | ((u>>18)&0xff)).chr
339
+ a[i+1] = (Utagx | ((u>>12)&Umaskx)).chr
340
+ a[i+2] = (Utagx | ((u>>6)&Umaskx)).chr
341
+ a[i+3] = (Utagx | (u&Umaskx)).chr
342
+ 4
343
+ end
344
+ end
345
+
346
+
347
+ def hexdec4(s)
348
+ if s.length != 4
349
+ raise Error, 'short'
350
+ end
351
+ (nibble(s[0])<<12) | (nibble(s[1])<<8) | (nibble(s[2])<<4) | nibble(s[3])
352
+ end
353
+
354
+
355
+ def subst(u1, u2)
356
+ if Usurr1 <= u1 && u1 < Usurr2 && Usurr2 <= u2 && u2 < Usurr3
357
+ return ((u1-Usurr1)<<10) | (u2-Usurr2) + Usurrself
358
+ end
359
+ return Ucharerr
360
+ end
361
+
362
+
363
+ def unsubst(u)
364
+ if u < Usurrself || u > Umax || surrogate?(u)
365
+ return Ucharerr, Ucharerr
366
+ end
367
+ u -= Usurrself
368
+ [Usurr1 + ((u>>10)&0x3ff), Usurr2 + (u&0x3ff)]
369
+ end
370
+
371
+
372
+ def surrogate?(u)
373
+ Usurr1 <= u && u < Usurr3
374
+ end
375
+
376
+
377
+ def nibble(c)
378
+ case true
379
+ when ?0 <= c && c <= ?9 then c.ord - ?0.ord
380
+ when ?a <= c && c <= ?z then c.ord - ?a.ord + 10
381
+ when ?A <= c && c <= ?Z then c.ord - ?A.ord + 10
382
+ else
383
+ raise Error, "invalid hex code #{c}"
384
+ end
385
+ end
386
+
387
+
388
+ # Encodes x into a json text. It may contain only
389
+ # Array, Hash, String, Numeric, true, false, nil.
390
+ # (Note, this list excludes Symbol.)
391
+ # X itself must be an Array or a Hash.
392
+ # No other value can be encoded, and an error will
393
+ # be raised if x contains any other value, such as
394
+ # Nan, Infinity, Symbol, and Proc, or if a Hash key
395
+ # is not a String.
396
+ # Strings contained in x must be valid UTF-8.
397
+ def encode(x)
398
+ case x
399
+ when Hash then objenc(x)
400
+ when Array then arrenc(x)
401
+ else
402
+ raise Error, 'root value must be an Array or a Hash'
403
+ end
404
+ end
405
+
406
+
407
+ def valenc(x)
408
+ case x
409
+ when Hash then objenc(x)
410
+ when Array then arrenc(x)
411
+ when String then strenc(x)
412
+ when Numeric then numenc(x)
413
+ when true then "true"
414
+ when false then "false"
415
+ when nil then "null"
416
+ else
417
+ if x.respond_to?(:to_json)
418
+ x.to_json
419
+ else
420
+ raise Error, "cannot encode #{x.class}: #{x.inspect}"
421
+ end
422
+ end
423
+ end
424
+
425
+
426
+ def objenc(x)
427
+ '{' + x.map{|k,v| keyenc(k) + ':' + valenc(v)}.join(',') + '}'
428
+ end
429
+
430
+
431
+ def arrenc(a)
432
+ '[' + a.map{|x| valenc(x)}.join(',') + ']'
433
+ end
434
+
435
+
436
+ def keyenc(k)
437
+ case k
438
+ when String then strenc(k)
439
+ else
440
+ raise Error, "Hash key is not a string: #{k.inspect}"
441
+ end
442
+ end
443
+
444
+
445
+ def strenc(s)
446
+ t = StringIO.new
447
+ t.putc(?")
448
+ r = 0
449
+ while r < s.length
450
+ case s[r]
451
+ when ?" then t.print('\\"')
452
+ when ?\\ then t.print('\\\\')
453
+ when ?\b then t.print('\\b')
454
+ when ?\f then t.print('\\f')
455
+ when ?\n then t.print('\\n')
456
+ when ?\r then t.print('\\r')
457
+ when ?\t then t.print('\\t')
458
+ else
459
+ c = s[r]
460
+ case true
461
+ when Spc <= c && c <= ?~
462
+ t.putc(c)
463
+ when true
464
+ u, size = uchardec(s, r)
465
+ r += size - 1 # we add one more at the bottom of the loop
466
+ if u < 0x10000
467
+ t.print('\\u')
468
+ hexenc4(t, u)
469
+ else
470
+ u1, u2 = unsubst(u)
471
+ t.print('\\u')
472
+ hexenc4(t, u1)
473
+ t.print('\\u')
474
+ hexenc4(t, u2)
475
+ end
476
+ else
477
+ # invalid byte; skip it
478
+ end
479
+ end
480
+ r += 1
481
+ end
482
+ t.putc(?")
483
+ t.string
484
+ end
485
+
486
+
487
+ def hexenc4(t, u)
488
+ t.putc(Hex[(u>>12)&0xf])
489
+ t.putc(Hex[(u>>8)&0xf])
490
+ t.putc(Hex[(u>>4)&0xf])
491
+ t.putc(Hex[u&0xf])
492
+ end
493
+
494
+
495
+ def numenc(x)
496
+ if x.nan? || x.infinite?
497
+ return 'null'
498
+ end rescue nil
499
+ "#{x}"
500
+ end
501
+
502
+
503
+ # Decodes unicode character u from UTF-8
504
+ # bytes in string s at position i.
505
+ # Returns u and the number of bytes read.
506
+ def uchardec(s, i)
507
+ n = s.length - i
508
+ return [Ucharerr, 1] if n < 1
509
+
510
+ c0 = s[i].ord
511
+
512
+ # 1-byte, 7-bit sequence?
513
+ if c0 < Utagx
514
+ return [c0, 1]
515
+ end
516
+
517
+ # unexpected continuation byte?
518
+ return [Ucharerr, 1] if c0 < Utag2
519
+
520
+ # need continuation byte
521
+ return [Ucharerr, 1] if n < 2
522
+ c1 = s[i+1].ord
523
+ return [Ucharerr, 1] if c1 < Utagx || Utag2 <= c1
524
+
525
+ # 2-byte, 11-bit sequence?
526
+ if c0 < Utag3
527
+ u = (c0&Umask2)<<6 | (c1&Umaskx)
528
+ return [Ucharerr, 1] if u <= Uchar1max
529
+ return [u, 2]
530
+ end
531
+
532
+ # need second continuation byte
533
+ return [Ucharerr, 1] if n < 3
534
+ c2 = s[i+2].ord
535
+ return [Ucharerr, 1] if c2 < Utagx || Utag2 <= c2
536
+
537
+ # 3-byte, 16-bit sequence?
538
+ if c0 < Utag4
539
+ u = (c0&Umask3)<<12 | (c1&Umaskx)<<6 | (c2&Umaskx)
540
+ return [Ucharerr, 1] if u <= Uchar2max
541
+ return [u, 3]
542
+ end
543
+
544
+ # need third continuation byte
545
+ return [Ucharerr, 1] if n < 4
546
+ c3 = s[i+3].ord
547
+ return [Ucharerr, 1] if c3 < Utagx || Utag2 <= c3
548
+
549
+ # 4-byte, 21-bit sequence?
550
+ if c0 < Utag5
551
+ u = (c0&Umask4)<<18 | (c1&Umaskx)<<12 | (c2&Umaskx)<<6 | (c3&Umaskx)
552
+ return [Ucharerr, 1] if u <= Uchar3max
553
+ return [u, 4]
554
+ end
555
+
556
+ return [Ucharerr, 1]
557
+ end
558
+
559
+
560
+ class Error < ::StandardError
561
+ end
562
+
563
+
564
+ Utagx = 0x80 # 1000 0000
565
+ Utag2 = 0xc0 # 1100 0000
566
+ Utag3 = 0xe0 # 1110 0000
567
+ Utag4 = 0xf0 # 1111 0000
568
+ Utag5 = 0xF8 # 1111 1000
569
+ Umaskx = 0x3f # 0011 1111
570
+ Umask2 = 0x1f # 0001 1111
571
+ Umask3 = 0x0f # 0000 1111
572
+ Umask4 = 0x07 # 0000 0111
573
+ Uchar1max = (1<<7) - 1
574
+ Uchar2max = (1<<11) - 1
575
+ Uchar3max = (1<<16) - 1
576
+ Ucharerr = 0xFFFD # unicode "replacement char"
577
+ Usurrself = 0x10000
578
+ Usurr1 = 0xd800
579
+ Usurr2 = 0xdc00
580
+ Usurr3 = 0xe000
581
+ Umax = 0x10ffff
582
+
583
+ Spc = ' '[0]
584
+ Unesc = {?b=>?\b, ?f=>?\f, ?n=>?\n, ?r=>?\r, ?t=>?\t}
585
+ Hex = '0123456789abcdef'
586
+ end
587
+ end
@@ -0,0 +1,3 @@
1
+ module MultiJson
2
+ VERSION = "1.0.4"
3
+ end
@@ -88,27 +88,34 @@ module JSON
88
88
  }
89
89
  @@validators = {}
90
90
  @@default_validator = nil
91
- @@available_json_backends = []
92
- @@json_backend = nil
93
91
  @@errors = []
94
92
 
93
+ def self.version_string_for(version)
94
+ # I'm not a fan of this, but it's quick and dirty to get it working for now
95
+ return "draft-03" unless version
96
+ case version.to_s
97
+ when "draft3"
98
+ "draft-03"
99
+ when "draft2"
100
+ "draft-02"
101
+ when "draft1"
102
+ "draft-01"
103
+ else
104
+ raise JSON::Schema::SchemaError.new("The requested JSON schema version is not supported")
105
+ end
106
+ end
107
+
108
+ def self.metaschema_for(version_string)
109
+ File.join(Pathname.new(File.dirname(__FILE__)).parent.parent, "resources", "#{version_string}.json").to_s
110
+ end
111
+
95
112
  def initialize(schema_data, data, opts={})
96
113
  @options = @@default_opts.clone.merge(opts)
97
114
 
98
115
  # I'm not a fan of this, but it's quick and dirty to get it working for now
99
116
  version_string = "draft-03"
100
117
  if @options[:version]
101
- @options[:version] = case @options[:version].to_s
102
- when "draft3"
103
- "draft-03"
104
- when "draft2"
105
- "draft-02"
106
- when "draft1"
107
- "draft-01"
108
- else
109
- raise JSON::Schema::SchemaError.new("The requested JSON schema version is not supported")
110
- end
111
- version_string = @options[:version]
118
+ version_string = @options[:version] = self.class.version_string_for(@options[:version])
112
119
  u = URI.parse("http://json-schema.org/#{@options[:version]}/schema#")
113
120
  validator = JSON::Validator.validators["#{u.scheme}://#{u.host}#{u.path}"]
114
121
  @options[:version] = validator
@@ -119,8 +126,7 @@ module JSON
119
126
  # validate the schema, if requested
120
127
  if @options[:validate_schema]
121
128
  begin
122
- metaschema_file = File.join(Pathname.new(File.dirname(__FILE__)).parent.parent, "resources", "#{version_string}.json").to_s
123
- meta_validator = JSON::Validator.new(metaschema_file, schema_data)
129
+ meta_validator = JSON::Validator.new(self.class.metaschema_for(version_string), schema_data)
124
130
  meta_validator.validate
125
131
  rescue JSON::Schema::ValidationError, JSON::Schema::SchemaError
126
132
  raise $!
@@ -267,6 +273,12 @@ module JSON
267
273
  validator.validate
268
274
  end
269
275
 
276
+ def fully_validate_schema(schema, opts={})
277
+ data = schema
278
+ schema = metaschema_for(version_string_for(opts[:version]))
279
+ fully_validate(schema, data, opts)
280
+ end
281
+
270
282
 
271
283
  def clear_cache
272
284
  @@schemas = {} if @@cache_schemas == false
@@ -309,59 +321,19 @@ module JSON
309
321
  end
310
322
 
311
323
  def json_backend
312
- @@json_backend
324
+ MultiJson.engine
313
325
  end
314
-
326
+
315
327
  def json_backend=(backend)
316
- backend = backend.to_s
317
- if @@available_json_backends.include?(backend)
318
- @@json_backend = backend
319
- else
320
- raise JSON::Schema::JsonParseError.new("The JSON backend '#{backend}' could not be found.")
321
- end
328
+ backend = 'json_gem' if backend.to_s == 'json'
329
+ MultiJson.engine = backend
322
330
  end
323
331
 
324
332
  def parse(s)
325
- case @@json_backend.to_s
326
- when 'json'
327
- JSON.parse(s)
328
- when 'yajl'
329
- json = StringIO.new(s)
330
- parser = Yajl::Parser.new
331
- parser.parse(json)
332
- else
333
- raise JSON::Schema::JsonParseError.new("No supported JSON parsers found. The following parsers are suported:\n * yajl-ruby\n * json")
334
- end
335
- end
336
- end
337
-
338
-
339
- if begin
340
- Gem::Specification::find_by_name('json')
341
- rescue Gem::LoadError
342
- false
343
- rescue
344
- Gem.available?('json')
333
+ MultiJson.decode(s)
345
334
  end
346
- require 'json'
347
- @@available_json_backends << 'json'
348
- @@json_backend = 'json'
349
335
  end
350
336
 
351
-
352
- if begin
353
- Gem::Specification::find_by_name('yajl-ruby')
354
- rescue Gem::LoadError
355
- false
356
- rescue
357
- Gem.available?('yajl-ruby')
358
- end
359
- require 'yajl'
360
- @@available_json_backends << 'yajl'
361
- @@json_backend = 'yajl'
362
- end
363
-
364
-
365
337
  private
366
338
 
367
339
  if begin
@@ -378,17 +350,10 @@ module JSON
378
350
  @@fake_uri_generator = lambda{|s| JSON::Util::UUID.create_v5(s,JSON::Util::UUID::Nil).to_s }
379
351
  end
380
352
 
381
- if @@json_backend == 'yajl'
382
- @@serializer = lambda{|o| Yajl::Encoder.encode(o) }
383
- else
384
- @@serializer = lambda{|o| Marshal.dump(o) }
385
- end
386
-
387
353
  def serialize schema
388
- @@serializer.call(schema)
354
+ MultiJson.encode(schema)
389
355
  end
390
356
 
391
-
392
357
  def fake_uri schema
393
358
  @@fake_uri_generator.call(schema)
394
359
  end
data/test/test_files.rb CHANGED
@@ -8,36 +8,28 @@ class JSONSchemaTest < Test::Unit::TestCase
8
8
  #
9
9
 
10
10
  def test_schema_from_file
11
- if JSON::Validator.json_backend != nil
12
- data = {"a" => 5}
13
- assert(JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),data))
14
- data = {"a" => "bad"}
15
- assert(!JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),data))
16
- end
11
+ data = {"a" => 5}
12
+ assert(JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),data))
13
+ data = {"a" => "bad"}
14
+ assert(!JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),data))
17
15
  end
18
16
 
19
17
  def test_data_from_file
20
- if JSON::Validator.json_backend != nil
21
- schema = {"type" => "object", "properties" => {"a" => {"type" => "integer"}}}
22
- assert(JSON::Validator.validate(schema,File.join(File.dirname(__FILE__),"data/good_data_1.json")))
23
- assert(!JSON::Validator.validate(schema,File.join(File.dirname(__FILE__),"data/bad_data_1.json")))
24
- end
18
+ schema = {"type" => "object", "properties" => {"a" => {"type" => "integer"}}}
19
+ assert(JSON::Validator.validate(schema,File.join(File.dirname(__FILE__),"data/good_data_1.json")))
20
+ assert(!JSON::Validator.validate(schema,File.join(File.dirname(__FILE__),"data/bad_data_1.json")))
25
21
  end
26
22
 
27
23
  def test_both_from_file
28
- if JSON::Validator.json_backend != nil
29
- assert(JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),File.join(File.dirname(__FILE__),"data/good_data_1.json")))
30
- assert(!JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),File.join(File.dirname(__FILE__),"data/bad_data_1.json")))
31
- end
24
+ assert(JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),File.join(File.dirname(__FILE__),"data/good_data_1.json")))
25
+ assert(!JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),File.join(File.dirname(__FILE__),"data/bad_data_1.json")))
32
26
  end
33
27
 
34
28
  def test_file_ref
35
- if JSON::Validator.json_backend != nil
36
- data = {"b" => {"a" => 5}}
37
- assert(JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_2.json"),data))
38
-
39
- data = {"b" => {"a" => "boo"}}
40
- assert(!JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),data))
41
- end
29
+ data = {"b" => {"a" => 5}}
30
+ assert(JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_2.json"),data))
31
+
32
+ data = {"b" => {"a" => "boo"}}
33
+ assert(!JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),data))
42
34
  end
43
- end
35
+ end
@@ -4,37 +4,35 @@ require File.dirname(__FILE__) + '/../lib/json-schema'
4
4
  class JSONFullValidation < Test::Unit::TestCase
5
5
 
6
6
  def test_full_validation
7
- if JSON::Validator.json_backend != nil
8
- data = {"b" => {"a" => 5}}
9
- schema = {
10
- "$schema" => "http://json-schema.org/draft-03/schema#",
11
- "type" => "object",
12
- "properties" => {
13
- "b" => {
14
- "required" => true
15
- }
7
+ data = {"b" => {"a" => 5}}
8
+ schema = {
9
+ "$schema" => "http://json-schema.org/draft-03/schema#",
10
+ "type" => "object",
11
+ "properties" => {
12
+ "b" => {
13
+ "required" => true
16
14
  }
17
15
  }
18
-
19
- errors = JSON::Validator.fully_validate(schema,data)
20
- assert(errors.empty?)
16
+ }
17
+
18
+ errors = JSON::Validator.fully_validate(schema,data)
19
+ assert(errors.empty?)
21
20
 
22
- data = {"c" => 5}
23
- schema = {
24
- "$schema" => "http://json-schema.org/draft-03/schema#",
25
- "type" => "object",
26
- "properties" => {
27
- "b" => {
28
- "required" => true
29
- },
30
- "c" => {
31
- "type" => "string"
32
- }
21
+ data = {"c" => 5}
22
+ schema = {
23
+ "$schema" => "http://json-schema.org/draft-03/schema#",
24
+ "type" => "object",
25
+ "properties" => {
26
+ "b" => {
27
+ "required" => true
28
+ },
29
+ "c" => {
30
+ "type" => "string"
33
31
  }
34
32
  }
33
+ }
35
34
 
36
- errors = JSON::Validator.fully_validate(schema,data)
37
- assert(errors.length == 2)
38
- end
35
+ errors = JSON::Validator.fully_validate(schema,data)
36
+ assert(errors.length == 2)
39
37
  end
40
- end
38
+ end
@@ -2,32 +2,42 @@ require 'test/unit'
2
2
  require File.dirname(__FILE__) + '/../lib/json-schema'
3
3
 
4
4
  class JSONSchemaValidation < Test::Unit::TestCase
5
-
6
- def test_draft03_validation
7
- if JSON::Validator.json_backend != nil
8
- data = {"b" => {"a" => 5}}
9
- schema = {
10
- "$schema" => "http://json-schema.org/draft-03/schema#",
11
- "type" => "object",
12
- "properties" => {
13
- "b" => {
14
- "required" => true
15
- }
5
+ def valid_schema
6
+ {
7
+ "$schema" => "http://json-schema.org/draft-03/schema#",
8
+ "type" => "object",
9
+ "properties" => {
10
+ "b" => {
11
+ "required" => true
16
12
  }
17
13
  }
18
-
19
- assert(JSON::Validator.validate(schema,data,:validate_schema => true))
20
-
21
- schema = {
22
- "$schema" => "http://json-schema.org/draft-03/schema#",
23
- "type" => "object",
24
- "properties" => {
25
- "b" => {
26
- "required" => "true"
27
- }
14
+ }
15
+ end
16
+
17
+ def invalid_schema
18
+ {
19
+ "$schema" => "http://json-schema.org/draft-03/schema#",
20
+ "type" => "object",
21
+ "properties" => {
22
+ "b" => {
23
+ "required" => "true"
28
24
  }
29
- }
30
- assert(!JSON::Validator.validate(schema,data,:validate_schema => true))
31
- end
25
+ }
26
+ }
27
+ end
28
+
29
+ def test_draft03_validation
30
+ data = {"b" => {"a" => 5}}
31
+ assert(JSON::Validator.validate(valid_schema,data,:validate_schema => true))
32
+ assert(!JSON::Validator.validate(invalid_schema,data,:validate_schema => true))
33
+ end
34
+
35
+ def test_validate_just_schema
36
+ errors = JSON::Validator.fully_validate_schema(valid_schema)
37
+ assert_equal [], errors
38
+
39
+ errors = JSON::Validator.fully_validate_schema(invalid_schema)
40
+ assert_equal 1, errors.size
41
+ assert_match /the property .*required.*did not match/i, errors.first
32
42
  end
33
- end
43
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 1
7
7
  - 0
8
- - 1
9
- version: 1.0.1
8
+ - 2
9
+ version: 1.0.2
10
10
  platform: ruby
11
11
  authors:
12
12
  - Kenny Hoxworth
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2012-01-25 00:00:00 -05:00
17
+ date: 2012-02-13 00:00:00 -05:00
18
18
  default_executable:
19
19
  dependencies: []
20
20
 
@@ -26,6 +26,8 @@ extensions: []
26
26
 
27
27
  extra_rdoc_files:
28
28
  - README.textile
29
+ - LICENSE.md
30
+ - LICENSE-multi_json.md
29
31
  files:
30
32
  - lib/json-schema/attributes/additionalitems.rb
31
33
  - lib/json-schema/attributes/additionalproperties.rb
@@ -52,6 +54,14 @@ files:
52
54
  - lib/json-schema/attributes/ref.rb
53
55
  - lib/json-schema/attributes/type.rb
54
56
  - lib/json-schema/attributes/uniqueitems.rb
57
+ - lib/json-schema/lib/multi_json/multi_json/engines/json_common.rb
58
+ - lib/json-schema/lib/multi_json/multi_json/engines/json_gem.rb
59
+ - lib/json-schema/lib/multi_json/multi_json/engines/json_pure.rb
60
+ - lib/json-schema/lib/multi_json/multi_json/engines/ok_json.rb
61
+ - lib/json-schema/lib/multi_json/multi_json/engines/yajl.rb
62
+ - lib/json-schema/lib/multi_json/multi_json/vendor/ok_json.rb
63
+ - lib/json-schema/lib/multi_json/multi_json/version.rb
64
+ - lib/json-schema/lib/multi_json/multi_json.rb
55
65
  - lib/json-schema/schema.rb
56
66
  - lib/json-schema/uri/file.rb
57
67
  - lib/json-schema/uri/uuid.rb
@@ -64,6 +74,8 @@ files:
64
74
  - resources/draft-02.json
65
75
  - resources/draft-03.json
66
76
  - README.textile
77
+ - LICENSE.md
78
+ - LICENSE-multi_json.md
67
79
  has_rdoc: true
68
80
  homepage: http://github.com/hoxworth/json-schema/tree/master
69
81
  licenses: []