restify 1.15.2 → 2.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 (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