multi_json 1.5.1 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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