Skip to content

Commit a153956

Browse files
committed
Added load_options/dump_options to MultiJson and adapters
1 parent ec9c30a commit a153956

7 files changed

Lines changed: 219 additions & 14 deletions

File tree

lib/multi_json.rb

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
require 'multi_json/options'
2+
13
module MultiJson
4+
include Options
25
extend self
36

47
class LoadError < StandardError
@@ -11,9 +14,17 @@ def initialize(message='', backtrace=[], data='')
1114
end
1215
DecodeError = LoadError # Legacy support
1316

17+
# Since `default_options` is deprecated, the
18+
# reader is aliased to `dump_options` and the
19+
# writer sets both `dump_options` and `load_options`
20+
alias :default_options :dump_options
21+
22+
def default_options=(value)
23+
Kernel.warn "MultiJson.default_options setter is deprecated\n" +
24+
"Use MultiJson.load_options and MultiJson.dump_options instead"
1425

15-
@default_options = {}
16-
attr_accessor :default_options
26+
self.load_options = self.dump_options = value
27+
end
1728

1829
REQUIREMENT_MAP = [
1930
['oj', :oj],
@@ -117,9 +128,7 @@ def current_adapter(options={})
117128

118129
# Encodes a Ruby object as JSON.
119130
def dump(object, options={})
120-
options = default_options.merge(options)
121-
adapter = current_adapter(options)
122-
adapter.dump(object, options)
131+
current_adapter(options).dump(object, options)
123132
end
124133
# :nodoc:
125134
alias :encode :dump

lib/multi_json/adapter.rb

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,35 @@
11
require 'singleton'
2-
require 'forwardable'
2+
require 'multi_json/options'
33

44
module MultiJson
55
class Adapter
6+
extend Options
67
include Singleton
78
class << self
8-
extend Forwardable
9-
def_delegators :instance, :load, :dump
9+
10+
def load(string, options={})
11+
instance.load(string, collect_load_options(string, options))
12+
end
13+
14+
def dump(object, options={})
15+
instance.dump(object, collect_dump_options(object, options))
16+
end
17+
18+
protected
19+
20+
def collect_load_options(string, options)
21+
collect_options :load_options, options, [ string, options ]
22+
end
23+
24+
def collect_dump_options(object, options)
25+
collect_options :dump_options, options, [ object, options ]
26+
end
27+
28+
def collect_options(method, overrides, args)
29+
global, local = *[MultiJson, self].map{ |r| r.send(method, *args) }
30+
global.merge(local).merge(overrides)
31+
end
32+
1033
end
1134
end
1235
end

lib/multi_json/options.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
module MultiJson
2+
module Options
3+
attr_writer :load_options, :dump_options
4+
5+
def load_options(*args)
6+
get_options :load_options, *args
7+
end
8+
9+
def dump_options(*args)
10+
get_options :dump_options, *args
11+
end
12+
13+
def default_load_options
14+
@default_load_options ||= {}
15+
end
16+
17+
def default_dump_options
18+
@default_dump_options ||= {}
19+
end
20+
21+
private
22+
23+
def get_options(ivar, *args)
24+
defaults = send("default_#{ivar}")
25+
26+
return defaults unless instance_variable_defined?("@#{ivar}")
27+
28+
value = instance_variable_get("@#{ivar}")
29+
30+
if value.respond_to?(:call) and value.arity
31+
value.arity == 0 ? value[] : value[*args]
32+
elsif Hash === value or value.respond_to?(:to_hash)
33+
value.to_hash
34+
else
35+
defaults
36+
end
37+
end
38+
end
39+
end

spec/adapter_shared_example.rb

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,32 @@
1010
end
1111
end
1212

13+
it_behaves_like 'has options', lambda{ MultiJson.adapter }
14+
1315
describe '.dump' do
16+
describe '#dump_options' do
17+
before{ MultiJson.dump_options = MultiJson.adapter.dump_options = {} }
18+
19+
after do
20+
MultiJson.adapter.instance.should_receive(:dump).with(1, :foo=>'bar', :fizz=>'buzz')
21+
MultiJson.dump(1, :fizz => 'buzz')
22+
MultiJson.dump_options = MultiJson.adapter.dump_options = nil
23+
end
24+
25+
it 'respects global dump options' do
26+
MultiJson.dump_options = {:foo => 'bar'}
27+
end
28+
29+
it 'respects per-adapter dump options' do
30+
MultiJson.adapter.dump_options = {:foo => 'bar'}
31+
end
32+
33+
it 'overrides global options with adapter-specific' do
34+
MultiJson.dump_options = {:foo => 'foo'}
35+
MultiJson.adapter.dump_options = {:foo => 'bar'}
36+
end
37+
end
38+
1439
it 'writes decodable JSON' do
1540
[
1641
{'abc' => 'def'},
@@ -96,6 +121,29 @@ def to_json(*)
96121
end
97122

98123
describe '.load' do
124+
describe '#load_options' do
125+
before{ MultiJson.load_options = MultiJson.adapter.load_options = {} }
126+
127+
after do
128+
MultiJson.adapter.instance.should_receive(:load).with('1', :foo => 'bar', :fizz => 'buzz')
129+
MultiJson.load('1', :fizz => 'buzz')
130+
MultiJson.load_options = MultiJson.adapter.load_options = nil
131+
end
132+
133+
it 'respects global load options' do
134+
MultiJson.load_options = {:foo => 'bar'}
135+
end
136+
137+
it 'respects per-adapter load options' do
138+
MultiJson.adapter.load_options = {:foo => 'bar'}
139+
end
140+
141+
it 'overrides global options with adapter-specific' do
142+
MultiJson.load_options = {:foo => 'foo'}
143+
MultiJson.adapter.load_options = {:foo => 'bar'}
144+
end
145+
end
146+
99147
it 'properly loads valid JSON' do
100148
expect(MultiJson.load('{"abc":"def"}')).to eq({'abc' => 'def'})
101149
end

spec/has_options.rb

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
shared_examples_for 'has options' do |object|
2+
3+
if object.respond_to?(:call)
4+
subject{ object.call }
5+
else
6+
subject{ object }
7+
end
8+
9+
%w(dump_options load_options).each do |getter|
10+
11+
let(:getter){ getter }
12+
let(:default_getter){ "default_#{getter}" }
13+
let(:setter){ "#{getter}=" }
14+
let(:defaults){ subject.send(default_getter) }
15+
let(:ivar){ "@#{getter}" }
16+
17+
describe getter.tr('_', ' ') do
18+
before{ set nil }
19+
after{ set nil }
20+
21+
def get(*args)
22+
subject.send(getter, *args)
23+
end
24+
25+
def set(value)
26+
subject.send(setter, value)
27+
end
28+
29+
it 'returns default options if not set' do
30+
expect(get).to eq(defaults)
31+
end
32+
33+
it 'allows hashes' do
34+
set :foo => 'bar'
35+
expect(get).to eq(:foo => 'bar')
36+
end
37+
38+
it 'allows objects that implement #to_hash' do
39+
value = Class.new do
40+
def to_hash
41+
{:foo=>'bar'}
42+
end
43+
end.new
44+
45+
set value
46+
expect(get).to eq(:foo => 'bar')
47+
end
48+
49+
it 'evaluates lambda returning options (with args)' do
50+
set lambda{ |a1, a2| { a1 => a2 }}
51+
expect(get('1', '2')).to eq('1' => '2')
52+
end
53+
54+
it 'evaluates lambda returning options (with no args)' do
55+
set lambda{{:foo => 'bar'}}
56+
expect(get).to eq(:foo => 'bar')
57+
end
58+
59+
it 'returns empty hash in all other cases' do
60+
set true
61+
expect(get).to eq(defaults)
62+
63+
set false
64+
expect(get).to eq(defaults)
65+
66+
set 10
67+
expect(get).to eq(defaults)
68+
69+
set nil
70+
expect(get).to eq(defaults)
71+
end
72+
end
73+
end
74+
end

spec/json_common_shared_example.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
end
99

1010
describe '.dump' do
11+
before{ MultiJson.dump_options = MultiJson.adapter.dump_options = nil }
12+
1113
describe 'with :pretty option set to true' do
1214
it 'passes default pretty options' do
1315
object = 'foo'
@@ -26,6 +28,8 @@
2628
end
2729

2830
describe '.load' do
31+
before{ MultiJson.load_options = MultiJson.adapter.load_options = nil }
32+
2933
describe 'with :quirks_mode option' do
3034
it 'passes it on load' do
3135
::JSON.should_receive(:parse).with('["foo"]', {:quirks_mode => true, :create_additions => false}).and_return(['foo'])

spec/multi_json_spec.rb

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require 'helper'
22
require 'adapter_shared_example'
33
require 'json_common_shared_example'
4+
require 'has_options'
45
require 'stringio'
56

67
describe 'MultiJson' do
@@ -127,14 +128,21 @@
127128
end
128129
end
129130

130-
it 'has default_options setter' do
131-
MultiJson.use MockDecoder
132-
MockDecoder.should_receive(:dump).with('123', :foo => 'lol', :bar => 'bar', :fizz => 'buzz')
133-
MultiJson.default_options = { :foo => 'foo', :bar => 'bar' }
134-
MultiJson.dump('123', :fizz => 'buzz', :foo => 'lol')
135-
MultiJson.default_options = {}
131+
describe 'default options' do
132+
it 'is deprecated' do
133+
Kernel.should_receive(:warn).with(/deprecated/i)
134+
silence_warnings{ MultiJson.default_options = {:foo => 'bar'} }
135+
end
136+
137+
it 'sets both load and dump options' do
138+
MultiJson.should_receive(:dump_options=).with(:foo => 'bar')
139+
MultiJson.should_receive(:load_options=).with(:foo => 'bar')
140+
silence_warnings{ MultiJson.default_options = {:foo => 'bar'} }
141+
end
136142
end
137143

144+
it_behaves_like 'has options', MultiJson
145+
138146
%w(gson json_gem json_pure nsjsonserialization oj ok_json yajl).each do |adapter|
139147
next if adapter == 'gson' && !jruby?
140148
next if adapter == 'nsjsonserialization' && !macruby?

0 commit comments

Comments
 (0)