ddy_remote_resource 0.4.11 → 1.0.0.rc1

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/Guardfile +3 -0
  4. data/lib/remote_resource.rb +77 -34
  5. data/lib/remote_resource/base.rb +20 -8
  6. data/lib/remote_resource/connection.rb +26 -5
  7. data/lib/remote_resource/connection_options.rb +5 -3
  8. data/lib/remote_resource/querying/finder_methods.rb +3 -3
  9. data/lib/remote_resource/querying/persistence_methods.rb +17 -12
  10. data/lib/remote_resource/request.rb +96 -62
  11. data/lib/remote_resource/response.rb +5 -1
  12. data/lib/remote_resource/rest.rb +13 -17
  13. data/lib/remote_resource/url_naming.rb +4 -10
  14. data/lib/remote_resource/url_naming_determination.rb +1 -3
  15. data/lib/remote_resource/util.rb +64 -0
  16. data/lib/remote_resource/version.rb +1 -1
  17. data/remote_resource.gemspec +2 -2
  18. data/spec/fixtures/text_file.txt +1 -0
  19. data/spec/integration/all_spec.rb +166 -0
  20. data/spec/integration/collection_prefix_spec.rb +99 -0
  21. data/spec/integration/create_spec.rb +181 -0
  22. data/spec/integration/destroy_spec.rb +252 -0
  23. data/spec/integration/find_by_spec.rb +168 -0
  24. data/spec/integration/find_spec.rb +139 -0
  25. data/spec/integration/headers_spec.rb +222 -0
  26. data/spec/integration/naming_spec.rb +138 -0
  27. data/spec/integration/save_spec.rb +320 -0
  28. data/spec/integration/update_attributes_spec.rb +221 -0
  29. data/spec/integration/where_spec.rb +152 -0
  30. data/spec/lib/extensions/ethon/easy/queryable_spec.rb +4 -4
  31. data/spec/lib/remote_resource/base_spec.rb +54 -110
  32. data/spec/lib/remote_resource/builder_spec.rb +1 -1
  33. data/spec/lib/remote_resource/collection_spec.rb +1 -1
  34. data/spec/lib/remote_resource/connection_options_spec.rb +20 -17
  35. data/spec/lib/remote_resource/connection_spec.rb +36 -27
  36. data/spec/lib/remote_resource/querying/finder_methods_spec.rb +199 -72
  37. data/spec/lib/remote_resource/querying/persistence_methods_spec.rb +228 -220
  38. data/spec/lib/remote_resource/request_spec.rb +313 -342
  39. data/spec/lib/remote_resource/response_spec.rb +9 -3
  40. data/spec/lib/remote_resource/rest_spec.rb +11 -11
  41. data/spec/lib/remote_resource/url_naming_determination_spec.rb +1 -1
  42. data/spec/lib/remote_resource/url_naming_spec.rb +7 -22
  43. data/spec/lib/remote_resource/util_spec.rb +56 -0
  44. data/spec/lib/remote_resource/version_spec.rb +2 -3
  45. data/spec/spec_helper.rb +37 -0
  46. metadata +33 -22
  47. data/lib/remote_resource/http_errors.rb +0 -33
  48. data/lib/remote_resource/response_handeling.rb +0 -48
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe RemoteResource::Connection do
3
+ RSpec.describe RemoteResource::Connection do
4
4
 
5
5
  module RemoteResource
6
6
  class ConnectionDummy
@@ -20,42 +20,51 @@ describe RemoteResource::Connection do
20
20
  end
21
21
  end
22
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'
23
+ describe '.default_headers' do
24
+ it 'returns an empty Hash' do
25
+ expect(dummy_class.default_headers).to eql({})
26
+ end
27
+ end
31
28
 
32
- dummy_class.content_type = original_content_type
33
- end
29
+ describe '.content_type=' do
30
+ it 'warns that the method is deprecated' do
31
+ expect(dummy_class).to receive(:warn).with('[DEPRECATION] `.content_type=` is deprecated. Please use `.extension=` instead.')
32
+ dummy_class.content_type = '.json'
34
33
  end
34
+ end
35
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
36
+ describe '.content_type' do
37
+ it 'warns that the method is deprecated' do
38
+ expect(dummy_class).to receive(:warn).with('[DEPRECATION] `.content_type` is deprecated. Please use `.extension` instead.')
39
+ dummy_class.content_type
40
40
  end
41
41
  end
42
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" }
43
+ describe '.extra_headers=' do
44
+ it 'warns that the method is deprecated' do
45
+ expect(dummy_class).to receive(:warn).with('[DEPRECATION] `.extra_headers=` is deprecated. Please overwrite the .headers method to set custom headers.')
46
+ dummy_class.extra_headers = '.json'
47
+ end
48
+ end
47
49
 
48
- expect(dummy_class.headers).to eql({ "Accept" => "application/json", "Foo" => "Bar" })
50
+ describe '.extra_headers' do
51
+ it 'warns that the method is deprecated' do
52
+ expect(dummy_class).to receive(:warn).with('[DEPRECATION] `.extra_headers` is deprecated. Please overwrite the .headers method to set custom headers.')
53
+ dummy_class.extra_headers
54
+ end
55
+ end
49
56
 
50
- dummy_class.extra_headers = nil
51
- end
57
+ describe '.headers=' do
58
+ it 'warns that the method is not used to set custom headers' do
59
+ expect(dummy_class).to receive(:warn).with('[WARNING] `.headers=` can not be used to set custom headers. Please overwrite the .headers method to set custom headers.')
60
+ dummy_class.headers = { 'Foo' => 'Bar' }
52
61
  end
62
+ end
53
63
 
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
64
+ describe '.headers' do
65
+ it 'returns an empty Hash' do
66
+ expect(dummy_class.headers).to eql({})
58
67
  end
59
68
  end
60
69
 
61
- end
70
+ end
@@ -1,138 +1,265 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe RemoteResource::Querying::FinderMethods do
3
+ RSpec.describe RemoteResource::Querying::FinderMethods do
4
4
 
5
- module RemoteResource
6
- module Querying
7
- class FinderMethodsDummy
8
- include RemoteResource::Base
5
+ class Post
6
+ include RemoteResource::Base
9
7
 
10
- self.site = 'https://foobar.com'
8
+ self.site = 'https://www.example.com'
9
+ self.collection = true
10
+ self.root_element = :data
11
11
 
12
- end
13
- end
12
+ attribute :title, String
13
+ attribute :body, String
14
+ attribute :featured, Boolean
15
+ attribute :created_at, Time
14
16
  end
15
17
 
16
- let(:dummy_class) { RemoteResource::Querying::FinderMethodsDummy }
17
- let(:dummy) { dummy_class.new }
18
-
19
18
  describe '.find' do
20
- let(:response) { instance_double(RemoteResource::Response) }
19
+ let(:response_body) do
20
+ {
21
+ data: {
22
+ id: 12,
23
+ title: 'Lorem Ipsum',
24
+ body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
25
+ featured: true,
26
+ created_at: Time.new(2015, 10, 4, 9, 30, 0),
27
+ }
28
+ }
29
+ end
21
30
 
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 }
31
+ let!(:expected_request) do
32
+ mock_request = stub_request(:get, 'https://www.example.com/posts/12.json')
33
+ mock_request.to_return(status: 200, body: response_body.to_json)
34
+ mock_request
25
35
  end
26
36
 
27
- it 'performs a RemoteResource::Request with the connection_options no_attributes' do
28
- stub_request(:get, 'https://foobar.com/finder_methods_dummy/12.json').to_return(status: 200, body: {}.to_json)
29
- expect(RemoteResource::Request).to receive(:new).with(dummy_class, :get, { id: '12' }, { no_attributes: true }).and_call_original
30
- expect_any_instance_of(RemoteResource::Request).to receive(:perform).and_call_original
31
- dummy_class.find('12')
37
+ it 'performs the HTTP request' do
38
+ Post.find(12)
39
+ expect(expected_request).to have_been_requested
32
40
  end
33
41
 
34
- it 'builds the resource from the RemoteResource::Response' do
35
- expect(dummy_class).to receive(:build_resource_from_response).with response
36
- dummy_class.find('12')
42
+ it 'builds the correct resource' do
43
+ post = Post.find(12)
44
+
45
+ aggregate_failures do
46
+ expect(post.id).to eql 12
47
+ expect(post.title).to eql 'Lorem Ipsum'
48
+ expect(post.body).to eql 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
49
+ expect(post.featured).to eql true
50
+ expect(post.created_at).to eql Time.new(2015, 10, 4, 9, 30, 0)
51
+ end
37
52
  end
38
53
 
39
54
  it 'does NOT change the given connection_options' do
40
55
  connection_options = { headers: { 'Foo' => 'Bar' } }
41
56
 
42
- expect { dummy_class.find('12', connection_options) }.not_to change { connection_options }.from({ headers: { 'Foo' => 'Bar' } })
57
+ expect { Post.find(12, connection_options) }.not_to change { connection_options }.from(connection_options.dup)
43
58
  end
44
59
 
45
- context 'with extra params' do
46
- it 'performs a RemoteResource::Request with params' do
47
- stub_request(:get, 'https://foobar.com/finder_methods_dummy/12.json?skip_associations=true').to_return(status: 200, body: {}.to_json)
48
- expect(RemoteResource::Request).to receive(:new).with(dummy_class, :get, { id: '12' }, { params: { skip_associations: true }, no_attributes: true }).and_call_original
49
- expect_any_instance_of(RemoteResource::Request).to receive(:perform).and_call_original
50
- dummy_class.find('12', params: { skip_associations: true })
51
- end
60
+ xcontext 'return value #success? and NOT #success?' do
52
61
  end
53
62
  end
54
63
 
55
64
  describe '.find_by' do
56
- let(:response) { instance_double(RemoteResource::Response) }
57
- let(:params) do
58
- { id: '12' }
65
+ let(:response_body) do
66
+ {
67
+ data: {
68
+ id: 12,
69
+ title: 'Lorem Ipsum',
70
+ body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
71
+ featured: true,
72
+ created_at: Time.new(2015, 10, 4, 9, 30, 0),
73
+ }
74
+ }
59
75
  end
60
76
 
61
- before do
62
- allow(dummy_class).to receive(:build_resource_from_response) { dummy }
63
- allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
77
+ let!(:expected_request) do
78
+ mock_request = stub_request(:get, 'https://www.example.com/posts/current.json')
79
+ mock_request.with(query: { title: 'Lorem Ipsum', featured: true })
80
+ mock_request.to_return(status: 200, body: response_body.to_json)
81
+ mock_request
64
82
  end
65
83
 
66
- it 'performs a RemoteResource::Request' do
67
- expect(RemoteResource::Request).to receive(:new).with(dummy_class, :get, params, {}).and_call_original
68
- expect_any_instance_of(RemoteResource::Request).to receive(:perform)
69
- dummy_class.find_by params
84
+ it 'performs the HTTP request' do
85
+ Post.find_by({ title: 'Lorem Ipsum', featured: true }, path_postfix: '/current')
86
+ expect(expected_request).to have_been_requested
70
87
  end
71
88
 
72
- it 'builds the resource from the RemoteResource::Response' do
73
- expect(dummy_class).to receive(:build_resource_from_response).with response
74
- dummy_class.find_by params
89
+ it 'builds the correct resource' do
90
+ post = Post.find_by({ title: 'Lorem Ipsum', featured: true }, path_postfix: '/current')
91
+
92
+ aggregate_failures do
93
+ expect(post.id).to eql 12
94
+ expect(post.title).to eql 'Lorem Ipsum'
95
+ expect(post.body).to eql 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
96
+ expect(post.featured).to eql true
97
+ expect(post.created_at).to eql Time.new(2015, 10, 4, 9, 30, 0)
98
+ end
99
+ end
100
+
101
+ it 'does NOT change the given params' do
102
+ params = { title: 'Lorem Ipsum', featured: true }
103
+
104
+ expect { Post.find_by(params, path_postfix: '/current') }.not_to change { params }.from(params.dup)
75
105
  end
76
106
 
77
107
  it 'does NOT change the given connection_options' do
78
- connection_options = { headers: { 'Foo' => 'Bar' } }
108
+ params = { title: 'Lorem Ipsum', featured: true }
109
+ connection_options = { path_postfix: '/current', headers: { 'Foo' => 'Bar' } }
110
+
111
+ expect { Post.find_by(params, connection_options) }.not_to change { connection_options }.from(connection_options.dup)
112
+ end
79
113
 
80
- expect { dummy_class.find_by(params, connection_options) }.not_to change { connection_options }.from({ headers: { 'Foo' => 'Bar' } })
114
+ xcontext 'return value #success? and NOT #success?' do
81
115
  end
82
116
  end
83
117
 
84
118
  describe '.all' do
85
- let(:response) { instance_double(RemoteResource::Response) }
119
+ let(:response_body) do
120
+ {
121
+ data: [
122
+ {
123
+ id: 12,
124
+ title: 'Lorem Ipsum',
125
+ body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
126
+ created_at: Time.new(2015, 10, 4, 9, 30, 0),
127
+ },
128
+ {
129
+ id: 14,
130
+ title: 'Mauris Purus',
131
+ body: 'Mauris purus urna, ultrices et suscipit ut, faucibus eget mauris.',
132
+ created_at: Time.new(2015, 12, 11, 11, 32, 0),
133
+ },
134
+ {
135
+ id: 16,
136
+ title: 'Vestibulum Commodo',
137
+ body: 'Vestibulum commodo fringilla suscipit.',
138
+ created_at: Time.new(2016, 2, 6, 18, 45, 0),
139
+ },
140
+ ]
141
+ }
142
+ end
86
143
 
87
- before do
88
- allow(dummy_class).to receive(:build_collection_from_response) { dummy }
89
- allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
144
+ let!(:expected_request) do
145
+ mock_request = stub_request(:get, 'https://www.example.com/posts.json')
146
+ mock_request.to_return(status: 200, body: response_body.to_json)
147
+ mock_request
90
148
  end
91
149
 
92
- it 'performs a RemoteResource::Request with the connection_options collection' do
93
- expect(RemoteResource::Request).to receive(:new).with(dummy_class, :get, {}, { collection: true }).and_call_original
94
- expect_any_instance_of(RemoteResource::Request).to receive(:perform)
95
- dummy_class.all
150
+ it 'performs the HTTP request' do
151
+ Post.all
152
+ expect(expected_request).to have_been_requested
96
153
  end
97
154
 
98
- it 'builds the resources from the RemoteResource::Response' do
99
- expect(dummy_class).to receive(:build_collection_from_response).with response
100
- dummy_class.all
155
+ it 'builds the correct collection of resources' do
156
+ posts = Post.all
157
+
158
+ aggregate_failures do
159
+ expect(posts).to respond_to :each
160
+ expect(posts).to all(be_a(Post))
161
+ expect(posts.size).to eql 3
162
+
163
+ expect(posts[0].id).to eql 12
164
+ expect(posts[0].title).to eql 'Lorem Ipsum'
165
+ expect(posts[0].body).to eql 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
166
+ expect(posts[0].created_at).to eql Time.new(2015, 10, 4, 9, 30, 0)
167
+ expect(posts[1].id).to eql 14
168
+ expect(posts[1].title).to eql 'Mauris Purus'
169
+ expect(posts[1].body).to eql 'Mauris purus urna, ultrices et suscipit ut, faucibus eget mauris.'
170
+ expect(posts[1].created_at).to eql Time.new(2015, 12, 11, 11, 32, 0)
171
+ expect(posts[2].id).to eql 16
172
+ expect(posts[2].title).to eql 'Vestibulum Commodo'
173
+ expect(posts[2].body).to eql 'Vestibulum commodo fringilla suscipit.'
174
+ expect(posts[2].created_at).to eql Time.new(2016, 2, 6, 18, 45, 0)
175
+ end
101
176
  end
102
177
 
103
178
  it 'does NOT change the given connection_options' do
104
179
  connection_options = { headers: { 'Foo' => 'Bar' } }
105
180
 
106
- expect { dummy_class.all(connection_options) }.not_to change { connection_options }.from({ headers: { 'Foo' => 'Bar' } })
181
+ expect { Post.all(connection_options) }.not_to change { connection_options }.from(connection_options.dup)
182
+ end
183
+
184
+ xcontext 'return value #success? and NOT #success?' do
107
185
  end
108
186
  end
109
187
 
110
188
  describe '.where' do
111
- let(:response) { instance_double(RemoteResource::Response) }
112
- let(:params) do
113
- { username: 'mies' }
189
+ let(:response_body) do
190
+ {
191
+ data: [
192
+ {
193
+ id: 12,
194
+ title: 'Lorem Ipsum',
195
+ body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
196
+ created_at: Time.new(2015, 10, 4, 9, 30, 0),
197
+ },
198
+ {
199
+ id: 14,
200
+ title: 'Mauris Purus',
201
+ body: 'Mauris purus urna, ultrices et suscipit ut, faucibus eget mauris.',
202
+ created_at: Time.new(2015, 12, 11, 11, 32, 0),
203
+ },
204
+ {
205
+ id: 16,
206
+ title: 'Vestibulum Commodo',
207
+ body: 'Vestibulum commodo fringilla suscipit.',
208
+ created_at: Time.new(2016, 2, 6, 18, 45, 0),
209
+ },
210
+ ]
211
+ }
212
+ end
213
+
214
+ let!(:expected_request) do
215
+ mock_request = stub_request(:get, 'https://www.example.com/posts.json')
216
+ mock_request.with(query: { pseudonym: 'pseudonym' })
217
+ mock_request.to_return(status: 200, body: response_body.to_json)
218
+ mock_request
114
219
  end
115
220
 
116
- before do
117
- allow(dummy_class).to receive(:build_collection_from_response) { dummy }
118
- allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
221
+ it 'performs the HTTP request' do
222
+ Post.where({ pseudonym: 'pseudonym' })
223
+ expect(expected_request).to have_been_requested
119
224
  end
120
225
 
121
- it 'performs a RemoteResource::Request with the connection_options collection' do
122
- expect(RemoteResource::Request).to receive(:new).with(dummy_class, :get, params, { collection: true }).and_call_original
123
- expect_any_instance_of(RemoteResource::Request).to receive(:perform)
124
- dummy_class.where params
226
+ it 'builds the correct collection of resources' do
227
+ posts = Post.where({ pseudonym: 'pseudonym' })
228
+
229
+ aggregate_failures do
230
+ expect(posts).to respond_to :each
231
+ expect(posts).to all(be_a(Post))
232
+ expect(posts.size).to eql 3
233
+
234
+ expect(posts[0].id).to eql 12
235
+ expect(posts[0].title).to eql 'Lorem Ipsum'
236
+ expect(posts[0].body).to eql 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
237
+ expect(posts[0].created_at).to eql Time.new(2015, 10, 4, 9, 30, 0)
238
+ expect(posts[1].id).to eql 14
239
+ expect(posts[1].title).to eql 'Mauris Purus'
240
+ expect(posts[1].body).to eql 'Mauris purus urna, ultrices et suscipit ut, faucibus eget mauris.'
241
+ expect(posts[1].created_at).to eql Time.new(2015, 12, 11, 11, 32, 0)
242
+ expect(posts[2].id).to eql 16
243
+ expect(posts[2].title).to eql 'Vestibulum Commodo'
244
+ expect(posts[2].body).to eql 'Vestibulum commodo fringilla suscipit.'
245
+ expect(posts[2].created_at).to eql Time.new(2016, 2, 6, 18, 45, 0)
246
+ end
125
247
  end
126
248
 
127
- it 'builds the resources from the RemoteResource::Response' do
128
- expect(dummy_class).to receive(:build_collection_from_response).with response
129
- dummy_class.where params
249
+ it 'does NOT change the given params' do
250
+ params = { pseudonym: 'pseudonym' }
251
+
252
+ expect { Post.where(params) }.not_to change { params }.from(params.dup)
130
253
  end
131
254
 
132
255
  it 'does NOT change the given connection_options' do
256
+ params = { pseudonym: 'pseudonym' }
133
257
  connection_options = { headers: { 'Foo' => 'Bar' } }
134
258
 
135
- expect { dummy_class.where(params, connection_options) }.not_to change { connection_options }.from({ headers: { 'Foo' => 'Bar' } })
259
+ expect { Post.where(params, connection_options) }.not_to change { connection_options }.from(connection_options.dup)
260
+ end
261
+
262
+ xcontext 'return value #success? and NOT #success?' do
136
263
  end
137
264
  end
138
265
 
@@ -1,342 +1,350 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe RemoteResource::Querying::PersistenceMethods do
3
+ RSpec.describe RemoteResource::Querying::PersistenceMethods do
4
4
 
5
- module RemoteResource
6
- module Querying
7
- class PersistenceMethodsDummy
8
- include RemoteResource::Base
5
+ class Post
6
+ include RemoteResource::Base
9
7
 
10
- self.site = 'https://foobar.com'
8
+ self.site = 'https://www.example.com'
9
+ self.collection = true
10
+ self.root_element = :data
11
11
 
12
- attribute :name
13
-
14
- end
15
- end
12
+ attribute :title, String
13
+ attribute :body, String
14
+ attribute :featured, Boolean
15
+ attribute :created_at, Time
16
16
  end
17
17
 
18
- let(:dummy_class) { RemoteResource::Querying::PersistenceMethodsDummy }
19
- let(:dummy) { dummy_class.new }
20
-
21
18
  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
19
+ let(:response_body) do
20
+ {
21
+ data: {
22
+ id: 12,
23
+ title: 'Lorem Ipsum',
24
+ body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
25
+ featured: true,
26
+ created_at: Time.new(2015, 10, 4, 9, 30, 0),
27
+ }
28
+ }
29
+ end
30
+
31
+ let!(:expected_request) do
32
+ mock_request = stub_request(:post, 'https://www.example.com/posts.json')
33
+ mock_request.to_return(status: 201, body: JSON.generate(response_body))
34
+ mock_request
35
+ end
36
+
37
+ it 'performs the HTTP request' do
38
+ Post.create(title: 'Lorem Ipsum', body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', featured: true)
39
+ expect(expected_request).to have_been_requested
40
+ end
41
+
42
+ it 'builds the correct resource' do
43
+ post = Post.create(title: 'Lorem Ipsum', body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', featured: true)
44
+
45
+ aggregate_failures do
46
+ expect(post.persisted?).to eql true
47
+ expect(post.id).to eql 12
48
+ expect(post.title).to eql 'Lorem Ipsum'
49
+ expect(post.body).to eql 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
50
+ expect(post.featured).to eql true
51
+ expect(post.created_at).to eql Time.new(2015, 10, 4, 9, 30, 0)
52
+ end
46
53
  end
47
54
 
48
55
  it 'does NOT change the given attributes' do
49
- expect { dummy_class.create(attributes) }.not_to change { attributes }.from({ name: 'Mies' })
56
+ attributes = { title: 'Lorem Ipsum', body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', featured: true }
57
+
58
+ expect { Post.create(attributes) }.not_to change { attributes }.from(attributes.dup)
50
59
  end
51
60
 
52
61
  it 'does NOT change the given connection_options' do
62
+ attributes = { title: 'Lorem Ipsum', body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', featured: true }
53
63
  connection_options = { headers: { 'Foo' => 'Bar' } }
54
64
 
55
- expect { dummy_class.create(attributes, connection_options) }.not_to change { connection_options }.from({ headers: { 'Foo' => 'Bar' } })
65
+ expect { Post.create(attributes, connection_options) }.not_to change { connection_options }.from(connection_options.dup)
66
+ end
67
+
68
+ xcontext 'return value #success? and NOT #success?' do
56
69
  end
57
70
  end
58
71
 
59
72
  describe '.destroy' do
60
- let(:response) { instance_double(RemoteResource::Response) }
61
-
62
- before do
63
- allow_any_instance_of(dummy_class).to receive(:handle_response)
64
- allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
73
+ let(:response_body) do
74
+ {}
65
75
  end
66
76
 
67
- it 'instantiates the resource without attributes' do
68
- expect(dummy_class).to receive(:new).with(no_args()).and_call_original
69
- dummy_class.destroy('15')
77
+ let!(:expected_request) do
78
+ mock_request = stub_request(:delete, 'https://www.example.com/posts/12.json')
79
+ mock_request.to_return(status: 204, body: response_body.to_json)
80
+ mock_request
70
81
  end
71
82
 
72
- it 'performs a RemoteResource::Request' do
73
- expect(RemoteResource::Request).to receive(:new).with(dummy_class, :delete, { id: '15' }, { no_attributes: true }).and_call_original
74
- expect_any_instance_of(RemoteResource::Request).to receive(:perform)
75
- dummy_class.destroy('15')
83
+ it 'performs the HTTP request' do
84
+ Post.destroy(12)
85
+ expect(expected_request).to have_been_requested
76
86
  end
77
87
 
78
- it 'handles the RemoteResource::Response' do
79
- expect_any_instance_of(dummy_class).to receive(:handle_response).with response
80
- dummy_class.destroy('15')
88
+ it 'builds the correct non-persistent resource' do
89
+ post = Post.destroy(12)
90
+
91
+ aggregate_failures do
92
+ expect(post.id).to eql 12
93
+ expect(post.persisted?).to eql false
94
+ end
81
95
  end
82
96
 
83
97
  it 'does NOT change the given connection_options' do
84
98
  connection_options = { headers: { 'Foo' => 'Bar' } }
85
99
 
86
- expect { dummy_class.destroy('15', connection_options) }.not_to change { connection_options }.from({ headers: { 'Foo' => 'Bar' } })
100
+ expect { Post.destroy(12, connection_options) }.not_to change { connection_options }.from(connection_options.dup)
87
101
  end
88
102
 
89
- context 'request' do
90
- let(:response) { { status: 200, body: '' } }
91
- before { allow_any_instance_of(RemoteResource::Request).to receive(:perform).and_call_original }
92
-
93
- it 'generates correct request url' do
94
- stub_request(:delete, 'https://foobar.com/persistence_methods_dummy/15.json').with(body: nil).to_return(response)
95
- dummy_class.destroy('15')
96
- end
97
-
98
- it 'includes params' do
99
- stub_request(:delete, 'https://foobar.com/persistence_methods_dummy/15.json?pseudonym=3s8e3j').with(body: nil).to_return(response)
100
- dummy_class.destroy('15', params: { pseudonym: '3s8e3j' })
101
- end
103
+ xcontext 'return value #success? and NOT #success?' do
102
104
  end
103
105
  end
104
106
 
105
107
  describe '#update_attributes' do
106
- let(:dummy) { dummy_class.new id: 10 }
107
-
108
- let(:attributes) do
109
- { name: 'Noot' }
110
- end
111
-
112
- before do
113
- allow(dummy).to receive(:create_or_update) { dummy }
114
- allow(dummy).to receive(:success?)
115
- end
116
-
117
- it 'rebuilds the resource with the attributes' do
118
- expect(dummy).to receive(:rebuild_resource).with(attributes).and_call_original
119
- dummy.update_attributes attributes
120
- end
121
-
122
- context 'when the id is given in the attributes' do
123
- let(:attributes) do
124
- { id: 14, name: 'Noot' }
108
+ let(:response_body) do
109
+ {
110
+ data: {
111
+ id: 12,
112
+ title: 'Aliquam lobortis',
113
+ body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
114
+ featured: true,
115
+ created_at: Time.new(2015, 10, 4, 9, 30, 0),
116
+ }
117
+ }
118
+ end
119
+
120
+ context 'when resource is persisted' do
121
+ let(:resource) { Post.new(id: 12, title: 'Lorem Ipsum', body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', featured: true, created_at: Time.new(2015, 10, 4, 9, 30, 0)) }
122
+
123
+ let!(:expected_request) do
124
+ mock_request = stub_request(:patch, 'https://www.example.com/posts/12.json')
125
+ mock_request.to_return(status: 201, body: JSON.generate(response_body))
126
+ mock_request
125
127
  end
126
128
 
127
- it 'calls #create_or_update with the attributes and given id' do
128
- expect(dummy).to receive(:create_or_update).with(attributes, {})
129
- dummy.update_attributes attributes
129
+ it 'performs the HTTP request' do
130
+ resource.update_attributes(title: 'Aliquam lobortis', featured: true)
131
+ expect(expected_request).to have_been_requested
130
132
  end
131
133
 
132
- it 'does NOT change the given attributes' do
133
- expect { dummy.update_attributes(attributes) }.not_to change { attributes }.from({ id: 14, name: 'Noot' })
134
- end
134
+ it 'builds the correct resource' do
135
+ resource.update_attributes(title: 'Aliquam lobortis', featured: true)
135
136
 
136
- it 'does NOT change the given connection_options' do
137
- connection_options = { headers: { 'Foo' => 'Bar' } }
138
-
139
- expect { dummy.update_attributes(attributes, connection_options) }.not_to change { connection_options }.from({ headers: { 'Foo' => 'Bar' } })
140
- end
141
- end
137
+ post = resource
142
138
 
143
- context 'when the id is NOT given in the attributes' do
144
- it 'calls #create_or_update with the attributes and #id of resource' do
145
- expect(dummy).to receive(:create_or_update).with(attributes.merge(id: dummy.id), {})
146
- dummy.update_attributes attributes
139
+ aggregate_failures do
140
+ expect(post.persisted?).to eql true
141
+ expect(post.id).to eql 12
142
+ expect(post.title).to eql 'Aliquam lobortis'
143
+ expect(post.body).to eql 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
144
+ expect(post.featured).to eql true
145
+ expect(post.created_at).to eql Time.new(2015, 10, 4, 9, 30, 0)
146
+ end
147
147
  end
148
148
 
149
149
  it 'does NOT change the given attributes' do
150
- expect { dummy.update_attributes(attributes) }.not_to change { attributes }.from({ name: 'Noot' })
150
+ attributes = { title: 'Aliquam lobortis', featured: true }
151
+
152
+ expect { resource.update_attributes(attributes) }.not_to change { attributes }.from(attributes.dup)
151
153
  end
152
154
 
153
155
  it 'does NOT change the given connection_options' do
156
+ attributes = { title: 'Aliquam lobortis', featured: true }
154
157
  connection_options = { headers: { 'Foo' => 'Bar' } }
155
158
 
156
- expect { dummy.update_attributes(attributes, connection_options) }.not_to change { connection_options }.from({ headers: { 'Foo' => 'Bar' } })
159
+ expect { resource.update_attributes(attributes, connection_options) }.not_to change { connection_options }.from(connection_options.dup)
157
160
  end
158
- end
159
-
160
- context 'when the save was successful' do
161
- it 'returns the resource' do
162
- allow(dummy).to receive(:success?) { true }
163
161
 
164
- expect(dummy.update_attributes attributes).to eql dummy
162
+ xcontext 'return value #success? and NOT #success?' do
165
163
  end
166
164
  end
167
165
 
168
- context 'when the save was NOT successful' do
169
- it 'returns false' do
170
- allow(dummy).to receive(:success?) { false }
166
+ context 'when resource is NOT persisted' do
167
+ let(:resource) { Post.new(title: 'Lorem Ipsum', body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', featured: true) }
171
168
 
172
- expect(dummy.update_attributes attributes).to eql false
169
+ let!(:expected_request) do
170
+ mock_request = stub_request(:post, 'https://www.example.com/posts.json')
171
+ mock_request.to_return(status: 201, body: JSON.generate(response_body))
172
+ mock_request
173
173
  end
174
- end
175
- end
176
174
 
177
- describe '#save' do
178
- let(:attributes) { dummy.attributes }
175
+ it 'performs the HTTP request' do
176
+ resource.update_attributes(title: 'Aliquam lobortis', featured: true)
177
+ expect(expected_request).to have_been_requested
178
+ end
179
179
 
180
- before do
181
- allow(dummy).to receive(:create_or_update) { dummy }
182
- allow(dummy).to receive(:success?)
183
- end
180
+ it 'builds the correct resource' do
181
+ resource.update_attributes(title: 'Aliquam lobortis', featured: true)
184
182
 
185
- it 'calls #create_or_update with the attributes' do
186
- expect(dummy).to receive(:create_or_update).with(attributes, {})
187
- dummy.save
188
- end
183
+ post = resource
189
184
 
190
- it 'does NOT change the given connection_options' do
191
- connection_options = { headers: { 'Foo' => 'Bar' } }
192
-
193
- expect { dummy.save(connection_options) }.not_to change { connection_options }.from({ headers: { 'Foo' => 'Bar' } })
194
- end
185
+ aggregate_failures do
186
+ expect(post.persisted?).to eql true
187
+ expect(post.id).to eql 12
188
+ expect(post.title).to eql 'Aliquam lobortis'
189
+ expect(post.body).to eql 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
190
+ expect(post.featured).to eql true
191
+ expect(post.created_at).to eql Time.new(2015, 10, 4, 9, 30, 0)
192
+ end
193
+ end
195
194
 
196
- context 'when the save was successful' do
197
- it 'returns the resource' do
198
- allow(dummy).to receive(:success?) { true }
195
+ it 'does NOT change the given attributes' do
196
+ attributes = { title: 'Aliquam lobortis', featured: true }
199
197
 
200
- expect(dummy.save).to eql dummy
198
+ expect { resource.update_attributes(attributes) }.not_to change { attributes }.from(attributes.dup)
201
199
  end
202
- end
203
200
 
204
- context 'when the save was NOT successful' do
205
- it 'returns false' do
206
- allow(dummy).to receive(:success?) { false }
201
+ it 'does NOT change the given connection_options' do
202
+ attributes = { title: 'Aliquam lobortis', featured: true }
203
+ connection_options = { headers: { 'Foo' => 'Bar' } }
207
204
 
208
- expect(dummy.save).to eql false
205
+ expect { resource.update_attributes(attributes, connection_options) }.not_to change { connection_options }.from(connection_options.dup)
209
206
  end
210
- end
211
- end
212
-
213
- describe '#create_or_update' do
214
- let(:response) { instance_double(RemoteResource::Response) }
215
207
 
216
- before do
217
- allow(dummy).to receive(:handle_response) { dummy }
218
- allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
208
+ xcontext 'return value #success? and NOT #success?' do
209
+ end
219
210
  end
211
+ end
220
212
 
221
- context 'when the attributes contain an id' do
222
- let(:attributes) do
223
- { id: 10, name: 'Kees' }
213
+ describe '#save' do
214
+ let(:response_body) do
215
+ {
216
+ data: {
217
+ id: 12,
218
+ title: 'Lorem Ipsum',
219
+ body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
220
+ featured: false,
221
+ created_at: Time.new(2015, 10, 4, 9, 30, 0),
222
+ }
223
+ }
224
+ end
225
+
226
+ context 'when resource is persisted' do
227
+ let(:resource) { Post.new(id: 12, title: 'Lorem Ipsum', body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', featured: false, created_at: Time.new(2015, 10, 4, 9, 30, 0)) }
228
+
229
+ let!(:expected_request) do
230
+ mock_request = stub_request(:patch, 'https://www.example.com/posts/12.json')
231
+ mock_request.to_return(status: 200, body: JSON.generate(response_body))
232
+ mock_request
224
233
  end
225
234
 
226
- it 'performs a RemoteResource::Request with rest_action :patch' do
227
- expect(RemoteResource::Request).to receive(:new).with(dummy, :patch, attributes, {}).and_call_original
228
- expect_any_instance_of(RemoteResource::Request).to receive(:perform)
229
- dummy.create_or_update attributes
235
+ it 'performs the HTTP request' do
236
+ resource.save
237
+ expect(expected_request).to have_been_requested
230
238
  end
231
239
 
232
- it 'handles the RemoteResource::Response' do
233
- expect(dummy).to receive(:handle_response).with response
234
- dummy.create_or_update attributes
235
- end
240
+ it 'builds the correct resource' do
241
+ resource.save
236
242
 
237
- it 'does NOT change the given attributes' do
238
- expect { dummy.create_or_update(attributes) }.not_to change { attributes }.from({ id: 10, name: 'Kees' })
243
+ post = resource
244
+
245
+ aggregate_failures do
246
+ expect(post.persisted?).to eql true
247
+ expect(post.id).to eql 12
248
+ expect(post.title).to eql 'Lorem Ipsum'
249
+ expect(post.body).to eql 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
250
+ expect(post.featured).to eql false
251
+ expect(post.created_at).to eql Time.new(2015, 10, 4, 9, 30, 0)
252
+ end
239
253
  end
240
254
 
241
255
  it 'does NOT change the given connection_options' do
242
256
  connection_options = { headers: { 'Foo' => 'Bar' } }
243
257
 
244
- expect { dummy.create_or_update(attributes, connection_options) }.not_to change { connection_options }.from({ headers: { 'Foo' => 'Bar' } })
258
+ expect { resource.save(connection_options) }.not_to change { connection_options }.from(connection_options.dup)
245
259
  end
246
- end
247
260
 
248
- context 'when the attributes do NOT contain an id' do
249
- let(:attributes) do
250
- { name: 'Mies' }
261
+ xcontext 'return value #success? and NOT #success?' do
251
262
  end
263
+ end
252
264
 
253
- it 'performs a RemoteResource::Request with rest_action :post' do
254
- expect(RemoteResource::Request).to receive(:new).with(dummy, :post, attributes, {}).and_call_original
255
- expect_any_instance_of(RemoteResource::Request).to receive(:perform)
256
- dummy.create_or_update attributes
265
+ context 'when resource is NOT persisted' do
266
+ let(:resource) { Post.new(title: 'Lorem Ipsum', body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', featured: false) }
267
+
268
+ let!(:expected_request) do
269
+ mock_request = stub_request(:post, 'https://www.example.com/posts.json')
270
+ mock_request.to_return(status: 201, body: JSON.generate(response_body))
271
+ mock_request
257
272
  end
258
273
 
259
- it 'handles the RemoteResource::Response' do
260
- expect(dummy).to receive(:handle_response).with response
261
- dummy.create_or_update attributes
274
+ it 'performs the HTTP request' do
275
+ resource.save
276
+ expect(expected_request).to have_been_requested
262
277
  end
263
278
 
264
- it 'does NOT change the given attributes' do
265
- expect { dummy.create_or_update(attributes) }.not_to change { attributes }.from({ name: 'Mies' })
279
+ it 'builds the correct resource' do
280
+ resource.save
281
+
282
+ post = resource
283
+
284
+ aggregate_failures do
285
+ expect(post.persisted?).to eql true
286
+ expect(post.id).to eql 12
287
+ expect(post.title).to eql 'Lorem Ipsum'
288
+ expect(post.body).to eql 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
289
+ expect(post.featured).to eql false
290
+ expect(post.created_at).to eql Time.new(2015, 10, 4, 9, 30, 0)
291
+ end
266
292
  end
267
293
 
268
294
  it 'does NOT change the given connection_options' do
269
295
  connection_options = { headers: { 'Foo' => 'Bar' } }
270
296
 
271
- expect { dummy.create_or_update(attributes, connection_options) }.not_to change { connection_options }.from({ headers: { 'Foo' => 'Bar' } })
297
+ expect { resource.save(connection_options) }.not_to change { connection_options }.from(connection_options.dup)
298
+ end
299
+
300
+ xcontext 'return value #success? and NOT #success?' do
272
301
  end
273
302
  end
274
303
  end
275
304
 
276
305
  describe '#destroy' do
277
- let(:dummy) { dummy_class.new(id: 18) }
306
+ let(:resource) { Post.new(id: 12) }
278
307
 
279
- let(:response) { instance_double(RemoteResource::Response) }
280
-
281
- before do
282
- allow(dummy).to receive(:handle_response) { dummy }
283
- allow_any_instance_of(RemoteResource::Request).to receive(:perform) { response }
284
- allow(dummy).to receive(:success?)
308
+ let(:response_body) do
309
+ {}
285
310
  end
286
311
 
287
- it 'performs a RemoteResource::Request with rest_action :delete' do
288
- expect(RemoteResource::Request).to receive(:new).with(dummy, :delete, { id: dummy.id }, { no_attributes: true }).and_call_original
289
- expect_any_instance_of(RemoteResource::Request).to receive(:perform)
290
- dummy.destroy
312
+ let!(:expected_request) do
313
+ mock_request = stub_request(:delete, 'https://www.example.com/posts/12.json')
314
+ mock_request.to_return(status: 204, body: response_body.to_json)
315
+ mock_request
291
316
  end
292
317
 
293
- it 'handles the RemoteResource::Response' do
294
- expect(dummy).to receive(:handle_response).with response
295
- dummy.destroy
318
+ it 'performs the HTTP request' do
319
+ resource.destroy
320
+ expect(expected_request).to have_been_requested
296
321
  end
297
322
 
298
- it 'does NOT change the given connection_options' do
299
- connection_options = { headers: { 'Foo' => 'Bar' } }
300
-
301
- expect { dummy.destroy(connection_options) }.not_to change { connection_options }.from({ headers: { 'Foo' => 'Bar' } })
302
- end
323
+ it 'builds the correct non-persistent resource' do
324
+ resource.destroy
303
325
 
304
- context 'request' do
305
- let(:response) { { status: 200, body: '' } }
306
- before { allow_any_instance_of(RemoteResource::Request).to receive(:perform).and_call_original }
307
-
308
- it 'generates correct request url' do
309
- stub_request(:delete, 'https://foobar.com/persistence_methods_dummy/18.json').with(body: nil).to_return(response)
310
- dummy.destroy
311
- end
326
+ post = resource
312
327
 
313
- it 'includes params' do
314
- stub_request(:delete, 'https://foobar.com/persistence_methods_dummy/18.json?pseudonym=3s8e3j').with(body: nil).to_return(response)
315
- dummy.destroy(params: { pseudonym: '3s8e3j' })
328
+ aggregate_failures do
329
+ expect(post.id).to eql 12
330
+ expect(post.persisted?).to eql false
316
331
  end
317
332
  end
318
333
 
319
- context 'when the destroy was successful' do
320
- it 'returns the resource' do
321
- allow(dummy).to receive(:success?) { true }
334
+ it 'does NOT change the given connection_options' do
335
+ connection_options = { headers: { 'Foo' => 'Bar' } }
322
336
 
323
- expect(dummy.destroy).to eql dummy
324
- end
337
+ expect { resource.destroy(connection_options) }.not_to change { connection_options }.from(connection_options.dup)
325
338
  end
326
339
 
327
- context 'when the destroy was NOT successful' do
328
- it 'returns false' do
329
- allow(dummy).to receive(:success?) { false }
330
-
331
- expect(dummy.destroy).to eql false
332
- end
340
+ xcontext 'return value #success? and NOT #success?' do
333
341
  end
334
342
 
335
343
  context 'when the id is NOT present' do
336
- let(:dummy) { dummy_class.new }
344
+ let(:resource) { Post.new }
337
345
 
338
346
  it 'raises the RemoteResource::IdMissingError error' do
339
- expect { dummy.destroy }.to raise_error(RemoteResource::IdMissingError, "`id` is missing from resource")
347
+ expect { resource.destroy }.to raise_error(RemoteResource::IdMissingError, "`id` is missing from resource")
340
348
  end
341
349
  end
342
350
  end