multi_json 1.5.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ require 'gson' unless defined?(::Gson)
2
+
3
+ module MultiJson
4
+ module Adapters
5
+ # Use the gson.rb library to dump/load.
6
+ module Gson
7
+ extend self
8
+
9
+ ParseError = ::Gson::DecodeError
10
+
11
+ def load(string, options={}) #:nodoc:
12
+ ::Gson::Decoder.new(options).decode(string)
13
+ end
14
+
15
+ def dump(object, options={}) #:nodoc:
16
+ ::Gson::Encoder.new(options).encode(object)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,23 +1,33 @@
1
1
  module MultiJson
2
2
  module Adapters
3
3
  module JsonCommon
4
-
5
4
  def load(string, options={})
6
5
  string = string.read if string.respond_to?(:read)
7
- ::JSON.parse(string, :symbolize_names => options[:symbolize_keys], :quirks_mode => true, :create_additions => false)
6
+ ::JSON.parse("[#{string}]", process_load_options!(options)).first
8
7
  end
9
8
 
10
9
  def dump(object, options={})
11
- object.to_json(process_options(options))
10
+ ::JSON.generate([object], process_dump_options!(options)).strip[1..-2]
12
11
  end
13
12
 
14
13
  protected
15
14
 
16
- def process_options(options={})
17
- return options if options.empty?
18
- opts = {}
19
- opts.merge!(JSON::PRETTY_STATE_PROTOTYPE.to_h) if options.delete(:pretty)
20
- opts.merge!(options)
15
+ def process_load_options!(options={})
16
+ process_options!({:create_additions => false}, options) do |opts|
17
+ opts.merge!(:symbolize_names => true) if options.delete(:symbolize_keys)
18
+ end
19
+ end
20
+
21
+ def process_dump_options!(options={})
22
+ process_options!({}, options) do |opts|
23
+ opts.merge!(::JSON::PRETTY_STATE_PROTOTYPE.to_h) if options.delete(:pretty)
24
+ end
25
+ end
26
+
27
+ def process_options!(default_options, options)
28
+ return default_options if options.empty?
29
+ yield default_options
30
+ default_options.merge!(options)
21
31
  end
22
32
 
23
33
  end
@@ -4,7 +4,7 @@ require 'multi_json/adapters/json_common'
4
4
  module MultiJson
5
5
  module Adapters
6
6
  # Use the JSON gem to dump/load.
7
- class JsonGem
7
+ module JsonGem
8
8
  ParseError = ::JSON::ParserError
9
9
  extend JsonCommon
10
10
  end
@@ -4,7 +4,7 @@ require 'multi_json/adapters/json_common'
4
4
  module MultiJson
5
5
  module Adapters
6
6
  # Use JSON pure to dump/load.
7
- class JsonPure
7
+ module JsonPure
8
8
  ParseError = ::JSON::ParserError
9
9
  extend JsonCommon
10
10
  end
@@ -3,10 +3,11 @@ require 'multi_json/adapters/ok_json'
3
3
 
4
4
  module MultiJson
5
5
  module Adapters
6
- class Nsjsonserialization < MultiJson::Adapters::OkJson
6
+ module Nsjsonserialization
7
+ extend self, MultiJson::Adapters::OkJson
7
8
  ParseError = ::MultiJson::OkJson::Error
8
9
 
9
- def self.load(string, options={})
10
+ def load(string, options={})
10
11
  string = string.read if string.respond_to?(:read)
11
12
  data = string.dataUsingEncoding(NSUTF8StringEncoding)
12
13
  object = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves, error: nil)
@@ -18,7 +19,7 @@ module MultiJson
18
19
  end
19
20
  end
20
21
 
21
- def self.dump(object, options={})
22
+ def dump(object, options={})
22
23
  pretty = options[:pretty] ? NSJSONWritingPrettyPrinted : 0
23
24
  object = object.as_json if object.respond_to?(:as_json)
24
25
  if NSJSONSerialization.isValidJSONObject(object)
@@ -3,24 +3,26 @@ require 'oj' unless defined?(::Oj)
3
3
  module MultiJson
4
4
  module Adapters
5
5
  # Use the Oj library to dump/load.
6
- class Oj
6
+ module Oj
7
+ extend self
8
+
9
+ DEFAULT_OPTIONS = {:mode => :compat, :time_format => :ruby}.freeze
10
+
7
11
  ParseError = if defined?(::Oj::ParseError)
8
12
  ::Oj::ParseError
9
13
  else
10
14
  SyntaxError
11
15
  end
12
16
 
13
- ::Oj.default_options = {:mode => :compat}
14
-
15
- def self.load(string, options={}) #:nodoc:
17
+ def load(string, options={}) #:nodoc:
16
18
  options.merge!(:symbol_keys => options[:symbolize_keys])
17
19
  options[:mode] = :strict
18
- ::Oj.load(string, options)
20
+ ::Oj.load(string, DEFAULT_OPTIONS.merge(options))
19
21
  end
20
22
 
21
- def self.dump(object, options={}) #:nodoc:
23
+ def dump(object, options={}) #:nodoc:
22
24
  options.merge!(:indent => 2) if options[:pretty]
23
- ::Oj.dump(object, options)
25
+ ::Oj.dump(object, DEFAULT_OPTIONS.merge(options))
24
26
  end
25
27
  end
26
28
  end
@@ -2,45 +2,55 @@ require 'multi_json/vendor/okjson'
2
2
 
3
3
  module MultiJson
4
4
  module Adapters
5
- class OkJson
5
+ module OkJson
6
+ extend self
7
+
6
8
  ParseError = ::MultiJson::OkJson::Error
7
9
 
8
- def self.load(string, options={}) #:nodoc:
10
+ def load(string, options={}) #:nodoc:
9
11
  string = string.read if string.respond_to?(:read)
10
12
  result = ::MultiJson::OkJson.decode("[#{string}]").first
11
13
  options[:symbolize_keys] ? symbolize_keys(result) : result
12
14
  end
13
15
 
14
- def self.dump(object, options={}) #:nodoc:
16
+ def dump(object, options={}) #:nodoc:
15
17
  ::MultiJson::OkJson.valenc(stringify_keys(object))
16
18
  end
17
19
 
18
- def self.symbolize_keys(object) #:nodoc:
19
- modify_keys(object) do |key|
20
+ def symbolize_keys(object) #:nodoc:
21
+ prepare_object(object) do |key|
20
22
  key.is_a?(String) ? key.to_sym : key
21
23
  end
22
24
  end
23
25
 
24
- def self.stringify_keys(object) #:nodoc:
25
- modify_keys(object) do |key|
26
+ def stringify_keys(object) #:nodoc:
27
+ prepare_object(object) do |key|
26
28
  key.respond_to?(:to_s) ? key.to_s : key
27
29
  end
28
30
  end
29
31
 
30
- def self.modify_keys(object, &modifier) #:nodoc:
32
+ def prepare_object(object, &key_modifier) #:nodoc:
31
33
  case object
32
34
  when Array
33
35
  object.map do |value|
34
- modify_keys(value, &modifier)
36
+ prepare_object(value, &key_modifier)
35
37
  end
36
38
  when Hash
37
39
  object.inject({}) do |result, (key, value)|
38
- new_key = modifier.call(key)
39
- new_value = modify_keys(value, &modifier)
40
+ new_key = key_modifier.call(key)
41
+ new_value = prepare_object(value, &key_modifier)
40
42
  result.merge! new_key => new_value
41
43
  end
42
- else
44
+ when String, Numeric, true, false, nil
43
45
  object
46
+ else
47
+ if object.respond_to?(:to_json)
48
+ object
49
+ elsif object.respond_to?(:to_s)
50
+ object.to_s
51
+ else
52
+ object
53
+ end
44
54
  end
45
55
  end
46
56
  end
@@ -3,14 +3,16 @@ require 'yajl' unless defined?(::Yajl)
3
3
  module MultiJson
4
4
  module Adapters
5
5
  # Use the Yajl-Ruby library to dump/load.
6
- class Yajl
6
+ module Yajl
7
+ extend self
8
+
7
9
  ParseError = ::Yajl::ParseError
8
10
 
9
- def self.load(string, options={}) #:nodoc:
11
+ def load(string, options={}) #:nodoc:
10
12
  ::Yajl::Parser.new(:symbolize_keys => options[:symbolize_keys]).parse(string)
11
13
  end
12
14
 
13
- def self.dump(object, options={}) #:nodoc:
15
+ def dump(object, options={}) #:nodoc:
14
16
  ::Yajl::Encoder.encode(object, options)
15
17
  end
16
18
  end
@@ -1,3 +1,3 @@
1
1
  module MultiJson
2
- VERSION = "1.5.1" unless defined?(MultiJson::VERSION)
2
+ VERSION = "1.6.0" unless defined?(MultiJson::VERSION)
3
3
  end
data/multi_json.gemspec CHANGED
@@ -1,23 +1,22 @@
1
- # encoding: utf-8
2
- require File.expand_path("../lib/multi_json/version", __FILE__)
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'multi_json/version'
3
5
 
4
- Gem::Specification.new do |gem|
5
- gem.add_development_dependency 'rake', '>= 0.9'
6
- gem.add_development_dependency 'rdoc', '>= 3.9'
7
- gem.add_development_dependency 'rspec', '>= 2.6'
8
- gem.add_development_dependency 'simplecov', '>= 0.4'
9
- gem.authors = ["Michael Bleigh", "Josh Kalderimis", "Erik Michaels-Ober"]
10
- gem.description = %q{A gem to provide easy switching between different JSON backends, including Oj, Yajl, the JSON gem (with C-extensions), the pure-Ruby JSON gem, and OkJson.}
11
- gem.email = ['michael@intridea.com', 'josh.kalderimis@gmail.com', 'sferik@gmail.com']
12
- gem.extra_rdoc_files = ['LICENSE.md', 'README.md']
13
- gem.files = Dir['LICENSE.md', 'README.md', 'Rakefile', 'multi_json.gemspec', 'Gemfile', '.document', '.rspec', '.travis.yml' ,'spec/**/*', 'lib/**/*']
14
- gem.homepage = 'http://github.com/intridea/multi_json'
15
- gem.licenses = ['MIT']
16
- gem.name = 'multi_json'
17
- gem.rdoc_options = ["--charset=UTF-8"]
18
- gem.require_paths = ['lib']
19
- gem.required_rubygems_version = Gem::Requirement.new(">= 1.3.6")
20
- gem.summary = %q{A gem to provide swappable JSON backends.}
21
- gem.test_files = Dir['spec/**/*']
22
- gem.version = MultiJson::VERSION
6
+ Gem::Specification.new do |spec|
7
+ spec.add_development_dependency 'bundler', '~> 1.0'
8
+ spec.authors = ["Michael Bleigh", "Josh Kalderimis", "Erik Michaels-Ober", "Pavel Pravosud"]
9
+ spec.cert_chain = %w(certs/sferik.pem)
10
+ spec.description = %q{A gem to provide easy switching between different JSON backends, including Oj, Yajl, the JSON gem (with C-extensions), the pure-Ruby JSON gem, and OkJson.}
11
+ spec.email = ['michael@intridea.com', 'josh.kalderimis@gmail.com', 'sferik@gmail.com']
12
+ spec.files = Dir['.yardopts', 'CHANGELOG.md', 'CONTRIBUTING.md', 'LICENSE.md', 'README.md', 'Rakefile', 'multi_json.gemspec', 'Gemfile', '.document', '.rspec', '.travis.yml' ,'spec/**/*', 'lib/**/*']
13
+ spec.homepage = 'http://github.com/intridea/multi_json'
14
+ spec.licenses = ['MIT']
15
+ spec.name = 'multi_json'
16
+ spec.require_paths = ['lib']
17
+ spec.required_rubygems_version = '>= 1.3.6'
18
+ spec.signing_key = File.expand_path("~/.gem/private_key.pem") if $0 =~ /gem\z/
19
+ spec.summary = %q{A gem to provide swappable JSON backends.}
20
+ spec.test_files = Dir['spec/**/*']
21
+ spec.version = MultiJson::VERSION
23
22
  end
@@ -1,4 +1,6 @@
1
- shared_examples_for "an adapter" do |adapter|
1
+ # encoding: UTF-8
2
+
3
+ shared_examples_for 'an adapter' do |adapter|
2
4
 
3
5
  before do
4
6
  begin
@@ -12,12 +14,31 @@ shared_examples_for "an adapter" do |adapter|
12
14
  it 'writes decodable JSON' do
13
15
  [
14
16
  {'abc' => 'def'},
15
- [1, 2, 3, "4"],
17
+ [1, 2, 3, '4', true, false, nil]
16
18
  ].each do |example|
17
19
  expect(MultiJson.load(MultiJson.dump(example))).to eq example
18
20
  end
19
21
  end
20
22
 
23
+ unless 'json_pure' == adapter || 'json_gem' == adapter
24
+ it 'dumps time in correct format' do
25
+ time = Time.at(1355218745).utc
26
+
27
+ # time does not respond to to_json method
28
+ class << time
29
+ undef_method :to_json
30
+ end
31
+
32
+ dumped_json = MultiJson.dump(time)
33
+ expected = if RUBY_VERSION > '1.9'
34
+ '2012-12-11 09:39:05 UTC'
35
+ else
36
+ 'Tue Dec 11 09:39:05 UTC 2012'
37
+ end
38
+ expect(MultiJson.load(dumped_json)).to eq expected
39
+ end
40
+ end
41
+
21
42
  it 'dumps symbol and fixnum keys as strings' do
22
43
  [
23
44
  [
@@ -43,8 +64,8 @@ shared_examples_for "an adapter" do |adapter|
43
64
  end
44
65
 
45
66
  it 'dumps rootless JSON' do
46
- expect(MultiJson.dump("random rootless string")).to eq "\"random rootless string\""
47
- expect(MultiJson.dump(123)).to eq "123"
67
+ expect(MultiJson.dump('random rootless string')).to eq '"random rootless string"'
68
+ expect(MultiJson.dump(123)).to eq '123'
48
69
  end
49
70
 
50
71
  it 'passes options to the adapter' do
@@ -52,24 +73,26 @@ shared_examples_for "an adapter" do |adapter|
52
73
  MultiJson.dump('foo', :bar => :baz)
53
74
  end
54
75
 
55
- if adapter == 'json_gem' || adapter == 'json_pure'
56
- describe 'with :pretty option set to true' do
57
- it 'passes default pretty options' do
58
- object = 'foo'
59
- object.should_receive(:to_json).with(JSON::PRETTY_STATE_PROTOTYPE.to_h)
60
- MultiJson.dump(object,:pretty => true)
76
+ # This behavior is currently not supported by gson.rb
77
+ # See discussion at https://github.com/intridea/multi_json/pull/71
78
+ unless adapter == 'gson'
79
+ it 'dumps custom objects that implement to_json' do
80
+ klass = Class.new do
81
+ def to_json(*)
82
+ '"foobar"'
83
+ end
61
84
  end
85
+ expect(MultiJson.dump(klass.new)).to eq '"foobar"'
62
86
  end
63
87
  end
64
88
 
65
- it 'dumps custom objects which implement as_json' do
66
- expect(MultiJson.dump(TimeWithZone.new)).to eq "\"2005-02-01T15:15:10Z\""
67
- end
68
-
69
- it 'allow to dump JSON values' do
89
+ it 'allows to dump JSON values' do
70
90
  expect(MultiJson.dump(42)).to eq '42'
71
91
  end
72
92
 
93
+ it 'allows to dump JSON with UTF-8 characters' do
94
+ expect(MultiJson.dump({'color' => 'żółć'})).to eq('{"color":"żółć"}')
95
+ end
73
96
  end
74
97
 
75
98
  describe '.load' do
@@ -77,11 +100,20 @@ shared_examples_for "an adapter" do |adapter|
77
100
  expect(MultiJson.load('{"abc":"def"}')).to eq({'abc' => 'def'})
78
101
  end
79
102
 
80
- it 'raises MultiJson::DecodeError on invalid JSON' do
81
- expect{MultiJson.load('{"abc"}')}.to raise_error(MultiJson::DecodeError)
103
+ it 'raises MultiJson::LoadError on invalid JSON' do
104
+ expect{MultiJson.load('{"abc"}')}.to raise_error(MultiJson::LoadError)
82
105
  end
83
106
 
84
- it 'raises MultiJson::DecodeError with data on invalid JSON' do
107
+ it 'raises MultiJson::LoadError with data on invalid JSON' do
108
+ data = '{invalid}'
109
+ begin
110
+ MultiJson.load(data)
111
+ rescue MultiJson::LoadError => le
112
+ expect(le.data).to eq data
113
+ end
114
+ end
115
+
116
+ it 'catches MultiJson::DecodeError for legacy support' do
85
117
  data = '{invalid}'
86
118
  begin
87
119
  MultiJson.load(data)
@@ -92,7 +124,7 @@ shared_examples_for "an adapter" do |adapter|
92
124
 
93
125
  it 'stringifys symbol keys when encoding' do
94
126
  dumped_json = MultiJson.dump(:a => 1, :b => {:c => 2})
95
- expect(MultiJson.load(dumped_json)).to eq({"a" => 1, "b" => {"c" => 2}})
127
+ expect(MultiJson.load(dumped_json)).to eq({'a' => 1, 'b' => {'c' => 2}})
96
128
  end
97
129
 
98
130
  it 'properly loads valid JSON in StringIOs' do
@@ -119,9 +151,12 @@ shared_examples_for "an adapter" do |adapter|
119
151
  end
120
152
  end
121
153
 
122
- it 'allow to load JSON values' do
154
+ it 'allows to load JSON values' do
123
155
  expect(MultiJson.load('42')).to eq 42
124
156
  end
125
157
 
158
+ it 'allows to load JSON with UTF-8 characters' do
159
+ expect(MultiJson.load('{"color":"żółć"}')).to eq({'color' => 'żółć'})
160
+ end
126
161
  end
127
162
  end
data/spec/helper.rb CHANGED
@@ -1,15 +1,7 @@
1
- def jruby?
2
- defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
3
- end
4
-
5
- def macruby?
6
- defined?(RUBY_ENGINE) && RUBY_ENGINE == 'macruby'
7
- end
8
-
9
- unless ENV['CI'] || macruby?
1
+ if !ENV['CI'] && defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby'
10
2
  require 'simplecov'
11
3
  SimpleCov.start do
12
- add_filter 'spec'
4
+ add_filter 'vendor'
13
5
  end
14
6
  end
15
7
 
@@ -22,6 +14,14 @@ RSpec.configure do |config|
22
14
  end
23
15
  end
24
16
 
17
+ def macruby?
18
+ defined?(RUBY_ENGINE) && RUBY_ENGINE == 'macruby'
19
+ end
20
+
21
+ def jruby?
22
+ defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
23
+ end
24
+
25
25
  class MockDecoder
26
26
  def self.load(string, options={})
27
27
  {'abc' => 'def'}
@@ -32,8 +32,14 @@ class MockDecoder
32
32
  end
33
33
  end
34
34
 
35
- class TimeWithZone
36
- def to_json(options={})
37
- "\"2005-02-01T15:15:10Z\""
35
+ module MockModuleDecoder
36
+ extend self
37
+
38
+ def load(string, options={})
39
+ {'abc' => 'def'}
38
40
  end
39
- end
41
+
42
+ def dump(string)
43
+ '{"abc":"def"}'
44
+ end
45
+ end