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