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,348 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), "../spec_helper.rb")
|
4
|
+
|
5
|
+
describe Restorm::Model::Paths do
|
6
|
+
context "building request paths" do
|
7
|
+
context "simple model" do
|
8
|
+
before do
|
9
|
+
spawn_model "Foo::User"
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#request_path" do
|
13
|
+
it "builds paths with defaults" do
|
14
|
+
expect(Foo::User.new(id: "foo").request_path).to eq("users/foo")
|
15
|
+
expect(Foo::User.new(id: nil).request_path).to eq("users")
|
16
|
+
expect(Foo::User.new.request_path).to eq("users")
|
17
|
+
end
|
18
|
+
|
19
|
+
it "builds paths with custom collection path" do
|
20
|
+
Foo::User.collection_path "/utilisateurs"
|
21
|
+
expect(Foo::User.new(id: "foo").request_path).to eq("/utilisateurs/foo")
|
22
|
+
expect(Foo::User.new.request_path).to eq("/utilisateurs")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "builds paths with custom relative collection path" do
|
26
|
+
Foo::User.collection_path "utilisateurs"
|
27
|
+
expect(Foo::User.new(id: "foo").request_path).to eq("utilisateurs/foo")
|
28
|
+
expect(Foo::User.new.request_path).to eq("utilisateurs")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "builds paths with custom collection path with multiple variables" do
|
32
|
+
Foo::User.collection_path "/organizations/:organization_id/utilisateurs"
|
33
|
+
|
34
|
+
expect(Foo::User.new(id: "foo").request_path(_organization_id: "acme")).to eq("/organizations/acme/utilisateurs/foo")
|
35
|
+
expect(Foo::User.new.request_path(_organization_id: "acme")).to eq("/organizations/acme/utilisateurs")
|
36
|
+
|
37
|
+
expect(Foo::User.new(id: "foo", organization_id: "acme").request_path).to eq("/organizations/acme/utilisateurs/foo")
|
38
|
+
expect(Foo::User.new(organization_id: "acme").request_path).to eq("/organizations/acme/utilisateurs")
|
39
|
+
end
|
40
|
+
|
41
|
+
it "builds paths with custom relative collection path with multiple variables" do
|
42
|
+
Foo::User.collection_path "organizations/:organization_id/utilisateurs"
|
43
|
+
|
44
|
+
expect(Foo::User.new(id: "foo").request_path(_organization_id: "acme")).to eq("organizations/acme/utilisateurs/foo")
|
45
|
+
expect(Foo::User.new.request_path(_organization_id: "acme")).to eq("organizations/acme/utilisateurs")
|
46
|
+
|
47
|
+
expect(Foo::User.new(id: "foo", organization_id: "acme").request_path).to eq("organizations/acme/utilisateurs/foo")
|
48
|
+
expect(Foo::User.new(organization_id: "acme").request_path).to eq("organizations/acme/utilisateurs")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "builds paths with custom item path" do
|
52
|
+
Foo::User.resource_path "/utilisateurs/:id"
|
53
|
+
expect(Foo::User.new(id: "foo").request_path).to eq("/utilisateurs/foo")
|
54
|
+
expect(Foo::User.new.request_path).to eq("users")
|
55
|
+
end
|
56
|
+
|
57
|
+
it "builds paths with custom relative item path" do
|
58
|
+
Foo::User.resource_path "utilisateurs/:id"
|
59
|
+
expect(Foo::User.new(id: "foo").request_path).to eq("utilisateurs/foo")
|
60
|
+
expect(Foo::User.new.request_path).to eq("users")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "raises exceptions when building a path without required custom variables" do
|
64
|
+
Foo::User.collection_path "/organizations/:organization_id/utilisateurs"
|
65
|
+
expect { Foo::User.new(id: "foo").request_path }.to raise_error(Restorm::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `/organizations/:organization_id/utilisateurs/:id`. Parameters are `{:id=>\"foo\"}`.")
|
66
|
+
end
|
67
|
+
|
68
|
+
it "escapes the variable values" do
|
69
|
+
Foo::User.collection_path "organizations/:organization_id/utilisateurs"
|
70
|
+
expect(Foo::User.new(id: "Привет").request_path(_organization_id: "лол")).to eq("organizations/%D0%BB%D0%BE%D0%BB/utilisateurs/%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82")
|
71
|
+
expect(Foo::User.new(organization_id: "лол", id: "Привет").request_path).to eq("organizations/%D0%BB%D0%BE%D0%BB/utilisateurs/%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "simple model with multiple words" do
|
77
|
+
before do
|
78
|
+
spawn_model "Foo::AdminUser"
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "#request_path" do
|
82
|
+
it "builds paths with defaults" do
|
83
|
+
expect(Foo::AdminUser.new(id: "foo").request_path).to eq("admin_users/foo")
|
84
|
+
expect(Foo::AdminUser.new.request_path).to eq("admin_users")
|
85
|
+
end
|
86
|
+
|
87
|
+
it "builds paths with custom collection path" do
|
88
|
+
Foo::AdminUser.collection_path "/users"
|
89
|
+
expect(Foo::AdminUser.new(id: "foo").request_path).to eq("/users/foo")
|
90
|
+
expect(Foo::AdminUser.new.request_path).to eq("/users")
|
91
|
+
end
|
92
|
+
|
93
|
+
it "builds paths with custom relative collection path" do
|
94
|
+
Foo::AdminUser.collection_path "users"
|
95
|
+
expect(Foo::AdminUser.new(id: "foo").request_path).to eq("users/foo")
|
96
|
+
expect(Foo::AdminUser.new.request_path).to eq("users")
|
97
|
+
end
|
98
|
+
|
99
|
+
it "builds paths with custom collection path with multiple variables" do
|
100
|
+
Foo::AdminUser.collection_path "/organizations/:organization_id/users"
|
101
|
+
expect(Foo::AdminUser.new(id: "foo").request_path(_organization_id: "acme")).to eq("/organizations/acme/users/foo")
|
102
|
+
expect(Foo::AdminUser.new.request_path(_organization_id: "acme")).to eq("/organizations/acme/users")
|
103
|
+
end
|
104
|
+
|
105
|
+
it "builds paths with custom relative collection path with multiple variables" do
|
106
|
+
Foo::AdminUser.collection_path "organizations/:organization_id/users"
|
107
|
+
expect(Foo::AdminUser.new(id: "foo").request_path(_organization_id: "acme")).to eq("organizations/acme/users/foo")
|
108
|
+
expect(Foo::AdminUser.new.request_path(_organization_id: "acme")).to eq("organizations/acme/users")
|
109
|
+
end
|
110
|
+
|
111
|
+
it "builds paths with custom item path" do
|
112
|
+
Foo::AdminUser.resource_path "/users/:id"
|
113
|
+
expect(Foo::AdminUser.new(id: "foo").request_path).to eq("/users/foo")
|
114
|
+
expect(Foo::AdminUser.new.request_path).to eq("admin_users")
|
115
|
+
end
|
116
|
+
|
117
|
+
it "builds paths with custom relative item path" do
|
118
|
+
Foo::AdminUser.resource_path "users/:id"
|
119
|
+
expect(Foo::AdminUser.new(id: "foo").request_path).to eq("users/foo")
|
120
|
+
expect(Foo::AdminUser.new.request_path).to eq("admin_users")
|
121
|
+
end
|
122
|
+
|
123
|
+
it "raises exceptions when building a path without required custom variables" do
|
124
|
+
Foo::AdminUser.collection_path "/organizations/:organization_id/users"
|
125
|
+
expect { Foo::AdminUser.new(id: "foo").request_path }.to raise_error(Restorm::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `/organizations/:organization_id/users/:id`. Parameters are `{:id=>\"foo\"}`.")
|
126
|
+
end
|
127
|
+
|
128
|
+
it "raises exceptions when building a relative path without required custom variables" do
|
129
|
+
Foo::AdminUser.collection_path "organizations/:organization_id/users"
|
130
|
+
expect { Foo::AdminUser.new(id: "foo").request_path }.to raise_error(Restorm::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `organizations/:organization_id/users/:id`. Parameters are `{:id=>\"foo\"}`.")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context "children model" do
|
136
|
+
before do
|
137
|
+
Restorm::API.setup url: "https://api.example.com" do |builder|
|
138
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
139
|
+
builder.use Faraday::Request::UrlEncoded
|
140
|
+
builder.adapter :test do |stub|
|
141
|
+
stub.get("/users/foo") { [200, {}, { id: "foo" }.to_json] }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
spawn_model("Foo::Model") { include_root_in_json true }
|
146
|
+
|
147
|
+
class User < Foo::Model; end
|
148
|
+
@spawned_models << :User
|
149
|
+
end
|
150
|
+
|
151
|
+
it "builds path using the children model name" do
|
152
|
+
expect(User.find("foo").id).to eq("foo")
|
153
|
+
expect(User.find("foo").id).to eq("foo")
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context "nested model" do
|
158
|
+
before do
|
159
|
+
spawn_model "Foo::User"
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "#request_path" do
|
163
|
+
it "builds paths with defaults" do
|
164
|
+
expect(Foo::User.new(id: "foo").request_path).to eq("users/foo")
|
165
|
+
expect(Foo::User.new.request_path).to eq("users")
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context "custom primary key" do
|
171
|
+
before do
|
172
|
+
spawn_model "User" do
|
173
|
+
primary_key "UserId"
|
174
|
+
resource_path "users/:UserId"
|
175
|
+
end
|
176
|
+
|
177
|
+
spawn_model "Customer" do
|
178
|
+
primary_key :customer_id
|
179
|
+
resource_path "customers/:id"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe "#request_path" do
|
184
|
+
it "uses the correct primary key attribute" do
|
185
|
+
expect(User.new(UserId: "foo").request_path).to eq("users/foo")
|
186
|
+
expect(User.new(id: "foo").request_path).to eq("users")
|
187
|
+
end
|
188
|
+
|
189
|
+
it "replaces :id with the appropriate primary key" do
|
190
|
+
expect(Customer.new(customer_id: "joe").request_path).to eq("customers/joe")
|
191
|
+
expect(Customer.new(id: "joe").request_path).to eq("customers")
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context "making subdomain HTTP requests" do
|
198
|
+
before do
|
199
|
+
Restorm::API.setup url: "https://api.example.com/" do |builder|
|
200
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
201
|
+
builder.use Faraday::Request::UrlEncoded
|
202
|
+
builder.adapter :test do |stub|
|
203
|
+
stub.get("organizations/2/users") { [200, {}, [{ id: 1, fullname: "Tobias Fünke", organization_id: 2 }, { id: 2, fullname: "Lindsay Fünke", organization_id: 2 }].to_json] }
|
204
|
+
stub.post("organizations/2/users") { [200, {}, { id: 1, fullname: "Tobias Fünke", organization_id: 2 }.to_json] }
|
205
|
+
stub.put("organizations/2/users/1") { [200, {}, { id: 1, fullname: "Lindsay Fünke", organization_id: 2 }.to_json] }
|
206
|
+
stub.get("organizations/2/users/1") { [200, {}, { id: 1, fullname: "Tobias Fünke", organization_id: 2, active: true }.to_json] }
|
207
|
+
stub.delete("organizations/2/users/1") { [200, {}, { id: 1, fullname: "Lindsay Fünke", organization_id: 2, active: false }.to_json] }
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
spawn_model "Foo::User" do
|
212
|
+
collection_path "organizations/:organization_id/users"
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe "fetching a resource" do
|
217
|
+
it "maps a single resource to a Ruby object" do
|
218
|
+
@user = Foo::User.find(1, _organization_id: 2)
|
219
|
+
expect(@user.id).to eq(1)
|
220
|
+
expect(@user.fullname).to eq("Tobias Fünke")
|
221
|
+
end
|
222
|
+
|
223
|
+
it "maps a single resource using a scope to a Ruby object" do
|
224
|
+
Foo::User.scope :for_organization, ->(o) { where(organization_id: o) }
|
225
|
+
@user = Foo::User.for_organization(2).find(1)
|
226
|
+
expect(@user.id).to eq(1)
|
227
|
+
expect(@user.fullname).to eq("Tobias Fünke")
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
describe "fetching a collection" do
|
232
|
+
it "maps a collection of resources to an array of Ruby objects" do
|
233
|
+
@users = Foo::User.where(_organization_id: 2).all
|
234
|
+
expect(@users.length).to eq(2)
|
235
|
+
expect(@users.first.fullname).to eq("Tobias Fünke")
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
describe "handling new resource" do
|
240
|
+
it "handles new resource" do
|
241
|
+
@new_user = Foo::User.new(fullname: "Tobias Fünke", organization_id: 2)
|
242
|
+
expect(@new_user.new?).to be_truthy
|
243
|
+
|
244
|
+
@existing_user = Foo::User.find(1, _organization_id: 2)
|
245
|
+
expect(@existing_user.new?).to be_falsey
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
describe "creating resources" do
|
250
|
+
it "handle one-line resource creation" do
|
251
|
+
@user = Foo::User.create(fullname: "Tobias Fünke", organization_id: 2)
|
252
|
+
expect(@user.id).to eq(1)
|
253
|
+
expect(@user.fullname).to eq("Tobias Fünke")
|
254
|
+
end
|
255
|
+
|
256
|
+
it "handle resource creation through Model.new + #save" do
|
257
|
+
@user = Foo::User.new(fullname: "Tobias Fünke", organization_id: 2)
|
258
|
+
@user.save
|
259
|
+
expect(@user.fullname).to eq("Tobias Fünke")
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
context "updating resources" do
|
264
|
+
it "handle resource data update without saving it" do
|
265
|
+
@user = Foo::User.find(1, _organization_id: 2)
|
266
|
+
expect(@user.fullname).to eq("Tobias Fünke")
|
267
|
+
@user.fullname = "Kittie Sanchez"
|
268
|
+
expect(@user.fullname).to eq("Kittie Sanchez")
|
269
|
+
end
|
270
|
+
|
271
|
+
it "handle resource update through the .update class method" do
|
272
|
+
@user = Foo::User.save_existing(1, fullname: "Lindsay Fünke", organization_id: 2)
|
273
|
+
expect(@user.fullname).to eq("Lindsay Fünke")
|
274
|
+
end
|
275
|
+
|
276
|
+
it "handle resource update through #save on an existing resource" do
|
277
|
+
@user = Foo::User.find(1, _organization_id: 2)
|
278
|
+
@user.fullname = "Lindsay Fünke"
|
279
|
+
@user.save
|
280
|
+
expect(@user.fullname).to eq("Lindsay Fünke")
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
context "deleting resources" do
|
285
|
+
it "handle resource deletion through the .destroy class method" do
|
286
|
+
@user = Foo::User.destroy_existing(1, _organization_id: 2)
|
287
|
+
expect(@user.active).to be_falsey
|
288
|
+
end
|
289
|
+
|
290
|
+
it "handle resource deletion through #destroy on an existing resource" do
|
291
|
+
@user = Foo::User.find(1, _organization_id: 2)
|
292
|
+
@user.destroy
|
293
|
+
expect(@user.active).to be_falsey
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
context "making path HTTP requests" do
|
299
|
+
before do
|
300
|
+
Restorm::API.setup url: "https://example.com/api/" do |builder|
|
301
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
302
|
+
builder.use Faraday::Request::UrlEncoded
|
303
|
+
builder.adapter :test do |stub|
|
304
|
+
stub.get("/api/organizations/2/users") { [200, {}, [{ id: 1, fullname: "Tobias Fünke", organization_id: 2 }, { id: 2, fullname: "Lindsay Fünke", organization_id: 2 }].to_json] }
|
305
|
+
stub.get("/api/organizations/2/users/1") { [200, {}, { id: 1, fullname: "Tobias Fünke", organization_id: 2, active: true }.to_json] }
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
spawn_model "Foo::User" do
|
310
|
+
collection_path "organizations/:organization_id/users"
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
describe "fetching a resource" do
|
315
|
+
it "maps a single resource to a Ruby object" do
|
316
|
+
@user = Foo::User.find(1, _organization_id: 2)
|
317
|
+
expect(@user.id).to eq(1)
|
318
|
+
expect(@user.fullname).to eq("Tobias Fünke")
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
describe "fetching a collection" do
|
323
|
+
it "maps a collection of resources to an array of Ruby objects" do
|
324
|
+
@users = Foo::User.where(_organization_id: 2).all
|
325
|
+
expect(@users.length).to eq(2)
|
326
|
+
expect(@users.first.fullname).to eq("Tobias Fünke")
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
describe "fetching a resource with absolute path" do
|
331
|
+
it "maps a single resource to a Ruby object" do
|
332
|
+
Foo::User.resource_path "/api/" + Foo::User.resource_path
|
333
|
+
@user = Foo::User.find(1, _organization_id: 2)
|
334
|
+
expect(@user.id).to eq(1)
|
335
|
+
expect(@user.fullname).to eq("Tobias Fünke")
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
describe "fetching a collection with absolute path" do
|
340
|
+
it "maps a collection of resources to an array of Ruby objects" do
|
341
|
+
Foo::User.collection_path "/api/" + Foo::User.collection_path
|
342
|
+
@users = Foo::User.where(_organization_id: 2).all
|
343
|
+
expect(@users.length).to eq(2)
|
344
|
+
expect(@users.first.fullname).to eq("Tobias Fünke")
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
@@ -0,0 +1,247 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), "../spec_helper.rb")
|
4
|
+
|
5
|
+
describe Restorm::Model::Relation do
|
6
|
+
describe :where do
|
7
|
+
context "for base classes" do
|
8
|
+
before do
|
9
|
+
Restorm::API.setup url: "https://api.example.com" do |builder|
|
10
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
11
|
+
builder.adapter :test do |stub|
|
12
|
+
stub.get("/users?foo=1&bar=2") { ok! [{ id: 2, fullname: "Tobias Fünke" }] }
|
13
|
+
stub.get("/users?admin=1") { ok! [{ id: 1, fullname: "Tobias Fünke" }] }
|
14
|
+
stub.get("/users?id=3&foo=2") { ok! [{ id: 3, fullname: "Tobias Fünke" }] }
|
15
|
+
|
16
|
+
stub.get("/users") do
|
17
|
+
ok! [
|
18
|
+
{ id: 1, fullname: "Tobias Fünke" },
|
19
|
+
{ id: 2, fullname: "Lindsay Fünke" },
|
20
|
+
@created_user
|
21
|
+
].compact
|
22
|
+
end
|
23
|
+
|
24
|
+
stub.post("/users") do
|
25
|
+
@created_user = { id: 3, fullname: "George Michael Bluth" }
|
26
|
+
ok! @created_user
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
spawn_model "Foo::User"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "doesn't fetch the data immediatly" do
|
35
|
+
expect(Foo::User).to receive(:request).never
|
36
|
+
@users = Foo::User.where(admin: 1)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "fetches the data and passes query parameters" do
|
40
|
+
expect(Foo::User).to receive(:request).once.and_call_original
|
41
|
+
@users = Foo::User.where(admin: 1)
|
42
|
+
expect(@users).to respond_to(:length)
|
43
|
+
expect(@users.size).to eql 1
|
44
|
+
end
|
45
|
+
|
46
|
+
it "fetches the data by parameters including primary_key" do
|
47
|
+
expect(Foo::User).to receive(:request).once.and_call_original
|
48
|
+
@users = Foo::User.where(id: 3, foo: 2)
|
49
|
+
expect(@users).to respond_to(:length)
|
50
|
+
expect(@users.size).to eql 1
|
51
|
+
end
|
52
|
+
|
53
|
+
it "chains multiple where statements" do
|
54
|
+
@user = Foo::User.where(foo: 1).where(bar: 2).first
|
55
|
+
expect(@user.id).to eq(2)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "does not reuse relations" do
|
59
|
+
expect(Foo::User.all.size).to eql 2
|
60
|
+
expect(Foo::User.create(fullname: "George Michael Bluth").id).to eq(3)
|
61
|
+
expect(Foo::User.all.size).to eql 3
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "for parent class" do
|
66
|
+
before do
|
67
|
+
Restorm::API.setup url: "https://api.example.com" do |builder|
|
68
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
69
|
+
builder.adapter :test do |stub|
|
70
|
+
stub.get("/users?page=2") { ok! [{ id: 1, fullname: "Tobias Fünke" }, { id: 2, fullname: "Lindsay Fünke" }] }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
spawn_model("Foo::Model") do
|
75
|
+
scope :page, ->(page) { where(page: page) }
|
76
|
+
end
|
77
|
+
|
78
|
+
class User < Foo::Model; end
|
79
|
+
@spawned_models << :User
|
80
|
+
end
|
81
|
+
|
82
|
+
it "propagates the scopes through its children" do
|
83
|
+
expect(User.page(2).length).to eq(2)
|
84
|
+
expect(User.scoped.page(2).length).to eq(2)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe :create do
|
90
|
+
before do
|
91
|
+
Restorm::API.setup url: "https://api.example.com" do |builder|
|
92
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
93
|
+
builder.use Faraday::Request::UrlEncoded
|
94
|
+
builder.adapter :test do |stub|
|
95
|
+
stub.post("/users") { |env| ok! id: 1, fullname: params(env)[:fullname], email: params(env)[:email] }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
spawn_model "Foo::User"
|
100
|
+
end
|
101
|
+
|
102
|
+
context "with a single where call" do
|
103
|
+
it "creates a resource and passes the query parameters" do
|
104
|
+
@user = Foo::User.where(fullname: "Tobias Fünke", email: "tobias@bluth.com").create
|
105
|
+
expect(@user.id).to eq(1)
|
106
|
+
expect(@user.fullname).to eq("Tobias Fünke")
|
107
|
+
expect(@user.email).to eq("tobias@bluth.com")
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "with multiple where calls" do
|
112
|
+
it "creates a resource and passes the query parameters" do
|
113
|
+
@user = Foo::User.where(fullname: "Tobias Fünke").create(email: "tobias@bluth.com")
|
114
|
+
expect(@user.id).to eq(1)
|
115
|
+
expect(@user.fullname).to eq("Tobias Fünke")
|
116
|
+
expect(@user.email).to eq("tobias@bluth.com")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe :build do
|
122
|
+
before { spawn_model "Foo::User" }
|
123
|
+
|
124
|
+
it "handles new resource with build" do
|
125
|
+
@new_user = Foo::User.where(fullname: "Tobias Fünke").build
|
126
|
+
expect(@new_user.new?).to be_truthy
|
127
|
+
expect(@new_user.fullname).to eq("Tobias Fünke")
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe :scope do
|
132
|
+
before do
|
133
|
+
Restorm::API.setup url: "https://api.example.com" do |builder|
|
134
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
135
|
+
builder.adapter :test do |stub|
|
136
|
+
stub.get("/users?what=4&where=3") { ok! [{ id: 3, fullname: "Maeby Fünke" }] }
|
137
|
+
stub.get("/users?what=2") { ok! [{ id: 2, fullname: "Lindsay Fünke" }] }
|
138
|
+
stub.get("/users?where=6") { ok! [{ id: 4, fullname: "Tobias Fünke" }] }
|
139
|
+
stub.get('/bar/users') { ok! [] }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
spawn_model "Foo::User" do
|
144
|
+
scope :foo, ->(v) { where(what: v) }
|
145
|
+
scope :bar, ->(v) { where(where: v) }
|
146
|
+
scope :baz, -> { bar(6) }
|
147
|
+
end
|
148
|
+
|
149
|
+
spawn_model "Bar::User" do
|
150
|
+
collection_path '/bar/users'
|
151
|
+
scope :baz, -> { where(where: 7) }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
it "passes query parameters" do
|
156
|
+
@user = Foo::User.foo(2).first
|
157
|
+
expect(@user.id).to eq(2)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "passes multiple query parameters" do
|
161
|
+
@user = Foo::User.foo(4).bar(3).first
|
162
|
+
expect(@user.id).to eq(3)
|
163
|
+
end
|
164
|
+
|
165
|
+
it "handles embedded scopes" do
|
166
|
+
@user = Foo::User.baz.first
|
167
|
+
expect(@user.id).to eq(4)
|
168
|
+
end
|
169
|
+
|
170
|
+
it "does not share scope with other models" do
|
171
|
+
expect(Bar::User.scoped).not_to respond_to(:foo, :bar)
|
172
|
+
expect(Foo::User.scoped.baz.params[:where]).to eq(6)
|
173
|
+
expect(Bar::User.scoped.baz.params[:where]).to eq(7)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe :default_scope do
|
178
|
+
context "for new objects" do
|
179
|
+
before do
|
180
|
+
spawn_model "Foo::User" do
|
181
|
+
default_scope -> { where(active: true) }
|
182
|
+
default_scope -> { where(admin: true) }
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should apply the scope to the attributes" do
|
187
|
+
expect(Foo::User.new).to be_active
|
188
|
+
expect(Foo::User.new).to be_admin
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context "for fetched resources" do
|
193
|
+
before do
|
194
|
+
Restorm::API.setup url: "https://api.example.com" do |builder|
|
195
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
196
|
+
builder.use Faraday::Request::UrlEncoded
|
197
|
+
builder.adapter :test do |stub|
|
198
|
+
stub.post("/users") { |env| ok! id: 3, active: (params(env)[:active] == "true") }
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
spawn_model "Foo::User" do
|
203
|
+
default_scope -> { where(active: true) }
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
it("should apply the scope to the request") { expect(Foo::User.create).to be_active }
|
208
|
+
end
|
209
|
+
|
210
|
+
context "for fetched collections" do
|
211
|
+
before do
|
212
|
+
Restorm::API.setup url: "https://api.example.com" do |builder|
|
213
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
214
|
+
builder.use Faraday::Request::UrlEncoded
|
215
|
+
builder.adapter :test do |stub|
|
216
|
+
stub.get("/users?active=true") { |env| ok! [{ id: 3, active: (params(env)[:active] == "true") }] }
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
spawn_model "Foo::User" do
|
221
|
+
default_scope -> { where(active: true) }
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
it("should apply the scope to the request") { expect(Foo::User.all.first).to be_active }
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe :map do
|
230
|
+
before do
|
231
|
+
Restorm::API.setup url: "https://api.example.com" do |builder|
|
232
|
+
builder.use Restorm::Middleware::FirstLevelParseJSON
|
233
|
+
builder.adapter :test do |stub|
|
234
|
+
stub.get("/users") do
|
235
|
+
ok! [{ id: 1, fullname: "Tobias Fünke" }, { id: 2, fullname: "Lindsay Fünke" }]
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
spawn_model "Foo::User"
|
241
|
+
end
|
242
|
+
|
243
|
+
it "delegates the method to the fetched collection" do
|
244
|
+
expect(Foo::User.all.map(&:fullname)).to eq(["Tobias Fünke", "Lindsay Fünke"])
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), "../spec_helper.rb")
|
4
|
+
|
5
|
+
describe "Restorm::Model and ActiveModel::Validations" do
|
6
|
+
context "validating attributes" do
|
7
|
+
before do
|
8
|
+
spawn_model "Foo::User" do
|
9
|
+
attributes :fullname, :email
|
10
|
+
validates_presence_of :fullname
|
11
|
+
validates_presence_of :email
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "validates attributes when calling #valid?" do
|
16
|
+
user = Foo::User.new
|
17
|
+
expect(user).not_to be_valid
|
18
|
+
expect(user.errors.full_messages).to include("Fullname can't be blank")
|
19
|
+
expect(user.errors.full_messages).to include("Email can't be blank")
|
20
|
+
user.fullname = "Tobias Fünke"
|
21
|
+
user.email = "tobias@bluthcompany.com"
|
22
|
+
expect(user).to be_valid
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "handling server errors" do
|
27
|
+
before do
|
28
|
+
spawn_model("Foo::Model") do
|
29
|
+
def errors
|
30
|
+
@response_errors
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class User < Foo::Model; end
|
35
|
+
@spawned_models << :User
|
36
|
+
end
|
37
|
+
|
38
|
+
it "validates attributes when calling #valid?" do
|
39
|
+
user = User.new(_errors: ["Email cannot be blank"])
|
40
|
+
expect(user.errors).to include("Email cannot be blank")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/spec/model_spec.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe Restorm::Model do
|
6
|
+
before do
|
7
|
+
Restorm::API.setup url: "https://api.example.com" do |connection|
|
8
|
+
connection.use Restorm::Middleware::FirstLevelParseJSON
|
9
|
+
connection.adapter :test do |stub|
|
10
|
+
stub.get("/users/1") { [200, {}, { id: 1, name: "Tobias Fünke" }.to_json] }
|
11
|
+
stub.get("/users/1/comments") { [200, {}, [{ id: 4, body: "They're having a FIRESALE?" }].to_json] }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
spawn_model("Foo::User") { has_many :comments }
|
16
|
+
spawn_model("Foo::Comment")
|
17
|
+
end
|
18
|
+
subject { Foo::User.find(1) }
|
19
|
+
|
20
|
+
describe :has_key? do
|
21
|
+
it { is_expected.not_to have_key(:unknown_method_for_a_user) }
|
22
|
+
it { is_expected.not_to have_key(:unknown_method_for_a_user) }
|
23
|
+
it { is_expected.to have_key(:name) }
|
24
|
+
it { is_expected.to have_key(:comments) }
|
25
|
+
end
|
26
|
+
|
27
|
+
describe :serialization do
|
28
|
+
it "should be serialized without an error" do
|
29
|
+
expect { Marshal.dump(subject.comments) }.not_to raise_error
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should correctly load serialized object" do
|
33
|
+
serialized_comments = Marshal.load(Marshal.dump(subject.comments))
|
34
|
+
expect(subject.comments.size).to eq(serialized_comments.size)
|
35
|
+
expect(subject.comments.first.id).to eq(serialized_comments.first.id)
|
36
|
+
expect(subject.comments.first.body).to eq(serialized_comments.first.body)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe :[] do
|
41
|
+
it { is_expected.not_to have_key(:unknown_method_for_a_user) }
|
42
|
+
specify { expect(subject[:name]).to eq("Tobias Fünke") }
|
43
|
+
specify { expect(subject[:comments].first.body).to eq("They're having a FIRESALE?") }
|
44
|
+
end
|
45
|
+
end
|