her 0.8.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -1
  3. data/.rubocop.yml +1291 -0
  4. data/.travis.yml +2 -0
  5. data/README.md +23 -3
  6. data/her.gemspec +1 -3
  7. data/lib/her/middleware/json_api_parser.rb +1 -1
  8. data/lib/her/model/associations/association.rb +31 -0
  9. data/lib/her/model/associations/association_proxy.rb +1 -1
  10. data/lib/her/model/attributes.rb +2 -0
  11. data/lib/her/model/orm.rb +79 -6
  12. data/lib/her/model/parse.rb +8 -12
  13. data/lib/her/model/relation.rb +45 -1
  14. data/lib/her/version.rb +1 -1
  15. data/spec/api_spec.rb +34 -31
  16. data/spec/collection_spec.rb +25 -10
  17. data/spec/json_api/model_spec.rb +75 -72
  18. data/spec/middleware/accept_json_spec.rb +1 -1
  19. data/spec/middleware/first_level_parse_json_spec.rb +20 -20
  20. data/spec/middleware/json_api_parser_spec.rb +26 -7
  21. data/spec/middleware/second_level_parse_json_spec.rb +8 -9
  22. data/spec/model/associations/association_proxy_spec.rb +2 -5
  23. data/spec/model/associations_spec.rb +248 -161
  24. data/spec/model/attributes_spec.rb +106 -99
  25. data/spec/model/callbacks_spec.rb +58 -26
  26. data/spec/model/dirty_spec.rb +30 -29
  27. data/spec/model/http_spec.rb +67 -35
  28. data/spec/model/introspection_spec.rb +26 -22
  29. data/spec/model/nested_attributes_spec.rb +31 -31
  30. data/spec/model/orm_spec.rb +312 -155
  31. data/spec/model/parse_spec.rb +77 -77
  32. data/spec/model/paths_spec.rb +109 -109
  33. data/spec/model/relation_spec.rb +76 -68
  34. data/spec/model/validations_spec.rb +6 -6
  35. data/spec/model_spec.rb +17 -17
  36. data/spec/spec_helper.rb +2 -3
  37. data/spec/support/macros/model_macros.rb +2 -2
  38. metadata +32 -59
@@ -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 :url => "https://api.example.com" do |builder|
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") { |env| [200, {}, { :id => 1, :name => "Tobias Fünke" }.to_json] }
13
- stub.get("/users") { |env| [200, {}, [{ :id => 1, :name => "Tobias Fünke" }, { :id => 2, :name => "Lindsay Fünke" }].to_json] }
14
- stub.get("/admin_users") { |env| [200, {}, [{ :admin_id => 1, :name => "Tobias Fünke" }, { :admin_id => 2, :name => "Lindsay Fünke" }].to_json] }
15
- stub.get("/admin_users/1") { |env| [200, {}, { :admin_id => 1, :name => "Tobias Fünke" }.to_json] }
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.should == 1
32
- @user.name.should == "Tobias Fünke"
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.should == 1
36
- @admin.name.should == "Tobias Fünke"
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.should == 2
42
- @users.first.name.should == "Tobias Fünke"
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.should == 2
46
- @users.first.name.should == "Tobias Fünke"
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(:fullname => "Tobias Fünke")
51
- @new_user.new?.should be_truthy
52
- @new_user.new_record?.should be_truthy
53
- @new_user.fullname.should == "Tobias Fünke"
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?.should be_falsey
57
- @existing_user.new_record?.should be_falsey
56
+ expect(@existing_user.new?).to be_falsey
57
+ expect(@existing_user.new_record?).to be_falsey
58
58
  end
59
59
 
60
- it 'handles new resource with custom primary key' do
61
- @new_user = Foo::AdminUser.new(:fullname => 'Lindsay Fünke', :id => -1)
62
- @new_user.should be_new
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.should_not be_new
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 :url => "https://api.example.com" do |builder|
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].should == 10
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.should == 3
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(:name => "George Michael Bluth")
98
- @user.metadata[:foo].should == "bar"
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(:name => "George Michael Bluth")
103
- @user.response_errors.should == ["Yes", "Sir"]
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 :url => "https://api.example.com" do |builder|
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") { |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] }
115
- stub.post("/users") { |env| [200, {}, { 'data' => { :name => "George Michael Bluth" }, 'metadata' => { :foo => "bar" }, 'errors' => ["Yes", "Sir"] }.to_json] }
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].should == 10
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.should == 3
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(:name => "George Michael Bluth")
136
- @user.metadata[:foo].should == "bar"
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(:name => "George Michael Bluth")
141
- @user.response_errors.should == ["Yes", "Sir"]
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 :url => "https://api.example.com" do |builder|
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") { |env| [200, {}, { :id => 1, :friends => ["Maeby", "GOB", "Anne"] }.to_json] }
153
- stub.get("/users/2") { |env| [200, {}, { :id => 1 }.to_json] }
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,7 +171,7 @@ describe Her::Model::ORM do
159
171
  belongs_to :organization
160
172
 
161
173
  def friends=(val)
162
- val = val.gsub("\r", "").split("\n").map { |friend| friend.gsub(/^\s*\*\s*/, "") } if val and val.is_a?(String)
174
+ val = val.delete("\r").split("\n").map { |friend| friend.gsub(/^\s*\*\s*/, "") } if val && val.is_a?(String)
163
175
  @attributes[:friends] = val
164
176
  end
165
177
 
@@ -171,18 +183,18 @@ describe Her::Model::ORM do
171
183
 
172
184
  it "handles custom setters" do
173
185
  @user = User.find(1)
174
- @user.friends.should == "* Maeby\n* GOB\n* Anne"
186
+ expect(@user.friends).to eq("* Maeby\n* GOB\n* Anne")
175
187
  @user.instance_eval do
176
- @attributes[:friends] = ["Maeby", "GOB", "Anne"]
188
+ @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.should == "* George\n* Oscar\n* Lucille"
195
+ expect(@user.friends).to eq("* George\n* Oscar\n* Lucille")
184
196
  @user.instance_eval do
185
- @attributes[:friends] = ["George", "Oscar", "Lucille"]
197
+ @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 :url => "https://api.example.com" do |builder|
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") { |env| [200, {}, { :id => 1, :age => 42 }.to_json] }
198
- stub.get("/users/2") { |env| [200, {}, { :id => 2, :age => 34 }.to_json] }
199
- stub.get("/users?id[]=1&id[]=2") { |env| [200, {}, [{ :id => 1, :age => 42 }, { :id => 2, :age => 34 }].to_json] }
200
- stub.get("/users?age=42&foo=bar") { |env| [200, {}, [{ :id => 3, :age => 42 }].to_json] }
201
- stub.get("/users?age=42") { |env| [200, {}, [{ :id => 1, :age => 42 }].to_json] }
202
- stub.get("/users?age=40") { |env| [200, {}, [{ :id => 1, :age => 40 }].to_json] }
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.should == 1
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.should be_kind_of(Array)
219
- @users.length.should == 2
220
- @users[0].id.should == 1
221
- @users[1].id.should == 2
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.should be_kind_of(Array)
227
- @users.length.should == 2
228
- @users[0].id.should == 1
229
- @users[1].id.should == 2
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.should be_kind_of(Array)
235
- @users.length.should == 1
236
- @users[0].id.should == 1
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.should be_kind_of(Array)
242
- @users.length.should == 2
243
- @users[0].id.should == 1
244
- @users[1].id.should == 2
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 'handles finding with id parameter as an array' do
261
+ it "handles finding with id parameter as an array" do
248
262
  @users = User.where(id: [1, 2])
249
- @users.should be_kind_of(Array)
250
- @users.length.should == 2
251
- @users[0].id.should == 1
252
- @users[1].id.should == 2
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(:age => 42, :foo => "bar").all
257
- @users.should be_kind_of(Array)
258
- @users.first.id.should == 3
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(:age => 42).should be_all { |u| u.age == 42 }
264
- @users.where(:age => 40).should be_all { |u| u.age == 40 }
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.should_not_receive(:request)
276
- @new_user = Foo::User.build(:fullname => "Tobias Fünke")
277
- @new_user.new?.should be_truthy
278
- @new_user.fullname.should == "Tobias Fünke"
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 :url => "https://api.example.com" do |builder|
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! :id => nil, :fullname => params(env)[:fullname], :email => "tobias@bluthcompany.com" }
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.should_receive(:request).once.and_call_original
297
- @new_user = Foo::User.build(:fullname => "Tobias Fünke")
298
- @new_user.new?.should be_truthy
299
- @new_user.fullname.should == "Tobias Fünke"
300
- @new_user.email.should == "tobias@bluthcompany.com"
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 :url => "https://api.example.com" do |builder|
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, {}, { :id => 1, :fullname => Faraday::Utils.parse_query(env[:body])['fullname'], :email => Faraday::Utils.parse_query(env[:body])['email'] }.to_json] }
312
- stub.post("/companies") { |env| [200, {}, { :errors => ["name is required"] }.to_json] }
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(:fullname => "Tobias Fünke", :email => "tobias@bluth.com")
322
- @user.id.should == 1
323
- @user.fullname.should == "Tobias Fünke"
324
- @user.email.should == "tobias@bluth.com"
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(:fullname => "Tobias Fünke")
329
- @user.save.should be_truthy
330
- @user.fullname.should == "Tobias Fünke"
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(:fullname => "Tobias Fünke")
335
- @user.save!.should be_truthy
336
- @user.fullname.should == "Tobias Fünke"
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.should be_falsey
381
+ expect(@company.save).to be_falsey
342
382
  end
343
383
 
344
384
  it "raises ResourceInvalid when #save! gets errors" do
@@ -347,54 +387,110 @@ 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(:name => 'Company Inc.')
351
- @company.save.should be_falsey
352
- @company.name.should == "Company Inc."
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 :url => "https://api.example.com" do |builder|
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") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke" }.to_json] }
363
- stub.put("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke" }.to_json] }
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.should == "Tobias Fünke"
415
+ expect(@user.fullname).to eq("Tobias Fünke")
373
416
  @user.fullname = "Kittie Sanchez"
374
- @user.fullname.should == "Kittie Sanchez"
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, { :fullname => "Lindsay Fünke" })
379
- @user.fullname.should == "Lindsay Fünke"
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.should == "Lindsay Fünke"
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 :url => "https://api.example.com" do |builder|
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") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :active => true }.to_json] }
397
- stub.delete("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke", :active => false }.to_json] }
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] }
398
494
  end
399
495
  end
400
496
 
@@ -403,104 +499,165 @@ describe Her::Model::ORM do
403
499
 
404
500
  it "handle resource deletion through the .destroy class method" do
405
501
  @user = Foo::User.destroy_existing(1)
406
- @user.active.should be_falsey
407
- @user.should be_destroyed
502
+ expect(@user.active).to be_falsey
503
+ expect(@user).to be_destroyed
408
504
  end
409
505
 
410
506
  it "handle resource deletion through #destroy on an existing resource" do
411
507
  @user = Foo::User.find(1)
412
508
  @user.destroy
413
- @user.active.should be_falsey
414
- @user.should be_destroyed
509
+ expect(@user.active).to be_falsey
510
+ expect(@user).to be_destroyed
511
+ end
512
+
513
+ context "with response_errors" do
514
+ let(:status) { 422 }
515
+ it "set user.destroyed to false if errors are present through the .destroy class method" do
516
+ @user = Foo::User.destroy_existing(1)
517
+ expect(@user).not_to be_destroyed
518
+ end
519
+
520
+ it "set user.destroyed to false if errors are present through #destroy on an existing resource" do
521
+ @user = Foo::User.find(1)
522
+ @user.destroy
523
+ expect(@user).not_to be_destroyed
524
+ end
415
525
  end
416
526
 
417
527
  context "with params" do
418
528
  before do
419
- Her::API.setup :url => "https://api.example.com" do |builder|
529
+ Her::API.setup url: "https://api.example.com" do |builder|
420
530
  builder.use Her::Middleware::FirstLevelParseJSON
421
531
  builder.use Faraday::Request::UrlEncoded
422
532
  builder.adapter :test do |stub|
423
- stub.delete("/users/1?delete_type=soft") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke", :active => false }.to_json] }
533
+ stub.delete("/users/1?delete_type=soft") { [200, {}, { id: 1, fullname: "Lindsay Fünke", active: false }.to_json] }
424
534
  end
425
535
  end
426
536
  end
427
537
 
428
538
  it "handle resource deletion through the .destroy class method" do
429
- @user = Foo::User.destroy_existing(1, delete_type: 'soft')
430
- @user.active.should be_false
431
- @user.should be_destroyed
539
+ @user = Foo::User.destroy_existing(1, delete_type: "soft")
540
+ expect(@user.active).to be_falsey
541
+ expect(@user).to be_destroyed
432
542
  end
433
543
 
434
544
  it "handle resource deletion through #destroy on an existing resource" do
435
545
  @user = Foo::User.find(1)
436
- @user.destroy(delete_type: 'soft')
437
- @user.active.should be_false
438
- @user.should be_destroyed
546
+ @user.destroy(delete_type: "soft")
547
+ expect(@user.active).to be_falsey
548
+ expect(@user).to be_destroyed
439
549
  end
440
550
  end
441
551
  end
442
552
 
443
- context 'customizing HTTP methods' do
553
+ context "customizing HTTP methods" do
444
554
  before do
445
- Her::API.setup :url => "https://api.example.com" do |builder|
555
+ Her::API.setup url: "https://api.example.com" do |builder|
446
556
  builder.use Her::Middleware::FirstLevelParseJSON
447
557
  builder.use Faraday::Request::UrlEncoded
448
558
  end
449
559
  end
450
560
 
451
- context 'create' do
561
+ context "create" do
452
562
  before do
453
563
  Her::API.default_api.connection.adapter :test do |stub|
454
- stub.put('/users') { |env| [200, {}, { :id => 1, :fullname => 'Tobias Fünke' }.to_json] }
564
+ stub.put("/users") { [200, {}, { id: 1, fullname: "Tobias Fünke" }.to_json] }
455
565
  end
456
- spawn_model 'Foo::User' do
566
+ spawn_model "Foo::User" do
457
567
  attributes :fullname, :email
458
- method_for :create, 'PUT'
568
+ method_for :create, "PUT"
459
569
  end
460
570
  end
461
571
 
462
- context 'for top-level class' do
463
- it 'uses the custom method (PUT) instead of default method (POST)' do
464
- user = Foo::User.new(:fullname => 'Tobias Fünke')
465
- user.should be_new
466
- user.save.should be_truthy
572
+ context "for top-level class" do
573
+ it "uses the custom method (PUT) instead of default method (POST)" do
574
+ user = Foo::User.new(fullname: "Tobias Fünke")
575
+ expect(user).to be_new
576
+ expect(user.save).to be_truthy
467
577
  end
468
578
  end
469
579
 
470
- context 'for children class' do
580
+ context "for children class" do
471
581
  before do
472
582
  class User < Foo::User; end
473
583
  @spawned_models << :User
474
584
  end
475
585
 
476
- it 'uses the custom method (PUT) instead of default method (POST)' do
477
- user = User.new(:fullname => 'Tobias Fünke')
478
- user.should be_new
479
- user.save.should be_truthy
586
+ it "uses the custom method (PUT) instead of default method (POST)" do
587
+ user = User.new(fullname: "Tobias Fünke")
588
+ expect(user).to be_new
589
+ expect(user.save).to be_truthy
480
590
  end
481
591
  end
482
592
  end
483
593
 
484
- context 'update' do
594
+ context "update" do
485
595
  before do
486
596
  Her::API.default_api.connection.adapter :test do |stub|
487
- stub.get('/users/1') { |env| [200, {}, { :id => 1, :fullname => 'Lindsay Fünke' }.to_json] }
488
- stub.post('/users/1') { |env| [200, {}, { :id => 1, :fullname => 'Tobias Fünke' }.to_json] }
597
+ stub.get("/users/1") { [200, {}, { id: 1, fullname: "Lindsay Fünke" }.to_json] }
598
+ stub.post("/users/1") { [200, {}, { id: 1, fullname: "Tobias Fünke" }.to_json] }
489
599
  end
490
600
 
491
- spawn_model 'Foo::User' do
601
+ spawn_model "Foo::User" do
492
602
  attributes :fullname, :email
493
603
  method_for :update, :post
494
604
  end
495
605
  end
496
606
 
497
- it 'uses the custom method (POST) instead of default method (PUT)' do
607
+ it "uses the custom method (POST) instead of default method (PUT)" do
498
608
  user = Foo::User.find(1)
499
- user.fullname.should eq 'Lindsay Fünke'
500
- user.fullname = 'Toby Fünke'
609
+ expect(user.fullname).to eq "Lindsay Fünke"
610
+ user.fullname = "Toby Fünke"
501
611
  user.save
502
- user.fullname.should eq 'Tobias Fünke'
612
+ expect(user.fullname).to eq "Tobias Fünke"
503
613
  end
504
614
  end
505
615
  end
616
+
617
+ context "registering callbacks" do
618
+ before do
619
+ Her::API.setup url: "https://api.example.com" do |builder|
620
+ builder.use Her::Middleware::FirstLevelParseJSON
621
+ builder.use Faraday::Request::UrlEncoded
622
+ builder.adapter :test do |stub|
623
+ stub.get("/users/1") { [200, {}, { id: 1, fullname: "Tobias Fünke" }.to_json] }
624
+ stub.put("/users/1") { [200, {}, { id: 1, fullname: "Tobias Fünke" }.to_json] }
625
+ stub.post("/users") { [200, {}, { id: 2, fullname: "Lindsay Fünke" }.to_json] }
626
+ end
627
+ end
628
+
629
+ spawn_model "User" do
630
+ before_save :before_save_callback
631
+ before_create :before_create_callback
632
+ before_update :before_update_callback
633
+ after_update :after_update_callback
634
+ after_create :after_create_callback
635
+ after_save :after_save_callback
636
+ def before_save_callback; end
637
+ def before_create_callback; end
638
+ def before_update_callback; end
639
+ def after_update_callback; end
640
+ def after_create_callback; end
641
+ def after_save_callback; end
642
+ end
643
+ end
644
+
645
+ it "runs create callbacks in the correct order" do
646
+ @user = User.new(fullname: "Tobias Fünke")
647
+ expect(@user).to receive(:before_save_callback).ordered
648
+ expect(@user).to receive(:before_create_callback).ordered
649
+ expect(@user).to receive(:after_create_callback).ordered
650
+ expect(@user).to receive(:after_save_callback).ordered
651
+ @user.save
652
+ end
653
+
654
+ it "runs update callbacks in the correct order" do
655
+ @user = User.find(1)
656
+ expect(@user).to receive(:before_save_callback).ordered
657
+ expect(@user).to receive(:before_update_callback).ordered
658
+ expect(@user).to receive(:after_update_callback).ordered
659
+ expect(@user).to receive(:after_save_callback).ordered
660
+ @user.save
661
+ end
662
+ end
506
663
  end