acfs 1.0.0.dev.1.b305 → 1.0.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.
Files changed (89) hide show
  1. checksums.yaml +5 -13
  2. data/CHANGELOG.md +64 -0
  3. data/README.md +2 -2
  4. data/acfs.gemspec +4 -4
  5. data/lib/acfs.rb +7 -5
  6. data/lib/acfs/adapter/base.rb +0 -2
  7. data/lib/acfs/adapter/typhoeus.rb +17 -13
  8. data/lib/acfs/collections/paginatable.rb +10 -10
  9. data/lib/acfs/configuration.rb +4 -5
  10. data/lib/acfs/errors.rb +10 -9
  11. data/lib/acfs/global.rb +16 -7
  12. data/lib/acfs/location.rb +11 -11
  13. data/lib/acfs/middleware/base.rb +1 -2
  14. data/lib/acfs/middleware/json.rb +27 -0
  15. data/lib/acfs/middleware/logger.rb +0 -2
  16. data/lib/acfs/middleware/msgpack.rb +30 -0
  17. data/lib/acfs/middleware/print.rb +0 -2
  18. data/lib/acfs/middleware/serializer.rb +39 -0
  19. data/lib/acfs/operation.rb +5 -5
  20. data/lib/acfs/request.rb +4 -4
  21. data/lib/acfs/request/callbacks.rb +2 -3
  22. data/lib/acfs/resource.rb +2 -2
  23. data/lib/acfs/resource/attributes.rb +10 -37
  24. data/lib/acfs/resource/attributes/base.rb +10 -19
  25. data/lib/acfs/resource/attributes/boolean.rb +10 -8
  26. data/lib/acfs/resource/attributes/date_time.rb +6 -9
  27. data/lib/acfs/resource/attributes/dict.rb +37 -0
  28. data/lib/acfs/resource/attributes/float.rb +11 -5
  29. data/lib/acfs/resource/attributes/integer.rb +7 -5
  30. data/lib/acfs/resource/attributes/list.rb +13 -6
  31. data/lib/acfs/resource/attributes/string.rb +3 -5
  32. data/lib/acfs/resource/attributes/uuid.rb +8 -17
  33. data/lib/acfs/resource/loadable.rb +0 -1
  34. data/lib/acfs/resource/locatable.rb +0 -4
  35. data/lib/acfs/resource/operational.rb +0 -2
  36. data/lib/acfs/resource/persistence.rb +17 -17
  37. data/lib/acfs/resource/query_methods.rb +3 -5
  38. data/lib/acfs/resource/service.rb +0 -2
  39. data/lib/acfs/resource/validation.rb +0 -2
  40. data/lib/acfs/response.rb +1 -2
  41. data/lib/acfs/response/formats.rb +1 -2
  42. data/lib/acfs/response/status.rb +3 -5
  43. data/lib/acfs/runner.rb +21 -11
  44. data/lib/acfs/service.rb +4 -6
  45. data/lib/acfs/service/middleware.rb +20 -30
  46. data/lib/acfs/service/middleware/stack.rb +63 -0
  47. data/lib/acfs/singleton_resource.rb +0 -2
  48. data/lib/acfs/stub.rb +30 -21
  49. data/lib/acfs/util.rb +1 -1
  50. data/lib/acfs/version.rb +4 -2
  51. data/spec/acfs/adapter/typhoeus_spec.rb +12 -4
  52. data/spec/acfs/collection_spec.rb +45 -33
  53. data/spec/acfs/configuration_spec.rb +9 -1
  54. data/spec/acfs/global_spec.rb +21 -3
  55. data/spec/acfs/middleware/json_spec.rb +63 -0
  56. data/spec/acfs/middleware/msgpack_spec.rb +60 -0
  57. data/spec/acfs/operation_spec.rb +10 -0
  58. data/spec/acfs/request/callbacks_spec.rb +8 -8
  59. data/spec/acfs/request_spec.rb +5 -5
  60. data/spec/acfs/resource/attributes/boolean_spec.rb +40 -9
  61. data/spec/acfs/resource/attributes/date_time_spec.rb +29 -35
  62. data/spec/acfs/resource/attributes/dict_spec.rb +75 -0
  63. data/spec/acfs/resource/attributes/float_spec.rb +48 -9
  64. data/spec/acfs/resource/attributes/integer_spec.rb +34 -0
  65. data/spec/acfs/resource/attributes/list_spec.rb +43 -19
  66. data/spec/acfs/resource/attributes/uuid_spec.rb +31 -54
  67. data/spec/acfs/resource/attributes_spec.rb +17 -31
  68. data/spec/acfs/resource/locatable_spec.rb +2 -2
  69. data/spec/acfs/resource/persistance_spec.rb +65 -34
  70. data/spec/acfs/resource/query_methods_spec.rb +87 -87
  71. data/spec/acfs/resource/validation_spec.rb +4 -5
  72. data/spec/acfs/response/formats_spec.rb +1 -1
  73. data/spec/acfs/response/status_spec.rb +1 -1
  74. data/spec/acfs/runner_spec.rb +28 -3
  75. data/spec/acfs/service/middleware_spec.rb +4 -20
  76. data/spec/acfs/service_spec.rb +3 -5
  77. data/spec/acfs/singleton_resource_spec.rb +1 -2
  78. data/spec/acfs/stub_spec.rb +137 -22
  79. data/spec/acfs_spec.rb +22 -19
  80. data/spec/spec_helper.rb +2 -1
  81. data/spec/support/service.rb +10 -6
  82. data/spec/support/shared/find_callbacks.rb +7 -7
  83. metadata +37 -30
  84. data/lib/acfs/middleware/json_decoder.rb +0 -16
  85. data/lib/acfs/middleware/json_encoder.rb +0 -20
  86. data/lib/acfs/middleware/msgpack_decoder.rb +0 -26
  87. data/lib/acfs/middleware/msgpack_encoder.rb +0 -19
  88. data/spec/acfs/middleware/json_decoder_spec.rb +0 -45
  89. data/spec/acfs/middleware/msgpack_decoder_spec.rb +0 -36
@@ -1,7 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Acfs::Configuration do
4
-
5
4
  let(:cfg) { Acfs::Configuration.new }
6
5
  before { @configuration = Acfs::Configuration.current.dup }
7
6
  after { Acfs::Configuration.set @configuration }
@@ -40,4 +39,13 @@ describe Acfs::Configuration do
40
39
  end
41
40
  end
42
41
  end
42
+
43
+ describe '#adapter' do
44
+ let(:object) { Object.new }
45
+
46
+ it 'should be a accessor' do
47
+ cfg.adapter = object
48
+ expect(cfg.adapter).to eq object
49
+ end
50
+ end
43
51
  end
@@ -14,11 +14,11 @@ describe ::Acfs::Global do
14
14
  let(:adapter) { ::NullAdapter.new }
15
15
  let(:runner) { double 'runner' }
16
16
  let(:collector) { NotificationCollector.new }
17
- let(:acfs) { Object.new.tap { |o| o.extend ::Acfs::Global } }
17
+ let(:acfs) { Object.new.tap {|o| o.extend ::Acfs::Global } }
18
18
 
19
19
  describe 'instrumentation' do
20
20
  before do
21
- #allow(runner).to receive(:start)
21
+ # allow(runner).to receive(:start)
22
22
  allow(acfs).to receive(:runner).and_return runner
23
23
  end
24
24
 
@@ -48,7 +48,7 @@ describe ::Acfs::Global do
48
48
  stub_request(:get, %r{http://users.example.org/users/\d+}).to_return(
49
49
  status: 200,
50
50
  body: '{}',
51
- headers: { 'Content-Type' => 'application/json' })
51
+ headers: {'Content-Type' => 'application/json'})
52
52
  end
53
53
 
54
54
  it 'should invoke when both resources' do
@@ -72,4 +72,22 @@ describe ::Acfs::Global do
72
72
  Acfs.run
73
73
  end
74
74
  end
75
+
76
+ describe '#runner' do
77
+ it 'returns per-thread runner' do
78
+ runner1 = Thread.new { acfs.runner } .value
79
+ runner2 = Thread.new { acfs.runner } .value
80
+
81
+ expect(runner1).to_not equal runner2
82
+ end
83
+
84
+ it 'uses configurated adapter' do
85
+ adapter = double :adapter
86
+ expect(Acfs::Configuration.current).to receive(:adapter).and_return(-> { adapter })
87
+
88
+ runner = Thread.new { acfs.runner }.value
89
+
90
+ expect(runner.adapter).to equal adapter
91
+ end
92
+ end
75
93
  end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ describe Acfs::Middleware::JSON do
4
+ let(:data) { [{id: 1, name: 'Anon'}, {id: 2, name: 'John', friends: [1]}] }
5
+ let(:body) { '' }
6
+ let(:headers) { {} }
7
+ let(:request) { Acfs::Request.new 'url', method: 'GET', data: data }
8
+ let(:response) { Acfs::Response.new request, status: 200, headers: headers, body: body }
9
+ let(:decoder) { Acfs::Middleware::JSON.new ->(req) { req } }
10
+
11
+ before do
12
+ decoder.call request
13
+ end
14
+
15
+ describe 'encode' do
16
+ context 'with not serialized request' do
17
+ it 'should set Content-Type' do
18
+ expect(request.headers['Content-Type']).to eq 'application/json'
19
+ end
20
+
21
+ it 'should append Accept header' do
22
+ expect(request.headers['Accept']).to eq 'application/json;q=1'
23
+ end
24
+
25
+ it 'should serialize data to JSON' do
26
+ expect(JSON.parse(request.body)).to eq data.map(&:stringify_keys)
27
+ end
28
+ end
29
+ end
30
+
31
+ describe 'decode' do
32
+ context 'with JSON response' do
33
+ let(:headers) { {'Content-Type' => 'application/json; charset=utf-8'} }
34
+ let(:body) { data.to_json }
35
+
36
+ it 'should decode body data' do
37
+ request.complete! response
38
+
39
+ expect(response.data).to be == data.map(&:stringify_keys)
40
+ end
41
+ end
42
+
43
+ context 'with invalid JSON response' do
44
+ let(:headers) { {'Content-Type' => 'application/json'} }
45
+ let(:body) { data.to_json[4..-4] }
46
+
47
+ it 'should raise an error' do
48
+ expect { request.complete! response }.to raise_error(MultiJson::LoadError)
49
+ end
50
+ end
51
+
52
+ context 'without JSON response' do
53
+ let(:headers) { {'Content-Type' => 'application/text'} }
54
+ let(:body) { data.to_json }
55
+
56
+ it 'should not decode non-JSON encoded responses' do
57
+ request.complete! response
58
+
59
+ expect(response.data).to be_nil
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Acfs::Middleware::MessagePack do
4
+ let(:data) { [{id: 1, name: 'Anon'}, {id: 2, name: 'John', friends: [1]}] }
5
+ let(:body) { '' }
6
+ let(:headers) { {} }
7
+ let(:request) { Acfs::Request.new 'url', data: data }
8
+ let(:response) { Acfs::Response.new request, status: 200, headers: headers, body: body }
9
+ let(:decoder) { Acfs::Middleware::MessagePack.new ->(req) { req } }
10
+
11
+ before do
12
+ decoder.call request
13
+ end
14
+
15
+ describe 'encode' do
16
+ context 'with not serialized request' do
17
+ it 'should set Content-Type' do
18
+ expect(request.headers['Content-Type']).to eq 'application/x-msgpack'
19
+ end
20
+
21
+ it 'should append Accept header' do
22
+ expect(request.headers['Accept']).to eq 'application/x-msgpack;q=1'
23
+ end
24
+
25
+ context 'with JSON chained' do
26
+ let(:decoder) { Acfs::Middleware::JSON.new super(), q: 0.5 }
27
+
28
+ it 'should append to Accept header' do
29
+ expect(request.headers['Accept']).to eq 'application/json;q=0.5,application/x-msgpack;q=1'
30
+ end
31
+ end
32
+
33
+ it 'should serialize data to MessagePack' do
34
+ expect(MessagePack.unpack(request.body)).to eq data.map(&:stringify_keys)
35
+ end
36
+ end
37
+ end
38
+
39
+ context 'with Message Pack response' do
40
+ let(:headers) { {'Content-Type' => 'application/x-msgpack'} }
41
+ let(:body) { MessagePack.pack data }
42
+
43
+ it 'should decode body data' do
44
+ request.complete! response
45
+
46
+ expect(response.data).to be == data.map(&:stringify_keys)
47
+ end
48
+ end
49
+
50
+ context 'without Message Pack response' do
51
+ let(:headers) { {'Content-Type' => 'application/text'} }
52
+ let(:body) { data.to_json }
53
+
54
+ it 'should not decode non-MessagePack encoded responses' do
55
+ request.complete! response
56
+
57
+ expect(response.data).to be_nil
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe ::Acfs::Operation do
4
+ let(:operation) { described_class.new MyUser, :read, params: {id: 0} }
5
+
6
+ context '#request' do
7
+ subject { operation.request }
8
+ its(:operation) { should eq operation }
9
+ end
10
+ end
@@ -1,20 +1,20 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Acfs::Request::Callbacks do
4
- let(:callback) { lambda { |res| } }
4
+ let(:callback) { ->(_res) {} }
5
5
  let(:request) { Acfs::Request.new('fubar') }
6
6
 
7
7
  describe '#on_complete' do
8
8
  it 'should store a given callback' do
9
- request.on_complete &callback
9
+ request.on_complete(&callback)
10
10
 
11
11
  expect(request.callbacks).to have(1).item
12
12
  expect(request.callbacks[0]).to be == callback
13
13
  end
14
14
 
15
15
  it 'should store multiple callback' do
16
- request.on_complete { |res| "abc" }
17
- request.on_complete &callback
16
+ request.on_complete {|_res| 'abc' }
17
+ request.on_complete(&callback)
18
18
 
19
19
  expect(request.callbacks).to have(2).item
20
20
  expect(request.callbacks[0]).to be == callback
@@ -27,16 +27,16 @@ describe Acfs::Request::Callbacks do
27
27
  it 'should trigger registered callbacks with given response' do
28
28
  expect(callback).to receive(:call).with(response, kind_of(Proc))
29
29
 
30
- request.on_complete &callback
30
+ request.on_complete(&callback)
31
31
  request.complete! response
32
32
  end
33
33
 
34
34
  it 'should trigger multiple callback in reverted insertion order' do
35
35
  check = []
36
36
 
37
- request.on_complete { |res, nxt| check << 1; nxt.call res }
38
- request.on_complete { |res, nxt| check << 2; nxt.call res }
39
- request.on_complete { |res, nxt| check << 3; nxt.call res }
37
+ request.on_complete {|res, nxt| check << 1; nxt.call res }
38
+ request.on_complete {|res, nxt| check << 2; nxt.call res }
39
+ request.on_complete {|res, nxt| check << 3; nxt.call res }
40
40
 
41
41
  request.complete! response
42
42
 
@@ -15,7 +15,7 @@ describe Acfs::Request do
15
15
  end
16
16
 
17
17
  context 'with parameters' do
18
- let(:params) { { id: 10 }}
18
+ let(:params) { {id: 10} }
19
19
 
20
20
  it 'should return URL without query' do
21
21
  expect(request.url).to be == "#{url}"
@@ -24,7 +24,7 @@ describe Acfs::Request do
24
24
  end
25
25
 
26
26
  describe '#headers' do
27
- let(:headers) { { 'Accept' => 'application/json' } }
27
+ let(:headers) { {'Accept' => 'application/json'} }
28
28
 
29
29
  it 'should return request headers' do
30
30
  expect(request.headers).to be == headers
@@ -46,7 +46,7 @@ describe Acfs::Request do
46
46
  end
47
47
 
48
48
  describe '#params' do
49
- let(:params) { { id: 10 }}
49
+ let(:params) { {id: 10} }
50
50
 
51
51
  it 'should return request headers' do
52
52
  expect(request.params).to be == params
@@ -54,7 +54,7 @@ describe Acfs::Request do
54
54
  end
55
55
 
56
56
  describe '#data' do
57
- let(:data) { { id: 10, name: 'Anon' } }
57
+ let(:data) { {id: 10, name: 'Anon'} }
58
58
 
59
59
  it 'should return request data' do
60
60
  expect(request.data).to be == data
@@ -63,7 +63,7 @@ describe Acfs::Request do
63
63
 
64
64
  describe '#data' do
65
65
  context 'with data' do
66
- let(:data) { { id: 10, name: 'Anon' } }
66
+ let(:data) { {id: 10, name: 'Anon'} }
67
67
 
68
68
  it { expect(request).to be_data }
69
69
  end
@@ -3,23 +3,54 @@ require 'spec_helper'
3
3
  describe Acfs::Resource::Attributes::Boolean do
4
4
  subject { Acfs::Resource::Attributes::Boolean.new }
5
5
 
6
- describe 'cast' do
7
- it 'should preserve boolean values' do
6
+ describe '#cast' do
7
+ it 'casts nil' do
8
+ expect(subject.cast(nil)).to eq nil
9
+ end
10
+
11
+ it 'casts empty string to false' do
12
+ expect(subject.cast('')).to eq nil
13
+ end
14
+
15
+ it 'casts blank string to false' do
16
+ expect(subject.cast(" \t")).to eq nil
17
+ end
18
+
19
+ it 'preserves boolean values' do
8
20
  expect(subject.cast(false)).to eq false
9
21
  expect(subject.cast(true)).to eq true
10
22
  end
11
23
 
12
- it 'should cast TRUE_VALUES to true' do
24
+ it 'casts falsy values to false' do
25
+ expect(subject.cast(false)).to eq false
26
+ expect(subject.cast(0)).to eq false
27
+ expect(subject.cast('0')).to eq false
28
+ expect(subject.cast('no')).to eq false
29
+ expect(subject.cast('NO')).to eq false
30
+ expect(subject.cast('off')).to eq false
31
+ expect(subject.cast('OFF')).to eq false
32
+ expect(subject.cast('false')).to eq false
33
+ expect(subject.cast('FALSE')).to eq false
34
+ expect(subject.cast('f')).to eq false
35
+ expect(subject.cast('F')).to eq false
36
+ end
37
+
38
+ it 'casts any other value to true' do
39
+ expect(subject.cast(true)).to eq true
40
+ expect(subject.cast(1)).to eq true
41
+ expect(subject.cast('1')).to eq true
13
42
  expect(subject.cast('yes')).to eq true
43
+ expect(subject.cast('YES')).to eq true
14
44
  expect(subject.cast('on')).to eq true
45
+ expect(subject.cast('ON')).to eq true
15
46
  expect(subject.cast('true')).to eq true
16
- expect(subject.cast('1')).to eq true
17
- end
47
+ expect(subject.cast('TRUE')).to eq true
48
+ expect(subject.cast('t')).to eq true
49
+ expect(subject.cast('T')).to eq true
18
50
 
19
- it 'should cast any other value to false' do
20
- expect(subject.cast('')).to eq false
21
- expect(subject.cast('wrong')).to eq false
22
- expect(subject.cast('random')).to eq false
51
+ expect(subject.cast(2)).to eq true
52
+ expect(subject.cast('wrong')).to eq true
53
+ expect(subject.cast('random')).to eq true
23
54
  end
24
55
  end
25
56
  end
@@ -1,55 +1,49 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Acfs::Resource::Attributes::DateTime do
4
- let(:model) { Class.new Acfs::Resource }
5
- let(:params) { {} }
6
- subject { Acfs::Resource::Attributes::DateTime.new params }
4
+ let(:type) { Acfs::Resource::Attributes::DateTime.new }
7
5
 
8
- describe 'cast' do
9
- it 'should return same object, if obj is already of DateTime class' do
10
- date_time = DateTime.now
11
- retval = subject.cast(date_time)
12
- expect(retval).to be == date_time
6
+ describe '#cast' do
7
+ subject { -> { type.cast value } }
8
+
9
+ context 'with nil' do
10
+ let(:value) { nil }
11
+ it { expect(subject.call).to eq nil }
13
12
  end
14
13
 
15
- it 'should return parsed object, if obj is of Time class' do
16
- time = Time.now
17
- retval = subject.cast(time)
18
- expect(retval).to be == DateTime.iso8601(time.iso8601)
14
+ context 'with empty string' do
15
+ let(:value) { '' }
16
+ it { expect(subject.call).to eq nil }
19
17
  end
20
18
 
21
- it 'should return parsed object, if obj is of Date class' do
22
- date = Date.today
23
- retval = subject.cast(date)
24
- expect(retval).to be == DateTime.iso8601(date.iso8601)
19
+ context 'with blank string' do
20
+ let(:value) { " \t" }
21
+ it { expect(subject.call).to eq nil }
25
22
  end
26
23
 
27
- it 'should return parsed object, if obj is of String class in ISO-8601 format' do
28
- date_time_string = DateTime.now.iso8601
29
- retval = subject.cast(date_time_string)
30
- expect(retval).to be == DateTime.iso8601(date_time_string)
24
+ context 'with DateTime' do
25
+ let(:value) { DateTime.now }
26
+ it { expect(subject.call).to eq value }
31
27
  end
32
28
 
33
- it 'should raise an error if obj is of String class not in valid ISO-8601 format' do
34
- malformed_string = 'qwe123'
35
- expect {
36
- subject.cast(malformed_string)
37
- }.to raise_error ArgumentError
29
+ context 'with Time' do
30
+ let(:value) { Time.now }
31
+ it { expect(subject.call).to eq value.to_datetime }
38
32
  end
39
33
 
40
- it 'should raise an error if obj is of wrong class (Fixnum)' do
41
- fixnum = 12
42
- expect {
43
- subject.cast(fixnum)
44
- }.to raise_error TypeError
34
+ context 'with Date' do
35
+ let(:value) { Date.today }
36
+ it { expect(subject.call).to eq value.to_datetime }
45
37
  end
46
38
 
47
- context 'with allow_nil option' do
48
- let(:params) { {allow_nil: true} }
39
+ context 'with ISO8601' do
40
+ let(:value) { DateTime.now.iso8601 }
41
+ it { expect(subject.call.iso8601).to eq value }
42
+ end
49
43
 
50
- it 'should accept empty string as nil' do
51
- expect(subject.cast('')).to eq nil
52
- end
44
+ context 'with invalid string' do
45
+ let(:value) { 'qwe123' }
46
+ it { is_expected.to raise_error ArgumentError }
53
47
  end
54
48
  end
55
49
  end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe Acfs::Resource::Attributes::Dict do
4
+ let(:type) { Acfs::Resource::Attributes::Dict.new }
5
+
6
+ describe '#cast' do
7
+ subject { -> { type.cast value } }
8
+
9
+ context 'with nil' do
10
+ let(:value) { nil }
11
+ it { expect(subject.call).to eq nil }
12
+ end
13
+
14
+ context 'with blank string (I)' do
15
+ let(:value) { '' }
16
+ it { expect(subject.call).to eq Hash.new }
17
+ end
18
+
19
+ context 'with blank string (II)' do
20
+ let(:value) { " \t" }
21
+ it { expect(subject.call).to eq Hash.new }
22
+ end
23
+
24
+ context 'with hash' do
25
+ let(:value) { {3 => true, abc: 4} }
26
+ it { expect(subject.call).to eq value }
27
+ end
28
+
29
+ context 'with non hashable object' do
30
+ let(:value) { Object.new }
31
+ it { is_expected.to raise_error TypeError }
32
+ end
33
+
34
+ context 'with hashable object (I)' do
35
+ let(:value) do
36
+ Class.new do
37
+ def to_hash
38
+ {id: object_id}
39
+ end
40
+ end.new
41
+ end
42
+
43
+ it { expect(subject.call).to eq id: value.object_id }
44
+ end
45
+
46
+ context 'with hashable object (II)' do
47
+ let(:value) do
48
+ Class.new do
49
+ def to_h
50
+ {id: object_id}
51
+ end
52
+ end.new
53
+ end
54
+
55
+ it { expect(subject.call).to eq id: value.object_id }
56
+ end
57
+
58
+ context 'with serializable object' do
59
+ let(:value) do
60
+ Class.new do
61
+ def serializable_hash
62
+ {id: object_id}
63
+ end
64
+ end.new
65
+ end
66
+
67
+ it { expect(subject.call).to eq id: value.object_id }
68
+ end
69
+
70
+ context 'with hash subclass object' do
71
+ let(:value) { HashWithIndifferentAccess.new test: :foo }
72
+ it { expect(subject.call).to be value }
73
+ end
74
+ end
75
+ end