microfiche 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/ProjectInfo ADDED
@@ -0,0 +1,31 @@
1
+ PROJECT : microfiche
2
+ VERSION : '1.0.0'
3
+ DATE : '2006-11-26'
4
+ STATUS : stable
5
+
6
+ CONTACT : Trans <transfire_AT_gmail.com>
7
+ EMAIL : facets-universal@rubyforge.org
8
+ HOMEPAGE : "http://facets.rubyforge.org"
9
+
10
+ TITLE : Microfiche
11
+ SUMMARY : Collection of microformat libraries.
12
+ DESCRIPTION : >
13
+ Micorfiche is an assortment of microformat libraries.
14
+ So far only XOXO is included, but other will be added
15
+ soon. Also included in a JSON library, not strictly a
16
+ micorformat, its usage is closely paired with these
17
+ other technologies, and so is included.
18
+
19
+ DEPENDENCIES: [ corsets ]
20
+
21
+ RUBYFORGE:
22
+ project : facets
23
+ username : transami
24
+ package : microfiche
25
+
26
+ RDOC:
27
+ template : jamis
28
+ options : ['--all', '--inline-source']
29
+
30
+ TEST:
31
+ extract : true
data/README ADDED
@@ -0,0 +1,24 @@
1
+ = Microfiche
2
+
3
+ Collection of Microformat libraries.
4
+
5
+
6
+ == Install
7
+
8
+ Microfiche is available in via gem or source tarball.
9
+
10
+ gem install microfiche
11
+
12
+ or
13
+
14
+ tar -xvzf microfiche-1.0.0.tar.gz
15
+ cd microfiche-1.0.0
16
+ sudo ruby setup.rb
17
+
18
+
19
+ == License
20
+
21
+ Copyright(c)2006-2007 Florian Frank, Christian Neukirchen, Thomas Sawyer
22
+
23
+ Ruby/License
24
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'autorake'
2
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0 stable (2007-03-16)
@@ -0,0 +1,917 @@
1
+ # = json.rb
2
+ #
3
+ # == Copyright (c) 2006 Florian Frank
4
+ #
5
+ # GNU General Public License
6
+ #
7
+ # This program is free software; you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation; either version 2 of the License, or (at
10
+ # your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful, but
13
+ # WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
+ # General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program; if not, write to the Free Software
19
+ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
+ #
21
+ # == Author(s)
22
+ #
23
+ # * Florian Frank
24
+ #
25
+ # == Additional Information
26
+ #
27
+ # The latest version of this library can be downloaded at:
28
+ # * http://rubyforge.org/frs?group_id=953
29
+ #
30
+ # Online Documentation should be located at:
31
+ # * http://json.rubyforge.org
32
+
33
+ # Author:: Florian Frank <mailto:flori@ping.de>
34
+ # Copyright:: Copyright (c) 2006 Florian Frank
35
+ # License:: GNU General Public License (GPL)
36
+
37
+ # = JSON library for Ruby
38
+ #
39
+ # Ruby support of Javascript Object Notation.
40
+ #
41
+ # == Examples
42
+ #
43
+ # To create a JSON string from a ruby data structure, you
44
+ # can call JSON.unparse like that:
45
+ #
46
+ # json = JSON.unparse [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
47
+ # # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
48
+ #
49
+ # It's also possible to call the #to_json method directly.
50
+ #
51
+ # json = [1, 2, {"a"=>3.141}, false, true, nil, 4..10].to_json
52
+ # # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
53
+ #
54
+ # To get back a ruby data structure, you have to call
55
+ # JSON.parse on the JSON string:
56
+ #
57
+ # JSON.parse json
58
+ # # => [1, 2, {"a"=>3.141}, false, true, nil, "4..10"]
59
+ #
60
+ # Note, that the range from the original data structure is a simple
61
+ # string now. The reason for this is, that JSON doesn't support ranges
62
+ # or arbitrary classes. In this case the json library falls back to call
63
+ # Object#to_json, which is the same as #to_s.to_json.
64
+ #
65
+ # It's possible to extend JSON to support serialization of arbitray classes by
66
+ # simply implementing a more specialized version of the #to_json method, that
67
+ # should return a JSON object (a hash converted to JSON with #to_json)
68
+ # like this (don't forget the *a for all the arguments):
69
+ #
70
+ # class Range
71
+ # def to_json(*a)
72
+ # {
73
+ # 'json_class' => self.class.name,
74
+ # 'data' => [ first, last, exclude_end? ]
75
+ # }.to_json(*a)
76
+ # end
77
+ # end
78
+ #
79
+ # The hash key 'json_class' is the class, that will be asked to deserialize the
80
+ # JSON representation later. In this case it's 'Range', but any namespace of
81
+ # the form 'A::B' or '::A::B' will do. All other keys are arbitrary and can be
82
+ # used to store the necessary data to configure the object to be deserialized.
83
+ #
84
+ # If a the key 'json_class' is found in a JSON object, the JSON parser checks
85
+ # if the given class responds to the json_create class method. If so, it is
86
+ # called with the JSON object converted to a Ruby hash. So a range can
87
+ # be deserialized by implementing Range.json_create like this:
88
+ #
89
+ # class Range
90
+ # def self.json_create(o)
91
+ # new(*o['data'])
92
+ # end
93
+ # end
94
+ #
95
+ # Now it possible to serialize/deserialize ranges as well:
96
+ #
97
+ # json = JSON.unparse [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
98
+ # # => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
99
+ # JSON.parse json
100
+ # # => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
101
+ #
102
+ # JSON.unparse always creates the shortes possible string representation of a
103
+ # ruby data structure in one line. This good for data storage or network
104
+ # protocols, but not so good for humans to read. Fortunately there's
105
+ # also JSON.pretty_unparse that creates a more readable output:
106
+ #
107
+ # puts JSON.pretty_unparse([1, 2, {"a"=>3.141}, false, true, nil, 4..10])
108
+ # [
109
+ # 1,
110
+ # 2,
111
+ # {
112
+ # "a": 3.141
113
+ # },
114
+ # false,
115
+ # true,
116
+ # null,
117
+ # {
118
+ # "json_class": "Range",
119
+ # "data": [
120
+ # 4,
121
+ # 10,
122
+ # false
123
+ # ]
124
+ # }
125
+ # ]
126
+ #
127
+ # There are also the methods Kernel#j for unparse, and Kernel#jj for
128
+ # pretty_unparse output to the console, that work analogous to Kernel#p and
129
+ # Kernel#pp.
130
+ #
131
+
132
+ require 'strscan'
133
+
134
+ # This module is the namespace for all the JSON related classes. It also
135
+ # defines some module functions to expose a nicer API to users, instead
136
+ # of using the parser and other classes directly.
137
+
138
+ module JSON
139
+
140
+ # The base exception for JSON errors.
141
+ JSONError = Class.new StandardError
142
+
143
+ # This exception is raise, if a parser error occurs.
144
+ ParserError = Class.new JSONError
145
+
146
+ # This exception is raise, if a unparser error occurs.
147
+ UnparserError = Class.new JSONError
148
+
149
+ # If a circular data structure is encountered while unparsing
150
+ # this exception is raised.
151
+ CircularDatastructure = Class.new UnparserError
152
+
153
+ class << self
154
+ # Switches on Unicode support, if _enable_ is _true_. Otherwise switches
155
+ # Unicode support off.
156
+ def support_unicode=(enable)
157
+ @support_unicode = enable
158
+ end
159
+
160
+ # Returns _true_ if JSON supports unicode, otherwise _false_ is returned.
161
+ def support_unicode?
162
+ !!@support_unicode
163
+ end
164
+ end
165
+ JSON.support_unicode = true # default, hower it's possible to switch off full
166
+ # unicode support, if non-ascii bytes should be
167
+ # just passed through.
168
+
169
+ begin
170
+ require 'iconv'
171
+ # An iconv instance to convert from UTF8 to UTF16 Big Endian.
172
+ UTF16toUTF8 = Iconv.new('utf-8', 'utf-16be')
173
+ # An iconv instance to convert from UTF16 Big Endian to UTF8.
174
+ UTF8toUTF16 = Iconv.new('utf-16be', 'utf-8'); UTF8toUTF16.iconv('no bom')
175
+ rescue LoadError
176
+ JSON.support_unicode = false # enforce disabling of unicode support
177
+ end
178
+
179
+ # This class implements the JSON parser that is used to parse a JSON string
180
+ # into a Ruby data structure.
181
+ class Parser < StringScanner
182
+ STRING = /"((?:[^"\\]|\\.)*)"/
183
+ INTEGER = /-?\d+/
184
+ FLOAT = /-?\d+\.(\d*)(?i:e[+-]?\d+)?/
185
+ OBJECT_OPEN = /\{/
186
+ OBJECT_CLOSE = /\}/
187
+ ARRAY_OPEN = /\[/
188
+ ARRAY_CLOSE = /\]/
189
+ PAIR_DELIMITER = /:/
190
+ COLLECTION_DELIMITER = /,/
191
+ TRUE = /true/
192
+ FALSE = /false/
193
+ NULL = /null/
194
+ IGNORE = %r(
195
+ (?:
196
+ //[^\n\r]*[\n\r]| # line comments
197
+ /\* # c-style comments
198
+ (?:
199
+ [^*/]| # normal chars
200
+ /[^*]| # slashes that do not start a nested comment
201
+ \*[^/]| # asterisks that do not end this comment
202
+ /(?=\*/) # single slash before this comment's end
203
+ )*
204
+ \*/ # the end of this comment
205
+ |\s+ # whitespaces
206
+ )+
207
+ )mx
208
+
209
+ UNPARSED = Object.new
210
+
211
+ # Parses the current JSON string and returns the complete data structure
212
+ # as a result.
213
+ def parse
214
+ reset
215
+ until eos?
216
+ case
217
+ when scan(ARRAY_OPEN)
218
+ return parse_array
219
+ when scan(OBJECT_OPEN)
220
+ return parse_object
221
+ when skip(IGNORE)
222
+ ;
223
+ when !((value = parse_value).equal? UNPARSED)
224
+ return value
225
+ else
226
+ raise ParserError, "source '#{peek(20)}' not in JSON!"
227
+ end
228
+ end
229
+ end
230
+
231
+ private
232
+
233
+ def parse_string
234
+ if scan(STRING)
235
+ return '' if self[1].empty?
236
+ self[1].gsub(/\\(?:[\\bfnrt"]|u([A-Fa-f\d]{4}))/) do
237
+ case $~[0]
238
+ when '\\\\' then '\\'
239
+ when '\\b' then "\b"
240
+ when '\\f' then "\f"
241
+ when '\\n' then "\n"
242
+ when '\\r' then "\r"
243
+ when '\\t' then "\t"
244
+ when '\\"' then '"'
245
+ else
246
+ if JSON.support_unicode? and $KCODE == 'UTF8'
247
+ JSON.utf16_to_utf8($~[1])
248
+ else
249
+ # if utf8 mode is switched off or unicode not supported, try to
250
+ # transform unicode \u-notation to bytes directly:
251
+ $~[1].to_i(16).chr
252
+ end
253
+ end
254
+ end
255
+ else
256
+ UNPARSED
257
+ end
258
+ end
259
+
260
+ def parse_value
261
+ case
262
+ when scan(FLOAT)
263
+ Float(self[0])
264
+ when scan(INTEGER)
265
+ Integer(self[0])
266
+ when scan(TRUE)
267
+ true
268
+ when scan(FALSE)
269
+ false
270
+ when scan(NULL)
271
+ nil
272
+ when (string = parse_string) != UNPARSED
273
+ string
274
+ when scan(ARRAY_OPEN)
275
+ parse_array
276
+ when scan(OBJECT_OPEN)
277
+ parse_object
278
+ else
279
+ UNPARSED
280
+ end
281
+ end
282
+
283
+ def parse_array
284
+ result = []
285
+ until eos?
286
+ case
287
+ when (value = parse_value) != UNPARSED
288
+ result << value
289
+ skip(IGNORE)
290
+ unless scan(COLLECTION_DELIMITER) or match?(ARRAY_CLOSE)
291
+ raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
292
+ end
293
+ when scan(ARRAY_CLOSE)
294
+ break
295
+ when skip(IGNORE)
296
+ ;
297
+ else
298
+ raise ParserError, "unexpected token in array at '#{peek(20)}'!"
299
+ end
300
+ end
301
+ result
302
+ end
303
+
304
+ def parse_object
305
+ result = {}
306
+ until eos?
307
+ case
308
+ when (string = parse_string) != UNPARSED
309
+ skip(IGNORE)
310
+ unless scan(PAIR_DELIMITER)
311
+ raise ParserError, "expected ':' in object at '#{peek(20)}'!"
312
+ end
313
+ skip(IGNORE)
314
+ unless (value = parse_value).equal? UNPARSED
315
+ result[string] = value
316
+ skip(IGNORE)
317
+ unless scan(COLLECTION_DELIMITER) or match?(OBJECT_CLOSE)
318
+ raise ParserError,
319
+ "expected ',' or '}' in object at '#{peek(20)}'!"
320
+ end
321
+ else
322
+ raise ParserError, "expected value in object at '#{peek(20)}'!"
323
+ end
324
+ when scan(OBJECT_CLOSE)
325
+ if klassname = result['json_class']
326
+ klass = klassname.sub(/^:+/, '').split(/::/).inject(Object) do |p,k|
327
+ p.const_get(k) rescue nil
328
+ end
329
+ break unless klass and klass.json_creatable?
330
+ result = klass.json_create(result)
331
+ end
332
+ break
333
+ when skip(IGNORE)
334
+ ;
335
+ else
336
+ raise ParserError, "unexpected token in object at '#{peek(20)}'!"
337
+ end
338
+ end
339
+ result
340
+ end
341
+ end
342
+
343
+ # This class is used to create State instances, that are use to hold data
344
+ # while unparsing a Ruby data structure into a JSON string.
345
+ class State
346
+ # Creates a State object from _opts_, which ought to be Hash to create a
347
+ # new State instance configured by opts, something else to create an
348
+ # unconfigured instance. If _opts_ is a State object, it is just returned.
349
+ def self.from_state(opts)
350
+ case opts
351
+ when self
352
+ opts
353
+ when Hash
354
+ new(opts)
355
+ else
356
+ new
357
+ end
358
+ end
359
+
360
+ # Instantiates a new State object, configured by _opts_.
361
+ def initialize(opts = {})
362
+ @indent = opts[:indent] || ''
363
+ @space = opts[:space] || ''
364
+ @object_nl = opts[:object_nl] || ''
365
+ @array_nl = opts[:array_nl] || ''
366
+ @seen = {}
367
+ end
368
+
369
+ # This string is used to indent levels in the JSON string.
370
+ attr_accessor :indent
371
+
372
+ # This string is used to include a space between the tokens in a JSON
373
+ # string.
374
+ attr_accessor :space
375
+
376
+ # This string is put at the end of a line that holds a JSON object (or
377
+ # Hash).
378
+ attr_accessor :object_nl
379
+
380
+ # This string is put at the end of a line that holds a JSON array.
381
+ attr_accessor :array_nl
382
+
383
+ # Returns _true_, if _object_ was already seen during this Unparsing run.
384
+ def seen?(object)
385
+ @seen.key?(object.__id__)
386
+ end
387
+
388
+ # Remember _object_, to find out if it was already encountered (to find out
389
+ # if a cyclic data structure is unparsed).
390
+ def remember(object)
391
+ @seen[object.__id__] = true
392
+ end
393
+
394
+ # Forget _object_ for this Unparsing run.
395
+ def forget(object)
396
+ @seen.delete object.__id__
397
+ end
398
+ end
399
+
400
+ module_function
401
+
402
+ # Convert _string_ from UTF8 encoding to UTF16 (big endian) encoding and
403
+ # return it.
404
+ def utf8_to_utf16(string)
405
+ JSON::UTF8toUTF16.iconv(string).unpack('H*')[0]
406
+ end
407
+
408
+ # Convert _string_ from UTF16 (big endian) encoding to UTF8 encoding and
409
+ # return it.
410
+ def utf16_to_utf8(string)
411
+ bytes = '' << string[0, 2].to_i(16) << string[2, 2].to_i(16)
412
+ JSON::UTF16toUTF8.iconv(bytes)
413
+ end
414
+
415
+ # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
416
+ # UTF16 big endian characters as \u????, and return it.
417
+ def utf8_to_json(string)
418
+ i, n, result = 0, string.size, ''
419
+ while i < n
420
+ char = string[i]
421
+ case
422
+ when char == ?\b then result << '\b'
423
+ when char == ?\t then result << '\t'
424
+ when char == ?\n then result << '\n'
425
+ when char == ?\f then result << '\f'
426
+ when char == ?\r then result << '\r'
427
+ when char == ?" then result << '\"'
428
+ when char == ?\\ then result << '\\\\'
429
+ when char.between?(0x0, 0x1f) then result << "\\u%04x" % char
430
+ when char.between?(0x20, 0x7f) then result << char
431
+ when !(JSON.support_unicode? && $KCODE == 'UTF8')
432
+ # if utf8 mode is switched off or unicode not supported, just pass
433
+ # bytes through:
434
+ result << char
435
+ when char & 0xe0 == 0xc0
436
+ result << '\u' << utf8_to_utf16(string[i, 2])
437
+ i += 1
438
+ when char & 0xf0 == 0xe0
439
+ result << '\u' << utf8_to_utf16(string[i, 3])
440
+ i += 2
441
+ when char & 0xf8 == 0xf0
442
+ result << '\u' << utf8_to_utf16(string[i, 4])
443
+ i += 3
444
+ when char & 0xfc == 0xf8
445
+ result << '\u' << utf8_to_utf16(string[i, 5])
446
+ i += 4
447
+ when char & 0xfe == 0xfc
448
+ result << '\u' << utf8_to_utf16(string[i, 6])
449
+ i += 5
450
+ else
451
+ raise JSON::UnparserError, "Encountered unknown UTF-8 byte: %x!" % char
452
+ end
453
+ i += 1
454
+ end
455
+ result
456
+ end
457
+
458
+ # Parse the JSON string _source_ into a Ruby data structure and return it.
459
+ def parse(source)
460
+ Parser.new(source).parse
461
+ end
462
+
463
+ # Unparse the Ruby data structure _obj_ into a single line JSON string and
464
+ # return it. _state_ is a JSON::State object, that can be used to configure
465
+ # the output further.
466
+ def unparse(obj, state = nil)
467
+ obj.to_json(JSON::State.from_state(state))
468
+ end
469
+
470
+ # Unparse the Ruby data structure _obj_ into a JSON string and return it.
471
+ # The returned string is a prettier form of the string returned by #unparse.
472
+ def pretty_unparse(obj)
473
+ state = JSON::State.new(
474
+ :indent => ' ',
475
+ :space => ' ',
476
+ :object_nl => "\n",
477
+ :array_nl => "\n"
478
+ )
479
+ obj.to_json(state)
480
+ end
481
+ end
482
+
483
+ require 'yaml'
484
+
485
+ class Object
486
+ #--
487
+ # Converts this object to a string (calling #to_s), converts
488
+ # it to a JSON string, and returns the result. This is a fallback, if no
489
+ # special method #to_json was defined for some object.
490
+ # _state_ is a JSON::State object, that can also be used
491
+ # to configure the produced JSON string output further.
492
+ #
493
+ #def to_json(*) to_s.to_json end
494
+ #++
495
+
496
+ # Fallback to_json method uses to_yaml, strips the type header and then
497
+ # reloads as a pure hash, which is then converted to JSON.
498
+ def to_json(*)
499
+ str = to_yaml
500
+ str.sub!(/^---.*?$/, '---')
501
+ YAML.load(str).to_json
502
+ end
503
+ end
504
+
505
+ class Module
506
+ def to_json(*) to_s.to_json end
507
+ end
508
+
509
+ class Hash
510
+ # Returns a JSON string containing a JSON object, that is unparsed from
511
+ # this Hash instance.
512
+ # _state_ is a JSON::State object, that can also be used to configure the
513
+ # produced JSON string output further.
514
+ # _depth_ is used to find out nesting depth, to indent accordingly.
515
+ def to_json(state = nil, depth = 0)
516
+ state = JSON::State.from_state(state)
517
+ json_check_circular(state) { json_transform(state, depth) }
518
+ end
519
+
520
+ private
521
+
522
+ def json_check_circular(state)
523
+ if state
524
+ state.seen?(self) and raise JSON::CircularDatastructure,
525
+ "circular data structures not supported!"
526
+ state.remember self
527
+ end
528
+ yield
529
+ ensure
530
+ state and state.forget self
531
+ end
532
+
533
+ def json_shift(state, depth)
534
+ state and not state.object_nl.empty? or return ''
535
+ state.indent * depth
536
+ end
537
+
538
+ def json_transform(state, depth)
539
+ delim = ','
540
+ delim << state.object_nl if state
541
+ result = '{'
542
+ result << state.object_nl if state
543
+ result << map { |key,value|
544
+ json_shift(state, depth + 1) <<
545
+ key.to_s.to_json(state, depth + 1) <<
546
+ ':' << state.space << value.to_json(state, depth + 1)
547
+ }.join(delim)
548
+ result << state.object_nl if state
549
+ result << json_shift(state, depth)
550
+ result << '}'
551
+ result
552
+ end
553
+ end
554
+
555
+ class Array
556
+ # Returns a JSON string containing a JSON array, that is unparsed from
557
+ # this Array instance.
558
+ # _state_ is a JSON::State object, that can also be used to configure the
559
+ # produced JSON string output further.
560
+ # _depth_ is used to find out nesting depth, to indent accordingly.
561
+ def to_json(state = nil, depth = 0)
562
+ state = JSON::State.from_state(state)
563
+ json_check_circular(state) { json_transform(state, depth) }
564
+ end
565
+
566
+ private
567
+
568
+ def json_check_circular(state)
569
+ if state
570
+ state.seen?(self) and raise JSON::CircularDatastructure,
571
+ "circular data structures not supported!"
572
+ state.remember self
573
+ end
574
+ yield
575
+ ensure
576
+ state and state.forget self
577
+ end
578
+
579
+ def json_shift(state, depth)
580
+ state and not state.array_nl.empty? or return ''
581
+ state.indent * depth
582
+ end
583
+
584
+ def json_transform(state, depth)
585
+ delim = ','
586
+ delim << state.array_nl if state
587
+ result = '['
588
+ result << state.array_nl if state
589
+ result << map { |value|
590
+ json_shift(state, depth + 1) << value.to_json(state, depth + 1)
591
+ }.join(delim)
592
+ result << state.array_nl if state
593
+ result << json_shift(state, depth)
594
+ result << ']'
595
+ result
596
+ end
597
+ end
598
+
599
+ class Integer #:nodoc:
600
+ # Returns a JSON string representation for this Integer number.
601
+ def to_json(*) to_s end
602
+ end
603
+
604
+ class Float #:nodoc:
605
+ # Returns a JSON string representation for this Float number.
606
+ def to_json(*) to_s end
607
+ end
608
+
609
+ class String
610
+ # This string should be encoded with UTF-8 (if JSON unicode support is
611
+ # enabled). A call to this method returns a JSON string
612
+ # encoded with UTF16 big endian characters as \u????. If
613
+ # JSON.support_unicode? is false only control characters are encoded this
614
+ # way, all 8-bit bytes are just passed through.
615
+ def to_json(*)
616
+ '"' << JSON::utf8_to_json(self) << '"'
617
+ end
618
+
619
+ # Raw Strings are JSON Objects (the raw bytes are stored in an array for the
620
+ # key "raw"). The Ruby String can be created by this class method.
621
+ def self.json_create(o)
622
+ o['raw'].pack('C*')
623
+ end
624
+
625
+ # This method creates a raw object, that can be nested into other data
626
+ # structures and will be unparsed as a raw string.
627
+ def to_json_raw_object
628
+ {
629
+ 'json_class' => self.class.name,
630
+ 'raw' => self.unpack('C*'),
631
+ }
632
+ end
633
+
634
+ # This method should be used, if you want to convert raw strings to JSON
635
+ # instead of UTF-8 strings, e. g. binary data (and JSON Unicode support is
636
+ # enabled).
637
+ def to_json_raw(*args)
638
+ to_json_raw_object.to_json(*args)
639
+ end
640
+ end
641
+
642
+ class TrueClass #:nodoc:
643
+ # Returns a JSON string for true: 'true'.
644
+ def to_json(*) to_s end
645
+ end
646
+
647
+ class FalseClass #:nodoc:
648
+ # Returns a JSON string for false: 'false'.
649
+ def to_json(*) to_s end
650
+ end
651
+
652
+ class NilClass #:nodoc:
653
+ # Returns a JSON string for nil: 'null'.
654
+ def to_json(*) 'null' end
655
+ end
656
+
657
+ module Kernel
658
+ # Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in
659
+ # one line.
660
+ def j(*objs)
661
+ objs.each do |obj|
662
+ puts JSON::unparse(obj)
663
+ end
664
+ nil
665
+ end
666
+
667
+ # Ouputs _objs_ to STDOUT as JSON strings in a pretty format, with
668
+ # indentation and over many lines.
669
+ def jj(*objs)
670
+ objs.each do |obj|
671
+ puts JSON::pretty_unparse(obj)
672
+ end
673
+ nil
674
+ end
675
+ end
676
+
677
+ class Class
678
+ # Returns true, if this class can be used to create an instance
679
+ # from a serialised JSON string. The class has to implement a class
680
+ # method _json_create_ that expects a hash as first parameter, which includes
681
+ # the required data.
682
+ def json_creatable?
683
+ respond_to?(:json_create)
684
+ end
685
+ end
686
+ # vim: set et sw=2 ts=2:
687
+
688
+
689
+
690
+
691
+ # _____ _
692
+ # |_ _|__ ___| |_
693
+ # | |/ _ \/ __| __|
694
+ # | | __/\__ \ |_
695
+ # |_|\___||___/\__|
696
+ #
697
+ =begin test
698
+
699
+ require 'test/unit'
700
+
701
+ class TC_JSON < Test::Unit::TestCase
702
+
703
+ include JSON
704
+
705
+ class A
706
+ def initialize(a)
707
+ @a = a
708
+ end
709
+
710
+ attr_reader :a
711
+
712
+ def ==(other)
713
+ a == other.a
714
+ end
715
+
716
+ def self.json_create(object)
717
+ new(*object['args'])
718
+ end
719
+
720
+ def to_json(*args)
721
+ {
722
+ 'json_class' => self.class,
723
+ 'args' => [ @a ],
724
+ }.to_json(*args)
725
+ end
726
+ end
727
+
728
+ def setup
729
+ $KCODE = 'UTF8'
730
+ @ary = [1, "foo", 3.14, 4711.0, 2.718, nil, [1,-2,3], false, true]
731
+ @ary_to_parse = ["1", '"foo"', "3.14", "4711.0", "2.718", "null",
732
+ "[1,-2,3]", "false", "true"]
733
+ @hash = {
734
+ 'a' => 2,
735
+ 'b' => 3.141,
736
+ 'c' => 'c',
737
+ 'd' => [ 1, "b", 3.14 ],
738
+ 'e' => { 'foo' => 'bar' },
739
+ 'g' => "\"\0\037",
740
+ 'h' => 1000.0,
741
+ 'i' => 0.001
742
+ }
743
+ @json = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},' +
744
+ '"g":"\\"\\u0000\\u001f","h":1.0E3,"i":1.0E-3}'
745
+ @json2 = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},' +
746
+ '"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}'
747
+ end
748
+
749
+ def test_parse_value
750
+ assert_equal("", parse('""'))
751
+ assert_equal("\\", parse('"\\\\"'))
752
+ assert_equal('"', parse('"\""'))
753
+ assert_equal('\\"\\', parse('"\\\\\\"\\\\"'))
754
+ assert_equal("\\a\"\b\f\n\r\t\0\037",
755
+ parse('"\\a\"\b\f\n\r\t\u0000\u001f"'))
756
+ for i in 0 ... @ary.size
757
+ assert_equal(@ary[i], parse(@ary_to_parse[i]))
758
+ end
759
+ end
760
+
761
+ def test_parse_array
762
+ assert_equal([], parse('[]'))
763
+ assert_equal([], parse(' [ ] '))
764
+ assert_equal([1], parse('[1]'))
765
+ assert_equal([1], parse(' [ 1 ] '))
766
+ assert_equal(@ary,
767
+ parse('[1,"foo",3.14,47.11e+2,2718.E-3,null,[1,-2,3],false,true]'))
768
+ assert_equal(@ary, parse(%Q{ [ 1 , "foo" , 3.14 \t , 47.11e+2
769
+ , 2718.E-3 ,\n null , [1, -2, 3 ], false , true\n ] }))
770
+ end
771
+
772
+ def test_parse_object
773
+ assert_equal({}, parse('{}'))
774
+ assert_equal({}, parse(' { } '))
775
+ assert_equal({'foo'=>'bar'}, parse('{"foo":"bar"}'))
776
+ assert_equal({'foo'=>'bar'}, parse(' { "foo" : "bar" } '))
777
+ end
778
+
779
+ def test_unparse
780
+ json = unparse(@hash)
781
+ assert_equal(@json2, json)
782
+ parsed_json = parse(json)
783
+ assert_equal(@hash, parsed_json)
784
+ json = unparse({1=>2})
785
+ assert_equal('{"1":2}', json)
786
+ parsed_json = parse(json)
787
+ assert_equal({"1"=>2}, parsed_json)
788
+ end
789
+
790
+ def test_parser_reset
791
+ parser = Parser.new(@json)
792
+ assert_equal(@hash, parser.parse)
793
+ assert_equal(@hash, parser.parse)
794
+ end
795
+
796
+ def test_unicode
797
+ assert_equal '""', ''.to_json
798
+ assert_equal '"\\b"', "\b".to_json
799
+ assert_equal '"\u0001"', 0x1.chr.to_json
800
+ assert_equal '"\u001f"', 0x1f.chr.to_json
801
+ assert_equal '" "', ' '.to_json
802
+ assert_equal "\"#{0x7f.chr}\"", 0x7f.chr.to_json
803
+ utf8 = '© ≠ €!'
804
+ json = '"\u00a9 \u2260 \u20ac!"'
805
+ assert_equal json, utf8.to_json
806
+ assert_equal utf8, parse(json)
807
+ utf8 = "\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"
808
+ json = '"\u3042\u3044\u3046\u3048\u304a"'
809
+ assert_equal json, utf8.to_json
810
+ assert_equal utf8, parse(json)
811
+ utf8 = 'საქართველო'
812
+ json = '"\u10e1\u10d0\u10e5\u10d0\u10e0\u10d7\u10d5\u10d4\u10da\u10dd"'
813
+ assert_equal json, utf8.to_json
814
+ assert_equal utf8, parse(json)
815
+ end
816
+
817
+ def test_comments
818
+ json = <<EOT
819
+ {
820
+ "key1":"value1", // eol comment
821
+ "key2":"value2" /* multi line
822
+ * comment */,
823
+ "key3":"value3" /* multi line
824
+ // nested eol comment
825
+ * comment */
826
+ }
827
+ EOT
828
+ assert_equal(
829
+ { "key1" => "value1", "key2" => "value2", "key3" => "value3" },
830
+ parse(json))
831
+ json = <<EOT
832
+ {
833
+ "key1":"value1" /* multi line
834
+ // nested eol comment
835
+ /* illegal nested multi line comment */
836
+ * comment */
837
+ }
838
+ EOT
839
+ assert_raises(ParserError) { parse(json) }
840
+ json = <<EOT
841
+ {
842
+ "key1":"value1" /* multi line
843
+ // nested eol comment
844
+ closed multi comment */
845
+ and again, throw an Error */
846
+ }
847
+ EOT
848
+ assert_raises(ParserError) { parse(json) }
849
+ json = <<EOT
850
+ {
851
+ "key1":"value1" /*/*/
852
+ }
853
+ EOT
854
+ assert_equal({ "key1" => "value1" }, parse(json))
855
+ end
856
+
857
+ def test_extended_json
858
+ a = A.new(666)
859
+ json = a.to_json
860
+ a_again = JSON.parse(json)
861
+ assert_kind_of a.class, a_again
862
+ assert_equal a, a_again
863
+ end
864
+
865
+ def test_raw_strings
866
+ raw = ''
867
+ raw_array = []
868
+ for i in 0..255
869
+ raw << i
870
+ raw_array << i
871
+ end
872
+ json = raw.to_json_raw
873
+ json_raw_object = raw.to_json_raw_object
874
+ hash = { 'json_class' => 'String', 'raw'=> raw_array }
875
+ assert_equal hash, json_raw_object
876
+ json_raw = <<EOT.chomp
877
+ {\"json_class\":\"String\",\"raw\":[0,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,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255]}
878
+ EOT
879
+ # "
880
+ assert_equal json_raw, json
881
+ raw_again = JSON.parse(json)
882
+ assert_equal raw, raw_again
883
+ end
884
+
885
+ def test_utf8_mode
886
+ $KCODE = 'NONE'
887
+ utf8 = "© ≠ €! - \001"
888
+ json = "\"© ≠ €! - \\u0001\""
889
+ assert_equal json, utf8.to_json
890
+ assert_equal utf8, parse(json)
891
+ assert JSON.support_unicode?
892
+ $KCODE = 'UTF8'
893
+ utf8 = '© ≠ €!'
894
+ json = '"\u00a9 \u2260 \u20ac!"'
895
+ assert_equal json, utf8.to_json
896
+ assert_equal utf8, parse(json)
897
+ JSON.support_unicode = false
898
+ assert !JSON.support_unicode?
899
+ utf8 = "© ≠ €! - \001"
900
+ json = "\"© ≠ €! - \\u0001\""
901
+ assert_equal json, utf8.to_json
902
+ assert_equal utf8, parse(json)
903
+ end
904
+
905
+ def test_backslash
906
+ json = '"\\\\.(?i:gif|jpe?g|png)$"'
907
+ data = JSON.parse(json)
908
+ assert_equal json, JSON.unparse(data)
909
+ json = '"\\""'
910
+ data = JSON.parse(json)
911
+ assert_equal json, JSON.unparse(data)
912
+ end
913
+ end
914
+
915
+ =end
916
+
917
+ # vim: set et sw=2 ts=2: