Module: NOSJ
- Defined in:
- lib/nosj.rb,
lib/nosj/json.rb,
lib/nosj/lazy.rb,
lib/nosj/rails.rb,
lib/nosj/version.rb,
lib/nosj/multi_json.rb,
sig/nosj.rbs
Overview
Public interfaces only. The native layer (NOSJ.parse_native and friends) and the JSON drop-in's patched JSON singleton methods are implementation detail and deliberately unsigned.
Defined Under Namespace
Modules: JSONDropIn Classes: Error, GeneratorError, Lazy, MultiJsonAdapter, NestingError, ParserError, PatchError, RailsEncoder
Constant Summary collapse
- VERSION =
The gem version.
"0.3.0"
Class Method Summary collapse
-
.at_pointer(source, pointer, opts = nil) ⇒ Object?
Partial parsing by JSON Pointer (with the standard +~0+/+~1+ escapes).
-
.at_pointer_file(path, pointer, opts = nil) ⇒ Object?
NOSJ.at_pointer against a file: memory-maps it, resolves the pointer, materializes only the matched subtree, and never reads the rest into Ruby.
-
.at_pointers(source, pointers, opts = nil) ⇒ Array<Object, nil>
Batch NOSJ.at_pointer: the pointer-string counterpart of NOSJ.dig_many, resolving the whole set in one pass.
-
.dig(source, *path) ⇒ Object?
Partial parsing, Hash#dig-shaped: extracts one value from a JSON string without materializing the rest of the document.
-
.dig_file(path, *path_elements) ⇒ Object?
NOSJ.dig against a file: the Hash#dig-shaped counterpart of NOSJ.at_pointer_file.
-
.dig_many(source, paths, opts = nil) ⇒ Array<Object, nil>
Batch NOSJ.dig: many paths resolved in ONE pass over the document.
-
.each_line(source, opts = nil) {|arg0| ... } ⇒ Object
NDJSON / JSON Lines: one parsed value per non-blank line.
-
.each_line_file(path, opts = nil) {|value, arg0| ... } ⇒ Enumerator
NOSJ.each_line against a file: the NDJSON stream is walked over a read-only memory map, so the file never becomes a Ruby String.
-
.generate(obj, opts = nil) ⇒ String
Generates JSON, JSON.generate-compatible: identical output bytes, including the gem's exact float formatting.
-
.generate_lines(values, opts = nil) ⇒ String
Generates NDJSON / JSON Lines: one compact document per element, each terminated with a newline, built in a single pass into one buffer.
-
.lazy(source, opts = nil) ⇒ NOSJ::Lazy, Object
Wraps a JSON document for lazy, on-demand access: returns a Lazy node for a container root, or the materialized value for a scalar root.
-
.load_file(path, opts = nil) ⇒ Object
Parses a JSON file, like +JSON.load_file+—except the file is read natively into a reused buffer, so no file-sized Ruby String is ever created (or garbage-collected).
-
.load_lazy_file(path, opts = nil) ⇒ NOSJ::Lazy, Object
Wraps a JSON file as a lazy document (NOSJ.lazy for files): the file is memory-mapped read-only, so beyond one sequential UTF-8 check, pages you never read are never loaded from disk.
-
.merge_patch(json, patch, opts = nil) ⇒ String
RFC 7386 JSON Merge Patch (semantic: parse, merge, generate).
-
.minify(json, opts = nil) ⇒ String
Reformat without building values: parser events pipe straight into the emission kernels.
-
.parse(source, opts = nil) ⇒ Object
Parses a JSON document, JSON.parse-compatible: same values, same option names, same behavior, byte-for-byte.
-
.patch(json, ops, opts = nil) ⇒ String
RFC 6902 JSON Patch over the raw document.
-
.pretty_generate(obj, opts = nil) ⇒ String
Generates human-readable JSON, JSON.pretty_generate-compatible (two-space indent, newlines between elements).
-
.reformat(json, opts = nil) ⇒ String
Reformats a document without building any Ruby values: NOSJ.minify's pipe with formatting.
-
.reformat_file(path, opts = nil) ⇒ String
NOSJ.reformat against a file: the pipe runs over a read-only memory map, so the input document never becomes a Ruby String; only the result does.
-
.splice(json, edits, opts = nil) ⇒ String
Byte-splicing edits: JSON Pointer => replacement value, resolved in one pass; bytes outside the target spans are copied untouched.
-
.stats(source, opts = nil) ⇒ Hash[Symbol, untyped]
Document statistics (byte_size, root, max_depth, values, keys, key_histogram, containers, strings) from one counting-sink pass.
-
.stats_file(path, opts = nil) ⇒ Hash
NOSJ.stats against a file: memory-maps it and runs the counting pass without reading the document into Ruby.
-
.valid?(source, opts = nil) ⇒ Boolean
Validates a document without building any Ruby values: the full parser (tokenizers, string decode, number validation) runs into a null sink, 1.8-2.5x faster than NOSJ.parse.
-
.write_file(path, obj, opts = nil) ⇒ Integer
Generates
objas JSON and writes it topath, streaming the generator's buffer straight to disk—no intermediate Ruby String. -
.write_lines(path, values, opts = nil) ⇒ Integer
NOSJ.generate_lines straight to a file, streaming the generator's buffer to disk like NOSJ.write_file.
Class Method Details
.at_pointer(source, pointer, opts = nil) ⇒ Object?
Partial parsing by JSON Pointer (with the standard +~0+/+~1+ escapes). The matched subtree materializes under the same options as parse.
221 222 223 |
# File 'lib/nosj.rb', line 221 def self.at_pointer(source, pointer, opts = nil) at_pointer_native(source, pointer, opts) end |
.at_pointer_file(path, pointer, opts = nil) ⇒ Object?
at_pointer against a file: memory-maps it, resolves the pointer, materializes only the matched subtree, and never reads the rest into Ruby.
305 306 307 |
# File 'lib/nosj.rb', line 305 def self.at_pointer_file(path, pointer, opts = nil) at_pointer_file_native(path, pointer, opts) end |
.at_pointers(source, pointers, opts = nil) ⇒ Array<Object, nil>
Batch at_pointer: the pointer-string counterpart of dig_many, resolving the whole set in one pass.
237 238 239 |
# File 'lib/nosj.rb', line 237 def self.at_pointers(source, pointers, opts = nil) at_pointers_native(source, pointers, opts) end |
.dig(source, *path) ⇒ Object?
Partial parsing, Hash#dig-shaped: extracts one value from a JSON string without materializing the rest of the document. Skipped content is stepped over at SIMD block speed, so a lookup costs what it skips, not what the document weighs.
180 181 182 |
# File 'lib/nosj.rb', line 180 def self.dig(source, *path) dig_native(source, path) end |
.dig_file(path, *path_elements) ⇒ Object?
dig against a file: the Hash#dig-shaped counterpart of at_pointer_file. Negative indices resolve to nil.
318 319 320 |
# File 'lib/nosj.rb', line 318 def self.dig_file(path, *path_elements) dig_file_native(path, path_elements) end |
.dig_many(source, paths, opts = nil) ⇒ Array<Object, nil>
Batch dig: many paths resolved in ONE pass over the document. The walk descends only into subtrees some path still needs, so a batch costs about as much as its single deepest lookup.
On malformed documents a batch may raise where a single dig would
return nil: one pass scans every byte some path needs.
202 203 204 |
# File 'lib/nosj.rb', line 202 def self.dig_many(source, paths, opts = nil) dig_many_native(source, paths, opts) end |
.each_line(source, opts) ⇒ nil .each_line(source, opts) ⇒ Enumerator[value, nil]
NDJSON / JSON Lines: one parsed value per non-blank line.
500 501 502 503 |
# File 'lib/nosj.rb', line 500 def self.each_line(source, opts = nil, &block) return enum_for(:each_line, source, opts) unless block each_line_native(source, opts, &block) end |
.each_line_file(path, opts) ⇒ nil .each_line_file(path, opts) ⇒ Enumerator[value, nil]
each_line against a file: the NDJSON stream is walked over a read-only memory map, so the file never becomes a Ruby String.
517 518 519 520 |
# File 'lib/nosj.rb', line 517 def self.each_line_file(path, opts = nil, &block) return enum_for(:each_line_file, path, opts) unless block each_line_file_native(path, opts, &block) end |
.generate(obj, opts = nil) ⇒ String
Generates JSON, JSON.generate-compatible: identical output bytes, including the gem's exact float formatting. Implemented natively (no Ruby forwarder frame; the definition lives in the extension).
|
|
# File 'lib/nosj.rb', line 111
|
.generate_lines(values, opts = nil) ⇒ String
Generates NDJSON / JSON Lines: one compact document per element, each terminated with a newline, built in a single pass into one buffer. Formatting options containing newlines raise ArgumentError (they would break the line framing); everything else from generate applies.
536 537 538 |
# File 'lib/nosj.rb', line 536 def self.generate_lines(values, opts = nil) generate_lines_native(lines_array(values), opts) end |
.lazy(source, opts = nil) ⇒ NOSJ::Lazy, Object
Wraps a JSON document for lazy, on-demand access: returns a Lazy node for a container root, or the materialized value for a scalar root. The document bytes are copied once; no parsing happens beyond locating the root value.
162 163 164 |
# File 'lib/nosj/lazy.rb', line 162 def self.lazy(source, opts = nil) lazy_native(source, opts) end |
.load_file(path, opts = nil) ⇒ Object
Parses a JSON file, like +JSON.load_file+—except the file is read natively into a reused buffer, so no file-sized Ruby String is ever created (or garbage-collected).
253 254 255 |
# File 'lib/nosj.rb', line 253 def self.load_file(path, opts = nil) load_file_native(path, opts) end |
.load_lazy_file(path, opts = nil) ⇒ NOSJ::Lazy, Object
Wraps a JSON file as a lazy document (lazy for files): the file is memory-mapped read-only, so beyond one sequential UTF-8 check, pages you never read are never loaded from disk. The mapping lives as long as any node on it; the file must not be modified while it is in use.
289 290 291 |
# File 'lib/nosj.rb', line 289 def self.load_lazy_file(path, opts = nil) load_lazy_file_native(path, opts) end |
.merge_patch(json, patch, opts = nil) ⇒ String
RFC 7386 JSON Merge Patch (semantic: parse, merge, generate).
458 459 460 461 |
# File 'lib/nosj.rb', line 458 def self.merge_patch(json, patch, opts = nil) return generate(patch, opts) unless patch.is_a?(Hash) generate(merge_patch_value(parse(json), patch), opts) end |
.minify(json, opts = nil) ⇒ String
Reformat without building values: parser events pipe straight into the emission kernels. minify is compact; reformat takes pretty: and the generate formatting/escape options.
340 341 342 |
# File 'lib/nosj.rb', line 340 def self.minify(json, opts = nil) reformat_native(json, opts) end |
.parse(source, opts = nil) ⇒ Object
Parses a JSON document, JSON.parse-compatible: same values, same option names, same behavior, byte-for-byte.
The json gem's legacy object-deserialization options
(+object_class+, array_class, decimal_class,
create_additions) are deliberately unsupported and raise
ArgumentError.
107 108 109 |
# File 'lib/nosj.rb', line 107 def self.parse(source, opts = nil) parse_native(source, opts) end |
.patch(json, ops, opts = nil) ⇒ String
RFC 6902 JSON Patch over the raw document.
439 440 441 |
# File 'lib/nosj.rb', line 439 def self.patch(json, ops, opts = nil) patch_native(json, ops, opts) end |
.pretty_generate(obj, opts = nil) ⇒ String
Generates human-readable JSON, JSON.pretty_generate-compatible (two-space indent, newlines between elements). Options override the pretty defaults and are otherwise the same as generate.
138 139 140 141 |
# File 'lib/nosj.rb', line 138 def self.pretty_generate(obj, opts = nil) opts = opts.nil? ? PRETTY_GENERATE_OPTS : PRETTY_GENERATE_OPTS.merge(opts) generate_native(obj, opts) end |
.reformat(json, opts = nil) ⇒ String
Reformats a document without building any Ruby values: minify's
pipe with formatting. pretty: true is a shorthand for
pretty_generate's layout; the individual generate formatting
and escape options (+indent+, space, object_nl, ascii_only,
script_safe, ...) compose with it and win over it.
362 363 364 365 366 367 368 369 |
# File 'lib/nosj.rb', line 362 def self.reformat(json, opts = nil) if opts&.key?(:pretty) pretty = opts[:pretty] opts = opts.except(:pretty) opts = PRETTY_GENERATE_OPTS.merge(opts) if pretty end reformat_native(json, opts) end |
.reformat_file(path, opts = nil) ⇒ String
reformat against a file: the pipe runs over a read-only memory map, so the input document never becomes a Ruby String; only the result does.
384 385 386 387 388 389 390 391 |
# File 'lib/nosj.rb', line 384 def self.reformat_file(path, opts = nil) if opts&.key?(:pretty) pretty = opts[:pretty] opts = opts.except(:pretty) opts = PRETTY_GENERATE_OPTS.merge(opts) if pretty end reformat_file_native(path, opts) end |
.splice(json, edits, opts = nil) ⇒ String
Byte-splicing edits: JSON Pointer => replacement value, resolved in one pass; bytes outside the target spans are copied untouched.
414 415 416 |
# File 'lib/nosj.rb', line 414 def self.splice(json, edits, opts = nil) splice_native(json, edits, opts) end |
.stats(source, opts = nil) ⇒ Hash[Symbol, untyped]
Document statistics (byte_size, root, max_depth, values, keys, key_histogram, containers, strings) from one counting-sink pass.
603 604 605 |
# File 'lib/nosj.rb', line 603 def self.stats(source, opts = nil) stats_native(source, opts) end |
.stats_file(path, opts = nil) ⇒ Hash
stats against a file: memory-maps it and runs the counting pass
without reading the document into Ruby. byte_size is the file
size.
619 620 621 |
# File 'lib/nosj.rb', line 619 def self.stats_file(path, opts = nil) stats_file_native(path, opts) end |
.valid?(source, opts = nil) ⇒ Boolean
Validates a document without building any Ruby values: the full parser (tokenizers, string decode, number validation) runs into a null sink, 1.8-2.5x faster than parse.
Returns true iff NOSJ.parse(source, opts) would
succeed: parse refusals (malformed JSON, wrong encoding, too-deep
nesting) are false, while option and argument-type errors still
raise exactly like parse.
160 161 162 |
# File 'lib/nosj.rb', line 160 def self.valid?(source, opts = nil) valid_native(source, opts) end |
.write_file(path, obj, opts = nil) ⇒ Integer
Generates obj as JSON and writes it to path, streaming the
generator's buffer straight to disk—no intermediate Ruby String.
270 271 272 |
# File 'lib/nosj.rb', line 270 def self.write_file(path, obj, opts = nil) write_file_native(path, obj, opts) end |
.write_lines(path, values, opts = nil) ⇒ Integer
generate_lines straight to a file, streaming the generator's buffer to disk like write_file.
553 554 555 |
# File 'lib/nosj.rb', line 553 def self.write_lines(path, values, opts = nil) write_lines_native(path, lines_array(values), opts) end |