json 2.3.1 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,