taps 0.3.18 → 0.3.19.pre1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ require 'json/common'
2
+ require 'json/pure/parser'
3
+ require 'json/pure/generator'
4
+
5
+ module JSON
6
+ # This module holds all the modules/classes that implement JSON's
7
+ # functionality in pure ruby.
8
+ module Pure
9
+ $DEBUG and warn "Using Pure library for JSON."
10
+ JSON.parser = Parser
11
+ JSON.generator = Generator
12
+ end
13
+
14
+ JSON_LOADED = true unless defined?(::JSON::JSON_LOADED)
15
+ end
@@ -0,0 +1,441 @@
1
+ module JSON
2
+ MAP = {
3
+ "\x0" => '\u0000',
4
+ "\x1" => '\u0001',
5
+ "\x2" => '\u0002',
6
+ "\x3" => '\u0003',
7
+ "\x4" => '\u0004',
8
+ "\x5" => '\u0005',
9
+ "\x6" => '\u0006',
10
+ "\x7" => '\u0007',
11
+ "\b" => '\b',
12
+ "\t" => '\t',
13
+ "\n" => '\n',
14
+ "\xb" => '\u000b',
15
+ "\f" => '\f',
16
+ "\r" => '\r',
17
+ "\xe" => '\u000e',
18
+ "\xf" => '\u000f',
19
+ "\x10" => '\u0010',
20
+ "\x11" => '\u0011',
21
+ "\x12" => '\u0012',
22
+ "\x13" => '\u0013',
23
+ "\x14" => '\u0014',
24
+ "\x15" => '\u0015',
25
+ "\x16" => '\u0016',
26
+ "\x17" => '\u0017',
27
+ "\x18" => '\u0018',
28
+ "\x19" => '\u0019',
29
+ "\x1a" => '\u001a',
30
+ "\x1b" => '\u001b',
31
+ "\x1c" => '\u001c',
32
+ "\x1d" => '\u001d',
33
+ "\x1e" => '\u001e',
34
+ "\x1f" => '\u001f',
35
+ '"' => '\"',
36
+ '\\' => '\\\\',
37
+ } # :nodoc:
38
+
39
+ # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
40
+ # UTF16 big endian characters as \u????, and return it.
41
+ if defined?(::Encoding)
42
+ def utf8_to_json(string) # :nodoc:
43
+ string = string.dup
44
+ string << '' # XXX workaround: avoid buffer sharing
45
+ string.force_encoding(::Encoding::ASCII_8BIT)
46
+ string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
47
+ string.force_encoding(::Encoding::UTF_8)
48
+ string
49
+ end
50
+
51
+ def utf8_to_json_ascii(string) # :nodoc:
52
+ string = string.dup
53
+ string << '' # XXX workaround: avoid buffer sharing
54
+ string.force_encoding(::Encoding::ASCII_8BIT)
55
+ string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
56
+ string.gsub!(/(
57
+ (?:
58
+ [\xc2-\xdf][\x80-\xbf] |
59
+ [\xe0-\xef][\x80-\xbf]{2} |
60
+ [\xf0-\xf4][\x80-\xbf]{3}
61
+ )+ |
62
+ [\x80-\xc1\xf5-\xff] # invalid
63
+ )/nx) { |c|
64
+ c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
65
+ s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
66
+ s.gsub!(/.{4}/n, '\\\\u\&')
67
+ }
68
+ string.force_encoding(::Encoding::UTF_8)
69
+ string
70
+ rescue => e
71
+ raise GeneratorError, "Caught #{e.class}: #{e}"
72
+ end
73
+ else
74
+ def utf8_to_json(string) # :nodoc:
75
+ string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
76
+ end
77
+
78
+ def utf8_to_json_ascii(string) # :nodoc:
79
+ string = string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
80
+ string.gsub!(/(
81
+ (?:
82
+ [\xc2-\xdf][\x80-\xbf] |
83
+ [\xe0-\xef][\x80-\xbf]{2} |
84
+ [\xf0-\xf4][\x80-\xbf]{3}
85
+ )+ |
86
+ [\x80-\xc1\xf5-\xff] # invalid
87
+ )/nx) { |c|
88
+ c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
89
+ s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
90
+ s.gsub!(/.{4}/n, '\\\\u\&')
91
+ }
92
+ string
93
+ rescue => e
94
+ raise GeneratorError, "Caught #{e.class}: #{e}"
95
+ end
96
+ end
97
+ module_function :utf8_to_json, :utf8_to_json_ascii
98
+
99
+ module Pure
100
+ module Generator
101
+ # This class is used to create State instances, that are use to hold data
102
+ # while generating a JSON text from a a Ruby data structure.
103
+ class State
104
+ # Creates a State object from _opts_, which ought to be Hash to create
105
+ # a new State instance configured by _opts_, something else to create
106
+ # an unconfigured instance. If _opts_ is a State object, it is just
107
+ # returned.
108
+ def self.from_state(opts)
109
+ case
110
+ when self === opts
111
+ opts
112
+ when opts.respond_to?(:to_hash)
113
+ new(opts.to_hash)
114
+ when opts.respond_to?(:to_h)
115
+ new(opts.to_h)
116
+ else
117
+ SAFE_STATE_PROTOTYPE.dup
118
+ end
119
+ end
120
+
121
+ # Instantiates a new State object, configured by _opts_.
122
+ #
123
+ # _opts_ can have the following keys:
124
+ #
125
+ # * *indent*: a string used to indent levels (default: ''),
126
+ # * *space*: a string that is put after, a : or , delimiter (default: ''),
127
+ # * *space_before*: a string that is put before a : pair delimiter (default: ''),
128
+ # * *object_nl*: a string that is put at the end of a JSON object (default: ''),
129
+ # * *array_nl*: a string that is put at the end of a JSON array (default: ''),
130
+ # * *check_circular*: is deprecated now, use the :max_nesting option instead,
131
+ # * *max_nesting*: sets the maximum level of data structure nesting in
132
+ # the generated JSON, max_nesting = 0 if no maximum should be checked.
133
+ # * *allow_nan*: true if NaN, Infinity, and -Infinity should be
134
+ # generated, otherwise an exception is thrown, if these values are
135
+ # encountered. This options defaults to false.
136
+ def initialize(opts = {})
137
+ @indent = ''
138
+ @space = ''
139
+ @space_before = ''
140
+ @object_nl = ''
141
+ @array_nl = ''
142
+ @allow_nan = false
143
+ @ascii_only = false
144
+ configure opts
145
+ end
146
+
147
+ # This string is used to indent levels in the JSON text.
148
+ attr_accessor :indent
149
+
150
+ # This string is used to insert a space between the tokens in a JSON
151
+ # string.
152
+ attr_accessor :space
153
+
154
+ # This string is used to insert a space before the ':' in JSON objects.
155
+ attr_accessor :space_before
156
+
157
+ # This string is put at the end of a line that holds a JSON object (or
158
+ # Hash).
159
+ attr_accessor :object_nl
160
+
161
+ # This string is put at the end of a line that holds a JSON array.
162
+ attr_accessor :array_nl
163
+
164
+ # This integer returns the maximum level of data structure nesting in
165
+ # the generated JSON, max_nesting = 0 if no maximum is checked.
166
+ attr_accessor :max_nesting
167
+
168
+ # This integer returns the current depth data structure nesting in the
169
+ # generated JSON.
170
+ attr_accessor :depth
171
+
172
+ def check_max_nesting # :nodoc:
173
+ return if @max_nesting.zero?
174
+ current_nesting = depth + 1
175
+ current_nesting > @max_nesting and
176
+ raise NestingError, "nesting of #{current_nesting} is too deep"
177
+ end
178
+
179
+ # Returns true, if circular data structures are checked,
180
+ # otherwise returns false.
181
+ def check_circular?
182
+ !@max_nesting.zero?
183
+ end
184
+
185
+ # Returns true if NaN, Infinity, and -Infinity should be considered as
186
+ # valid JSON and output.
187
+ def allow_nan?
188
+ @allow_nan
189
+ end
190
+
191
+ def ascii_only?
192
+ @ascii_only
193
+ end
194
+
195
+ # Configure this State instance with the Hash _opts_, and return
196
+ # itself.
197
+ def configure(opts)
198
+ @indent = opts[:indent] if opts.key?(:indent)
199
+ @space = opts[:space] if opts.key?(:space)
200
+ @space_before = opts[:space_before] if opts.key?(:space_before)
201
+ @object_nl = opts[:object_nl] if opts.key?(:object_nl)
202
+ @array_nl = opts[:array_nl] if opts.key?(:array_nl)
203
+ @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
204
+ @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
205
+ @depth = opts[:depth] || 0
206
+ if !opts.key?(:max_nesting) # defaults to 19
207
+ @max_nesting = 19
208
+ elsif opts[:max_nesting]
209
+ @max_nesting = opts[:max_nesting]
210
+ else
211
+ @max_nesting = 0
212
+ end
213
+ self
214
+ end
215
+
216
+ # Returns the configuration instance variables as a hash, that can be
217
+ # passed to the configure method.
218
+ def to_h
219
+ result = {}
220
+ for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting ascii_only depth]
221
+ result[iv.intern] = instance_variable_get("@#{iv}")
222
+ end
223
+ result
224
+ end
225
+
226
+ # Generates a valid JSON document from object +obj+ and returns the
227
+ # result. If no valid JSON document can be created this method raises a
228
+ # GeneratorError exception.
229
+ def generate(obj)
230
+ result = obj.to_json(self)
231
+ if result !~ /\A\s*(?:\[.*\]|\{.*\})\s*\Z/m
232
+ raise GeneratorError, "only generation of JSON objects or arrays allowed"
233
+ end
234
+ result
235
+ end
236
+
237
+ # Return the value returned by method +name+.
238
+ def [](name)
239
+ __send__ name
240
+ end
241
+ end
242
+
243
+ module GeneratorMethods
244
+ module Object
245
+ # Converts this object to a string (calling #to_s), converts
246
+ # it to a JSON string, and returns the result. This is a fallback, if no
247
+ # special method #to_json was defined for some object.
248
+ def to_json(*) to_s.to_json end
249
+ end
250
+
251
+ module Hash
252
+ # Returns a JSON string containing a JSON object, that is unparsed from
253
+ # this Hash instance.
254
+ # _state_ is a JSON::State object, that can also be used to configure the
255
+ # produced JSON string output further.
256
+ # _depth_ is used to find out nesting depth, to indent accordingly.
257
+ def to_json(state = nil, *)
258
+ state = State.from_state(state)
259
+ state.check_max_nesting
260
+ json_transform(state)
261
+ end
262
+
263
+ private
264
+
265
+ def json_shift(state)
266
+ state.object_nl.empty? or return ''
267
+ state.indent * state.depth
268
+ end
269
+
270
+ def json_transform(state)
271
+ delim = ','
272
+ delim << state.object_nl
273
+ result = '{'
274
+ result << state.object_nl
275
+ depth = state.depth += 1
276
+ first = true
277
+ indent = !state.object_nl.empty?
278
+ each { |key,value|
279
+ result << delim unless first
280
+ result << state.indent * depth if indent
281
+ result << key.to_s.to_json(state)
282
+ result << state.space_before
283
+ result << ':'
284
+ result << state.space
285
+ result << value.to_json(state)
286
+ first = false
287
+ }
288
+ depth = state.depth -= 1
289
+ result << state.object_nl
290
+ result << state.indent * depth if indent if indent
291
+ result << '}'
292
+ result
293
+ end
294
+ end
295
+
296
+ module Array
297
+ # Returns a JSON string containing a JSON array, that is unparsed from
298
+ # this Array instance.
299
+ # _state_ is a JSON::State object, that can also be used to configure the
300
+ # produced JSON string output further.
301
+ def to_json(state = nil, *)
302
+ state = State.from_state(state)
303
+ state.check_max_nesting
304
+ json_transform(state)
305
+ end
306
+
307
+ private
308
+
309
+ def json_transform(state)
310
+ delim = ','
311
+ delim << state.array_nl
312
+ result = '['
313
+ result << state.array_nl
314
+ depth = state.depth += 1
315
+ first = true
316
+ indent = !state.array_nl.empty?
317
+ each { |value|
318
+ result << delim unless first
319
+ result << state.indent * depth if indent
320
+ result << value.to_json(state)
321
+ first = false
322
+ }
323
+ depth = state.depth -= 1
324
+ result << state.array_nl
325
+ result << state.indent * depth if indent
326
+ result << ']'
327
+ end
328
+ end
329
+
330
+ module Integer
331
+ # Returns a JSON string representation for this Integer number.
332
+ def to_json(*) to_s end
333
+ end
334
+
335
+ module Float
336
+ # Returns a JSON string representation for this Float number.
337
+ def to_json(state = nil, *)
338
+ state = State.from_state(state)
339
+ case
340
+ when infinite?
341
+ if state.allow_nan?
342
+ to_s
343
+ else
344
+ raise GeneratorError, "#{self} not allowed in JSON"
345
+ end
346
+ when nan?
347
+ if state.allow_nan?
348
+ to_s
349
+ else
350
+ raise GeneratorError, "#{self} not allowed in JSON"
351
+ end
352
+ else
353
+ to_s
354
+ end
355
+ end
356
+ end
357
+
358
+ module String
359
+ if defined?(::Encoding)
360
+ # This string should be encoded with UTF-8 A call to this method
361
+ # returns a JSON string encoded with UTF16 big endian characters as
362
+ # \u????.
363
+ def to_json(state = nil, *args)
364
+ state = State.from_state(state)
365
+ if encoding == ::Encoding::UTF_8
366
+ string = self
367
+ else
368
+ string = encode(::Encoding::UTF_8)
369
+ end
370
+ if state.ascii_only?
371
+ '"' << JSON.utf8_to_json_ascii(string) << '"'
372
+ else
373
+ '"' << JSON.utf8_to_json(string) << '"'
374
+ end
375
+ end
376
+ else
377
+ # This string should be encoded with UTF-8 A call to this method
378
+ # returns a JSON string encoded with UTF16 big endian characters as
379
+ # \u????.
380
+ def to_json(state = nil, *args)
381
+ state = State.from_state(state)
382
+ if state.ascii_only?
383
+ '"' << JSON.utf8_to_json_ascii(self) << '"'
384
+ else
385
+ '"' << JSON.utf8_to_json(self) << '"'
386
+ end
387
+ end
388
+ end
389
+
390
+ # Module that holds the extinding methods if, the String module is
391
+ # included.
392
+ module Extend
393
+ # Raw Strings are JSON Objects (the raw bytes are stored in an
394
+ # array for the key "raw"). The Ruby String can be created by this
395
+ # module method.
396
+ def json_create(o)
397
+ o['raw'].pack('C*')
398
+ end
399
+ end
400
+
401
+ # Extends _modul_ with the String::Extend module.
402
+ def self.included(modul)
403
+ modul.extend Extend
404
+ end
405
+
406
+ # This method creates a raw object hash, that can be nested into
407
+ # other data structures and will be unparsed as a raw string. This
408
+ # method should be used, if you want to convert raw strings to JSON
409
+ # instead of UTF-8 strings, e. g. binary data.
410
+ def to_json_raw_object
411
+ {
412
+ JSON.create_id => self.class.name,
413
+ 'raw' => self.unpack('C*'),
414
+ }
415
+ end
416
+
417
+ # This method creates a JSON text from the result of
418
+ # a call to to_json_raw_object of this String.
419
+ def to_json_raw(*args)
420
+ to_json_raw_object.to_json(*args)
421
+ end
422
+ end
423
+
424
+ module TrueClass
425
+ # Returns a JSON string for true: 'true'.
426
+ def to_json(*) 'true' end
427
+ end
428
+
429
+ module FalseClass
430
+ # Returns a JSON string for false: 'false'.
431
+ def to_json(*) 'false' end
432
+ end
433
+
434
+ module NilClass
435
+ # Returns a JSON string for nil: 'null'.
436
+ def to_json(*) 'null' end
437
+ end
438
+ end
439
+ end
440
+ end
441
+ end