restify 1.15.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -2
  3. data/README.md +23 -31
  4. data/lib/restify/adapter/base.rb +4 -0
  5. data/lib/restify/adapter/telemetry.rb +54 -0
  6. data/lib/restify/adapter/typhoeus.rb +21 -3
  7. data/lib/restify/context.rb +3 -3
  8. data/lib/restify/error.rb +2 -2
  9. data/lib/restify/link.rb +4 -4
  10. data/lib/restify/processors/base/parsing.rb +2 -21
  11. data/lib/restify/processors/base.rb +1 -1
  12. data/lib/restify/promise.rb +2 -2
  13. data/lib/restify/registry.rb +1 -1
  14. data/lib/restify/relation.rb +45 -17
  15. data/lib/restify/request.rb +6 -6
  16. data/lib/restify/timeout.rb +2 -2
  17. data/lib/restify/version.rb +3 -3
  18. data/lib/restify.rb +0 -1
  19. data/spec/restify/cache_spec.rb +16 -12
  20. data/spec/restify/context_spec.rb +8 -3
  21. data/spec/restify/error_spec.rb +13 -16
  22. data/spec/restify/features/head_requests_spec.rb +5 -4
  23. data/spec/restify/features/request_bodies_spec.rb +8 -8
  24. data/spec/restify/features/request_errors_spec.rb +2 -2
  25. data/spec/restify/features/request_headers_spec.rb +3 -6
  26. data/spec/restify/features/response_errors_spec.rb +1 -1
  27. data/spec/restify/global_spec.rb +10 -10
  28. data/spec/restify/processors/base_spec.rb +6 -7
  29. data/spec/restify/processors/json_spec.rb +21 -62
  30. data/spec/restify/processors/msgpack_spec.rb +33 -70
  31. data/spec/restify/promise_spec.rb +31 -31
  32. data/spec/restify/registry_spec.rb +5 -7
  33. data/spec/restify/relation_spec.rb +185 -7
  34. data/spec/restify/resource_spec.rb +47 -53
  35. data/spec/restify/timeout_spec.rb +3 -3
  36. data/spec/restify_spec.rb +12 -73
  37. data/spec/spec_helper.rb +11 -15
  38. metadata +33 -64
  39. data/lib/restify/adapter/em.rb +0 -134
  40. data/lib/restify/adapter/pooled_em.rb +0 -269
@@ -4,95 +4,94 @@ require 'spec_helper'
4
4
 
5
5
  describe Restify::Processors::Msgpack do
6
6
  let(:context) { Restify::Context.new('http://test.host/') }
7
- let(:response) { double 'response' }
7
+ let(:response) { instance_double(Restify::Response) }
8
8
 
9
9
  before do
10
- allow(response).to receive(:links).and_return []
11
- allow(response).to receive(:follow_location).and_return nil
10
+ allow(response).to receive_messages(links: [], follow_location: nil)
12
11
  end
13
12
 
14
13
  describe 'class' do
15
14
  describe '#accept?' do
16
- subject { described_class.accept? response }
15
+ subject(:accept) { described_class.accept?(response) }
17
16
 
18
17
  it 'accepts msgpack mime type (I)' do
19
- expect(response).to receive(:content_type).and_return('application/msgpack')
20
- expect(subject).to be_truthy
18
+ allow(response).to receive(:content_type).and_return('application/msgpack')
19
+ expect(accept).to be_truthy
21
20
  end
22
21
 
23
22
  it 'accepts msgpack mime type (II)' do
24
- expect(response).to receive(:content_type).and_return('application/msgpack; abc')
25
- expect(subject).to be_truthy
23
+ allow(response).to receive(:content_type).and_return('application/msgpack; abc')
24
+ expect(accept).to be_truthy
26
25
  end
27
26
 
28
27
  it 'accepts x-msgpack mime type (I)' do
29
- expect(response).to receive(:content_type).and_return('application/x-msgpack')
30
- expect(subject).to be_truthy
28
+ allow(response).to receive(:content_type).and_return('application/x-msgpack')
29
+ expect(accept).to be_truthy
31
30
  end
32
31
 
33
32
  it 'accepts x-msgpack mime type (II)' do
34
- expect(response).to receive(:content_type).and_return('application/x-msgpack; abc')
35
- expect(subject).to be_truthy
33
+ allow(response).to receive(:content_type).and_return('application/x-msgpack; abc')
34
+ expect(accept).to be_truthy
36
35
  end
37
36
  end
38
37
  end
39
38
 
40
39
  describe '#resource' do
41
- subject { described_class.new(context, response).resource }
40
+ subject(:resource) { described_class.new(context, response).resource }
42
41
 
43
42
  before { allow(response).to receive(:body).and_return(body) }
44
43
 
45
44
  describe 'parsing' do
46
45
  context 'single object' do
47
46
  let(:body) do
48
- ::MessagePack.dump('msg' => 'value')
47
+ MessagePack.dump('msg' => 'value')
49
48
  end
50
49
 
51
50
  it { is_expected.to be_a Restify::Resource }
52
- it { expect(subject.response).to be response }
51
+ it { expect(resource.response).to be response }
53
52
  it { is_expected.to eq 'msg' => 'value' }
54
53
  end
55
54
 
56
55
  context 'object with relation fields' do
57
56
  let(:body) do
58
- ::MessagePack.dump(
57
+ MessagePack.dump(
59
58
  'msg' => 'value',
60
59
  'search_url' => 'https://google.com{?q}',
61
60
  )
62
61
  end
63
62
 
64
63
  it do
65
- expect(subject).to eq \
64
+ expect(resource).to eq \
66
65
  'msg' => 'value', 'search_url' => 'https://google.com{?q}'
67
66
  end
68
67
 
69
68
  it { is_expected.to have_relation :search }
70
- it { expect(subject.relation(:search)).to eq 'https://google.com{?q}' }
69
+ it { expect(resource.relation(:search)).to eq 'https://google.com{?q}' }
71
70
  end
72
71
 
73
72
  context 'object with implicit self relation' do
74
73
  let(:body) do
75
- ::MessagePack.dump(
74
+ MessagePack.dump(
76
75
  'url' => '/self',
77
76
  )
78
77
  end
79
78
 
80
- it { expect(subject.relation(:self)).to eq '/self' }
79
+ it { expect(resource.relation(:self)).to eq '/self' }
81
80
  end
82
81
 
83
82
  context 'single array' do
84
83
  let(:body) do
85
- ::MessagePack.dump([1, 2, nil, 'STR'])
84
+ MessagePack.dump([1, 2, nil, 'STR'])
86
85
  end
87
86
 
88
87
  it { is_expected.to be_a Restify::Resource }
89
- it { expect(subject.response).to be response }
88
+ it { expect(resource.response).to be response }
90
89
  it { is_expected.to eq [1, 2, nil, 'STR'] }
91
90
  end
92
91
 
93
92
  context 'array with objects' do
94
93
  let(:body) do
95
- ::MessagePack.dump([{'a' => 0}, {'b' => 1}])
94
+ MessagePack.dump([{'a' => 0}, {'b' => 1}])
96
95
  end
97
96
 
98
97
  it { is_expected.to eq [{'a' => 0}, {'b' => 1}] }
@@ -100,87 +99,51 @@ describe Restify::Processors::Msgpack do
100
99
 
101
100
  context 'array with resources' do
102
101
  let(:body) do
103
- ::MessagePack.dump([
102
+ MessagePack.dump([
104
103
  {'name' => 'John', 'self_url' => '/users/john'},
105
104
  {'name' => 'Jane', 'self_url' => '/users/jane'},
106
105
  ])
107
106
  end
108
107
 
109
108
  it 'parses objects as resources' do
110
- expect(subject).to all(be_a(Restify::Resource))
109
+ expect(resource).to all(be_a(Restify::Resource))
111
110
  end
112
111
 
113
112
  it 'parses relations of resources' do
114
- expect(subject.map {|r| r.relation :self }).to eq \
113
+ expect(resource.map {|r| r.relation :self }).to eq \
115
114
  ['/users/john', '/users/jane']
116
115
  end
117
116
  end
118
117
 
119
118
  context 'nested objects' do
120
119
  let(:body) do
121
- ::MessagePack.dump(
120
+ MessagePack.dump(
122
121
  'john' => {'name' => 'John'},
123
122
  'jane' => {'name' => 'Jane'},
124
123
  )
125
124
  end
126
125
 
127
126
  it { is_expected.to be_a Restify::Resource }
128
- it { expect(subject.response).to be response }
127
+ it { expect(resource.response).to be response }
129
128
 
130
129
  it 'parses objects as resources' do
131
- expect(subject['john']).to be_a Restify::Resource
132
- expect(subject['jane']).to be_a Restify::Resource
130
+ expect(resource['john']).to be_a Restify::Resource
131
+ expect(resource['jane']).to be_a Restify::Resource
133
132
 
134
- expect(subject['john']['name']).to eq 'John'
135
- expect(subject['jane']['name']).to eq 'Jane'
133
+ expect(resource['john']['name']).to eq 'John'
134
+ expect(resource['jane']['name']).to eq 'Jane'
136
135
  end
137
136
  end
138
137
 
139
138
  context 'single value' do
140
139
  let(:body) do
141
- ::MessagePack.dump('BLUB')
140
+ MessagePack.dump('BLUB')
142
141
  end
143
142
 
144
143
  it { is_expected.to be_a Restify::Resource }
145
- it { expect(subject.response).to be response }
144
+ it { expect(resource.response).to be response }
146
145
  it { is_expected.to eq 'BLUB' }
147
146
  end
148
-
149
- context 'with indifferent access' do
150
- let(:body) do
151
- ::MessagePack.dump('name' => 'John', 'age' => 24)
152
- end
153
-
154
- it '#key?' do
155
- expect(subject).to have_key 'name'
156
- expect(subject).to have_key 'age'
157
-
158
- expect(subject).to have_key :name
159
- expect(subject).to have_key :age
160
- end
161
-
162
- it '#[]' do
163
- expect(subject['name']).to eq 'John'
164
- expect(subject['age']).to eq 24
165
-
166
- expect(subject[:name]).to eq 'John'
167
- expect(subject[:age]).to eq 24
168
- end
169
- end
170
-
171
- context 'with method getter access' do
172
- let(:body) do
173
- ::MessagePack.dump('name' => 'John', 'age' => 24)
174
- end
175
-
176
- it '#<method getter>' do
177
- expect(subject).to respond_to :name
178
- expect(subject).to respond_to :age
179
-
180
- expect(subject.name).to eq 'John'
181
- expect(subject.age).to eq 24
182
- end
183
- end
184
147
  end
185
148
  end
186
149
  end
@@ -7,42 +7,42 @@ describe Restify::Promise do
7
7
 
8
8
  describe 'factory methods' do
9
9
  describe '#fulfilled' do
10
- subject { described_class.fulfilled(fulfill_value) }
10
+ subject(:promise) { described_class.fulfilled(fulfill_value) }
11
11
 
12
12
  let(:fulfill_value) { 42 }
13
13
 
14
14
  it 'returns a fulfilled promise' do
15
- expect(subject.fulfilled?).to be true
16
- expect(subject.rejected?).to be false
15
+ expect(promise.fulfilled?).to be true
16
+ expect(promise.rejected?).to be false
17
17
  end
18
18
 
19
19
  it 'wraps the given value' do
20
- expect(subject.value!).to eq 42
20
+ expect(promise.value!).to eq 42
21
21
  end
22
22
  end
23
23
 
24
24
  describe '#rejected' do
25
- subject { described_class.rejected(rejection_reason) }
25
+ subject(:promise) { described_class.rejected(rejection_reason) }
26
26
 
27
27
  let(:rejection_reason) { ArgumentError }
28
28
 
29
29
  it 'returns a rejected promise' do
30
- expect(subject.fulfilled?).to be false
31
- expect(subject.rejected?).to be true
30
+ expect(promise.fulfilled?).to be false
31
+ expect(promise.rejected?).to be true
32
32
  end
33
33
 
34
34
  it 'bubbles up the caught exception on #value!' do
35
- expect { subject.value! }.to raise_error(ArgumentError)
35
+ expect { promise.value! }.to raise_error(ArgumentError)
36
36
  end
37
37
 
38
38
  it 'swallows the exception on #value' do
39
- expect(subject.value).to be nil
39
+ expect(promise.value).to be_nil
40
40
  end
41
41
  end
42
42
 
43
43
  describe '#create' do
44
44
  context 'when fulfilling the promise in the writer block' do
45
- subject do
45
+ subject(:promise) do
46
46
  described_class.create do |writer|
47
47
  # Calculate a value and fulfill the promise with it
48
48
  writer.fulfill 42
@@ -50,17 +50,17 @@ describe Restify::Promise do
50
50
  end
51
51
 
52
52
  it 'returns a fulfilled promise' do
53
- expect(subject.fulfilled?).to be true
54
- expect(subject.rejected?).to be false
53
+ expect(promise.fulfilled?).to be true
54
+ expect(promise.rejected?).to be false
55
55
  end
56
56
 
57
57
  it 'wraps the given value' do
58
- expect(subject.value!).to eq 42
58
+ expect(promise.value!).to eq 42
59
59
  end
60
60
  end
61
61
 
62
62
  context 'when rejecting the promise in the writer block' do
63
- subject do
63
+ subject(:promise) do
64
64
  described_class.create do |writer|
65
65
  # Calculate a value and fulfill the promise with it
66
66
  writer.reject ArgumentError
@@ -68,21 +68,21 @@ describe Restify::Promise do
68
68
  end
69
69
 
70
70
  it 'returns a rejected promise' do
71
- expect(subject.fulfilled?).to be false
72
- expect(subject.rejected?).to be true
71
+ expect(promise.fulfilled?).to be false
72
+ expect(promise.rejected?).to be true
73
73
  end
74
74
 
75
75
  it 'bubbles up the caught exception on #value!' do
76
- expect { subject.value! }.to raise_error(ArgumentError)
76
+ expect { promise.value! }.to raise_error(ArgumentError)
77
77
  end
78
78
 
79
79
  it 'swallows the exception on #value' do
80
- expect(subject.value).to be nil
80
+ expect(promise.value).to be_nil
81
81
  end
82
82
  end
83
83
 
84
84
  context 'when fulfilling the promise asynchronously' do
85
- subject do
85
+ subject(:promise) do
86
86
  described_class.create do |writer|
87
87
  Thread.new do
88
88
  sleep 0.1
@@ -92,20 +92,20 @@ describe Restify::Promise do
92
92
  end
93
93
 
94
94
  it 'returns a pending promise' do
95
- expect(subject.fulfilled?).to be false
96
- expect(subject.rejected?).to be false
97
- expect(subject.pending?).to be true
95
+ expect(promise.fulfilled?).to be false
96
+ expect(promise.rejected?).to be false
97
+ expect(promise.pending?).to be true
98
98
  end
99
99
 
100
100
  it 'waits for the fulfillment value' do
101
- expect(subject.value!).to eq 42
101
+ expect(promise.value!).to eq 42
102
102
  end
103
103
  end
104
104
  end
105
105
  end
106
106
 
107
107
  describe 'result' do
108
- subject { described_class.new(*dependencies, &task).value! }
108
+ subject(:result) { described_class.new(*dependencies, &task).value! }
109
109
 
110
110
  let(:dependencies) { [] }
111
111
  let(:task) { nil }
@@ -119,7 +119,7 @@ describe Restify::Promise do
119
119
  end
120
120
 
121
121
  it 'is calculated using the task block' do
122
- expect(subject).to eq 42
122
+ expect(result).to eq 42
123
123
  end
124
124
  end
125
125
 
@@ -133,7 +133,7 @@ describe Restify::Promise do
133
133
  end
134
134
 
135
135
  it 'is an array of the dependencies\' results' do
136
- expect(subject).to eq [1, 2, 3]
136
+ expect(result).to eq [1, 2, 3]
137
137
  end
138
138
  end
139
139
 
@@ -147,7 +147,7 @@ describe Restify::Promise do
147
147
  end
148
148
 
149
149
  it 'is an array of the dependencies\' results' do
150
- expect(subject).to eq [1, 2, 3]
150
+ expect(result).to eq [1, 2, 3]
151
151
  end
152
152
  end
153
153
 
@@ -163,7 +163,7 @@ describe Restify::Promise do
163
163
  end
164
164
 
165
165
  it 'can use the dependencies to calculate the value' do
166
- expect(subject).to eq 17
166
+ expect(result).to eq 17
167
167
  end
168
168
  end
169
169
 
@@ -178,19 +178,19 @@ describe Restify::Promise do
178
178
 
179
179
  describe '#wait' do
180
180
  it 'can time out' do
181
- expect { promise.wait(0.1) }.to raise_error ::Timeout::Error
181
+ expect { promise.wait(0.1) }.to raise_error Timeout::Error
182
182
  end
183
183
  end
184
184
 
185
185
  describe '#value' do
186
186
  it 'can time out' do
187
- expect { promise.value(0.1) }.to raise_error ::Timeout::Error
187
+ expect { promise.value(0.1) }.to raise_error Timeout::Error
188
188
  end
189
189
  end
190
190
 
191
191
  describe '#value!' do
192
192
  it 'can time out' do
193
- expect { promise.value!(0.1) }.to raise_error ::Timeout::Error
193
+ expect { promise.value!(0.1) }.to raise_error Timeout::Error
194
194
  end
195
195
  end
196
196
  end
@@ -3,15 +3,13 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Restify::Registry do
6
- let(:registry) { described_class.instance }
6
+ subject(:registry) { described_class.instance }
7
7
 
8
8
  describe 'class' do
9
9
  describe '#instance' do
10
- subject { described_class.instance }
11
-
12
10
  it 'returns singleton instance' do
13
- expect(subject).to be_a described_class
14
- expect(subject).to be described_class.instance
11
+ expect(registry).to be_a described_class
12
+ expect(registry).to be described_class.instance
15
13
  end
16
14
  end
17
15
 
@@ -36,14 +34,14 @@ describe Restify::Registry do
36
34
  end
37
35
 
38
36
  describe '#store / #fetch' do
39
- subject { registry.store name, uri, **opts }
37
+ subject(:store) { registry.store(name, uri, **opts) }
40
38
 
41
39
  let(:name) { 'remote' }
42
40
  let(:uri) { 'http://remote/entry/point' }
43
41
  let(:opts) { {accept: 'application/vnd.remote+json'} }
44
42
 
45
43
  it 'stores registry item' do
46
- subject
44
+ store
47
45
 
48
46
  item = registry.fetch name
49
47
 
@@ -1,21 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'spec_helper'
4
+ require 'active_support'
4
5
 
5
6
  describe Restify::Relation do
6
7
  subject(:relation) { described_class.new context, pattern }
7
8
 
8
9
  let(:context) { Restify::Context.new('http://test.host/') }
9
- let(:pattern) { '/resource/{id}' }
10
+ let(:pattern) { '/resource/{id}{?q*}' }
10
11
 
11
12
  describe '#==' do
12
13
  it 'equals pattern' do
13
- expect(subject).to eq pattern
14
+ expect(relation).to eq pattern
14
15
  end
15
16
  end
16
17
 
17
18
  describe '#expand' do
18
- subject(:expaned) { relation.expand params }
19
+ subject(:expanded) { relation.expand params }
19
20
 
20
21
  let(:params) { {id: 1337} }
21
22
  let(:cls_to_param) do
@@ -28,29 +29,206 @@ describe Restify::Relation do
28
29
 
29
30
  it { is_expected.to be_a Addressable::URI }
30
31
 
32
+ context 'with nil' do
33
+ let(:params) { {id: nil} }
34
+
35
+ it { expect(expanded.to_s).to eq 'http://test.host/resource/' }
36
+ end
37
+
38
+ context 'with false' do
39
+ let(:params) { {id: false} }
40
+
41
+ it { expect(expanded.to_s).to eq 'http://test.host/resource/' }
42
+ end
43
+
44
+ context 'with true' do
45
+ let(:params) { {id: true} }
46
+
47
+ it { expect(expanded.to_s).to eq 'http://test.host/resource/true' }
48
+ end
49
+
31
50
  context 'with #to_param object' do
32
51
  let(:params) { {id: cls_to_param.new} }
33
52
 
34
- it { expect(expaned.to_s).to eq 'http://test.host/resource/42' }
53
+ it { expect(expanded.to_s).to eq 'http://test.host/resource/42' }
54
+ end
55
+
56
+ context 'with array parameter' do
57
+ let(:params) { {id: [1, 2]} }
58
+
59
+ it { expect(expanded.to_s).to eq 'http://test.host/resource/1,2' }
35
60
  end
36
61
 
37
62
  context 'with unknown additional query parameter' do
38
63
  let(:pattern) { '/resource{?a,b}' }
39
64
  let(:params) { {a: 1, b: 2, c: 3} }
40
65
 
41
- it { expect(expaned.to_s).to eq 'http://test.host/resource?a=1&b=2&c=3' }
66
+ it { expect(expanded.to_s).to eq 'http://test.host/resource?a=1&b=2&c=3' }
42
67
  end
43
68
 
44
69
  context 'with additional parameters' do
45
70
  let(:params) { {id: '5', abc: 'cde'} }
46
71
 
47
- it { expect(expaned.to_s).to eq 'http://test.host/resource/5?abc=cde' }
72
+ it { expect(expanded.to_s).to eq 'http://test.host/resource/5?abc=cde' }
73
+ end
74
+
75
+ context 'with additional nil parameters' do
76
+ let(:params) { {id: '5', abc: nil} }
77
+
78
+ it { expect(expanded.to_s).to eq 'http://test.host/resource/5?abc' }
79
+ end
80
+
81
+ context 'with additional false parameters' do
82
+ let(:params) { {id: '5', abc: false} }
83
+
84
+ it { expect(expanded.to_s).to eq 'http://test.host/resource/5?abc=false' }
85
+ end
86
+
87
+ context 'with additional true parameters' do
88
+ let(:params) { {id: '5', abc: true} }
89
+
90
+ it { expect(expanded.to_s).to eq 'http://test.host/resource/5?abc=true' }
48
91
  end
49
92
 
50
93
  context 'with additional #to_param parameter' do
51
94
  let(:params) { {id: '5', abc: cls_to_param.new} }
52
95
 
53
- it { expect(expaned.to_s).to eq 'http://test.host/resource/5?abc=42' }
96
+ it { expect(expanded.to_s).to eq 'http://test.host/resource/5?abc=42' }
97
+ end
98
+
99
+ context 'with additional array parameter' do
100
+ let(:params) { {id: 5, abc: [1, 2]} }
101
+
102
+ it { expect(expanded.to_s).to eq 'http://test.host/resource/5?abc=1&abc=2' }
54
103
  end
104
+
105
+ context 'with additional array parameter with objects' do
106
+ let(:params) { {id: 5, abc: [1, 2, cls_to_param.new]} }
107
+
108
+ it { expect(expanded.to_s).to eq 'http://test.host/resource/5?abc=1&abc=2&abc=42' }
109
+ end
110
+
111
+ context 'with additional nested array parameter' do
112
+ let(:params) { {id: 5, abc: [1, 2, [1]]} }
113
+
114
+ it { expect { expanded }.to raise_exception TypeError, /Can't convert Array into String./ }
115
+ end
116
+
117
+ context 'with additional hash parameter' do
118
+ let(:params) { {id: 5, abc: {a: 1, b: 2}} }
119
+
120
+ it { expect { expanded }.to raise_exception TypeError, /Can't convert Hash into String./ }
121
+ end
122
+ end
123
+
124
+ shared_examples 'non-data-request' do |method|
125
+ it "issues a #{method.upcase} request" do
126
+ expect(context).to receive(:request) do |meth, uri, opts|
127
+ expect(meth).to eq method
128
+ expect(uri.to_s).to eq 'http://test.host/resource/'
129
+ expect(opts).to be_nil
130
+ end
131
+ relation.send(method)
132
+ end
133
+
134
+ it 'accepts params as keyword argument' do
135
+ expect(context).to receive(:request) do |meth, uri, opts|
136
+ expect(meth).to eq method
137
+ expect(uri.to_s).to eq 'http://test.host/resource/1'
138
+ expect(opts).to be_nil
139
+ end
140
+ relation.send(method, params: {id: 1})
141
+ end
142
+
143
+ it 'accepts params as positional argument' do
144
+ expect(context).to receive(:request) do |meth, uri, opts|
145
+ expect(meth).to eq method
146
+ expect(uri.to_s).to eq 'http://test.host/resource/1'
147
+ expect(opts).to be_nil
148
+ end
149
+ relation.send(method, {id: 1})
150
+ end
151
+
152
+ it 'merges keyword params into positional params' do
153
+ expect(context).to receive(:request) do |meth, uri, opts|
154
+ expect(meth).to eq method
155
+ expect(uri.to_s).to eq 'http://test.host/resource/1?a=2&b=3'
156
+ expect(opts).to be_nil
157
+ end
158
+ relation.send(method, {id: 1, q: {a: 1, c: 1}}, params: {q: {a: 2, b: 3}})
159
+ end
160
+ end
161
+
162
+ describe '#get' do
163
+ include_examples 'non-data-request', :get
164
+ end
165
+
166
+ describe '#head' do
167
+ include_examples 'non-data-request', :head
168
+ end
169
+
170
+ describe '#delete' do
171
+ include_examples 'non-data-request', :delete
172
+ end
173
+
174
+ shared_examples 'data-request' do |method|
175
+ let(:data) { Object.new }
176
+
177
+ it "issues a #{method.upcase} request" do
178
+ expect(context).to receive(:request) do |meth, uri, opts|
179
+ expect(meth).to eq method
180
+ expect(uri.to_s).to eq 'http://test.host/resource/'
181
+ expect(opts).to eq({data: nil})
182
+ end
183
+ relation.send(method)
184
+ end
185
+
186
+ it 'accepts params as keyword argument' do
187
+ expect(context).to receive(:request) do |meth, uri, opts|
188
+ expect(meth).to eq method
189
+ expect(uri.to_s).to eq 'http://test.host/resource/1'
190
+ expect(opts).to eq({data: nil})
191
+ end
192
+ relation.send(method, params: {id: 1})
193
+ end
194
+
195
+ it 'accepts data as keyword argument' do
196
+ expect(context).to receive(:request) do |meth, uri, opts|
197
+ expect(meth).to eq method
198
+ expect(uri.to_s).to eq 'http://test.host/resource/1'
199
+ expect(opts).to eq({data:})
200
+ end
201
+ relation.send(method, data:, params: {id: 1})
202
+ end
203
+
204
+ it 'accepts data as position argument' do
205
+ expect(context).to receive(:request) do |meth, uri, opts|
206
+ expect(meth).to eq method
207
+ expect(uri.to_s).to eq 'http://test.host/resource/1'
208
+ expect(opts).to eq({data:})
209
+ end
210
+ relation.send(method, data, params: {id: 1})
211
+ end
212
+
213
+ it 'prefers data from keyword argument' do
214
+ expect(context).to receive(:request) do |meth, uri, opts|
215
+ expect(meth).to eq method
216
+ expect(uri.to_s).to eq 'http://test.host/resource/1'
217
+ expect(opts).to eq({data:})
218
+ end
219
+ relation.send(method, 'DATA', data:, params: {id: 1})
220
+ end
221
+ end
222
+
223
+ describe '#post' do
224
+ include_examples 'data-request', :post
225
+ end
226
+
227
+ describe '#put' do
228
+ include_examples 'data-request', :put
229
+ end
230
+
231
+ describe '#patch' do
232
+ include_examples 'data-request', :patch
55
233
  end
56
234
  end