smarter_json 0.5.2

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.

Potentially problematic release.


This version of smarter_json might be problematic. Click here for more details.

data/docs/examples.md ADDED
@@ -0,0 +1,140 @@
1
+
2
+ ### Contents
3
+
4
+ * [Introduction](./_introduction.md)
5
+ * [The Basic Read API](./basic_read_api.md)
6
+ * [The Basic Write API](./basic_write_api.md)
7
+ * [Configuration Options](./options.md)
8
+ * [**Examples**](./examples.md)
9
+
10
+ --------------
11
+
12
+ # Examples
13
+
14
+ **Rescue from `SmarterJSON::Error` (recommended):** SmarterJSON raises only on genuinely unparseable input (an unterminated string, a mismatched bracket), with line and column in the message. Rescuing from `SmarterJSON::Error` lets your application handle bad input gracefully.
15
+
16
+ ---
17
+
18
+ 1. [Read a JSON String](#example-1-read-a-json-string)
19
+ 2. [Read a JSON File](#example-2-read-a-json-file)
20
+ 3. [Implicit Root Object (config-style, no braces)](#example-3-implicit-root-object-config-style-no-braces)
21
+ 4. [Multiple Documents (NDJSON) → Array](#example-4-multiple-documents-ndjson--array)
22
+ 5. [Streaming a Large File with a Block](#example-5-streaming-a-large-file-with-a-block)
23
+ 6. [Symbolize Keys](#example-6-symbolize-keys)
24
+ 7. [Duplicate Keys](#example-7-duplicate-keys)
25
+ 8. [High-Precision Numbers: BigDecimal vs Float](#example-8-high-precision-numbers-bigdecimal-vs-float)
26
+ 9. [Lenient Input: Comments, Trailing Commas, Unquoted Keys](#example-9-lenient-input-comments-trailing-commas-unquoted-keys)
27
+ 10. [Write JSON](#example-10-write-json)
28
+ 11. [Write NDJSON](#example-11-write-ndjson)
29
+ 12. [Round-Trip Read and Write](#example-12-round-trip-read-and-write)
30
+
31
+ ---
32
+
33
+ ### Example 1: Read a JSON String
34
+
35
+ ```ruby
36
+ require "smarter_json"
37
+
38
+ SmarterJSON.process('{"a": 1, "b": [2, 3]}') # => {"a"=>1, "b"=>[2, 3]}
39
+ ```
40
+
41
+ ### Example 2: Read a JSON File
42
+
43
+ ```ruby
44
+ SmarterJSON.process_file("config.json") # => the parsed value
45
+ ```
46
+
47
+ `process_file` opens the file, reads it with the labeled [`encoding:`](./options.md) (default `"UTF-8"`), and parses it.
48
+
49
+ ### Example 3: Implicit Root Object (config-style, no braces)
50
+
51
+ A config file that starts with `key: value` and has no outer `{}` is read as an object:
52
+
53
+ ```ruby
54
+ SmarterJSON.process("host: localhost\nport: 5432") # => {"host"=>"localhost", "port"=>5432}
55
+ ```
56
+
57
+ ### Example 4: Multiple Documents (NDJSON) → Array
58
+
59
+ Plain `process` reads NDJSON / JSONL / concatenated documents with no block and no special method. Zero documents → `nil`, one → its value, two or more → an `Array`:
60
+
61
+ ```ruby
62
+ SmarterJSON.process(%({"id":1}\n{"id":2}\n{"id":3})) # => [{"id"=>1}, {"id"=>2}, {"id"=>3}]
63
+ SmarterJSON.process('{"id":1}') # => {"id"=>1}
64
+ SmarterJSON.process("") # => nil
65
+ ```
66
+
67
+ ### Example 5: Streaming a Large File with a Block
68
+
69
+ For input larger than memory, pass a block. Each document is yielded as it is read; the whole file is never loaded:
70
+
71
+ ```ruby
72
+ SmarterJSON.process_file("events.ndjson") { |event| EventJob.perform_async(event) }
73
+ ```
74
+
75
+ ### Example 6: Symbolize Keys
76
+
77
+ ```ruby
78
+ SmarterJSON.process('{"a": 1, "b": 2}', symbolize_keys: true) # => {:a=>1, :b=>2}
79
+ ```
80
+
81
+ ### Example 7: Duplicate Keys
82
+
83
+ By default the last value wins. Choose `:first_wins` or `:raise` instead:
84
+
85
+ ```ruby
86
+ SmarterJSON.process('{"a":1,"a":2}') # => {"a"=>2} (:last_wins, the default)
87
+ SmarterJSON.process('{"a":1,"a":2}', duplicate_key: :first_wins) # => {"a"=>1}
88
+ SmarterJSON.process('{"a":1,"a":2}', duplicate_key: :raise) # raises SmarterJSON::ParseError
89
+ ```
90
+
91
+ ### Example 8: High-Precision Numbers: BigDecimal vs Float
92
+
93
+ The default `:auto` keeps high-precision decimals as `BigDecimal` (matching Oj). Force `Float` for raw speed when you don't need the precision:
94
+
95
+ ```ruby
96
+ SmarterJSON.process("65.613616999999977") # => BigDecimal (:auto, the default)
97
+ SmarterJSON.process("65.613616999999977", bigdecimal_load: :float) # => 65.613616999999977 (a Float)
98
+ ```
99
+
100
+ ### Example 9: Lenient Input: Comments, Trailing Commas, Unquoted Keys
101
+
102
+ ```ruby
103
+ SmarterJSON.process(<<~JSON)
104
+ {
105
+ host: localhost, # unquoted key, quoteless value, and a trailing comma
106
+ port: 5432,
107
+ /* block comment */
108
+ url: http://example.com
109
+ }
110
+ JSON
111
+ # => {"host"=>"localhost", "port"=>5432, "url"=>"http://example.com"}
112
+ ```
113
+
114
+ A `#`/`//` only starts a comment when preceded by whitespace, so `http://example.com` stays a string rather than being truncated.
115
+
116
+ ### Example 10: Write JSON
117
+
118
+ ```ruby
119
+ SmarterJSON.generate({ "a" => 1, "b" => [2, 3] }) # => '{"a":1,"b":[2,3]}'
120
+ SmarterJSON.generate([1, 2, 3]) # => '[1,2,3]'
121
+ ```
122
+
123
+ ### Example 11: Write NDJSON
124
+
125
+ An Array writes one element per line:
126
+
127
+ ```ruby
128
+ SmarterJSON.generate([{ "id" => 1 }, { "id" => 2 }], format: :ndjson) # => "{\"id\":1}\n{\"id\":2}\n"
129
+ ```
130
+
131
+ ### Example 12: Round-Trip Read and Write
132
+
133
+ ```ruby
134
+ obj = { "a" => 1, "b" => [2, "three", nil, true] }
135
+ SmarterJSON.process(SmarterJSON.generate(obj)) == obj # => true
136
+ ```
137
+
138
+ ---------------
139
+
140
+ PREVIOUS: [Configuration Options](./options.md) | UP: [README](../README.md)
data/docs/options.md ADDED
@@ -0,0 +1,69 @@
1
+
2
+ ### Contents
3
+
4
+ * [Introduction](./_introduction.md)
5
+ * [The Basic Read API](./basic_read_api.md)
6
+ * [The Basic Write API](./basic_write_api.md)
7
+ * [**Configuration Options**](./options.md)
8
+ * [Examples](./examples.md)
9
+
10
+ --------------
11
+
12
+ # Configuration Options
13
+
14
+ ## Reading
15
+
16
+ These options are passed to [`SmarterJSON.process`](./basic_read_api.md) and `SmarterJSON.process_file` as the second argument; anything you set overrides the defaults below.
17
+
18
+ | Option | Default | Explanation |
19
+ |-------------------|--------------|------------------------------------------------------------------------------------------------------------------------|
20
+ | `:symbolize_keys` | `false` | Return object keys as Symbols instead of Strings. |
21
+ | `:duplicate_key` | `:last_wins` | How to handle a key that repeats within one object: `:last_wins`, `:first_wins`, or `:raise`. |
22
+ | `:bigdecimal_load`| `:auto` | `:auto` keeps high-precision decimals as `BigDecimal` (matches Oj); `:float` forces every number to `Float`; `:bigdecimal` forces every decimal to `BigDecimal`. |
23
+ | `:acceleration` | `true` | Use the C extension when it is compiled and loadable; `false` forces the pure-Ruby parser. Both produce identical results. |
24
+ | `:encoding` | `nil` | Labels the input's encoding (e.g. `"UTF-8"`). It does **not** trigger a transcoding pass — see below. |
25
+
26
+ ```ruby
27
+ SmarterJSON.process('{"a": 1}', symbolize_keys: true) # => {:a=>1}
28
+ SmarterJSON.process('{"a":1,"a":2}', duplicate_key: :raise) # raises SmarterJSON::ParseError
29
+ SmarterJSON.process(big_decimal_json, bigdecimal_load: :float) # every number as Float (fastest)
30
+ ```
31
+
32
+ ### A note on `:encoding`
33
+
34
+ `:encoding` labels what the input *is* — it does not transcode. The parser works on the bytes in their native encoding and emits string values with the same encoding tag, the same way `smarter_csv` handles encodings. Bytes that are invalid for the claimed encoding raise `SmarterJSON::EncodingError` (a kind of `SmarterJSON::ParseError`). A UTF-8 BOM is handled automatically; UTF-16 / UTF-32 input is out of scope.
35
+
36
+ ### A note on `:bigdecimal_load`
37
+
38
+ The default `:auto` preserves high-precision numbers as `BigDecimal`, matching Oj's default. That is intrinsically slower than producing `Float` on number-heavy files (e.g. `canada.json`). For raw speed when you don't need the extra precision, pass `bigdecimal_load: :float`.
39
+
40
+ ## Writing
41
+
42
+ These options are passed to [`SmarterJSON.generate`](./basic_write_api.md) as the second argument.
43
+
44
+ | Option | Default | Explanation |
45
+ |------------|---------|-----------------------------------------------------------------------------------------------------------------------------|
46
+ | `:format` | `:json` | `:json` writes standard JSON (Hash → object, Array → array, scalar → scalar). `:ndjson` writes newline-delimited JSON: an Array becomes one element per line, any other value becomes a single line. |
47
+ | `:indent` | `0` | Spaces per nesting level for pretty-printing. `0` (the default) is compact output. Empty objects/arrays stay inline. Not allowed with `:ndjson` (a record must be a single line). |
48
+ | `:sort_keys` | `false` | Emit object keys in sorted order (Symbol keys sorted by their string form). Useful for canonical, diff-friendly output. |
49
+ | `:ascii_only` | `false` | Escape every non-ASCII character as `\uXXXX` (astral characters as a UTF-16 surrogate pair). The default emits raw UTF-8. |
50
+ | `:script_safe` | `false` | Escape the `/` in `</` and the JS line separators U+2028 / U+2029, so output is safe to embed in an HTML `<script>` tag. |
51
+ | `:coerce` | `false` | When `true`, a value that isn't natively supported is converted by its own `as_json` (the result is re-emitted, so the other options still apply) or, failing that, `to_json` (spliced verbatim). When `false` (the default), such a value raises `SmarterJSON::GenerateError`. |
52
+
53
+ Any other `:format` value, a negative/non-Integer `:indent`, or combining `:indent` with `:ndjson`, raises `ArgumentError`.
54
+
55
+ ```ruby
56
+ SmarterJSON.generate([1, 2, 3]) # => "[1,2,3]" (default :json — a single JSON array)
57
+ SmarterJSON.generate([1, 2, 3], format: :ndjson) # => "1\n2\n3\n" (one element per line)
58
+ SmarterJSON.generate({ "a" => 1 }, indent: 2) # => "{\n \"a\": 1\n}" (pretty-printed)
59
+ SmarterJSON.generate({ "b" => 2, "a" => 1 }, sort_keys: true) # => '{"a":1,"b":2}'
60
+ SmarterJSON.generate("café", ascii_only: true) # => '"caf\u00e9"'
61
+ SmarterJSON.generate("</script>", script_safe: true) # => '"<\/script>"'
62
+ SmarterJSON.generate(model, coerce: true) # => uses model.as_json (else model.to_json)
63
+ SmarterJSON.generate(model) # raises SmarterJSON::GenerateError (coerce off)
64
+ SmarterJSON.generate({}, format: :bogus) # raises ArgumentError
65
+ ```
66
+
67
+ ---------------
68
+
69
+ PREVIOUS: [The Basic Write API](./basic_write_api.md) | NEXT: [Examples](./examples.md) | UP: [README](../README.md)
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+ require "rbconfig"
5
+
6
+ # Ruby sometimes ships CFLAGS with "-g -O3"; drop the debug half so the
7
+ # extension is built optimized, not with debug info.
8
+ if RbConfig::MAKEFILE_CONFIG["CFLAGS"].include?("-g -O3")
9
+ RbConfig::MAKEFILE_CONFIG["CFLAGS"] = RbConfig::MAKEFILE_CONFIG["CFLAGS"].sub("-g -O3", "-O3 $(cflags)")
10
+ end
11
+
12
+ optflags = "-O3 -flto -fomit-frame-pointer -DNDEBUG".dup
13
+ # -march=native is skipped on arm64-darwin (Apple clang already targets the host).
14
+ optflags << " -march=native" unless RUBY_PLATFORM.start_with?("arm64-darwin")
15
+ # -fno-semantic-interposition: GCC/Clang only (not MSVC).
16
+ optflags << " -fno-semantic-interposition" unless RUBY_PLATFORM.include?("mswin")
17
+
18
+ CONFIG["optflags"] = optflags
19
+ CONFIG["debugflags"] = ""
20
+
21
+ # rb_enc_interned_str (Ruby 3.0+) lets us intern object keys straight from the
22
+ # input bytes; on older Rubies the C code falls back to a plain new string.
23
+ have_func("rb_enc_interned_str", "ruby.h")
24
+
25
+ # Pre-sized hashes (3.2+) and bulk insert (2.6+) for object building; the C code
26
+ # falls back to rb_hash_new + per-pair aset when these are unavailable.
27
+ have_func("rb_hash_new_capa", "ruby.h")
28
+ have_func("rb_hash_bulk_insert", "ruby.h")
29
+
30
+ create_makefile("smarter_json/smarter_json")