crazy_ivan 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. data/.gitignore +7 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +94 -0
  4. data/Rakefile +92 -0
  5. data/TODO +33 -0
  6. data/VERSION +1 -0
  7. data/bin/crazy_ivan +114 -0
  8. data/crazy_ivan.gemspec +182 -0
  9. data/lib/crazy_ivan.rb +5 -0
  10. data/lib/html_asset_crush.rb +56 -0
  11. data/lib/report_assembler.rb +78 -0
  12. data/lib/test_runner.rb +71 -0
  13. data/templates/css/ci.css +11 -0
  14. data/templates/index.html +105 -0
  15. data/templates/javascript/json-template.js +544 -0
  16. data/templates/javascript/prototype.js +4917 -0
  17. data/test/crazy_ivan_test.rb +4 -0
  18. data/test/test_helper.rb +9 -0
  19. data/vendor/json-1.1.7/CHANGES +119 -0
  20. data/vendor/json-1.1.7/GPL +340 -0
  21. data/vendor/json-1.1.7/README +78 -0
  22. data/vendor/json-1.1.7/RUBY +58 -0
  23. data/vendor/json-1.1.7/Rakefile +270 -0
  24. data/vendor/json-1.1.7/TODO +1 -0
  25. data/vendor/json-1.1.7/VERSION +1 -0
  26. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkComparison.log +52 -0
  27. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast-autocorrelation.dat +1000 -0
  28. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast.dat +1001 -0
  29. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty-autocorrelation.dat +900 -0
  30. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty.dat +901 -0
  31. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe-autocorrelation.dat +1000 -0
  32. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe.dat +1001 -0
  33. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt.log +261 -0
  34. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast-autocorrelation.dat +1000 -0
  35. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast.dat +1001 -0
  36. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty-autocorrelation.dat +1000 -0
  37. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty.dat +1001 -0
  38. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe-autocorrelation.dat +1000 -0
  39. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe.dat +1001 -0
  40. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure.log +262 -0
  41. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator-autocorrelation.dat +1000 -0
  42. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator.dat +1001 -0
  43. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails.log +82 -0
  44. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkComparison.log +34 -0
  45. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser-autocorrelation.dat +900 -0
  46. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser.dat +901 -0
  47. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt.log +81 -0
  48. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser-autocorrelation.dat +1000 -0
  49. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser.dat +1001 -0
  50. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure.log +82 -0
  51. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser-autocorrelation.dat +1000 -0
  52. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser.dat +1001 -0
  53. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails.log +82 -0
  54. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser-autocorrelation.dat +1000 -0
  55. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser.dat +1001 -0
  56. data/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML.log +82 -0
  57. data/vendor/json-1.1.7/benchmarks/generator_benchmark.rb +165 -0
  58. data/vendor/json-1.1.7/benchmarks/parser_benchmark.rb +197 -0
  59. data/vendor/json-1.1.7/bin/edit_json.rb +9 -0
  60. data/vendor/json-1.1.7/bin/prettify_json.rb +75 -0
  61. data/vendor/json-1.1.7/data/example.json +1 -0
  62. data/vendor/json-1.1.7/data/index.html +38 -0
  63. data/vendor/json-1.1.7/data/prototype.js +4184 -0
  64. data/vendor/json-1.1.7/doc-templates/main.txt +283 -0
  65. data/vendor/json-1.1.7/ext/json/ext/generator/extconf.rb +11 -0
  66. data/vendor/json-1.1.7/ext/json/ext/generator/generator.c +919 -0
  67. data/vendor/json-1.1.7/ext/json/ext/generator/unicode.c +182 -0
  68. data/vendor/json-1.1.7/ext/json/ext/generator/unicode.h +53 -0
  69. data/vendor/json-1.1.7/ext/json/ext/parser/extconf.rb +11 -0
  70. data/vendor/json-1.1.7/ext/json/ext/parser/parser.c +1829 -0
  71. data/vendor/json-1.1.7/ext/json/ext/parser/parser.rl +686 -0
  72. data/vendor/json-1.1.7/ext/json/ext/parser/unicode.c +154 -0
  73. data/vendor/json-1.1.7/ext/json/ext/parser/unicode.h +58 -0
  74. data/vendor/json-1.1.7/install.rb +26 -0
  75. data/vendor/json-1.1.7/lib/json.rb +10 -0
  76. data/vendor/json-1.1.7/lib/json/Array.xpm +21 -0
  77. data/vendor/json-1.1.7/lib/json/FalseClass.xpm +21 -0
  78. data/vendor/json-1.1.7/lib/json/Hash.xpm +21 -0
  79. data/vendor/json-1.1.7/lib/json/Key.xpm +73 -0
  80. data/vendor/json-1.1.7/lib/json/NilClass.xpm +21 -0
  81. data/vendor/json-1.1.7/lib/json/Numeric.xpm +28 -0
  82. data/vendor/json-1.1.7/lib/json/String.xpm +96 -0
  83. data/vendor/json-1.1.7/lib/json/TrueClass.xpm +21 -0
  84. data/vendor/json-1.1.7/lib/json/add/core.rb +135 -0
  85. data/vendor/json-1.1.7/lib/json/add/rails.rb +58 -0
  86. data/vendor/json-1.1.7/lib/json/common.rb +354 -0
  87. data/vendor/json-1.1.7/lib/json/editor.rb +1371 -0
  88. data/vendor/json-1.1.7/lib/json/ext.rb +15 -0
  89. data/vendor/json-1.1.7/lib/json/json.xpm +1499 -0
  90. data/vendor/json-1.1.7/lib/json/pure.rb +77 -0
  91. data/vendor/json-1.1.7/lib/json/pure/generator.rb +430 -0
  92. data/vendor/json-1.1.7/lib/json/pure/parser.rb +269 -0
  93. data/vendor/json-1.1.7/lib/json/version.rb +8 -0
  94. data/vendor/json-1.1.7/tests/fixtures/fail1.json +1 -0
  95. data/vendor/json-1.1.7/tests/fixtures/fail10.json +1 -0
  96. data/vendor/json-1.1.7/tests/fixtures/fail11.json +1 -0
  97. data/vendor/json-1.1.7/tests/fixtures/fail12.json +1 -0
  98. data/vendor/json-1.1.7/tests/fixtures/fail13.json +1 -0
  99. data/vendor/json-1.1.7/tests/fixtures/fail14.json +1 -0
  100. data/vendor/json-1.1.7/tests/fixtures/fail18.json +1 -0
  101. data/vendor/json-1.1.7/tests/fixtures/fail19.json +1 -0
  102. data/vendor/json-1.1.7/tests/fixtures/fail2.json +1 -0
  103. data/vendor/json-1.1.7/tests/fixtures/fail20.json +1 -0
  104. data/vendor/json-1.1.7/tests/fixtures/fail21.json +1 -0
  105. data/vendor/json-1.1.7/tests/fixtures/fail22.json +1 -0
  106. data/vendor/json-1.1.7/tests/fixtures/fail23.json +1 -0
  107. data/vendor/json-1.1.7/tests/fixtures/fail24.json +1 -0
  108. data/vendor/json-1.1.7/tests/fixtures/fail25.json +1 -0
  109. data/vendor/json-1.1.7/tests/fixtures/fail27.json +2 -0
  110. data/vendor/json-1.1.7/tests/fixtures/fail28.json +2 -0
  111. data/vendor/json-1.1.7/tests/fixtures/fail3.json +1 -0
  112. data/vendor/json-1.1.7/tests/fixtures/fail4.json +1 -0
  113. data/vendor/json-1.1.7/tests/fixtures/fail5.json +1 -0
  114. data/vendor/json-1.1.7/tests/fixtures/fail6.json +1 -0
  115. data/vendor/json-1.1.7/tests/fixtures/fail7.json +1 -0
  116. data/vendor/json-1.1.7/tests/fixtures/fail8.json +1 -0
  117. data/vendor/json-1.1.7/tests/fixtures/fail9.json +1 -0
  118. data/vendor/json-1.1.7/tests/fixtures/pass1.json +56 -0
  119. data/vendor/json-1.1.7/tests/fixtures/pass15.json +1 -0
  120. data/vendor/json-1.1.7/tests/fixtures/pass16.json +1 -0
  121. data/vendor/json-1.1.7/tests/fixtures/pass17.json +1 -0
  122. data/vendor/json-1.1.7/tests/fixtures/pass2.json +1 -0
  123. data/vendor/json-1.1.7/tests/fixtures/pass26.json +1 -0
  124. data/vendor/json-1.1.7/tests/fixtures/pass3.json +6 -0
  125. data/vendor/json-1.1.7/tests/test_json.rb +312 -0
  126. data/vendor/json-1.1.7/tests/test_json_addition.rb +164 -0
  127. data/vendor/json-1.1.7/tests/test_json_fixtures.rb +34 -0
  128. data/vendor/json-1.1.7/tests/test_json_generate.rb +106 -0
  129. data/vendor/json-1.1.7/tests/test_json_rails.rb +146 -0
  130. data/vendor/json-1.1.7/tests/test_json_unicode.rb +62 -0
  131. data/vendor/json-1.1.7/tools/fuzz.rb +139 -0
  132. data/vendor/json-1.1.7/tools/server.rb +61 -0
  133. metadata +196 -0
@@ -0,0 +1,283 @@
1
+ # = json - JSON for Ruby
2
+ #
3
+ # == Description
4
+ #
5
+ # This is a implementation of the JSON specification according to RFC 4627
6
+ # (http://www.ietf.org/rfc/rfc4627.txt). Starting from version 1.0.0 on there
7
+ # will be two variants available:
8
+ #
9
+ # * A pure ruby variant, that relies on the iconv and the stringscan
10
+ # extensions, which are both part of the ruby standard library.
11
+ # * The quite a bit faster C extension variant, which is in parts implemented
12
+ # in C and comes with its own unicode conversion functions and a parser
13
+ # generated by the ragel state machine compiler
14
+ # (http://www.cs.queensu.ca/~thurston/ragel).
15
+ #
16
+ # Both variants of the JSON generator escape all non-ASCII an control
17
+ # characters with \uXXXX escape sequences, and support UTF-16 surrogate pairs
18
+ # in order to be able to generate the whole range of unicode code points. This
19
+ # means that generated JSON text is encoded as UTF-8 (because ASCII is a subset
20
+ # of UTF-8) and at the same time avoids decoding problems for receiving
21
+ # endpoints, that don't expect UTF-8 encoded texts. On the negative side this
22
+ # may lead to a bit longer strings than necessarry.
23
+ #
24
+ # All strings, that are to be encoded as JSON strings, should be UTF-8 byte
25
+ # sequences on the Ruby side. To encode raw binary strings, that aren't UTF-8
26
+ # encoded, please use the to_json_raw_object method of String (which produces
27
+ # an object, that contains a byte array) and decode the result on the receiving
28
+ # endpoint.
29
+ #
30
+ # == Author
31
+ #
32
+ # Florian Frank <mailto:flori@ping.de>
33
+ #
34
+ # == License
35
+ #
36
+ # This software is distributed under the same license as Ruby itself, see
37
+ # http://www.ruby-lang.org/en/LICENSE.txt.
38
+ #
39
+ # == Download
40
+ #
41
+ # The latest version of this library can be downloaded at
42
+ #
43
+ # * http://rubyforge.org/frs?group_id=953
44
+ #
45
+ # Online Documentation should be located at
46
+ #
47
+ # * http://json.rubyforge.org
48
+ #
49
+ # == Usage
50
+ #
51
+ # To use JSON you can
52
+ # require 'json'
53
+ # to load the installed variant (either the extension 'json' or the pure
54
+ # variant 'json_pure'). If you have installed the extension variant, you can
55
+ # pick either the extension variant or the pure variant by typing
56
+ # require 'json/ext'
57
+ # or
58
+ # require 'json/pure'
59
+ #
60
+ # You can choose to load a set of common additions to ruby core's objects if
61
+ # you
62
+ # require 'json/add/core'
63
+ #
64
+ # After requiring this you can, e. g., serialise/deserialise Ruby ranges:
65
+ #
66
+ # JSON JSON(1..10) # => 1..10
67
+ #
68
+ # To find out how to add JSON support to other or your own classes, read the
69
+ # Examples section below.
70
+ #
71
+ # To get the best compatibility to rails' JSON implementation, you can
72
+ # require 'json/add/rails'
73
+ #
74
+ # Both of the additions attempt to require 'json' (like above) first, if it has
75
+ # not been required yet.
76
+ #
77
+ # == Speed Comparisons
78
+ #
79
+ # I have created some benchmark results (see the benchmarks/data-p4-3Ghz
80
+ # subdir of the package) for the JSON-parser to estimate the speed up in the C
81
+ # extension:
82
+ #
83
+ # Comparing times (call_time_mean):
84
+ # 1 ParserBenchmarkExt#parser 900 repeats:
85
+ # 553.922304770 ( real) -> 21.500x
86
+ # 0.001805307
87
+ # 2 ParserBenchmarkYAML#parser 1000 repeats:
88
+ # 224.513358139 ( real) -> 8.714x
89
+ # 0.004454078
90
+ # 3 ParserBenchmarkPure#parser 1000 repeats:
91
+ # 26.755020642 ( real) -> 1.038x
92
+ # 0.037376163
93
+ # 4 ParserBenchmarkRails#parser 1000 repeats:
94
+ # 25.763381731 ( real) -> 1.000x
95
+ # 0.038814780
96
+ # calls/sec ( time) -> speed covers
97
+ # secs/call
98
+ #
99
+ # In the table above 1 is JSON::Ext::Parser, 2 is YAML.load with YAML
100
+ # compatbile JSON document, 3 is is JSON::Pure::Parser, and 4 is
101
+ # ActiveSupport::JSON.decode. The ActiveSupport JSON-decoder converts the
102
+ # input first to YAML and then uses the YAML-parser, the conversion seems to
103
+ # slow it down so much that it is only as fast as the JSON::Pure::Parser!
104
+ #
105
+ # If you look at the benchmark data you can see that this is mostly caused by
106
+ # the frequent high outliers - the median of the Rails-parser runs is still
107
+ # overall smaller than the median of the JSON::Pure::Parser runs:
108
+ #
109
+ # Comparing times (call_time_median):
110
+ # 1 ParserBenchmarkExt#parser 900 repeats:
111
+ # 800.592479481 ( real) -> 26.936x
112
+ # 0.001249075
113
+ # 2 ParserBenchmarkYAML#parser 1000 repeats:
114
+ # 271.002390644 ( real) -> 9.118x
115
+ # 0.003690004
116
+ # 3 ParserBenchmarkRails#parser 1000 repeats:
117
+ # 30.227910865 ( real) -> 1.017x
118
+ # 0.033082008
119
+ # 4 ParserBenchmarkPure#parser 1000 repeats:
120
+ # 29.722384421 ( real) -> 1.000x
121
+ # 0.033644676
122
+ # calls/sec ( time) -> speed covers
123
+ # secs/call
124
+ #
125
+ # I have benchmarked the JSON-Generator as well. This generated a few more
126
+ # values, because there are different modes that also influence the achieved
127
+ # speed:
128
+ #
129
+ # Comparing times (call_time_mean):
130
+ # 1 GeneratorBenchmarkExt#generator_fast 1000 repeats:
131
+ # 547.354332608 ( real) -> 15.090x
132
+ # 0.001826970
133
+ # 2 GeneratorBenchmarkExt#generator_safe 1000 repeats:
134
+ # 443.968212317 ( real) -> 12.240x
135
+ # 0.002252414
136
+ # 3 GeneratorBenchmarkExt#generator_pretty 900 repeats:
137
+ # 375.104545883 ( real) -> 10.341x
138
+ # 0.002665923
139
+ # 4 GeneratorBenchmarkPure#generator_fast 1000 repeats:
140
+ # 49.978706968 ( real) -> 1.378x
141
+ # 0.020008521
142
+ # 5 GeneratorBenchmarkRails#generator 1000 repeats:
143
+ # 38.531868759 ( real) -> 1.062x
144
+ # 0.025952543
145
+ # 6 GeneratorBenchmarkPure#generator_safe 1000 repeats:
146
+ # 36.927649925 ( real) -> 1.018x 7 (>=3859)
147
+ # 0.027079979
148
+ # 7 GeneratorBenchmarkPure#generator_pretty 1000 repeats:
149
+ # 36.272134441 ( real) -> 1.000x 6 (>=3859)
150
+ # 0.027569373
151
+ # calls/sec ( time) -> speed covers
152
+ # secs/call
153
+ #
154
+ # In the table above 1-3 are JSON::Ext::Generator methods. 4, 6, and 7 are
155
+ # JSON::Pure::Generator methods and 5 is the Rails JSON generator. It is now a
156
+ # bit faster than the generator_safe and generator_pretty methods of the pure
157
+ # variant but slower than the others.
158
+ #
159
+ # To achieve the fastest JSON text output, you can use the fast_generate
160
+ # method. Beware, that this will disable the checking for circular Ruby data
161
+ # structures, which may cause JSON to go into an infinite loop.
162
+ #
163
+ # Here are the median comparisons for completeness' sake:
164
+ #
165
+ # Comparing times (call_time_median):
166
+ # 1 GeneratorBenchmarkExt#generator_fast 1000 repeats:
167
+ # 708.258020939 ( real) -> 16.547x
168
+ # 0.001411915
169
+ # 2 GeneratorBenchmarkExt#generator_safe 1000 repeats:
170
+ # 569.105020353 ( real) -> 13.296x
171
+ # 0.001757145
172
+ # 3 GeneratorBenchmarkExt#generator_pretty 900 repeats:
173
+ # 482.825371244 ( real) -> 11.280x
174
+ # 0.002071142
175
+ # 4 GeneratorBenchmarkPure#generator_fast 1000 repeats:
176
+ # 62.717626652 ( real) -> 1.465x
177
+ # 0.015944481
178
+ # 5 GeneratorBenchmarkRails#generator 1000 repeats:
179
+ # 43.965681162 ( real) -> 1.027x
180
+ # 0.022745013
181
+ # 6 GeneratorBenchmarkPure#generator_safe 1000 repeats:
182
+ # 43.929073409 ( real) -> 1.026x 7 (>=3859)
183
+ # 0.022763968
184
+ # 7 GeneratorBenchmarkPure#generator_pretty 1000 repeats:
185
+ # 42.802514491 ( real) -> 1.000x 6 (>=3859)
186
+ # 0.023363113
187
+ # calls/sec ( time) -> speed covers
188
+ # secs/call
189
+ #
190
+ # == Examples
191
+ #
192
+ # To create a JSON text from a ruby data structure, you can call JSON.generate
193
+ # like that:
194
+ #
195
+ # json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
196
+ # # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
197
+ #
198
+ # To create a valid JSON text you have to make sure, that the output is
199
+ # embedded in either a JSON array [] or a JSON object {}. The easiest way to do
200
+ # this, is by putting your values in a Ruby Array or Hash instance.
201
+ #
202
+ # To get back a ruby data structure from a JSON text, you have to call
203
+ # JSON.parse on it:
204
+ #
205
+ # JSON.parse json
206
+ # # => [1, 2, {"a"=>3.141}, false, true, nil, "4..10"]
207
+ #
208
+ # Note, that the range from the original data structure is a simple
209
+ # string now. The reason for this is, that JSON doesn't support ranges
210
+ # or arbitrary classes. In this case the json library falls back to call
211
+ # Object#to_json, which is the same as #to_s.to_json.
212
+ #
213
+ # It's possible to add JSON support serialization to arbitrary classes by
214
+ # simply implementing a more specialized version of the #to_json method, that
215
+ # should return a JSON object (a hash converted to JSON with #to_json) like
216
+ # this (don't forget the *a for all the arguments):
217
+ #
218
+ # class Range
219
+ # def to_json(*a)
220
+ # {
221
+ # 'json_class' => self.class.name, # = 'Range'
222
+ # 'data' => [ first, last, exclude_end? ]
223
+ # }.to_json(*a)
224
+ # end
225
+ # end
226
+ #
227
+ # The hash key 'json_class' is the class, that will be asked to deserialise the
228
+ # JSON representation later. In this case it's 'Range', but any namespace of
229
+ # the form 'A::B' or '::A::B' will do. All other keys are arbitrary and can be
230
+ # used to store the necessary data to configure the object to be deserialised.
231
+ #
232
+ # If a the key 'json_class' is found in a JSON object, the JSON parser checks
233
+ # if the given class responds to the json_create class method. If so, it is
234
+ # called with the JSON object converted to a Ruby hash. So a range can
235
+ # be deserialised by implementing Range.json_create like this:
236
+ #
237
+ # class Range
238
+ # def self.json_create(o)
239
+ # new(*o['data'])
240
+ # end
241
+ # end
242
+ #
243
+ # Now it possible to serialise/deserialise ranges as well:
244
+ #
245
+ # json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
246
+ # # => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
247
+ # JSON.parse json
248
+ # # => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
249
+ #
250
+ # JSON.generate always creates the shortest possible string representation of a
251
+ # ruby data structure in one line. This good for data storage or network
252
+ # protocols, but not so good for humans to read. Fortunately there's also
253
+ # JSON.pretty_generate (or JSON.pretty_generate) that creates a more
254
+ # readable output:
255
+ #
256
+ # puts JSON.pretty_generate([1, 2, {"a"=>3.141}, false, true, nil, 4..10])
257
+ # [
258
+ # 1,
259
+ # 2,
260
+ # {
261
+ # "a": 3.141
262
+ # },
263
+ # false,
264
+ # true,
265
+ # null,
266
+ # {
267
+ # "json_class": "Range",
268
+ # "data": [
269
+ # 4,
270
+ # 10,
271
+ # false
272
+ # ]
273
+ # }
274
+ # ]
275
+ #
276
+ # There are also the methods Kernel#j for generate, and Kernel#jj for
277
+ # pretty_generate output to the console, that work analogous to Core Ruby's p
278
+ # and the pp library's pp methods.
279
+ #
280
+ # The script tools/server.rb contains a small example if you want to test, how
281
+ # receiving a JSON object from a webrick server in your browser with the
282
+ # javasript prototype library (http://www.prototypejs.org) works.
283
+ #
@@ -0,0 +1,11 @@
1
+ require 'mkmf'
2
+ require 'rbconfig'
3
+
4
+ if CONFIG['CC'] =~ /gcc/
5
+ $CFLAGS += ' -Wall'
6
+ #$CFLAGS += ' -O0 -ggdb'
7
+ end
8
+
9
+ have_header("ruby/st.h") || have_header("st.h")
10
+ have_header("ruby/encoding.h")
11
+ create_makefile 'generator'
@@ -0,0 +1,919 @@
1
+ #include <string.h>
2
+ #include "ruby.h"
3
+ #if HAVE_RUBY_ST_H
4
+ #include "ruby/st.h"
5
+ #endif
6
+ #if HAVE_ST_H
7
+ #include "st.h"
8
+ #endif
9
+ #include "unicode.h"
10
+ #include <math.h>
11
+
12
+ #ifndef RHASH_TBL
13
+ #define RHASH_TBL(hsh) (RHASH(hsh)->tbl)
14
+ #endif
15
+
16
+ #ifndef RHASH_SIZE
17
+ #define RHASH_SIZE(hsh) (RHASH(hsh)->tbl->num_entries)
18
+ #endif
19
+
20
+ #ifndef RFLOAT_VALUE
21
+ #define RFLOAT_VALUE(val) (RFLOAT(val)->value)
22
+ #endif
23
+
24
+ #ifdef HAVE_RUBY_ENCODING_H
25
+ #include "ruby/encoding.h"
26
+ #define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding())
27
+ #else
28
+ #define FORCE_UTF8(obj)
29
+ #endif
30
+
31
+ #define check_max_nesting(state, depth) do { \
32
+ long current_nesting = 1 + depth; \
33
+ if (state->max_nesting != 0 && current_nesting > state->max_nesting) \
34
+ rb_raise(eNestingError, "nesting of %ld is too deep", current_nesting); \
35
+ } while (0);
36
+
37
+ static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject,
38
+ mHash, mArray, mInteger, mFloat, mString, mString_Extend,
39
+ mTrueClass, mFalseClass, mNilClass, eGeneratorError,
40
+ eCircularDatastructure, eNestingError;
41
+
42
+ static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before,
43
+ i_object_nl, i_array_nl, i_check_circular, i_max_nesting,
44
+ i_allow_nan, i_pack, i_unpack, i_create_id, i_extend;
45
+
46
+ typedef struct JSON_Generator_StateStruct {
47
+ VALUE indent;
48
+ VALUE space;
49
+ VALUE space_before;
50
+ VALUE object_nl;
51
+ VALUE array_nl;
52
+ int check_circular;
53
+ VALUE seen;
54
+ VALUE memo;
55
+ VALUE depth;
56
+ long max_nesting;
57
+ int flag;
58
+ int allow_nan;
59
+ } JSON_Generator_State;
60
+
61
+ #define GET_STATE(self) \
62
+ JSON_Generator_State *state; \
63
+ Data_Get_Struct(self, JSON_Generator_State, state);
64
+
65
+ /*
66
+ * Document-module: JSON::Ext::Generator
67
+ *
68
+ * This is the JSON generator implemented as a C extension. It can be
69
+ * configured to be used by setting
70
+ *
71
+ * JSON.generator = JSON::Ext::Generator
72
+ *
73
+ * with the method generator= in JSON.
74
+ *
75
+ */
76
+
77
+ static int hash_to_json_state_i(VALUE key, VALUE value, VALUE Vstate)
78
+ {
79
+ VALUE json, buf, Vdepth;
80
+ GET_STATE(Vstate);
81
+ buf = state->memo;
82
+ Vdepth = state->depth;
83
+
84
+ if (key == Qundef) return ST_CONTINUE;
85
+ if (state->flag) {
86
+ state->flag = 0;
87
+ rb_str_buf_cat2(buf, ",");
88
+ if (RSTRING_LEN(state->object_nl)) rb_str_buf_append(buf, state->object_nl);
89
+ }
90
+ if (RSTRING_LEN(state->object_nl)) {
91
+ rb_str_buf_append(buf, rb_str_times(state->indent, Vdepth));
92
+ }
93
+ json = rb_funcall(rb_funcall(key, i_to_s, 0), i_to_json, 2, Vstate, Vdepth);
94
+ Check_Type(json, T_STRING);
95
+ rb_str_buf_append(buf, json);
96
+ OBJ_INFECT(buf, json);
97
+ if (RSTRING_LEN(state->space_before)) {
98
+ rb_str_buf_append(buf, state->space_before);
99
+ }
100
+ rb_str_buf_cat2(buf, ":");
101
+ if (RSTRING_LEN(state->space)) rb_str_buf_append(buf, state->space);
102
+ json = rb_funcall(value, i_to_json, 2, Vstate, Vdepth);
103
+ Check_Type(json, T_STRING);
104
+ state->flag = 1;
105
+ rb_str_buf_append(buf, json);
106
+ OBJ_INFECT(buf, json);
107
+ state->depth = Vdepth;
108
+ state->memo = buf;
109
+ return ST_CONTINUE;
110
+ }
111
+
112
+ inline static VALUE mHash_json_transfrom(VALUE self, VALUE Vstate, VALUE Vdepth) {
113
+ long depth, len = RHASH_SIZE(self);
114
+ VALUE result;
115
+ GET_STATE(Vstate);
116
+
117
+ depth = 1 + FIX2LONG(Vdepth);
118
+ result = rb_str_buf_new(len);
119
+ state->memo = result;
120
+ state->depth = LONG2FIX(depth);
121
+ state->flag = 0;
122
+ rb_str_buf_cat2(result, "{");
123
+ if (RSTRING_LEN(state->object_nl)) rb_str_buf_append(result, state->object_nl);
124
+ rb_hash_foreach(self, hash_to_json_state_i, Vstate);
125
+ if (RSTRING_LEN(state->object_nl)) rb_str_buf_append(result, state->object_nl);
126
+ if (RSTRING_LEN(state->object_nl)) {
127
+ rb_str_buf_append(result, rb_str_times(state->indent, Vdepth));
128
+ }
129
+ rb_str_buf_cat2(result, "}");
130
+ return result;
131
+ }
132
+
133
+ static int hash_to_json_i(VALUE key, VALUE value, VALUE buf)
134
+ {
135
+ VALUE tmp;
136
+
137
+ if (key == Qundef) return ST_CONTINUE;
138
+ if (RSTRING_LEN(buf) > 1) rb_str_buf_cat2(buf, ",");
139
+ tmp = rb_funcall(rb_funcall(key, i_to_s, 0), i_to_json, 0);
140
+ Check_Type(tmp, T_STRING);
141
+ rb_str_buf_append(buf, tmp);
142
+ OBJ_INFECT(buf, tmp);
143
+ rb_str_buf_cat2(buf, ":");
144
+ tmp = rb_funcall(value, i_to_json, 0);
145
+ Check_Type(tmp, T_STRING);
146
+ rb_str_buf_append(buf, tmp);
147
+ OBJ_INFECT(buf, tmp);
148
+
149
+ return ST_CONTINUE;
150
+ }
151
+
152
+ /*
153
+ * call-seq: to_json(state = nil, depth = 0)
154
+ *
155
+ * Returns a JSON string containing a JSON object, that is unparsed from
156
+ * this Hash instance.
157
+ * _state_ is a JSON::State object, that can also be used to configure the
158
+ * produced JSON string output further.
159
+ * _depth_ is used to find out nesting depth, to indent accordingly.
160
+ */
161
+ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
162
+ {
163
+ VALUE Vstate, Vdepth, result;
164
+ long depth;
165
+
166
+ rb_scan_args(argc, argv, "02", &Vstate, &Vdepth);
167
+ depth = NIL_P(Vdepth) ? 0 : FIX2LONG(Vdepth);
168
+ if (NIL_P(Vstate)) {
169
+ long len = RHASH_SIZE(self);
170
+ result = rb_str_buf_new(len);
171
+ rb_str_buf_cat2(result, "{");
172
+ rb_hash_foreach(self, hash_to_json_i, result);
173
+ rb_str_buf_cat2(result, "}");
174
+ } else {
175
+ GET_STATE(Vstate);
176
+ check_max_nesting(state, depth);
177
+ if (state->check_circular) {
178
+ VALUE self_id = rb_obj_id(self);
179
+ if (RTEST(rb_hash_aref(state->seen, self_id))) {
180
+ rb_raise(eCircularDatastructure,
181
+ "circular data structures not supported!");
182
+ }
183
+ rb_hash_aset(state->seen, self_id, Qtrue);
184
+ result = mHash_json_transfrom(self, Vstate, LONG2FIX(depth));
185
+ rb_hash_delete(state->seen, self_id);
186
+ } else {
187
+ result = mHash_json_transfrom(self, Vstate, LONG2FIX(depth));
188
+ }
189
+ }
190
+ OBJ_INFECT(result, self);
191
+ FORCE_UTF8(result);
192
+ return result;
193
+ }
194
+
195
+ inline static VALUE mArray_json_transfrom(VALUE self, VALUE Vstate, VALUE Vdepth) {
196
+ long i, len = RARRAY_LEN(self);
197
+ VALUE shift, result;
198
+ long depth = NIL_P(Vdepth) ? 0 : FIX2LONG(Vdepth);
199
+ VALUE delim = rb_str_new2(",");
200
+ GET_STATE(Vstate);
201
+
202
+ check_max_nesting(state, depth);
203
+ if (state->check_circular) {
204
+ VALUE self_id = rb_obj_id(self);
205
+ rb_hash_aset(state->seen, self_id, Qtrue);
206
+ result = rb_str_buf_new(len);
207
+ if (RSTRING_LEN(state->array_nl)) rb_str_append(delim, state->array_nl);
208
+ shift = rb_str_times(state->indent, LONG2FIX(depth + 1));
209
+
210
+ rb_str_buf_cat2(result, "[");
211
+ OBJ_INFECT(result, self);
212
+ rb_str_buf_append(result, state->array_nl);
213
+ for (i = 0; i < len; i++) {
214
+ VALUE element = RARRAY_PTR(self)[i];
215
+ if (RTEST(rb_hash_aref(state->seen, rb_obj_id(element)))) {
216
+ rb_raise(eCircularDatastructure,
217
+ "circular data structures not supported!");
218
+ }
219
+ OBJ_INFECT(result, element);
220
+ if (i > 0) rb_str_buf_append(result, delim);
221
+ rb_str_buf_append(result, shift);
222
+ element = rb_funcall(element, i_to_json, 2, Vstate, LONG2FIX(depth + 1));
223
+ Check_Type(element, T_STRING);
224
+ rb_str_buf_append(result, element);
225
+ }
226
+ if (RSTRING_LEN(state->array_nl)) {
227
+ rb_str_buf_append(result, state->array_nl);
228
+ rb_str_buf_append(result, rb_str_times(state->indent, LONG2FIX(depth)));
229
+ }
230
+ rb_str_buf_cat2(result, "]");
231
+ rb_hash_delete(state->seen, self_id);
232
+ } else {
233
+ result = rb_str_buf_new(len);
234
+ OBJ_INFECT(result, self);
235
+ if (RSTRING_LEN(state->array_nl)) rb_str_append(delim, state->array_nl);
236
+ shift = rb_str_times(state->indent, LONG2FIX(depth + 1));
237
+
238
+ rb_str_buf_cat2(result, "[");
239
+ rb_str_buf_append(result, state->array_nl);
240
+ for (i = 0; i < len; i++) {
241
+ VALUE element = RARRAY_PTR(self)[i];
242
+ OBJ_INFECT(result, element);
243
+ if (i > 0) rb_str_buf_append(result, delim);
244
+ rb_str_buf_append(result, shift);
245
+ element = rb_funcall(element, i_to_json, 2, Vstate, LONG2FIX(depth + 1));
246
+ Check_Type(element, T_STRING);
247
+ rb_str_buf_append(result, element);
248
+ }
249
+ rb_str_buf_append(result, state->array_nl);
250
+ if (RSTRING_LEN(state->array_nl)) {
251
+ rb_str_buf_append(result, rb_str_times(state->indent, LONG2FIX(depth)));
252
+ }
253
+ rb_str_buf_cat2(result, "]");
254
+ }
255
+ return result;
256
+ }
257
+
258
+ /*
259
+ * call-seq: to_json(state = nil, depth = 0)
260
+ *
261
+ * Returns a JSON string containing a JSON array, that is unparsed from
262
+ * this Array instance.
263
+ * _state_ is a JSON::State object, that can also be used to configure the
264
+ * produced JSON string output further.
265
+ * _depth_ is used to find out nesting depth, to indent accordingly.
266
+ */
267
+ static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
268
+ VALUE Vstate, Vdepth, result;
269
+
270
+ rb_scan_args(argc, argv, "02", &Vstate, &Vdepth);
271
+ if (NIL_P(Vstate)) {
272
+ long i, len = RARRAY_LEN(self);
273
+ result = rb_str_buf_new(2 + 2 * len);
274
+ rb_str_buf_cat2(result, "[");
275
+ OBJ_INFECT(result, self);
276
+ for (i = 0; i < len; i++) {
277
+ VALUE element = RARRAY_PTR(self)[i];
278
+ OBJ_INFECT(result, element);
279
+ if (i > 0) rb_str_buf_cat2(result, ",");
280
+ element = rb_funcall(element, i_to_json, 0);
281
+ Check_Type(element, T_STRING);
282
+ rb_str_buf_append(result, element);
283
+ }
284
+ rb_str_buf_cat2(result, "]");
285
+ } else {
286
+ result = mArray_json_transfrom(self, Vstate, Vdepth);
287
+ }
288
+ OBJ_INFECT(result, self);
289
+ FORCE_UTF8(result);
290
+ return result;
291
+ }
292
+
293
+ /*
294
+ * call-seq: to_json(*)
295
+ *
296
+ * Returns a JSON string representation for this Integer number.
297
+ */
298
+ static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
299
+ {
300
+ VALUE result = rb_funcall(self, i_to_s, 0);
301
+ FORCE_UTF8(result);
302
+ return result;
303
+ }
304
+
305
+ /*
306
+ * call-seq: to_json(*)
307
+ *
308
+ * Returns a JSON string representation for this Float number.
309
+ */
310
+ static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
311
+ {
312
+ JSON_Generator_State *state = NULL;
313
+ VALUE Vstate, rest, tmp, result;
314
+ double value = RFLOAT_VALUE(self);
315
+ rb_scan_args(argc, argv, "01*", &Vstate, &rest);
316
+ if (!NIL_P(Vstate)) Data_Get_Struct(Vstate, JSON_Generator_State, state);
317
+ if (isinf(value)) {
318
+ if (!state || state->allow_nan) {
319
+ result = rb_funcall(self, i_to_s, 0);
320
+ } else {
321
+ tmp = rb_funcall(self, i_to_s, 0);
322
+ rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
323
+ }
324
+ } else if (isnan(value)) {
325
+ if (!state || state->allow_nan) {
326
+ result = rb_funcall(self, i_to_s, 0);
327
+ } else {
328
+ tmp = rb_funcall(self, i_to_s, 0);
329
+ rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
330
+ }
331
+ } else {
332
+ result = rb_funcall(self, i_to_s, 0);
333
+ }
334
+ FORCE_UTF8(result);
335
+ return result;
336
+ }
337
+
338
+ /*
339
+ * call-seq: String.included(modul)
340
+ *
341
+ * Extends _modul_ with the String::Extend module.
342
+ */
343
+ static VALUE mString_included_s(VALUE self, VALUE modul) {
344
+ VALUE result = rb_funcall(modul, i_extend, 1, mString_Extend);
345
+ FORCE_UTF8(result);
346
+ return result;
347
+ }
348
+
349
+ /*
350
+ * call-seq: to_json(*)
351
+ *
352
+ * This string should be encoded with UTF-8 A call to this method
353
+ * returns a JSON string encoded with UTF16 big endian characters as
354
+ * \u????.
355
+ */
356
+ static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
357
+ {
358
+ VALUE result = rb_str_buf_new(RSTRING_LEN(self));
359
+ rb_str_buf_cat2(result, "\"");
360
+ JSON_convert_UTF8_to_JSON(result, self, strictConversion);
361
+ rb_str_buf_cat2(result, "\"");
362
+ FORCE_UTF8(result);
363
+ return result;
364
+ }
365
+
366
+ /*
367
+ * call-seq: to_json_raw_object()
368
+ *
369
+ * This method creates a raw object hash, that can be nested into
370
+ * other data structures and will be unparsed as a raw string. This
371
+ * method should be used, if you want to convert raw strings to JSON
372
+ * instead of UTF-8 strings, e. g. binary data.
373
+ */
374
+ static VALUE mString_to_json_raw_object(VALUE self) {
375
+ VALUE ary;
376
+ VALUE result = rb_hash_new();
377
+ rb_hash_aset(result, rb_funcall(mJSON, i_create_id, 0), rb_class_name(rb_obj_class(self)));
378
+ ary = rb_funcall(self, i_unpack, 1, rb_str_new2("C*"));
379
+ rb_hash_aset(result, rb_str_new2("raw"), ary);
380
+ FORCE_UTF8(result);
381
+ return result;
382
+ }
383
+
384
+ /*
385
+ * call-seq: to_json_raw(*args)
386
+ *
387
+ * This method creates a JSON text from the result of a call to
388
+ * to_json_raw_object of this String.
389
+ */
390
+ static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self) {
391
+ VALUE result, obj = mString_to_json_raw_object(self);
392
+ Check_Type(obj, T_HASH);
393
+ result = mHash_to_json(argc, argv, obj);
394
+ FORCE_UTF8(result);
395
+ return result;
396
+ }
397
+
398
+ /*
399
+ * call-seq: json_create(o)
400
+ *
401
+ * Raw Strings are JSON Objects (the raw bytes are stored in an array for the
402
+ * key "raw"). The Ruby String can be created by this module method.
403
+ */
404
+ static VALUE mString_Extend_json_create(VALUE self, VALUE o) {
405
+ VALUE ary;
406
+ Check_Type(o, T_HASH);
407
+ ary = rb_hash_aref(o, rb_str_new2("raw"));
408
+ return rb_funcall(ary, i_pack, 1, rb_str_new2("C*"));
409
+ }
410
+
411
+ /*
412
+ * call-seq: to_json(state = nil, depth = 0)
413
+ *
414
+ * Returns a JSON string for true: 'true'.
415
+ */
416
+ static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
417
+ {
418
+ VALUE result = rb_str_new2("true");
419
+ FORCE_UTF8(result);
420
+ return result;
421
+ }
422
+
423
+ /*
424
+ * call-seq: to_json(state = nil, depth = 0)
425
+ *
426
+ * Returns a JSON string for false: 'false'.
427
+ */
428
+ static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
429
+ {
430
+ VALUE result = rb_str_new2("false");
431
+ FORCE_UTF8(result);
432
+ return result;
433
+ }
434
+
435
+ /*
436
+ * call-seq: to_json(state = nil, depth = 0)
437
+ *
438
+ */
439
+ static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
440
+ {
441
+ VALUE result = rb_str_new2("null");
442
+ FORCE_UTF8(result);
443
+ return result;
444
+ }
445
+
446
+ /*
447
+ * call-seq: to_json(*)
448
+ *
449
+ * Converts this object to a string (calling #to_s), converts
450
+ * it to a JSON string, and returns the result. This is a fallback, if no
451
+ * special method #to_json was defined for some object.
452
+ */
453
+ static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
454
+ {
455
+ VALUE result, string = rb_funcall(self, i_to_s, 0);
456
+ Check_Type(string, T_STRING);
457
+ result = mString_to_json(argc, argv, string);
458
+ FORCE_UTF8(result);
459
+ return result;
460
+ }
461
+
462
+ /*
463
+ * Document-class: JSON::Ext::Generator::State
464
+ *
465
+ * This class is used to create State instances, that are use to hold data
466
+ * while generating a JSON text from a a Ruby data structure.
467
+ */
468
+
469
+ static void State_mark(JSON_Generator_State *state)
470
+ {
471
+ rb_gc_mark_maybe(state->indent);
472
+ rb_gc_mark_maybe(state->space);
473
+ rb_gc_mark_maybe(state->space_before);
474
+ rb_gc_mark_maybe(state->object_nl);
475
+ rb_gc_mark_maybe(state->array_nl);
476
+ rb_gc_mark_maybe(state->seen);
477
+ rb_gc_mark_maybe(state->memo);
478
+ rb_gc_mark_maybe(state->depth);
479
+ }
480
+
481
+ static JSON_Generator_State *State_allocate()
482
+ {
483
+ JSON_Generator_State *state = ALLOC(JSON_Generator_State);
484
+ return state;
485
+ }
486
+
487
+ static VALUE cState_s_allocate(VALUE klass)
488
+ {
489
+ JSON_Generator_State *state = State_allocate();
490
+ return Data_Wrap_Struct(klass, State_mark, -1, state);
491
+ }
492
+
493
+ /*
494
+ * call-seq: configure(opts)
495
+ *
496
+ * Configure this State instance with the Hash _opts_, and return
497
+ * itself.
498
+ */
499
+ static VALUE cState_configure(VALUE self, VALUE opts)
500
+ {
501
+ VALUE tmp;
502
+ GET_STATE(self);
503
+ tmp = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
504
+ if (NIL_P(tmp)) tmp = rb_convert_type(opts, T_HASH, "Hash", "to_h");
505
+ if (NIL_P(tmp)) {
506
+ rb_raise(rb_eArgError, "opts has to be hash like or convertable into a hash");
507
+ }
508
+ opts = tmp;
509
+ tmp = rb_hash_aref(opts, ID2SYM(i_indent));
510
+ if (RTEST(tmp)) {
511
+ Check_Type(tmp, T_STRING);
512
+ state->indent = tmp;
513
+ }
514
+ tmp = rb_hash_aref(opts, ID2SYM(i_space));
515
+ if (RTEST(tmp)) {
516
+ Check_Type(tmp, T_STRING);
517
+ state->space = tmp;
518
+ }
519
+ tmp = rb_hash_aref(opts, ID2SYM(i_space_before));
520
+ if (RTEST(tmp)) {
521
+ Check_Type(tmp, T_STRING);
522
+ state->space_before = tmp;
523
+ }
524
+ tmp = rb_hash_aref(opts, ID2SYM(i_array_nl));
525
+ if (RTEST(tmp)) {
526
+ Check_Type(tmp, T_STRING);
527
+ state->array_nl = tmp;
528
+ }
529
+ tmp = rb_hash_aref(opts, ID2SYM(i_object_nl));
530
+ if (RTEST(tmp)) {
531
+ Check_Type(tmp, T_STRING);
532
+ state->object_nl = tmp;
533
+ }
534
+ tmp = ID2SYM(i_check_circular);
535
+ if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
536
+ tmp = rb_hash_aref(opts, ID2SYM(i_check_circular));
537
+ state->check_circular = RTEST(tmp);
538
+ } else {
539
+ state->check_circular = 1;
540
+ }
541
+ tmp = ID2SYM(i_max_nesting);
542
+ state->max_nesting = 19;
543
+ if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
544
+ VALUE max_nesting = rb_hash_aref(opts, tmp);
545
+ if (RTEST(max_nesting)) {
546
+ Check_Type(max_nesting, T_FIXNUM);
547
+ state->max_nesting = FIX2LONG(max_nesting);
548
+ } else {
549
+ state->max_nesting = 0;
550
+ }
551
+ }
552
+ tmp = rb_hash_aref(opts, ID2SYM(i_allow_nan));
553
+ state->allow_nan = RTEST(tmp);
554
+ return self;
555
+ }
556
+
557
+ /*
558
+ * call-seq: to_h
559
+ *
560
+ * Returns the configuration instance variables as a hash, that can be
561
+ * passed to the configure method.
562
+ */
563
+ static VALUE cState_to_h(VALUE self)
564
+ {
565
+ VALUE result = rb_hash_new();
566
+ GET_STATE(self);
567
+ rb_hash_aset(result, ID2SYM(i_indent), state->indent);
568
+ rb_hash_aset(result, ID2SYM(i_space), state->space);
569
+ rb_hash_aset(result, ID2SYM(i_space_before), state->space_before);
570
+ rb_hash_aset(result, ID2SYM(i_object_nl), state->object_nl);
571
+ rb_hash_aset(result, ID2SYM(i_array_nl), state->array_nl);
572
+ rb_hash_aset(result, ID2SYM(i_check_circular), state->check_circular ? Qtrue : Qfalse);
573
+ rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse);
574
+ rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting));
575
+ return result;
576
+ }
577
+
578
+
579
+ /*
580
+ * call-seq: new(opts = {})
581
+ *
582
+ * Instantiates a new State object, configured by _opts_.
583
+ *
584
+ * _opts_ can have the following keys:
585
+ *
586
+ * * *indent*: a string used to indent levels (default: ''),
587
+ * * *space*: a string that is put after, a : or , delimiter (default: ''),
588
+ * * *space_before*: a string that is put before a : pair delimiter (default: ''),
589
+ * * *object_nl*: a string that is put at the end of a JSON object (default: ''),
590
+ * * *array_nl*: a string that is put at the end of a JSON array (default: ''),
591
+ * * *check_circular*: true if checking for circular data structures
592
+ * should be done, false (the default) otherwise.
593
+ * * *allow_nan*: true if NaN, Infinity, and -Infinity should be
594
+ * generated, otherwise an exception is thrown, if these values are
595
+ * encountered. This options defaults to false.
596
+ */
597
+ static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
598
+ {
599
+ VALUE opts;
600
+ GET_STATE(self);
601
+
602
+ rb_scan_args(argc, argv, "01", &opts);
603
+ state->indent = rb_str_new2("");
604
+ state->space = rb_str_new2("");
605
+ state->space_before = rb_str_new2("");
606
+ state->array_nl = rb_str_new2("");
607
+ state->object_nl = rb_str_new2("");
608
+ if (NIL_P(opts)) {
609
+ state->check_circular = 1;
610
+ state->allow_nan = 0;
611
+ state->max_nesting = 19;
612
+ } else {
613
+ cState_configure(self, opts);
614
+ }
615
+ state->seen = rb_hash_new();
616
+ state->memo = Qnil;
617
+ state->depth = INT2FIX(0);
618
+ return self;
619
+ }
620
+
621
+ /*
622
+ * call-seq: from_state(opts)
623
+ *
624
+ * Creates a State object from _opts_, which ought to be Hash to create a
625
+ * new State instance configured by _opts_, something else to create an
626
+ * unconfigured instance. If _opts_ is a State object, it is just returned.
627
+ */
628
+ static VALUE cState_from_state_s(VALUE self, VALUE opts)
629
+ {
630
+ if (rb_obj_is_kind_of(opts, self)) {
631
+ return opts;
632
+ } else if (rb_obj_is_kind_of(opts, rb_cHash)) {
633
+ return rb_funcall(self, i_new, 1, opts);
634
+ } else {
635
+ return rb_funcall(self, i_new, 0);
636
+ }
637
+ }
638
+
639
+ /*
640
+ * call-seq: indent()
641
+ *
642
+ * This string is used to indent levels in the JSON text.
643
+ */
644
+ static VALUE cState_indent(VALUE self)
645
+ {
646
+ GET_STATE(self);
647
+ return state->indent;
648
+ }
649
+
650
+ /*
651
+ * call-seq: indent=(indent)
652
+ *
653
+ * This string is used to indent levels in the JSON text.
654
+ */
655
+ static VALUE cState_indent_set(VALUE self, VALUE indent)
656
+ {
657
+ GET_STATE(self);
658
+ Check_Type(indent, T_STRING);
659
+ return state->indent = indent;
660
+ }
661
+
662
+ /*
663
+ * call-seq: space()
664
+ *
665
+ * This string is used to insert a space between the tokens in a JSON
666
+ * string.
667
+ */
668
+ static VALUE cState_space(VALUE self)
669
+ {
670
+ GET_STATE(self);
671
+ return state->space;
672
+ }
673
+
674
+ /*
675
+ * call-seq: space=(space)
676
+ *
677
+ * This string is used to insert a space between the tokens in a JSON
678
+ * string.
679
+ */
680
+ static VALUE cState_space_set(VALUE self, VALUE space)
681
+ {
682
+ GET_STATE(self);
683
+ Check_Type(space, T_STRING);
684
+ return state->space = space;
685
+ }
686
+
687
+ /*
688
+ * call-seq: space_before()
689
+ *
690
+ * This string is used to insert a space before the ':' in JSON objects.
691
+ */
692
+ static VALUE cState_space_before(VALUE self)
693
+ {
694
+ GET_STATE(self);
695
+ return state->space_before;
696
+ }
697
+
698
+ /*
699
+ * call-seq: space_before=(space_before)
700
+ *
701
+ * This string is used to insert a space before the ':' in JSON objects.
702
+ */
703
+ static VALUE cState_space_before_set(VALUE self, VALUE space_before)
704
+ {
705
+ GET_STATE(self);
706
+ Check_Type(space_before, T_STRING);
707
+ return state->space_before = space_before;
708
+ }
709
+
710
+ /*
711
+ * call-seq: object_nl()
712
+ *
713
+ * This string is put at the end of a line that holds a JSON object (or
714
+ * Hash).
715
+ */
716
+ static VALUE cState_object_nl(VALUE self)
717
+ {
718
+ GET_STATE(self);
719
+ return state->object_nl;
720
+ }
721
+
722
+ /*
723
+ * call-seq: object_nl=(object_nl)
724
+ *
725
+ * This string is put at the end of a line that holds a JSON object (or
726
+ * Hash).
727
+ */
728
+ static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
729
+ {
730
+ GET_STATE(self);
731
+ Check_Type(object_nl, T_STRING);
732
+ return state->object_nl = object_nl;
733
+ }
734
+
735
+ /*
736
+ * call-seq: array_nl()
737
+ *
738
+ * This string is put at the end of a line that holds a JSON array.
739
+ */
740
+ static VALUE cState_array_nl(VALUE self)
741
+ {
742
+ GET_STATE(self);
743
+ return state->array_nl;
744
+ }
745
+
746
+ /*
747
+ * call-seq: array_nl=(array_nl)
748
+ *
749
+ * This string is put at the end of a line that holds a JSON array.
750
+ */
751
+ static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
752
+ {
753
+ GET_STATE(self);
754
+ Check_Type(array_nl, T_STRING);
755
+ return state->array_nl = array_nl;
756
+ }
757
+
758
+ /*
759
+ * call-seq: check_circular?
760
+ *
761
+ * Returns true, if circular data structures should be checked,
762
+ * otherwise returns false.
763
+ */
764
+ static VALUE cState_check_circular_p(VALUE self)
765
+ {
766
+ GET_STATE(self);
767
+ return state->check_circular ? Qtrue : Qfalse;
768
+ }
769
+
770
+ /*
771
+ * call-seq: max_nesting
772
+ *
773
+ * This integer returns the maximum level of data structure nesting in
774
+ * the generated JSON, max_nesting = 0 if no maximum is checked.
775
+ */
776
+ static VALUE cState_max_nesting(VALUE self)
777
+ {
778
+ GET_STATE(self);
779
+ return LONG2FIX(state->max_nesting);
780
+ }
781
+
782
+ /*
783
+ * call-seq: max_nesting=(depth)
784
+ *
785
+ * This sets the maximum level of data structure nesting in the generated JSON
786
+ * to the integer depth, max_nesting = 0 if no maximum should be checked.
787
+ */
788
+ static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
789
+ {
790
+ GET_STATE(self);
791
+ Check_Type(depth, T_FIXNUM);
792
+ state->max_nesting = FIX2LONG(depth);
793
+ return Qnil;
794
+ }
795
+
796
+ /*
797
+ * call-seq: allow_nan?
798
+ *
799
+ * Returns true, if NaN, Infinity, and -Infinity should be generated, otherwise
800
+ * returns false.
801
+ */
802
+ static VALUE cState_allow_nan_p(VALUE self)
803
+ {
804
+ GET_STATE(self);
805
+ return state->allow_nan ? Qtrue : Qfalse;
806
+ }
807
+
808
+ /*
809
+ * call-seq: seen?(object)
810
+ *
811
+ * Returns _true_, if _object_ was already seen during this generating run.
812
+ */
813
+ static VALUE cState_seen_p(VALUE self, VALUE object)
814
+ {
815
+ GET_STATE(self);
816
+ return rb_hash_aref(state->seen, rb_obj_id(object));
817
+ }
818
+
819
+ /*
820
+ * call-seq: remember(object)
821
+ *
822
+ * Remember _object_, to find out if it was already encountered (if a cyclic
823
+ * data structure is rendered).
824
+ */
825
+ static VALUE cState_remember(VALUE self, VALUE object)
826
+ {
827
+ GET_STATE(self);
828
+ return rb_hash_aset(state->seen, rb_obj_id(object), Qtrue);
829
+ }
830
+
831
+ /*
832
+ * call-seq: forget(object)
833
+ *
834
+ * Forget _object_ for this generating run.
835
+ */
836
+ static VALUE cState_forget(VALUE self, VALUE object)
837
+ {
838
+ GET_STATE(self);
839
+ return rb_hash_delete(state->seen, rb_obj_id(object));
840
+ }
841
+
842
+ /*
843
+ *
844
+ */
845
+ void Init_generator()
846
+ {
847
+ rb_require("json/common");
848
+ mJSON = rb_define_module("JSON");
849
+ mExt = rb_define_module_under(mJSON, "Ext");
850
+ mGenerator = rb_define_module_under(mExt, "Generator");
851
+ eGeneratorError = rb_path2class("JSON::GeneratorError");
852
+ eCircularDatastructure = rb_path2class("JSON::CircularDatastructure");
853
+ eNestingError = rb_path2class("JSON::NestingError");
854
+ cState = rb_define_class_under(mGenerator, "State", rb_cObject);
855
+ rb_define_alloc_func(cState, cState_s_allocate);
856
+ rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
857
+ rb_define_method(cState, "initialize", cState_initialize, -1);
858
+
859
+ rb_define_method(cState, "indent", cState_indent, 0);
860
+ rb_define_method(cState, "indent=", cState_indent_set, 1);
861
+ rb_define_method(cState, "space", cState_space, 0);
862
+ rb_define_method(cState, "space=", cState_space_set, 1);
863
+ rb_define_method(cState, "space_before", cState_space_before, 0);
864
+ rb_define_method(cState, "space_before=", cState_space_before_set, 1);
865
+ rb_define_method(cState, "object_nl", cState_object_nl, 0);
866
+ rb_define_method(cState, "object_nl=", cState_object_nl_set, 1);
867
+ rb_define_method(cState, "array_nl", cState_array_nl, 0);
868
+ rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
869
+ rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
870
+ rb_define_method(cState, "max_nesting", cState_max_nesting, 0);
871
+ rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1);
872
+ rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0);
873
+ rb_define_method(cState, "seen?", cState_seen_p, 1);
874
+ rb_define_method(cState, "remember", cState_remember, 1);
875
+ rb_define_method(cState, "forget", cState_forget, 1);
876
+ rb_define_method(cState, "configure", cState_configure, 1);
877
+ rb_define_method(cState, "to_h", cState_to_h, 0);
878
+
879
+ mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
880
+ mObject = rb_define_module_under(mGeneratorMethods, "Object");
881
+ rb_define_method(mObject, "to_json", mObject_to_json, -1);
882
+ mHash = rb_define_module_under(mGeneratorMethods, "Hash");
883
+ rb_define_method(mHash, "to_json", mHash_to_json, -1);
884
+ mArray = rb_define_module_under(mGeneratorMethods, "Array");
885
+ rb_define_method(mArray, "to_json", mArray_to_json, -1);
886
+ mInteger = rb_define_module_under(mGeneratorMethods, "Integer");
887
+ rb_define_method(mInteger, "to_json", mInteger_to_json, -1);
888
+ mFloat = rb_define_module_under(mGeneratorMethods, "Float");
889
+ rb_define_method(mFloat, "to_json", mFloat_to_json, -1);
890
+ mString = rb_define_module_under(mGeneratorMethods, "String");
891
+ rb_define_singleton_method(mString, "included", mString_included_s, 1);
892
+ rb_define_method(mString, "to_json", mString_to_json, -1);
893
+ rb_define_method(mString, "to_json_raw", mString_to_json_raw, -1);
894
+ rb_define_method(mString, "to_json_raw_object", mString_to_json_raw_object, 0);
895
+ mString_Extend = rb_define_module_under(mString, "Extend");
896
+ rb_define_method(mString_Extend, "json_create", mString_Extend_json_create, 1);
897
+ mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass");
898
+ rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1);
899
+ mFalseClass = rb_define_module_under(mGeneratorMethods, "FalseClass");
900
+ rb_define_method(mFalseClass, "to_json", mFalseClass_to_json, -1);
901
+ mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass");
902
+ rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1);
903
+
904
+ i_to_s = rb_intern("to_s");
905
+ i_to_json = rb_intern("to_json");
906
+ i_new = rb_intern("new");
907
+ i_indent = rb_intern("indent");
908
+ i_space = rb_intern("space");
909
+ i_space_before = rb_intern("space_before");
910
+ i_object_nl = rb_intern("object_nl");
911
+ i_array_nl = rb_intern("array_nl");
912
+ i_check_circular = rb_intern("check_circular");
913
+ i_max_nesting = rb_intern("max_nesting");
914
+ i_allow_nan = rb_intern("allow_nan");
915
+ i_pack = rb_intern("pack");
916
+ i_unpack = rb_intern("unpack");
917
+ i_create_id = rb_intern("create_id");
918
+ i_extend = rb_intern("extend");
919
+ }