ddy_remote_resource 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +3 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +182 -0
  10. data/Rakefile +7 -0
  11. data/lib/extensions/ethon/easy/queryable.rb +36 -0
  12. data/lib/remote_resource.rb +64 -0
  13. data/lib/remote_resource/base.rb +126 -0
  14. data/lib/remote_resource/builder.rb +53 -0
  15. data/lib/remote_resource/collection.rb +31 -0
  16. data/lib/remote_resource/connection.rb +24 -0
  17. data/lib/remote_resource/connection_options.rb +41 -0
  18. data/lib/remote_resource/http_errors.rb +33 -0
  19. data/lib/remote_resource/querying/finder_methods.rb +34 -0
  20. data/lib/remote_resource/querying/persistence_methods.rb +38 -0
  21. data/lib/remote_resource/request.rb +106 -0
  22. data/lib/remote_resource/response.rb +69 -0
  23. data/lib/remote_resource/response_handeling.rb +48 -0
  24. data/lib/remote_resource/rest.rb +29 -0
  25. data/lib/remote_resource/url_naming.rb +34 -0
  26. data/lib/remote_resource/url_naming_determination.rb +39 -0
  27. data/lib/remote_resource/version.rb +3 -0
  28. data/remote_resource.gemspec +32 -0
  29. data/spec/lib/extensions/ethon/easy/queryable_spec.rb +135 -0
  30. data/spec/lib/remote_resource/base_spec.rb +388 -0
  31. data/spec/lib/remote_resource/builder_spec.rb +245 -0
  32. data/spec/lib/remote_resource/collection_spec.rb +148 -0
  33. data/spec/lib/remote_resource/connection_options_spec.rb +124 -0
  34. data/spec/lib/remote_resource/connection_spec.rb +61 -0
  35. data/spec/lib/remote_resource/querying/finder_methods_spec.rb +105 -0
  36. data/spec/lib/remote_resource/querying/persistence_methods_spec.rb +174 -0
  37. data/spec/lib/remote_resource/request_spec.rb +594 -0
  38. data/spec/lib/remote_resource/response_spec.rb +196 -0
  39. data/spec/lib/remote_resource/rest_spec.rb +98 -0
  40. data/spec/lib/remote_resource/url_naming_determination_spec.rb +225 -0
  41. data/spec/lib/remote_resource/url_naming_spec.rb +72 -0
  42. data/spec/lib/remote_resource/version_spec.rb +8 -0
  43. data/spec/spec_helper.rb +4 -0
  44. metadata +242 -0
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe RemoteResource::Connection do
4
+
5
+ module RemoteResource
6
+ class ConnectionDummy
7
+ include RemoteResource::Base
8
+
9
+ self.site = 'https://foobar.com'
10
+
11
+ end
12
+ end
13
+
14
+ let(:dummy_class) { RemoteResource::ConnectionDummy }
15
+ let(:dummy) { dummy_class.new }
16
+
17
+ describe '.connection' do
18
+ it 'uses Typhoeus::Request' do
19
+ expect(dummy_class.connection).to eql Typhoeus::Request
20
+ end
21
+ end
22
+
23
+ describe '.content_type' do
24
+ let!(:original_content_type) { dummy_class.content_type }
25
+
26
+ context 'when content_type is set' do
27
+ it 'returns the given content_type' do
28
+ dummy_class.content_type = '.html'
29
+
30
+ expect(dummy_class.content_type).to eql '.html'
31
+
32
+ dummy_class.content_type = original_content_type
33
+ end
34
+ end
35
+
36
+ context 'when NO content_type is set' do
37
+ it 'returns the default content_type' do
38
+ expect(dummy_class.content_type).to eql '.json'
39
+ end
40
+ end
41
+ end
42
+
43
+ describe '.headers' do
44
+ context 'when .extra_headers are set' do
45
+ it 'returns the default headers merged with the set .extra_headers' do
46
+ dummy_class.extra_headers = { "Foo" => "Bar" }
47
+
48
+ expect(dummy_class.headers).to eql({ "Accept" => "application/json", "Foo" => "Bar" })
49
+
50
+ dummy_class.extra_headers = nil
51
+ end
52
+ end
53
+
54
+ context 'when NO .extra_headers are set' do
55
+ it 'returns the default headers' do
56
+ expect(dummy_class.headers).to eql({ "Accept" => "application/json" })
57
+ end
58
+ end
59
+ end
60
+
61
+ end
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+
3
+ describe RemoteResource::Querying::FinderMethods do
4
+
5
+ module RemoteResource
6
+ module Querying
7
+ class FinderMethodsDummy
8
+ include RemoteResource::Base
9
+
10
+ self.site = 'https://foobar.com'
11
+
12
+ end
13
+ end
14
+ end
15
+
16
+ let(:dummy_class) { RemoteResource::Querying::FinderMethodsDummy }
17
+ let(:dummy) { dummy_class.new }
18
+
19
+ describe '.find' do
20
+ let(:response) { instance_double(RemoteResource::Response) }
21
+
22
+ before do
23
+ allow(dummy_class).to receive(:build_resource_from_response) { dummy }
24
+ allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
25
+ end
26
+
27
+ it 'performs a RemoteResource::Request with the connection_options no_params' do
28
+ expect(RemoteResource::Request).to receive(:new).with(dummy_class, :get, { id: '12' }, { no_params: true }).and_call_original
29
+ expect_any_instance_of(RemoteResource::Request).to receive(:perform)
30
+ dummy_class.find '12'
31
+ end
32
+
33
+ it 'builds the resource from the RemoteResource::Response' do
34
+ expect(dummy_class).to receive(:build_resource_from_response).with response
35
+ dummy_class.find '12'
36
+ end
37
+ end
38
+
39
+ describe '.find_by' do
40
+ let(:response) { instance_double(RemoteResource::Response) }
41
+ let(:params) do
42
+ { id: '12' }
43
+ end
44
+
45
+ before do
46
+ allow(dummy_class).to receive(:build_resource_from_response) { dummy }
47
+ allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
48
+ end
49
+
50
+ it 'performs a RemoteResource::Request' do
51
+ expect(RemoteResource::Request).to receive(:new).with(dummy_class, :get, params, {}).and_call_original
52
+ expect_any_instance_of(RemoteResource::Request).to receive(:perform)
53
+ dummy_class.find_by params
54
+ end
55
+
56
+ it 'builds the resource from the RemoteResource::Response' do
57
+ expect(dummy_class).to receive(:build_resource_from_response).with response
58
+ dummy_class.find_by params
59
+ end
60
+ end
61
+
62
+ describe '.all' do
63
+ let(:response) { instance_double(RemoteResource::Response) }
64
+
65
+ before do
66
+ allow(dummy_class).to receive(:build_collection_from_response) { dummy }
67
+ allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
68
+ end
69
+
70
+ it 'performs a RemoteResource::Request with the connection_options collection' do
71
+ expect(RemoteResource::Request).to receive(:new).with(dummy_class, :get, {}, { collection: true }).and_call_original
72
+ expect_any_instance_of(RemoteResource::Request).to receive(:perform)
73
+ dummy_class.all
74
+ end
75
+
76
+ it 'builds the resources from the RemoteResource::Response' do
77
+ expect(dummy_class).to receive(:build_collection_from_response).with response
78
+ dummy_class.all
79
+ end
80
+ end
81
+
82
+ describe '.where' do
83
+ let(:response) { instance_double(RemoteResource::Response) }
84
+ let(:params) do
85
+ { username: 'mies' }
86
+ end
87
+
88
+ before do
89
+ allow(dummy_class).to receive(:build_collection_from_response) { dummy }
90
+ allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
91
+ end
92
+
93
+ it 'performs a RemoteResource::Request with the connection_options collection' do
94
+ expect(RemoteResource::Request).to receive(:new).with(dummy_class, :get, params, { collection: true }).and_call_original
95
+ expect_any_instance_of(RemoteResource::Request).to receive(:perform)
96
+ dummy_class.where params
97
+ end
98
+
99
+ it 'builds the resources from the RemoteResource::Response' do
100
+ expect(dummy_class).to receive(:build_collection_from_response).with response
101
+ dummy_class.where params
102
+ end
103
+ end
104
+
105
+ end
@@ -0,0 +1,174 @@
1
+ require 'spec_helper'
2
+
3
+ describe RemoteResource::Querying::PersistenceMethods do
4
+
5
+ module RemoteResource
6
+ module Querying
7
+ class PersistenceMethodsDummy
8
+ include RemoteResource::Base
9
+
10
+ self.site = 'https://foobar.com'
11
+
12
+ attribute :name
13
+
14
+ end
15
+ end
16
+ end
17
+
18
+ let(:dummy_class) { RemoteResource::Querying::PersistenceMethodsDummy }
19
+ let(:dummy) { dummy_class.new }
20
+
21
+ describe '.create' do
22
+ let(:response) { instance_double(RemoteResource::Response) }
23
+ let(:attributes) do
24
+ { name: 'Mies' }
25
+ end
26
+
27
+ before do
28
+ allow_any_instance_of(dummy_class).to receive(:handle_response)
29
+ allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
30
+ end
31
+
32
+ it 'instantiates the resource with the attributes' do
33
+ expect(dummy_class).to receive(:new).with(attributes).and_call_original
34
+ dummy_class.create attributes
35
+ end
36
+
37
+ it 'performs a RemoteResource::Request' do
38
+ expect(RemoteResource::Request).to receive(:new).with(dummy_class, :post, attributes, {}).and_call_original
39
+ expect_any_instance_of(RemoteResource::Request).to receive(:perform)
40
+ dummy_class.create attributes
41
+ end
42
+
43
+ it 'handles the RemoteResource::Response' do
44
+ expect_any_instance_of(dummy_class).to receive(:handle_response).with response
45
+ dummy_class.create attributes
46
+ end
47
+ end
48
+
49
+ describe '#update_attributes' do
50
+ let(:dummy) { dummy_class.new id: 10 }
51
+
52
+ let(:attributes) do
53
+ { name: 'Noot' }
54
+ end
55
+
56
+ before do
57
+ allow(dummy).to receive(:create_or_update) { dummy }
58
+ allow(dummy).to receive(:success?)
59
+ end
60
+
61
+ it 'rebuilds the resource with the attributes' do
62
+ expect(dummy).to receive(:rebuild_resource).with(attributes).and_call_original
63
+ dummy.update_attributes attributes
64
+ end
65
+
66
+ context 'when the id is given in the attributes' do
67
+ let(:attributes) do
68
+ { id: 14, name: 'Noot' }
69
+ end
70
+
71
+ it 'calls #create_or_update with the attributes and given id' do
72
+ expect(dummy).to receive(:create_or_update).with(attributes, {})
73
+ dummy.update_attributes attributes
74
+ end
75
+ end
76
+
77
+ context 'when the id is NOT given in the attributes' do
78
+ it 'calls #create_or_update with the attributes and #id of resource' do
79
+ expect(dummy).to receive(:create_or_update).with(attributes.merge(id: dummy.id), {})
80
+ dummy.update_attributes attributes
81
+ end
82
+ end
83
+
84
+ context 'when the save was successful' do
85
+ it 'returns the resource' do
86
+ allow(dummy).to receive(:success?) { true }
87
+
88
+ expect(dummy.update_attributes attributes).to eql dummy
89
+ end
90
+ end
91
+
92
+ context 'when the save was NOT successful' do
93
+ it 'returns false' do
94
+ allow(dummy).to receive(:success?) { false }
95
+
96
+ expect(dummy.update_attributes attributes).to eql false
97
+ end
98
+ end
99
+ end
100
+
101
+ describe '#save' do
102
+ let(:attributes) { dummy.attributes }
103
+
104
+ before do
105
+ allow(dummy).to receive(:create_or_update) { dummy }
106
+ allow(dummy).to receive(:success?)
107
+ end
108
+
109
+ it 'calls #create_or_update with the attributes' do
110
+ expect(dummy).to receive(:create_or_update).with(attributes, {})
111
+ dummy.save
112
+ end
113
+
114
+ context 'when the save was successful' do
115
+ it 'returns the resource' do
116
+ allow(dummy).to receive(:success?) { true }
117
+
118
+ expect(dummy.save).to eql dummy
119
+ end
120
+ end
121
+
122
+ context 'when the save was NOT successful' do
123
+ it 'returns false' do
124
+ allow(dummy).to receive(:success?) { false }
125
+
126
+ expect(dummy.save).to eql false
127
+ end
128
+ end
129
+ end
130
+
131
+ describe '#create_or_update' do
132
+ let(:response) { instance_double(RemoteResource::Response) }
133
+
134
+ before do
135
+ allow(dummy).to receive(:handle_response) { dummy }
136
+ allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
137
+ end
138
+
139
+ context 'when the attributes contain an id' do
140
+ let(:attributes) do
141
+ { id: 10, name: 'Kees' }
142
+ end
143
+
144
+ it 'performs a RemoteResource::Request with rest_action :patch' do
145
+ expect(RemoteResource::Request).to receive(:new).with(dummy, :patch, attributes, {}).and_call_original
146
+ expect_any_instance_of(RemoteResource::Request).to receive(:perform)
147
+ dummy.create_or_update attributes
148
+ end
149
+
150
+ it 'handles the RemoteResource::Response' do
151
+ expect(dummy).to receive(:handle_response).with response
152
+ dummy.create_or_update attributes
153
+ end
154
+ end
155
+
156
+ context 'when the attributes do NOT contain an id' do
157
+ let(:attributes) do
158
+ { name: 'Mies' }
159
+ end
160
+
161
+ it 'performs a RemoteResource::Request with rest_action :post' do
162
+ expect(RemoteResource::Request).to receive(:new).with(dummy, :post, attributes, {}).and_call_original
163
+ expect_any_instance_of(RemoteResource::Request).to receive(:perform)
164
+ dummy.create_or_update attributes
165
+ end
166
+
167
+ it 'handles the RemoteResource::Response' do
168
+ expect(dummy).to receive(:handle_response).with response
169
+ dummy.create_or_update attributes
170
+ end
171
+ end
172
+ end
173
+
174
+ end
@@ -0,0 +1,594 @@
1
+ require 'spec_helper'
2
+
3
+ describe RemoteResource::Request do
4
+
5
+ module RemoteResource
6
+ class RequestDummy
7
+ include RemoteResource::Base
8
+
9
+ self.site = 'http://www.foobar.com'
10
+
11
+ attr_accessor :name
12
+
13
+ end
14
+ end
15
+
16
+ let(:dummy_class) { RemoteResource::RequestDummy }
17
+ let(:dummy) { dummy_class.new id: '12' }
18
+
19
+ let(:resource) { dummy_class }
20
+ let(:rest_action) { :get }
21
+ let(:connection_options) { {} }
22
+ let(:attributes) do
23
+ { name: 'Mies' }
24
+ end
25
+
26
+ let(:request) { described_class.new resource, rest_action, attributes, connection_options.dup }
27
+
28
+ specify { expect(described_class).to include RemoteResource::HTTPErrors }
29
+
30
+ describe '#connection' do
31
+ it 'uses the connection of the resource_klass' do
32
+ expect(request.connection).to eql Typhoeus::Request
33
+ end
34
+ end
35
+
36
+ describe '#connection_options' do
37
+ let(:threaded_connection_options_thread_name) { 'remote_resource.request_dummy.threaded_connection_options' }
38
+
39
+ before { Thread.current[threaded_connection_options_thread_name] = threaded_connection_options }
40
+ after { Thread.current[threaded_connection_options_thread_name] = nil }
41
+
42
+ context 'when the given connection_options contain other values than the resource threaded_connection_options or connection_options' do
43
+ let(:connection_options) do
44
+ {
45
+ site: 'http://www.barbaz.com',
46
+ collection: true,
47
+ path_prefix: '/api',
48
+ root_element: :bazbar
49
+ }
50
+ end
51
+
52
+ let(:threaded_connection_options) do
53
+ {
54
+ site: 'http://www.bazbazbaz.com',
55
+ path_prefix: '/registration',
56
+ path_postfix: '/promotion',
57
+ root_element: :bazbazbaz
58
+ }
59
+ end
60
+
61
+ it 'merges the given connection_options with the resource connection_options while taking precedence over the resource connection_options after the threaded_connection_options' do
62
+ expect(request.connection_options[:site]).to eql 'http://www.barbaz.com'
63
+ expect(request.connection_options[:collection]).to eql true
64
+ expect(request.connection_options[:path_prefix]).to eql '/api'
65
+ expect(request.connection_options[:path_postfix]).to eql '/promotion'
66
+ expect(request.connection_options[:root_element]).to eql :bazbar
67
+ end
68
+ end
69
+
70
+ context 'when the given connection_options do NOT contain other values than the resource threaded_connection_options or connection_options' do
71
+ let(:connection_options) do
72
+ {
73
+ collection: true,
74
+ path_prefix: '/api',
75
+ root_element: :bazbar
76
+ }
77
+ end
78
+
79
+ let(:threaded_connection_options) do
80
+ {
81
+ site: 'http://www.bazbazbaz.com',
82
+ path_prefix: '/api',
83
+ path_postfix: '/promotion'
84
+ }
85
+ end
86
+
87
+ it 'merges the given connection_options with the resource threaded_connection_options and connection_options' do
88
+ expect(request.connection_options[:site]).to eql 'http://www.bazbazbaz.com'
89
+ expect(request.connection_options[:collection]).to eql true
90
+ expect(request.connection_options[:path_prefix]).to eql '/api'
91
+ expect(request.connection_options[:path_postfix]).to eql '/promotion'
92
+ expect(request.connection_options[:root_element]).to eql :bazbar
93
+ end
94
+ end
95
+ end
96
+
97
+ describe '#original_connection_options' do
98
+ let(:threaded_connection_options_thread_name) { 'remote_resource.request_dummy.threaded_connection_options' }
99
+
100
+ before { Thread.current[threaded_connection_options_thread_name] = threaded_connection_options }
101
+ after { Thread.current[threaded_connection_options_thread_name] = nil }
102
+
103
+ context 'when the given connection_options (original_connection_options) contain other values than the resource threaded_connection_options' do
104
+ let(:connection_options) do
105
+ {
106
+ site: 'http://www.barbaz.com',
107
+ collection: true,
108
+ path_prefix: '/api',
109
+ root_element: :bazbar
110
+ }
111
+ end
112
+
113
+ let(:threaded_connection_options) do
114
+ {
115
+ site: 'http://www.bazbazbaz.com',
116
+ path_prefix: '/registration',
117
+ path_postfix: '/promotion',
118
+ root_element: :bazbazbaz
119
+ }
120
+ end
121
+
122
+ it 'merges the given connection_options (original_connection_options) with the resource threaded_connection_options while taking precedence over the resource threaded_connection_options' do
123
+ expect(request.original_connection_options[:site]).to eql 'http://www.barbaz.com'
124
+ expect(request.original_connection_options[:collection]).to eql true
125
+ expect(request.original_connection_options[:path_prefix]).to eql '/api'
126
+ expect(request.original_connection_options[:path_postfix]).to eql '/promotion'
127
+ expect(request.original_connection_options[:root_element]).to eql :bazbar
128
+ end
129
+ end
130
+
131
+ context 'when the given connection_options (original_connection_options) do NOT contain other values than the resource threaded_connection_options' do
132
+ let(:connection_options) do
133
+ {
134
+ collection: true,
135
+ path_prefix: '/api',
136
+ root_element: :bazbar
137
+ }
138
+ end
139
+
140
+ let(:threaded_connection_options) do
141
+ {
142
+ site: 'http://www.bazbazbaz.com',
143
+ path_prefix: '/api',
144
+ path_postfix: '/promotion'
145
+ }
146
+ end
147
+
148
+ it 'merges the given connection_options (original_connection_options) with the resource threaded_connection_options' do
149
+ expect(request.original_connection_options[:site]).to eql 'http://www.bazbazbaz.com'
150
+ expect(request.original_connection_options[:collection]).to eql true
151
+ expect(request.original_connection_options[:path_prefix]).to eql '/api'
152
+ expect(request.original_connection_options[:path_postfix]).to eql '/promotion'
153
+ expect(request.original_connection_options[:root_element]).to eql :bazbar
154
+ end
155
+ end
156
+ end
157
+
158
+ describe '#perform' do
159
+ let(:connection) { Typhoeus::Request }
160
+ let(:determined_request_url) { 'http://www.foobar.com/request_dummy.json' }
161
+ let(:determined_params) { attributes }
162
+ let(:determined_attributes) { attributes }
163
+ let(:determined_headers) { { "Accept"=>"application/json" } }
164
+
165
+ let(:typhoeus_request) { Typhoeus::Request.new determined_request_url }
166
+ let(:typhoeus_response) do
167
+ response = Typhoeus::Response.new
168
+ response.request = typhoeus_request
169
+ response
170
+ end
171
+
172
+ let(:determined_connection_options) { request.connection_options }
173
+
174
+ before do
175
+ allow_any_instance_of(Typhoeus::Request).to receive(:run) { typhoeus_response }
176
+ allow(typhoeus_response).to receive(:response_code)
177
+ allow(typhoeus_response).to receive(:success?) { true }
178
+ end
179
+
180
+ shared_examples 'a conditional construct for the response' do
181
+ context 'when the response is successful' do
182
+ it 'makes a RemoteResource::Response object with the Typhoeus::Response object and the connection_options' do
183
+ expect(RemoteResource::Response).to receive(:new).with(typhoeus_response, determined_connection_options).and_call_original
184
+ request.perform
185
+ end
186
+
187
+ it 'returns a RemoteResource::Response object' do
188
+ expect(request.perform).to be_a RemoteResource::Response
189
+ end
190
+ end
191
+
192
+ context 'when the response_code of the response is 422' do
193
+ before { allow(typhoeus_response).to receive(:response_code) { 422 } }
194
+
195
+ it 'makes a RemoteResource::Response object with the Typhoeus::Response object and the connection_options' do
196
+ expect(RemoteResource::Response).to receive(:new).with(typhoeus_response, determined_connection_options).and_call_original
197
+ request.perform
198
+ end
199
+
200
+ it 'returns a RemoteResource::Response object' do
201
+ expect(request.perform).to be_a RemoteResource::Response
202
+ end
203
+ end
204
+
205
+ context 'when the response is NOT successful' do
206
+ before { allow(typhoeus_response).to receive(:success?) { false } }
207
+
208
+ it 'calls #raise_http_errors to raise an error' do
209
+ expect(request).to receive(:raise_http_errors).with typhoeus_response
210
+ request.perform
211
+ end
212
+ end
213
+ end
214
+
215
+ context 'when the rest_action is :get' do
216
+ let(:rest_action) { 'get' }
217
+
218
+ it 'makes a GET request with the attributes as params' do
219
+ expect(connection).to receive(:get).with(determined_request_url, params: determined_params, headers: determined_headers).and_call_original
220
+ request.perform
221
+ end
222
+
223
+ it_behaves_like 'a conditional construct for the response'
224
+ end
225
+
226
+ context 'when the rest_action is :put' do
227
+ let(:rest_action) { 'put' }
228
+
229
+ it 'makes a PUT request with the attributes as body' do
230
+ expect(connection).to receive(:put).with(determined_request_url, body: determined_attributes, headers: determined_headers).and_call_original
231
+ request.perform
232
+ end
233
+
234
+ it_behaves_like 'a conditional construct for the response'
235
+ end
236
+
237
+ context 'when the rest_action is :put' do
238
+ let(:rest_action) { 'put' }
239
+
240
+ it 'makes a PUT request with the attributes as body' do
241
+ expect(connection).to receive(:put).with(determined_request_url, body: determined_attributes, headers: determined_headers).and_call_original
242
+ request.perform
243
+ end
244
+
245
+ it_behaves_like 'a conditional construct for the response'
246
+ end
247
+
248
+ context 'when the rest_action is :patch' do
249
+ let(:rest_action) { 'patch' }
250
+
251
+ it 'makes a PATCH request with the attributes as body' do
252
+ expect(connection).to receive(:patch).with(determined_request_url, body: determined_attributes, headers: determined_headers).and_call_original
253
+ request.perform
254
+ end
255
+
256
+ it_behaves_like 'a conditional construct for the response'
257
+ end
258
+
259
+ context 'when the rest_action is :post' do
260
+ let(:rest_action) { 'post' }
261
+
262
+ it 'makes a POST request with the attributes as body' do
263
+ expect(connection).to receive(:post).with(determined_request_url, body: determined_attributes, headers: determined_headers).and_call_original
264
+ request.perform
265
+ end
266
+
267
+ it_behaves_like 'a conditional construct for the response'
268
+ end
269
+
270
+ context 'when the rest_action is unknown' do
271
+ let(:rest_action) { 'foo' }
272
+
273
+ it 'raises the RemoteResource::RESTActionUnknown error' do
274
+ expect{ request.perform }.to raise_error RemoteResource::RESTActionUnknown, "for action: 'foo'"
275
+ end
276
+ end
277
+ end
278
+
279
+ describe '#determined_request_url' do
280
+ context 'the attributes contain an id' do
281
+ let(:attributes) do
282
+ { id: 12, name: 'Mies' }
283
+ end
284
+
285
+ it 'uses the id for the request url' do
286
+ expect(request.determined_request_url).to eql 'http://www.foobar.com/request_dummy/12.json'
287
+ end
288
+ end
289
+
290
+ context 'the attributes do NOT contain an id' do
291
+ it 'does NOT use the id for the request url' do
292
+ expect(request.determined_request_url).to eql 'http://www.foobar.com/request_dummy.json'
293
+ end
294
+ end
295
+
296
+ context 'the given connection_options (original_connection_options) contain a base_url' do
297
+ let(:connection_options) do
298
+ { base_url: 'http://www.foo.com/api' }
299
+ end
300
+
301
+ it 'uses the base_url for the request url' do
302
+ expect(request.determined_request_url).to eql 'http://www.foo.com/api.json'
303
+ end
304
+ end
305
+
306
+ context 'the given connection_options (original_connection_options) do NOT contain a base_url' do
307
+ it 'does NOT use the base_url for the request url' do
308
+ expect(request.determined_request_url).to eql 'http://www.foobar.com/request_dummy.json'
309
+ end
310
+ end
311
+
312
+ context 'the given connection_options contain a collection' do
313
+ let(:connection_options) do
314
+ { collection: true }
315
+ end
316
+
317
+ it 'uses the collection to determine the base_url for the request url' do
318
+ expect(request.determined_request_url).to eql 'http://www.foobar.com/request_dummies.json'
319
+ end
320
+ end
321
+
322
+ context 'the connection_options contain a content_type' do
323
+ let(:connection_options) do
324
+ { content_type: '' }
325
+ end
326
+
327
+ it 'uses the content_type for the request url' do
328
+ expect(request.determined_request_url).to eql 'http://www.foobar.com/request_dummy'
329
+ end
330
+ end
331
+
332
+ context 'the connection_options do NOT contain a content_type' do
333
+ it 'does NOT use the content_type for the request url' do
334
+ expect(request.determined_request_url).to eql 'http://www.foobar.com/request_dummy.json'
335
+ end
336
+ end
337
+ end
338
+
339
+ describe '#determined_params' do
340
+ context 'the connection_options contain no_params' do
341
+ let(:connection_options) do
342
+ {
343
+ params: { page: 5, limit: 15 },
344
+ no_params: true
345
+ }
346
+ end
347
+
348
+ it 'returns nil' do
349
+ expect(request.determined_params).to be_nil
350
+ end
351
+ end
352
+
353
+ context 'the connection_options do NOT contain a no_params' do
354
+ context 'and the connection_options contain no_attributes' do
355
+ let(:connection_options) do
356
+ {
357
+ params: { page: 5, limit: 15 },
358
+ no_params: false,
359
+ no_attributes: true
360
+ }
361
+ end
362
+
363
+ it 'returns the params' do
364
+ expect(request.determined_params).to eql({ page: 5, limit: 15 })
365
+ end
366
+ end
367
+
368
+ context 'and the connection_options do NOT contain no_attributes' do
369
+ let(:connection_options) do
370
+ {
371
+ params: { page: 5, limit: 15 },
372
+ no_params: false,
373
+ no_attributes: false
374
+ }
375
+ end
376
+
377
+ it 'returns the params merge with the attributes' do
378
+ expect(request.determined_params).to eql({ name: 'Mies', page: 5, limit: 15 })
379
+ end
380
+ end
381
+ end
382
+ end
383
+
384
+ describe '#determined_attributes' do
385
+ context 'the connection_options contain no_attributes' do
386
+ let(:connection_options) do
387
+ { no_attributes: true }
388
+ end
389
+
390
+ it 'returns an empty Hash' do
391
+ expect(request.determined_attributes).to eql({})
392
+ end
393
+ end
394
+
395
+ context 'the connection_options do NOT contain a no_attributes' do
396
+ it 'does NOT return an empty Hash' do
397
+ expect(request.determined_attributes).not_to eql({})
398
+ end
399
+ end
400
+
401
+ context 'the connection_options contain a root_element' do
402
+ let(:connection_options) do
403
+ { root_element: :foobar }
404
+ end
405
+
406
+ let(:packed_up_attributes) do
407
+ { 'foobar' => { name: 'Mies' } }
408
+ end
409
+
410
+ it 'packs up the attributes with the root_element' do
411
+ expect(request.determined_attributes).to eql packed_up_attributes
412
+ end
413
+ end
414
+
415
+ context 'the connection_options do NOT contain a root_element' do
416
+ it 'does NOT pack up the attributes with the root_element' do
417
+ expect(request.determined_attributes).to eql attributes
418
+ end
419
+ end
420
+ end
421
+
422
+ describe '#determined_headers' do
423
+ let(:global_headers) do
424
+ {
425
+ 'X-Locale' => 'en',
426
+ 'Authorization' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
427
+ }
428
+ end
429
+
430
+ before { RemoteResource::Base.global_headers = global_headers }
431
+ after { RemoteResource::Base.global_headers = nil }
432
+
433
+ let(:headers) do
434
+ { 'Baz' => 'FooBar' }
435
+ end
436
+
437
+ let(:default_headers) do
438
+ dummy_class.default_headers
439
+ end
440
+
441
+ context 'the connection_options contain a default_headers' do
442
+ let(:default_headers) do
443
+ { 'Foo' => 'Bar' }
444
+ end
445
+
446
+ context 'and the given connection_options (original_connection_options) contain a headers' do
447
+ let(:connection_options) do
448
+ { default_headers: default_headers, headers: headers }
449
+ end
450
+
451
+ it 'uses the default_headers for the request headers' do
452
+ expect(request.determined_headers).to eql default_headers.merge(global_headers)
453
+ end
454
+ end
455
+
456
+ context 'and the given connection_options (original_connection_options) do NOT contain a headers' do
457
+ let(:connection_options) do
458
+ { default_headers: default_headers }
459
+ end
460
+
461
+ it 'uses the default_headers for the request headers' do
462
+ expect(request.determined_headers).to eql default_headers.merge(global_headers)
463
+ end
464
+ end
465
+ end
466
+
467
+ context 'the connection_options do NOT contain a default_headers' do
468
+ context 'and the given connection_options (original_connection_options) contain a headers' do
469
+ let(:connection_options) do
470
+ { headers: headers }
471
+ end
472
+
473
+ it 'uses the headers for the request headers' do
474
+ expect(request.determined_headers).to eql default_headers.merge(headers).merge(global_headers)
475
+ end
476
+ end
477
+
478
+ context 'and the given connection_options (original_connection_options) do NOT contain a headers' do
479
+ context 'and the resource contains a extra_headers' do
480
+ let(:extra_headers) do
481
+ { 'BarBaz' => 'Baz' }
482
+ end
483
+
484
+ it 'uses the headers of the resource for the request headers' do
485
+ dummy_class.extra_headers = extra_headers
486
+ dummy_class.connection_options.reload!
487
+
488
+ expect(request.determined_headers).to eql default_headers.merge(extra_headers).merge(global_headers)
489
+
490
+ dummy_class.extra_headers = nil
491
+ dummy_class.connection_options.reload!
492
+ end
493
+ end
494
+
495
+ context 'and the resource does NOT contain a extra_headers' do
496
+ it 'does NOT use the headers for the request headers' do
497
+ expect(request.determined_headers).to eql default_headers.merge(global_headers)
498
+ end
499
+ end
500
+ end
501
+ end
502
+ end
503
+
504
+ describe '#resource_klass' do
505
+ context 'when the resource is a RemoteResource class' do
506
+ let(:resource) { dummy_class }
507
+
508
+ it 'returns the resource' do
509
+ expect(request.send :resource_klass).to eql RemoteResource::RequestDummy
510
+ end
511
+ end
512
+
513
+ context 'when the resource is a RemoteResource object' do
514
+ let(:resource) { dummy }
515
+
516
+ it 'returns the Class of the resource' do
517
+ expect(request.send :resource_klass).to eql RemoteResource::RequestDummy
518
+ end
519
+ end
520
+ end
521
+
522
+ describe '#raise_http_errors' do
523
+ let(:response) { instance_double Typhoeus::Response }
524
+ let(:raise_http_errors) { request.send :raise_http_errors, response }
525
+
526
+ before { allow(response).to receive(:response_code) { response_code } }
527
+
528
+ context 'when the response code is 301, 302, 303 or 307' do
529
+ response_codes = [301, 302, 303, 307]
530
+ response_codes.each do |response_code|
531
+
532
+ it "raises a RemoteResource::HTTPRedirectionError with response code #{response_code}" do
533
+ allow(response).to receive(:response_code) { response_code }
534
+
535
+ expect{ raise_http_errors }.to raise_error RemoteResource::HTTPRedirectionError, "with HTTP response status: #{response_code} and response: #{response}"
536
+ end
537
+ end
538
+ end
539
+
540
+ context 'when the response code is in the 4xx range' do
541
+ response_codes_with_error_class = {
542
+ 400 => RemoteResource::HTTPBadRequest,
543
+ 401 => RemoteResource::HTTPUnauthorized,
544
+ 403 => RemoteResource::HTTPForbidden,
545
+ 404 => RemoteResource::HTTPNotFound,
546
+ 405 => RemoteResource::HTTPMethodNotAllowed,
547
+ 406 => RemoteResource::HTTPNotAcceptable,
548
+ 408 => RemoteResource::HTTPRequestTimeout,
549
+ 409 => RemoteResource::HTTPConflict,
550
+ 410 => RemoteResource::HTTPGone,
551
+ 418 => RemoteResource::HTTPTeapot,
552
+ 444 => RemoteResource::HTTPNoResponse,
553
+ 494 => RemoteResource::HTTPRequestHeaderTooLarge,
554
+ 495 => RemoteResource::HTTPCertError,
555
+ 496 => RemoteResource::HTTPNoCert,
556
+ 497 => RemoteResource::HTTPToHTTPS,
557
+ 499 => RemoteResource::HTTPClientClosedRequest,
558
+ }
559
+ response_codes_with_error_class.each do |response_code, error_class|
560
+
561
+ it "raises a #{error_class} with response code #{response_code}" do
562
+ allow(response).to receive(:response_code) { response_code }
563
+
564
+ expect{ raise_http_errors }.to raise_error error_class, "with HTTP response status: #{response_code} and response: #{response}"
565
+ end
566
+ end
567
+ end
568
+
569
+ context 'when the response code is in the 4xx range and no other error is raised' do
570
+ let(:response_code) { 430 }
571
+
572
+ it 'raises a RemoteResource::HTTPClientError' do
573
+ expect{ raise_http_errors }.to raise_error RemoteResource::HTTPClientError, "with HTTP response status: #{response_code} and response: #{response}"
574
+ end
575
+ end
576
+
577
+ context 'when the response code is in the 5xx range and no other error is raised' do
578
+ let(:response_code) { 501 }
579
+
580
+ it 'raises a RemoteResource::HTTPServerError' do
581
+ expect{ raise_http_errors }.to raise_error RemoteResource::HTTPServerError, "with HTTP response status: #{response_code} and response: #{response}"
582
+ end
583
+ end
584
+
585
+ context 'when the response code is nothing and no other error is raised' do
586
+ let(:response_code) { nil }
587
+
588
+ it 'raises a RemoteResource::HTTPError' do
589
+ expect{ raise_http_errors }.to raise_error RemoteResource::HTTPError, "with HTTP response: #{response}"
590
+ end
591
+ end
592
+ end
593
+
594
+ end