oj 2.18.5 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }