oj 2.18.5 → 3.0.0

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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +33 -226
  3. data/ext/oj/circarray.c +0 -25
  4. data/ext/oj/circarray.h +0 -25
  5. data/ext/oj/code.c +227 -0
  6. data/ext/oj/code.h +40 -0
  7. data/ext/oj/compat.c +126 -38
  8. data/ext/oj/custom.c +1097 -0
  9. data/ext/oj/dump.c +658 -2376
  10. data/ext/oj/dump.h +92 -0
  11. data/ext/oj/dump_compat.c +937 -0
  12. data/ext/oj/dump_leaf.c +254 -0
  13. data/ext/oj/dump_object.c +810 -0
  14. data/ext/oj/dump_rails.c +329 -0
  15. data/ext/oj/dump_strict.c +416 -0
  16. data/ext/oj/err.c +0 -25
  17. data/ext/oj/err.h +8 -2
  18. data/ext/oj/fast.c +24 -24
  19. data/ext/oj/mimic_json.c +817 -0
  20. data/ext/oj/mimic_rails.c +806 -0
  21. data/ext/oj/mimic_rails.h +17 -0
  22. data/ext/oj/object.c +18 -72
  23. data/ext/oj/odd.c +0 -25
  24. data/ext/oj/odd.h +2 -27
  25. data/ext/oj/oj.c +655 -1503
  26. data/ext/oj/oj.h +93 -40
  27. data/ext/oj/parse.c +99 -46
  28. data/ext/oj/parse.h +12 -26
  29. data/ext/oj/reader.c +1 -25
  30. data/ext/oj/reader.h +3 -25
  31. data/ext/oj/resolve.c +9 -11
  32. data/ext/oj/resolve.h +2 -2
  33. data/ext/oj/rxclass.c +133 -0
  34. data/ext/oj/rxclass.h +27 -0
  35. data/ext/oj/saj.c +4 -25
  36. data/ext/oj/scp.c +3 -25
  37. data/ext/oj/sparse.c +89 -13
  38. data/ext/oj/stream_writer.c +301 -0
  39. data/ext/oj/strict.c +4 -27
  40. data/ext/oj/string_writer.c +480 -0
  41. data/ext/oj/val_stack.h +6 -2
  42. data/lib/oj.rb +1 -23
  43. data/lib/oj/easy_hash.rb +12 -4
  44. data/lib/oj/json.rb +172 -0
  45. data/lib/oj/mimic.rb +123 -18
  46. data/lib/oj/state.rb +131 -0
  47. data/lib/oj/version.rb +1 -1
  48. data/pages/Advanced.md +22 -0
  49. data/pages/Compatibility.md +25 -0
  50. data/pages/Custom.md +23 -0
  51. data/pages/Encoding.md +65 -0
  52. data/pages/JsonGem.md +79 -0
  53. data/pages/Modes.md +140 -0
  54. data/pages/Options.md +250 -0
  55. data/pages/Rails.md +60 -0
  56. data/pages/Security.md +20 -0
  57. data/test/activesupport4/decoding_test.rb +105 -0
  58. data/test/activesupport4/encoding_test.rb +531 -0
  59. data/test/activesupport4/test_helper.rb +41 -0
  60. data/test/activesupport5/decoding_test.rb +125 -0
  61. data/test/activesupport5/encoding_test.rb +483 -0
  62. data/test/activesupport5/encoding_test_cases.rb +90 -0
  63. data/test/activesupport5/test_helper.rb +50 -0
  64. data/test/activesupport5/time_zone_test_helpers.rb +24 -0
  65. data/test/json_gem/json_addition_test.rb +216 -0
  66. data/test/json_gem/json_common_interface_test.rb +143 -0
  67. data/test/json_gem/json_encoding_test.rb +109 -0
  68. data/test/json_gem/json_ext_parser_test.rb +20 -0
  69. data/test/json_gem/json_fixtures_test.rb +35 -0
  70. data/test/json_gem/json_generator_test.rb +383 -0
  71. data/test/json_gem/json_generic_object_test.rb +90 -0
  72. data/test/json_gem/json_parser_test.rb +470 -0
  73. data/test/json_gem/json_string_matching_test.rb +42 -0
  74. data/test/json_gem/test_helper.rb +18 -0
  75. data/test/perf_compat.rb +30 -28
  76. data/test/perf_object.rb +1 -1
  77. data/test/perf_strict.rb +18 -1
  78. data/test/sample.rb +0 -1
  79. data/test/test_compat.rb +169 -93
  80. data/test/test_custom.rb +355 -0
  81. data/test/test_file.rb +0 -8
  82. data/test/test_null.rb +376 -0
  83. data/test/test_object.rb +268 -3
  84. data/test/test_scp.rb +22 -1
  85. data/test/test_strict.rb +160 -4
  86. data/test/test_various.rb +52 -620
  87. data/test/tests.rb +14 -0
  88. data/test/tests_mimic.rb +14 -0
  89. data/test/tests_mimic_addition.rb +7 -0
  90. metadata +89 -47
  91. data/test/activesupport_datetime_test.rb +0 -23
  92. data/test/bug.rb +0 -51
  93. data/test/bug2.rb +0 -10
  94. data/test/bug3.rb +0 -46
  95. data/test/bug_fast.rb +0 -32
  96. data/test/bug_load.rb +0 -24
  97. data/test/crash.rb +0 -111
  98. data/test/curl/curl_oj.rb +0 -46
  99. data/test/curl/get_oj.rb +0 -24
  100. data/test/curl/just_curl.rb +0 -31
  101. data/test/curl/just_oj.rb +0 -51
  102. data/test/example.rb +0 -11
  103. data/test/foo.rb +0 -24
  104. data/test/io.rb +0 -48
  105. data/test/isolated/test_mimic_rails_datetime.rb +0 -27
  106. data/test/mod.rb +0 -16
  107. data/test/rails.rb +0 -50
  108. data/test/russian.rb +0 -18
  109. data/test/struct.rb +0 -29
  110. data/test/test_serializer.rb +0 -59
  111. data/test/write_timebars.rb +0 -31
@@ -1,31 +1,6 @@
1
1
  /* err.c
2
2
  * Copyright (c) 2011, Peter Ohler
3
3
  * All rights reserved.
4
- *
5
- * Redistribution and use in source and binary forms, with or without
6
- * modification, are permitted provided that the following conditions are met:
7
- *
8
- * - Redistributions of source code must retain the above copyright notice, this
9
- * list of conditions and the following disclaimer.
10
- *
11
- * - Redistributions in binary form must reproduce the above copyright notice,
12
- * this list of conditions and the following disclaimer in the documentation
13
- * and/or other materials provided with the distribution.
14
- *
15
- * - Neither the name of Peter Ohler nor the names of its contributors may be
16
- * used to endorse or promote products derived from this software without
17
- * specific prior written permission.
18
- *
19
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
4
  */
30
5
 
31
6
  #include <stdarg.h>
@@ -32,6 +32,10 @@
32
32
  #define __OJ_ERR_H__
33
33
 
34
34
  #include "ruby.h"
35
+ // Needed to silence 2.4.0 warnings.
36
+ #ifndef NORETURN
37
+ # define NORETURN(x) x
38
+ #endif
35
39
 
36
40
  #define set_error(err, eclas, msg, json, current) _oj_err_set_with_location(err, eclas, msg, json, current, __FILE__, __LINE__)
37
41
 
@@ -44,10 +48,12 @@ extern VALUE oj_parse_error_class;
44
48
 
45
49
  extern void oj_err_set(Err e, VALUE clas, const char *format, ...);
46
50
  extern void _oj_err_set_with_location(Err err, VALUE eclas, const char *msg, const char *json, const char *current, const char* file, int line);
47
- extern void oj_err_raise(Err e);
51
+
52
+ NORETURN(extern void oj_err_raise(Err e));
48
53
 
49
54
  #define raise_error(msg, json, current) _oj_raise_error(msg, json, current, __FILE__, __LINE__)
50
- extern void _oj_raise_error(const char *msg, const char *json, const char *current, const char* file, int line);
55
+
56
+ NORETURN(extern void _oj_raise_error(const char *msg, const char *json, const char *current, const char* file, int line));
51
57
 
52
58
 
53
59
  inline static void
@@ -1099,14 +1099,14 @@ each_value(Doc doc, Leaf leaf) {
1099
1099
 
1100
1100
  // doc functions
1101
1101
 
1102
- /* call-seq: open(json) { |doc| ... } => Object
1102
+ /* @overload open(json) { |doc| ... } => Object
1103
1103
  *
1104
1104
  * Parses a JSON document String and then yields to the provided block if one
1105
1105
  * is given with an instance of the Oj::Doc as the single yield parameter. If
1106
1106
  * a block is not given then an Oj::Doc instance is returned and must be
1107
1107
  * closed with a call to the #close() method when no longer needed.
1108
1108
  *
1109
- * @param [String] json JSON document string
1109
+ * @param [String] json JSON document string
1110
1110
  * @yieldparam [Oj::Doc] doc parsed JSON document
1111
1111
  * @yieldreturn [Object] returns the result of the yield as the result of the method call
1112
1112
  * @example
@@ -1140,14 +1140,14 @@ doc_open(VALUE clas, VALUE str) {
1140
1140
  return obj;
1141
1141
  }
1142
1142
 
1143
- /* call-seq: open_file(filename) { |doc| ... } => Object
1143
+ /* @overload open_file(filename) { |doc| ... } => Object
1144
1144
  *
1145
1145
  * Parses a JSON document from a file and then yields to the provided block if
1146
1146
  * one is given with an instance of the Oj::Doc as the single yield
1147
1147
  * parameter. If a block is not given then an Oj::Doc instance is returned and
1148
1148
  * must be closed with a call to the #close() method when no longer needed.
1149
1149
  *
1150
- * @param [String] filename name of file that contains a JSON document
1150
+ * @param [String] filename name of file that contains a JSON document
1151
1151
  * @yieldparam [Oj::Doc] doc parsed JSON document
1152
1152
  * @yieldreturn [Object] returns the result of the yield as the result of the method call
1153
1153
  * @example
@@ -1223,7 +1223,7 @@ append_key(char *p, const char *key) {
1223
1223
  * @see Oj::Doc.open
1224
1224
  */
1225
1225
 
1226
- /* call-seq: where?() => String
1226
+ /* @overload where?() => String
1227
1227
  *
1228
1228
  * Returns a String that describes the absolute path to the current location
1229
1229
  * in the JSON document.
@@ -1265,7 +1265,7 @@ doc_where(VALUE self) {
1265
1265
  }
1266
1266
  }
1267
1267
 
1268
- /* call-seq: local_key() => String, Fixnum, nil
1268
+ /* @overload local_key() => String, Fixnum, nil
1269
1269
  *
1270
1270
  * Returns the final key to the current location.
1271
1271
  * @example
@@ -1288,7 +1288,7 @@ doc_local_key(VALUE self) {
1288
1288
  return key;
1289
1289
  }
1290
1290
 
1291
- /* call-seq: home() => nil
1291
+ /* @overload home() => nil
1292
1292
  *
1293
1293
  * Moves the document marker or location to the hoot or home position. The
1294
1294
  * same operation can be performed with a Oj::Doc.move('/').
@@ -1305,13 +1305,13 @@ doc_home(VALUE self) {
1305
1305
  return oj_slash_string;
1306
1306
  }
1307
1307
 
1308
- /* call-seq: type(path=nil) => Class
1308
+ /* @overload type(path=nil) => Class
1309
1309
  *
1310
1310
  * Returns the Class of the data value at the location identified by the path
1311
1311
  * or the current location if the path is nil or not provided. This method
1312
1312
  * does not create the Ruby Object at the location specified so the overhead
1313
1313
  * is low.
1314
- * @param [String] path path to the location to get the type of if provided
1314
+ * @param [String] path path to the location to get the type of if provided
1315
1315
  * @example
1316
1316
  * Oj::Doc.open('[1,2]') { |doc| doc.type() } #=> Array
1317
1317
  * Oj::Doc.open('[1,2]') { |doc| doc.type('/1') } #=> Fixnum
@@ -1347,14 +1347,14 @@ doc_type(int argc, VALUE *argv, VALUE self) {
1347
1347
  return type;
1348
1348
  }
1349
1349
 
1350
- /* call-seq: fetch(path=nil) => nil, true, false, Fixnum, Float, String, Array, Hash
1350
+ /* @overload fetch(path=nil) => nil, true, false, Fixnum, Float, String, Array, Hash
1351
1351
  *
1352
1352
  * Returns the value at the location identified by the path or the current
1353
1353
  * location if the path is nil or not provided. This method will create and
1354
1354
  * return an Array or Hash if that is the type of Object at the location
1355
1355
  * specified. This is more expensive than navigating to the leaves of the JSON
1356
1356
  * document.
1357
- * @param [String] path path to the location to get the type of if provided
1357
+ * @param [String] path path to the location to get the type of if provided
1358
1358
  * @example
1359
1359
  * Oj::Doc.open('[1,2]') { |doc| doc.fetch() } #=> [1, 2]
1360
1360
  * Oj::Doc.open('[1,2]') { |doc| doc.fetch('/1') } #=> 1
@@ -1380,12 +1380,12 @@ doc_fetch(int argc, VALUE *argv, VALUE self) {
1380
1380
  return val;
1381
1381
  }
1382
1382
 
1383
- /* call-seq: each_leaf(path=nil) => nil
1383
+ /* @overload each_leaf(path=nil) => nil
1384
1384
  *
1385
1385
  * Yields to the provided block for each leaf node with the identified
1386
1386
  * location of the JSON document as the root. The parameter passed to the
1387
1387
  * block on yield is the Doc instance after moving to the child location.
1388
- * @param [String] path if provided it identified the top of the branch to process the leaves of
1388
+ * @param [String] path if provided it identified the top of the branch to process the leaves of
1389
1389
  * @yieldparam [Doc] Doc at the child location
1390
1390
  * @example
1391
1391
  * Oj::Doc.open('[3,[2,1]]') { |doc|
@@ -1429,11 +1429,11 @@ doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1429
1429
  return Qnil;
1430
1430
  }
1431
1431
 
1432
- /* call-seq: move(path) => nil
1432
+ /* @overload move(path) => nil
1433
1433
  *
1434
1434
  * Moves the document marker to the path specified. The path can an absolute
1435
1435
  * path or a relative path.
1436
- * @param [String] path path to the location to move to
1436
+ * @param [String] path path to the location to move to
1437
1437
  * @example
1438
1438
  * Oj::Doc.open('{"one":[1,2]') { |doc| doc.move('/one/2'); doc.where? } #=> "/one/2"
1439
1439
  */
@@ -1455,13 +1455,13 @@ doc_move(VALUE self, VALUE str) {
1455
1455
  return Qnil;
1456
1456
  }
1457
1457
 
1458
- /* call-seq: each_child(path=nil) { |doc| ... } => nil
1458
+ /* @overload each_child(path=nil) { |doc| ... } => nil
1459
1459
  *
1460
1460
  * Yields to the provided block for each immediate child node with the
1461
1461
  * identified location of the JSON document as the root. The parameter passed
1462
1462
  * to the block on yield is the Doc instance after moving to the child
1463
1463
  * location.
1464
- * @param [String] path if provided it identified the top of the branch to process the chilren of
1464
+ * @param [String] path if provided it identified the top of the branch to process the chilren of
1465
1465
  * @yieldparam [Doc] Doc at the child location
1466
1466
  * @example
1467
1467
  * Oj::Doc.open('[3,[2,1]]') { |doc|
@@ -1515,13 +1515,13 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
1515
1515
  return Qnil;
1516
1516
  }
1517
1517
 
1518
- /* call-seq: each_value(path=nil) { |val| ... } => nil
1518
+ /* @overload each_value(path=nil) { |val| ... } => nil
1519
1519
  *
1520
1520
  * Yields to the provided block for each leaf value in the identified location
1521
1521
  * of the JSON document. The parameter passed to the block on yield is the
1522
1522
  * value of the leaf. Only those leaves below the element specified by the
1523
1523
  * path parameter are processed.
1524
- * @param [String] path if provided it identified the top of the branch to process the leaf values of
1524
+ * @param [String] path if provided it identified the top of the branch to process the leaf values of
1525
1525
  * @yieldparam [Object] val each leaf value
1526
1526
  * @example
1527
1527
  * Oj::Doc.open('[3,[2,1]]') { |doc|
@@ -1556,12 +1556,12 @@ doc_each_value(int argc, VALUE *argv, VALUE self) {
1556
1556
  return Qnil;
1557
1557
  }
1558
1558
 
1559
- /* call-seq: dump(path=nil) => String
1559
+ /* @overload dump(path, filename)
1560
1560
  *
1561
1561
  * Dumps the document or nodes to a new JSON document. It uses the default
1562
1562
  * options for generating the JSON.
1563
- * @param [String] path if provided it identified the top of the branch to dump to JSON
1564
- * @param [String] filename if provided it is the filename to write the output to
1563
+ * @param path [String] if provided it identified the top of the branch to dump to JSON
1564
+ * @param filename [String] if provided it is the filename to write the output to
1565
1565
  * @example
1566
1566
  * Oj::Doc.open('[3,[2,1]]') { |doc|
1567
1567
  * doc.dump('/2')
@@ -1610,7 +1610,7 @@ doc_dump(int argc, VALUE *argv, VALUE self) {
1610
1610
  return Qnil;
1611
1611
  }
1612
1612
 
1613
- /* call-seq: size() => Fixnum
1613
+ /* @overload size() => Fixnum
1614
1614
  *
1615
1615
  * Returns the number of nodes in the JSON document where a node is any one of
1616
1616
  * the basic JSON components.
@@ -1623,7 +1623,7 @@ doc_size(VALUE self) {
1623
1623
  return ULONG2NUM(((Doc)DATA_PTR(self))->size);
1624
1624
  }
1625
1625
 
1626
- /* call-seq: close() => nil
1626
+ /* @overload close() => nil
1627
1627
  *
1628
1628
  * Closes an open document. No further calls to the document will be valid
1629
1629
  * after closing.
@@ -0,0 +1,817 @@
1
+ /* mimic_json.c
2
+ * Copyright (c) 2012, 2017, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #include "oj.h"
7
+ #include "encode.h"
8
+ #include "dump.h"
9
+ #include "parse.h"
10
+
11
+ static VALUE symbolize_names_sym;
12
+
13
+ static const char json_class[] = "json_class";
14
+
15
+ VALUE oj_array_nl_sym;
16
+ VALUE oj_ascii_only_sym;
17
+ VALUE oj_json_generator_error_class;
18
+ VALUE oj_json_parser_error_class;
19
+ VALUE oj_max_nesting_sym;
20
+ VALUE oj_object_nl_sym;
21
+ VALUE oj_space_before_sym;
22
+ VALUE oj_space_sym;
23
+
24
+ static VALUE state_class;
25
+
26
+ // mimic JSON documentation
27
+
28
+ /* Document-module: JSON::Ext
29
+ *
30
+ * The Ext module is a placeholder in the mimic JSON module used for
31
+ * compatibility only.
32
+ */
33
+ /* Document-class: JSON::Ext::Parser
34
+ *
35
+ * The JSON::Ext::Parser is a placeholder in the mimic JSON module used for
36
+ * compatibility only.
37
+ */
38
+ /* Document-class: JSON::Ext::Generator
39
+ *
40
+ * The JSON::Ext::Generator is a placeholder in the mimic JSON module used for
41
+ * compatibility only.
42
+ */
43
+
44
+ /* Document-method: parser=
45
+ * call-seq: parser=(parser)
46
+ *
47
+ * Does nothing other than provide compatibiltiy.
48
+ * - *parser* [_Object_] ignored
49
+ */
50
+ /* Document-method: generator=
51
+ * call-seq: generator=(generator)
52
+ *
53
+ * Does nothing other than provide compatibiltiy.
54
+ * - *generator* [_Object_] ignored
55
+ */
56
+
57
+ VALUE
58
+ oj_get_json_err_class(const char *err_classname) {
59
+ volatile VALUE json_module;
60
+ volatile VALUE clas;
61
+ volatile VALUE json_error_class;
62
+
63
+ if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
64
+ json_module = rb_const_get_at(rb_cObject, rb_intern("JSON"));
65
+ } else {
66
+ json_module = rb_define_module("JSON");
67
+ }
68
+ if (rb_const_defined_at(json_module, rb_intern("JSONError"))) {
69
+ json_error_class = rb_const_get(json_module, rb_intern("JSONError"));
70
+ } else {
71
+ json_error_class = rb_define_class_under(json_module, "JSONError", rb_eStandardError);
72
+ }
73
+ if (0 == strcmp(err_classname, "JSONError")) {
74
+ clas = json_error_class;
75
+ } else {
76
+ if (rb_const_defined_at(json_module, rb_intern(err_classname))) {
77
+ clas = rb_const_get(json_module, rb_intern(err_classname));
78
+ } else {
79
+ clas = rb_define_class_under(json_module, err_classname, json_error_class);
80
+ }
81
+ }
82
+ return clas;
83
+ }
84
+
85
+ void
86
+ oj_parse_mimic_dump_options(VALUE ropts, Options copts) {
87
+ VALUE v;
88
+ size_t len;
89
+
90
+ if (T_HASH != rb_type(ropts)) {
91
+ if (rb_respond_to(ropts, oj_to_hash_id)) {
92
+ ropts = rb_funcall(ropts, oj_to_hash_id, 0);
93
+ } else if (rb_respond_to(ropts, oj_to_h_id)) {
94
+ ropts = rb_funcall(ropts, oj_to_h_id, 0);
95
+ } else {
96
+ rb_raise(rb_eArgError, "options must be a hash.");
97
+ }
98
+ }
99
+ v = rb_hash_lookup(ropts, oj_max_nesting_sym);
100
+ if (Qtrue == v) {
101
+ copts->dump_opts.max_depth = 100;
102
+ } else if (Qfalse == v || Qnil == v) {
103
+ copts->dump_opts.max_depth = MAX_DEPTH;
104
+ } else if (T_FIXNUM == rb_type(v)) {
105
+ copts->dump_opts.max_depth = NUM2INT(v);
106
+ if (0 >= copts->dump_opts.max_depth) {
107
+ copts->dump_opts.max_depth = MAX_DEPTH;
108
+ }
109
+ }
110
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_allow_nan_sym))) {
111
+ copts->dump_opts.nan_dump = (Qtrue == v);
112
+ }
113
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_indent_sym))) {
114
+ rb_check_type(v, T_STRING);
115
+ if (sizeof(copts->dump_opts.indent_str) <= (len = RSTRING_LEN(v))) {
116
+ rb_raise(rb_eArgError, "indent string is limited to %lu characters.", sizeof(copts->dump_opts.indent_str));
117
+ }
118
+ strcpy(copts->dump_opts.indent_str, StringValuePtr(v));
119
+ copts->dump_opts.indent_size = (uint8_t)len;
120
+ copts->dump_opts.use = true;
121
+ }
122
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_space_sym))) {
123
+ rb_check_type(v, T_STRING);
124
+ if (sizeof(copts->dump_opts.after_sep) <= (len = RSTRING_LEN(v))) {
125
+ rb_raise(rb_eArgError, "space string is limited to %lu characters.", sizeof(copts->dump_opts.after_sep));
126
+ }
127
+ strcpy(copts->dump_opts.after_sep, StringValuePtr(v));
128
+ copts->dump_opts.after_size = (uint8_t)len;
129
+ copts->dump_opts.use = true;
130
+ }
131
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_space_before_sym))) {
132
+ rb_check_type(v, T_STRING);
133
+ if (sizeof(copts->dump_opts.before_sep) <= (len = RSTRING_LEN(v))) {
134
+ rb_raise(rb_eArgError, "space_before string is limited to %lu characters.", sizeof(copts->dump_opts.before_sep));
135
+ }
136
+ strcpy(copts->dump_opts.before_sep, StringValuePtr(v));
137
+ copts->dump_opts.before_size = (uint8_t)len;
138
+ copts->dump_opts.use = true;
139
+ }
140
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_object_nl_sym))) {
141
+ rb_check_type(v, T_STRING);
142
+ if (sizeof(copts->dump_opts.hash_nl) <= (len = RSTRING_LEN(v))) {
143
+ rb_raise(rb_eArgError, "object_nl string is limited to %lu characters.", sizeof(copts->dump_opts.hash_nl));
144
+ }
145
+ strcpy(copts->dump_opts.hash_nl, StringValuePtr(v));
146
+ copts->dump_opts.hash_size = (uint8_t)len;
147
+ copts->dump_opts.use = true;
148
+ }
149
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_array_nl_sym))) {
150
+ rb_check_type(v, T_STRING);
151
+ if (sizeof(copts->dump_opts.array_nl) <= (len = RSTRING_LEN(v))) {
152
+ rb_raise(rb_eArgError, "array_nl string is limited to %lu characters.", sizeof(copts->dump_opts.array_nl));
153
+ }
154
+ strcpy(copts->dump_opts.array_nl, StringValuePtr(v));
155
+ copts->dump_opts.array_size = (uint8_t)len;
156
+ copts->dump_opts.use = true;
157
+ }
158
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_ascii_only_sym))) {
159
+ // generate seems to assume anything except nil and false are true.
160
+ if (Qfalse == v) {
161
+ copts->escape_mode = JXEsc; // JSONEsc;
162
+ } else {
163
+ copts->escape_mode = ASCIIEsc;
164
+ }
165
+ }
166
+ }
167
+
168
+ static int
169
+ mimic_limit_arg(VALUE a) {
170
+ if (Qnil == a || T_FIXNUM != rb_type(a)) {
171
+ return -1;
172
+ }
173
+ return NUM2INT(a);
174
+ }
175
+
176
+ /* Document-method: dump
177
+ * call-seq: dump(obj, anIO=nil, limit=nil)
178
+ *
179
+ * Encodes an object as a JSON String.
180
+ *
181
+ * - *obj* [_Object_] object to convert to encode as JSON
182
+ * - *anIO* [_IO_] an IO that allows writing
183
+ * - *limit* [_Fixnum_] ignored
184
+ *
185
+ * Returns [_String_] a JSON string.
186
+ */
187
+ static VALUE
188
+ mimic_dump(int argc, VALUE *argv, VALUE self) {
189
+ char buf[4096];
190
+ struct _Out out;
191
+ struct _Options copts = oj_default_options;
192
+ VALUE rstr;
193
+
194
+ copts.str_rx.head = NULL;
195
+ copts.str_rx.tail = NULL;
196
+ out.buf = buf;
197
+ out.end = buf + sizeof(buf) - 10;
198
+ out.allocated = 0;
199
+ out.caller = CALLER_DUMP;
200
+
201
+ if (No == copts.nilnil && Qnil == *argv) {
202
+ rb_raise(rb_eTypeError, "nil not allowed.");
203
+ }
204
+ copts.dump_opts.max_depth = MAX_DEPTH; // when using dump there is no limit
205
+ out.omit_nil = copts.dump_opts.omit_nil;
206
+ if (2 <= argc) {
207
+ int limit;
208
+
209
+ // The json gem take a more liberal approach to optional
210
+ // arguments. Expected are (obj, anIO=nil, limit=nil) yet the io
211
+ // argument can be left off completely and the 2nd argument is then
212
+ // the limit.
213
+ if (0 <= (limit = mimic_limit_arg(argv[1]))) {
214
+ copts.dump_opts.max_depth = limit;
215
+ }
216
+ if (3 <= argc && 0 <= (limit = mimic_limit_arg(argv[2]))) {
217
+ copts.dump_opts.max_depth = limit;
218
+ }
219
+ }
220
+ oj_dump_obj_to_json(*argv, &copts, &out);
221
+ if (0 == out.buf) {
222
+ rb_raise(rb_eNoMemError, "Not enough memory.");
223
+ }
224
+ rstr = rb_str_new2(out.buf);
225
+ rstr = oj_encode(rstr);
226
+ if (2 <= argc && Qnil != argv[1] && rb_respond_to(argv[1], oj_write_id)) {
227
+ VALUE io = argv[1];
228
+ VALUE args[1];
229
+
230
+ *args = rstr;
231
+ rb_funcall2(io, oj_write_id, 1, args);
232
+ rstr = io;
233
+ }
234
+ if (out.allocated) {
235
+ xfree(out.buf);
236
+ }
237
+ return rstr;
238
+ }
239
+
240
+ // This is the signature for the hash_foreach callback also.
241
+ static int
242
+ mimic_walk(VALUE key, VALUE obj, VALUE proc) {
243
+ switch (rb_type(obj)) {
244
+ case T_HASH:
245
+ rb_hash_foreach(obj, mimic_walk, proc);
246
+ break;
247
+ case T_ARRAY:
248
+ {
249
+ size_t cnt = RARRAY_LEN(obj);
250
+ size_t i;
251
+
252
+ for (i = 0; i < cnt; i++) {
253
+ mimic_walk(Qnil, rb_ary_entry(obj, i), proc);
254
+ }
255
+ break;
256
+ }
257
+ default:
258
+ break;
259
+ }
260
+ if (Qnil == proc) {
261
+ if (rb_block_given_p()) {
262
+ rb_yield(obj);
263
+ }
264
+ } else {
265
+ #if HAS_PROC_WITH_BLOCK
266
+ VALUE args[1];
267
+
268
+ *args = obj;
269
+ rb_proc_call_with_block(proc, 1, args, Qnil);
270
+ #else
271
+ rb_raise(rb_eNotImpError, "Calling a Proc with a block not supported in this version. Use func() {|x| } syntax instead.");
272
+ #endif
273
+ }
274
+ return ST_CONTINUE;
275
+ }
276
+
277
+ /* Document-method: restore
278
+ * call-seq: restore(source, proc=nil)
279
+ *
280
+ * Loads a Ruby Object from a JSON source that can be either a String or an
281
+ * IO. If Proc is given or a block is providedit is called with each nested
282
+ * element of the loaded Object.
283
+ *
284
+ * - *source* [_String_|IO] JSON source
285
+ * - *proc* [_Proc_] to yield to on each element or nil
286
+ *
287
+ * Returns [_Object_] the decoded Object.
288
+ */
289
+
290
+ /* Document-method: load
291
+ * call-seq: load(source, proc=nil)
292
+ *
293
+ * Loads a Ruby Object from a JSON source that can be either a String or an
294
+ * IO. If Proc is given or a block is providedit is called with each nested
295
+ * element of the loaded Object.
296
+ *
297
+ * - *source* [_String_|IO] JSON source
298
+ * - *proc* [_Proc_] to yield to on each element or nil
299
+ *
300
+ * Returns [_Object_] the decode Object.
301
+ */
302
+ static VALUE
303
+ mimic_load(int argc, VALUE *argv, VALUE self) {
304
+ VALUE obj;
305
+ VALUE p = Qnil;
306
+
307
+ obj = oj_compat_parse(argc, argv, self);
308
+ if (2 <= argc) {
309
+ if (rb_cProc == rb_obj_class(argv[1])) {
310
+ p = argv[1];
311
+ } else if (3 <= argc) {
312
+ if (rb_cProc == rb_obj_class(argv[2])) {
313
+ p = argv[2];
314
+ }
315
+ }
316
+ }
317
+ mimic_walk(Qnil, obj, p);
318
+
319
+ return obj;
320
+ }
321
+
322
+ /* Document-method: []
323
+ * call-seq: [](obj, opts={})
324
+ *
325
+ * If the obj argument is a String then it is assumed to be a JSON String and
326
+ * parsed otherwise the obj is encoded as a JSON String.
327
+ *
328
+ * - *obj* [_String_|Hash|Array] object to convert
329
+ * - *opts* [_Hash_] same options as either generate or parse
330
+ *
331
+ * Returns [_Object_]
332
+ */
333
+ static VALUE
334
+ mimic_dump_load(int argc, VALUE *argv, VALUE self) {
335
+ if (1 > argc) {
336
+ rb_raise(rb_eArgError, "wrong number of arguments (0 for 1)");
337
+ } else if (T_STRING == rb_type(*argv)) {
338
+ return mimic_load(argc, argv, self);
339
+ } else {
340
+ return mimic_dump(argc, argv, self);
341
+ }
342
+ return Qnil;
343
+ }
344
+
345
+ static VALUE
346
+ mimic_generate_core(int argc, VALUE *argv, Options copts) {
347
+ char buf[4096];
348
+ struct _Out out;
349
+ VALUE rstr;
350
+
351
+ out.buf = buf;
352
+ out.end = buf + sizeof(buf) - 10;
353
+ out.allocated = 0;
354
+ out.omit_nil = copts->dump_opts.omit_nil;
355
+ out.caller = CALLER_GENERATE;
356
+ // For obj.to_json or generate nan is not allowed but if called from dump
357
+ // it is.
358
+ copts->dump_opts.nan_dump = false;
359
+ copts->mode = CompatMode;
360
+ if (2 == argc && Qnil != argv[1]) {
361
+ oj_parse_mimic_dump_options(argv[1], copts);
362
+ }
363
+ if (No == copts->nilnil && Qnil == *argv) {
364
+ rb_raise(rb_eTypeError, "nil not allowed.");
365
+ }
366
+ oj_dump_obj_to_json(*argv, copts, &out);
367
+ if (0 == out.buf) {
368
+ rb_raise(rb_eNoMemError, "Not enough memory.");
369
+ }
370
+ rstr = rb_str_new2(out.buf);
371
+ rstr = oj_encode(rstr);
372
+ if (out.allocated) {
373
+ xfree(out.buf);
374
+ }
375
+ return rstr;
376
+ }
377
+
378
+ /* Document-method: fast_generate
379
+ * call-seq: fast_generate(obj, opts=nil)
380
+ * Same as generate().
381
+ * @see generate
382
+ */
383
+
384
+ /* Document-method: generate
385
+ * call-seq: generate(obj, opts=nil)
386
+ *
387
+ * Encode obj as a JSON String. The obj argument must be a Hash, Array, or
388
+ * respond to to_h or to_json. Options other than those listed such as
389
+ * +:allow_nan+ or +:max_nesting+ are ignored.
390
+ *
391
+ * - *obj* [_Object_|Hash|Array] object to convert to a JSON String
392
+ * - *opts* [_Hash_] options
393
+ * - - *:indent* [_String_] String to use for indentation.
394
+ * - *:space* [_String_] String placed after a , or : delimiter
395
+ * - *:space_before* [_String_] String placed before a : delimiter
396
+ * - *:object_nl* [_String_] String placed after a JSON object
397
+ * - *:array_nl* [_String_] String placed after a JSON array
398
+ * - *:ascii_only* [_Boolean_] if not nil or false then use only ascii characters in the output. Note JSON.generate does support this even if it is not documented.
399
+ *
400
+ * Returns [_String_] generated JSON.
401
+ */
402
+ VALUE
403
+ oj_mimic_generate(int argc, VALUE *argv, VALUE self) {
404
+ struct _Options copts = oj_default_options;
405
+
406
+ copts.str_rx.head = NULL;
407
+ copts.str_rx.tail = NULL;
408
+
409
+ return mimic_generate_core(argc, argv, &copts);
410
+ }
411
+
412
+ /* Document-method: pretty_generate
413
+ * call-seq: pretty_generate(obj, opts=nil)
414
+ *
415
+ * Same as generate() but with different defaults for the spacing options.
416
+ * @see generate
417
+ *
418
+ * Return [_String_] the generated JSON.
419
+ */
420
+ VALUE
421
+ oj_mimic_pretty_generate(int argc, VALUE *argv, VALUE self) {
422
+ struct _Options copts = oj_default_options;
423
+
424
+ copts.str_rx.head = NULL;
425
+ copts.str_rx.tail = NULL;
426
+ strcpy(copts.dump_opts.indent_str, " ");
427
+ copts.dump_opts.indent_size = (uint8_t)strlen(copts.dump_opts.indent_str);
428
+ strcpy(copts.dump_opts.before_sep, "");
429
+ copts.dump_opts.before_size = (uint8_t)strlen(copts.dump_opts.before_sep);
430
+ strcpy(copts.dump_opts.after_sep, " ");
431
+ copts.dump_opts.after_size = (uint8_t)strlen(copts.dump_opts.after_sep);
432
+ strcpy(copts.dump_opts.hash_nl, "\n");
433
+ copts.dump_opts.hash_size = (uint8_t)strlen(copts.dump_opts.hash_nl);
434
+ strcpy(copts.dump_opts.array_nl, "\n");
435
+ copts.dump_opts.array_size = (uint8_t)strlen(copts.dump_opts.array_nl);
436
+ copts.dump_opts.use = true;
437
+
438
+ return mimic_generate_core(argc, argv, &copts);
439
+ }
440
+
441
+ static VALUE
442
+ mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
443
+ struct _ParseInfo pi;
444
+ VALUE args[1];
445
+
446
+ if (argc < 1) {
447
+ rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
448
+ }
449
+ parse_info_init(&pi);
450
+ oj_set_compat_callbacks(&pi);
451
+ // TBD
452
+ pi.err_class = oj_json_parser_error_class;
453
+ //pi.err_class = Qnil;
454
+
455
+ pi.options = oj_default_options;
456
+ pi.options.auto_define = No;
457
+ pi.options.quirks_mode = Yes;
458
+ pi.options.allow_invalid = No;
459
+ pi.options.empty_string = No;
460
+ pi.options.create_ok = No;
461
+ pi.options.allow_nan = (bang ? Yes : No);
462
+ pi.options.nilnil = No;
463
+ pi.max_depth = 100;
464
+
465
+ if (2 <= argc) {
466
+ VALUE ropts = argv[1];
467
+ VALUE v;
468
+
469
+ if (T_HASH != rb_type(ropts)) {
470
+ rb_raise(rb_eArgError, "options must be a hash.");
471
+ }
472
+ if (Qnil != (v = rb_hash_lookup(ropts, symbolize_names_sym))) {
473
+ pi.options.sym_key = (Qtrue == v) ? Yes : No;
474
+ }
475
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_quirks_mode_sym))) {
476
+ pi.options.quirks_mode = (Qtrue == v) ? Yes : No;
477
+ }
478
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_create_additions_sym))) {
479
+ pi.options.create_ok = (Qtrue == v) ? Yes : No;
480
+ }
481
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_allow_nan_sym))) {
482
+ pi.options.allow_nan = (Qtrue == v) ? Yes : No;
483
+ }
484
+
485
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_hash_class_sym)) {
486
+ if (Qnil == (v = rb_hash_lookup(ropts, oj_hash_class_sym))) {
487
+ pi.options.hash_class = Qnil;
488
+ } else {
489
+ rb_check_type(v, T_CLASS);
490
+ pi.options.hash_class = v;
491
+ }
492
+ }
493
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_object_class_sym)) {
494
+ if (Qnil == (v = rb_hash_lookup(ropts, oj_object_class_sym))) {
495
+ pi.options.hash_class = Qnil;
496
+ } else {
497
+ rb_check_type(v, T_CLASS);
498
+ pi.options.hash_class = v;
499
+ }
500
+ }
501
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_array_class_sym)) {
502
+ if (Qnil == (v = rb_hash_lookup(ropts, oj_array_class_sym))) {
503
+ pi.options.array_class = Qnil;
504
+ } else {
505
+ rb_check_type(v, T_CLASS);
506
+ pi.options.array_class = v;
507
+ }
508
+ }
509
+ v = rb_hash_lookup(ropts, oj_max_nesting_sym);
510
+ if (Qtrue == v) {
511
+ pi.max_depth = 100;
512
+ } else if (Qfalse == v || Qnil == v) {
513
+ pi.max_depth = 0;
514
+ } else if (T_FIXNUM == rb_type(v)) {
515
+ pi.max_depth = NUM2INT(v);
516
+ }
517
+ oj_parse_opt_match_string(&pi.options.str_rx, ropts);
518
+ if (Yes == pi.options.create_ok && Yes == pi.options.sym_key) {
519
+ rb_raise(rb_eArgError, ":symbolize_names and :create_additions can not both be true.");
520
+ }
521
+ }
522
+ *args = *argv;
523
+
524
+ return oj_pi_parse(1, args, &pi, 0, 0, 0);
525
+ }
526
+
527
+ /* Document-method: parse
528
+ * call-seq: parse(source, opts=nil)
529
+ *
530
+ * Parses a JSON String or IO into a Ruby Object. Options other than those
531
+ * listed such as +:allow_nan+ or +:max_nesting+ are ignored. +:object_class+ and
532
+ * +:array_object+ are not supported.
533
+ *
534
+ * - *source* [_String_|IO] source to parse
535
+ * - *opts* [_Hash_] options
536
+ * - *:symbolize* [Boolean] _names flag indicating JSON object keys should be Symbols instead of Strings
537
+ * - *:create_additions* [Boolean] flag indicating a key matching +create_id+ in a JSON object should trigger the creation of Ruby Object
538
+ *
539
+ * Returns [Object]
540
+ * @see create_id=
541
+ */
542
+ VALUE
543
+ oj_mimic_parse(int argc, VALUE *argv, VALUE self) {
544
+ return mimic_parse_core(argc, argv, self, false);
545
+ }
546
+
547
+ /* Document-method: parse!
548
+ * call-seq: parse!(source, opts=nil)
549
+ *
550
+ * Same as parse().
551
+ * @see parse
552
+ */
553
+ static VALUE
554
+ mimic_parse_bang(int argc, VALUE *argv, VALUE self) {
555
+ return mimic_parse_core(argc, argv, self, true);
556
+ }
557
+
558
+ /* Document-method: recurse_proc
559
+ * call-seq: recurse_proc(obj, &proc)
560
+ *
561
+ * Yields to the proc for every element in the obj recursivly.
562
+ *
563
+ * - *obj* [_Hash_|Array] object to walk
564
+ * - *proc* [_Proc_] to yield to on each element
565
+ */
566
+ static VALUE
567
+ mimic_recurse_proc(VALUE self, VALUE obj) {
568
+ rb_need_block();
569
+ mimic_walk(Qnil, obj, Qnil);
570
+
571
+ return Qnil;
572
+ }
573
+
574
+ /* Document-method: create_id=
575
+ * call-seq: create_id=(id)
576
+ *
577
+ * Sets the create_id tag to look for in JSON document. That key triggers the
578
+ * creation of a class with the same name.
579
+ *
580
+ * - *id* [_nil_|String] new create_id
581
+ *
582
+ * Returns [_String_] the id.
583
+ */
584
+ static VALUE
585
+ mimic_set_create_id(VALUE self, VALUE id) {
586
+ Check_Type(id, T_STRING);
587
+
588
+ if (0 != oj_default_options.create_id) {
589
+ if (json_class != oj_default_options.create_id) {
590
+ xfree((char*)oj_default_options.create_id);
591
+ }
592
+ oj_default_options.create_id = 0;
593
+ oj_default_options.create_id_len = 0;
594
+ }
595
+ if (Qnil != id) {
596
+ size_t len = RSTRING_LEN(id) + 1;
597
+
598
+ oj_default_options.create_id = ALLOC_N(char, len);
599
+ strcpy((char*)oj_default_options.create_id, StringValuePtr(id));
600
+ oj_default_options.create_id_len = len - 1;
601
+ }
602
+ return id;
603
+ }
604
+
605
+ /* Document-method: create_id
606
+ * call-seq: create_id()
607
+ *
608
+ * Returns [_String_] the create_id.
609
+ */
610
+ static VALUE
611
+ mimic_create_id(VALUE self) {
612
+ if (0 != oj_default_options.create_id) {
613
+ return oj_encode(rb_str_new_cstr(oj_default_options.create_id));
614
+ }
615
+ return rb_str_new_cstr(json_class);
616
+ }
617
+
618
+ static struct _Options mimic_object_to_json_options = {
619
+ 0, // indent
620
+ No, // circular
621
+ No, // auto_define
622
+ No, // sym_key
623
+ JXEsc, // escape_mode
624
+ CompatMode, // mode
625
+ No, // class_cache
626
+ RubyTime, // time_format
627
+ No, // bigdec_as_num
628
+ FloatDec, // bigdec_load
629
+ No, // to_hash
630
+ No, // to_json
631
+ No, // as_json
632
+ No, // nilnil
633
+ Yes, // empty_string
634
+ Yes, // allow_gc
635
+ Yes, // quirks_mode
636
+ No, // allow_invalid
637
+ No, // create_ok
638
+ No, // allow_nan
639
+ json_class, // create_id
640
+ 10, // create_id_len
641
+ 3, // sec_prec
642
+ 16, // float_prec
643
+ "%0.15g", // float_fmt
644
+ Qnil, // hash_class
645
+ Qnil, // array_class
646
+ { // dump_opts
647
+ false, //use
648
+ "", // indent
649
+ "", // before_sep
650
+ "", // after_sep
651
+ "", // hash_nl
652
+ "", // array_nl
653
+ 0, // indent_size
654
+ 0, // before_size
655
+ 0, // after_size
656
+ 0, // hash_size
657
+ 0, // array_size
658
+ AutoNan,// nan_dump
659
+ false, // omit_nil
660
+ 100, // max_depth
661
+ },
662
+ { // str_rx
663
+ NULL, // head
664
+ NULL, // tail
665
+ { '\0' }, // err
666
+ }
667
+ };
668
+
669
+ static VALUE
670
+ mimic_object_to_json(int argc, VALUE *argv, VALUE self) {
671
+ char buf[4096];
672
+ struct _Out out;
673
+ VALUE rstr;
674
+ struct _Options copts = oj_default_options;
675
+
676
+ copts.str_rx.head = NULL;
677
+ copts.str_rx.tail = NULL;
678
+ out.buf = buf;
679
+ out.end = buf + sizeof(buf) - 10;
680
+ out.allocated = 0;
681
+ out.omit_nil = copts.dump_opts.omit_nil;
682
+ copts.mode = CompatMode;
683
+ copts.to_json = No;
684
+ if (1 <= argc && Qnil != argv[0]) {
685
+ oj_parse_mimic_dump_options(argv[0], &copts);
686
+ }
687
+ // To be strict the mimic_object_to_json_options should be used but people
688
+ // seem to prefer the option of changing that.
689
+ //oj_dump_obj_to_json(self, &mimic_object_to_json_options, &out);
690
+ oj_dump_obj_to_json_using_params(self, &copts, &out, argc, argv);
691
+ if (0 == out.buf) {
692
+ rb_raise(rb_eNoMemError, "Not enough memory.");
693
+ }
694
+ rstr = rb_str_new2(out.buf);
695
+ rstr = oj_encode(rstr);
696
+ if (out.allocated) {
697
+ xfree(out.buf);
698
+ }
699
+ return rstr;
700
+ }
701
+
702
+ /* Document-method: state
703
+ * call-seq: state()
704
+ *
705
+ * Returns [_JSON::State_] the JSON::State class.
706
+ */
707
+ static VALUE
708
+ mimic_state(VALUE self) {
709
+ return state_class;
710
+ }
711
+
712
+ /* Document-module: JSON
713
+ *
714
+ * A mimic of the json gem module.
715
+ */
716
+ VALUE
717
+ oj_define_mimic_json(int argc, VALUE *argv, VALUE self) {
718
+ VALUE ext;
719
+ VALUE dummy;
720
+ VALUE verbose;
721
+ VALUE json_error;
722
+ VALUE json;
723
+ VALUE generator;
724
+
725
+ // Either set the paths to indicate JSON has been loaded or replaces the
726
+ // methods if it has been loaded.
727
+ if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
728
+ json = rb_const_get_at(rb_cObject, rb_intern("JSON"));
729
+ } else {
730
+ json = rb_define_module("JSON");
731
+ }
732
+ verbose = rb_gv_get("$VERBOSE");
733
+ rb_gv_set("$VERBOSE", Qfalse);
734
+ rb_define_module_function(rb_cObject, "JSON", mimic_dump_load, -1);
735
+ dummy = rb_gv_get("$LOADED_FEATURES");
736
+ if (rb_type(dummy) == T_ARRAY) {
737
+ rb_ary_push(dummy, rb_str_new2("json"));
738
+ if (0 < argc) {
739
+ VALUE mimic_args[1];
740
+
741
+ *mimic_args = *argv;
742
+ rb_funcall2(Oj, rb_intern("mimic_loaded"), 1, mimic_args);
743
+ } else {
744
+ rb_funcall2(Oj, rb_intern("mimic_loaded"), 0, 0);
745
+ }
746
+ }
747
+ if (rb_const_defined_at(json, rb_intern("Ext"))) {
748
+ ext = rb_const_get_at(json, rb_intern("Ext"));
749
+ } else {
750
+ ext = rb_define_module_under(json, "Ext");
751
+ }
752
+ if (rb_const_defined_at(ext, rb_intern("Generator"))) {
753
+ generator = rb_const_get_at(ext, rb_intern("Generator"));
754
+ } else {
755
+ generator = rb_define_module_under(ext, "Generator");
756
+ }
757
+
758
+ // convince Ruby that the json gem has already been loaded
759
+ // Pull in the JSON::State mimic file.
760
+ rb_require("oj/state");
761
+ state_class = rb_const_get_at(generator, rb_intern("State"));
762
+ // TBD create all modules in mimic_loaded
763
+
764
+ rb_define_module_function(json, "create_id=", mimic_set_create_id, 1);
765
+ rb_define_module_function(json, "create_id", mimic_create_id, 0);
766
+
767
+ rb_define_module_function(json, "dump", mimic_dump, -1);
768
+ rb_define_module_function(json, "load", mimic_load, -1);
769
+ rb_define_module_function(json, "restore", mimic_load, -1);
770
+ rb_define_module_function(json, "recurse_proc", mimic_recurse_proc, 1);
771
+ rb_define_module_function(json, "[]", mimic_dump_load, -1);
772
+
773
+ rb_define_module_function(json, "generate", oj_mimic_generate, -1);
774
+ rb_define_module_function(json, "fast_generate", oj_mimic_generate, -1);
775
+ rb_define_module_function(json, "pretty_generate", oj_mimic_pretty_generate, -1);
776
+ // For older versions of JSON, the deprecated unparse methods.
777
+ rb_define_module_function(json, "unparse", oj_mimic_generate, -1);
778
+ rb_define_module_function(json, "fast_unparse", oj_mimic_generate, -1);
779
+ rb_define_module_function(json, "pretty_unparse", oj_mimic_pretty_generate, -1);
780
+
781
+ rb_define_module_function(json, "parse", oj_mimic_parse, -1);
782
+ rb_define_module_function(json, "parse!", mimic_parse_bang, -1);
783
+
784
+ rb_define_module_function(json, "state", mimic_state, 0);
785
+
786
+ rb_define_method(rb_cObject, "to_json", mimic_object_to_json, -1);
787
+
788
+ rb_gv_set("$VERBOSE", verbose);
789
+
790
+ symbolize_names_sym = ID2SYM(rb_intern("symbolize_names")); rb_gc_register_address(&symbolize_names_sym);
791
+
792
+ if (rb_const_defined_at(json, rb_intern("JSONError"))) {
793
+ json_error = rb_const_get(json, rb_intern("JSONError"));
794
+ } else {
795
+ json_error = rb_define_class_under(json, "JSONError", rb_eStandardError);
796
+ }
797
+ if (rb_const_defined_at(json, rb_intern("ParserError"))) {
798
+ oj_json_parser_error_class = rb_const_get(json, rb_intern("ParserError"));
799
+ } else {
800
+ oj_json_parser_error_class = rb_define_class_under(json, "ParserError", json_error);
801
+ }
802
+ if (rb_const_defined_at(json, rb_intern("GeneratorError"))) {
803
+ oj_json_generator_error_class = rb_const_get(json, rb_intern("GeneratorError"));
804
+ } else {
805
+ oj_json_generator_error_class = rb_define_class_under(json, "GeneratorError", json_error);
806
+ }
807
+ if (rb_const_defined_at(json, rb_intern("NestingError"))) {
808
+ rb_const_get(json, rb_intern("NestingError"));
809
+ } else {
810
+ rb_define_class_under(json, "NestingError", json_error);
811
+ }
812
+
813
+ oj_default_options = mimic_object_to_json_options;
814
+ oj_default_options.to_json = Yes;
815
+
816
+ return json;
817
+ }