json 2.3.1 → 2.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +22 -0
  3. data/LICENSE +56 -0
  4. data/VERSION +1 -1
  5. data/ext/json/ext/generator/generator.c +60 -11
  6. data/ext/json/ext/generator/generator.h +5 -2
  7. data/ext/json/ext/parser/extconf.rb +25 -0
  8. data/ext/json/ext/parser/parser.c +110 -68
  9. data/ext/json/ext/parser/parser.h +1 -0
  10. data/ext/json/ext/parser/parser.rl +67 -25
  11. data/ext/json/extconf.rb +1 -0
  12. data/json.gemspec +11 -77
  13. data/lib/json.rb +171 -0
  14. data/lib/json/add/complex.rb +0 -1
  15. data/lib/json/add/rational.rb +0 -1
  16. data/lib/json/common.rb +240 -228
  17. data/lib/json/pure/generator.rb +28 -8
  18. data/lib/json/pure/parser.rb +20 -2
  19. data/lib/json/version.rb +1 -1
  20. data/tests/fixtures/fail29.json +1 -0
  21. data/tests/fixtures/fail30.json +1 -0
  22. data/tests/fixtures/fail31.json +1 -0
  23. data/tests/fixtures/fail32.json +1 -0
  24. data/tests/json_addition_test.rb +0 -4
  25. data/tests/json_common_interface_test.rb +43 -0
  26. data/tests/json_fixtures_test.rb +3 -0
  27. data/tests/json_generator_test.rb +16 -38
  28. data/tests/json_parser_test.rb +25 -0
  29. data/tests/lib/core_assertions.rb +763 -0
  30. data/tests/lib/envutil.rb +365 -0
  31. data/tests/lib/find_executable.rb +22 -0
  32. data/tests/lib/helper.rb +4 -0
  33. data/tests/ractor_test.rb +30 -0
  34. data/tests/test_helper.rb +3 -3
  35. metadata +16 -37
  36. data/.gitignore +0 -18
  37. data/.travis.yml +0 -26
  38. data/README-json-jruby.md +0 -33
  39. data/Rakefile +0 -334
  40. data/diagrams/.keep +0 -0
  41. data/install.rb +0 -23
  42. data/java/src/json/ext/ByteListTranscoder.java +0 -166
  43. data/java/src/json/ext/Generator.java +0 -466
  44. data/java/src/json/ext/GeneratorMethods.java +0 -231
  45. data/java/src/json/ext/GeneratorService.java +0 -42
  46. data/java/src/json/ext/GeneratorState.java +0 -490
  47. data/java/src/json/ext/OptionsReader.java +0 -113
  48. data/java/src/json/ext/Parser.java +0 -2362
  49. data/java/src/json/ext/Parser.rl +0 -893
  50. data/java/src/json/ext/ParserService.java +0 -34
  51. data/java/src/json/ext/RuntimeInfo.java +0 -116
  52. data/java/src/json/ext/StringDecoder.java +0 -166
  53. data/java/src/json/ext/StringEncoder.java +0 -111
  54. data/java/src/json/ext/Utils.java +0 -88
  55. data/json-java.gemspec +0 -37
  56. data/json_pure.gemspec +0 -33
  57. data/references/rfc7159.txt +0 -899
  58. data/tools/diff.sh +0 -18
  59. data/tools/fuzz.rb +0 -131
  60. data/tools/server.rb +0 -62
@@ -37,6 +37,7 @@ typedef struct JSON_ParserStruct {
37
37
  int allow_nan;
38
38
  int parsing_name;
39
39
  int symbolize_names;
40
+ int freeze;
40
41
  VALUE object_class;
41
42
  VALUE array_class;
42
43
  VALUE decimal_class;
@@ -89,13 +89,12 @@ static int convert_UTF32_to_UTF8(char *buf, UTF32 ch)
89
89
 
90
90
  static VALUE mJSON, mExt, cParser, eParserError, eNestingError;
91
91
  static VALUE CNaN, CInfinity, CMinusInfinity;
92
- static VALUE cBigDecimal = Qundef;
93
92
 
94
93
  static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
95
94
  i_chr, i_max_nesting, i_allow_nan, i_symbolize_names,
96
95
  i_object_class, i_array_class, i_decimal_class, i_key_p,
97
96
  i_deep_const_get, i_match, i_match_string, i_aset, i_aref,
98
- i_leftshift, i_new, i_BigDecimal;
97
+ i_leftshift, i_new, i_try_convert, i_freeze, i_uminus;
99
98
 
100
99
  %%{
101
100
  machine JSON_common;
@@ -290,6 +289,10 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul
290
289
  %% write init;
291
290
  %% write exec;
292
291
 
292
+ if (json->freeze) {
293
+ OBJ_FREEZE(*result);
294
+ }
295
+
293
296
  if (cs >= JSON_value_first_final) {
294
297
  return p;
295
298
  } else {
@@ -341,19 +344,6 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res
341
344
  ) (^[0-9Ee.\-]? @exit );
342
345
  }%%
343
346
 
344
- static int is_bigdecimal_class(VALUE obj)
345
- {
346
- if (cBigDecimal == Qundef) {
347
- if (rb_const_defined(rb_cObject, i_BigDecimal)) {
348
- cBigDecimal = rb_const_get_at(rb_cObject, i_BigDecimal);
349
- }
350
- else {
351
- return 0;
352
- }
353
- }
354
- return obj == cBigDecimal;
355
- }
356
-
357
347
  static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
358
348
  {
359
349
  int cs = EVIL;
@@ -363,21 +353,46 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
363
353
  %% write exec;
364
354
 
365
355
  if (cs >= JSON_float_first_final) {
356
+ VALUE mod = Qnil;
357
+ ID method_id = 0;
358
+ if (rb_respond_to(json->decimal_class, i_try_convert)) {
359
+ mod = json->decimal_class;
360
+ method_id = i_try_convert;
361
+ } else if (rb_respond_to(json->decimal_class, i_new)) {
362
+ mod = json->decimal_class;
363
+ method_id = i_new;
364
+ } else if (RB_TYPE_P(json->decimal_class, T_CLASS)) {
365
+ VALUE name = rb_class_name(json->decimal_class);
366
+ const char *name_cstr = RSTRING_PTR(name);
367
+ const char *last_colon = strrchr(name_cstr, ':');
368
+ if (last_colon) {
369
+ const char *mod_path_end = last_colon - 1;
370
+ VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr);
371
+ mod = rb_path_to_class(mod_path);
372
+
373
+ const char *method_name_beg = last_colon + 1;
374
+ long before_len = method_name_beg - name_cstr;
375
+ long len = RSTRING_LEN(name) - before_len;
376
+ VALUE method_name = rb_str_substr(name, before_len, len);
377
+ method_id = SYM2ID(rb_str_intern(method_name));
378
+ } else {
379
+ mod = rb_mKernel;
380
+ method_id = SYM2ID(rb_str_intern(name));
381
+ }
382
+ }
383
+
366
384
  long len = p - json->memo;
367
385
  fbuffer_clear(json->fbuffer);
368
386
  fbuffer_append(json->fbuffer, json->memo, len);
369
387
  fbuffer_append_char(json->fbuffer, '\0');
370
- if (NIL_P(json->decimal_class)) {
371
- *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
388
+
389
+ if (method_id) {
390
+ VALUE text = rb_str_new2(FBUFFER_PTR(json->fbuffer));
391
+ *result = rb_funcallv(mod, method_id, 1, &text);
372
392
  } else {
373
- VALUE text;
374
- text = rb_str_new2(FBUFFER_PTR(json->fbuffer));
375
- if (is_bigdecimal_class(json->decimal_class)) {
376
- *result = rb_funcall(Qnil, i_BigDecimal, 1, text);
377
- } else {
378
- *result = rb_funcall(json->decimal_class, i_new, 1, text);
379
- }
393
+ *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
380
394
  }
395
+
381
396
  return p + 1;
382
397
  } else {
383
398
  return NULL;
@@ -573,7 +588,22 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
573
588
  if (json->symbolize_names && json->parsing_name) {
574
589
  *result = rb_str_intern(*result);
575
590
  } else if (RB_TYPE_P(*result, T_STRING)) {
591
+ # if STR_UMINUS_DEDUPE_FROZEN
592
+ if (json->freeze) {
593
+ // Starting from MRI 2.8 it is preferable to freeze the string
594
+ // before deduplication so that it can be interned directly
595
+ // otherwise it would be duplicated first which is wasteful.
596
+ *result = rb_funcall(rb_str_freeze(*result), i_uminus, 0);
597
+ }
598
+ # elif STR_UMINUS_DEDUPE
599
+ if (json->freeze) {
600
+ // MRI 2.5 and older do not deduplicate strings that are already
601
+ // frozen.
602
+ *result = rb_funcall(*result, i_uminus, 0);
603
+ }
604
+ # else
576
605
  rb_str_resize(*result, RSTRING_LEN(*result));
606
+ # endif
577
607
  }
578
608
  if (cs >= JSON_string_first_final) {
579
609
  return p + 1;
@@ -681,6 +711,12 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
681
711
  } else {
682
712
  json->symbolize_names = 0;
683
713
  }
714
+ tmp = ID2SYM(i_freeze);
715
+ if (option_given_p(opts, tmp)) {
716
+ json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
717
+ } else {
718
+ json->freeze = 0;
719
+ }
684
720
  tmp = ID2SYM(i_create_additions);
685
721
  if (option_given_p(opts, tmp)) {
686
722
  json->create_additions = RTEST(rb_hash_aref(opts, tmp));
@@ -843,6 +879,10 @@ static VALUE cParser_source(VALUE self)
843
879
 
844
880
  void Init_parser(void)
845
881
  {
882
+ #ifdef HAVE_RB_EXT_RACTOR_SAFE
883
+ rb_ext_ractor_safe(true);
884
+ #endif
885
+
846
886
  #undef rb_intern
847
887
  rb_require("json/common");
848
888
  mJSON = rb_define_module("JSON");
@@ -885,7 +925,9 @@ void Init_parser(void)
885
925
  i_aref = rb_intern("[]");
886
926
  i_leftshift = rb_intern("<<");
887
927
  i_new = rb_intern("new");
888
- i_BigDecimal = rb_intern("BigDecimal");
928
+ i_try_convert = rb_intern("try_convert");
929
+ i_freeze = rb_intern("freeze");
930
+ i_uminus = rb_intern("-@");
889
931
  }
890
932
 
891
933
  /*
@@ -1,2 +1,3 @@
1
1
  require 'mkmf'
2
+
2
3
  create_makefile('json')
@@ -2,25 +2,23 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "json"
5
- s.version = File.read('VERSION').chomp
5
+ s.version = File.read(File.expand_path('../VERSION', __FILE__)).chomp
6
6
 
7
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
- s.require_paths = ["lib"]
9
- s.authors = ["Florian Frank"]
7
+ s.summary = "JSON Implementation for Ruby"
10
8
  s.description = "This is a JSON implementation as a Ruby extension in C."
9
+ s.licenses = ["Ruby"]
10
+ s.authors = ["Florian Frank"]
11
11
  s.email = "flori@ping.de"
12
+
12
13
  s.extensions = ["ext/json/ext/generator/extconf.rb", "ext/json/ext/parser/extconf.rb", "ext/json/extconf.rb"]
13
14
  s.extra_rdoc_files = ["README.md"]
15
+ s.rdoc_options = ["--title", "JSON implementation for Ruby", "--main", "README.md"]
14
16
  s.files = [
15
- ".gitignore",
16
- ".travis.yml",
17
17
  "CHANGES.md",
18
18
  "Gemfile",
19
- "README-json-jruby.md",
19
+ "LICENSE",
20
20
  "README.md",
21
- "Rakefile",
22
21
  "VERSION",
23
- "diagrams/.keep",
24
22
  "ext/json/ext/fbuffer/fbuffer.h",
25
23
  "ext/json/ext/generator/depend",
26
24
  "ext/json/ext/generator/extconf.rb",
@@ -32,23 +30,7 @@ Gem::Specification.new do |s|
32
30
  "ext/json/ext/parser/parser.h",
33
31
  "ext/json/ext/parser/parser.rl",
34
32
  "ext/json/extconf.rb",
35
- "install.rb",
36
- "java/src/json/ext/ByteListTranscoder.java",
37
- "java/src/json/ext/Generator.java",
38
- "java/src/json/ext/GeneratorMethods.java",
39
- "java/src/json/ext/GeneratorService.java",
40
- "java/src/json/ext/GeneratorState.java",
41
- "java/src/json/ext/OptionsReader.java",
42
- "java/src/json/ext/Parser.java",
43
- "java/src/json/ext/Parser.rl",
44
- "java/src/json/ext/ParserService.java",
45
- "java/src/json/ext/RuntimeInfo.java",
46
- "java/src/json/ext/StringDecoder.java",
47
- "java/src/json/ext/StringEncoder.java",
48
- "java/src/json/ext/Utils.java",
49
- "json-java.gemspec",
50
33
  "json.gemspec",
51
- "json_pure.gemspec",
52
34
  "lib/json.rb",
53
35
  "lib/json/add/bigdecimal.rb",
54
36
  "lib/json/add/complex.rb",
@@ -72,53 +54,7 @@ Gem::Specification.new do |s|
72
54
  "lib/json/pure/generator.rb",
73
55
  "lib/json/pure/parser.rb",
74
56
  "lib/json/version.rb",
75
- "references/rfc7159.txt",
76
- "tests/fixtures/fail10.json",
77
- "tests/fixtures/fail11.json",
78
- "tests/fixtures/fail12.json",
79
- "tests/fixtures/fail13.json",
80
- "tests/fixtures/fail14.json",
81
- "tests/fixtures/fail18.json",
82
- "tests/fixtures/fail19.json",
83
- "tests/fixtures/fail2.json",
84
- "tests/fixtures/fail20.json",
85
- "tests/fixtures/fail21.json",
86
- "tests/fixtures/fail22.json",
87
- "tests/fixtures/fail23.json",
88
- "tests/fixtures/fail24.json",
89
- "tests/fixtures/fail25.json",
90
- "tests/fixtures/fail27.json",
91
- "tests/fixtures/fail28.json",
92
- "tests/fixtures/fail3.json",
93
- "tests/fixtures/fail4.json",
94
- "tests/fixtures/fail5.json",
95
- "tests/fixtures/fail6.json",
96
- "tests/fixtures/fail7.json",
97
- "tests/fixtures/fail8.json",
98
- "tests/fixtures/fail9.json",
99
- "tests/fixtures/obsolete_fail1.json",
100
- "tests/fixtures/pass1.json",
101
- "tests/fixtures/pass15.json",
102
- "tests/fixtures/pass16.json",
103
- "tests/fixtures/pass17.json",
104
- "tests/fixtures/pass2.json",
105
- "tests/fixtures/pass26.json",
106
- "tests/fixtures/pass3.json",
107
- "tests/json_addition_test.rb",
108
- "tests/json_common_interface_test.rb",
109
- "tests/json_encoding_test.rb",
110
- "tests/json_ext_parser_test.rb",
111
- "tests/json_fixtures_test.rb",
112
- "tests/json_generator_test.rb",
113
- "tests/json_generic_object_test.rb",
114
- "tests/json_parser_test.rb",
115
- "tests/json_string_matching_test.rb",
116
- "tests/test_helper.rb",
117
- "tests/test_helper.rb",
118
- "tools/diff.sh",
119
- "tools/fuzz.rb",
120
- "tools/server.rb",
121
- ]
57
+ ] + Dir["tests/**/*"]
122
58
  s.homepage = "http://flori.github.com/json"
123
59
  s.metadata = {
124
60
  'bug_tracker_uri' => 'https://github.com/flori/json/issues',
@@ -128,12 +64,10 @@ Gem::Specification.new do |s|
128
64
  'source_code_uri' => 'https://github.com/flori/json',
129
65
  'wiki_uri' => 'https://github.com/flori/json/wiki'
130
66
  }
131
- s.licenses = ["Ruby"]
132
- s.rdoc_options = ["--title", "JSON implemention for Ruby", "--main", "README.md"]
67
+
133
68
  s.required_ruby_version = Gem::Requirement.new(">= 2.0")
134
- s.summary = "JSON Implementation for Ruby"
135
69
  s.test_files = ["tests/test_helper.rb"]
136
70
 
137
- s.add_development_dependency("rake", [">= 0"])
138
- s.add_development_dependency("test-unit", [">= 2.0", "< 4.0"])
71
+ s.add_development_dependency "rake"
72
+ s.add_development_dependency "test-unit"
139
73
  end
@@ -107,6 +107,89 @@ require 'json/common'
107
107
  # ruby # => nil
108
108
  # ruby.class # => NilClass
109
109
  #
110
+ # ==== Parsing Options
111
+ #
112
+ # ====== Input Options
113
+ #
114
+ # Option +max_nesting+ (\Integer) specifies the maximum nesting depth allowed;
115
+ # defaults to +100+; specify +false+ to disable depth checking.
116
+ #
117
+ # With the default, +false+:
118
+ # source = '[0, [1, [2, [3]]]]'
119
+ # ruby = JSON.parse(source)
120
+ # ruby # => [0, [1, [2, [3]]]]
121
+ # Too deep:
122
+ # # Raises JSON::NestingError (nesting of 2 is too deep):
123
+ # JSON.parse(source, {max_nesting: 1})
124
+ # Bad value:
125
+ # # Raises TypeError (wrong argument type Symbol (expected Fixnum)):
126
+ # JSON.parse(source, {max_nesting: :foo})
127
+ #
128
+ # ---
129
+ #
130
+ # Option +allow_nan+ (boolean) specifies whether to allow
131
+ # NaN, Infinity, and MinusInfinity in +source+;
132
+ # defaults to +false+.
133
+ #
134
+ # With the default, +false+:
135
+ # # Raises JSON::ParserError (225: unexpected token at '[NaN]'):
136
+ # JSON.parse('[NaN]')
137
+ # # Raises JSON::ParserError (232: unexpected token at '[Infinity]'):
138
+ # JSON.parse('[Infinity]')
139
+ # # Raises JSON::ParserError (248: unexpected token at '[-Infinity]'):
140
+ # JSON.parse('[-Infinity]')
141
+ # Allow:
142
+ # source = '[NaN, Infinity, -Infinity]'
143
+ # ruby = JSON.parse(source, {allow_nan: true})
144
+ # ruby # => [NaN, Infinity, -Infinity]
145
+ #
146
+ # ====== Output Options
147
+ #
148
+ # Option +symbolize_names+ (boolean) specifies whether returned \Hash keys
149
+ # should be Symbols;
150
+ # defaults to +false+ (use Strings).
151
+ #
152
+ # With the default, +false+:
153
+ # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
154
+ # ruby = JSON.parse(source)
155
+ # ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
156
+ # Use Symbols:
157
+ # ruby = JSON.parse(source, {symbolize_names: true})
158
+ # ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil}
159
+ #
160
+ # ---
161
+ #
162
+ # Option +object_class+ (\Class) specifies the Ruby class to be used
163
+ # for each \JSON object;
164
+ # defaults to \Hash.
165
+ #
166
+ # With the default, \Hash:
167
+ # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
168
+ # ruby = JSON.parse(source)
169
+ # ruby.class # => Hash
170
+ # Use class \OpenStruct:
171
+ # ruby = JSON.parse(source, {object_class: OpenStruct})
172
+ # ruby # => #<OpenStruct a="foo", b=1.0, c=true, d=false, e=nil>
173
+ #
174
+ # ---
175
+ #
176
+ # Option +array_class+ (\Class) specifies the Ruby class to be used
177
+ # for each \JSON array;
178
+ # defaults to \Array.
179
+ #
180
+ # With the default, \Array:
181
+ # source = '["foo", 1.0, true, false, null]'
182
+ # ruby = JSON.parse(source)
183
+ # ruby.class # => Array
184
+ # Use class \Set:
185
+ # ruby = JSON.parse(source, {array_class: Set})
186
+ # ruby # => #<Set: {"foo", 1.0, true, false, nil}>
187
+ #
188
+ # ---
189
+ #
190
+ # Option +create_additions+ (boolean) specifies whether to use \JSON additions in parsing.
191
+ # See {\JSON Additions}[#module-JSON-label-JSON+Additions].
192
+ #
110
193
  # === Generating \JSON
111
194
  #
112
195
  # To generate a Ruby \String containing \JSON data,
@@ -169,6 +252,94 @@ require 'json/common'
169
252
  # JSON.generate(Complex(0, 0)) # => '"0+0i"'
170
253
  # JSON.generate(Dir.new('.')) # => '"#<Dir>"'
171
254
  #
255
+ # ==== Generating Options
256
+ #
257
+ # ====== Input Options
258
+ #
259
+ # Option +allow_nan+ (boolean) specifies whether
260
+ # +NaN+, +Infinity+, and <tt>-Infinity</tt> may be generated;
261
+ # defaults to +false+.
262
+ #
263
+ # With the default, +false+:
264
+ # # Raises JSON::GeneratorError (920: NaN not allowed in JSON):
265
+ # JSON.generate(JSON::NaN)
266
+ # # Raises JSON::GeneratorError (917: Infinity not allowed in JSON):
267
+ # JSON.generate(JSON::Infinity)
268
+ # # Raises JSON::GeneratorError (917: -Infinity not allowed in JSON):
269
+ # JSON.generate(JSON::MinusInfinity)
270
+ #
271
+ # Allow:
272
+ # ruby = [Float::NaN, Float::Infinity, Float::MinusInfinity]
273
+ # JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,-Infinity]'
274
+ #
275
+ # ---
276
+ #
277
+ # Option +max_nesting+ (\Integer) specifies the maximum nesting depth
278
+ # in +obj+; defaults to +100+.
279
+ #
280
+ # With the default, +100+:
281
+ # obj = [[[[[[0]]]]]]
282
+ # JSON.generate(obj) # => '[[[[[[0]]]]]]'
283
+ #
284
+ # Too deep:
285
+ # # Raises JSON::NestingError (nesting of 2 is too deep):
286
+ # JSON.generate(obj, max_nesting: 2)
287
+ #
288
+ # ====== Output Options
289
+ #
290
+ # The default formatting options generate the most compact
291
+ # \JSON data, all on one line and with no whitespace.
292
+ #
293
+ # You can use these formatting options to generate
294
+ # \JSON data in a more open format, using whitespace.
295
+ # See also JSON.pretty_generate.
296
+ #
297
+ # - Option +array_nl+ (\String) specifies a string (usually a newline)
298
+ # to be inserted after each \JSON array; defaults to the empty \String, <tt>''</tt>.
299
+ # - Option +object_nl+ (\String) specifies a string (usually a newline)
300
+ # to be inserted after each \JSON object; defaults to the empty \String, <tt>''</tt>.
301
+ # - Option +indent+ (\String) specifies the string (usually spaces) to be
302
+ # used for indentation; defaults to the empty \String, <tt>''</tt>;
303
+ # defaults to the empty \String, <tt>''</tt>;
304
+ # has no effect unless options +array_nl+ or +object_nl+ specify newlines.
305
+ # - Option +space+ (\String) specifies a string (usually a space) to be
306
+ # inserted after the colon in each \JSON object's pair;
307
+ # defaults to the empty \String, <tt>''</tt>.
308
+ # - Option +space_before+ (\String) specifies a string (usually a space) to be
309
+ # inserted before the colon in each \JSON object's pair;
310
+ # defaults to the empty \String, <tt>''</tt>.
311
+ #
312
+ # In this example, +obj+ is used first to generate the shortest
313
+ # \JSON data (no whitespace), then again with all formatting options
314
+ # specified:
315
+ #
316
+ # obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
317
+ # json = JSON.generate(obj)
318
+ # puts 'Compact:', json
319
+ # opts = {
320
+ # array_nl: "\n",
321
+ # object_nl: "\n",
322
+ # indent: ' ',
323
+ # space_before: ' ',
324
+ # space: ' '
325
+ # }
326
+ # puts 'Open:', JSON.generate(obj, opts)
327
+ #
328
+ # Output:
329
+ # Compact:
330
+ # {"foo":["bar","baz"],"bat":{"bam":0,"bad":1}}
331
+ # Open:
332
+ # {
333
+ # "foo" : [
334
+ # "bar",
335
+ # "baz"
336
+ # ],
337
+ # "bat" : {
338
+ # "bam" : 0,
339
+ # "bad" : 1
340
+ # }
341
+ # }
342
+ #
172
343
  # == \JSON Additions
173
344
  #
174
345
  # When you "round trip" a non-\String object from Ruby to \JSON and back,