her5 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
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