restorm 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/.rubocop.yml +31 -0
- data/.rubocop_todo.yml +232 -0
- data/.ruby-version +1 -0
- data/.travis.yml +55 -0
- data/.yardopts +2 -0
- data/CONTRIBUTING.md +26 -0
- data/Gemfile +10 -0
- data/HER_README.md +1065 -0
- data/LICENSE +7 -0
- data/README.md +7 -0
- data/Rakefile +11 -0
- data/UPGRADE.md +101 -0
- data/gemfiles/Gemfile.activemodel-4.2 +6 -0
- data/gemfiles/Gemfile.activemodel-5.0 +6 -0
- data/gemfiles/Gemfile.activemodel-5.1 +6 -0
- data/gemfiles/Gemfile.activemodel-5.2 +6 -0
- data/gemfiles/Gemfile.faraday-1.0 +6 -0
- data/lib/restorm/api.rb +121 -0
- data/lib/restorm/collection.rb +13 -0
- data/lib/restorm/errors.rb +29 -0
- data/lib/restorm/json_api/model.rb +42 -0
- data/lib/restorm/middleware/accept_json.rb +18 -0
- data/lib/restorm/middleware/first_level_parse_json.rb +37 -0
- data/lib/restorm/middleware/json_api_parser.rb +37 -0
- data/lib/restorm/middleware/parse_json.rb +22 -0
- data/lib/restorm/middleware/second_level_parse_json.rb +37 -0
- data/lib/restorm/middleware.rb +12 -0
- data/lib/restorm/model/associations/association.rb +128 -0
- data/lib/restorm/model/associations/association_proxy.rb +44 -0
- data/lib/restorm/model/associations/belongs_to_association.rb +95 -0
- data/lib/restorm/model/associations/has_many_association.rb +100 -0
- data/lib/restorm/model/associations/has_one_association.rb +79 -0
- data/lib/restorm/model/associations.rb +141 -0
- data/lib/restorm/model/attributes.rb +322 -0
- data/lib/restorm/model/base.rb +33 -0
- data/lib/restorm/model/deprecated_methods.rb +61 -0
- data/lib/restorm/model/http.rb +119 -0
- data/lib/restorm/model/introspection.rb +67 -0
- data/lib/restorm/model/nested_attributes.rb +45 -0
- data/lib/restorm/model/orm.rb +299 -0
- data/lib/restorm/model/parse.rb +223 -0
- data/lib/restorm/model/paths.rb +125 -0
- data/lib/restorm/model/relation.rb +209 -0
- data/lib/restorm/model.rb +75 -0
- data/lib/restorm/version.rb +3 -0
- data/lib/restorm.rb +19 -0
- data/restorm.gemspec +29 -0
- data/spec/api_spec.rb +120 -0
- data/spec/collection_spec.rb +41 -0
- data/spec/json_api/model_spec.rb +169 -0
- data/spec/middleware/accept_json_spec.rb +11 -0
- data/spec/middleware/first_level_parse_json_spec.rb +63 -0
- data/spec/middleware/json_api_parser_spec.rb +52 -0
- data/spec/middleware/second_level_parse_json_spec.rb +35 -0
- data/spec/model/associations/association_proxy_spec.rb +29 -0
- data/spec/model/associations_spec.rb +911 -0
- data/spec/model/attributes_spec.rb +354 -0
- data/spec/model/callbacks_spec.rb +176 -0
- data/spec/model/dirty_spec.rb +133 -0
- data/spec/model/http_spec.rb +201 -0
- data/spec/model/introspection_spec.rb +81 -0
- data/spec/model/nested_attributes_spec.rb +135 -0
- data/spec/model/orm_spec.rb +704 -0
- data/spec/model/parse_spec.rb +520 -0
- data/spec/model/paths_spec.rb +348 -0
- data/spec/model/relation_spec.rb +247 -0
- data/spec/model/validations_spec.rb +43 -0
- data/spec/model_spec.rb +45 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/macros/her_macros.rb +17 -0
- data/spec/support/macros/model_macros.rb +36 -0
- data/spec/support/macros/request_macros.rb +27 -0
- metadata +203 -0
@@ -0,0 +1,704 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), "../spec_helper.rb")
|
4
|
+
|
5
|
+
describe Restorm::Model::ORM do
|
6
|
+
context "mapping data to Ruby objects" do
|
7
|
+
before do
|
8
|
+
api = Restorm::API.new
|
9
|
+
api.setup url: "https://api.example.com" do |builder|
|
10
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
11
|
+
builder.use Faraday::Request::UrlEncoded
|
12
|
+
builder.adapter :test do |stub|
|
13
|
+
stub.get("/users/1") { [200, {}, { id: 1, name: "Tobias Fünke" }.to_json] }
|
14
|
+
stub.get("/users") { [200, {}, [{ id: 1, name: "Tobias Fünke" }, { id: 2, name: "Lindsay Fünke" }].to_json] }
|
15
|
+
stub.get("/admin_users") { [200, {}, [{ admin_id: 1, name: "Tobias Fünke" }, { admin_id: 2, name: "Lindsay Fünke" }].to_json] }
|
16
|
+
stub.get("/admin_users/1") { [200, {}, { admin_id: 1, name: "Tobias Fünke" }.to_json] }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
spawn_model "Foo::User" do
|
21
|
+
uses_api api
|
22
|
+
end
|
23
|
+
|
24
|
+
spawn_model "Foo::AdminUser" do
|
25
|
+
uses_api api
|
26
|
+
primary_key :admin_id
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it "maps a single resource to a Ruby object" do
|
31
|
+
@user = Foo::User.find(1)
|
32
|
+
expect(@user.id).to eq(1)
|
33
|
+
expect(@user.name).to eq("Tobias Fünke")
|
34
|
+
|
35
|
+
@admin = Foo::AdminUser.find(1)
|
36
|
+
expect(@admin.id).to eq(1)
|
37
|
+
expect(@admin.name).to eq("Tobias Fünke")
|
38
|
+
end
|
39
|
+
|
40
|
+
it "maps a collection of resources to an array of Ruby objects" do
|
41
|
+
@users = Foo::User.all
|
42
|
+
expect(@users.length).to eq(2)
|
43
|
+
expect(@users.first.name).to eq("Tobias Fünke")
|
44
|
+
|
45
|
+
@users = Foo::AdminUser.all
|
46
|
+
expect(@users.length).to eq(2)
|
47
|
+
expect(@users.first.name).to eq("Tobias Fünke")
|
48
|
+
end
|
49
|
+
|
50
|
+
it "handles new resource" do
|
51
|
+
@new_user = Foo::User.new(fullname: "Tobias Fünke")
|
52
|
+
expect(@new_user.new?).to be_truthy
|
53
|
+
expect(@new_user.new_record?).to be_truthy
|
54
|
+
expect(@new_user.fullname).to eq("Tobias Fünke")
|
55
|
+
|
56
|
+
@existing_user = Foo::User.find(1)
|
57
|
+
expect(@existing_user.new?).to be_falsey
|
58
|
+
expect(@existing_user.new_record?).to be_falsey
|
59
|
+
end
|
60
|
+
|
61
|
+
it "handles new resource with custom primary key" do
|
62
|
+
@new_user = Foo::AdminUser.new(fullname: "Lindsay Fünke", id: -1)
|
63
|
+
expect(@new_user).to be_new
|
64
|
+
|
65
|
+
@existing_user = Foo::AdminUser.find(1)
|
66
|
+
expect(@existing_user).not_to be_new
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "mapping data, metadata and error data to Ruby objects" do
|
71
|
+
before do
|
72
|
+
api = Restorm::API.new
|
73
|
+
api.setup url: "https://api.example.com" do |builder|
|
74
|
+
builder.use Restorm::Middleware::SecondLevelParseJSON
|
75
|
+
builder.use Faraday::Request::UrlEncoded
|
76
|
+
builder.adapter :test do |stub|
|
77
|
+
stub.get("/users") { [200, {}, { data: [{ id: 1, name: "Tobias Fünke" }, { id: 2, name: "Lindsay Fünke" }], metadata: { total_pages: 10, next_page: 2 }, errors: %w[Oh My God] }.to_json] }
|
78
|
+
stub.get("/users") { [200, {}, { :data => [{ :id => 1, :name => "Tobias Fünke" }, { :id => 2, :name => "Lindsay Fünke" }], :metadata => { :total_pages => 10, :next_page => 2 }, :errors => ["Oh", "My", "God"] }.to_json] }
|
79
|
+
stub.post("/users") { [200, {}, { :data => { :name => "George Michael Bluth" }, :metadata => { :foo => "bar" }, :errors => ["Yes", "Sir"] }.to_json] }
|
80
|
+
stub.delete("/users/1") { [200, {}, { :data => { :id => 1 }, :metadata => { :foo => "bar" }, :errors => ["Yes", "Sir"] }.to_json] }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
spawn_model :User do
|
85
|
+
uses_api api
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it "handles metadata on a collection" do
|
90
|
+
@users = User.all
|
91
|
+
expect(@users.metadata[:total_pages]).to eq(10)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "handles error data on a collection" do
|
95
|
+
@users = User.all
|
96
|
+
expect(@users.errors.length).to eq(3)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "handles metadata on a resource" do
|
100
|
+
@user = User.create(name: "George Michael Bluth")
|
101
|
+
expect(@user.metadata[:foo]).to eq("bar")
|
102
|
+
end
|
103
|
+
|
104
|
+
it "handles error data on a resource" do
|
105
|
+
@user = User.create(name: "George Michael Bluth")
|
106
|
+
expect(@user.response_errors).to eq(%w[Yes Sir])
|
107
|
+
end
|
108
|
+
|
109
|
+
it "handles metadata on a destroyed resource" do
|
110
|
+
@user = User.destroy_existing(1)
|
111
|
+
expect(@user.metadata[:foo]).to eq("bar")
|
112
|
+
end
|
113
|
+
|
114
|
+
it "handles error data on a destroyed resource" do
|
115
|
+
@user = User.destroy_existing(1)
|
116
|
+
expect(@user.response_errors).to eq(%w[Yes Sir])
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "mapping data, metadata and error data in string keys to Ruby objects" do
|
121
|
+
before do
|
122
|
+
api = Restorm::API.new
|
123
|
+
api.setup url: "https://api.example.com" do |builder|
|
124
|
+
builder.use Restorm::Middleware::SecondLevelParseJSON
|
125
|
+
builder.use Faraday::Request::UrlEncoded
|
126
|
+
builder.adapter :test do |stub|
|
127
|
+
stub.get("/users") { [200, {}, { data: [{ id: 1, name: "Tobias Fünke" }, { id: 2, name: "Lindsay Fünke" }], metadata: { total_pages: 10, next_page: 2 }, errors: %w[Oh My God] }.to_json] }
|
128
|
+
stub.post("/users") { [200, {}, { data: { name: "George Michael Bluth" }, metadata: { foo: "bar" }, errors: %w[Yes Sir] }.to_json] }
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
spawn_model :User do
|
133
|
+
uses_api api
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
it "handles metadata on a collection" do
|
138
|
+
@users = User.all
|
139
|
+
expect(@users.metadata[:total_pages]).to eq(10)
|
140
|
+
end
|
141
|
+
|
142
|
+
it "handles error data on a collection" do
|
143
|
+
@users = User.all
|
144
|
+
expect(@users.errors.length).to eq(3)
|
145
|
+
end
|
146
|
+
|
147
|
+
it "handles metadata on a resource" do
|
148
|
+
@user = User.create(name: "George Michael Bluth")
|
149
|
+
expect(@user.metadata[:foo]).to eq("bar")
|
150
|
+
end
|
151
|
+
|
152
|
+
it "handles error data on a resource" do
|
153
|
+
@user = User.create(name: "George Michael Bluth")
|
154
|
+
expect(@user.response_errors).to eq(%w[Yes Sir])
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "defining custom getters and setters" do
|
159
|
+
before do
|
160
|
+
api = Restorm::API.new
|
161
|
+
api.setup url: "https://api.example.com" do |builder|
|
162
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
163
|
+
builder.use Faraday::Request::UrlEncoded
|
164
|
+
builder.adapter :test do |stub|
|
165
|
+
stub.get("/users/1") { [200, {}, { id: 1, friends: %w[Maeby GOB Anne] }.to_json] }
|
166
|
+
stub.get("/users/2") { [200, {}, { id: 1 }.to_json] }
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
spawn_model :User do
|
171
|
+
uses_api api
|
172
|
+
belongs_to :organization
|
173
|
+
|
174
|
+
def friends=(val)
|
175
|
+
val = val.delete("\r").split("\n").map { |friend| friend.gsub(/^\s*\*\s*/, "") } if val && val.is_a?(String)
|
176
|
+
@_restorm_attributes[:friends] = val
|
177
|
+
end
|
178
|
+
|
179
|
+
def friends
|
180
|
+
@_restorm_attributes[:friends].map { |friend| "* #{friend}" }.join("\n")
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
it "handles custom setters" do
|
186
|
+
@user = User.find(1)
|
187
|
+
expect(@user.friends).to eq("* Maeby\n* GOB\n* Anne")
|
188
|
+
@user.instance_eval do
|
189
|
+
@_restorm_attributes[:friends] = %w[Maeby GOB Anne]
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
it "handles custom getters" do
|
194
|
+
@user = User.new
|
195
|
+
@user.friends = "* George\n* Oscar\n* Lucille"
|
196
|
+
expect(@user.friends).to eq("* George\n* Oscar\n* Lucille")
|
197
|
+
@user.instance_eval do
|
198
|
+
@_restorm_attributes[:friends] = %w[George Oscar Lucille]
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
context "finding resources" do
|
204
|
+
before do
|
205
|
+
api = Restorm::API.new
|
206
|
+
api.setup url: "https://api.example.com" do |builder|
|
207
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
208
|
+
builder.use Faraday::Request::UrlEncoded
|
209
|
+
builder.adapter :test do |stub|
|
210
|
+
stub.get("/users/1") { [200, {}, { id: 1, age: 42 }.to_json] }
|
211
|
+
stub.get("/users/2") { [200, {}, { id: 2, age: 34 }.to_json] }
|
212
|
+
stub.get("/users?id[]=1&id[]=2") { [200, {}, [{ id: 1, age: 42 }, { id: 2, age: 34 }].to_json] }
|
213
|
+
stub.get("/users?age=42&foo=bar") { [200, {}, [{ id: 3, age: 42 }].to_json] }
|
214
|
+
stub.get("/users?age=42") { [200, {}, [{ id: 1, age: 42 }].to_json] }
|
215
|
+
stub.get("/users?age=40") { [200, {}, [{ id: 1, age: 40 }].to_json] }
|
216
|
+
stub.get("/users?name=baz") { [200, {}, [].to_json] }
|
217
|
+
stub.post("/users") { [200, {}, { id: 5, name: "baz" }.to_json] }
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
spawn_model :User do
|
222
|
+
uses_api api
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
it "handles finding by a single id" do
|
227
|
+
@user = User.find(1)
|
228
|
+
expect(@user.id).to eq(1)
|
229
|
+
end
|
230
|
+
|
231
|
+
it "handles finding by multiple ids" do
|
232
|
+
@users = User.find(1, 2)
|
233
|
+
expect(@users).to be_kind_of(Array)
|
234
|
+
expect(@users.length).to eq(2)
|
235
|
+
expect(@users[0].id).to eq(1)
|
236
|
+
expect(@users[1].id).to eq(2)
|
237
|
+
end
|
238
|
+
|
239
|
+
it "handles finding by an array of ids" do
|
240
|
+
@users = User.find([1, 2])
|
241
|
+
expect(@users).to be_kind_of(Array)
|
242
|
+
expect(@users.length).to eq(2)
|
243
|
+
expect(@users[0].id).to eq(1)
|
244
|
+
expect(@users[1].id).to eq(2)
|
245
|
+
end
|
246
|
+
|
247
|
+
it "handles finding by an array of ids of length 1" do
|
248
|
+
@users = User.find([1])
|
249
|
+
expect(@users).to be_kind_of(Array)
|
250
|
+
expect(@users.length).to eq(1)
|
251
|
+
expect(@users[0].id).to eq(1)
|
252
|
+
end
|
253
|
+
|
254
|
+
it "handles finding by an array id param of length 2" do
|
255
|
+
@users = User.find(id: [1, 2])
|
256
|
+
expect(@users).to be_kind_of(Array)
|
257
|
+
expect(@users.length).to eq(2)
|
258
|
+
expect(@users[0].id).to eq(1)
|
259
|
+
expect(@users[1].id).to eq(2)
|
260
|
+
end
|
261
|
+
|
262
|
+
it "handles finding with id parameter as an array" do
|
263
|
+
@users = User.where(id: [1, 2])
|
264
|
+
expect(@users).to be_kind_of(Array)
|
265
|
+
expect(@users.length).to eq(2)
|
266
|
+
expect(@users[0].id).to eq(1)
|
267
|
+
expect(@users[1].id).to eq(2)
|
268
|
+
end
|
269
|
+
|
270
|
+
it "handles finding by attributes" do
|
271
|
+
@user = User.find_by(age: 42)
|
272
|
+
expect(@user).to be_a(User)
|
273
|
+
expect(@user.id).to eq(1)
|
274
|
+
end
|
275
|
+
|
276
|
+
it "handles find or create by attributes" do
|
277
|
+
@user = User.find_or_create_by(name: "baz")
|
278
|
+
expect(@user).to be_a(User)
|
279
|
+
expect(@user.id).to eq(5)
|
280
|
+
end
|
281
|
+
|
282
|
+
it "handles find or initialize by attributes" do
|
283
|
+
@user = User.find_or_initialize_by(name: "baz")
|
284
|
+
expect(@user).to be_a(User)
|
285
|
+
expect(@user).to_not be_persisted
|
286
|
+
end
|
287
|
+
|
288
|
+
it "handles finding with other parameters" do
|
289
|
+
@users = User.where(age: 42, foo: "bar").all
|
290
|
+
expect(@users).to be_kind_of(Array)
|
291
|
+
expect(@users.first.id).to eq(3)
|
292
|
+
end
|
293
|
+
|
294
|
+
it "handles finding with other parameters and scoped" do
|
295
|
+
@users = User.scoped
|
296
|
+
expect(@users.where(age: 42)).to be_all { |u| u.age == 42 }
|
297
|
+
expect(@users.where(age: 40)).to be_all { |u| u.age == 40 }
|
298
|
+
end
|
299
|
+
|
300
|
+
it "handles reloading a resource" do
|
301
|
+
@user = User.find(1)
|
302
|
+
@user.age = "Oops"
|
303
|
+
@user.reload
|
304
|
+
expect(@user.age).to eq 42
|
305
|
+
expect(@user).to be_persisted
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
context "building resources" do
|
310
|
+
context "when request_new_object_on_build is not set (default)" do
|
311
|
+
before do
|
312
|
+
spawn_model("Foo::User")
|
313
|
+
end
|
314
|
+
|
315
|
+
it "builds a new resource without requesting it" do
|
316
|
+
expect(Foo::User).not_to receive(:request)
|
317
|
+
@new_user = Foo::User.build(fullname: "Tobias Fünke")
|
318
|
+
expect(@new_user.new?).to be_truthy
|
319
|
+
expect(@new_user.fullname).to eq("Tobias Fünke")
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
context "when request_new_object_on_build is set" do
|
324
|
+
before do
|
325
|
+
Restorm::API.setup url: "https://api.example.com" do |builder|
|
326
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
327
|
+
builder.use Faraday::Request::UrlEncoded
|
328
|
+
builder.adapter :test do |stub|
|
329
|
+
stub.get("/users/new") { |env| ok! id: nil, fullname: params(env)[:fullname], email: "tobias@bluthcompany.com" }
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
spawn_model("Foo::User") { request_new_object_on_build true }
|
334
|
+
end
|
335
|
+
|
336
|
+
it "requests a new resource" do
|
337
|
+
expect(Foo::User).to receive(:request).once.and_call_original
|
338
|
+
@new_user = Foo::User.build(fullname: "Tobias Fünke")
|
339
|
+
expect(@new_user.new?).to be_truthy
|
340
|
+
expect(@new_user.fullname).to eq("Tobias Fünke")
|
341
|
+
expect(@new_user.email).to eq("tobias@bluthcompany.com")
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
context "creating resources" do
|
347
|
+
before do
|
348
|
+
Restorm::API.setup url: "https://api.example.com" do |builder|
|
349
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
350
|
+
builder.use Faraday::Request::UrlEncoded
|
351
|
+
builder.adapter :test do |stub|
|
352
|
+
stub.post("/users") { |env| [200, {}, { id: 1, fullname: Faraday::Utils.parse_query(env[:body])["fullname"], email: Faraday::Utils.parse_query(env[:body])["email"] }.to_json] }
|
353
|
+
stub.post("/companies") { [200, {}, { errors: ["name is required"] }.to_json] }
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
spawn_model "Foo::User"
|
358
|
+
spawn_model "Foo::Company"
|
359
|
+
end
|
360
|
+
|
361
|
+
it "handle one-line resource creation" do
|
362
|
+
@user = Foo::User.create(fullname: "Tobias Fünke", email: "tobias@bluth.com")
|
363
|
+
expect(@user.id).to eq(1)
|
364
|
+
expect(@user.fullname).to eq("Tobias Fünke")
|
365
|
+
expect(@user.email).to eq("tobias@bluth.com")
|
366
|
+
end
|
367
|
+
|
368
|
+
it "handle resource creation through Model.new + #save" do
|
369
|
+
@user = Foo::User.new(fullname: "Tobias Fünke")
|
370
|
+
expect(@user.save).to be_truthy
|
371
|
+
expect(@user.fullname).to eq("Tobias Fünke")
|
372
|
+
end
|
373
|
+
|
374
|
+
it "handle resource creation through Model.new + #save!" do
|
375
|
+
@user = Foo::User.new(fullname: "Tobias Fünke")
|
376
|
+
expect(@user.save!).to be_truthy
|
377
|
+
expect(@user.fullname).to eq("Tobias Fünke")
|
378
|
+
end
|
379
|
+
|
380
|
+
it "returns false when #save gets errors" do
|
381
|
+
@company = Foo::Company.new
|
382
|
+
expect(@company.save).to be_falsey
|
383
|
+
end
|
384
|
+
|
385
|
+
it "raises ResourceInvalid when #save! gets errors" do
|
386
|
+
@company = Foo::Company.new
|
387
|
+
expect { @company.save! }.to raise_error Restorm::Errors::ResourceInvalid, "Remote validation failed: name is required"
|
388
|
+
end
|
389
|
+
|
390
|
+
it "don't overwrite data if response is empty" do
|
391
|
+
@company = Foo::Company.new(name: "Company Inc.")
|
392
|
+
expect(@company.save).to be_falsey
|
393
|
+
expect(@company.name).to eq("Company Inc.")
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
context "updating resources" do
|
398
|
+
before do
|
399
|
+
Restorm::API.setup url: "https://api.example.com" do |builder|
|
400
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
401
|
+
builder.use Faraday::Request::UrlEncoded
|
402
|
+
builder.adapter :test do |stub|
|
403
|
+
stub.get("/users/1") { [200, {}, { id: 1, fullname: "Tobias Fünke", admin: false }.to_json] }
|
404
|
+
stub.put("/users/1") { [200, {}, { id: 1, fullname: "Lindsay Fünke", admin: true }.to_json] }
|
405
|
+
stub.put("/users/2") { [200, {}, { id: 2, errors: ["fullname has already been taken"] }.to_json] }
|
406
|
+
stub.get("/pages/1") { [200, {}, { id: 1, views: 1, unique_visitors: 4 }.to_json] }
|
407
|
+
stub.put("/pages/1") { [200, {}, { id: 1, views: 2, unique_visitors: 3 }.to_json] }
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
spawn_model "Foo::User"
|
412
|
+
spawn_model "Foo::Page"
|
413
|
+
end
|
414
|
+
|
415
|
+
it "handle resource data update without saving it" do
|
416
|
+
@user = Foo::User.find(1)
|
417
|
+
expect(@user.fullname).to eq("Tobias Fünke")
|
418
|
+
@user.fullname = "Kittie Sanchez"
|
419
|
+
expect(@user.fullname).to eq("Kittie Sanchez")
|
420
|
+
end
|
421
|
+
|
422
|
+
it "handle resource update through the .update class method" do
|
423
|
+
@user = Foo::User.save_existing(1, fullname: "Lindsay Fünke")
|
424
|
+
expect(@user.fullname).to eq("Lindsay Fünke")
|
425
|
+
end
|
426
|
+
|
427
|
+
it "handle resource update through .save_existing!" do
|
428
|
+
@user = Foo::User.save_existing!(1, fullname: "Lindsay Fünke")
|
429
|
+
expect(@user.fullname).to eq("Lindsay Fünke")
|
430
|
+
end
|
431
|
+
|
432
|
+
it "raises ResourceInvalid when .save_existing! has errors" do
|
433
|
+
expect { Foo::User.save_existing!(2, fullname: "Lindsay Fünke") }.to raise_error(Restorm::Errors::ResourceInvalid)
|
434
|
+
end
|
435
|
+
|
436
|
+
it "handle resource update through #save on an existing resource" do
|
437
|
+
@user = Foo::User.find(1)
|
438
|
+
@user.fullname = "Lindsay Fünke"
|
439
|
+
@user.save
|
440
|
+
expect(@user.fullname).to eq("Lindsay Fünke")
|
441
|
+
end
|
442
|
+
|
443
|
+
it "handle resource update through #update_attributes" do
|
444
|
+
@user = Foo::User.find(1)
|
445
|
+
expect(@user).to receive(:save).and_return(true)
|
446
|
+
@user.update_attributes(fullname: "Lindsay Fünke")
|
447
|
+
expect(@user.fullname).to eq("Lindsay Fünke")
|
448
|
+
end
|
449
|
+
|
450
|
+
it "handles resource update through #toggle without saving it" do
|
451
|
+
@user = Foo::User.find(1)
|
452
|
+
expect(@user.admin).to be_falsey
|
453
|
+
expect(@user).to_not receive(:save)
|
454
|
+
@user.toggle(:admin)
|
455
|
+
expect(@user.admin).to be_truthy
|
456
|
+
end
|
457
|
+
|
458
|
+
it "handles resource update through #toggle!" do
|
459
|
+
@user = Foo::User.find(1)
|
460
|
+
expect(@user.admin).to be_falsey
|
461
|
+
expect(@user).to receive(:save).and_return(true)
|
462
|
+
@user.toggle!(:admin)
|
463
|
+
expect(@user.admin).to be_truthy
|
464
|
+
end
|
465
|
+
|
466
|
+
it "handles resource update through #increment without saving it" do
|
467
|
+
page = Foo::Page.find(1)
|
468
|
+
expect(page.views).to be 1
|
469
|
+
expect(page).to_not receive(:save)
|
470
|
+
page.increment(:views)
|
471
|
+
expect(page.views).to be 2
|
472
|
+
page.increment(:views, 2)
|
473
|
+
expect(page.views).to be 4
|
474
|
+
end
|
475
|
+
|
476
|
+
it "handles resource update through #increment!" do
|
477
|
+
page = Foo::Page.find(1)
|
478
|
+
expect(page.views).to be 1
|
479
|
+
expect(page).to receive(:save).and_return(true)
|
480
|
+
page.increment!(:views)
|
481
|
+
expect(page.views).to be 2
|
482
|
+
end
|
483
|
+
|
484
|
+
it "handles resource update through #decrement without saving it" do
|
485
|
+
page = Foo::Page.find(1)
|
486
|
+
expect(page.unique_visitors).to be 4
|
487
|
+
expect(page).to_not receive(:save)
|
488
|
+
page.decrement(:unique_visitors)
|
489
|
+
expect(page.unique_visitors).to be 3
|
490
|
+
page.decrement(:unique_visitors, 2)
|
491
|
+
expect(page.unique_visitors).to be 1
|
492
|
+
end
|
493
|
+
|
494
|
+
it "handles resource update through #decrement!" do
|
495
|
+
page = Foo::Page.find(1)
|
496
|
+
expect(page.unique_visitors).to be 4
|
497
|
+
expect(page).to receive(:save).and_return(true)
|
498
|
+
page.decrement!(:unique_visitors)
|
499
|
+
expect(page.unique_visitors).to be 3
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
context "deleting resources" do
|
504
|
+
let(:status) { 200 }
|
505
|
+
before do
|
506
|
+
Restorm::API.setup url: "https://api.example.com" do |builder|
|
507
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
508
|
+
builder.use Faraday::Request::UrlEncoded
|
509
|
+
builder.adapter :test do |stub|
|
510
|
+
stub.get("/users/1") { [200, {}, { id: 1, fullname: "Tobias Fünke", active: true }.to_json] }
|
511
|
+
stub.delete("/users/1") { [status, {}, { id: 1, fullname: "Lindsay Fünke", active: false }.to_json] }
|
512
|
+
|
513
|
+
stub.get("/child_users") { [200, {}, { data: [{ id: 1, name: "Tobias Fünke" }, { id: 2, name: "Lindsay Fünke" }], metadata: { total_pages: 10, next_page: 2 }, errors: %w[Oh My God] }.to_json] }
|
514
|
+
stub.get("/child_users") { [200, {}, { :data => [{ :id => 1, :name => "Tobias Fünke" }, { :id => 2, :name => "Lindsay Fünke" }], :metadata => { :total_pages => 10, :next_page => 2 }, :errors => ["Oh", "My", "God"] }.to_json] }
|
515
|
+
stub.post("/child_users") { [200, {}, { :data => { :name => "George Michael Bluth" }, :metadata => { :foo => "bar" }, :errors => ["Yes", "Sir"] }.to_json] }
|
516
|
+
stub.delete("/child_users/1") { [200, {}, { :data => { :id => 1 }, :metadata => { :foo => "bar" }, :errors => ["Yes", "Sir"] }.to_json] }
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
spawn_model "Foo::User"
|
521
|
+
end
|
522
|
+
|
523
|
+
it "handles proper resource deletion on a child model class" do
|
524
|
+
child_user_klass = Class.new(Foo::User) do
|
525
|
+
def self.name
|
526
|
+
"ChildUser"
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
child_user_klass.destroy_existing(1)
|
531
|
+
@child_user = child_user_klass.create(name: "George Michael Bluth")
|
532
|
+
|
533
|
+
expect { @child_user.save! }.to raise_error(Restorm::Errors::ResourceInvalid)
|
534
|
+
end
|
535
|
+
|
536
|
+
it "handle resource deletion through the .destroy class method" do
|
537
|
+
@user = Foo::User.destroy_existing(1)
|
538
|
+
expect(@user.active).to be_falsey
|
539
|
+
expect(@user).to be_destroyed
|
540
|
+
end
|
541
|
+
|
542
|
+
it "handle resource deletion through #destroy on an existing resource" do
|
543
|
+
@user = Foo::User.find(1)
|
544
|
+
@user.destroy
|
545
|
+
expect(@user.active).to be_falsey
|
546
|
+
expect(@user).to be_destroyed
|
547
|
+
end
|
548
|
+
|
549
|
+
context "with response_errors" do
|
550
|
+
let(:status) { 422 }
|
551
|
+
it "set user.destroyed to false if errors are present through the .destroy class method" do
|
552
|
+
@user = Foo::User.destroy_existing(1)
|
553
|
+
expect(@user).not_to be_destroyed
|
554
|
+
end
|
555
|
+
|
556
|
+
it "set user.destroyed to false if errors are present through #destroy on an existing resource" do
|
557
|
+
@user = Foo::User.find(1)
|
558
|
+
@user.destroy
|
559
|
+
expect(@user).not_to be_destroyed
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
context "with params" do
|
564
|
+
before do
|
565
|
+
Restorm::API.setup url: "https://api.example.com" do |builder|
|
566
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
567
|
+
builder.use Faraday::Request::UrlEncoded
|
568
|
+
builder.adapter :test do |stub|
|
569
|
+
stub.delete("/users/1?delete_type=soft") { [200, {}, { id: 1, fullname: "Lindsay Fünke", active: false }.to_json] }
|
570
|
+
end
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
it "handle resource deletion through the .destroy class method" do
|
575
|
+
@user = Foo::User.destroy_existing(1, delete_type: "soft")
|
576
|
+
expect(@user.active).to be_falsey
|
577
|
+
expect(@user).to be_destroyed
|
578
|
+
end
|
579
|
+
|
580
|
+
it "handle resource deletion through #destroy on an existing resource" do
|
581
|
+
@user = Foo::User.find(1)
|
582
|
+
@user.destroy(delete_type: "soft")
|
583
|
+
expect(@user.active).to be_falsey
|
584
|
+
expect(@user).to be_destroyed
|
585
|
+
end
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
context "customizing HTTP methods" do
|
590
|
+
before do
|
591
|
+
Restorm::API.setup url: "https://api.example.com" do |builder|
|
592
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
593
|
+
builder.use Faraday::Request::UrlEncoded
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
context "create" do
|
598
|
+
before do
|
599
|
+
Restorm::API.default_api.connection.adapter :test do |stub|
|
600
|
+
stub.put("/users") { [200, {}, { id: 1, fullname: "Tobias Fünke" }.to_json] }
|
601
|
+
end
|
602
|
+
spawn_model "Foo::User" do
|
603
|
+
attributes :fullname, :email
|
604
|
+
method_for :create, "PUT"
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
context "for top-level class" do
|
609
|
+
it "uses the custom method (PUT) instead of default method (POST)" do
|
610
|
+
user = Foo::User.new(fullname: "Tobias Fünke")
|
611
|
+
expect(user).to be_new
|
612
|
+
expect(user.save).to be_truthy
|
613
|
+
end
|
614
|
+
end
|
615
|
+
|
616
|
+
context "for children class" do
|
617
|
+
before do
|
618
|
+
class User < Foo::User; end
|
619
|
+
@spawned_models << :User
|
620
|
+
end
|
621
|
+
|
622
|
+
it "uses the custom method (PUT) instead of default method (POST)" do
|
623
|
+
user = User.new(fullname: "Tobias Fünke")
|
624
|
+
expect(user).to be_new
|
625
|
+
expect(user.save).to be_truthy
|
626
|
+
end
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
630
|
+
context "update" do
|
631
|
+
before do
|
632
|
+
Restorm::API.default_api.connection.adapter :test do |stub|
|
633
|
+
stub.get("/users/1") { [200, {}, { id: 1, fullname: "Lindsay Fünke" }.to_json] }
|
634
|
+
stub.post("/users/1") { [200, {}, { id: 1, fullname: "Tobias Fünke" }.to_json] }
|
635
|
+
end
|
636
|
+
|
637
|
+
spawn_model "Foo::User" do
|
638
|
+
attributes :fullname, :email
|
639
|
+
method_for :update, :post
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
it "uses the custom method (POST) instead of default method (PUT)" do
|
644
|
+
user = Foo::User.find(1)
|
645
|
+
expect(user.fullname).to eq "Lindsay Fünke"
|
646
|
+
user.fullname = "Toby Fünke"
|
647
|
+
user.save
|
648
|
+
expect(user.fullname).to eq "Tobias Fünke"
|
649
|
+
end
|
650
|
+
end
|
651
|
+
end
|
652
|
+
|
653
|
+
context "registering callbacks" do
|
654
|
+
before do
|
655
|
+
Restorm::API.setup url: "https://api.example.com" do |builder|
|
656
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
657
|
+
builder.use Faraday::Request::UrlEncoded
|
658
|
+
builder.adapter :test do |stub|
|
659
|
+
stub.get("/users/1") { [200, {}, { id: 1, fullname: "Tobias Fünke" }.to_json] }
|
660
|
+
stub.put("/users/1") { [200, {}, { id: 1, fullname: "Tobias Fünke" }.to_json] }
|
661
|
+
stub.post("/users") { [200, {}, { id: 2, fullname: "Lindsay Fünke" }.to_json] }
|
662
|
+
end
|
663
|
+
end
|
664
|
+
|
665
|
+
spawn_model "User" do
|
666
|
+
before_save :before_save_callback
|
667
|
+
before_create :before_create_callback
|
668
|
+
before_update :before_update_callback
|
669
|
+
after_update :after_update_callback
|
670
|
+
after_create :after_create_callback
|
671
|
+
after_save :after_save_callback
|
672
|
+
def before_save_callback; end
|
673
|
+
|
674
|
+
def before_create_callback; end
|
675
|
+
|
676
|
+
def before_update_callback; end
|
677
|
+
|
678
|
+
def after_update_callback; end
|
679
|
+
|
680
|
+
def after_create_callback; end
|
681
|
+
|
682
|
+
def after_save_callback; end
|
683
|
+
end
|
684
|
+
end
|
685
|
+
|
686
|
+
it "runs create callbacks in the correct order" do
|
687
|
+
@user = User.new(fullname: "Tobias Fünke")
|
688
|
+
expect(@user).to receive(:before_save_callback).ordered
|
689
|
+
expect(@user).to receive(:before_create_callback).ordered
|
690
|
+
expect(@user).to receive(:after_create_callback).ordered
|
691
|
+
expect(@user).to receive(:after_save_callback).ordered
|
692
|
+
@user.save
|
693
|
+
end
|
694
|
+
|
695
|
+
it "runs update callbacks in the correct order" do
|
696
|
+
@user = User.find(1)
|
697
|
+
expect(@user).to receive(:before_save_callback).ordered
|
698
|
+
expect(@user).to receive(:before_update_callback).ordered
|
699
|
+
expect(@user).to receive(:after_update_callback).ordered
|
700
|
+
expect(@user).to receive(:after_save_callback).ordered
|
701
|
+
@user.save
|
702
|
+
end
|
703
|
+
end
|
704
|
+
end
|