Add bin and edit workflow
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s

This commit is contained in:
2026-09-16 13:11:16 -06:00
parent c8ac4fcae5
commit 4cee170d66
17576 changed files with 895740 additions and 2 deletions
+22
View File
@@ -0,0 +1,22 @@
Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
+590
View File
@@ -0,0 +1,590 @@
# Changes
### 2025-03-12 (2.10.2)
* Fix a potential crash in the C extension parser.
* Raise a ParserError on all incomplete unicode escape sequence. This was the behavior until `2.10.0` unadvertently changed it.
* Ensure document snippets that are included in parser errors don't include truncated multibyte characters.
### 2025-02-10 (2.10.1)
* Fix a compatibility issue with `MultiJson.dump(obj, pretty: true)`: `no implicit conversion of false into Proc (TypeError)`.
### 2025-02-10 (2.10.0)
* `strict: true` now accept symbols as values. Previously they'd only be accepted as hash keys.
* The C extension Parser has been entirely reimplemented from scratch.
* Introduced `JSON::Coder` as a new API allowing to customize how non native types are serialized in a non-global way.
* Introduced `JSON::Fragment` to allow assembling cached fragments in a safe way.
* The Java implementation of the generator received many optimizations.
### 2024-12-18 (2.9.1)
* Fix support for Solaris 10.
### 2024-12-03 (2.9.0)
* Fix C implementation of `script_safe` escaping to not confuse some other 3 wide characters with `\u2028` and `\u2029`.
e.g. `JSON.generate(["倩", "瀨"], script_safe: true)` would generate the wrong JSON.
* `JSON.dump(object, some_io)` now write into the IO in chunks while previously it would buffer the entire JSON before writing.
* `JSON::GeneratorError` now has a `#invalid_object` attribute, making it easier to understand why an object tree cannot be serialized.
* Numerous improvements to the JRuby extension.
### 2024-11-14 (2.8.2)
* `JSON.load_file` explictly read the file as UTF-8.
### 2024-11-06 (2.8.1)
* Fix the java packages to include the extension.
### 2024-11-06 (2.8.0)
* Emit a deprecation warning when `JSON.load` create custom types without the `create_additions` option being explictly enabled.
* Prefer to use `JSON.unsafe_load(string)` or `JSON.load(string, create_additions: true)`.
* Emit a deprecation warning when serializing valid UTF-8 strings encoded in `ASCII_8BIT` aka `BINARY`.
* Bump required Ruby version to 2.7.
* Add support for optionally parsing trailing commas, via `allow_trailing_comma: true`, which in cunjunction with the
pre-existing support for comments, make it suitable to parse `jsonc` documents.
* Many performance improvements to `JSON.parse` and `JSON.load`, up to `1.7x` faster on real world documents.
* Some minor performance improvements to `JSON.dump` and `JSON.generate`.
* `JSON.pretty_generate` no longer include newline inside empty object and arrays.
### 2024-11-04 (2.7.6)
* Fix a regression in JSON.generate when dealing with Hash keys that are string subclasses, call `to_json` on them.
### 2024-10-25 (2.7.5)
* Fix a memory leak when `#to_json` methods raise an exception.
* Gracefully handle formatting configs being set to `nil` instead of `""`.
* Workaround another issue caused by conflicting versions of both `json_pure` and `json` being loaded.
### 2024-10-25 (2.7.4)
* Workaround a bug in 3.4.8 and older https://github.com/rubygems/rubygems/pull/6490.
This bug would cause some gems with native extension to fail during compilation.
* Workaround different versions of `json` and `json_pure` being loaded (not officially supported).
* Make `json_pure` Ractor compatible.
### 2024-10-24 (2.7.3)
* Numerous performance optimizations in `JSON.generate` and `JSON.dump` (up to 2 times faster).
* Limit the size of ParserError exception messages, only include up to 32 bytes of the unparseable source.
* Fix json-pure's `Object#to_json` to accept non state arguments
* Fix multiline comment support in `json-pure`.
* Fix `JSON.parse` to no longer mutate the argument encoding when passed an ASCII-8BIT string.
* Fix `String#to_json` to raise on invalid encoding in `json-pure`.
* Delete code that was based on CVTUTF.
* Use the pure-Ruby generator on TruffleRuby.
* Fix `strict` mode in `json-pure` to not break on Integer.
### 2024-04-04 (2.7.2)
* Use rb_sym2str instead of SYM2ID #561
* Fix memory leak when exception is raised during JSON generation #574
* Remove references to "19" methods in JRuby #576
* Make OpenStruct support as optional by @hsbt in #565
* Autoload JSON::GenericObject to avoid require ostruct warning in Ruby 3.4 #577
* Warn to install ostruct if json couldn't load it by @hsbt #578
### 2023-12-05 (2.7.1)
* JSON.dump: handle unenclosed hashes regression #554
* Overload kwargs in JSON.dump #556
* [DOC] RDoc for additions #557
* Fix JSON.dump overload combination #558
### 2023-12-01 (2.7.0)
* Add a strict option to Generator #519
* `escape_slash` option was renamed as `script_safe` and now also escape U+2028 and U+2029. `escape_slash` is now an alias of `script_safe` #525
* Remove unnecessary initialization of create_id in JSON.parse() #454
* Improvements to Hash#to_json in pure implementation generator #203
* Use ruby_xfree to free buffers #518
* Fix "unexpected token" offset for Infinity #507
* Avoid using deprecated BigDecimal.new on JRuby #546
* Removed code for Ruby 1.8 #540
* Rename JSON::ParseError to JSON:ParserError #530
* Call super in included hook #486
* JRuby requires a minimum of Java 8 #516
* Always indent even if empty #517
### 2022-11-30 (2.6.3)
* bugfix json/pure mixing escaped with literal unicode raises Encoding::CompatibilityError #483
* Stop including the parser source __LINE__ in exceptions #470
### 2022-11-17 (2.6.2)
* Remove unknown keyword arg from DateTime.parse #488
* Ignore java artifacts by @hsbt #489
* Fix parser bug for empty string allocation #496
### 2021-10-24 (2.6.1)
* Restore version.rb with 2.6.1
### 2021-10-14 (2.6.0)
* Use `rb_enc_interned_str` if available to reduce allocations in `freeze: true` mode. #451.
* Bump required_ruby_version to 2.3.
* Fix compatibility with `GC.compact`.
* Fix some compilation warnings. #469
## 2020-12-22 (2.5.1)
* Restore the compatibility for constants of JSON class.
## 2020-12-22 (2.5.0)
* Ready to Ractor-safe at Ruby 3.0.
## 2020-12-17 (2.4.1)
* Restore version.rb with 2.4.1
## 2020-12-15 (2.4.0)
* Implement a freeze: parser option #447
* Fix an issue with generate_pretty and empty objects in the Ruby and Java implementations #449
* Fix JSON.load_file doc #448
* Fix pure parser with unclosed arrays / objects #425
* bundle the LICENSE file in the gem #444
* Add an option to escape forward slash character #405
* RDoc for JSON #439 #446 #442 #434 #433 #430
## 2020-06-30 (2.3.1)
* Spelling and grammar fixes for comments. Pull request #191 by Josh
Kline.
* Enhance generic JSON and #generate docs. Pull request #347 by Victor
Shepelev.
* Add :nodoc: for GeneratorMethods. Pull request #349 by Victor Shepelev.
* Baseline changes to help (JRuby) development. Pull request #371 by Karol
Bucek.
* Add metadata for rubygems.org. Pull request #379 by Alexandre ZANNI.
* Remove invalid JSON.generate description from JSON module rdoc. Pull
request #384 by Jeremy Evans.
* Test with TruffleRuby in CI. Pull request #402 by Benoit Daloze.
* Rdoc enhancements. Pull request #413 by Burdette Lamar.
* Fixtures/ are not being tested... Pull request #416 by Marc-André
Lafortune.
* Use frozen string for hash key. Pull request #420 by Marc-André
Lafortune.
* Added :call-seq: to RDoc for some methods. Pull request #422 by Burdette
Lamar.
* Small typo fix. Pull request #423 by Marc-André Lafortune.
## 2019-12-11 (2.3.0)
* Fix default of `create_additions` to always be `false` for `JSON(user_input)`
and `JSON.parse(user_input, nil)`.
Note that `JSON.load` remains with default `true` and is meant for internal
serialization of trusted data. [CVE-2020-10663]
* Fix passing args all #to_json in json/add/*.
* Fix encoding issues
* Fix issues of keyword vs positional parameter
* Fix JSON::Parser against bigdecimal updates
* Bug fixes to JRuby port
## 2019-02-21 (2.2.0)
* Adds support for 2.6 BigDecimal and ruby standard library Set datetype.
## 2017-04-18 (2.1.0)
* Allow passing of `decimal_class` option to specify a class as which to parse
JSON float numbers.
## 2017-03-23 (2.0.4)
* Raise exception for incomplete unicode surrogates/character escape
sequences. This problem was reported by Daniel Gollahon (dgollahon).
* Fix arbitrary heap exposure problem. This problem was reported by Ahmad
Sherif (ahmadsherif).
## 2017-01-12 (2.0.3)
* Set `required_ruby_version` to 1.9
* Some small fixes
## 2016-07-26 (2.0.2)
* Specify `required_ruby_version` for json\_pure.
* Fix issue #295 failure when parsing frozen strings.
## 2016-07-01 (2.0.1)
* Fix problem when requiring json\_pure and Parser constant was defined top
level.
* Add `RB_GC_GUARD` to avoid possible GC problem via Pete Johns.
* Store `current_nesting` on stack by Aaron Patterson.
## 2015-09-11 (2.0.0)
* Now complies to newest JSON RFC 7159.
* Implements compatibility to ruby 2.4 integer unification.
* Drops support for old rubies whose life has ended, that is rubies < 2.0.
Also see https://www.ruby-lang.org/en/news/2014/07/01/eol-for-1-8-7-and-1-9-2/
* There were still some mentions of dual GPL licensing in the source, but JSON
has just the Ruby license that itself includes an explicit dual-licensing
clause that allows covered software to be distributed under the terms of
the Simplified BSD License instead for all ruby versions >= 1.9.3. This is
however a GPL compatible license according to the Free Software Foundation.
I changed these mentions to be consistent with the Ruby license setting in
the gemspec files which were already correct now.
## 2017-01-13 (1.8.6)
* Be compatible with ancient ruby 1.8 (maybe?)
## 2015-09-11 (1.8.5)
* Be compatible with ruby 2.4.0
* There were still some mentions of dual GPL licensing in the source, but JSON
has just the Ruby license that itself includes an explicit dual-licensing
clause that allows covered software to be distributed under the terms of
the Simplified BSD License instead for all ruby versions >= 1.9.3. This is
however a GPL compatible license according to the Free Software Foundation.
I changed these mentions to be consistent with the Ruby license setting in
the gemspec files which were already correct now.
## 2015-06-01 (1.8.3)
* Fix potential memory leak, thx to nobu.
## 2015-01-08 (1.8.2)
* Some performance improvements by Vipul A M <vipulnsward@gmail.com>.
* Fix by Jason R. Clark <jclark@newrelic.com> to avoid mutation of
`JSON.dump_default_options`.
* More tests by Michael Mac-Vicar <mmacvicar@gmail.com> and fixing
`space_before` accessor in generator.
* Performance on Jruby improved by Ben Browning <bbrownin@redhat.com>.
* Some fixes to be compatible with the new Ruby 2.2 by Zachary Scott <e@zzak.io>
and SHIBATA Hiroshi <hsbt@ruby-lang.org>.
## 2013-05-13 (1.8.1)
* Remove Rubinius exception since transcoding should be working now.
## 2013-05-13 (1.8.0)
* Fix https://github.com/ruby/json/issues/162 reported by Marc-Andre
Lafortune <github_rocks@marc-andre.ca>. Thanks!
* Applied patches by Yui NARUSE <naruse@airemix.jp> to suppress warning with
-Wchar-subscripts and better validate UTF-8 strings.
* Applied patch by ginriki@github to remove unnecessary if.
* Add load/dump interface to `JSON::GenericObject` to make
serialize :some_attribute, `JSON::GenericObject`
work in Rails active models for convenient `SomeModel#some_attribute.foo.bar`
access to serialised JSON data.
## 2013-02-04 (1.7.7)
* Security fix for JSON create_additions default value and
`JSON::GenericObject`. It should not be possible to create additions unless
explicitly requested by setting the create_additions argument to true or
using the JSON.load/dump interface. If `JSON::GenericObject` is supposed to
be automatically deserialised, this has to be explicitly enabled by
setting
JSON::GenericObject.json_creatable = true
as well.
* Remove useless assert in fbuffer implementation.
* Apply patch attached to https://github.com/ruby/json/issues#issue/155
provided by John Shahid <jvshahid@gmail.com>, Thx!
* Add license information to rubygems spec data, reported by Jordi Massaguer Pla <jmassaguerpla@suse.de>.
* Improve documentation, thx to Zachary Scott <zachary@zacharyscott.net>.
## 2012-11-29 (1.7.6)
* Add `GeneratorState#merge` alias for JRuby, fix state accessor methods. Thx to
jvshahid@github.
* Increase hash likeness of state objects.
## 2012-08-17 (1.7.5)
* Fix compilation of extension on older rubies.
## 2012-07-26 (1.7.4)
* Fix compilation problem on AIX, see https://github.com/ruby/json/issues/142
## 2012-05-12 (1.7.3)
* Work around Rubinius encoding issues using iconv for conversion instead.
## 2012-05-11 (1.7.2)
* Fix some encoding issues, that cause problems for the pure and the
extension variant in jruby 1.9 mode.
## 2012-04-28 (1.7.1)
* Some small fixes for building
## 2012-04-28 (1.7.0)
* Add `JSON::GenericObject` for method access to objects transmitted via JSON.
## 2012-04-27 (1.6.7)
* Fix possible crash when trying to parse nil value.
## 2012-02-11 (1.6.6)
* Propagate src encoding to values made from it (fixes 1.9 mode converting
everything to ascii-8bit; harmless for 1.8 mode too) (Thomas E. Enebo
<tom.enebo@gmail.com>), should fix
https://github.com/ruby/json/issues#issue/119.
* Fix https://github.com/ruby/json/issues#issue/124 Thx to Jason Hutchens.
* Fix https://github.com/ruby/json/issues#issue/117
## 2012-01-15 (1.6.5)
* Vit Ondruch <v.ondruch@tiscali.cz> reported a bug that shows up when using
optimisation under GCC 4.7. Thx to him, Bohuslav Kabrda
<bkabrda@redhat.com> and Yui NARUSE <naruse@airemix.jp> for debugging and
developing a patch fix.
## 2011-12-24 (1.6.4)
* Patches that improve speed on JRuby contributed by Charles Oliver Nutter
<headius@headius.com>.
* Support `object_class`/`array_class` with duck typed hash/array.
## 2011-12-01 (1.6.3)
* Let `JSON.load('')` return nil as well to make mysql text columns (default to
`''`) work better for serialization.
## 2011-11-21 (1.6.2)
* Add support for OpenStruct and BigDecimal.
* Fix bug when parsing nil in `quirks_mode`.
* Make JSON.dump and JSON.load methods better cooperate with Rails' serialize
method. Just use:
serialize :value, JSON
* Fix bug with time serialization concerning nanoseconds. Thanks for the
patch go to Josh Partlow (jpartlow@github).
* Improve parsing speed for JSON numbers (integers and floats) in a similar way to
what Evan Phoenix <evan@phx.io> suggested in:
https://github.com/ruby/json/pull/103
## 2011-09-18 (1.6.1)
* Using -target 1.5 to force Java bits to compile with 1.5.
## 2011-09-12 (1.6.0)
* Extract utilities (prettifier and GUI-editor) in its own gem json-utils.
* Split json/add/core into different files for classes to be serialised.
## 2011-08-31 (1.5.4)
* Fix memory leak when used from multiple JRuby. (Patch by
jfirebaugh@github).
* Apply patch by Eric Wong <nocode@yhbt.net> that fixes garbage collection problem
reported in https://github.com/ruby/json/issues/46.
* Add :quirks_mode option to parser and generator.
* Add support for Rational and Complex number additions via json/add/complex
and json/add/rational requires.
## 2011-06-20 (1.5.3)
* Alias State#configure method as State#merge to increase duck type synonymy with Hash.
* Add `as_json` methods in json/add/core, so rails can create its json objects the new way.
## 2011-05-11 (1.5.2)
* Apply documentation patch by Cory Monty <cory.monty@gmail.com>.
* Add gemspecs for json and json\_pure.
* Fix bug in jruby pretty printing.
* Fix bug in `object_class` and `array_class` when inheriting from Hash or
Array.
## 2011-01-24 (1.5.1)
* Made rake-compiler build a fat binary gem. This should fix issue
https://github.com/ruby/json/issues#issue/54.
## 2011-01-22 (1.5.0)
* Included Java source codes for the Jruby extension made by Daniel Luz
<dev@mernen.com>.
* Output full exception message of `deep_const_get` to aid debugging.
* Fixed an issue with ruby 1.9 `Module#const_defined?` method, that was
reported by Riley Goodside.
## 2010-08-09 (1.4.6)
* Fixed oversight reported in http://github.com/ruby/json/issues/closed#issue/23,
always create a new object from the state prototype.
* Made pure and ext api more similar again.
## 2010-08-07 (1.4.5)
* Manage data structure nesting depth in state object during generation. This
should reduce problems with `to_json` method definіtions that only have one
argument.
* Some fixes in the state objects and additional tests.
## 2010-08-06 (1.4.4)
* Fixes build problem for rubinius under OS X, http://github.com/ruby/json/issues/closed#issue/25
* Fixes crashes described in http://github.com/ruby/json/issues/closed#issue/21 and
http://github.com/ruby/json/issues/closed#issue/23
## 2010-05-05 (1.4.3)
* Fixed some test assertions, from Ruby r27587 and r27590, patch by nobu.
* Fixed issue http://github.com/ruby/json/issues/#issue/20 reported by
electronicwhisper@github. Thx!
## 2010-04-26 (1.4.2)
* Applied patch from naruse Yui NARUSE <naruse@airemix.com> to make building with
Microsoft Visual C possible again.
* Applied patch from devrandom <c1.github@niftybox.net> in order to allow building of
json_pure if extensiontask is not present.
* Thanks to Dustin Schneider <dustin@stocktwits.com>, who reported a memory
leak, which is fixed in this release.
* Applied 993f261ccb8f911d2ae57e9db48ec7acd0187283 patch from josh@github.
## 2010-04-25 (1.4.1)
* Fix for a bug reported by Dan DeLeo <dan@kallistec.com>, caused by T_FIXNUM
being different on 32bit/64bit architectures.
## 2010-04-23 (1.4.0)
* Major speed improvements and building with simplified
directory/file-structure.
* Extension should at least be compatible with MRI, YARV and Rubinius.
## 2010-04-07 (1.2.4)
* Trigger const_missing callback to make Rails' dynamic class loading work.
## 2010-03-11 (1.2.3)
* Added a `State#[]` method which returns an attribute's value in order to
increase duck type compatibility to Hash.
## 2010-02-27 (1.2.2)
* Made some changes to make the building of the parser/generator compatible
to Rubinius.
## 2009-11-25 (1.2.1)
* Added `:symbolize_names` option to Parser, which returns symbols instead of
strings in object names/keys.
## 2009-10-01 (1.2.0)
* `fast_generate` now raises an exception for nan and infinite floats.
* On Ruby 1.8 json supports parsing of UTF-8, UTF-16BE, UTF-16LE, UTF-32BE,
and UTF-32LE JSON documents now. Under Ruby 1.9 the M17n conversion
functions are used to convert from all supported encodings. ASCII-8BIT
encoded strings are handled like all strings under Ruby 1.8 were.
* Better documentation
## 2009-08-23 (1.1.9)
* Added forgotten main doc file `extra_rdoc_files`.
## 2009-08-23 (1.1.8)
* Applied a patch by OZAWA Sakuro <sakuro@2238club.org> to make json/pure
work in environments that don't provide iconv.
* Applied patch by okkez_ in order to fix Ruby Bug #1768:
http://redmine.ruby-lang.org/issues/show/1768.
* Finally got around to avoid the rather paranoid escaping of ?/ characters
in the generator's output. The parsers aren't affected by this change.
Thanks to Rich Apodaca <rapodaca@metamolecular.com> for the suggestion.
## 2009-06-29 (1.1.7)
* Security Fix for JSON::Pure::Parser. A specially designed string could
cause catastrophic backtracking in one of the parser's regular expressions
in earlier 1.1.x versions. JSON::Ext::Parser isn't affected by this issue.
Thanks to Bartosz Blimke <bartosz@new-bamboo.co.uk> for reporting this
problem.
* This release also uses a less strict ruby version requirement for the
creation of the mswin32 native gem.
## 2009-05-10 (1.1.6)
* No changes. І tested native linux gems in the last release and they don't
play well with different ruby versions other than the one the gem was built
with. This release is just to bump the version number in order to skip the
native gem on rubyforge.
## 2009-05-10 (1.1.5)
* Started to build gems with rake-compiler gem.
* Applied patch object/array class patch from Brian Candler
<B.Candler@pobox.com> and fixes.
## 2009-04-01 (1.1.4)
* Fixed a bug in the creation of serialized generic rails objects reported by
Friedrich Graeter <graeter@hydrixos.org>.
* Deleted tests/runner.rb, we're using testrb instead.
* Editor supports Infinity in numbers now.
* Made some changes in order to get the library to compile/run under Ruby
1.9.
* Improved speed of the code path for the fast_generate method in the pure
variant.
## 2008-07-10 (1.1.3)
* Wesley Beary <monki@geemus.com> reported a bug in json/add/core's DateTime
handling: If the nominator and denominator of the offset were divisible by
each other Ruby's Rational#to_s returns them as an integer not a fraction
with '/'. This caused a ZeroDivisionError during parsing.
* Use Date#start and DateTime#start instead of sg method, while
remaining backwards compatible.
* Supports ragel >= 6.0 now.
* Corrected some tests.
* Some minor changes.
## 2007-11-27 (1.1.2)
* Remember default dir (last used directory) in editor.
* JSON::Editor.edit method added, the editor can now receive json texts from
the clipboard via C-v.
* Load json texts from an URL pasted via middle button press.
* Added :create_additions option to Parser. This makes it possible to disable
the creation of additions by force, in order to treat json texts as data
while having additions loaded.
* Jacob Maine <jmaine@blurb.com> reported, that JSON(:foo) outputs a JSON
object if the rails addition is enabled, which is wrong. It now outputs a
JSON string "foo" instead, like suggested by Jacob Maine.
* Discovered a bug in the Ruby Bugs Tracker on rubyforge, that was reported
by John Evans lgastako@gmail.com. He could produce a crash in the JSON
generator by returning something other than a String instance from a
to_json method. I now guard against this by doing a rather crude type
check, which raises an exception instead of crashing.
## 2007-07-06 (1.1.1)
* Yui NARUSE <naruse@airemix.com> sent some patches to fix tests for Ruby
1.9. I applied them and adapted some of them a bit to run both on 1.8 and
1.9.
* Introduced a `JSON.parse!` method without depth checking for people who
like danger.
* Made generate and `pretty_generate` methods configurable by an options hash.
* Added :allow_nan option to parser and generator in order to handle NaN,
Infinity, and -Infinity correctly - if requested. Floats, which aren't numbers,
aren't valid JSON according to RFC4627, so by default an exception will be
raised if any of these symbols are encountered. Thanks to Andrea Censi
<andrea.censi@dis.uniroma1.it> for his hint about this.
* Fixed some more tests for Ruby 1.9.
* Implemented dump/load interface of Marshal as suggested in ruby-core:11405
by murphy <murphy@rubychan.de>.
* Implemented the `max_nesting` feature for generate methods, too.
* Added some implementations for ruby core's custom objects for
serialisation/deserialisation purposes.
## 2007-05-21 (1.1.0)
* Implemented max_nesting feature for parser to avoid stack overflows for
data from untrusted sources. If you trust the source, you can disable it
with the option max_nesting => false.
* Piers Cawley <pdcawley@bofh.org.uk> reported a bug, that not every
character can be escaped by `\` as required by RFC4627. There's a
contradiction between David Crockford's JSON checker test vectors (in
tests/fixtures) and RFC4627, though. I decided to stick to the RFC, because
the JSON checker seems to be a bit older than the RFC.
* Extended license to Ruby License, which includes the GPL.
* Added keyboard shortcuts, and 'Open location' menu item to edit_json.rb.
## 2007-05-09 (1.0.4)
* Applied a patch from Yui NARUSE <naruse@airemix.com> to make JSON compile
under Ruby 1.9. Thank you very much for mailing it to me!
* Made binary variants of JSON fail early, instead of falling back to the
pure version. This should avoid overshadowing of eventual problems while
loading of the binary.
## 2007-03-24 (1.0.3)
* Improved performance of pure variant a bit.
* The ext variant of this release supports the mswin32 platform. Ugh!
## 2007-03-24 (1.0.2)
* Ext Parser didn't parse 0e0 correctly into 0.0: Fixed!
## 2007-03-24 (1.0.1)
* Forgot some object files in the build dir. I really like that - not!
## 2007-03-24 (1.0.0)
* Added C implementations for the JSON generator and a ragel based JSON
parser in C.
* Much more tests, especially fixtures from json.org.
* Further improved conformance to RFC4627.
## 2007-02-09 (0.4.3)
* Conform more to RFC4627 for JSON: This means JSON strings
now always must contain exactly one object `"{ ... }"` or array `"[ ... ]"` in
order to be parsed without raising an exception. The definition of what
constitutes a whitespace is narrower in JSON than in Ruby ([ \t\r\n]), and
there are differences in floats and integers (no octals or hexadecimals) as
well.
* Added aliases generate and `pretty_generate` of unparse and `pretty_unparse`.
* Fixed a test case.
* Catch an `Iconv::InvalidEncoding` exception, that seems to occur on some Sun
boxes with SunOS 5.8, if iconv doesn't support utf16 conversions. This was
reported by Andrew R Jackson <andrewj@bcm.tmc.edu>, thanks a bunch!
## 2006-08-25 (0.4.2)
* Fixed a bug in handling solidi (/-characters), that was reported by
Kevin Gilpin <kevin.gilpin@alum.mit.edu>.
## 2006-02-06 (0.4.1)
* Fixed a bug related to escaping with backslashes. Thanks for the report go
to Florian Munz <surf@theflow.de>.
## 2005-09-23 (0.4.0)
* Initial Rubyforge Version
+56
View File
@@ -0,0 +1,56 @@
Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
You can redistribute it and/or modify it under either the terms of the
2-clause BSDL (see the file BSDL), or the conditions below:
1. You may make and give away verbatim copies of the source form of the
software without restriction, provided that you duplicate all of the
original copyright notices and associated disclaimers.
2. You may modify your copy of the software in any way, provided that
you do at least ONE of the following:
a) place your modifications in the Public Domain or otherwise
make them Freely Available, such as by posting said
modifications to Usenet or an equivalent medium, or by allowing
the author to include your modifications in the software.
b) use the modified software only within your corporation or
organization.
c) give non-standard binaries non-standard names, with
instructions on where to get the original software distribution.
d) make other distribution arrangements with the author.
3. You may distribute the software in object code or binary form,
provided that you do at least ONE of the following:
a) distribute the binaries and library files of the software,
together with instructions (in the manual page or equivalent)
on where to get the original distribution.
b) accompany the distribution with the machine-readable source of
the software.
c) give non-standard binaries non-standard names, with
instructions on where to get the original software distribution.
d) make other distribution arrangements with the author.
4. You may modify and include the part of the software into any other
software (possibly commercial). But some files in the distribution
are not written by the author, so that they are not under these terms.
For the list of those files and their copying conditions, see the
file LEGAL.
5. The scripts and library files supplied as input to or produced as
output from the software do not automatically fall under the
copyright of the software, but belong to whomever generated them,
and may be sold commercially, and may be aggregated with this
software.
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.
+8
View File
@@ -0,0 +1,8 @@
# -*- rdoc -*-
= LEGAL NOTICE INFORMATION
--------------------------
All the files in this distribution are covered under either the Ruby's
license (see the file COPYING) or public-domain except some files
mentioned below.
+268
View File
@@ -0,0 +1,268 @@
# JSON implementation for Ruby
[![CI](https://github.com/ruby/json/actions/workflows/ci.yml/badge.svg)](https://github.com/ruby/json/actions/workflows/ci.yml)
## Description
This is an implementation of the JSON specification according to RFC 7159
http://www.ietf.org/rfc/rfc7159.txt .
The JSON generator generate UTF-8 character sequences by default.
If an :ascii\_only option with a true value is given, they escape all
non-ASCII and control characters with \uXXXX escape sequences, and support
UTF-16 surrogate pairs in order to be able to generate the whole range of
unicode code points.
All strings, that are to be encoded as JSON strings, should be UTF-8 byte
sequences on the Ruby side. To encode raw binary strings, that aren't UTF-8
encoded, please use the to\_json\_raw\_object method of String (which produces
an object, that contains a byte array) and decode the result on the receiving
endpoint.
## Installation
Install the gem and add to the application's Gemfile by executing:
$ bundle add json
If bundler is not being used to manage dependencies, install the gem by executing:
$ gem install json
## Basic Usage
To use JSON you can
```ruby
require 'json'
```
Now you can parse a JSON document into a ruby data structure by calling
```ruby
JSON.parse(document)
```
If you want to generate a JSON document from a ruby data structure call
```ruby
JSON.generate(data)
```
You can also use the `pretty_generate` method (which formats the output more
verbosely and nicely) or `fast_generate` (which doesn't do any of the security
checks generate performs, e. g. nesting deepness checks).
## Casting non native types
JSON documents can only support Hashes, Arrays, Strings, Integers and Floats.
By default if you attempt to serialize something else, `JSON.generate` will
search for a `#to_json` method on that object:
```ruby
Position = Struct.new(:latitude, :longitude) do
def to_json(state = nil, *)
JSON::State.from_state(state).generate({
latitude: latitude,
longitude: longitude,
})
end
end
JSON.generate([
Position.new(12323.234, 435345.233),
Position.new(23434.676, 159435.324),
]) # => [{"latitude":12323.234,"longitude":435345.233},{"latitude":23434.676,"longitude":159435.324}]
```
If a `#to_json` method isn't defined on the object, `JSON.generate` will fallback to call `#to_s`:
```ruby
JSON.generate(Object.new) # => "#<Object:0x000000011e768b98>"
```
Both of these behavior can be disabled using the `strict: true` option:
```ruby
JSON.generate(Object.new, strict: true) # => Object not allowed in JSON (JSON::GeneratorError)
JSON.generate(Position.new(1, 2)) # => Position not allowed in JSON (JSON::GeneratorError)
```
## JSON::Coder
Since `#to_json` methods are global, it can sometimes be problematic if you need a given type to be
serialized in different ways in different locations.
Instead it is recommended to use the newer `JSON::Coder` API:
```ruby
module MyApp
API_JSON_CODER = JSON::Coder.new do |object|
case object
when Time
object.iso8601(3)
else
object
end
end
end
puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
```
The provided block is called for all objects that don't have a native JSON equivalent, and
must return a Ruby object that has a native JSON equivalent.
## Combining JSON fragments
To combine JSON fragments into a bigger JSON document, you can use `JSON::Fragment`:
```ruby
posts_json = cache.fetch_multi(post_ids) do |post_id|
JSON.generate(Post.find(post_id))
end
posts_json.map! { |post_json| JSON::Fragment.new(post_json) }
JSON.generate({ posts: posts_json, count: posts_json.count })
```
## Round-tripping arbitrary types
> [!CAUTION]
> You should never use `JSON.unsafe_load` nor `JSON.parse(str, create_additions: true)` to parse untrusted user input,
> as it can lead to remote code execution vulnerabilities.
To create a JSON document from a ruby data structure, you can call
`JSON.generate` like that:
```ruby
json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
# => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
```
To get back a ruby data structure from a JSON document, you have to call
JSON.parse on it:
```ruby
JSON.parse json
# => [1, 2, {"a"=>3.141}, false, true, nil, "4..10"]
```
Note, that the range from the original data structure is a simple
string now. The reason for this is, that JSON doesn't support ranges
or arbitrary classes. In this case the json library falls back to call
`Object#to_json`, which is the same as `#to_s.to_json`.
It's possible to add JSON support serialization to arbitrary classes by
simply implementing a more specialized version of the `#to_json method`, that
should return a JSON object (a hash converted to JSON with `#to_json`) like
this (don't forget the `*a` for all the arguments):
```ruby
class Range
def to_json(*a)
{
'json_class' => self.class.name, # = 'Range'
'data' => [ first, last, exclude_end? ]
}.to_json(*a)
end
end
```
The hash key `json_class` is the class, that will be asked to deserialise the
JSON representation later. In this case it's `Range`, but any namespace of
the form `A::B` or `::A::B` will do. All other keys are arbitrary and can be
used to store the necessary data to configure the object to be deserialised.
If the key `json_class` is found in a JSON object, the JSON parser checks
if the given class responds to the `json_create` class method. If so, it is
called with the JSON object converted to a Ruby hash. So a range can
be deserialised by implementing `Range.json_create` like this:
```ruby
class Range
def self.json_create(o)
new(*o['data'])
end
end
```
Now it possible to serialise/deserialise ranges as well:
```ruby
json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
# => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
JSON.parse json
# => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
# => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
JSON.unsafe_load json
# => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
```
`JSON.generate` always creates the shortest possible string representation of a
ruby data structure in one line. This is good for data storage or network
protocols, but not so good for humans to read. Fortunately there's also
`JSON.pretty_generate` (or `JSON.pretty_generate`) that creates a more readable
output:
```ruby
puts JSON.pretty_generate([1, 2, {"a"=>3.141}, false, true, nil, 4..10])
[
1,
2,
{
"a": 3.141
},
false,
true,
null,
{
"json_class": "Range",
"data": [
4,
10,
false
]
}
]
```
There are also the methods `Kernel#j` for generate, and `Kernel#jj` for
`pretty_generate` output to the console, that work analogous to Core Ruby's `p` and
the `pp` library's `pp` methods.
## Development
### Release
Update the `lib/json/version.rb` file.
```
rbenv shell 2.6.5
rake build
gem push pkg/json-2.3.0.gem
rbenv shell jruby-9.2.9.0
rake build
gem push pkg/json-2.3.0-java.gem
```
## Author
Florian Frank <mailto:flori@ping.de>
## License
Ruby License, see https://www.ruby-lang.org/en/about/license.txt.
## Download
The latest version of this library can be downloaded at
* https://rubygems.org/gems/json
Online Documentation should be located at
* https://www.rubydoc.info/gems/json
[Ragel]: http://www.colm.net/open-source/ragel/
@@ -0,0 +1,205 @@
#ifndef _FBUFFER_H_
#define _FBUFFER_H_
#include "ruby.h"
#include "ruby/encoding.h"
/* shims */
/* This is the fallback definition from Ruby 3.4 */
#ifndef RBIMPL_STDBOOL_H
#if defined(__cplusplus)
# if defined(HAVE_STDBOOL_H) && (__cplusplus >= 201103L)
# include <cstdbool>
# endif
#elif defined(HAVE_STDBOOL_H)
# include <stdbool.h>
#elif !defined(HAVE__BOOL)
typedef unsigned char _Bool;
# define bool _Bool
# define true ((_Bool)+1)
# define false ((_Bool)+0)
# define __bool_true_false_are_defined
#endif
#endif
#ifndef RB_UNLIKELY
#define RB_UNLIKELY(expr) expr
#endif
#ifndef RB_LIKELY
#define RB_LIKELY(expr) expr
#endif
#ifndef MAYBE_UNUSED
# define MAYBE_UNUSED(x) x
#endif
enum fbuffer_type {
FBUFFER_HEAP_ALLOCATED = 0,
FBUFFER_STACK_ALLOCATED = 1,
};
typedef struct FBufferStruct {
enum fbuffer_type type;
unsigned long initial_length;
unsigned long len;
unsigned long capa;
char *ptr;
VALUE io;
} FBuffer;
#define FBUFFER_STACK_SIZE 512
#define FBUFFER_IO_BUFFER_SIZE (16384 - 1)
#define FBUFFER_INITIAL_LENGTH_DEFAULT 1024
#define FBUFFER_PTR(fb) ((fb)->ptr)
#define FBUFFER_LEN(fb) ((fb)->len)
#define FBUFFER_CAPA(fb) ((fb)->capa)
#define FBUFFER_PAIR(fb) FBUFFER_PTR(fb), FBUFFER_LEN(fb)
static void fbuffer_free(FBuffer *fb);
static void fbuffer_clear(FBuffer *fb);
static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len);
static void fbuffer_append_long(FBuffer *fb, long number);
static inline void fbuffer_append_char(FBuffer *fb, char newchr);
static VALUE fbuffer_finalize(FBuffer *fb);
static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *stack_buffer, long stack_buffer_size)
{
fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
if (stack_buffer) {
fb->type = FBUFFER_STACK_ALLOCATED;
fb->ptr = stack_buffer;
fb->capa = stack_buffer_size;
}
}
static void fbuffer_free(FBuffer *fb)
{
if (fb->ptr && fb->type == FBUFFER_HEAP_ALLOCATED) {
ruby_xfree(fb->ptr);
}
}
static void fbuffer_clear(FBuffer *fb)
{
fb->len = 0;
}
static void fbuffer_flush(FBuffer *fb)
{
rb_io_write(fb->io, rb_utf8_str_new(fb->ptr, fb->len));
fbuffer_clear(fb);
}
static void fbuffer_realloc(FBuffer *fb, unsigned long required)
{
if (required > fb->capa) {
if (fb->type == FBUFFER_STACK_ALLOCATED) {
const char *old_buffer = fb->ptr;
fb->ptr = ALLOC_N(char, required);
fb->type = FBUFFER_HEAP_ALLOCATED;
MEMCPY(fb->ptr, old_buffer, char, fb->len);
} else {
REALLOC_N(fb->ptr, char, required);
}
fb->capa = required;
}
}
static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested)
{
if (RB_UNLIKELY(fb->io)) {
if (fb->capa < FBUFFER_IO_BUFFER_SIZE) {
fbuffer_realloc(fb, FBUFFER_IO_BUFFER_SIZE);
} else {
fbuffer_flush(fb);
}
if (RB_LIKELY(requested < fb->capa)) {
return;
}
}
unsigned long required;
if (RB_UNLIKELY(!fb->ptr)) {
fb->ptr = ALLOC_N(char, fb->initial_length);
fb->capa = fb->initial_length;
}
for (required = fb->capa; requested > required - fb->len; required <<= 1);
fbuffer_realloc(fb, required);
}
static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
{
if (RB_UNLIKELY(requested > fb->capa - fb->len)) {
fbuffer_do_inc_capa(fb, requested);
}
}
static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
{
if (len > 0) {
fbuffer_inc_capa(fb, len);
MEMCPY(fb->ptr + fb->len, newstr, char, len);
fb->len += len;
}
}
static void fbuffer_append_str(FBuffer *fb, VALUE str)
{
const char *newstr = StringValuePtr(str);
unsigned long len = RSTRING_LEN(str);
RB_GC_GUARD(str);
fbuffer_append(fb, newstr, len);
}
static inline void fbuffer_append_char(FBuffer *fb, char newchr)
{
fbuffer_inc_capa(fb, 1);
*(fb->ptr + fb->len) = newchr;
fb->len++;
}
static long fltoa(long number, char *buf)
{
static const char digits[] = "0123456789";
long sign = number;
char* tmp = buf;
if (sign < 0) number = -number;
do *tmp-- = digits[number % 10]; while (number /= 10);
if (sign < 0) *tmp-- = '-';
return buf - tmp;
}
#define LONG_BUFFER_SIZE 20
static void fbuffer_append_long(FBuffer *fb, long number)
{
char buf[LONG_BUFFER_SIZE];
char *buffer_end = buf + LONG_BUFFER_SIZE;
long len = fltoa(number, buffer_end - 1);
fbuffer_append(fb, buffer_end - len, len);
}
static VALUE fbuffer_finalize(FBuffer *fb)
{
if (fb->io) {
fbuffer_flush(fb);
fbuffer_free(fb);
rb_io_flush(fb->io);
return fb->io;
} else {
VALUE result = rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
fbuffer_free(fb);
return result;
}
}
#endif
@@ -0,0 +1,269 @@
SHELL = /bin/sh
# V=0 quiet, V=1 verbose. other values don't work.
V = 0
V0 = $(V:0=)
Q1 = $(V:1=)
Q = $(Q1:0=@)
ECHO1 = $(V:1=@ :)
ECHO = $(ECHO1:0=@ echo)
NULLCMD = :
#### Start of system configuration section. ####
srcdir = .
topdir = /usr/include/ruby-3.2.0
hdrdir = $(topdir)
arch_hdrdir = /usr/include/x86_64-linux-gnu/ruby-3.2.0
PATH_SEPARATOR = :
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
prefix = $(DESTDIR)/usr
rubysitearchprefix = $(sitearchlibdir)/$(RUBY_BASE_NAME)
rubyarchprefix = $(archlibdir)/$(RUBY_BASE_NAME)
rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
exec_prefix = $(prefix)
vendorarchhdrdir = $(sitearchincludedir)/$(RUBY_VERSION_NAME)/vendor_ruby
sitearchhdrdir = $(sitearchincludedir)/$(RUBY_VERSION_NAME)/site_ruby
rubyarchhdrdir = $(archincludedir)/$(RUBY_VERSION_NAME)
vendorhdrdir = $(rubyhdrdir)/vendor_ruby
sitehdrdir = $(rubyhdrdir)/site_ruby
rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME)
vendorarchdir = $(rubysitearchprefix)/vendor_ruby/$(ruby_version)
vendorlibdir = $(vendordir)/$(ruby_version)
vendordir = $(rubylibprefix)/vendor_ruby
sitearchdir = $(DESTDIR)/usr/local/lib/x86_64-linux-gnu/site_ruby
sitelibdir = $(sitedir)/$(ruby_version)
sitedir = $(DESTDIR)/usr/local/lib/site_ruby
rubyarchdir = $(rubyarchprefix)/$(ruby_version)
rubylibdir = $(rubylibprefix)/$(ruby_version)
sitearchincludedir = $(includedir)/$(sitearch)
archincludedir = $(includedir)/$(arch)
sitearchlibdir = $(libdir)/$(sitearch)
archlibdir = $(libdir)/$(arch)
ridir = $(datarootdir)/$(RI_BASE_NAME)
mandir = $(datarootdir)/man
localedir = $(datarootdir)/locale
libdir = $(exec_prefix)/lib
psdir = $(docdir)
pdfdir = $(docdir)
dvidir = $(docdir)
htmldir = $(docdir)
infodir = $(datarootdir)/info
docdir = $(datarootdir)/doc/$(PACKAGE)
oldincludedir = $(DESTDIR)/usr/include
includedir = $(prefix)/include
runstatedir = $(DESTDIR)/var/run
localstatedir = $(DESTDIR)/var
sharedstatedir = $(prefix)/com
sysconfdir = $(DESTDIR)/etc
datadir = $(datarootdir)
datarootdir = $(prefix)/share
libexecdir = $(exec_prefix)/libexec
sbindir = $(exec_prefix)/sbin
bindir = $(exec_prefix)/bin
archdir = $(rubyarchdir)
CC_WRAPPER =
CC = x86_64-linux-gnu-gcc
CXX = x86_64-linux-gnu-g++
LIBRUBY = $(LIBRUBY_SO)
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static $(MAINLIBS)
empty =
OUTFLAG = -o $(empty)
COUTFLAG = -o $(empty)
CSRCFLAG = $(empty)
RUBY_EXTCONF_H =
cflags = $(optflags) $(debugflags) $(warnflags)
cxxflags =
optflags = -O3 -fno-fast-math
debugflags = -ggdb3
warnflags = -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wundef
cppflags =
CCDLFLAGS = -fPIC
CFLAGS = $(CCDLFLAGS) -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=BUILDDIR=. -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection -fdebug-prefix-map=BUILDDIR=/usr/src/ruby3.2-3.2.3-1ubuntu0.24.04.8 -fPIC -std=c99 $(ARCH_FLAG)
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
DEFS =
CPPFLAGS = -DJSON_GENERATOR -Wdate-time -D_FORTIFY_SOURCE=3 $(DEFS) $(cppflags)
CXXFLAGS = $(CCDLFLAGS) -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=BUILDDIR=. -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection -fdebug-prefix-map=BUILDDIR=/usr/src/ruby3.2-3.2.3-1ubuntu0.24.04.8 $(ARCH_FLAG)
ldflags = -L. -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed
dldflags = -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now
ARCH_FLAG =
DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
LDSHARED = $(CC) -shared
LDSHAREDXX = $(CXX) -shared
AR = x86_64-linux-gnu-gcc-ar
EXEEXT =
RUBY_INSTALL_NAME = $(RUBY_BASE_NAME)3.2
RUBY_SO_NAME = ruby-3.2
RUBYW_INSTALL_NAME =
RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
RUBYW_BASE_NAME = rubyw
RUBY_BASE_NAME = ruby
arch = x86_64-linux-gnu
sitearch = $(arch)
ruby_version = 3.2.0
ruby = $(bindir)/$(RUBY_BASE_NAME)3.2
RUBY = $(ruby)
BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME)3.2
ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h
RM = rm -f
RM_RF = rm -fr
RMDIRS = rmdir --ignore-fail-on-non-empty -p
MAKEDIRS = /bin/mkdir -p
INSTALL = /usr/bin/install -c
INSTALL_PROG = $(INSTALL) -m 0755
INSTALL_DATA = $(INSTALL) -m 644
COPY = cp
TOUCH = exit >
#### End of system configuration section. ####
preload =
libpath = . $(archlibdir)
LIBPATH = -L. -L$(archlibdir)
DEFFILE =
CLEANFILES = mkmf.log
DISTCLEANFILES =
DISTCLEANDIRS =
extout =
extout_prefix =
target_prefix = /json/ext
LOCAL_LIBS =
LIBS = $(LIBRUBYARG_SHARED) -lm -lpthread -lc
ORIG_SRCS = generator.c
SRCS = $(ORIG_SRCS)
OBJS = generator.o
HDRS =
LOCAL_HDRS =
TARGET = generator
TARGET_NAME = generator
TARGET_ENTRY = Init_$(TARGET_NAME)
DLLIB = $(TARGET).so
EXTSTATIC =
STATIC_LIB =
TIMESTAMP_DIR = .
BINDIR = $(bindir)
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
RUBYLIBDIR = $(sitelibdir)$(target_prefix)
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
HDRDIR = $(sitehdrdir)$(target_prefix)
ARCHHDRDIR = $(sitearchhdrdir)$(target_prefix)
TARGET_SO_DIR =
TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
CLEANLIBS = $(TARGET_SO) false
CLEANOBJS = $(OBJS) *.bak
TARGET_SO_DIR_TIMESTAMP = $(TIMESTAMP_DIR)/.sitearchdir.-.json.-.ext.time
all: $(DLLIB)
static: $(STATIC_LIB)
.PHONY: all install static install-so install-rb
.PHONY: clean clean-so clean-static clean-rb
clean-static::
clean-rb-default::
clean-rb::
clean-so::
clean: clean-so clean-static clean-rb-default clean-rb
-$(Q)$(RM_RF) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time
distclean-rb-default::
distclean-rb::
distclean-so::
distclean-static::
distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
-$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
-$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
-$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
realclean: distclean
install: install-so install-rb
install-so: $(DLLIB) $(TARGET_SO_DIR_TIMESTAMP)
$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
clean-static::
-$(Q)$(RM) $(STATIC_LIB)
install-rb: pre-install-rb do-install-rb install-rb-default
install-rb-default: pre-install-rb-default do-install-rb-default
pre-install-rb: Makefile
pre-install-rb-default: Makefile
do-install-rb:
do-install-rb-default:
pre-install-rb-default:
@$(NULLCMD)
$(TARGET_SO_DIR_TIMESTAMP):
$(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
$(Q) $(TOUCH) $@
site-install: site-install-so site-install-rb
site-install-so: install-so
site-install-rb: install-rb
.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S
.cc.o:
$(ECHO) compiling $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.cc.S:
$(ECHO) translating $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.mm.o:
$(ECHO) compiling $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.mm.S:
$(ECHO) translating $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.cxx.o:
$(ECHO) compiling $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.cxx.S:
$(ECHO) translating $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.cpp.o:
$(ECHO) compiling $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.cpp.S:
$(ECHO) translating $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.c.o:
$(ECHO) compiling $(<)
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.c.S:
$(ECHO) translating $(<)
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.m.o:
$(ECHO) compiling $(<)
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.m.S:
$(ECHO) translating $(<)
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
$(TARGET_SO): $(OBJS) Makefile
$(ECHO) linking shared-object json/ext/$(DLLIB)
-$(Q)$(RM) $(@)
$(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
$(OBJS): $(HDRS) $(ruby_headers)
@@ -0,0 +1,10 @@
require 'mkmf'
if RUBY_ENGINE == 'truffleruby'
# The pure-Ruby generator is faster on TruffleRuby, so skip compiling the generator extension
File.write('Makefile', dummy_makefile("").join)
else
append_cflags("-std=c99")
$defs << "-DJSON_GENERATOR"
create_makefile 'json/ext/generator'
end
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,269 @@
SHELL = /bin/sh
# V=0 quiet, V=1 verbose. other values don't work.
V = 0
V0 = $(V:0=)
Q1 = $(V:1=)
Q = $(Q1:0=@)
ECHO1 = $(V:1=@ :)
ECHO = $(ECHO1:0=@ echo)
NULLCMD = :
#### Start of system configuration section. ####
srcdir = .
topdir = /usr/include/ruby-3.2.0
hdrdir = $(topdir)
arch_hdrdir = /usr/include/x86_64-linux-gnu/ruby-3.2.0
PATH_SEPARATOR = :
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
prefix = $(DESTDIR)/usr
rubysitearchprefix = $(sitearchlibdir)/$(RUBY_BASE_NAME)
rubyarchprefix = $(archlibdir)/$(RUBY_BASE_NAME)
rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
exec_prefix = $(prefix)
vendorarchhdrdir = $(sitearchincludedir)/$(RUBY_VERSION_NAME)/vendor_ruby
sitearchhdrdir = $(sitearchincludedir)/$(RUBY_VERSION_NAME)/site_ruby
rubyarchhdrdir = $(archincludedir)/$(RUBY_VERSION_NAME)
vendorhdrdir = $(rubyhdrdir)/vendor_ruby
sitehdrdir = $(rubyhdrdir)/site_ruby
rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME)
vendorarchdir = $(rubysitearchprefix)/vendor_ruby/$(ruby_version)
vendorlibdir = $(vendordir)/$(ruby_version)
vendordir = $(rubylibprefix)/vendor_ruby
sitearchdir = $(DESTDIR)/usr/local/lib/x86_64-linux-gnu/site_ruby
sitelibdir = $(sitedir)/$(ruby_version)
sitedir = $(DESTDIR)/usr/local/lib/site_ruby
rubyarchdir = $(rubyarchprefix)/$(ruby_version)
rubylibdir = $(rubylibprefix)/$(ruby_version)
sitearchincludedir = $(includedir)/$(sitearch)
archincludedir = $(includedir)/$(arch)
sitearchlibdir = $(libdir)/$(sitearch)
archlibdir = $(libdir)/$(arch)
ridir = $(datarootdir)/$(RI_BASE_NAME)
mandir = $(datarootdir)/man
localedir = $(datarootdir)/locale
libdir = $(exec_prefix)/lib
psdir = $(docdir)
pdfdir = $(docdir)
dvidir = $(docdir)
htmldir = $(docdir)
infodir = $(datarootdir)/info
docdir = $(datarootdir)/doc/$(PACKAGE)
oldincludedir = $(DESTDIR)/usr/include
includedir = $(prefix)/include
runstatedir = $(DESTDIR)/var/run
localstatedir = $(DESTDIR)/var
sharedstatedir = $(prefix)/com
sysconfdir = $(DESTDIR)/etc
datadir = $(datarootdir)
datarootdir = $(prefix)/share
libexecdir = $(exec_prefix)/libexec
sbindir = $(exec_prefix)/sbin
bindir = $(exec_prefix)/bin
archdir = $(rubyarchdir)
CC_WRAPPER =
CC = x86_64-linux-gnu-gcc
CXX = x86_64-linux-gnu-g++
LIBRUBY = $(LIBRUBY_SO)
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static $(MAINLIBS)
empty =
OUTFLAG = -o $(empty)
COUTFLAG = -o $(empty)
CSRCFLAG = $(empty)
RUBY_EXTCONF_H =
cflags = $(optflags) $(debugflags) $(warnflags)
cxxflags =
optflags = -O3 -fno-fast-math
debugflags = -ggdb3
warnflags = -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wundef
cppflags =
CCDLFLAGS = -fPIC
CFLAGS = $(CCDLFLAGS) -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=BUILDDIR=. -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection -fdebug-prefix-map=BUILDDIR=/usr/src/ruby3.2-3.2.3-1ubuntu0.24.04.8 -fPIC -std=c99 $(ARCH_FLAG)
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
DEFS =
CPPFLAGS = -DHAVE_RB_ENC_INTERNED_STR -DHAVE_RB_HASH_NEW_CAPA -DHAVE_RB_HASH_BULK_INSERT -DHAVE_RB_CATEGORY_WARN -DHAVE_STRNLEN -Wdate-time -D_FORTIFY_SOURCE=3 $(DEFS) $(cppflags)
CXXFLAGS = $(CCDLFLAGS) -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=BUILDDIR=. -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection -fdebug-prefix-map=BUILDDIR=/usr/src/ruby3.2-3.2.3-1ubuntu0.24.04.8 $(ARCH_FLAG)
ldflags = -L. -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed
dldflags = -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now
ARCH_FLAG =
DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
LDSHARED = $(CC) -shared
LDSHAREDXX = $(CXX) -shared
AR = x86_64-linux-gnu-gcc-ar
EXEEXT =
RUBY_INSTALL_NAME = $(RUBY_BASE_NAME)3.2
RUBY_SO_NAME = ruby-3.2
RUBYW_INSTALL_NAME =
RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
RUBYW_BASE_NAME = rubyw
RUBY_BASE_NAME = ruby
arch = x86_64-linux-gnu
sitearch = $(arch)
ruby_version = 3.2.0
ruby = $(bindir)/$(RUBY_BASE_NAME)3.2
RUBY = $(ruby)
BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME)3.2
ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h
RM = rm -f
RM_RF = rm -fr
RMDIRS = rmdir --ignore-fail-on-non-empty -p
MAKEDIRS = /bin/mkdir -p
INSTALL = /usr/bin/install -c
INSTALL_PROG = $(INSTALL) -m 0755
INSTALL_DATA = $(INSTALL) -m 644
COPY = cp
TOUCH = exit >
#### End of system configuration section. ####
preload =
libpath = . $(archlibdir)
LIBPATH = -L. -L$(archlibdir)
DEFFILE =
CLEANFILES = mkmf.log
DISTCLEANFILES =
DISTCLEANDIRS =
extout =
extout_prefix =
target_prefix = /json/ext
LOCAL_LIBS =
LIBS = $(LIBRUBYARG_SHARED) -lm -lpthread -lc
ORIG_SRCS = parser.c
SRCS = $(ORIG_SRCS)
OBJS = parser.o
HDRS =
LOCAL_HDRS =
TARGET = parser
TARGET_NAME = parser
TARGET_ENTRY = Init_$(TARGET_NAME)
DLLIB = $(TARGET).so
EXTSTATIC =
STATIC_LIB =
TIMESTAMP_DIR = .
BINDIR = $(bindir)
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
RUBYLIBDIR = $(sitelibdir)$(target_prefix)
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
HDRDIR = $(sitehdrdir)$(target_prefix)
ARCHHDRDIR = $(sitearchhdrdir)$(target_prefix)
TARGET_SO_DIR =
TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
CLEANLIBS = $(TARGET_SO) false
CLEANOBJS = $(OBJS) *.bak
TARGET_SO_DIR_TIMESTAMP = $(TIMESTAMP_DIR)/.sitearchdir.-.json.-.ext.time
all: $(DLLIB)
static: $(STATIC_LIB)
.PHONY: all install static install-so install-rb
.PHONY: clean clean-so clean-static clean-rb
clean-static::
clean-rb-default::
clean-rb::
clean-so::
clean: clean-so clean-static clean-rb-default clean-rb
-$(Q)$(RM_RF) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time
distclean-rb-default::
distclean-rb::
distclean-so::
distclean-static::
distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
-$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
-$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
-$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
realclean: distclean
install: install-so install-rb
install-so: $(DLLIB) $(TARGET_SO_DIR_TIMESTAMP)
$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
clean-static::
-$(Q)$(RM) $(STATIC_LIB)
install-rb: pre-install-rb do-install-rb install-rb-default
install-rb-default: pre-install-rb-default do-install-rb-default
pre-install-rb: Makefile
pre-install-rb-default: Makefile
do-install-rb:
do-install-rb-default:
pre-install-rb-default:
@$(NULLCMD)
$(TARGET_SO_DIR_TIMESTAMP):
$(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
$(Q) $(TOUCH) $@
site-install: site-install-so site-install-rb
site-install-so: install-so
site-install-rb: install-rb
.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S
.cc.o:
$(ECHO) compiling $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.cc.S:
$(ECHO) translating $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.mm.o:
$(ECHO) compiling $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.mm.S:
$(ECHO) translating $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.cxx.o:
$(ECHO) compiling $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.cxx.S:
$(ECHO) translating $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.cpp.o:
$(ECHO) compiling $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.cpp.S:
$(ECHO) translating $(<)
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.c.o:
$(ECHO) compiling $(<)
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.c.S:
$(ECHO) translating $(<)
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
.m.o:
$(ECHO) compiling $(<)
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
.m.S:
$(ECHO) translating $(<)
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
$(TARGET_SO): $(OBJS) Makefile
$(ECHO) linking shared-object json/ext/$(DLLIB)
-$(Q)$(RM) $(@)
$(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
$(OBJS): $(HDRS) $(ruby_headers)
@@ -0,0 +1,12 @@
# frozen_string_literal: true
require 'mkmf'
have_func("rb_enc_interned_str", "ruby.h") # RUBY_VERSION >= 3.0
have_func("rb_hash_new_capa", "ruby.h") # RUBY_VERSION >= 3.2
have_func("rb_hash_bulk_insert", "ruby.h") # Missing on TruffleRuby
have_func("rb_category_warn", "ruby.h") # Missing on TruffleRuby
have_func("strnlen", "string.h") # Missing on Solaris 10
append_cflags("-std=c99")
create_makefile 'json/ext/parser'
File diff suppressed because it is too large Load Diff
+63
View File
@@ -0,0 +1,63 @@
# frozen_string_literal: true
version = File.foreach(File.join(__dir__, "lib/json/version.rb")) do |line|
/^\s*VERSION\s*=\s*'(.*)'/ =~ line and break $1
end rescue nil
spec = Gem::Specification.new do |s|
java_ext = Gem::Platform === s.platform && s.platform =~ 'java' || RUBY_ENGINE == 'jruby'
s.name = "json"
s.version = version
s.summary = "JSON Implementation for Ruby"
s.homepage = "https://github.com/ruby/json"
s.metadata = {
'bug_tracker_uri' => 'https://github.com/ruby/json/issues',
'changelog_uri' => 'https://github.com/ruby/json/blob/master/CHANGES.md',
'documentation_uri' => 'https://docs.ruby-lang.org/en/master/JSON.html',
'homepage_uri' => s.homepage,
'source_code_uri' => 'https://github.com/ruby/json',
}
s.required_ruby_version = Gem::Requirement.new(">= 2.7")
if java_ext
s.description = "A JSON implementation as a JRuby extension."
s.author = "Daniel Luz"
s.email = "dev+ruby@mernen.com"
else
s.description = "This is a JSON implementation as a Ruby extension in C."
s.authors = ["Florian Frank"]
s.email = "flori@ping.de"
end
s.licenses = ["Ruby"]
s.extra_rdoc_files = ["README.md"]
s.rdoc_options = ["--title", "JSON implementation for Ruby", "--main", "README.md"]
s.files = [
"CHANGES.md",
"COPYING",
"BSDL",
"LEGAL",
"README.md",
"json.gemspec",
*Dir["lib/**/*.rb"],
]
if java_ext
s.platform = 'java'
s.files += Dir["lib/json/ext/**/*.jar"]
else
s.extensions = Dir["ext/json/**/extconf.rb"]
s.files += Dir["ext/json/**/*.{c,h}"]
end
end
if RUBY_ENGINE == 'jruby' && $0 == __FILE__
Gem::Builder.new(spec).build
else
spec
end
+587
View File
@@ -0,0 +1,587 @@
# frozen_string_literal: true
require 'json/common'
##
# = JavaScript \Object Notation (\JSON)
#
# \JSON is a lightweight data-interchange format.
#
# A \JSON value is one of the following:
# - Double-quoted text: <tt>"foo"</tt>.
# - Number: +1+, +1.0+, +2.0e2+.
# - Boolean: +true+, +false+.
# - Null: +null+.
# - \Array: an ordered list of values, enclosed by square brackets:
# ["foo", 1, 1.0, 2.0e2, true, false, null]
#
# - \Object: a collection of name/value pairs, enclosed by curly braces;
# each name is double-quoted text;
# the values may be any \JSON values:
# {"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}
#
# A \JSON array or object may contain nested arrays, objects, and scalars
# to any depth:
# {"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}
# [{"foo": 0, "bar": 1}, ["baz", 2]]
#
# == Using \Module \JSON
#
# To make module \JSON available in your code, begin with:
# require 'json'
#
# All examples here assume that this has been done.
#
# === Parsing \JSON
#
# You can parse a \String containing \JSON data using
# either of two methods:
# - <tt>JSON.parse(source, opts)</tt>
# - <tt>JSON.parse!(source, opts)</tt>
#
# where
# - +source+ is a Ruby object.
# - +opts+ is a \Hash object containing options
# that control both input allowed and output formatting.
#
# The difference between the two methods
# is that JSON.parse! omits some checks
# and may not be safe for some +source+ data;
# use it only for data from trusted sources.
# Use the safer method JSON.parse for less trusted sources.
#
# ==== Parsing \JSON Arrays
#
# When +source+ is a \JSON array, JSON.parse by default returns a Ruby \Array:
# json = '["foo", 1, 1.0, 2.0e2, true, false, null]'
# ruby = JSON.parse(json)
# ruby # => ["foo", 1, 1.0, 200.0, true, false, nil]
# ruby.class # => Array
#
# The \JSON array may contain nested arrays, objects, and scalars
# to any depth:
# json = '[{"foo": 0, "bar": 1}, ["baz", 2]]'
# JSON.parse(json) # => [{"foo"=>0, "bar"=>1}, ["baz", 2]]
#
# ==== Parsing \JSON \Objects
#
# When the source is a \JSON object, JSON.parse by default returns a Ruby \Hash:
# json = '{"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}'
# ruby = JSON.parse(json)
# ruby # => {"a"=>"foo", "b"=>1, "c"=>1.0, "d"=>200.0, "e"=>true, "f"=>false, "g"=>nil}
# ruby.class # => Hash
#
# The \JSON object may contain nested arrays, objects, and scalars
# to any depth:
# json = '{"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}'
# JSON.parse(json) # => {"foo"=>{"bar"=>1, "baz"=>2}, "bat"=>[0, 1, 2]}
#
# ==== Parsing \JSON Scalars
#
# When the source is a \JSON scalar (not an array or object),
# JSON.parse returns a Ruby scalar.
#
# \String:
# ruby = JSON.parse('"foo"')
# ruby # => 'foo'
# ruby.class # => String
# \Integer:
# ruby = JSON.parse('1')
# ruby # => 1
# ruby.class # => Integer
# \Float:
# ruby = JSON.parse('1.0')
# ruby # => 1.0
# ruby.class # => Float
# ruby = JSON.parse('2.0e2')
# ruby # => 200
# ruby.class # => Float
# Boolean:
# ruby = JSON.parse('true')
# ruby # => true
# ruby.class # => TrueClass
# ruby = JSON.parse('false')
# ruby # => false
# ruby.class # => FalseClass
# Null:
# ruby = JSON.parse('null')
# ruby # => nil
# ruby.class # => NilClass
#
# ==== Parsing Options
#
# ====== Input Options
#
# Option +max_nesting+ (\Integer) specifies the maximum nesting depth allowed;
# defaults to +100+; specify +false+ to disable depth checking.
#
# With the default, +false+:
# source = '[0, [1, [2, [3]]]]'
# ruby = JSON.parse(source)
# ruby # => [0, [1, [2, [3]]]]
# Too deep:
# # Raises JSON::NestingError (nesting of 2 is too deep):
# JSON.parse(source, {max_nesting: 1})
# Bad value:
# # Raises TypeError (wrong argument type Symbol (expected Fixnum)):
# JSON.parse(source, {max_nesting: :foo})
#
# ---
#
# Option +allow_nan+ (boolean) specifies whether to allow
# NaN, Infinity, and MinusInfinity in +source+;
# defaults to +false+.
#
# With the default, +false+:
# # Raises JSON::ParserError (225: unexpected token at '[NaN]'):
# JSON.parse('[NaN]')
# # Raises JSON::ParserError (232: unexpected token at '[Infinity]'):
# JSON.parse('[Infinity]')
# # Raises JSON::ParserError (248: unexpected token at '[-Infinity]'):
# JSON.parse('[-Infinity]')
# Allow:
# source = '[NaN, Infinity, -Infinity]'
# ruby = JSON.parse(source, {allow_nan: true})
# ruby # => [NaN, Infinity, -Infinity]
#
# ====== Output Options
#
# Option +symbolize_names+ (boolean) specifies whether returned \Hash keys
# should be Symbols;
# defaults to +false+ (use Strings).
#
# With the default, +false+:
# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
# ruby = JSON.parse(source)
# ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
# Use Symbols:
# ruby = JSON.parse(source, {symbolize_names: true})
# ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil}
#
# ---
#
# Option +object_class+ (\Class) specifies the Ruby class to be used
# for each \JSON object;
# defaults to \Hash.
#
# With the default, \Hash:
# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
# ruby = JSON.parse(source)
# ruby.class # => Hash
# Use class \OpenStruct:
# ruby = JSON.parse(source, {object_class: OpenStruct})
# ruby # => #<OpenStruct a="foo", b=1.0, c=true, d=false, e=nil>
#
# ---
#
# Option +array_class+ (\Class) specifies the Ruby class to be used
# for each \JSON array;
# defaults to \Array.
#
# With the default, \Array:
# source = '["foo", 1.0, true, false, null]'
# ruby = JSON.parse(source)
# ruby.class # => Array
# Use class \Set:
# ruby = JSON.parse(source, {array_class: Set})
# ruby # => #<Set: {"foo", 1.0, true, false, nil}>
#
# ---
#
# Option +create_additions+ (boolean) specifies whether to use \JSON additions in parsing.
# See {\JSON Additions}[#module-JSON-label-JSON+Additions].
#
# === Generating \JSON
#
# To generate a Ruby \String containing \JSON data,
# use method <tt>JSON.generate(source, opts)</tt>, where
# - +source+ is a Ruby object.
# - +opts+ is a \Hash object containing options
# that control both input allowed and output formatting.
#
# ==== Generating \JSON from Arrays
#
# When the source is a Ruby \Array, JSON.generate returns
# a \String containing a \JSON array:
# ruby = [0, 's', :foo]
# json = JSON.generate(ruby)
# json # => '[0,"s","foo"]'
#
# The Ruby \Array array may contain nested arrays, hashes, and scalars
# to any depth:
# ruby = [0, [1, 2], {foo: 3, bar: 4}]
# json = JSON.generate(ruby)
# json # => '[0,[1,2],{"foo":3,"bar":4}]'
#
# ==== Generating \JSON from Hashes
#
# When the source is a Ruby \Hash, JSON.generate returns
# a \String containing a \JSON object:
# ruby = {foo: 0, bar: 's', baz: :bat}
# json = JSON.generate(ruby)
# json # => '{"foo":0,"bar":"s","baz":"bat"}'
#
# The Ruby \Hash array may contain nested arrays, hashes, and scalars
# to any depth:
# ruby = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
# json = JSON.generate(ruby)
# json # => '{"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}'
#
# ==== Generating \JSON from Other Objects
#
# When the source is neither an \Array nor a \Hash,
# the generated \JSON data depends on the class of the source.
#
# When the source is a Ruby \Integer or \Float, JSON.generate returns
# a \String containing a \JSON number:
# JSON.generate(42) # => '42'
# JSON.generate(0.42) # => '0.42'
#
# When the source is a Ruby \String, JSON.generate returns
# a \String containing a \JSON string (with double-quotes):
# JSON.generate('A string') # => '"A string"'
#
# When the source is +true+, +false+ or +nil+, JSON.generate returns
# a \String containing the corresponding \JSON token:
# JSON.generate(true) # => 'true'
# JSON.generate(false) # => 'false'
# JSON.generate(nil) # => 'null'
#
# When the source is none of the above, JSON.generate returns
# a \String containing a \JSON string representation of the source:
# JSON.generate(:foo) # => '"foo"'
# JSON.generate(Complex(0, 0)) # => '"0+0i"'
# JSON.generate(Dir.new('.')) # => '"#<Dir>"'
#
# ==== Generating Options
#
# ====== Input Options
#
# Option +allow_nan+ (boolean) specifies whether
# +NaN+, +Infinity+, and <tt>-Infinity</tt> may be generated;
# defaults to +false+.
#
# With the default, +false+:
# # Raises JSON::GeneratorError (920: NaN not allowed in JSON):
# JSON.generate(JSON::NaN)
# # Raises JSON::GeneratorError (917: Infinity not allowed in JSON):
# JSON.generate(JSON::Infinity)
# # Raises JSON::GeneratorError (917: -Infinity not allowed in JSON):
# JSON.generate(JSON::MinusInfinity)
#
# Allow:
# ruby = [Float::NaN, Float::Infinity, Float::MinusInfinity]
# JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,-Infinity]'
#
# ---
#
# Option +max_nesting+ (\Integer) specifies the maximum nesting depth
# in +obj+; defaults to +100+.
#
# With the default, +100+:
# obj = [[[[[[0]]]]]]
# JSON.generate(obj) # => '[[[[[[0]]]]]]'
#
# Too deep:
# # Raises JSON::NestingError (nesting of 2 is too deep):
# JSON.generate(obj, max_nesting: 2)
#
# ====== Escaping Options
#
# Options +script_safe+ (boolean) specifies wether <tt>'\u2028'</tt>, <tt>'\u2029'</tt>
# and <tt>'/'</tt> should be escaped as to make the JSON object safe to interpolate in script
# tags.
#
# Options +ascii_only+ (boolean) specifies wether all characters outside the ASCII range
# should be escaped.
#
# ====== Output Options
#
# The default formatting options generate the most compact
# \JSON data, all on one line and with no whitespace.
#
# You can use these formatting options to generate
# \JSON data in a more open format, using whitespace.
# See also JSON.pretty_generate.
#
# - Option +array_nl+ (\String) specifies a string (usually a newline)
# to be inserted after each \JSON array; defaults to the empty \String, <tt>''</tt>.
# - Option +object_nl+ (\String) specifies a string (usually a newline)
# to be inserted after each \JSON object; defaults to the empty \String, <tt>''</tt>.
# - Option +indent+ (\String) specifies the string (usually spaces) to be
# used for indentation; defaults to the empty \String, <tt>''</tt>;
# defaults to the empty \String, <tt>''</tt>;
# has no effect unless options +array_nl+ or +object_nl+ specify newlines.
# - Option +space+ (\String) specifies a string (usually a space) to be
# inserted after the colon in each \JSON object's pair;
# defaults to the empty \String, <tt>''</tt>.
# - Option +space_before+ (\String) specifies a string (usually a space) to be
# inserted before the colon in each \JSON object's pair;
# defaults to the empty \String, <tt>''</tt>.
#
# In this example, +obj+ is used first to generate the shortest
# \JSON data (no whitespace), then again with all formatting options
# specified:
#
# obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
# json = JSON.generate(obj)
# puts 'Compact:', json
# opts = {
# array_nl: "\n",
# object_nl: "\n",
# indent: ' ',
# space_before: ' ',
# space: ' '
# }
# puts 'Open:', JSON.generate(obj, opts)
#
# Output:
# Compact:
# {"foo":["bar","baz"],"bat":{"bam":0,"bad":1}}
# Open:
# {
# "foo" : [
# "bar",
# "baz"
# ],
# "bat" : {
# "bam" : 0,
# "bad" : 1
# }
# }
#
# == \JSON Additions
#
# When you "round trip" a non-\String object from Ruby to \JSON and back,
# you have a new \String, instead of the object you began with:
# ruby0 = Range.new(0, 2)
# json = JSON.generate(ruby0)
# json # => '0..2"'
# ruby1 = JSON.parse(json)
# ruby1 # => '0..2'
# ruby1.class # => String
#
# You can use \JSON _additions_ to preserve the original object.
# The addition is an extension of a ruby class, so that:
# - \JSON.generate stores more information in the \JSON string.
# - \JSON.parse, called with option +create_additions+,
# uses that information to create a proper Ruby object.
#
# This example shows a \Range being generated into \JSON
# and parsed back into Ruby, both without and with
# the addition for \Range:
# ruby = Range.new(0, 2)
# # This passage does not use the addition for Range.
# json0 = JSON.generate(ruby)
# ruby0 = JSON.parse(json0)
# # This passage uses the addition for Range.
# require 'json/add/range'
# json1 = JSON.generate(ruby)
# ruby1 = JSON.parse(json1, create_additions: true)
# # Make a nice display.
# display = <<~EOT
# Generated JSON:
# Without addition: #{json0} (#{json0.class})
# With addition: #{json1} (#{json1.class})
# Parsed JSON:
# Without addition: #{ruby0.inspect} (#{ruby0.class})
# With addition: #{ruby1.inspect} (#{ruby1.class})
# EOT
# puts display
#
# This output shows the different results:
# Generated JSON:
# Without addition: "0..2" (String)
# With addition: {"json_class":"Range","a":[0,2,false]} (String)
# Parsed JSON:
# Without addition: "0..2" (String)
# With addition: 0..2 (Range)
#
# The \JSON module includes additions for certain classes.
# You can also craft custom additions.
# See {Custom \JSON Additions}[#module-JSON-label-Custom+JSON+Additions].
#
# === Built-in Additions
#
# The \JSON module includes additions for certain classes.
# To use an addition, +require+ its source:
# - BigDecimal: <tt>require 'json/add/bigdecimal'</tt>
# - Complex: <tt>require 'json/add/complex'</tt>
# - Date: <tt>require 'json/add/date'</tt>
# - DateTime: <tt>require 'json/add/date_time'</tt>
# - Exception: <tt>require 'json/add/exception'</tt>
# - OpenStruct: <tt>require 'json/add/ostruct'</tt>
# - Range: <tt>require 'json/add/range'</tt>
# - Rational: <tt>require 'json/add/rational'</tt>
# - Regexp: <tt>require 'json/add/regexp'</tt>
# - Set: <tt>require 'json/add/set'</tt>
# - Struct: <tt>require 'json/add/struct'</tt>
# - Symbol: <tt>require 'json/add/symbol'</tt>
# - Time: <tt>require 'json/add/time'</tt>
#
# To reduce punctuation clutter, the examples below
# show the generated \JSON via +puts+, rather than the usual +inspect+,
#
# \BigDecimal:
# require 'json/add/bigdecimal'
# ruby0 = BigDecimal(0) # 0.0
# json = JSON.generate(ruby0) # {"json_class":"BigDecimal","b":"27:0.0"}
# ruby1 = JSON.parse(json, create_additions: true) # 0.0
# ruby1.class # => BigDecimal
#
# \Complex:
# require 'json/add/complex'
# ruby0 = Complex(1+0i) # 1+0i
# json = JSON.generate(ruby0) # {"json_class":"Complex","r":1,"i":0}
# ruby1 = JSON.parse(json, create_additions: true) # 1+0i
# ruby1.class # Complex
#
# \Date:
# require 'json/add/date'
# ruby0 = Date.today # 2020-05-02
# json = JSON.generate(ruby0) # {"json_class":"Date","y":2020,"m":5,"d":2,"sg":2299161.0}
# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02
# ruby1.class # Date
#
# \DateTime:
# require 'json/add/date_time'
# ruby0 = DateTime.now # 2020-05-02T10:38:13-05:00
# json = JSON.generate(ruby0) # {"json_class":"DateTime","y":2020,"m":5,"d":2,"H":10,"M":38,"S":13,"of":"-5/24","sg":2299161.0}
# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02T10:38:13-05:00
# ruby1.class # DateTime
#
# \Exception (and its subclasses including \RuntimeError):
# require 'json/add/exception'
# ruby0 = Exception.new('A message') # A message
# json = JSON.generate(ruby0) # {"json_class":"Exception","m":"A message","b":null}
# ruby1 = JSON.parse(json, create_additions: true) # A message
# ruby1.class # Exception
# ruby0 = RuntimeError.new('Another message') # Another message
# json = JSON.generate(ruby0) # {"json_class":"RuntimeError","m":"Another message","b":null}
# ruby1 = JSON.parse(json, create_additions: true) # Another message
# ruby1.class # RuntimeError
#
# \OpenStruct:
# require 'json/add/ostruct'
# ruby0 = OpenStruct.new(name: 'Matz', language: 'Ruby') # #<OpenStruct name="Matz", language="Ruby">
# json = JSON.generate(ruby0) # {"json_class":"OpenStruct","t":{"name":"Matz","language":"Ruby"}}
# ruby1 = JSON.parse(json, create_additions: true) # #<OpenStruct name="Matz", language="Ruby">
# ruby1.class # OpenStruct
#
# \Range:
# require 'json/add/range'
# ruby0 = Range.new(0, 2) # 0..2
# json = JSON.generate(ruby0) # {"json_class":"Range","a":[0,2,false]}
# ruby1 = JSON.parse(json, create_additions: true) # 0..2
# ruby1.class # Range
#
# \Rational:
# require 'json/add/rational'
# ruby0 = Rational(1, 3) # 1/3
# json = JSON.generate(ruby0) # {"json_class":"Rational","n":1,"d":3}
# ruby1 = JSON.parse(json, create_additions: true) # 1/3
# ruby1.class # Rational
#
# \Regexp:
# require 'json/add/regexp'
# ruby0 = Regexp.new('foo') # (?-mix:foo)
# json = JSON.generate(ruby0) # {"json_class":"Regexp","o":0,"s":"foo"}
# ruby1 = JSON.parse(json, create_additions: true) # (?-mix:foo)
# ruby1.class # Regexp
#
# \Set:
# require 'json/add/set'
# ruby0 = Set.new([0, 1, 2]) # #<Set: {0, 1, 2}>
# json = JSON.generate(ruby0) # {"json_class":"Set","a":[0,1,2]}
# ruby1 = JSON.parse(json, create_additions: true) # #<Set: {0, 1, 2}>
# ruby1.class # Set
#
# \Struct:
# require 'json/add/struct'
# Customer = Struct.new(:name, :address) # Customer
# ruby0 = Customer.new("Dave", "123 Main") # #<struct Customer name="Dave", address="123 Main">
# json = JSON.generate(ruby0) # {"json_class":"Customer","v":["Dave","123 Main"]}
# ruby1 = JSON.parse(json, create_additions: true) # #<struct Customer name="Dave", address="123 Main">
# ruby1.class # Customer
#
# \Symbol:
# require 'json/add/symbol'
# ruby0 = :foo # foo
# json = JSON.generate(ruby0) # {"json_class":"Symbol","s":"foo"}
# ruby1 = JSON.parse(json, create_additions: true) # foo
# ruby1.class # Symbol
#
# \Time:
# require 'json/add/time'
# ruby0 = Time.now # 2020-05-02 11:28:26 -0500
# json = JSON.generate(ruby0) # {"json_class":"Time","s":1588436906,"n":840560000}
# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02 11:28:26 -0500
# ruby1.class # Time
#
#
# === Custom \JSON Additions
#
# In addition to the \JSON additions provided,
# you can craft \JSON additions of your own,
# either for Ruby built-in classes or for user-defined classes.
#
# Here's a user-defined class +Foo+:
# class Foo
# attr_accessor :bar, :baz
# def initialize(bar, baz)
# self.bar = bar
# self.baz = baz
# end
# end
#
# Here's the \JSON addition for it:
# # Extend class Foo with JSON addition.
# class Foo
# # Serialize Foo object with its class name and arguments
# def to_json(*args)
# {
# JSON.create_id => self.class.name,
# 'a' => [ bar, baz ]
# }.to_json(*args)
# end
# # Deserialize JSON string by constructing new Foo object with arguments.
# def self.json_create(object)
# new(*object['a'])
# end
# end
#
# Demonstration:
# require 'json'
# # This Foo object has no custom addition.
# foo0 = Foo.new(0, 1)
# json0 = JSON.generate(foo0)
# obj0 = JSON.parse(json0)
# # Lood the custom addition.
# require_relative 'foo_addition'
# # This foo has the custom addition.
# foo1 = Foo.new(0, 1)
# json1 = JSON.generate(foo1)
# obj1 = JSON.parse(json1, create_additions: true)
# # Make a nice display.
# display = <<~EOT
# Generated JSON:
# Without custom addition: #{json0} (#{json0.class})
# With custom addition: #{json1} (#{json1.class})
# Parsed JSON:
# Without custom addition: #{obj0.inspect} (#{obj0.class})
# With custom addition: #{obj1.inspect} (#{obj1.class})
# EOT
# puts display
#
# Output:
#
# Generated JSON:
# Without custom addition: "#<Foo:0x0000000006534e80>" (String)
# With custom addition: {"json_class":"Foo","a":[0,1]} (String)
# Parsed JSON:
# Without custom addition: "#<Foo:0x0000000006534e80>" (String)
# With custom addition: #<Foo:0x0000000006473bb8 @bar=0, @baz=1> (Foo)
#
module JSON
require 'json/version'
require 'json/ext'
end
@@ -0,0 +1,58 @@
# frozen_string_literal: true
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
begin
require 'bigdecimal'
rescue LoadError
end
class BigDecimal
# See #as_json.
def self.json_create(object)
BigDecimal._load object['b']
end
# Methods <tt>BigDecimal#as_json</tt> and +BigDecimal.json_create+ may be used
# to serialize and deserialize a \BigDecimal object;
# see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
#
# \Method <tt>BigDecimal#as_json</tt> serializes +self+,
# returning a 2-element hash representing +self+:
#
# require 'json/add/bigdecimal'
# x = BigDecimal(2).as_json # => {"json_class"=>"BigDecimal", "b"=>"27:0.2e1"}
# y = BigDecimal(2.0, 4).as_json # => {"json_class"=>"BigDecimal", "b"=>"36:0.2e1"}
# z = BigDecimal(Complex(2, 0)).as_json # => {"json_class"=>"BigDecimal", "b"=>"27:0.2e1"}
#
# \Method +JSON.create+ deserializes such a hash, returning a \BigDecimal object:
#
# BigDecimal.json_create(x) # => 0.2e1
# BigDecimal.json_create(y) # => 0.2e1
# BigDecimal.json_create(z) # => 0.2e1
#
def as_json(*)
{
JSON.create_id => self.class.name,
'b' => _dump.force_encoding(Encoding::UTF_8),
}
end
# Returns a JSON string representing +self+:
#
# require 'json/add/bigdecimal'
# puts BigDecimal(2).to_json
# puts BigDecimal(2.0, 4).to_json
# puts BigDecimal(Complex(2, 0)).to_json
#
# Output:
#
# {"json_class":"BigDecimal","b":"27:0.2e1"}
# {"json_class":"BigDecimal","b":"36:0.2e1"}
# {"json_class":"BigDecimal","b":"27:0.2e1"}
#
def to_json(*args)
as_json.to_json(*args)
end
end if defined?(::BigDecimal)
@@ -0,0 +1,51 @@
# frozen_string_literal: true
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
class Complex
# See #as_json.
def self.json_create(object)
Complex(object['r'], object['i'])
end
# Methods <tt>Complex#as_json</tt> and +Complex.json_create+ may be used
# to serialize and deserialize a \Complex object;
# see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
#
# \Method <tt>Complex#as_json</tt> serializes +self+,
# returning a 2-element hash representing +self+:
#
# require 'json/add/complex'
# x = Complex(2).as_json # => {"json_class"=>"Complex", "r"=>2, "i"=>0}
# y = Complex(2.0, 4).as_json # => {"json_class"=>"Complex", "r"=>2.0, "i"=>4}
#
# \Method +JSON.create+ deserializes such a hash, returning a \Complex object:
#
# Complex.json_create(x) # => (2+0i)
# Complex.json_create(y) # => (2.0+4i)
#
def as_json(*)
{
JSON.create_id => self.class.name,
'r' => real,
'i' => imag,
}
end
# Returns a JSON string representing +self+:
#
# require 'json/add/complex'
# puts Complex(2).to_json
# puts Complex(2.0, 4).to_json
#
# Output:
#
# {"json_class":"Complex","r":2,"i":0}
# {"json_class":"Complex","r":2.0,"i":4}
#
def to_json(*args)
as_json.to_json(*args)
end
end
+12
View File
@@ -0,0 +1,12 @@
# frozen_string_literal: true
# This file requires the implementations of ruby core's custom objects for
# serialisation/deserialisation.
require 'json/add/date'
require 'json/add/date_time'
require 'json/add/exception'
require 'json/add/range'
require 'json/add/regexp'
require 'json/add/struct'
require 'json/add/symbol'
require 'json/add/time'
+54
View File
@@ -0,0 +1,54 @@
# frozen_string_literal: true
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
require 'date'
class Date
# See #as_json.
def self.json_create(object)
civil(*object.values_at('y', 'm', 'd', 'sg'))
end
alias start sg unless method_defined?(:start)
# Methods <tt>Date#as_json</tt> and +Date.json_create+ may be used
# to serialize and deserialize a \Date object;
# see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
#
# \Method <tt>Date#as_json</tt> serializes +self+,
# returning a 2-element hash representing +self+:
#
# require 'json/add/date'
# x = Date.today.as_json
# # => {"json_class"=>"Date", "y"=>2023, "m"=>11, "d"=>21, "sg"=>2299161.0}
#
# \Method +JSON.create+ deserializes such a hash, returning a \Date object:
#
# Date.json_create(x)
# # => #<Date: 2023-11-21 ((2460270j,0s,0n),+0s,2299161j)>
#
def as_json(*)
{
JSON.create_id => self.class.name,
'y' => year,
'm' => month,
'd' => day,
'sg' => start,
}
end
# Returns a JSON string representing +self+:
#
# require 'json/add/date'
# puts Date.today.to_json
#
# Output:
#
# {"json_class":"Date","y":2023,"m":11,"d":21,"sg":2299161.0}
#
def to_json(*args)
as_json.to_json(*args)
end
end
@@ -0,0 +1,67 @@
# frozen_string_literal: true
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
require 'date'
class DateTime
# See #as_json.
def self.json_create(object)
args = object.values_at('y', 'm', 'd', 'H', 'M', 'S')
of_a, of_b = object['of'].split('/')
if of_b and of_b != '0'
args << Rational(of_a.to_i, of_b.to_i)
else
args << of_a
end
args << object['sg']
civil(*args)
end
alias start sg unless method_defined?(:start)
# Methods <tt>DateTime#as_json</tt> and +DateTime.json_create+ may be used
# to serialize and deserialize a \DateTime object;
# see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
#
# \Method <tt>DateTime#as_json</tt> serializes +self+,
# returning a 2-element hash representing +self+:
#
# require 'json/add/datetime'
# x = DateTime.now.as_json
# # => {"json_class"=>"DateTime", "y"=>2023, "m"=>11, "d"=>21, "sg"=>2299161.0}
#
# \Method +JSON.create+ deserializes such a hash, returning a \DateTime object:
#
# DateTime.json_create(x) # BUG? Raises Date::Error "invalid date"
#
def as_json(*)
{
JSON.create_id => self.class.name,
'y' => year,
'm' => month,
'd' => day,
'H' => hour,
'M' => min,
'S' => sec,
'of' => offset.to_s,
'sg' => start,
}
end
# Returns a JSON string representing +self+:
#
# require 'json/add/datetime'
# puts DateTime.now.to_json
#
# Output:
#
# {"json_class":"DateTime","y":2023,"m":11,"d":21,"sg":2299161.0}
#
def to_json(*args)
as_json.to_json(*args)
end
end
@@ -0,0 +1,49 @@
# frozen_string_literal: true
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
class Exception
# See #as_json.
def self.json_create(object)
result = new(object['m'])
result.set_backtrace object['b']
result
end
# Methods <tt>Exception#as_json</tt> and +Exception.json_create+ may be used
# to serialize and deserialize a \Exception object;
# see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
#
# \Method <tt>Exception#as_json</tt> serializes +self+,
# returning a 2-element hash representing +self+:
#
# require 'json/add/exception'
# x = Exception.new('Foo').as_json # => {"json_class"=>"Exception", "m"=>"Foo", "b"=>nil}
#
# \Method +JSON.create+ deserializes such a hash, returning a \Exception object:
#
# Exception.json_create(x) # => #<Exception: Foo>
#
def as_json(*)
{
JSON.create_id => self.class.name,
'm' => message,
'b' => backtrace,
}
end
# Returns a JSON string representing +self+:
#
# require 'json/add/exception'
# puts Exception.new('Foo').to_json
#
# Output:
#
# {"json_class":"Exception","m":"Foo","b":null}
#
def to_json(*args)
as_json.to_json(*args)
end
end
@@ -0,0 +1,54 @@
# frozen_string_literal: true
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
begin
require 'ostruct'
rescue LoadError
end
class OpenStruct
# See #as_json.
def self.json_create(object)
new(object['t'] || object[:t])
end
# Methods <tt>OpenStruct#as_json</tt> and +OpenStruct.json_create+ may be used
# to serialize and deserialize a \OpenStruct object;
# see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
#
# \Method <tt>OpenStruct#as_json</tt> serializes +self+,
# returning a 2-element hash representing +self+:
#
# require 'json/add/ostruct'
# x = OpenStruct.new('name' => 'Rowdy', :age => nil).as_json
# # => {"json_class"=>"OpenStruct", "t"=>{:name=>'Rowdy', :age=>nil}}
#
# \Method +JSON.create+ deserializes such a hash, returning a \OpenStruct object:
#
# OpenStruct.json_create(x)
# # => #<OpenStruct name='Rowdy', age=nil>
#
def as_json(*)
klass = self.class.name
klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!"
{
JSON.create_id => klass,
't' => table,
}
end
# Returns a JSON string representing +self+:
#
# require 'json/add/ostruct'
# puts OpenStruct.new('name' => 'Rowdy', :age => nil).to_json
#
# Output:
#
# {"json_class":"OpenStruct","t":{'name':'Rowdy',"age":null}}
#
def to_json(*args)
as_json.to_json(*args)
end
end if defined?(::OpenStruct)
@@ -0,0 +1,54 @@
# frozen_string_literal: true
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
class Range
# See #as_json.
def self.json_create(object)
new(*object['a'])
end
# Methods <tt>Range#as_json</tt> and +Range.json_create+ may be used
# to serialize and deserialize a \Range object;
# see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
#
# \Method <tt>Range#as_json</tt> serializes +self+,
# returning a 2-element hash representing +self+:
#
# require 'json/add/range'
# x = (1..4).as_json # => {"json_class"=>"Range", "a"=>[1, 4, false]}
# y = (1...4).as_json # => {"json_class"=>"Range", "a"=>[1, 4, true]}
# z = ('a'..'d').as_json # => {"json_class"=>"Range", "a"=>["a", "d", false]}
#
# \Method +JSON.create+ deserializes such a hash, returning a \Range object:
#
# Range.json_create(x) # => 1..4
# Range.json_create(y) # => 1...4
# Range.json_create(z) # => "a".."d"
#
def as_json(*)
{
JSON.create_id => self.class.name,
'a' => [ first, last, exclude_end? ]
}
end
# Returns a JSON string representing +self+:
#
# require 'json/add/range'
# puts (1..4).to_json
# puts (1...4).to_json
# puts ('a'..'d').to_json
#
# Output:
#
# {"json_class":"Range","a":[1,4,false]}
# {"json_class":"Range","a":[1,4,true]}
# {"json_class":"Range","a":["a","d",false]}
#
def to_json(*args)
as_json.to_json(*args)
end
end
@@ -0,0 +1,49 @@
# frozen_string_literal: true
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
class Rational
# See #as_json.
def self.json_create(object)
Rational(object['n'], object['d'])
end
# Methods <tt>Rational#as_json</tt> and +Rational.json_create+ may be used
# to serialize and deserialize a \Rational object;
# see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
#
# \Method <tt>Rational#as_json</tt> serializes +self+,
# returning a 2-element hash representing +self+:
#
# require 'json/add/rational'
# x = Rational(2, 3).as_json
# # => {"json_class"=>"Rational", "n"=>2, "d"=>3}
#
# \Method +JSON.create+ deserializes such a hash, returning a \Rational object:
#
# Rational.json_create(x)
# # => (2/3)
#
def as_json(*)
{
JSON.create_id => self.class.name,
'n' => numerator,
'd' => denominator,
}
end
# Returns a JSON string representing +self+:
#
# require 'json/add/rational'
# puts Rational(2, 3).to_json
#
# Output:
#
# {"json_class":"Rational","n":2,"d":3}
#
def to_json(*args)
as_json.to_json(*args)
end
end
@@ -0,0 +1,48 @@
# frozen_string_literal: true
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
class Regexp
# See #as_json.
def self.json_create(object)
new(object['s'], object['o'])
end
# Methods <tt>Regexp#as_json</tt> and +Regexp.json_create+ may be used
# to serialize and deserialize a \Regexp object;
# see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
#
# \Method <tt>Regexp#as_json</tt> serializes +self+,
# returning a 2-element hash representing +self+:
#
# require 'json/add/regexp'
# x = /foo/.as_json
# # => {"json_class"=>"Regexp", "o"=>0, "s"=>"foo"}
#
# \Method +JSON.create+ deserializes such a hash, returning a \Regexp object:
#
# Regexp.json_create(x) # => /foo/
#
def as_json(*)
{
JSON.create_id => self.class.name,
'o' => options,
's' => source,
}
end
# Returns a JSON string representing +self+:
#
# require 'json/add/regexp'
# puts /foo/.to_json
#
# Output:
#
# {"json_class":"Regexp","o":0,"s":"foo"}
#
def to_json(*args)
as_json.to_json(*args)
end
end
+48
View File
@@ -0,0 +1,48 @@
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
defined?(::Set) or require 'set'
class Set
# See #as_json.
def self.json_create(object)
new object['a']
end
# Methods <tt>Set#as_json</tt> and +Set.json_create+ may be used
# to serialize and deserialize a \Set object;
# see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
#
# \Method <tt>Set#as_json</tt> serializes +self+,
# returning a 2-element hash representing +self+:
#
# require 'json/add/set'
# x = Set.new(%w/foo bar baz/).as_json
# # => {"json_class"=>"Set", "a"=>["foo", "bar", "baz"]}
#
# \Method +JSON.create+ deserializes such a hash, returning a \Set object:
#
# Set.json_create(x) # => #<Set: {"foo", "bar", "baz"}>
#
def as_json(*)
{
JSON.create_id => self.class.name,
'a' => to_a,
}
end
# Returns a JSON string representing +self+:
#
# require 'json/add/set'
# puts Set.new(%w/foo bar baz/).to_json
#
# Output:
#
# {"json_class":"Set","a":["foo","bar","baz"]}
#
def to_json(*args)
as_json.to_json(*args)
end
end
@@ -0,0 +1,52 @@
# frozen_string_literal: true
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
class Struct
# See #as_json.
def self.json_create(object)
new(*object['v'])
end
# Methods <tt>Struct#as_json</tt> and +Struct.json_create+ may be used
# to serialize and deserialize a \Struct object;
# see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
#
# \Method <tt>Struct#as_json</tt> serializes +self+,
# returning a 2-element hash representing +self+:
#
# require 'json/add/struct'
# Customer = Struct.new('Customer', :name, :address, :zip)
# x = Struct::Customer.new.as_json
# # => {"json_class"=>"Struct::Customer", "v"=>[nil, nil, nil]}
#
# \Method +JSON.create+ deserializes such a hash, returning a \Struct object:
#
# Struct::Customer.json_create(x)
# # => #<struct Struct::Customer name=nil, address=nil, zip=nil>
#
def as_json(*)
klass = self.class.name
klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!"
{
JSON.create_id => klass,
'v' => values,
}
end
# Returns a JSON string representing +self+:
#
# require 'json/add/struct'
# Customer = Struct.new('Customer', :name, :address, :zip)
# puts Struct::Customer.new.to_json
#
# Output:
#
# {"json_class":"Struct","t":{'name':'Rowdy',"age":null}}
#
def to_json(*args)
as_json.to_json(*args)
end
end
@@ -0,0 +1,52 @@
# frozen_string_literal: true
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
class Symbol
# Methods <tt>Symbol#as_json</tt> and +Symbol.json_create+ may be used
# to serialize and deserialize a \Symbol object;
# see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
#
# \Method <tt>Symbol#as_json</tt> serializes +self+,
# returning a 2-element hash representing +self+:
#
# require 'json/add/symbol'
# x = :foo.as_json
# # => {"json_class"=>"Symbol", "s"=>"foo"}
#
# \Method +JSON.create+ deserializes such a hash, returning a \Symbol object:
#
# Symbol.json_create(x) # => :foo
#
def as_json(*)
{
JSON.create_id => self.class.name,
's' => to_s,
}
end
# Returns a JSON string representing +self+:
#
# require 'json/add/symbol'
# puts :foo.to_json
#
# Output:
#
# # {"json_class":"Symbol","s":"foo"}
#
def to_json(state = nil, *a)
state = ::JSON::State.from_state(state)
if state.strict?
super
else
as_json.to_json(state, *a)
end
end
# See #as_json.
def self.json_create(o)
o['s'].to_sym
end
end
+52
View File
@@ -0,0 +1,52 @@
# frozen_string_literal: true
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
class Time
# See #as_json.
def self.json_create(object)
if usec = object.delete('u') # used to be tv_usec -> tv_nsec
object['n'] = usec * 1000
end
at(object['s'], Rational(object['n'], 1000))
end
# Methods <tt>Time#as_json</tt> and +Time.json_create+ may be used
# to serialize and deserialize a \Time object;
# see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
#
# \Method <tt>Time#as_json</tt> serializes +self+,
# returning a 2-element hash representing +self+:
#
# require 'json/add/time'
# x = Time.now.as_json
# # => {"json_class"=>"Time", "s"=>1700931656, "n"=>472846644}
#
# \Method +JSON.create+ deserializes such a hash, returning a \Time object:
#
# Time.json_create(x)
# # => 2023-11-25 11:00:56.472846644 -0600
#
def as_json(*)
{
JSON.create_id => self.class.name,
's' => tv_sec,
'n' => tv_nsec,
}
end
# Returns a JSON string representing +self+:
#
# require 'json/add/time'
# puts Time.now.to_json
#
# Output:
#
# {"json_class":"Time","s":1700931678,"n":980650786}
#
def to_json(*args)
as_json.to_json(*args)
end
end
+987
View File
@@ -0,0 +1,987 @@
# frozen_string_literal: true
require 'json/version'
module JSON
autoload :GenericObject, 'json/generic_object'
NOT_SET = Object.new.freeze
private_constant :NOT_SET
class << self
# :call-seq:
# JSON[object] -> new_array or new_string
#
# If +object+ is a \String,
# calls JSON.parse with +object+ and +opts+ (see method #parse):
# json = '[0, 1, null]'
# JSON[json]# => [0, 1, nil]
#
# Otherwise, calls JSON.generate with +object+ and +opts+ (see method #generate):
# ruby = [0, 1, nil]
# JSON[ruby] # => '[0,1,null]'
def [](object, opts = {})
if object.is_a?(String)
return JSON.parse(object, opts)
elsif object.respond_to?(:to_str)
str = object.to_str
if str.is_a?(String)
return JSON.parse(str, opts)
end
end
JSON.generate(object, opts)
end
# Returns the JSON parser class that is used by JSON.
attr_reader :parser
# Set the JSON parser class _parser_ to be used by JSON.
def parser=(parser) # :nodoc:
@parser = parser
remove_const :Parser if const_defined?(:Parser, false)
const_set :Parser, parser
end
# Return the constant located at _path_. The format of _path_ has to be
# either ::A::B::C or A::B::C. In any case, A has to be located at the top
# level (absolute namespace path?). If there doesn't exist a constant at
# the given path, an ArgumentError is raised.
def deep_const_get(path) # :nodoc:
Object.const_get(path)
rescue NameError => e
raise ArgumentError, "can't get const #{path}: #{e}"
end
# Set the module _generator_ to be used by JSON.
def generator=(generator) # :nodoc:
old, $VERBOSE = $VERBOSE, nil
@generator = generator
generator_methods = generator::GeneratorMethods
for const in generator_methods.constants
klass = const_get(const)
modul = generator_methods.const_get(const)
klass.class_eval do
instance_methods(false).each do |m|
m.to_s == 'to_json' and remove_method m
end
include modul
end
end
self.state = generator::State
const_set :State, self.state
const_set :SAFE_STATE_PROTOTYPE, State.new # for JRuby
const_set :FAST_STATE_PROTOTYPE, create_fast_state
const_set :PRETTY_STATE_PROTOTYPE, create_pretty_state
ensure
$VERBOSE = old
end
def create_fast_state
State.new(
:indent => '',
:space => '',
:object_nl => "",
:array_nl => "",
:max_nesting => false
)
end
def create_pretty_state
State.new(
:indent => ' ',
:space => ' ',
:object_nl => "\n",
:array_nl => "\n"
)
end
# Returns the JSON generator module that is used by JSON.
attr_reader :generator
# Sets or Returns the JSON generator state class that is used by JSON.
attr_accessor :state
end
# Sets create identifier, which is used to decide if the _json_create_
# hook of a class should be called; initial value is +json_class+:
# JSON.create_id # => 'json_class'
def self.create_id=(new_value)
Thread.current[:"JSON.create_id"] = new_value.dup.freeze
end
# Returns the current create identifier.
# See also JSON.create_id=.
def self.create_id
Thread.current[:"JSON.create_id"] || 'json_class'
end
NaN = 0.0/0
Infinity = 1.0/0
MinusInfinity = -Infinity
# The base exception for JSON errors.
class JSONError < StandardError
def self.wrap(exception)
obj = new("Wrapped(#{exception.class}): #{exception.message.inspect}")
obj.set_backtrace exception.backtrace
obj
end
end
# This exception is raised if a parser error occurs.
class ParserError < JSONError; end
# This exception is raised if the nesting of parsed data structures is too
# deep.
class NestingError < ParserError; end
# :stopdoc:
class CircularDatastructure < NestingError; end
# :startdoc:
# This exception is raised if a generator or unparser error occurs.
class GeneratorError < JSONError
attr_reader :invalid_object
def initialize(message, invalid_object = nil)
super(message)
@invalid_object = invalid_object
end
def detailed_message(...)
# Exception#detailed_message doesn't exist until Ruby 3.2
super_message = defined?(super) ? super : message
if @invalid_object.nil?
super_message
else
"#{super_message}\nInvalid object: #{@invalid_object.inspect}"
end
end
end
# For backwards compatibility
UnparserError = GeneratorError # :nodoc:
# This exception is raised if the required unicode support is missing on the
# system. Usually this means that the iconv library is not installed.
class MissingUnicodeSupport < JSONError; end
# Fragment of JSON document that is to be included as is:
# fragment = JSON::Fragment.new("[1, 2, 3]")
# JSON.generate({ count: 3, items: fragments })
#
# This allows to easily assemble multiple JSON fragments that have
# been persisted somewhere without having to parse them nor resorting
# to string interpolation.
#
# Note: no validation is performed on the provided string. It is the
# responsability of the caller to ensure the string contains valid JSON.
Fragment = Struct.new(:json) do
def initialize(json)
unless string = String.try_convert(json)
raise TypeError, " no implicit conversion of #{json.class} into String"
end
super(string)
end
def to_json(state = nil, *)
json
end
end
module_function
# :call-seq:
# JSON.parse(source, opts) -> object
#
# Returns the Ruby objects created by parsing the given +source+.
#
# Argument +source+ contains the \String to be parsed.
#
# Argument +opts+, if given, contains a \Hash of options for the parsing.
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
#
# ---
#
# When +source+ is a \JSON array, returns a Ruby \Array:
# source = '["foo", 1.0, true, false, null]'
# ruby = JSON.parse(source)
# ruby # => ["foo", 1.0, true, false, nil]
# ruby.class # => Array
#
# When +source+ is a \JSON object, returns a Ruby \Hash:
# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
# ruby = JSON.parse(source)
# ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
# ruby.class # => Hash
#
# For examples of parsing for all \JSON data types, see
# {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
#
# Parses nested JSON objects:
# source = <<~JSON
# {
# "name": "Dave",
# "age" :40,
# "hats": [
# "Cattleman's",
# "Panama",
# "Tophat"
# ]
# }
# JSON
# ruby = JSON.parse(source)
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
#
# ---
#
# Raises an exception if +source+ is not valid JSON:
# # Raises JSON::ParserError (783: unexpected token at ''):
# JSON.parse('')
#
def parse(source, opts = nil)
Parser.parse(source, opts)
end
# :call-seq:
# JSON.parse!(source, opts) -> object
#
# Calls
# parse(source, opts)
# with +source+ and possibly modified +opts+.
#
# Differences from JSON.parse:
# - Option +max_nesting+, if not provided, defaults to +false+,
# which disables checking for nesting depth.
# - Option +allow_nan+, if not provided, defaults to +true+.
def parse!(source, opts = nil)
options = {
:max_nesting => false,
:allow_nan => true
}
options.merge!(opts) if opts
Parser.new(source, options).parse
end
# :call-seq:
# JSON.load_file(path, opts={}) -> object
#
# Calls:
# parse(File.read(path), opts)
#
# See method #parse.
def load_file(filespec, opts = nil)
parse(File.read(filespec, encoding: Encoding::UTF_8), opts)
end
# :call-seq:
# JSON.load_file!(path, opts = {})
#
# Calls:
# JSON.parse!(File.read(path, opts))
#
# See method #parse!
def load_file!(filespec, opts = nil)
parse!(File.read(filespec, encoding: Encoding::UTF_8), opts)
end
# :call-seq:
# JSON.generate(obj, opts = nil) -> new_string
#
# Returns a \String containing the generated \JSON data.
#
# See also JSON.fast_generate, JSON.pretty_generate.
#
# Argument +obj+ is the Ruby object to be converted to \JSON.
#
# Argument +opts+, if given, contains a \Hash of options for the generation.
# See {Generating Options}[#module-JSON-label-Generating+Options].
#
# ---
#
# When +obj+ is an \Array, returns a \String containing a \JSON array:
# obj = ["foo", 1.0, true, false, nil]
# json = JSON.generate(obj)
# json # => '["foo",1.0,true,false,null]'
#
# When +obj+ is a \Hash, returns a \String containing a \JSON object:
# obj = {foo: 0, bar: 's', baz: :bat}
# json = JSON.generate(obj)
# json # => '{"foo":0,"bar":"s","baz":"bat"}'
#
# For examples of generating from other Ruby objects, see
# {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects].
#
# ---
#
# Raises an exception if any formatting option is not a \String.
#
# Raises an exception if +obj+ contains circular references:
# a = []; b = []; a.push(b); b.push(a)
# # Raises JSON::NestingError (nesting of 100 is too deep):
# JSON.generate(a)
#
def generate(obj, opts = nil)
if State === opts
opts.generate(obj)
else
State.generate(obj, opts, nil)
end
end
# :stopdoc:
# I want to deprecate these later, so I'll first be silent about them, and
# later delete them.
alias unparse generate
module_function :unparse
# :startdoc:
# :call-seq:
# JSON.fast_generate(obj, opts) -> new_string
#
# Arguments +obj+ and +opts+ here are the same as
# arguments +obj+ and +opts+ in JSON.generate.
#
# By default, generates \JSON data without checking
# for circular references in +obj+ (option +max_nesting+ set to +false+, disabled).
#
# Raises an exception if +obj+ contains circular references:
# a = []; b = []; a.push(b); b.push(a)
# # Raises SystemStackError (stack level too deep):
# JSON.fast_generate(a)
def fast_generate(obj, opts = nil)
if State === opts
state = opts
else
state = JSON.create_fast_state.configure(opts)
end
state.generate(obj)
end
# :stopdoc:
# I want to deprecate these later, so I'll first be silent about them, and later delete them.
alias fast_unparse fast_generate
module_function :fast_unparse
# :startdoc:
# :call-seq:
# JSON.pretty_generate(obj, opts = nil) -> new_string
#
# Arguments +obj+ and +opts+ here are the same as
# arguments +obj+ and +opts+ in JSON.generate.
#
# Default options are:
# {
# indent: ' ', # Two spaces
# space: ' ', # One space
# array_nl: "\n", # Newline
# object_nl: "\n" # Newline
# }
#
# Example:
# obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
# json = JSON.pretty_generate(obj)
# puts json
# Output:
# {
# "foo": [
# "bar",
# "baz"
# ],
# "bat": {
# "bam": 0,
# "bad": 1
# }
# }
#
def pretty_generate(obj, opts = nil)
if State === opts
state, opts = opts, nil
else
state = JSON.create_pretty_state
end
if opts
if opts.respond_to? :to_hash
opts = opts.to_hash
elsif opts.respond_to? :to_h
opts = opts.to_h
else
raise TypeError, "can't convert #{opts.class} into Hash"
end
state.configure(opts)
end
state.generate(obj)
end
# :stopdoc:
# I want to deprecate these later, so I'll first be silent about them, and later delete them.
alias pretty_unparse pretty_generate
module_function :pretty_unparse
# :startdoc:
class << self
# Sets or returns default options for the JSON.unsafe_load method.
# Initially:
# opts = JSON.load_default_options
# opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
attr_accessor :unsafe_load_default_options
end
self.unsafe_load_default_options = {
:max_nesting => false,
:allow_nan => true,
:allow_blank => true,
:create_additions => true,
}
class << self
# Sets or returns default options for the JSON.load method.
# Initially:
# opts = JSON.load_default_options
# opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
attr_accessor :load_default_options
end
self.load_default_options = {
:allow_nan => true,
:allow_blank => true,
:create_additions => nil,
}
# :call-seq:
# JSON.unsafe_load(source, proc = nil, options = {}) -> object
#
# Returns the Ruby objects created by parsing the given +source+.
#
# BEWARE: This method is meant to serialise data from trusted user input,
# like from your own database server or clients under your control, it could
# be dangerous to allow untrusted users to pass JSON sources into it.
#
# - Argument +source+ must be, or be convertible to, a \String:
# - If +source+ responds to instance method +to_str+,
# <tt>source.to_str</tt> becomes the source.
# - If +source+ responds to instance method +to_io+,
# <tt>source.to_io.read</tt> becomes the source.
# - If +source+ responds to instance method +read+,
# <tt>source.read</tt> becomes the source.
# - If both of the following are true, source becomes the \String <tt>'null'</tt>:
# - Option +allow_blank+ specifies a truthy value.
# - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
# - Otherwise, +source+ remains the source.
# - Argument +proc+, if given, must be a \Proc that accepts one argument.
# It will be called recursively with each result (depth-first order).
# See details below.
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
# The default options can be changed via method JSON.unsafe_load_default_options=.
#
# ---
#
# When no +proc+ is given, modifies +source+ as above and returns the result of
# <tt>parse(source, opts)</tt>; see #parse.
#
# Source for following examples:
# source = <<~JSON
# {
# "name": "Dave",
# "age" :40,
# "hats": [
# "Cattleman's",
# "Panama",
# "Tophat"
# ]
# }
# JSON
#
# Load a \String:
# ruby = JSON.unsafe_load(source)
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
#
# Load an \IO object:
# require 'stringio'
# object = JSON.unsafe_load(StringIO.new(source))
# object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
#
# Load a \File object:
# path = 't.json'
# File.write(path, source)
# File.open(path) do |file|
# JSON.unsafe_load(file)
# end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
#
# ---
#
# When +proc+ is given:
# - Modifies +source+ as above.
# - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
# - Recursively calls <tt>proc(result)</tt>.
# - Returns the final result.
#
# Example:
# require 'json'
#
# # Some classes for the example.
# class Base
# def initialize(attributes)
# @attributes = attributes
# end
# end
# class User < Base; end
# class Account < Base; end
# class Admin < Base; end
# # The JSON source.
# json = <<-EOF
# {
# "users": [
# {"type": "User", "username": "jane", "email": "jane@example.com"},
# {"type": "User", "username": "john", "email": "john@example.com"}
# ],
# "accounts": [
# {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
# {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
# ],
# "admins": {"type": "Admin", "password": "0wn3d"}
# }
# EOF
# # Deserializer method.
# def deserialize_obj(obj, safe_types = %w(User Account Admin))
# type = obj.is_a?(Hash) && obj["type"]
# safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
# end
# # Call to JSON.unsafe_load
# ruby = JSON.unsafe_load(json, proc {|obj|
# case obj
# when Hash
# obj.each {|k, v| obj[k] = deserialize_obj v }
# when Array
# obj.map! {|v| deserialize_obj v }
# end
# })
# pp ruby
# Output:
# {"users"=>
# [#<User:0x00000000064c4c98
# @attributes=
# {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
# #<User:0x00000000064c4bd0
# @attributes=
# {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
# "accounts"=>
# [{"account"=>
# #<Account:0x00000000064c4928
# @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
# {"account"=>
# #<Account:0x00000000064c4680
# @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
# "admins"=>
# #<Admin:0x00000000064c41f8
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
#
def unsafe_load(source, proc = nil, options = nil)
opts = if options.nil?
unsafe_load_default_options
else
unsafe_load_default_options.merge(options)
end
unless source.is_a?(String)
if source.respond_to? :to_str
source = source.to_str
elsif source.respond_to? :to_io
source = source.to_io.read
elsif source.respond_to?(:read)
source = source.read
end
end
if opts[:allow_blank] && (source.nil? || source.empty?)
source = 'null'
end
result = parse(source, opts)
recurse_proc(result, &proc) if proc
result
end
# :call-seq:
# JSON.load(source, proc = nil, options = {}) -> object
#
# Returns the Ruby objects created by parsing the given +source+.
#
# BEWARE: This method is meant to serialise data from trusted user input,
# like from your own database server or clients under your control, it could
# be dangerous to allow untrusted users to pass JSON sources into it.
# If you must use it, use JSON.unsafe_load instead to make it clear.
#
# Since JSON version 2.8.0, `load` emits a deprecation warning when a
# non native type is deserialized, without `create_additions` being explicitly
# enabled, and in JSON version 3.0, `load` will have `create_additions` disabled
# by default.
#
# - Argument +source+ must be, or be convertible to, a \String:
# - If +source+ responds to instance method +to_str+,
# <tt>source.to_str</tt> becomes the source.
# - If +source+ responds to instance method +to_io+,
# <tt>source.to_io.read</tt> becomes the source.
# - If +source+ responds to instance method +read+,
# <tt>source.read</tt> becomes the source.
# - If both of the following are true, source becomes the \String <tt>'null'</tt>:
# - Option +allow_blank+ specifies a truthy value.
# - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
# - Otherwise, +source+ remains the source.
# - Argument +proc+, if given, must be a \Proc that accepts one argument.
# It will be called recursively with each result (depth-first order).
# See details below.
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
# The default options can be changed via method JSON.load_default_options=.
#
# ---
#
# When no +proc+ is given, modifies +source+ as above and returns the result of
# <tt>parse(source, opts)</tt>; see #parse.
#
# Source for following examples:
# source = <<~JSON
# {
# "name": "Dave",
# "age" :40,
# "hats": [
# "Cattleman's",
# "Panama",
# "Tophat"
# ]
# }
# JSON
#
# Load a \String:
# ruby = JSON.load(source)
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
#
# Load an \IO object:
# require 'stringio'
# object = JSON.load(StringIO.new(source))
# object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
#
# Load a \File object:
# path = 't.json'
# File.write(path, source)
# File.open(path) do |file|
# JSON.load(file)
# end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
#
# ---
#
# When +proc+ is given:
# - Modifies +source+ as above.
# - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
# - Recursively calls <tt>proc(result)</tt>.
# - Returns the final result.
#
# Example:
# require 'json'
#
# # Some classes for the example.
# class Base
# def initialize(attributes)
# @attributes = attributes
# end
# end
# class User < Base; end
# class Account < Base; end
# class Admin < Base; end
# # The JSON source.
# json = <<-EOF
# {
# "users": [
# {"type": "User", "username": "jane", "email": "jane@example.com"},
# {"type": "User", "username": "john", "email": "john@example.com"}
# ],
# "accounts": [
# {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
# {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
# ],
# "admins": {"type": "Admin", "password": "0wn3d"}
# }
# EOF
# # Deserializer method.
# def deserialize_obj(obj, safe_types = %w(User Account Admin))
# type = obj.is_a?(Hash) && obj["type"]
# safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
# end
# # Call to JSON.load
# ruby = JSON.load(json, proc {|obj|
# case obj
# when Hash
# obj.each {|k, v| obj[k] = deserialize_obj v }
# when Array
# obj.map! {|v| deserialize_obj v }
# end
# })
# pp ruby
# Output:
# {"users"=>
# [#<User:0x00000000064c4c98
# @attributes=
# {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
# #<User:0x00000000064c4bd0
# @attributes=
# {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
# "accounts"=>
# [{"account"=>
# #<Account:0x00000000064c4928
# @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
# {"account"=>
# #<Account:0x00000000064c4680
# @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
# "admins"=>
# #<Admin:0x00000000064c41f8
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
#
def load(source, proc = nil, options = nil)
opts = if options.nil?
load_default_options
else
load_default_options.merge(options)
end
unless source.is_a?(String)
if source.respond_to? :to_str
source = source.to_str
elsif source.respond_to? :to_io
source = source.to_io.read
elsif source.respond_to?(:read)
source = source.read
end
end
if opts[:allow_blank] && (source.nil? || source.empty?)
source = 'null'
end
result = parse(source, opts)
recurse_proc(result, &proc) if proc
result
end
# Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
def recurse_proc(result, &proc) # :nodoc:
case result
when Array
result.each { |x| recurse_proc x, &proc }
proc.call result
when Hash
result.each { |x, y| recurse_proc x, &proc; recurse_proc y, &proc }
proc.call result
else
proc.call result
end
end
alias restore load
module_function :restore
class << self
# Sets or returns the default options for the JSON.dump method.
# Initially:
# opts = JSON.dump_default_options
# opts # => {:max_nesting=>false, :allow_nan=>true}
attr_accessor :dump_default_options
end
self.dump_default_options = {
:max_nesting => false,
:allow_nan => true,
}
# :call-seq:
# JSON.dump(obj, io = nil, limit = nil)
#
# Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result.
#
# The default options can be changed via method JSON.dump_default_options.
#
# - Argument +io+, if given, should respond to method +write+;
# the \JSON \String is written to +io+, and +io+ is returned.
# If +io+ is not given, the \JSON \String is returned.
# - Argument +limit+, if given, is passed to JSON.generate as option +max_nesting+.
#
# ---
#
# When argument +io+ is not given, returns the \JSON \String generated from +obj+:
# obj = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
# json = JSON.dump(obj)
# json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}"
#
# When argument +io+ is given, writes the \JSON \String to +io+ and returns +io+:
# path = 't.json'
# File.open(path, 'w') do |file|
# JSON.dump(obj, file)
# end # => #<File:t.json (closed)>
# puts File.read(path)
# Output:
# {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
def dump(obj, anIO = nil, limit = nil, kwargs = nil)
if kwargs.nil?
if limit.nil?
if anIO.is_a?(Hash)
kwargs = anIO
anIO = nil
end
elsif limit.is_a?(Hash)
kwargs = limit
limit = nil
end
end
unless anIO.nil?
if anIO.respond_to?(:to_io)
anIO = anIO.to_io
elsif limit.nil? && !anIO.respond_to?(:write)
anIO, limit = nil, anIO
end
end
opts = JSON.dump_default_options
opts = opts.merge(:max_nesting => limit) if limit
opts = opts.merge(kwargs) if kwargs
begin
State.generate(obj, opts, anIO)
rescue JSON::NestingError
raise ArgumentError, "exceed depth limit"
end
end
# Encodes string using String.encode.
def self.iconv(to, from, string)
string.encode(to, from)
end
# JSON::Coder holds a parser and generator configuration.
#
# module MyApp
# JSONC_CODER = JSON::Coder.new(
# allow_trailing_comma: true
# )
# end
#
# MyApp::JSONC_CODER.load(document)
#
class Coder
# :call-seq:
# JSON.new(options = nil, &block)
#
# Argument +options+, if given, contains a \Hash of options for both parsing and generating.
# See {Parsing Options}[#module-JSON-label-Parsing+Options], and {Generating Options}[#module-JSON-label-Generating+Options].
#
# For generation, the <tt>strict: true</tt> option is always set. When a Ruby object with no native \JSON counterpart is
# encoutered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native
# \JSON counterpart:
#
# module MyApp
# API_JSON_CODER = JSON::Coder.new do |object|
# case object
# when Time
# object.iso8601(3)
# else
# object # Unknown type, will raise
# end
# end
# end
#
# puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
#
def initialize(options = nil, &as_json)
if options.nil?
options = { strict: true }
else
options = options.dup
options[:strict] = true
end
options[:as_json] = as_json if as_json
options[:create_additions] = false unless options.key?(:create_additions)
@state = State.new(options).freeze
@parser_config = Ext::Parser::Config.new(options)
end
# call-seq:
# dump(object) -> String
# dump(object, io) -> io
#
# Serialize the given object into a \JSON document.
def dump(object, io = nil)
@state.generate_new(object, io)
end
alias_method :generate, :dump
# call-seq:
# load(string) -> Object
#
# Parse the given \JSON document and return an equivalent Ruby object.
def load(source)
@parser_config.parse(source)
end
alias_method :parse, :load
# call-seq:
# load(path) -> Object
#
# Parse the given \JSON document and return an equivalent Ruby object.
def load_file(path)
load(File.read(path, encoding: Encoding::UTF_8))
end
end
end
module ::Kernel
private
# Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in
# one line.
def j(*objs)
objs.each do |obj|
puts JSON::generate(obj, :allow_nan => true, :max_nesting => false)
end
nil
end
# Outputs _objs_ to STDOUT as JSON strings in a pretty format, with
# indentation and over many lines.
def jj(*objs)
objs.each do |obj|
puts JSON::pretty_generate(obj, :allow_nan => true, :max_nesting => false)
end
nil
end
# If _object_ is string-like, parse the string and return the parsed result as
# a Ruby data structure. Otherwise, generate a JSON text from the Ruby data
# structure object and return it.
#
# The _opts_ argument is passed through to generate/parse respectively. See
# generate and parse for their documentation.
def JSON(object, *args)
if object.is_a?(String)
return JSON.parse(object, args.first)
elsif object.respond_to?(:to_str)
str = object.to_str
if str.is_a?(String)
return JSON.parse(object.to_str, args.first)
end
end
JSON.generate(object, args.first)
end
end
# Extends any Class to include _json_creatable?_ method.
class ::Class
# Returns true if this class can be used to create an instance
# from a serialised JSON string. The class has to implement a class
# method _json_create_ that expects a hash as first parameter. The hash
# should include the required data.
def json_creatable?
respond_to?(:json_create)
end
end
+45
View File
@@ -0,0 +1,45 @@
# frozen_string_literal: true
require 'json/common'
module JSON
# This module holds all the modules/classes that implement JSON's
# functionality as C extensions.
module Ext
class Parser
class << self
def parse(...)
new(...).parse
end
alias_method :parse, :parse # Allow redefinition by extensions
end
def initialize(source, opts = nil)
@source = source
@config = Config.new(opts)
end
def source
@source.dup
end
def parse
@config.parse(@source)
end
end
require 'json/ext/parser'
Ext::Parser::Config = Ext::ParserConfig
JSON.parser = Ext::Parser
if RUBY_ENGINE == 'truffleruby'
require 'json/truffle_ruby/generator'
JSON.generator = ::JSON::TruffleRuby::Generator
else
require 'json/ext/generator'
JSON.generator = Generator
end
end
JSON_LOADED = true unless defined?(::JSON::JSON_LOADED)
end
Binary file not shown.
@@ -0,0 +1,106 @@
# frozen_string_literal: true
module JSON
module Ext
module Generator
class State
# call-seq: new(opts = {})
#
# Instantiates a new State object, configured by _opts_.
#
# _opts_ can have the following keys:
#
# * *indent*: a string used to indent levels (default: ''),
# * *space*: a string that is put after, a : or , delimiter (default: ''),
# * *space_before*: a string that is put before a : pair delimiter (default: ''),
# * *object_nl*: a string that is put at the end of a JSON object (default: ''),
# * *array_nl*: a string that is put at the end of a JSON array (default: ''),
# * *allow_nan*: true if NaN, Infinity, and -Infinity should be
# generated, otherwise an exception is thrown, if these values are
# encountered. This options defaults to false.
# * *ascii_only*: true if only ASCII characters should be generated. This
# option defaults to false.
# * *buffer_initial_length*: sets the initial length of the generator's
# internal buffer.
def initialize(opts = nil)
if opts && !opts.empty?
configure(opts)
end
end
# call-seq: configure(opts)
#
# Configure this State instance with the Hash _opts_, and return
# itself.
def configure(opts)
unless opts.is_a?(Hash)
if opts.respond_to?(:to_hash)
opts = opts.to_hash
elsif opts.respond_to?(:to_h)
opts = opts.to_h
else
raise TypeError, "can't convert #{opts.class} into Hash"
end
end
_configure(opts)
end
alias_method :merge, :configure
# call-seq: to_h
#
# Returns the configuration instance variables as a hash, that can be
# passed to the configure method.
def to_h
result = {
indent: indent,
space: space,
space_before: space_before,
object_nl: object_nl,
array_nl: array_nl,
as_json: as_json,
allow_nan: allow_nan?,
ascii_only: ascii_only?,
max_nesting: max_nesting,
script_safe: script_safe?,
strict: strict?,
depth: depth,
buffer_initial_length: buffer_initial_length,
}
instance_variables.each do |iv|
iv = iv.to_s[1..-1]
result[iv.to_sym] = self[iv]
end
result
end
alias_method :to_hash, :to_h
# call-seq: [](name)
#
# Returns the value returned by method +name+.
def [](name)
if respond_to?(name)
__send__(name)
else
instance_variable_get("@#{name}") if
instance_variables.include?("@#{name}".to_sym) # avoid warning
end
end
# call-seq: []=(name, value)
#
# Sets the attribute name to value.
def []=(name, value)
if respond_to?(name_writer = "#{name}=")
__send__ name_writer, value
else
instance_variable_set "@#{name}", value
end
end
end
end
end
end
Binary file not shown.
@@ -0,0 +1,75 @@
# frozen_string_literal: true
begin
require 'ostruct'
rescue LoadError
warn "JSON::GenericObject requires 'ostruct'. Please install it with `gem install ostruct`."
end
module JSON
class GenericObject < OpenStruct
class << self
alias [] new
def json_creatable?
@json_creatable
end
attr_writer :json_creatable
def json_create(data)
data = data.dup
data.delete JSON.create_id
self[data]
end
def from_hash(object)
case
when object.respond_to?(:to_hash)
result = new
object.to_hash.each do |key, value|
result[key] = from_hash(value)
end
result
when object.respond_to?(:to_ary)
object.to_ary.map { |a| from_hash(a) }
else
object
end
end
def load(source, proc = nil, opts = {})
result = ::JSON.load(source, proc, opts.merge(:object_class => self))
result.nil? ? new : result
end
def dump(obj, *args)
::JSON.dump(obj, *args)
end
end
self.json_creatable = false
def to_hash
table
end
def [](name)
__send__(name)
end unless method_defined?(:[])
def []=(name, value)
__send__("#{name}=", value)
end unless method_defined?(:[]=)
def |(other)
self.class[other.to_hash.merge(to_hash)]
end
def as_json(*)
{ JSON.create_id => self.class.name }.merge to_hash
end
def to_json(*a)
as_json.to_json(*a)
end
end if defined?(::OpenStruct)
end
@@ -0,0 +1,690 @@
# frozen_string_literal: true
module JSON
module TruffleRuby
module Generator
MAP = {
"\x0" => '\u0000',
"\x1" => '\u0001',
"\x2" => '\u0002',
"\x3" => '\u0003',
"\x4" => '\u0004',
"\x5" => '\u0005',
"\x6" => '\u0006',
"\x7" => '\u0007',
"\b" => '\b',
"\t" => '\t',
"\n" => '\n',
"\xb" => '\u000b',
"\f" => '\f',
"\r" => '\r',
"\xe" => '\u000e',
"\xf" => '\u000f',
"\x10" => '\u0010',
"\x11" => '\u0011',
"\x12" => '\u0012',
"\x13" => '\u0013',
"\x14" => '\u0014',
"\x15" => '\u0015',
"\x16" => '\u0016',
"\x17" => '\u0017',
"\x18" => '\u0018',
"\x19" => '\u0019',
"\x1a" => '\u001a',
"\x1b" => '\u001b',
"\x1c" => '\u001c',
"\x1d" => '\u001d',
"\x1e" => '\u001e',
"\x1f" => '\u001f',
'"' => '\"',
'\\' => '\\\\',
}.freeze # :nodoc:
SCRIPT_SAFE_MAP = MAP.merge(
'/' => '\\/',
"\u2028" => '\u2028',
"\u2029" => '\u2029',
).freeze
SCRIPT_SAFE_ESCAPE_PATTERN = /[\/"\\\x0-\x1f\u2028-\u2029]/
# Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
# UTF16 big endian characters as \u????, and return it.
def self.utf8_to_json(string, script_safe = false) # :nodoc:
if script_safe
if SCRIPT_SAFE_ESCAPE_PATTERN.match?(string)
string.gsub(SCRIPT_SAFE_ESCAPE_PATTERN, SCRIPT_SAFE_MAP)
else
string
end
else
if /["\\\x0-\x1f]/.match?(string)
string.gsub(/["\\\x0-\x1f]/, MAP)
else
string
end
end
end
def self.utf8_to_json_ascii(original_string, script_safe = false) # :nodoc:
string = original_string.b
map = script_safe ? SCRIPT_SAFE_MAP : MAP
string.gsub!(/[\/"\\\x0-\x1f]/n) { map[$&] || $& }
string.gsub!(/(
(?:
[\xc2-\xdf][\x80-\xbf] |
[\xe0-\xef][\x80-\xbf]{2} |
[\xf0-\xf4][\x80-\xbf]{3}
)+ |
[\x80-\xc1\xf5-\xff] # invalid
)/nx) { |c|
c.size == 1 and raise GeneratorError.new("invalid utf8 byte: '#{c}'", original_string)
s = c.encode(::Encoding::UTF_16BE, ::Encoding::UTF_8).unpack('H*')[0]
s.force_encoding(::Encoding::BINARY)
s.gsub!(/.{4}/n, '\\\\u\&')
s.force_encoding(::Encoding::UTF_8)
}
string.force_encoding(::Encoding::UTF_8)
string
rescue => e
raise GeneratorError.new(e.message, original_string)
end
def self.valid_utf8?(string)
encoding = string.encoding
(encoding == Encoding::UTF_8 || encoding == Encoding::ASCII) &&
string.valid_encoding?
end
# This class is used to create State instances, that are use to hold data
# while generating a JSON text from a Ruby data structure.
class State
def self.generate(obj, opts = nil, io = nil)
new(opts).generate(obj, io)
end
# Creates a State object from _opts_, which ought to be Hash to create
# a new State instance configured by _opts_, something else to create
# an unconfigured instance. If _opts_ is a State object, it is just
# returned.
def self.from_state(opts)
if opts
case
when self === opts
return opts
when opts.respond_to?(:to_hash)
return new(opts.to_hash)
when opts.respond_to?(:to_h)
return new(opts.to_h)
end
end
SAFE_STATE_PROTOTYPE.dup
end
# Instantiates a new State object, configured by _opts_.
#
# _opts_ can have the following keys:
#
# * *indent*: a string used to indent levels (default: ''),
# * *space*: a string that is put after, a : or , delimiter (default: ''),
# * *space_before*: a string that is put before a : pair delimiter (default: ''),
# * *object_nl*: a string that is put at the end of a JSON object (default: ''),
# * *array_nl*: a string that is put at the end of a JSON array (default: ''),
# * *script_safe*: true if U+2028, U+2029 and forward slash (/) should be escaped
# as to make the JSON object safe to interpolate in a script tag (default: false).
# * *check_circular*: is deprecated now, use the :max_nesting option instead,
# * *max_nesting*: sets the maximum level of data structure nesting in
# the generated JSON, max_nesting = 0 if no maximum should be checked.
# * *allow_nan*: true if NaN, Infinity, and -Infinity should be
# generated, otherwise an exception is thrown, if these values are
# encountered. This options defaults to false.
def initialize(opts = nil)
@indent = ''
@space = ''
@space_before = ''
@object_nl = ''
@array_nl = ''
@allow_nan = false
@ascii_only = false
@as_json = false
@depth = 0
@buffer_initial_length = 1024
@script_safe = false
@strict = false
@max_nesting = 100
configure(opts) if opts
end
# This string is used to indent levels in the JSON text.
attr_accessor :indent
# This string is used to insert a space between the tokens in a JSON
# string.
attr_accessor :space
# This string is used to insert a space before the ':' in JSON objects.
attr_accessor :space_before
# This string is put at the end of a line that holds a JSON object (or
# Hash).
attr_accessor :object_nl
# This string is put at the end of a line that holds a JSON array.
attr_accessor :array_nl
# This proc converts unsupported types into native JSON types.
attr_accessor :as_json
# This integer returns the maximum level of data structure nesting in
# the generated JSON, max_nesting = 0 if no maximum is checked.
attr_accessor :max_nesting
# If this attribute is set to true, forward slashes will be escaped in
# all json strings.
attr_accessor :script_safe
# If this attribute is set to true, attempting to serialize types not
# supported by the JSON spec will raise a JSON::GeneratorError
attr_accessor :strict
# :stopdoc:
attr_reader :buffer_initial_length
def buffer_initial_length=(length)
if length > 0
@buffer_initial_length = length
end
end
# :startdoc:
# This integer returns the current depth data structure nesting in the
# generated JSON.
attr_accessor :depth
def check_max_nesting # :nodoc:
return if @max_nesting.zero?
current_nesting = depth + 1
current_nesting > @max_nesting and
raise NestingError, "nesting of #{current_nesting} is too deep"
end
# Returns true, if circular data structures are checked,
# otherwise returns false.
def check_circular?
!@max_nesting.zero?
end
# Returns true if NaN, Infinity, and -Infinity should be considered as
# valid JSON and output.
def allow_nan?
@allow_nan
end
# Returns true, if only ASCII characters should be generated. Otherwise
# returns false.
def ascii_only?
@ascii_only
end
# Returns true, if forward slashes are escaped. Otherwise returns false.
def script_safe?
@script_safe
end
# Returns true, if strict mode is enabled. Otherwise returns false.
# Strict mode only allow serializing JSON native types: Hash, Array,
# String, Integer, Float, true, false and nil.
def strict?
@strict
end
# Configure this State instance with the Hash _opts_, and return
# itself.
def configure(opts)
if opts.respond_to?(:to_hash)
opts = opts.to_hash
elsif opts.respond_to?(:to_h)
opts = opts.to_h
else
raise TypeError, "can't convert #{opts.class} into Hash"
end
opts.each do |key, value|
instance_variable_set "@#{key}", value
end
# NOTE: If adding new instance variables here, check whether #generate should check them for #generate_json
@indent = opts[:indent] || '' if opts.key?(:indent)
@space = opts[:space] || '' if opts.key?(:space)
@space_before = opts[:space_before] || '' if opts.key?(:space_before)
@object_nl = opts[:object_nl] || '' if opts.key?(:object_nl)
@array_nl = opts[:array_nl] || '' if opts.key?(:array_nl)
@allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
@as_json = opts[:as_json].to_proc if opts[:as_json]
@ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
@depth = opts[:depth] || 0
@buffer_initial_length ||= opts[:buffer_initial_length]
@script_safe = if opts.key?(:script_safe)
!!opts[:script_safe]
elsif opts.key?(:escape_slash)
!!opts[:escape_slash]
else
false
end
@strict = !!opts[:strict] if opts.key?(:strict)
if !opts.key?(:max_nesting) # defaults to 100
@max_nesting = 100
elsif opts[:max_nesting]
@max_nesting = opts[:max_nesting]
else
@max_nesting = 0
end
self
end
alias merge configure
# Returns the configuration instance variables as a hash, that can be
# passed to the configure method.
def to_h
result = {}
instance_variables.each do |iv|
iv = iv.to_s[1..-1]
result[iv.to_sym] = self[iv]
end
result
end
alias to_hash to_h
# Generates a valid JSON document from object +obj+ and
# returns the result. If no valid JSON document can be
# created this method raises a
# GeneratorError exception.
def generate(obj, anIO = nil)
if @indent.empty? and @space.empty? and @space_before.empty? and @object_nl.empty? and @array_nl.empty? and
!@ascii_only and !@script_safe and @max_nesting == 0 and (!@strict || Symbol === obj)
result = generate_json(obj, ''.dup)
else
result = obj.to_json(self)
end
JSON::TruffleRuby::Generator.valid_utf8?(result) or raise GeneratorError.new(
"source sequence #{result.inspect} is illegal/malformed utf-8",
obj
)
if anIO
anIO.write(result)
anIO
else
result
end
end
def generate_new(obj, anIO = nil) # :nodoc:
dup.generate(obj, anIO)
end
# Handles @allow_nan, @buffer_initial_length, other ivars must be the default value (see above)
private def generate_json(obj, buf)
case obj
when Hash
buf << '{'
first = true
obj.each_pair do |k,v|
buf << ',' unless first
key_str = k.to_s
if key_str.class == String
fast_serialize_string(key_str, buf)
elsif key_str.is_a?(String)
generate_json(key_str, buf)
else
raise TypeError, "#{k.class}#to_s returns an instance of #{key_str.class}, expected a String"
end
buf << ':'
generate_json(v, buf)
first = false
end
buf << '}'
when Array
buf << '['
first = true
obj.each do |e|
buf << ',' unless first
generate_json(e, buf)
first = false
end
buf << ']'
when String
if obj.class == String
fast_serialize_string(obj, buf)
else
buf << obj.to_json(self)
end
when Integer
buf << obj.to_s
when Symbol
if @strict
fast_serialize_string(obj.name, buf)
else
buf << obj.to_json(self)
end
else
# Note: Float is handled this way since Float#to_s is slow anyway
buf << obj.to_json(self)
end
end
# Assumes !@ascii_only, !@script_safe
private def fast_serialize_string(string, buf) # :nodoc:
buf << '"'
unless string.encoding == ::Encoding::UTF_8
begin
string = string.encode(::Encoding::UTF_8)
rescue Encoding::UndefinedConversionError => error
raise GeneratorError.new(error.message, string)
end
end
raise GeneratorError.new("source sequence is illegal/malformed utf-8", string) unless string.valid_encoding?
if /["\\\x0-\x1f]/.match?(string)
buf << string.gsub(/["\\\x0-\x1f]/, MAP)
else
buf << string
end
buf << '"'
end
# Return the value returned by method +name+.
def [](name)
if respond_to?(name)
__send__(name)
else
instance_variable_get("@#{name}") if
instance_variables.include?("@#{name}".to_sym) # avoid warning
end
end
def []=(name, value)
if respond_to?(name_writer = "#{name}=")
__send__ name_writer, value
else
instance_variable_set "@#{name}", value
end
end
end
module GeneratorMethods
module Object
# Converts this object to a string (calling #to_s), converts
# it to a JSON string, and returns the result. This is a fallback, if no
# special method #to_json was defined for some object.
def to_json(state = nil, *)
state = State.from_state(state) if state
if state&.strict?
value = self
if state.strict? && !(false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value)
if state.as_json
value = state.as_json.call(value)
unless false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value
raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
end
value.to_json(state)
else
raise GeneratorError.new("#{value.class} not allowed in JSON", value)
end
end
else
to_s.to_json
end
end
end
module Hash
# Returns a JSON string containing a JSON object, that is unparsed from
# this Hash instance.
# _state_ is a JSON::State object, that can also be used to configure the
# produced JSON string output further.
# _depth_ is used to find out nesting depth, to indent accordingly.
def to_json(state = nil, *)
state = State.from_state(state)
state.check_max_nesting
json_transform(state)
end
private
def json_shift(state)
state.object_nl.empty? or return ''
state.indent * state.depth
end
def json_transform(state)
depth = state.depth += 1
if empty?
state.depth -= 1
return '{}'
end
delim = ",#{state.object_nl}"
result = +"{#{state.object_nl}"
first = true
indent = !state.object_nl.empty?
each { |key, value|
result << delim unless first
result << state.indent * depth if indent
key_str = key.to_s
if key_str.is_a?(String)
key_json = key_str.to_json(state)
else
raise TypeError, "#{key.class}#to_s returns an instance of #{key_str.class}, expected a String"
end
result = +"#{result}#{key_json}#{state.space_before}:#{state.space}"
if state.strict? && !(false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value)
if state.as_json
value = state.as_json.call(value)
unless false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value
raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
end
result << value.to_json(state)
else
raise GeneratorError.new("#{value.class} not allowed in JSON", value)
end
elsif value.respond_to?(:to_json)
result << value.to_json(state)
else
result << %{"#{String(value)}"}
end
first = false
}
depth = state.depth -= 1
unless first
result << state.object_nl
result << state.indent * depth if indent
end
result << '}'
result
end
end
module Array
# Returns a JSON string containing a JSON array, that is unparsed from
# this Array instance.
# _state_ is a JSON::State object, that can also be used to configure the
# produced JSON string output further.
def to_json(state = nil, *)
state = State.from_state(state)
state.check_max_nesting
json_transform(state)
end
private
def json_transform(state)
depth = state.depth += 1
if empty?
state.depth -= 1
return '[]'
end
result = '['.dup
if state.array_nl.empty?
delim = ","
else
result << state.array_nl
delim = ",#{state.array_nl}"
end
first = true
indent = !state.array_nl.empty?
each { |value|
result << delim unless first
result << state.indent * depth if indent
if state.strict? && !(false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value || Symbol == value)
if state.as_json
value = state.as_json.call(value)
unless false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value || Symbol === value
raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
end
result << value.to_json(state)
else
raise GeneratorError.new("#{value.class} not allowed in JSON", value)
end
elsif value.respond_to?(:to_json)
result << value.to_json(state)
else
result << %{"#{String(value)}"}
end
first = false
}
depth = state.depth -= 1
result << state.array_nl
result << state.indent * depth if indent
result << ']'
end
end
module Integer
# Returns a JSON string representation for this Integer number.
def to_json(*) to_s end
end
module Float
# Returns a JSON string representation for this Float number.
def to_json(state = nil, *args)
state = State.from_state(state)
if infinite? || nan?
if state.allow_nan?
to_s
elsif state.strict? && state.as_json
casted_value = state.as_json.call(self)
if casted_value.equal?(self)
raise GeneratorError.new("#{self} not allowed in JSON", self)
end
state.check_max_nesting
state.depth += 1
result = casted_value.to_json(state, *args)
state.depth -= 1
result
else
raise GeneratorError.new("#{self} not allowed in JSON", self)
end
else
to_s
end
end
end
module Symbol
def to_json(state = nil, *args)
state = State.from_state(state)
if state.strict?
name.to_json(state, *args)
else
super
end
end
end
module String
# This string should be encoded with UTF-8 A call to this method
# returns a JSON string encoded with UTF16 big endian characters as
# \u????.
def to_json(state = nil, *args)
state = State.from_state(state)
if encoding == ::Encoding::UTF_8
unless valid_encoding?
raise GeneratorError.new("source sequence is illegal/malformed utf-8", self)
end
string = self
else
string = encode(::Encoding::UTF_8)
end
if state.ascii_only?
%("#{JSON::TruffleRuby::Generator.utf8_to_json_ascii(string, state.script_safe)}")
else
%("#{JSON::TruffleRuby::Generator.utf8_to_json(string, state.script_safe)}")
end
rescue Encoding::UndefinedConversionError => error
raise ::JSON::GeneratorError.new(error.message, self)
end
# Module that holds the extending methods if, the String module is
# included.
module Extend
# Raw Strings are JSON Objects (the raw bytes are stored in an
# array for the key "raw"). The Ruby String can be created by this
# module method.
def json_create(o)
o['raw'].pack('C*')
end
end
# Extends _modul_ with the String::Extend module.
def self.included(modul)
modul.extend Extend
end
# This method creates a raw object hash, that can be nested into
# other data structures and will be unparsed as a raw string. This
# method should be used, if you want to convert raw strings to JSON
# instead of UTF-8 strings, e. g. binary data.
def to_json_raw_object
{
JSON.create_id => self.class.name,
'raw' => self.unpack('C*'),
}
end
# This method creates a JSON text from the result of
# a call to to_json_raw_object of this String.
def to_json_raw(*args)
to_json_raw_object.to_json(*args)
end
end
module TrueClass
# Returns a JSON string for true: 'true'.
def to_json(*) 'true' end
end
module FalseClass
# Returns a JSON string for false: 'false'.
def to_json(*) 'false' end
end
module NilClass
# Returns a JSON string for nil: 'null'.
def to_json(*) 'null' end
end
end
end
end
end
+5
View File
@@ -0,0 +1,5 @@
# frozen_string_literal: true
module JSON
VERSION = '2.10.2'
end