pax_global_header00006660000000000000000000000064135512307520014515gustar00rootroot0000000000000052 comment=a5c95f18a3089b8c2619628b2fb1d79d68374582 multi_json-1.14.1/000077500000000000000000000000001355123075200137645ustar00rootroot00000000000000multi_json-1.14.1/.document000066400000000000000000000000751355123075200156050ustar00rootroot00000000000000LICENSE.md README.md bin/* features/**/*.feature lib/**/*.rb multi_json-1.14.1/.gitignore000066400000000000000000000003601355123075200157530ustar00rootroot00000000000000## TEXTMATE *.tmproj tmtags ## EMACS *~ \#* .\#* ## VIM *.swp ## PROJECT::GENERAL .yardoc coverage doc rdoc log ## BUNDLER *.gem .bundle pkg gemfiles/*.lock ## RBENV .ruby-version .rbenv* ## RCOV coverage.data tmp ## RUBINIUS *.rbc multi_json-1.14.1/.rspec000066400000000000000000000000271355123075200151000ustar00rootroot00000000000000--color --order random multi_json-1.14.1/.travis.yml000066400000000000000000000032371355123075200161020ustar00rootroot00000000000000language: ruby matrix: include: - rvm: 1.8 gemfile: gemfiles/gemfile-1-8 os: linux dist: trusty env: SKIP_ADAPTERS=yajl,gson,jr_jackson,nsjsonserialization - rvm: jruby-18mode gemfile: gemfiles/gemfile-1-8-jruby os: linux dist: trusty env: SKIP_ADAPTERS=oj,yajl,nsjsonserialization - rvm: 1.9 gemfile: gemfiles/gemfile-1-9 os: linux dist: trusty env: SKIP_ADAPTERS=gson,jr_jackson,nsjsonserialization - rvm: jruby-19mode gemfile: gemfiles/gemfile-1-9-jruby os: linux dist: trusty env: SKIP_ADAPTERS=oj,yajl,nsjsonserialization - rvm: 2.0 gemfile: gemfiles/gemfile-2-0 env: SKIP_ADAPTERS=gson,jr_jackson,nsjsonserialization - rvm: 2.1 gemfile: gemfiles/gemfile-2-0 env: SKIP_ADAPTERS=gson,jr_jackson,nsjsonserialization - rvm: 2.2 gemfile: gemfiles/gemfile-2-0 env: SKIP_ADAPTERS=gson,jr_jackson,nsjsonserialization - rvm: 2.3 gemfile: gemfiles/gemfile-2-3 env: SKIP_ADAPTERS=gson,jr_jackson,nsjsonserialization - rvm: 2.4 gemfile: gemfiles/gemfile-2-3 env: SKIP_ADAPTERS=gson,jr_jackson,nsjsonserialization - rvm: 2.5 gemfile: gemfiles/gemfile-2-3 env: SKIP_ADAPTERS=gson,jr_jackson,nsjsonserialization - rvm: 2.6 gemfile: gemfiles/gemfile-2-3 env: SKIP_ADAPTERS=gson,jr_jackson,nsjsonserialization - rvm: ruby-head gemfile: gemfiles/gemfile-2-3 env: SKIP_ADAPTERS=gson,jr_jackson,nsjsonserialization - rvm: jruby-9000 gemfile: gemfiles/gemfile-2-jruby env: SKIP_ADAPTERS=oj,yajl,jr_jackson,nsjsonserialization - rvm: jruby-head gemfile: gemfiles/gemfile-2-jruby env: SKIP_ADAPTERS=oj,yajl,jr_jackson,nsjsonserializationmulti_json-1.14.1/.yardopts000066400000000000000000000001061355123075200156270ustar00rootroot00000000000000--markup markdown - CHANGELOG.md CONTRIBUTING.md LICENSE.md README.md multi_json-1.14.1/CHANGELOG.md000066400000000000000000000307771355123075200156130ustar00rootroot000000000000001.14.1 ------ * [Fix a warning in Ruby 2.7](https://github.com/intridea/multi_json/commit/26a94ab8c78a394cc237e2ea292c1de4f6ed30d7) 1.14.0 ------ * [Support Oj 3.x gem](https://github.com/intridea/multi_json/commit/5d8febdbebc428882811b90d514f3628617a61d5) 1.13.1 ------ * [Fix missing stdlib set dependency in oj adapter](https://github.com/intridea/multi_json/commit/c4ff66e7bee6fb4f45e54429813d7fada1c152b8) 1.13.0 ----- * [Make Oj adapter handle JSON::ParseError correctly](https://github.com/intridea/multi_json/commit/275e3ffd8169797c510d23d9ef5b8b07e64c3b42) 1.12.2 ------ * [Renew gem certificate](https://github.com/intridea/multi_json/commit/57922d898c6eb587cc9a28ba5724c11e81724700) 1.12.1 ------ * [Prevent memory leak in OptionsCache](https://github.com/intridea/multi_json/commit/aa7498199ad272f3d4a13750d7c568a66047e2ee) 1.12.0 ------ * [Introduce global options cache to improve peroformance](https://github.com/intridea/multi_json/commit/7aaef2a1bc2b83c95e4208b12dad5d1d87ff20a6) 1.11.2 ------ * [Only pass one argument to JrJackson when two is not supported](https://github.com/intridea/multi_json/commit/e798fa517c817fc706982d3f3c61129b6651d601) 1.11.1 ------ * [Dump method passes options throught for JrJackson adapter](https://github.com/intridea/multi_json/commit/3c730fd12135c3e7bf212f878958004908f13909) 1.11.0 ------ * [Make all adapters read IO object before load](https://github.com/intridea/multi_json/commit/167f559e18d4efee05e1f160a2661d16dbb215d4) 1.10.1 ------ * [Explicitly require stringio for Gson adapter](https://github.com/intridea/multi_json/commit/623ec8142d4a212fa0db763bb71295789a119929) * [Do not read StringIO object before passing it to JrJackson](https://github.com/intridea/multi_json/commit/a6dc935df08e7b3d5d701fbb9298384c96df0fde) 1.10.0 ------ * [Performance tweaks](https://github.com/intridea/multi_json/commit/58724acfed31866d079eaafb1cd824e341ade287) 1.9.3 ----- * [Convert indent option to Fixnum before passing to Oj](https://github.com/intridea/multi_json/commit/826fc5535b863b74fc9f981dfdda3e26f1ee4e5b) 1.9.2 ----- * [Enable use_to_json option for Oj adapter by default](https://github.com/intridea/multi_json/commit/76a4aaf697b10bbabd5d535d83cf1149efcfe5c7) 1.9.1 ----- * [Remove unused LoadError file](https://github.com/intridea/multi_json/commit/65dedd84d59baeefc25c477fedf0bbe85e7ce2cd) 1.9.0 ---- * [Rename LoadError to ParseError](https://github.com/intridea/multi_json/commit/4abb98fe3a90b2a7b3d1594515c8a06042b4a27d) * [Adapter load failure throws AdapterError instead of ArgumentError](https://github.com/intridea/multi_json/commit/4da612b617bd932bb6fa1cc4c43210327f98f271) 1.8.4 ----- * [Make Gson adapter explicitly read StringIO object](https://github.com/intridea/multi_json/commit/b58b498747ff6e94f41488c971b2a30a98760ef2) 1.8.3 ----- * [Make JrJackson explicitly read StringIO objects](https://github.com/intridea/multi_json/commit/e1f162d5b668e5e4db5afa175361a601a8aa2b05) * [Prevent calling #downcase on alias symbols](https://github.com/intridea/multi_json/commit/c1cf075453ce0110f7decc4f906444b1233bb67c) 1.8.2 ----- * [Downcase adapter string name for OS compatibility](https://github.com/intridea/multi_json/commit/b8e15a032247a63f1410d21a18add05035f3fa66) 1.8.1 ----- * [Let the adapter handle strings with invalid encoding](https://github.com/intridea/multi_json/commit/6af2bf87b89f44eabf2ae9ca96779febc65ea94b) 1.8.0 ----- * [Raise MultiJson::LoadError on blank input](https://github.com/intridea/multi_json/commit/c44f9c928bb25fe672246ad394b3e5b991be32e6) 1.7.9 ----- * [Explicitly require json gem code even when constant is defined](https://github.com/intridea/multi_json/commit/36f7906c66477eb4b55b7afeaa3684b6db69eff2) 1.7.8 ----- * [Reorder JrJackson before json_gem](https://github.com/intridea/multi_json/commit/315b6e460b6e4dcdb6c82e04e4be8ee975d395da) * [Update vendored OkJson to version 43](https://github.com/intridea/multi_json/commit/99a6b662f6ef4036e3ee94d7eb547fa72fb2ab50) 1.7.7 ----- * [Fix options caching issues](https://github.com/intridea/multi_json/commit/a3f14c3661688c5927638fa6088c7b46a67e875e) 1.7.6 ----- * [Bring back MultiJson::VERSION constant](https://github.com/intridea/multi_json/commit/31b990c2725e6673bf8ce57540fe66b57a751a72) 1.7.5 ----- * [Fix warning '*' interpreted as argument prefix](https://github.com/intridea/multi_json/commit/b698962c7f64430222a1f06430669706a47aff89) * [Remove stdlib warning](https://github.com/intridea/multi_json/commit/d06eec6b7996ac8b4ff0e2229efd835379b0c30f) 1.7.4 ----- * [Cache options for better performance](https://github.com/intridea/multi_json/commit/8a26ee93140c4bed36194ed9fb887a1b6919257b) 1.7.3 ----- * [Require json/ext to ensure extension version gets loaded for json_gem](https://github.com/intridea/multi_json/commit/942686f7e8597418c6f90ee69e1d45242fac07b1) * [Rename JrJackson](https://github.com/intridea/multi_json/commit/078de7ba8b6035343c3e96b4767549e9ec43369a) * [Prefer JrJackson to JSON gem if present](https://github.com/intridea/multi_json/commit/af8bd9799a66855f04b3aff1c488485950cec7bf) * [Print a warning if outdated gem versions are used](https://github.com/intridea/multi_json/commit/e7438e7ba2be0236cfa24c2bb9ad40ee821286d1) * [Loosen required_rubygems_version for compatibility with Ubuntu 10.04](https://github.com/intridea/multi_json/commit/59fad014e8fe41dbc6f09485ea0dc21fc42fd7a7) 1.7.2 ----- * [Rename Jrjackson adapter to JrJackson](https://github.com/intridea/multi_json/commit/b36dc915fc0e6548cbad06b5db6f520e040c9c8b) * [Implement jrjackson -> jr_jackson alias for back-compatability](https://github.com/intridea/multi_json/commit/aa50ab8b7bb646b8b75d5d65dfeadae8248a4f10) * [Update vendored OkJson module](https://github.com/intridea/multi_json/commit/30a3f474e17dd86a697c3fab04f468d1a4fd69d7) 1.7.1 ----- * [Fix capitalization of JrJackson class](https://github.com/intridea/multi_json/commit/5373a5e38c647f02427a0477cb8e0e0dafad1b8d) 1.7.0 ----- * [Add load_options/dump_options to MultiJson](https://github.com/intridea/multi_json/commit/a153956be6b0df06ea1705ce3c1ff0b5b0e27ea5) * [MultiJson does not modify arguments](https://github.com/intridea/multi_json/commit/58525b01c4c2f6635ba2ac13d6fd987b79f3962f) * [Enable quirks_mode by default for json_gem/json_pure adapters](https://github.com/intridea/multi_json/commit/1fd4e6635c436515b7d7d5a0bee4548de8571520) * [Add JrJackson adapter](https://github.com/intridea/multi_json/commit/4dd86fa96300aaaf6d762578b9b31ea82adb056d) * [Raise ArgumentError on bad adapter input](https://github.com/intridea/multi_json/commit/911a3756bdff2cb5ac06497da3fa3e72199cb7ad) 1.6.1 ----- * [Revert "Use JSON.generate instead of #to_json"](https://github.com/intridea/multi_json/issues/86) 1.6.0 ----- * [Add gson.rb support](https://github.com/intridea/multi_json/pull/71) * [Add MultiJson.default_options](https://github.com/intridea/multi_json/pull/70) * [Add MultiJson.with_adapter](https://github.com/intridea/multi_json/pull/67) * [Stringify all possible keys for ok_json](https://github.com/intridea/multi_json/pull/66) * [Use JSON.generate instead of #to_json](https://github.com/intridea/multi_json/issues/73) * [Alias `MultiJson::DecodeError` to `MultiJson::LoadError`](https://github.com/intridea/multi_json/pull/79) 1.5.1 ----- * [Do not allow Oj or JSON to create symbols by searching for classes](https://github.com/intridea/multi_json/commit/193e28cf4dc61b6e7b7b7d80f06f74c76df65c41) 1.5.0 ----- * [Add `MultiJson.with_adapter` method](https://github.com/intridea/multi_json/commit/d14c5d28cae96557a0421298621b9499e1f28104) * [Stringify all possible keys for `ok_json`](https://github.com/intridea/multi_json/commit/73998074058e1e58c557ffa7b9541d486d6041fa) 1.4.0 ----- * [Allow `load`/`dump` of JSON fragments](https://github.com/intridea/multi_json/commit/707aae7d48d39c85b38febbd2c210ba87f6e4a36) 1.3.7 ----- * [Fix rescue clause for MagLev](https://github.com/intridea/multi_json/commit/39abdf50199828c50e85b2ce8f8ba31fcbbc9332) * [Remove unnecessary check for string version of options key](https://github.com/intridea/multi_json/commit/660101b70e962b3c007d0b90d45944fa47d13ec4) * [Explicitly set default adapter when adapter is set to `nil` or `false`](https://github.com/intridea/multi_json/commit/a9e587d5a63eafb4baee9fb211265e4dd96a26bc) * [Fix Oj `ParseError` mapping for Oj 1.4.0](https://github.com/intridea/multi_json/commit/7d9045338cc9029401c16f3c409d54ce97f275e2) 1.3.6 ----- * [Allow adapter-specific options to be passed through to Oj](https://github.com/intridea/multi_json/commit/d0e5feeebcba0bc69400dd203a295f5c30971223) 1.3.5 ----- * [Add pretty support to Oj adapter](https://github.com/intridea/multi_json/commit/0c8f75f03020c53bcf4c6be258faf433d24b2c2b) 1.3.4 ----- * [Use `class << self` instead of `module_function` to create aliases](https://github.com/intridea/multi_json/commit/ba1451c4c48baa297e049889be241a424cb05980) 1.3.3 ----- * [Remove deprecation warnings](https://github.com/intridea/multi_json/commit/36b524e71544eb0186826a891bcc03b2820a008f) 1.3.2 ----- * [Add ability to use adapter per call](https://github.com/intridea/multi_json/commit/106bbec469d5d0a832bfa31fffcb8c0f0cdc9bd3) * [Add and deprecate `default_engine` method](https://github.com/intridea/multi_json/commit/fc3df0c7a3e2ab9ce0c2c7e7617a4da97dd13f6e) 1.3.1 ----- * [Only warn once for each instance a deprecated method is called](https://github.com/intridea/multi_json/commit/e21d6eb7da74b3f283995c1d27d5880e75f0ae84) 1.3.0 ----- * [Implement `load`/`dump`; deprecate `decode`/`encode`](https://github.com/intridea/multi_json/commit/e90fd6cb1b0293eb0c73c2f4eb0f7a1764370216) * [Rename engines to adapters](https://github.com/intridea/multi_json/commit/ae7fd144a7949a9c221dcaa446196ec23db908df) 1.2.0 ----- * [Add support for Oj](https://github.com/intridea/multi_json/commit/acd06b233edabe6c44f226873db7b49dab560c60) 1.1.0 ----- * [`NSJSONSerialization` support for MacRuby](https://github.com/intridea/multi_json/commit/f862e2fc966cac8867fe7da3997fc76e8a6cf5d4) 1.0.4 ----- * [Set data context to `DecodeError` exception](https://github.com/intridea/multi_json/commit/19ddafd44029c6681f66fae2a0f6eabfd0f85176) * [Allow `ok_json` to fallback to `to_json`](https://github.com/intridea/multi_json/commit/c157240b1193b283d06d1bd4d4b5b06bcf3761f8) * [Add warning when using `ok_json`](https://github.com/intridea/multi_json/commit/dd4b68810c84f826fb98f9713bfb29ab96888d57) * [Options can be passed to an engine on encode](https://github.com/intridea/multi_json/commit/e0a7ff5d5ff621ffccc61617ed8aeec5816e81f7) 1.0.3 ----- * [`Array` support for `stringify_keys`](https://github.com/intridea/multi_json/commit/644d1c5c7c7f6a27663b11668527b346094e38b9) * [`Array` support for `symbolize_keys`](https://github.com/intridea/multi_json/commit/c885377d47a2aa39cb0d971fea78db2d2fa479a7) 1.0.2 ----- * [Allow encoding of rootless JSON when `ok_json` is used](https://github.com/intridea/multi_json/commit/d1cde7de97cb0f6152aef8daf14037521cdce8c6) 1.0.1 ----- * [Correct an issue with `ok_json` not being returned as the default engine](https://github.com/intridea/multi_json/commit/d33c141619c54cccd770199694da8fd1bd8f449d) 1.0.0 ----- * [Remove `ActiveSupport::JSON` support](https://github.com/intridea/multi_json/commit/c2f4140141d785a24b3f56e58811b0e561b37f6a) * [Fix `@engine` ivar warning](https://github.com/intridea/multi_json/commit/3b978a8995721a8dffedc3b75a7f49e5494ec553) * [Only `rescue` from parsing errors during decoding, not any `StandardError`](https://github.com/intridea/multi_json/commit/391d00b5e85294d42d41347605d8d46b4a7f66cc) * [Rename `okjson` engine and vendored lib to `ok_json`](https://github.com/intridea/multi_json/commit/5bd1afc977a8208ddb0443e1d57cb79665c019f1) * [Add `StringIO` support to `json` gem and `ok_json`](https://github.com/intridea/multi_json/commit/1706b11568db7f50af451fce5f4d679aeb3bbe8f) 0.0.5 ----- * [Trap all JSON decoding errors; raise `MultiJson::DecodeError`](https://github.com/intridea/multi_json/commit/dea9a1aef6dd1212aa1e5a37ab1669f9b045b732) 0.0.4 ----- * [Fix default_engine check for `json` gem](https://github.com/intridea/multi_json/commit/caced0c4e8c795922a109ebc00c3c4fa8635bed8) * [Make requirement mapper an `Array` to preserve order in Ruby versions < 1.9](https://github.com/intridea/multi_json/commit/526f5f29a42131574a088ad9bbb43d7f48439b2c) 0.0.3 ----- * [Improve defaulting and documentation](https://github.com/sferik/twitter/commit/3a0e41b9e4b0909201045fa47704b78c9d949b73) 0.0.2 ----- * [Rename to `multi_json`](https://github.com/sferik/twitter/commit/461ab89ce071c8c9fabfc183581e0ec523788b62) 0.0.1 ----- * [Initial commit](https://github.com/sferik/twitter/commit/518c21ab299c500527491e6c049ab2229e22a805) multi_json-1.14.1/CONTRIBUTING.md000066400000000000000000000033761355123075200162260ustar00rootroot00000000000000## Contributing In the spirit of [free software][free-sw], **everyone** is encouraged to help improve this project. [free-sw]: http://www.fsf.org/licensing/essays/free-sw.html Here are some ways *you* can contribute: * by using alpha, beta, and prerelease versions * by reporting bugs * by suggesting new features * by writing or editing documentation * by writing specifications * by writing code (**no patch is too small**: fix typos, add comments, clean up inconsistent whitespace) * by refactoring code * by closing [issues][] * by reviewing patches [issues]: https://github.com/intridea/multi_json/issues ## Submitting an Issue We use the [GitHub issue tracker][issues] to track bugs and features. Before submitting a bug report or feature request, check to make sure it hasn't already been submitted. When submitting a bug report, please include a [Gist][] that includes a stack trace and any details that may be necessary to reproduce the bug, including your gem version, Ruby version, and operating system. Ideally, a bug report should include a pull request with failing specs. [gist]: https://gist.github.com/ ## Submitting a Pull Request 1. [Fork the repository.][fork] 2. [Create a topic branch.][branch] 3. Add specs for your unimplemented feature or bug fix. 4. Run `bundle exec rake spec`. If your specs pass, return to step 3. 5. Implement your feature or bug fix. 6. Run `bundle exec rake spec`. If your specs fail, return to step 5. 7. Run `open coverage/index.html`. If your changes are not completely covered by your tests, return to step 3. 8. Add, commit, and push your changes. 9. [Submit a pull request.][pr] [fork]: http://help.github.com/fork-a-repo/ [branch]: http://learn.github.com/p/branching.html [pr]: http://help.github.com/send-pull-requests/ multi_json-1.14.1/LICENSE.md000066400000000000000000000021341355123075200153700ustar00rootroot00000000000000Copyright (c) 2010-2013 Michael Bleigh, Josh Kalderimis, Erik Michaels-Ober, Pavel Pravosud Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. multi_json-1.14.1/README.md000066400000000000000000000117601355123075200152500ustar00rootroot00000000000000# MultiJSON [![Gem Version](http://img.shields.io/gem/v/multi_json.svg)][gem] [![Build Status](http://travis-ci.org/intridea/multi_json.svg)][travis] [![Code Climate](https://codeclimate.com/github/intridea/multi_json.svg)][codeclimate] Lots of Ruby libraries parse JSON and everyone has their favorite JSON coder. Instead of choosing a single JSON coder and forcing users of your library to be stuck with it, you can use MultiJSON instead, which will simply choose the fastest available JSON coder. Here's how to use it: ```ruby require 'multi_json' MultiJson.load('{"abc":"def"}') #=> {"abc" => "def"} MultiJson.load('{"abc":"def"}', :symbolize_keys => true) #=> {:abc => "def"} MultiJson.dump({:abc => 'def'}) # convert Ruby back to JSON MultiJson.dump({:abc => 'def'}, :pretty => true) # encoded in a pretty form (if supported by the coder) ``` When loading invalid JSON, MultiJSON will throw a `MultiJson::ParseError`. `MultiJson::DecodeError` and `MultiJson::LoadError` are aliases for backwards compatibility. ```ruby begin MultiJson.load('{invalid json}') rescue MultiJson::ParseError => exception exception.data # => "{invalid json}" exception.cause # => JSON::ParserError: 795: unexpected token at '{invalid json}' end ``` `ParseError` instance has `cause` reader which contains the original exception. It also has `data` reader with the input that caused the problem. The `use` method, which sets the MultiJSON adapter, takes either a symbol or a class (to allow for custom JSON parsers) that responds to both `.load` and `.dump` at the class level. When MultiJSON fails to load the specified adapter, it'll throw `MultiJson::AdapterError` which inherits from `ArgumentError`. MultiJSON tries to have intelligent defaulting. That is, if you have any of the supported engines already loaded, it will utilize them before attempting to load any. When loading, libraries are ordered by speed. First Oj, then Yajl, then the JSON gem, then JSON pure. If no other JSON library is available, MultiJSON falls back to [OkJson][], a simple, vendorable JSON parser. ## Supported JSON Engines * [Oj][oj] Optimized JSON by Peter Ohler * [Yajl][yajl] Yet Another JSON Library by Brian Lopez * [JSON][json-gem] The default JSON gem with C-extensions (ships with Ruby 1.9+) * [JSON Pure][json-gem] A Ruby variant of the JSON gem * [NSJSONSerialization][nsjson] Wrapper for Apple's NSJSONSerialization in the Cocoa Framework (MacRuby only) * [gson.rb][gson] A Ruby wrapper for google-gson library (JRuby only) * [JrJackson][jrjackson] JRuby wrapper for Jackson (JRuby only) * [OkJson][okjson] A simple, vendorable JSON parser ## Supported Ruby Versions This library aims to support and is [tested against][travis] the following Ruby implementations: * Ruby 1.8 * Ruby 1.9 * Ruby 2.0 * Ruby 2.1 * Ruby 2.2 * Ruby 2.4 * Ruby 2.5 * Ruby 2.6 * [JRuby][] If something doesn't work in one of these implementations, it's a bug. This library may inadvertently work (or seem to work) on other Ruby implementations, however support will only be provided for the versions listed above. If you would like this library to support another Ruby version, you may volunteer to be a maintainer. Being a maintainer entails making sure all tests run and pass on that implementation. When something breaks on your implementation, you will be responsible for providing patches in a timely fashion. If critical issues for a particular implementation exist at the time of a major release, support for that Ruby version may be dropped. ## Versioning This library aims to adhere to [Semantic Versioning 2.0.0][semver]. Violations of this scheme should be reported as bugs. Specifically, if a minor or patch version is released that breaks backward compatibility, that version should be immediately yanked and/or a new version should be immediately released that restores compatibility. Breaking changes to the public API will only be introduced with new major versions. As a result of this policy, you can (and should) specify a dependency on this gem using the [Pessimistic Version Constraint][pvc] with two digits of precision. For example: ```ruby spec.add_dependency 'multi_json', '~> 1.0' ``` ## Copyright Copyright (c) 2010-2018 Michael Bleigh, Josh Kalderimis, Erik Michaels-Ober, and Pavel Pravosud. See [LICENSE][] for details. [codeclimate]: https://codeclimate.com/github/intridea/multi_json [gem]: https://rubygems.org/gems/multi_json [gson]: https://github.com/avsej/gson.rb [jrjackson]: https://github.com/guyboertje/jrjackson [jruby]: http://www.jruby.org/ [json-gem]: https://github.com/flori/json [json-pure]: https://github.com/flori/json [license]: LICENSE.md [macruby]: http://www.macruby.org/ [nsjson]: https://developer.apple.com/library/ios/#documentation/Foundation/Reference/NSJSONSerialization_Class/Reference/Reference.html [oj]: https://github.com/ohler55/oj [okjson]: https://github.com/kr/okjson [pvc]: http://docs.rubygems.org/read/chapter/16#page74 [semver]: http://semver.org/ [travis]: http://travis-ci.org/intridea/multi_json [yajl]: https://github.com/brianmario/yajl-ruby multi_json-1.14.1/Rakefile000066400000000000000000000013151355123075200154310ustar00rootroot00000000000000require 'bundler' Bundler::GemHelper.install_tasks require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:base_spec) do |task| task.pattern = 'spec/{multi_json,options_cache}_spec.rb' end namespace :adapters do Dir['spec/*_adapter_spec.rb'].each do |adapter_spec| adapter_name = adapter_spec[/(\w+)_adapter_spec/, 1] desc "Run #{adapter_name} adapter specs" RSpec::Core::RakeTask.new(adapter_name) do |task| task.pattern = adapter_spec end end end task :spec => %w[ base_spec adapters:oj adapters:yajl adapters:json_gem adapters:json_pure adapters:ok_json adapters:gson adapters:jr_jackson adapters:nsjsonserialization ] task :default => :spec task :test => :spec multi_json-1.14.1/benchmark.rb000066400000000000000000000003041355123075200162400ustar00rootroot00000000000000require 'oj' require 'multi_json' require 'benchmark/ips' MultiJson.use :oj Benchmark.ips do |x| x.time = 10 x.warmup = 1 x.report { MultiJson.load(MultiJson.dump(a: 1, b: 2, c: 3)) } end multi_json-1.14.1/gemfiles/000077500000000000000000000000001355123075200155575ustar00rootroot00000000000000multi_json-1.14.1/gemfiles/gemfile-1-8000066400000000000000000000002771355123075200174230ustar00rootroot00000000000000source "https://rubygems.org" gem "json", "~> 1.8", :require => false gem "json_pure", "~> 1.8", :require => false gem "oj", "~> 2.18", :require => false gemspec :path => ".."multi_json-1.14.1/gemfiles/gemfile-1-8-jruby000066400000000000000000000003611355123075200205460ustar00rootroot00000000000000source "https://rubygems.org" gem "json", "~> 1.8", :require => false gem "json_pure", "~> 1.8", :require => false gem "gson", ">= 0.6", :require => false gem "jrjackson", "~> 0.3.4", :require => false gemspec :path => ".."multi_json-1.14.1/gemfiles/gemfile-1-9000066400000000000000000000003411355123075200174140ustar00rootroot00000000000000source "https://rubygems.org" gem "json", "~> 1.8", require: false gem "json_pure", "~> 1.8", require: false gem "oj", "~> 2.18", require: false gem "yajl-ruby", "~> 1.3", require: false gemspec :path => ".."multi_json-1.14.1/gemfiles/gemfile-1-9-jruby000066400000000000000000000003451355123075200205510ustar00rootroot00000000000000source "https://rubygems.org" gem "json", "~> 1.8", require: false gem "json_pure", "~> 1.8", require: false gem "gson", ">= 0.6", require: false gem "jrjackson", "~> 0.3.4", require: false gemspec :path => ".."multi_json-1.14.1/gemfiles/gemfile-2-0000066400000000000000000000003361355123075200174100ustar00rootroot00000000000000source "https://rubygems.org" gem "json", "~> 2.0", require: false gem "json_pure", "~> 2.0", require: false gem "oj", "~> 2.0", require: false gem "yajl-ruby", "~> 1.3", require: false gemspec path: ".."multi_json-1.14.1/gemfiles/gemfile-2-3000066400000000000000000000003361355123075200174130ustar00rootroot00000000000000source "https://rubygems.org" gem "json", "~> 2.0", require: false gem "json_pure", "~> 2.0", require: false gem "oj", "~> 3.0", require: false gem "yajl-ruby", "~> 1.3", require: false gemspec path: ".."multi_json-1.14.1/gemfiles/gemfile-2-jruby000066400000000000000000000002641355123075200204040ustar00rootroot00000000000000source "https://rubygems.org" gem "json", "~> 2.0", require: false gem "json_pure", "~> 2.0", require: false gem "gson", ">= 0.6", require: false gemspec path: ".."multi_json-1.14.1/lib/000077500000000000000000000000001355123075200145325ustar00rootroot00000000000000multi_json-1.14.1/lib/multi_json.rb000066400000000000000000000102301355123075200172360ustar00rootroot00000000000000require 'multi_json/options' require 'multi_json/version' require 'multi_json/adapter_error' require 'multi_json/parse_error' require 'multi_json/options_cache' module MultiJson include Options extend self def default_options=(value) Kernel.warn "MultiJson.default_options setter is deprecated\n" \ 'Use MultiJson.load_options and MultiJson.dump_options instead' self.load_options = self.dump_options = value end def default_options Kernel.warn "MultiJson.default_options is deprecated\n" \ 'Use MultiJson.load_options or MultiJson.dump_options instead' load_options end %w(cached_options reset_cached_options!).each do |method_name| define_method method_name do |*| Kernel.warn "MultiJson.#{method_name} method is deprecated and no longer used." end end ALIASES = {'jrjackson' => 'jr_jackson'} REQUIREMENT_MAP = [ [:oj, 'oj'], [:yajl, 'yajl'], [:jr_jackson, 'jrjackson'], [:json_gem, 'json/ext'], [:gson, 'gson'], [:json_pure, 'json/pure'], ] # The default adapter based on what you currently # have loaded and installed. First checks to see # if any adapters are already loaded, then checks # to see which are installed if none are loaded. def default_adapter return :oj if defined?(::Oj) return :yajl if defined?(::Yajl) return :jr_jackson if defined?(::JrJackson) return :json_gem if defined?(::JSON::JSON_LOADED) return :gson if defined?(::Gson) REQUIREMENT_MAP.each do |adapter, library| begin require library return adapter rescue ::LoadError next end end Kernel.warn '[WARNING] MultiJson is using the default adapter (ok_json). ' \ 'We recommend loading a different JSON library to improve performance.' :ok_json end alias_method :default_engine, :default_adapter # Get the current adapter class. def adapter return @adapter if defined?(@adapter) && @adapter use nil # load default adapter @adapter end alias_method :engine, :adapter # Set the JSON parser utilizing a symbol, string, or class. # Supported by default are: # # * :oj # * :json_gem # * :json_pure # * :ok_json # * :yajl # * :nsjsonserialization (MacRuby only) # * :gson (JRuby only) # * :jr_jackson (JRuby only) def use(new_adapter) @adapter = load_adapter(new_adapter) ensure OptionsCache.reset end alias_method :adapter=, :use alias_method :engine=, :use def load_adapter(new_adapter) case new_adapter when String, Symbol load_adapter_from_string_name new_adapter.to_s when NilClass, FalseClass load_adapter default_adapter when Class, Module new_adapter else fail ::LoadError, new_adapter end rescue ::LoadError => exception raise AdapterError.build(exception) end # Decode a JSON string into Ruby. # # Options # # :symbolize_keys :: If true, will use symbols instead of strings for the keys. # :adapter :: If set, the selected adapter will be used for this call. def load(string, options = {}) adapter = current_adapter(options) begin adapter.load(string, options) rescue adapter::ParseError => exception raise ParseError.build(exception, string) end end alias_method :decode, :load def current_adapter(options = {}) if (new_adapter = options[:adapter]) load_adapter(new_adapter) else adapter end end # Encodes a Ruby object as JSON. def dump(object, options = {}) current_adapter(options).dump(object, options) end alias_method :encode, :dump # Executes passed block using specified adapter. def with_adapter(new_adapter) old_adapter = adapter self.adapter = new_adapter yield ensure self.adapter = old_adapter end alias_method :with_engine, :with_adapter private def load_adapter_from_string_name(name) name = ALIASES.fetch(name, name) require "multi_json/adapters/#{name.downcase}" klass_name = name.to_s.split('_').map(&:capitalize) * '' MultiJson::Adapters.const_get(klass_name) end end multi_json-1.14.1/lib/multi_json/000077500000000000000000000000001355123075200167155ustar00rootroot00000000000000multi_json-1.14.1/lib/multi_json/adapter.rb000066400000000000000000000023251355123075200206640ustar00rootroot00000000000000require 'singleton' require 'multi_json/options' module MultiJson class Adapter extend Options include Singleton class << self def defaults(action, value) metaclass = class << self; self; end metaclass.instance_eval do define_method("default_#{action}_options") { value } end end def load(string, options = {}) string = string.read if string.respond_to?(:read) fail self::ParseError if blank?(string) instance.load(string, cached_load_options(options)) end def dump(object, options = {}) instance.dump(object, cached_dump_options(options)) end private def blank?(input) input.nil? || /\A\s*\z/ === input rescue ArgumentError # invalid byte sequence in UTF-8 false end def cached_dump_options(options) OptionsCache.fetch(:dump, options) do dump_options(options).merge(MultiJson.dump_options(options)).merge!(options) end end def cached_load_options(options) OptionsCache.fetch(:load, options) do load_options(options).merge(MultiJson.load_options(options)).merge!(options) end end end end end multi_json-1.14.1/lib/multi_json/adapter_error.rb000066400000000000000000000006411355123075200220740ustar00rootroot00000000000000module MultiJson class AdapterError < ArgumentError attr_reader :cause def self.build(original_exception) message = "Did not recognize your adapter specification (#{original_exception.message})." new(message).tap do |exception| exception.instance_eval do @cause = original_exception set_backtrace original_exception.backtrace end end end end end multi_json-1.14.1/lib/multi_json/adapters/000077500000000000000000000000001355123075200205205ustar00rootroot00000000000000multi_json-1.14.1/lib/multi_json/adapters/gson.rb000066400000000000000000000006511355123075200220150ustar00rootroot00000000000000require 'gson' require 'stringio' require 'multi_json/adapter' module MultiJson module Adapters # Use the gson.rb library to dump/load. class Gson < Adapter ParseError = ::Gson::DecodeError def load(string, options = {}) ::Gson::Decoder.new(options).decode(string) end def dump(object, options = {}) ::Gson::Encoder.new(options).encode(object) end end end end multi_json-1.14.1/lib/multi_json/adapters/jr_jackson.rb000066400000000000000000000011471355123075200231730ustar00rootroot00000000000000require 'jrjackson' unless defined?(::JrJackson) require 'multi_json/adapter' module MultiJson module Adapters # Use the jrjackson.rb library to dump/load. class JrJackson < Adapter ParseError = ::JrJackson::ParseError def load(string, options = {}) #:nodoc: ::JrJackson::Json.load(string, options) end if ::JrJackson::Json.method(:dump).arity == 1 def dump(object, _) ::JrJackson::Json.dump(object) end else def dump(object, options = {}) ::JrJackson::Json.dump(object, options) end end end end end multi_json-1.14.1/lib/multi_json/adapters/json_common.rb000066400000000000000000000012031355123075200233620ustar00rootroot00000000000000require 'multi_json/adapter' module MultiJson module Adapters class JsonCommon < Adapter defaults :load, :create_additions => false, :quirks_mode => true def load(string, options = {}) if string.respond_to?(:force_encoding) string = string.dup.force_encoding(::Encoding::ASCII_8BIT) end options[:symbolize_names] = true if options.delete(:symbolize_keys) ::JSON.parse(string, options) end def dump(object, options = {}) options.merge!(::JSON::PRETTY_STATE_PROTOTYPE.to_h) if options.delete(:pretty) object.to_json(options) end end end end multi_json-1.14.1/lib/multi_json/adapters/json_gem.rb000066400000000000000000000003361355123075200226500ustar00rootroot00000000000000require 'json/ext' require 'multi_json/adapters/json_common' module MultiJson module Adapters # Use the JSON gem to dump/load. class JsonGem < JsonCommon ParseError = ::JSON::ParserError end end end multi_json-1.14.1/lib/multi_json/adapters/json_pure.rb000066400000000000000000000003351355123075200230520ustar00rootroot00000000000000require 'json/pure' require 'multi_json/adapters/json_common' module MultiJson module Adapters # Use JSON pure to dump/load. class JsonPure < JsonCommon ParseError = ::JSON::ParserError end end end multi_json-1.14.1/lib/multi_json/adapters/nsjsonserialization.rb000066400000000000000000000024631355123075200251620ustar00rootroot00000000000000# This adapter is here for legacy reasons. We can't really test it, so it's hard # to tell if it's even working properly. If you're still using it, please # consider migrating to any other adapter out there. framework 'Foundation' require 'multi_json/adapters/ok_json' module MultiJson module Adapters class Nsjsonserialization < MultiJson::Adapters::OkJson ParseError = ::MultiJson::OkJson::Error def load(string, options = {}) data = string.dataUsingEncoding(NSUTF8StringEncoding) object = NSJSONSerialization.JSONObjectWithData(data, :options => NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves, :error => nil) if object object = symbolize_keys(object) if options[:symbolize_keys] object else super(string, options) end end def dump(object, options = {}) pretty = options[:pretty] ? NSJSONWritingPrettyPrinted : 0 object = object.as_json if object.respond_to?(:as_json) if NSJSONSerialization.isValidJSONObject(object) data = NSJSONSerialization.dataWithJSONObject(object, :options => pretty, :error => nil) NSMutableString.alloc.initWithData(data, :encoding => NSUTF8StringEncoding) else super(object, options) end end end end end multi_json-1.14.1/lib/multi_json/adapters/oj.rb000066400000000000000000000040701355123075200214560ustar00rootroot00000000000000require 'set' require 'oj' require 'multi_json/adapter' module MultiJson module Adapters # Use the Oj library to dump/load. class Oj < Adapter defaults :load, :mode => :strict, :symbolize_keys => false defaults :dump, :mode => :compat, :time_format => :ruby, :use_to_json => true # In certain cases OJ gem may throw JSON::ParserError exception instead # of its own class. Also, we can't expect ::JSON::ParserError and # ::Oj::ParseError to always be defined, since it's often not the case. # Because of this, we can't reference those classes directly and have to # do string comparison instead. This will not catch subclasses, but it # shouldn't be a problem since the library is not known to be using it # (at least for now). class ParseError < ::SyntaxError WRAPPED_CLASSES = %w[Oj::ParseError JSON::ParserError].to_set.freeze def self.===(exception) case exception when ::SyntaxError true else WRAPPED_CLASSES.include?(exception.class.to_s) end end end def load(string, options = {}) options[:symbol_keys] = options[:symbolize_keys] ::Oj.load(string, options) end case ::Oj::VERSION when /\A2\./ def dump(object, options = {}) options.merge!(:indent => 2) if options[:pretty] options[:indent] = options[:indent].to_i if options[:indent] ::Oj.dump(object, options) end when /\A3\./ PRETTY_STATE_PROTOTYPE = { :indent => " ", :space => " ", :space_before => "", :object_nl => "\n", :array_nl => "\n", :ascii_only => false, } def dump(object, options = {}) options.merge!(PRETTY_STATE_PROTOTYPE.dup) if options.delete(:pretty) ::Oj.dump(object, options) end else fail "Unsupported Oj version: #{::Oj::VERSION}" end end end end multi_json-1.14.1/lib/multi_json/adapters/ok_json.rb000066400000000000000000000011721355123075200225100ustar00rootroot00000000000000require 'multi_json/adapter' require 'multi_json/convertible_hash_keys' require 'multi_json/vendor/okjson' module MultiJson module Adapters class OkJson < Adapter include ConvertibleHashKeys ParseError = ::MultiJson::OkJson::Error def load(string, options = {}) result = ::MultiJson::OkJson.decode("[#{string}]").first options[:symbolize_keys] ? symbolize_keys(result) : result rescue ArgumentError # invalid byte sequence in UTF-8 raise ParseError end def dump(object, _ = {}) ::MultiJson::OkJson.valenc(stringify_keys(object)) end end end end multi_json-1.14.1/lib/multi_json/adapters/yajl.rb000066400000000000000000000006651355123075200220130ustar00rootroot00000000000000require 'yajl' require 'multi_json/adapter' module MultiJson module Adapters # Use the Yajl-Ruby library to dump/load. class Yajl < Adapter ParseError = ::Yajl::ParseError def load(string, options = {}) ::Yajl::Parser.new(:symbolize_keys => options[:symbolize_keys]).parse(string) end def dump(object, options = {}) ::Yajl::Encoder.encode(object, options) end end end end multi_json-1.14.1/lib/multi_json/convertible_hash_keys.rb000066400000000000000000000017401355123075200236160ustar00rootroot00000000000000module MultiJson module ConvertibleHashKeys private def symbolize_keys(hash) prepare_hash(hash) do |key| key.respond_to?(:to_sym) ? key.to_sym : key end end def stringify_keys(hash) prepare_hash(hash) do |key| key.respond_to?(:to_s) ? key.to_s : key end end def prepare_hash(hash, &key_modifier) return hash unless block_given? case hash when Array hash.map do |value| prepare_hash(value, &key_modifier) end when Hash hash.inject({}) do |result, (key, value)| new_key = key_modifier.call(key) new_value = prepare_hash(value, &key_modifier) result.merge! new_key => new_value end when String, Numeric, true, false, nil hash else if hash.respond_to?(:to_json) hash elsif hash.respond_to?(:to_s) hash.to_s else hash end end end end end multi_json-1.14.1/lib/multi_json/options.rb000066400000000000000000000015541355123075200207420ustar00rootroot00000000000000module MultiJson module Options def load_options=(options) OptionsCache.reset @load_options = options end def dump_options=(options) OptionsCache.reset @dump_options = options end def load_options(*args) defined?(@load_options) && get_options(@load_options, *args) || default_load_options end def dump_options(*args) defined?(@dump_options) && get_options(@dump_options, *args) || default_dump_options end def default_load_options @default_load_options ||= {} end def default_dump_options @default_dump_options ||= {} end private def get_options(options, *args) if options.respond_to?(:call) && options.arity options.arity == 0 ? options[] : options[*args] elsif options.respond_to?(:to_hash) options.to_hash end end end end multi_json-1.14.1/lib/multi_json/options_cache.rb000066400000000000000000000013711355123075200220620ustar00rootroot00000000000000module MultiJson module OptionsCache extend self def reset @dump_cache = {} @load_cache = {} end def fetch(type, key, &block) cache = instance_variable_get("@#{type}_cache") cache.key?(key) ? cache[key] : write(cache, key, &block) end private # Normally MultiJson is used with a few option sets for both dump/load # methods. When options are generated dynamically though, every call would # cause a cache miss and the cache would grow indefinitely. To prevent # this, we just reset the cache every time the number of keys outgrows # 1000. MAX_CACHE_SIZE = 1000 def write(cache, key) cache.clear if cache.length >= MAX_CACHE_SIZE cache[key] = yield end end end multi_json-1.14.1/lib/multi_json/parse_error.rb000066400000000000000000000006571355123075200215750ustar00rootroot00000000000000module MultiJson class ParseError < StandardError attr_reader :data, :cause def self.build(original_exception, data) new(original_exception.message).tap do |exception| exception.instance_eval do @cause = original_exception set_backtrace original_exception.backtrace @data = data end end end end DecodeError = LoadError = ParseError # Legacy support end multi_json-1.14.1/lib/multi_json/vendor/000077500000000000000000000000001355123075200202125ustar00rootroot00000000000000multi_json-1.14.1/lib/multi_json/vendor/okjson.rb000066400000000000000000000355311355123075200220510ustar00rootroot00000000000000# encoding: UTF-8 # # Copyright 2011, 2012 Keith Rarick # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # See https://github.com/kr/okjson for updates. require 'stringio' module MultiJson # Some parts adapted from # https://golang.org/src/encoding/json/decode.go and # https://golang.org/src/unicode/utf8/utf8.go module OkJson Upstream = '45' extend self # Decodes a json document in string s and # returns the corresponding ruby value. # String s must be valid UTF-8. If you have # a string in some other encoding, convert # it first. # # String values in the resulting structure # will be UTF-8. def decode(s) ts = lex(s) v, ts = textparse(ts) if ts.length > 0 raise Error, 'trailing garbage' end v end # Encodes x into a json text. It may contain only # Array, Hash, String, Numeric, true, false, nil. # (Note, this list excludes Symbol.) # X itself must be an Array or a Hash. # No other value can be encoded, and an error will # be raised if x contains any other value, such as # Nan, Infinity, Symbol, and Proc, or if a Hash key # is not a String. # Strings contained in x must be valid UTF-8. def encode(x) case x when Hash then objenc(x) when Array then arrenc(x) else raise Error, 'root value must be an Array or a Hash' end end def valenc(x) case x when Hash then objenc(x) when Array then arrenc(x) when String then strenc(x) when Numeric then numenc(x) when true then "true" when false then "false" when nil then "null" else if x.respond_to?(:to_json) x.to_json else raise Error, "cannot encode #{x.class}: #{x.inspect}" end end end private # Parses a "json text" in the sense of RFC 4627. # Returns the parsed value and any trailing tokens. # Note: this is almost the same as valparse, # except that it does not accept atomic values. def textparse(ts) if ts.length <= 0 raise Error, 'empty' end typ, _, val = ts[0] case typ when '{' then objparse(ts) when '[' then arrparse(ts) else raise Error, "unexpected #{val.inspect}" end end # Parses a "value" in the sense of RFC 4627. # Returns the parsed value and any trailing tokens. def valparse(ts) if ts.length <= 0 raise Error, 'empty' end typ, _, val = ts[0] case typ when '{' then objparse(ts) when '[' then arrparse(ts) when :val,:str then [val, ts[1..-1]] else raise Error, "unexpected #{val.inspect}" end end # Parses an "object" in the sense of RFC 4627. # Returns the parsed value and any trailing tokens. def objparse(ts) ts = eat('{', ts) obj = {} if ts[0][0] == '}' return obj, ts[1..-1] end k, v, ts = pairparse(ts) obj[k] = v if ts[0][0] == '}' return obj, ts[1..-1] end loop do ts = eat(',', ts) k, v, ts = pairparse(ts) obj[k] = v if ts[0][0] == '}' return obj, ts[1..-1] end end end # Parses a "member" in the sense of RFC 4627. # Returns the parsed values and any trailing tokens. def pairparse(ts) (typ, _, k), ts = ts[0], ts[1..-1] if typ != :str raise Error, "unexpected #{k.inspect}" end ts = eat(':', ts) v, ts = valparse(ts) [k, v, ts] end # Parses an "array" in the sense of RFC 4627. # Returns the parsed value and any trailing tokens. def arrparse(ts) ts = eat('[', ts) arr = [] if ts[0][0] == ']' return arr, ts[1..-1] end v, ts = valparse(ts) arr << v if ts[0][0] == ']' return arr, ts[1..-1] end loop do ts = eat(',', ts) v, ts = valparse(ts) arr << v if ts[0][0] == ']' return arr, ts[1..-1] end end end def eat(typ, ts) if ts[0][0] != typ raise Error, "expected #{typ} (got #{ts[0].inspect})" end ts[1..-1] end # Scans s and returns a list of json tokens, # excluding white space (as defined in RFC 4627). def lex(s) ts = [] while s.length > 0 typ, lexeme, val = tok(s) if typ == nil raise Error, "invalid character at #{s[0,10].inspect}" end if typ != :space ts << [typ, lexeme, val] end s = s[lexeme.length..-1] end ts end # Scans the first token in s and # returns a 3-element list, or nil # if s does not begin with a valid token. # # The first list element is one of # '{', '}', ':', ',', '[', ']', # :val, :str, and :space. # # The second element is the lexeme. # # The third element is the value of the # token for :val and :str, otherwise # it is the lexeme. def tok(s) case s[0] when ?{ then ['{', s[0,1], s[0,1]] when ?} then ['}', s[0,1], s[0,1]] when ?: then [':', s[0,1], s[0,1]] when ?, then [',', s[0,1], s[0,1]] when ?[ then ['[', s[0,1], s[0,1]] when ?] then [']', s[0,1], s[0,1]] when ?n then nulltok(s) when ?t then truetok(s) when ?f then falsetok(s) when ?" then strtok(s) when Spc, ?\t, ?\n, ?\r then [:space, s[0,1], s[0,1]] else numtok(s) end end def nulltok(s); s[0,4] == 'null' ? [:val, 'null', nil] : [] end def truetok(s); s[0,4] == 'true' ? [:val, 'true', true] : [] end def falsetok(s); s[0,5] == 'false' ? [:val, 'false', false] : [] end def numtok(s) m = /(-?(?:[1-9][0-9]+|[0-9]))([.][0-9]+)?([eE][+-]?[0-9]+)?/.match(s) if m && m.begin(0) == 0 if !m[2] && !m[3] [:val, m[0], Integer(m[0])] elsif m[2] [:val, m[0], Float(m[0])] else [:val, m[0], Integer(m[1])*(10**m[3][1..-1].to_i(10))] end else [] end end def strtok(s) m = /"([^"\\]|\\["\/\\bfnrt]|\\u[0-9a-fA-F]{4})*"/.match(s) if ! m raise Error, "invalid string literal at #{abbrev(s)}" end [:str, m[0], unquote(m[0])] end def abbrev(s) t = s[0,10] p = t['`'] t = t[0,p] if p t = t + '...' if t.length < s.length '`' + t + '`' end # Converts a quoted json string literal q into a UTF-8-encoded string. # The rules are different than for Ruby, so we cannot use eval. # Unquote will raise an error if q contains control characters. def unquote(q) q = q[1...-1] a = q.dup # allocate a big enough string # In ruby >= 1.9, a[w] is a codepoint, not a byte. if rubydoesenc? a.force_encoding('UTF-8') end r, w = 0, 0 while r < q.length c = q[r] if c == ?\\ r += 1 if r >= q.length raise Error, "string literal ends with a \"\\\": \"#{q}\"" end case q[r] when ?",?\\,?/,?' a[w] = q[r] r += 1 w += 1 when ?b,?f,?n,?r,?t a[w] = Unesc[q[r]] r += 1 w += 1 when ?u r += 1 uchar = begin hexdec4(q[r,4]) rescue RuntimeError => e raise Error, "invalid escape sequence \\u#{q[r,4]}: #{e}" end r += 4 if surrogate? uchar if q.length >= r+6 uchar1 = hexdec4(q[r+2,4]) uchar = subst(uchar, uchar1) if uchar != Ucharerr # A valid pair; consume. r += 6 end end end if rubydoesenc? a[w] = '' << uchar w += 1 else w += ucharenc(a, w, uchar) end else raise Error, "invalid escape char #{q[r]} in \"#{q}\"" end elsif c == ?" || c < Spc raise Error, "invalid character in string literal \"#{q}\"" else # Copy anything else byte-for-byte. # Valid UTF-8 will remain valid UTF-8. # Invalid UTF-8 will remain invalid UTF-8. # In ruby >= 1.9, c is a codepoint, not a byte, # in which case this is still what we want. a[w] = c r += 1 w += 1 end end a[0,w] end # Encodes unicode character u as UTF-8 # bytes in string a at position i. # Returns the number of bytes written. def ucharenc(a, i, u) if u <= Uchar1max a[i] = (u & 0xff).chr 1 elsif u <= Uchar2max a[i+0] = (Utag2 | ((u>>6)&0xff)).chr a[i+1] = (Utagx | (u&Umaskx)).chr 2 elsif u <= Uchar3max a[i+0] = (Utag3 | ((u>>12)&0xff)).chr a[i+1] = (Utagx | ((u>>6)&Umaskx)).chr a[i+2] = (Utagx | (u&Umaskx)).chr 3 else a[i+0] = (Utag4 | ((u>>18)&0xff)).chr a[i+1] = (Utagx | ((u>>12)&Umaskx)).chr a[i+2] = (Utagx | ((u>>6)&Umaskx)).chr a[i+3] = (Utagx | (u&Umaskx)).chr 4 end end def hexdec4(s) if s.length != 4 raise Error, 'short' end (nibble(s[0])<<12) | (nibble(s[1])<<8) | (nibble(s[2])<<4) | nibble(s[3]) end def subst(u1, u2) if Usurr1 <= u1 && u1 < Usurr2 && Usurr2 <= u2 && u2 < Usurr3 return ((u1-Usurr1)<<10) | (u2-Usurr2) + Usurrself end return Ucharerr end def surrogate?(u) Usurr1 <= u && u < Usurr3 end def nibble(c) if ?0 <= c && c <= ?9 then c.ord - ?0.ord elsif ?a <= c && c <= ?z then c.ord - ?a.ord + 10 elsif ?A <= c && c <= ?Z then c.ord - ?A.ord + 10 else raise Error, "invalid hex code #{c}" end end def objenc(x) '{' + x.map{|k,v| keyenc(k) + ':' + valenc(v)}.join(',') + '}' end def arrenc(a) '[' + a.map{|x| valenc(x)}.join(',') + ']' end def keyenc(k) case k when String then strenc(k) else raise Error, "Hash key is not a string: #{k.inspect}" end end def strenc(s) t = StringIO.new t.putc(?") r = 0 while r < s.length case s[r] when ?" then t.print('\\"') when ?\\ then t.print('\\\\') when ?\b then t.print('\\b') when ?\f then t.print('\\f') when ?\n then t.print('\\n') when ?\r then t.print('\\r') when ?\t then t.print('\\t') else c = s[r] # In ruby >= 1.9, s[r] is a codepoint, not a byte. if rubydoesenc? begin # c.ord will raise an error if c is invalid UTF-8 if c.ord < Spc.ord c = "\\u%04x" % [c.ord] end t.write(c) rescue t.write(Ustrerr) end elsif c < Spc t.write("\\u%04x" % c) elsif Spc <= c && c <= ?~ t.putc(c) else n = ucharcopy(t, s, r) # ensure valid UTF-8 output r += n - 1 # r is incremented below end end r += 1 end t.putc(?") t.string end def numenc(x) if ((x.nan? || x.infinite?) rescue false) raise Error, "Numeric cannot be represented: #{x}" end "#{x}" end # Copies the valid UTF-8 bytes of a single character # from string s at position i to I/O object t, and # returns the number of bytes copied. # If no valid UTF-8 char exists at position i, # ucharcopy writes Ustrerr and returns 1. def ucharcopy(t, s, i) n = s.length - i raise Utf8Error if n < 1 c0 = s[i].ord # 1-byte, 7-bit sequence? if c0 < Utagx t.putc(c0) return 1 end raise Utf8Error if c0 < Utag2 # unexpected continuation byte? raise Utf8Error if n < 2 # need continuation byte c1 = s[i+1].ord raise Utf8Error if c1 < Utagx || Utag2 <= c1 # 2-byte, 11-bit sequence? if c0 < Utag3 raise Utf8Error if ((c0&Umask2)<<6 | (c1&Umaskx)) <= Uchar1max t.putc(c0) t.putc(c1) return 2 end # need second continuation byte raise Utf8Error if n < 3 c2 = s[i+2].ord raise Utf8Error if c2 < Utagx || Utag2 <= c2 # 3-byte, 16-bit sequence? if c0 < Utag4 u = (c0&Umask3)<<12 | (c1&Umaskx)<<6 | (c2&Umaskx) raise Utf8Error if u <= Uchar2max t.putc(c0) t.putc(c1) t.putc(c2) return 3 end # need third continuation byte raise Utf8Error if n < 4 c3 = s[i+3].ord raise Utf8Error if c3 < Utagx || Utag2 <= c3 # 4-byte, 21-bit sequence? if c0 < Utag5 u = (c0&Umask4)<<18 | (c1&Umaskx)<<12 | (c2&Umaskx)<<6 | (c3&Umaskx) raise Utf8Error if u <= Uchar3max t.putc(c0) t.putc(c1) t.putc(c2) t.putc(c3) return 4 end raise Utf8Error rescue Utf8Error t.write(Ustrerr) return 1 end def rubydoesenc? ::String.method_defined?(:force_encoding) end class Utf8Error < ::StandardError end class Error < ::StandardError end Utagx = 0b1000_0000 Utag2 = 0b1100_0000 Utag3 = 0b1110_0000 Utag4 = 0b1111_0000 Utag5 = 0b1111_1000 Umaskx = 0b0011_1111 Umask2 = 0b0001_1111 Umask3 = 0b0000_1111 Umask4 = 0b0000_0111 Uchar1max = (1<<7) - 1 Uchar2max = (1<<11) - 1 Uchar3max = (1<<16) - 1 Ucharerr = 0xFFFD # unicode "replacement char" Ustrerr = "\xef\xbf\xbd" # unicode "replacement char" Usurrself = 0x10000 Usurr1 = 0xd800 Usurr2 = 0xdc00 Usurr3 = 0xe000 Spc = ' '[0] Unesc = {?b=>?\b, ?f=>?\f, ?n=>?\n, ?r=>?\r, ?t=>?\t} end end multi_json-1.14.1/lib/multi_json/version.rb000066400000000000000000000006551355123075200207350ustar00rootroot00000000000000module MultiJson class Version MAJOR = 1 unless defined? MultiJson::Version::MAJOR MINOR = 14 unless defined? MultiJson::Version::MINOR PATCH = 1 unless defined? MultiJson::Version::PATCH PRE = nil unless defined? MultiJson::Version::PRE class << self # @return [String] def to_s [MAJOR, MINOR, PATCH, PRE].compact.join('.') end end end VERSION = Version.to_s.freeze end multi_json-1.14.1/multi_json.gemspec000066400000000000000000000026651355123075200175250ustar00rootroot00000000000000# coding: utf-8 require File.expand_path("../lib/multi_json/version.rb", __FILE__) Gem::Specification.new do |spec| spec.authors = ["Michael Bleigh", "Josh Kalderimis", "Erik Michaels-Ober", "Pavel Pravosud"] spec.email = %w[michael@intridea.com josh.kalderimis@gmail.com sferik@gmail.com pavel@pravosud.com] spec.summary = "A common interface to multiple JSON libraries." spec.description = "A common interface to multiple JSON libraries, including Oj, Yajl, the JSON gem (with C-extensions), the pure-Ruby JSON gem, NSJSONSerialization, gson.rb, JrJackson, and OkJson." spec.files = Dir["*.md", "lib/**/*"] spec.homepage = "https://github.com/intridea/multi_json" spec.license = "MIT" spec.name = "multi_json" spec.require_path = "lib" spec.version = MultiJson::Version spec.metadata = { 'bug_tracker_uri' => 'https://github.com/intridea/multi_json/issues', 'changelog_uri' => "https://github.com/intridea/multi_json/blob/v#{spec.version}/CHANGELOG.md", 'documentation_uri' => "https://www.rubydoc.info/gems/multi_json/#{spec.version}", 'source_code_uri' => "https://github.com/intridea/multi_json/tree/v#{spec.version}", 'wiki_uri' => 'https://github.com/intridea/multi_json/wiki' } spec.required_rubygems_version = ">= 1.3.5" spec.add_development_dependency "rake", "~> 10.5" spec.add_development_dependency "rspec", "~> 3.9" end multi_json-1.14.1/spec/000077500000000000000000000000001355123075200147165ustar00rootroot00000000000000multi_json-1.14.1/spec/gson_adapter_spec.rb000066400000000000000000000003171355123075200207240ustar00rootroot00000000000000require 'spec_helper' exit 0 if skip_adapter?('gson') require 'shared/adapter' require 'multi_json/adapters/gson' describe MultiJson::Adapters::Gson do it_behaves_like 'an adapter', described_class end multi_json-1.14.1/spec/jr_jackson_adapter_spec.rb000066400000000000000000000003401355123075200220750ustar00rootroot00000000000000require 'spec_helper' exit 0 if skip_adapter?('jr_jackson') require 'shared/adapter' require 'multi_json/adapters/jr_jackson' describe MultiJson::Adapters::JrJackson do it_behaves_like 'an adapter', described_class end multi_json-1.14.1/spec/json_gem_adapter_spec.rb000066400000000000000000000004201355123075200215520ustar00rootroot00000000000000require 'spec_helper' require 'shared/adapter' require 'shared/json_common_adapter' require 'multi_json/adapters/json_gem' describe MultiJson::Adapters::JsonGem do it_behaves_like 'an adapter', described_class it_behaves_like 'JSON-like adapter', described_class end multi_json-1.14.1/spec/json_pure_adapter_spec.rb000066400000000000000000000004221355123075200217570ustar00rootroot00000000000000require 'spec_helper' require 'shared/adapter' require 'shared/json_common_adapter' require 'multi_json/adapters/json_pure' describe MultiJson::Adapters::JsonPure do it_behaves_like 'an adapter', described_class it_behaves_like 'JSON-like adapter', described_class end multi_json-1.14.1/spec/multi_json_spec.rb000066400000000000000000000130301355123075200204350ustar00rootroot00000000000000require 'spec_helper' require 'shared/options' describe MultiJson do before(:all) do # make sure all available libs are required MultiJson::REQUIREMENT_MAP.each do |_, library| begin require library rescue ::LoadError next end end end context 'when no other json implementations are available' do around do |example| simulate_no_adapters { example.call } end it 'defaults to ok_json if no other json implementions are available' do silence_warnings do expect(MultiJson.default_adapter).to eq(:ok_json) end end it 'prints a warning' do expect(Kernel).to receive(:warn).with(/warning/i) MultiJson.default_adapter end end context 'caching' do before { MultiJson.use adapter } let(:adapter) { MultiJson::Adapters::JsonGem } let(:json_string) { '{"abc":"def"}' } it 'busts caches on global options change' do MultiJson.load_options = {:symbolize_keys => true} expect(MultiJson.load(json_string)).to eq(:abc => 'def') MultiJson.load_options = nil expect(MultiJson.load(json_string)).to eq('abc' => 'def') end it 'busts caches on per-adapter options change' do adapter.load_options = {:symbolize_keys => true} expect(MultiJson.load(json_string)).to eq(:abc => 'def') adapter.load_options = nil expect(MultiJson.load(json_string)).to eq('abc' => 'def') end end it 'defaults to the best available gem' do # Clear cache variable already set by previous tests MultiJson.send(:remove_instance_variable, :@adapter) if MultiJson.instance_variable_defined?(:@adapter) if jruby? && !skip_adapter?('jr_jackson') expect(MultiJson.adapter.to_s).to eq('MultiJson::Adapters::JrJackson') elsif jruby? expect(MultiJson.adapter.to_s).to eq('MultiJson::Adapters::JsonGem') else expect(MultiJson.adapter.to_s).to eq('MultiJson::Adapters::Oj') end end it 'looks for adapter even if @adapter variable is nil' do MultiJson.send(:instance_variable_set, :@adapter, nil) expect(MultiJson).to receive(:default_adapter).and_return(:ok_json) expect(MultiJson.adapter).to eq(MultiJson::Adapters::OkJson) end it 'is settable via a symbol' do MultiJson.use :json_gem expect(MultiJson.adapter).to eq(MultiJson::Adapters::JsonGem) end it 'is settable via a case-insensitive string' do MultiJson.use 'Json_Gem' expect(MultiJson.adapter).to eq(MultiJson::Adapters::JsonGem) end it 'is settable via a class' do adapter = Class.new MultiJson.use adapter expect(MultiJson.adapter).to eq(adapter) end it 'is settable via a module' do adapter = Module.new MultiJson.use adapter expect(MultiJson.adapter).to eq(adapter) end it 'throws AdapterError on bad input' do expect { MultiJson.use 'bad adapter' }.to raise_error(MultiJson::AdapterError, /bad adapter/) end it 'gives access to original error when raising AdapterError' do exception = get_exception(MultiJson::AdapterError) { MultiJson.use 'foobar' } expect(exception.cause).to be_instance_of(::LoadError) expect(exception.message).to include('-- multi_json/adapters/foobar') expect(exception.message).to include('Did not recognize your adapter specification') end context 'using one-shot parser' do before do expect(MultiJson::Adapters::JsonPure).to receive(:dump).once.and_return('dump_something') expect(MultiJson::Adapters::JsonPure).to receive(:load).once.and_return('load_something') end it 'should use the defined parser just for the call' do MultiJson.use :json_gem expect(MultiJson.dump('', :adapter => :json_pure)).to eq('dump_something') expect(MultiJson.load('', :adapter => :json_pure)).to eq('load_something') expect(MultiJson.adapter).to eq(MultiJson::Adapters::JsonGem) end end it 'can set adapter for a block' do MultiJson.use :ok_json MultiJson.with_adapter(:json_pure) do MultiJson.with_engine(:json_gem) do expect(MultiJson.adapter).to eq(MultiJson::Adapters::JsonGem) end expect(MultiJson.adapter).to eq(MultiJson::Adapters::JsonPure) end expect(MultiJson.adapter).to eq(MultiJson::Adapters::OkJson) end it 'JSON gem does not create symbols on parse' do skip 'breaks in JRuby' if jruby? MultiJson.with_engine(:json_gem) do MultiJson.load('{"json_class":"ZOMG"}') expect do MultiJson.load('{"json_class":"OMG"}') end.to_not change { Symbol.all_symbols.count } end end describe 'default options' do after(:all) { MultiJson.load_options = MultiJson.dump_options = nil } it 'is deprecated' do expect(Kernel).to receive(:warn).with(/deprecated/i) silence_warnings { MultiJson.default_options = {:foo => 'bar'} } end it 'sets both load and dump options' do expect(MultiJson).to receive(:dump_options=).with(:foo => 'bar') expect(MultiJson).to receive(:load_options=).with(:foo => 'bar') silence_warnings { MultiJson.default_options = {:foo => 'bar'} } end end it_behaves_like 'has options', MultiJson describe 'aliases' do unless skip_adapter?('jr_jackson') describe 'jrjackson' do after { expect(MultiJson.adapter).to eq(MultiJson::Adapters::JrJackson) } it 'allows jrjackson alias as symbol' do expect { MultiJson.use :jrjackson }.not_to raise_error end it 'allows jrjackson alias as string' do expect { MultiJson.use 'jrjackson' }.not_to raise_error end end end end end multi_json-1.14.1/spec/nsjsonserialization_adapter_spec.rb000066400000000000000000000003741355123075200240710ustar00rootroot00000000000000require 'spec_helper' exit 0 if skip_adapter?("nsjsonserialization") require 'shared/adapter' require 'multi_json/adapters/nsjsonserialization' describe MultiJson::Adapters::Nsjsonserialization do it_behaves_like 'an adapter', described_class end multi_json-1.14.1/spec/oj_adapter_spec.rb000066400000000000000000000020321355123075200203620ustar00rootroot00000000000000require 'spec_helper' exit 0 if skip_adapter?('oj') require 'shared/adapter' require 'multi_json/adapters/oj' describe MultiJson::Adapters::Oj do it_behaves_like 'an adapter', described_class describe '.dump' do describe '#dump_options' do around { |example| with_default_options(&example) } it 'ensures indent is a Fixnum' do expect { MultiJson.dump(42, :indent => '') }.not_to raise_error end end end it 'Oj does not create symbols on parse' do MultiJson.load('{"json_class":"ZOMG"}') expect do MultiJson.load('{"json_class":"OMG"}') end.to_not change { Symbol.all_symbols.count } end context 'with Oj.default_settings' do around do |example| options = Oj.default_options Oj.default_options = {:symbol_keys => true} example.call Oj.default_options = options end it 'ignores global settings' do example = '{"a": 1, "b": 2}' expected = {'a' => 1, 'b' => 2} expect(MultiJson.load(example)).to eq(expected) end end end multi_json-1.14.1/spec/ok_json_adapter_spec.rb000066400000000000000000000002621355123075200214170ustar00rootroot00000000000000require 'spec_helper' require 'shared/adapter' require 'multi_json/adapters/ok_json' describe MultiJson::Adapters::OkJson do it_behaves_like 'an adapter', described_class end multi_json-1.14.1/spec/options_cache_spec.rb000066400000000000000000000010111355123075200210640ustar00rootroot00000000000000require "spec_helper" describe MultiJson::OptionsCache do before { described_class.reset } it "doesn't leak memory" do described_class::MAX_CACHE_SIZE.succ.times do |i| described_class.fetch(:dump, :key => i) do { :foo => i } end described_class.fetch(:load, :key => i) do { :foo => i } end end expect(described_class.instance_variable_get(:@dump_cache).length).to eq(1) expect(described_class.instance_variable_get(:@load_cache).length).to eq(1) end end multi_json-1.14.1/spec/shared/000077500000000000000000000000001355123075200161645ustar00rootroot00000000000000multi_json-1.14.1/spec/shared/adapter.rb000066400000000000000000000160351355123075200201360ustar00rootroot00000000000000# encoding: UTF-8 require 'shared/options' require 'stringio' shared_examples_for 'an adapter' do |adapter| before { MultiJson.use adapter } it_behaves_like 'has options', adapter it 'does not modify argument hashes' do options = {:symbolize_keys => true, :pretty => false, :adapter => :ok_json} expect { MultiJson.load('{}', options) }.to_not change { options } expect { MultiJson.dump([42], options) }.to_not change { options } end describe '.dump' do describe '#dump_options' do before { MultiJson.dump_options = MultiJson.adapter.dump_options = {} } after do expect(MultiJson.adapter.instance).to receive(:dump).with(1, :foo => 'bar', :fizz => 'buzz') MultiJson.dump(1, :fizz => 'buzz') MultiJson.dump_options = MultiJson.adapter.dump_options = nil end it 'respects global dump options' do MultiJson.dump_options = {:foo => 'bar'} end it 'respects per-adapter dump options' do MultiJson.adapter.dump_options = {:foo => 'bar'} end it 'adapter-specific are overridden by global options' do MultiJson.adapter.dump_options = {:foo => 'foo'} MultiJson.dump_options = {:foo => 'bar'} end end it 'writes decodable JSON' do examples = [ {'abc' => 'def'}, [], 1, '2', true, false, nil, ] examples.each do |example| expect(MultiJson.load(MultiJson.dump(example))).to eq(example) end end unless 'json_pure' == adapter || 'json_gem' == adapter it 'dumps time in correct format' do time = Time.at(1_355_218_745).utc dumped_json = MultiJson.dump(time) expected = begin if RUBY_VERSION > '1.9' '2012-12-11 09:39:05 UTC' else 'Tue Dec 11 09:39:05 UTC 2012' end end expect(MultiJson.load(dumped_json)).to eq(expected) end end it 'dumps symbol and fixnum keys as strings' do [ [ {:foo => {:bar => 'baz'}}, {'foo' => {'bar' => 'baz'}}, ], [ [{:foo => {:bar => 'baz'}}], [{'foo' => {'bar' => 'baz'}}], ], [ {:foo => [{:bar => 'baz'}]}, {'foo' => [{'bar' => 'baz'}]}, ], [ {1 => {2 => {3 => 'bar'}}}, {'1' => {'2' => {'3' => 'bar'}}}, ], ].each do |example, expected| dumped_json = MultiJson.dump(example) expect(MultiJson.load(dumped_json)).to eq(expected) end end it 'dumps rootless JSON' do expect(MultiJson.dump('random rootless string')).to eq('"random rootless string"') expect(MultiJson.dump(123)).to eq('123') end it 'passes options to the adapter' do expect(MultiJson.adapter).to receive(:dump).with('foo', :bar => :baz) MultiJson.dump('foo', :bar => :baz) end it 'dumps custom objects that implement to_json' do pending 'not supported' if adapter.name == 'MultiJson::Adapters::Gson' klass = Class.new do def to_json(*) '"foobar"' end end expect(MultiJson.dump(klass.new)).to eq('"foobar"') end it 'allows to dump JSON values' do expect(MultiJson.dump(42)).to eq('42') end it 'allows to dump JSON with UTF-8 characters' do expect(MultiJson.dump('color' => 'żółć')).to eq('{"color":"żółć"}') end end describe '.load' do describe '#load_options' do before { MultiJson.load_options = MultiJson.adapter.load_options = {} } after do expect(MultiJson.adapter.instance).to receive(:load).with('1', :foo => 'bar', :fizz => 'buzz') MultiJson.load('1', :fizz => 'buzz') MultiJson.load_options = MultiJson.adapter.load_options = nil end it 'respects global load options' do MultiJson.load_options = {:foo => 'bar'} end it 'respects per-adapter load options' do MultiJson.adapter.load_options = {:foo => 'bar'} end it 'adapter-specific are overridden by global options' do MultiJson.adapter.load_options = {:foo => 'foo'} MultiJson.load_options = {:foo => 'bar'} end end it 'does not modify input' do input = %(\n\n {"foo":"bar"} \n\n\t) expect do MultiJson.load(input) end.to_not change { input } end # Ruby 1.8 doesn't support String encodings if RUBY_VERSION > '1.9' it 'does not modify input encoding' do input = '[123]' input.force_encoding('iso-8859-1') expect do MultiJson.load(input) end.to_not change { input.encoding } end end it 'properly loads valid JSON' do expect(MultiJson.load('{"abc":"def"}')).to eq('abc' => 'def') end examples = [nil, '{"abc"}', ' ', "\t\t\t", "\n", StringIO.new('')] # # GSON bug: https://github.com/avsej/gson.rb/issues/3 examples << "\x82\xAC\xEF" unless adapter.name =~ /Gson/ examples.each do |input| it "raises MultiJson::ParseError on invalid input: #{input.inspect}" do expect { MultiJson.load(input) }.to raise_error(MultiJson::ParseError) end end it 'raises MultiJson::ParseError with data on invalid JSON' do data = '{invalid}' exception = get_exception(MultiJson::ParseError) { MultiJson.load data } expect(exception.data).to eq(data) expect(exception.cause).to match(adapter::ParseError) end it 'catches MultiJson::DecodeError for legacy support' do data = '{invalid}' exception = get_exception(MultiJson::DecodeError) { MultiJson.load data } expect(exception.data).to eq(data) expect(exception.cause).to match(adapter::ParseError) end it 'catches MultiJson::LoadError for legacy support' do data = '{invalid}' exception = get_exception(MultiJson::LoadError) { MultiJson.load data } expect(exception.data).to eq(data) expect(exception.cause).to match(adapter::ParseError) end it 'stringifys symbol keys when encoding' do dumped_json = MultiJson.dump(:a => 1, :b => {:c => 2}) loaded_json = MultiJson.load(dumped_json) expect(loaded_json).to eq('a' => 1, 'b' => {'c' => 2}) end it 'properly loads valid JSON in StringIOs' do json = StringIO.new('{"abc":"def"}') expect(MultiJson.load(json)).to eq('abc' => 'def') end it 'allows for symbolization of keys' do [ [ '{"abc":{"def":"hgi"}}', {:abc => {:def => 'hgi'}}, ], [ '[{"abc":{"def":"hgi"}}]', [{:abc => {:def => 'hgi'}}], ], [ '{"abc":[{"def":"hgi"}]}', {:abc => [{:def => 'hgi'}]}, ], ].each do |example, expected| expect(MultiJson.load(example, :symbolize_keys => true)).to eq(expected) end end it 'allows to load JSON values' do expect(MultiJson.load('42')).to eq(42) end it 'allows to load JSON with UTF-8 characters' do expect(MultiJson.load('{"color":"żółć"}')).to eq('color' => 'żółć') end end end multi_json-1.14.1/spec/shared/json_common_adapter.rb000066400000000000000000000016361355123075200225400ustar00rootroot00000000000000shared_examples_for 'JSON-like adapter' do |adapter| before { MultiJson.use adapter } describe '.dump' do before { MultiJson.dump_options = MultiJson.adapter.dump_options = nil } describe 'with :pretty option set to true' do it 'passes default pretty options' do object = 'foo' expect(object).to receive(:to_json).with(JSON::PRETTY_STATE_PROTOTYPE.to_h) MultiJson.dump(object, :pretty => true) end end describe 'with :indent option' do it 'passes it on dump' do object = 'foo' expect(object).to receive(:to_json).with(:indent => "\t") MultiJson.dump(object, :indent => "\t") end end end describe '.load' do it 'passes :quirks_mode option' do expect(::JSON).to receive(:parse).with('[123]', :quirks_mode => false, :create_additions => false) MultiJson.load('[123]', :quirks_mode => false) end end end multi_json-1.14.1/spec/shared/options.rb000066400000000000000000000061451355123075200202120ustar00rootroot00000000000000shared_examples_for 'has options' do |object| if object.respond_to?(:call) subject { object.call } else subject { object } end describe 'dump options' do before do subject.dump_options = nil end after do subject.dump_options = nil end it 'returns default options if not set' do expect(subject.dump_options).to eq(subject.default_dump_options) end it 'allows hashes' do subject.dump_options = {:foo => 'bar'} expect(subject.dump_options).to eq(:foo => 'bar') end it 'allows objects that implement #to_hash' do value = Class.new do def to_hash {:foo => 'bar'} end end.new subject.dump_options = value expect(subject.dump_options).to eq(:foo => 'bar') end it 'evaluates lambda returning options (with args)' do subject.dump_options = lambda { |a1, a2| {a1 => a2} } expect(subject.dump_options('1', '2')).to eq('1' => '2') end it 'evaluates lambda returning options (with no args)' do subject.dump_options = lambda { {:foo => 'bar'} } expect(subject.dump_options).to eq(:foo => 'bar') end it 'returns empty hash in all other cases' do subject.dump_options = true expect(subject.dump_options).to eq(subject.default_dump_options) subject.dump_options = false expect(subject.dump_options).to eq(subject.default_dump_options) subject.dump_options = 10 expect(subject.dump_options).to eq(subject.default_dump_options) subject.dump_options = nil expect(subject.dump_options).to eq(subject.default_dump_options) end end describe 'load options' do before do subject.load_options = nil end after do subject.load_options = nil end it 'returns default options if not set' do expect(subject.load_options).to eq(subject.default_load_options) end it 'allows hashes' do subject.load_options = {:foo => 'bar'} expect(subject.load_options).to eq(:foo => 'bar') end it 'allows objects that implement #to_hash' do value = Class.new do def to_hash {:foo => 'bar'} end end.new subject.load_options = value expect(subject.load_options).to eq(:foo => 'bar') end it 'evaluates lambda returning options (with args)' do subject.load_options = lambda { |a1, a2| {a1 => a2} } expect(subject.load_options('1', '2')).to eq('1' => '2') end it 'evaluates lambda returning options (with no args)' do subject.load_options = lambda { {:foo => 'bar'} } expect(subject.load_options).to eq(:foo => 'bar') end it 'returns empty hash in all other cases' do subject.load_options = true expect(subject.load_options).to eq(subject.default_load_options) subject.load_options = false expect(subject.load_options).to eq(subject.default_load_options) subject.load_options = 10 expect(subject.load_options).to eq(subject.default_load_options) subject.load_options = nil expect(subject.load_options).to eq(subject.default_load_options) end end end multi_json-1.14.1/spec/spec_helper.rb000066400000000000000000000032171355123075200175370ustar00rootroot00000000000000require 'multi_json' require 'rspec' RSpec.configure do |config| config.expect_with :rspec do |c| c.syntax = :expect end end def silence_warnings old_verbose, $VERBOSE = $VERBOSE, nil yield ensure $VERBOSE = old_verbose end def macruby? defined?(RUBY_ENGINE) && RUBY_ENGINE == 'macruby' end def jruby? defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby' end def skip_adapter?(adapter_name) ENV.fetch("SKIP_ADAPTERS", "").split(",").include?(adapter_name) end def undefine_constants(*consts) values = {} consts.each do |const| if Object.const_defined?(const) values[const] = Object.const_get(const) Object.send :remove_const, const end end yield ensure values.each do |const, value| Object.const_set const, value end end def break_requirements requirements = MultiJson::REQUIREMENT_MAP MultiJson::REQUIREMENT_MAP.each_with_index do |(adapter, library), index| MultiJson::REQUIREMENT_MAP[index] = [adapter, "foo/#{library}"] end yield ensure requirements.each_with_index do |(adapter, library), index| MultiJson::REQUIREMENT_MAP[index] = [adapter, library] end end def simulate_no_adapters break_requirements do undefine_constants :JSON, :Oj, :Yajl, :Gson, :JrJackson do yield end end end def get_exception(exception_class = StandardError) yield rescue exception_class => exception exception end def with_default_options adapter = MultiJson.adapter adapter.load_options = adapter.dump_options = MultiJson.load_options = MultiJson.dump_options = nil yield ensure adapter.load_options = adapter.dump_options = MultiJson.load_options = MultiJson.dump_options = nil end multi_json-1.14.1/spec/yajl_adapter_spec.rb000066400000000000000000000003171355123075200207150ustar00rootroot00000000000000require 'spec_helper' exit 0 if skip_adapter?('yajl') require 'shared/adapter' require 'multi_json/adapters/yajl' describe MultiJson::Adapters::Yajl do it_behaves_like 'an adapter', described_class end