her5 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +17 -0
  5. data/.yardopts +2 -0
  6. data/CONTRIBUTING.md +26 -0
  7. data/Gemfile +10 -0
  8. data/LICENSE +7 -0
  9. data/README.md +1017 -0
  10. data/Rakefile +11 -0
  11. data/UPGRADE.md +101 -0
  12. data/gemfiles/Gemfile.activemodel-3.2.x +7 -0
  13. data/gemfiles/Gemfile.activemodel-4.0 +7 -0
  14. data/gemfiles/Gemfile.activemodel-4.1 +7 -0
  15. data/gemfiles/Gemfile.activemodel-4.2 +7 -0
  16. data/gemfiles/Gemfile.activemodel-5.0.x +7 -0
  17. data/her5.gemspec +30 -0
  18. data/lib/her.rb +19 -0
  19. data/lib/her/api.rb +120 -0
  20. data/lib/her/collection.rb +12 -0
  21. data/lib/her/errors.rb +104 -0
  22. data/lib/her/json_api/model.rb +57 -0
  23. data/lib/her/middleware.rb +12 -0
  24. data/lib/her/middleware/accept_json.rb +17 -0
  25. data/lib/her/middleware/first_level_parse_json.rb +36 -0
  26. data/lib/her/middleware/json_api_parser.rb +68 -0
  27. data/lib/her/middleware/parse_json.rb +28 -0
  28. data/lib/her/middleware/second_level_parse_json.rb +36 -0
  29. data/lib/her/model.rb +75 -0
  30. data/lib/her/model/associations.rb +141 -0
  31. data/lib/her/model/associations/association.rb +107 -0
  32. data/lib/her/model/associations/association_proxy.rb +45 -0
  33. data/lib/her/model/associations/belongs_to_association.rb +101 -0
  34. data/lib/her/model/associations/has_many_association.rb +101 -0
  35. data/lib/her/model/associations/has_one_association.rb +80 -0
  36. data/lib/her/model/attributes.rb +297 -0
  37. data/lib/her/model/base.rb +33 -0
  38. data/lib/her/model/deprecated_methods.rb +61 -0
  39. data/lib/her/model/http.rb +113 -0
  40. data/lib/her/model/introspection.rb +65 -0
  41. data/lib/her/model/nested_attributes.rb +84 -0
  42. data/lib/her/model/orm.rb +207 -0
  43. data/lib/her/model/parse.rb +221 -0
  44. data/lib/her/model/paths.rb +126 -0
  45. data/lib/her/model/relation.rb +164 -0
  46. data/lib/her/version.rb +3 -0
  47. data/spec/api_spec.rb +114 -0
  48. data/spec/collection_spec.rb +26 -0
  49. data/spec/json_api/model_spec.rb +305 -0
  50. data/spec/middleware/accept_json_spec.rb +10 -0
  51. data/spec/middleware/first_level_parse_json_spec.rb +62 -0
  52. data/spec/middleware/json_api_parser_spec.rb +32 -0
  53. data/spec/middleware/second_level_parse_json_spec.rb +35 -0
  54. data/spec/model/associations/association_proxy_spec.rb +31 -0
  55. data/spec/model/associations_spec.rb +504 -0
  56. data/spec/model/attributes_spec.rb +389 -0
  57. data/spec/model/callbacks_spec.rb +145 -0
  58. data/spec/model/dirty_spec.rb +91 -0
  59. data/spec/model/http_spec.rb +158 -0
  60. data/spec/model/introspection_spec.rb +76 -0
  61. data/spec/model/nested_attributes_spec.rb +134 -0
  62. data/spec/model/orm_spec.rb +506 -0
  63. data/spec/model/parse_spec.rb +345 -0
  64. data/spec/model/paths_spec.rb +347 -0
  65. data/spec/model/relation_spec.rb +226 -0
  66. data/spec/model/validations_spec.rb +42 -0
  67. data/spec/model_spec.rb +44 -0
  68. data/spec/spec_helper.rb +26 -0
  69. data/spec/support/extensions/array.rb +5 -0
  70. data/spec/support/extensions/hash.rb +5 -0
  71. data/spec/support/macros/her_macros.rb +17 -0
  72. data/spec/support/macros/model_macros.rb +36 -0
  73. data/spec/support/macros/request_macros.rb +27 -0
  74. metadata +289 -0
@@ -0,0 +1,506 @@
1
+ # encoding: utf-8
2
+ require File.join(File.dirname(__FILE__), "../spec_helper.rb")
3
+
4
+ describe Her::Model::ORM do
5
+ context "mapping data to Ruby objects" do
6
+ before do
7
+ api = Her::API.new
8
+ api.setup :url => "https://api.example.com" do |builder|
9
+ builder.use Her::Middleware::FirstLevelParseJSON
10
+ builder.use Faraday::Request::UrlEncoded
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] }
16
+ end
17
+ end
18
+
19
+ spawn_model "Foo::User" do
20
+ uses_api api
21
+ end
22
+
23
+ spawn_model "Foo::AdminUser" do
24
+ uses_api api
25
+ primary_key :admin_id
26
+ end
27
+ end
28
+
29
+ it "maps a single resource to a Ruby object" do
30
+ @user = Foo::User.find(1)
31
+ @user.id.should == 1
32
+ @user.name.should == "Tobias Fünke"
33
+
34
+ @admin = Foo::AdminUser.find(1)
35
+ @admin.id.should == 1
36
+ @admin.name.should == "Tobias Fünke"
37
+ end
38
+
39
+ it "maps a collection of resources to an array of Ruby objects" do
40
+ @users = Foo::User.all
41
+ @users.length.should == 2
42
+ @users.first.name.should == "Tobias Fünke"
43
+
44
+ @users = Foo::AdminUser.all
45
+ @users.length.should == 2
46
+ @users.first.name.should == "Tobias Fünke"
47
+ end
48
+
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"
54
+
55
+ @existing_user = Foo::User.find(1)
56
+ @existing_user.new?.should be_falsey
57
+ @existing_user.new_record?.should be_falsey
58
+ end
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
63
+
64
+ @existing_user = Foo::AdminUser.find(1)
65
+ @existing_user.should_not be_new
66
+ end
67
+ end
68
+
69
+ context "mapping data, metadata and error data to Ruby objects" do
70
+ before do
71
+ api = Her::API.new
72
+ api.setup :url => "https://api.example.com" do |builder|
73
+ builder.use Her::Middleware::SecondLevelParseJSON
74
+ builder.use Faraday::Request::UrlEncoded
75
+ builder.adapter :test do |stub|
76
+ 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
+ stub.post("/users") { |env| [200, {}, { :data => { :name => "George Michael Bluth" }, :metadata => { :foo => "bar" }, :errors => ["Yes", "Sir"] }.to_json] }
78
+ end
79
+ end
80
+
81
+ spawn_model :User do
82
+ uses_api api
83
+ end
84
+ end
85
+
86
+ it "handles metadata on a collection" do
87
+ @users = User.all
88
+ @users.metadata[:total_pages].should == 10
89
+ end
90
+
91
+ it "handles error data on a collection" do
92
+ @users = User.all
93
+ @users.errors.length.should == 3
94
+ end
95
+
96
+ it "handles metadata on a resource" do
97
+ @user = User.create(:name => "George Michael Bluth")
98
+ @user.metadata[:foo].should == "bar"
99
+ end
100
+
101
+ it "handles error data on a resource" do
102
+ @user = User.create(:name => "George Michael Bluth")
103
+ @user.response_errors.should == ["Yes", "Sir"]
104
+ end
105
+ end
106
+
107
+ context "mapping data, metadata and error data in string keys to Ruby objects" do
108
+ before do
109
+ api = Her::API.new
110
+ api.setup :url => "https://api.example.com" do |builder|
111
+ builder.use Her::Middleware::SecondLevelParseJSON
112
+ builder.use Faraday::Request::UrlEncoded
113
+ 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] }
116
+ end
117
+ end
118
+
119
+ spawn_model :User do
120
+ uses_api api
121
+ end
122
+ end
123
+
124
+ it "handles metadata on a collection" do
125
+ @users = User.all
126
+ @users.metadata[:total_pages].should == 10
127
+ end
128
+
129
+ it "handles error data on a collection" do
130
+ @users = User.all
131
+ @users.errors.length.should == 3
132
+ end
133
+
134
+ it "handles metadata on a resource" do
135
+ @user = User.create(:name => "George Michael Bluth")
136
+ @user.metadata[:foo].should == "bar"
137
+ end
138
+
139
+ it "handles error data on a resource" do
140
+ @user = User.create(:name => "George Michael Bluth")
141
+ @user.response_errors.should == ["Yes", "Sir"]
142
+ end
143
+ end
144
+
145
+ context "defining custom getters and setters" do
146
+ before do
147
+ api = Her::API.new
148
+ api.setup :url => "https://api.example.com" do |builder|
149
+ builder.use Her::Middleware::FirstLevelParseJSON
150
+ builder.use Faraday::Request::UrlEncoded
151
+ 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] }
154
+ end
155
+ end
156
+
157
+ spawn_model :User do
158
+ uses_api api
159
+ belongs_to :organization
160
+
161
+ def friends=(val)
162
+ val = val.gsub("\r", "").split("\n").map { |friend| friend.gsub(/^\s*\*\s*/, "") } if val and val.is_a?(String)
163
+ @attributes[:friends] = val
164
+ end
165
+
166
+ def friends
167
+ @attributes[:friends].map { |friend| "* #{friend}" }.join("\n")
168
+ end
169
+ end
170
+ end
171
+
172
+ it "handles custom setters" do
173
+ @user = User.find(1)
174
+ @user.friends.should == "* Maeby\n* GOB\n* Anne"
175
+ @user.instance_eval do
176
+ @attributes[:friends] = ["Maeby", "GOB", "Anne"]
177
+ end
178
+ end
179
+
180
+ it "handles custom getters" do
181
+ @user = User.new
182
+ @user.friends = "* George\n* Oscar\n* Lucille"
183
+ @user.friends.should == "* George\n* Oscar\n* Lucille"
184
+ @user.instance_eval do
185
+ @attributes[:friends] = ["George", "Oscar", "Lucille"]
186
+ end
187
+ end
188
+ end
189
+
190
+ context "finding resources" do
191
+ before do
192
+ api = Her::API.new
193
+ api.setup :url => "https://api.example.com" do |builder|
194
+ builder.use Her::Middleware::FirstLevelParseJSON
195
+ builder.use Faraday::Request::UrlEncoded
196
+ 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] }
203
+ end
204
+ end
205
+
206
+ spawn_model :User do
207
+ uses_api api
208
+ end
209
+ end
210
+
211
+ it "handles finding by a single id" do
212
+ @user = User.find(1)
213
+ @user.id.should == 1
214
+ end
215
+
216
+ it "handles finding by multiple ids" do
217
+ @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
222
+ end
223
+
224
+ it "handles finding by an array of ids" do
225
+ @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
230
+ end
231
+
232
+ it "handles finding by an array of ids of length 1" do
233
+ @users = User.find([1])
234
+ @users.should be_kind_of(Array)
235
+ @users.length.should == 1
236
+ @users[0].id.should == 1
237
+ end
238
+
239
+ it "handles finding by an array id param of length 2" do
240
+ @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
245
+ end
246
+
247
+ it 'handles finding with id parameter as an array' do
248
+ @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
253
+ end
254
+
255
+ 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
259
+ end
260
+
261
+ it "handles finding with other parameters and scoped" do
262
+ @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 }
265
+ end
266
+ end
267
+
268
+ context "building resources" do
269
+ context "when request_new_object_on_build is not set (default)" do
270
+ before do
271
+ spawn_model("Foo::User")
272
+ end
273
+
274
+ 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"
279
+ end
280
+ end
281
+
282
+ context "when request_new_object_on_build is set" do
283
+ before do
284
+ Her::API.setup :url => "https://api.example.com" do |builder|
285
+ builder.use Her::Middleware::FirstLevelParseJSON
286
+ builder.use Faraday::Request::UrlEncoded
287
+ builder.adapter :test do |stub|
288
+ stub.get("/users/new") { |env| ok! :id => nil, :fullname => params(env)[:fullname], :email => "tobias@bluthcompany.com" }
289
+ end
290
+ end
291
+
292
+ spawn_model("Foo::User") { request_new_object_on_build true }
293
+ end
294
+
295
+ 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"
301
+ end
302
+ end
303
+ end
304
+
305
+ context "creating resources" do
306
+ before do
307
+ Her::API.setup :url => "https://api.example.com" do |builder|
308
+ builder.use Her::Middleware::FirstLevelParseJSON
309
+ builder.use Faraday::Request::UrlEncoded
310
+ 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] }
313
+ end
314
+ end
315
+
316
+ spawn_model "Foo::User"
317
+ spawn_model "Foo::Company"
318
+ end
319
+
320
+ 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"
325
+ end
326
+
327
+ 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"
331
+ end
332
+
333
+ 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"
337
+ end
338
+
339
+ it "returns false when #save gets errors" do
340
+ @company = Foo::Company.new
341
+ @company.save.should be_falsey
342
+ end
343
+
344
+ it "raises ResourceInvalid when #save! gets errors" do
345
+ @company = Foo::Company.new
346
+ expect { @company.save! }.to raise_error Her::Errors::ResourceInvalid, "Remote validation failed: name is required"
347
+ end
348
+
349
+ 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."
353
+ end
354
+ end
355
+
356
+ context "updating resources" do
357
+ before do
358
+ Her::API.setup :url => "https://api.example.com" do |builder|
359
+ builder.use Her::Middleware::FirstLevelParseJSON
360
+ builder.use Faraday::Request::UrlEncoded
361
+ 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] }
364
+ end
365
+ end
366
+
367
+ spawn_model "Foo::User"
368
+ end
369
+
370
+ it "handle resource data update without saving it" do
371
+ @user = Foo::User.find(1)
372
+ @user.fullname.should == "Tobias Fünke"
373
+ @user.fullname = "Kittie Sanchez"
374
+ @user.fullname.should == "Kittie Sanchez"
375
+ end
376
+
377
+ 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"
380
+ end
381
+
382
+ it "handle resource update through #save on an existing resource" do
383
+ @user = Foo::User.find(1)
384
+ @user.fullname = "Lindsay Fünke"
385
+ @user.save
386
+ @user.fullname.should == "Lindsay Fünke"
387
+ end
388
+ end
389
+
390
+ context "deleting resources" do
391
+ before do
392
+ Her::API.setup :url => "https://api.example.com" do |builder|
393
+ builder.use Her::Middleware::FirstLevelParseJSON
394
+ builder.use Faraday::Request::UrlEncoded
395
+ 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] }
398
+ end
399
+ end
400
+
401
+ spawn_model "Foo::User"
402
+ end
403
+
404
+ it "handle resource deletion through the .destroy class method" do
405
+ @user = Foo::User.destroy_existing(1)
406
+ @user.active.should be_falsey
407
+ @user.should be_destroyed
408
+ end
409
+
410
+ it "handle resource deletion through #destroy on an existing resource" do
411
+ @user = Foo::User.find(1)
412
+ @user.destroy
413
+ @user.active.should be_falsey
414
+ @user.should be_destroyed
415
+ end
416
+
417
+ context "with params" do
418
+ before do
419
+ Her::API.setup :url => "https://api.example.com" do |builder|
420
+ builder.use Her::Middleware::FirstLevelParseJSON
421
+ builder.use Faraday::Request::UrlEncoded
422
+ 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] }
424
+ end
425
+ end
426
+ end
427
+
428
+ 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
432
+ end
433
+
434
+ it "handle resource deletion through #destroy on an existing resource" do
435
+ @user = Foo::User.find(1)
436
+ @user.destroy(delete_type: 'soft')
437
+ @user.active.should be_false
438
+ @user.should be_destroyed
439
+ end
440
+ end
441
+ end
442
+
443
+ context 'customizing HTTP methods' do
444
+ before do
445
+ Her::API.setup :url => "https://api.example.com" do |builder|
446
+ builder.use Her::Middleware::FirstLevelParseJSON
447
+ builder.use Faraday::Request::UrlEncoded
448
+ end
449
+ end
450
+
451
+ context 'create' do
452
+ before do
453
+ Her::API.default_api.connection.adapter :test do |stub|
454
+ stub.put('/users') { |env| [200, {}, { :id => 1, :fullname => 'Tobias Fünke' }.to_json] }
455
+ end
456
+ spawn_model 'Foo::User' do
457
+ attributes :fullname, :email
458
+ method_for :create, 'PUT'
459
+ end
460
+ end
461
+
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
467
+ end
468
+ end
469
+
470
+ context 'for children class' do
471
+ before do
472
+ class User < Foo::User; end
473
+ @spawned_models << :User
474
+ end
475
+
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
480
+ end
481
+ end
482
+ end
483
+
484
+ context 'update' do
485
+ before do
486
+ 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] }
489
+ end
490
+
491
+ spawn_model 'Foo::User' do
492
+ attributes :fullname, :email
493
+ method_for :update, :post
494
+ end
495
+ end
496
+
497
+ it 'uses the custom method (POST) instead of default method (PUT)' do
498
+ user = Foo::User.find(1)
499
+ user.fullname.should eq 'Lindsay Fünke'
500
+ user.fullname = 'Toby Fünke'
501
+ user.save
502
+ user.fullname.should eq 'Tobias Fünke'
503
+ end
504
+ end
505
+ end
506
+ end