herr 0.7.3

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 (68) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +15 -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 +990 -0
  10. data/Rakefile +11 -0
  11. data/UPGRADE.md +81 -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/her.gemspec +30 -0
  17. data/lib/her.rb +16 -0
  18. data/lib/her/api.rb +115 -0
  19. data/lib/her/collection.rb +12 -0
  20. data/lib/her/errors.rb +27 -0
  21. data/lib/her/middleware.rb +10 -0
  22. data/lib/her/middleware/accept_json.rb +17 -0
  23. data/lib/her/middleware/first_level_parse_json.rb +36 -0
  24. data/lib/her/middleware/parse_json.rb +21 -0
  25. data/lib/her/middleware/second_level_parse_json.rb +36 -0
  26. data/lib/her/model.rb +72 -0
  27. data/lib/her/model/associations.rb +141 -0
  28. data/lib/her/model/associations/association.rb +103 -0
  29. data/lib/her/model/associations/association_proxy.rb +46 -0
  30. data/lib/her/model/associations/belongs_to_association.rb +96 -0
  31. data/lib/her/model/associations/has_many_association.rb +100 -0
  32. data/lib/her/model/associations/has_one_association.rb +79 -0
  33. data/lib/her/model/attributes.rb +266 -0
  34. data/lib/her/model/base.rb +33 -0
  35. data/lib/her/model/deprecated_methods.rb +61 -0
  36. data/lib/her/model/http.rb +114 -0
  37. data/lib/her/model/introspection.rb +65 -0
  38. data/lib/her/model/nested_attributes.rb +45 -0
  39. data/lib/her/model/orm.rb +205 -0
  40. data/lib/her/model/parse.rb +227 -0
  41. data/lib/her/model/paths.rb +121 -0
  42. data/lib/her/model/relation.rb +164 -0
  43. data/lib/her/version.rb +3 -0
  44. data/spec/api_spec.rb +131 -0
  45. data/spec/collection_spec.rb +26 -0
  46. data/spec/middleware/accept_json_spec.rb +10 -0
  47. data/spec/middleware/first_level_parse_json_spec.rb +62 -0
  48. data/spec/middleware/second_level_parse_json_spec.rb +35 -0
  49. data/spec/model/associations_spec.rb +416 -0
  50. data/spec/model/attributes_spec.rb +268 -0
  51. data/spec/model/callbacks_spec.rb +145 -0
  52. data/spec/model/dirty_spec.rb +86 -0
  53. data/spec/model/http_spec.rb +194 -0
  54. data/spec/model/introspection_spec.rb +76 -0
  55. data/spec/model/nested_attributes_spec.rb +134 -0
  56. data/spec/model/orm_spec.rb +479 -0
  57. data/spec/model/parse_spec.rb +373 -0
  58. data/spec/model/paths_spec.rb +341 -0
  59. data/spec/model/relation_spec.rb +226 -0
  60. data/spec/model/validations_spec.rb +42 -0
  61. data/spec/model_spec.rb +31 -0
  62. data/spec/spec_helper.rb +26 -0
  63. data/spec/support/extensions/array.rb +5 -0
  64. data/spec/support/extensions/hash.rb +5 -0
  65. data/spec/support/macros/her_macros.rb +17 -0
  66. data/spec/support/macros/model_macros.rb +29 -0
  67. data/spec/support/macros/request_macros.rb +27 -0
  68. metadata +280 -0
@@ -0,0 +1,373 @@
1
+ # encoding: utf-8
2
+ require File.join(File.dirname(__FILE__), "../spec_helper.rb")
3
+
4
+ describe Her::Model::Parse do
5
+ context "when include_root_in_json is set" do
6
+ before do
7
+ Her::API.setup :url => "https://api.example.com" do |builder|
8
+ builder.use Her::Middleware::FirstLevelParseJSON
9
+ builder.use Faraday::Request::UrlEncoded
10
+ end
11
+
12
+ Her::API.default_api.connection.adapter :test do |stub|
13
+ stub.post("/users") { |env| [200, {}, { :user => { :id => 1, :fullname => params(env)[:user][:fullname] } }.to_json] }
14
+ stub.post("/users/admins") { |env| [200, {}, { :user => { :id => 1, :fullname => params(env)[:user][:fullname] } }.to_json] }
15
+ end
16
+ end
17
+
18
+ context "to true" do
19
+ before do
20
+ spawn_model "Foo::User" do
21
+ include_root_in_json true
22
+ parse_root_in_json true
23
+ custom_post :admins
24
+ end
25
+ end
26
+
27
+ it "wraps params in the element name in `to_params`" do
28
+ @new_user = Foo::User.new(:fullname => "Tobias Fünke")
29
+ @new_user.to_params.should == { :user => { :fullname => "Tobias Fünke" } }
30
+ end
31
+
32
+ it "wraps params in the element name in `.create`" do
33
+ @new_user = Foo::User.admins(:fullname => "Tobias Fünke")
34
+ @new_user.fullname.should == "Tobias Fünke"
35
+ end
36
+ end
37
+
38
+ context "to a symbol" do
39
+ before do
40
+ spawn_model "Foo::User" do
41
+ include_root_in_json :person
42
+ parse_root_in_json :person
43
+ end
44
+ end
45
+
46
+ it "wraps params in the specified value" do
47
+ @new_user = Foo::User.new(:fullname => "Tobias Fünke")
48
+ @new_user.to_params.should == { :person => { :fullname => "Tobias Fünke" } }
49
+ end
50
+ end
51
+
52
+ context "in the parent class" do
53
+ before do
54
+ spawn_model("Foo::Model") { include_root_in_json true }
55
+
56
+ class User < Foo::Model; end
57
+ @spawned_models << :User
58
+ end
59
+
60
+ it "wraps params with the class name" do
61
+ @new_user = User.new(:fullname => "Tobias Fünke")
62
+ @new_user.to_params.should == { :user => { :fullname => "Tobias Fünke" } }
63
+ end
64
+ end
65
+ end
66
+
67
+ context "when parse_root_in_json is set" do
68
+ before do
69
+ Her::API.setup :url => "https://api.example.com" do |builder|
70
+ builder.use Her::Middleware::FirstLevelParseJSON
71
+ builder.use Faraday::Request::UrlEncoded
72
+ end
73
+ end
74
+
75
+ context "to true" do
76
+ before do
77
+ Her::API.default_api.connection.adapter :test do |stub|
78
+ stub.post("/users") { |env| [200, {}, { :user => { :id => 1, :fullname => "Lindsay Fünke" } }.to_json] }
79
+ stub.get("/users") { |env| [200, {}, [{ :user => { :id => 1, :fullname => "Lindsay Fünke" } }].to_json] }
80
+ stub.get("/users/admins") { |env| [200, {}, [{ :user => { :id => 1, :fullname => "Lindsay Fünke" } }].to_json] }
81
+ stub.get("/users/1") { |env| [200, {}, { :user => { :id => 1, :fullname => "Lindsay Fünke" } }.to_json] }
82
+ stub.put("/users/1") { |env| [200, {}, { :user => { :id => 1, :fullname => "Tobias Fünke Jr." } }.to_json] }
83
+ end
84
+
85
+ spawn_model("Foo::User") do
86
+ parse_root_in_json true
87
+ custom_get :admins
88
+ end
89
+ end
90
+
91
+ it "parse the data from the JSON root element after .create" do
92
+ @new_user = Foo::User.create(:fullname => "Lindsay Fünke")
93
+ @new_user.fullname.should == "Lindsay Fünke"
94
+ end
95
+
96
+ it "parse the data from the JSON root element after an arbitrary HTTP request" do
97
+ @new_user = Foo::User.admins
98
+ @new_user.first.fullname.should == "Lindsay Fünke"
99
+ end
100
+
101
+ it "parse the data from the JSON root element after .all" do
102
+ @users = Foo::User.all
103
+ @users.first.fullname.should == "Lindsay Fünke"
104
+ end
105
+
106
+ it "parse the data from the JSON root element after .find" do
107
+ @user = Foo::User.find(1)
108
+ @user.fullname.should == "Lindsay Fünke"
109
+ end
110
+
111
+ it "parse the data from the JSON root element after .save" do
112
+ @user = Foo::User.find(1)
113
+ @user.fullname = "Tobias Fünke"
114
+ @user.save
115
+ @user.fullname.should == "Tobias Fünke Jr."
116
+ end
117
+ end
118
+
119
+ context "to a symbol" do
120
+ before do
121
+ Her::API.default_api.connection.adapter :test do |stub|
122
+ stub.post("/users") { |env| [200, {}, { :person => { :id => 1, :fullname => "Lindsay Fünke" } }.to_json] }
123
+ end
124
+
125
+ spawn_model("Foo::User") { parse_root_in_json :person }
126
+ end
127
+
128
+ it "parse the data with the symbol" do
129
+ @new_user = Foo::User.create(:fullname => "Lindsay Fünke")
130
+ @new_user.fullname.should == "Lindsay Fünke"
131
+ end
132
+ end
133
+
134
+ context "in the parent class" do
135
+ before do
136
+ Her::API.default_api.connection.adapter :test do |stub|
137
+ stub.post("/users") { |env| [200, {}, { :user => { :id => 1, :fullname => "Lindsay Fünke" } }.to_json] }
138
+ stub.get("/users") { |env| [200, {}, { :users => [ { :id => 1, :fullname => "Lindsay Fünke" } ] }.to_json] }
139
+ end
140
+
141
+ spawn_model("Foo::Model") { parse_root_in_json true, format: :active_model_serializers }
142
+ class User < Foo::Model
143
+ collection_path "/users"
144
+ end
145
+
146
+ @spawned_models << :User
147
+ end
148
+
149
+ it "parse the data with the symbol" do
150
+ @new_user = User.create(:fullname => "Lindsay Fünke")
151
+ @new_user.fullname.should == "Lindsay Fünke"
152
+ end
153
+
154
+ it "parses the collection of data" do
155
+ @users = User.all
156
+ @users.first.fullname.should == "Lindsay Fünke"
157
+ end
158
+ end
159
+
160
+ context "to true with :format => :active_model_serializers" do
161
+ before do
162
+ Her::API.default_api.connection.adapter :test do |stub|
163
+ stub.post("/users") { |env| [200, {}, { :user => { :id => 1, :fullname => "Lindsay Fünke" } }.to_json] }
164
+ stub.get("/users") { |env| [200, {}, { :users => [ { :id => 1, :fullname => "Lindsay Fünke" } ] }.to_json] }
165
+ stub.get("/users/admins") { |env| [200, {}, { :users => [ { :id => 1, :fullname => "Lindsay Fünke" } ] }.to_json] }
166
+ stub.get("/users/1") { |env| [200, {}, { :user => { :id => 1, :fullname => "Lindsay Fünke" } }.to_json] }
167
+ stub.put("/users/1") { |env| [200, {}, { :user => { :id => 1, :fullname => "Tobias Fünke Jr." } }.to_json] }
168
+ end
169
+
170
+ spawn_model("Foo::User") do
171
+ parse_root_in_json true, :format => :active_model_serializers
172
+ custom_get :admins
173
+ end
174
+ end
175
+
176
+ it "parse the data from the JSON root element after .create" do
177
+ @new_user = Foo::User.create(:fullname => "Lindsay Fünke")
178
+ @new_user.fullname.should == "Lindsay Fünke"
179
+ end
180
+
181
+ it "parse the data from the JSON root element after an arbitrary HTTP request" do
182
+ @users = Foo::User.admins
183
+ @users.first.fullname.should == "Lindsay Fünke"
184
+ end
185
+
186
+ it "parse the data from the JSON root element after .all" do
187
+ @users = Foo::User.all
188
+ @users.first.fullname.should == "Lindsay Fünke"
189
+ end
190
+
191
+ it "parse the data from the JSON root element after .find" do
192
+ @user = Foo::User.find(1)
193
+ @user.fullname.should == "Lindsay Fünke"
194
+ end
195
+
196
+ it "parse the data from the JSON root element after .save" do
197
+ @user = Foo::User.find(1)
198
+ @user.fullname = "Tobias Fünke"
199
+ @user.save
200
+ @user.fullname.should == "Tobias Fünke Jr."
201
+ end
202
+ end
203
+ end
204
+
205
+ context "when to_params is set" do
206
+ before do
207
+ Her::API.setup :url => "https://api.example.com" do |builder|
208
+ builder.use Her::Middleware::FirstLevelParseJSON
209
+ builder.use Faraday::Request::UrlEncoded
210
+ builder.adapter :test do |stub|
211
+ stub.post("/users") { |env| ok! :id => 1, :fullname => params(env)['fullname'] }
212
+ end
213
+ end
214
+
215
+ spawn_model "Foo::User" do
216
+ def to_params
217
+ { :fullname => "Lindsay Fünke" }
218
+ end
219
+ end
220
+ end
221
+
222
+ it "changes the request parameters for one-line resource creation" do
223
+ @user = Foo::User.create(:fullname => "Tobias Fünke")
224
+ @user.fullname.should == "Lindsay Fünke"
225
+ end
226
+
227
+ it "changes the request parameters for Model.new + #save" do
228
+ @user = Foo::User.new(:fullname => "Tobias Fünke")
229
+ @user.save
230
+ @user.fullname.should == "Lindsay Fünke"
231
+ end
232
+ end
233
+
234
+ context "when parse_root_in_json set json_api to true" do
235
+ before do
236
+ Her::API.setup :url => "https://api.example.com" do |builder|
237
+ builder.use Her::Middleware::FirstLevelParseJSON
238
+ builder.use Faraday::Request::UrlEncoded
239
+ builder.adapter :test do |stub|
240
+ stub.get("/users") { |env| [200, {}, { :users => [{ :id => 1, :fullname => "Lindsay Fünke" }] }.to_json] }
241
+ stub.get("/users/admins") { |env| [200, {}, { :users => [{ :id => 1, :fullname => "Lindsay Fünke" }] }.to_json] }
242
+ stub.get("/users/1") { |env| [200, {}, { :users => [{ :id => 1, :fullname => "Lindsay Fünke" }] }.to_json] }
243
+ stub.post("/users") { |env| [200, {}, { :users => [{ :fullname => "Lindsay Fünke" }] }.to_json] }
244
+ stub.put("/users/1") { |env| [200, {}, { :users => [{ :id => 1, :fullname => "Tobias Fünke Jr." }] }.to_json] }
245
+ end
246
+ end
247
+
248
+ spawn_model("Foo::User") do
249
+ parse_root_in_json true, :format => :json_api
250
+ include_root_in_json true
251
+ custom_get :admins
252
+ end
253
+ end
254
+ context "when the API we are hitting responds with an 'individual resource object' " do
255
+ # reference for JSON api behavior here:
256
+ # http://jsonapi.org/format/#document-structure-individual-resource-representations
257
+ let(:individual_object) do
258
+ #This is a valid way to return json api responses
259
+ { :id => 1, :fullname => "Lindsay Fünke" }
260
+ end
261
+ before do
262
+ Her::API.setup :url => "https://api.example.com" do |builder|
263
+ builder.use Her::Middleware::FirstLevelParseJSON
264
+ builder.use Faraday::Request::UrlEncoded
265
+ builder.adapter :test do |stub|
266
+ stub.get("/users/1") { |env| [200, {}, { :users => individual_object }.to_json] }
267
+ end
268
+ end
269
+
270
+ spawn_model("Foo::User") do
271
+ parse_root_in_json true, :format => :json_api
272
+ include_root_in_json true
273
+ custom_get :admins
274
+ end
275
+ end
276
+
277
+ it "parse the data from the JSON root element after .find" do
278
+ @user = Foo::User.find(1)
279
+ @user.fullname.should == "Lindsay Fünke"
280
+ end
281
+ end
282
+
283
+ it "parse the data from the JSON root element after .create" do
284
+ @new_user = Foo::User.create(:fullname => "Lindsay Fünke")
285
+ @new_user.fullname.should == "Lindsay Fünke"
286
+ end
287
+
288
+ it "parse the data from the JSON root element after an arbitrary HTTP request" do
289
+ @new_user = Foo::User.admins
290
+ @new_user.first.fullname.should == "Lindsay Fünke"
291
+ end
292
+
293
+ it "parse the data from the JSON root element after .all" do
294
+ @users = Foo::User.all
295
+ @users.first.fullname.should == "Lindsay Fünke"
296
+ end
297
+
298
+ it "parse the data from the JSON root element after .find" do
299
+ @user = Foo::User.find(1)
300
+ @user.fullname.should == "Lindsay Fünke"
301
+ end
302
+
303
+ it "parse the data from the JSON root element after .save" do
304
+ @user = Foo::User.find(1)
305
+ @user.fullname = "Tobias Fünke"
306
+ @user.save
307
+ @user.fullname.should == "Tobias Fünke Jr."
308
+ end
309
+
310
+ it "parse the data from the JSON root element after new/save" do
311
+ @user = Foo::User.new
312
+ @user.fullname = "Lindsay Fünke (before save)"
313
+ @user.save
314
+ @user.fullname.should == "Lindsay Fünke"
315
+ end
316
+ end
317
+
318
+ context "when include_root_in_json set json_api" do
319
+ before do
320
+ Her::API.setup :url => "https://api.example.com" do |builder|
321
+ builder.use Her::Middleware::FirstLevelParseJSON
322
+ builder.use Faraday::Request::UrlEncoded
323
+ end
324
+
325
+ Her::API.default_api.connection.adapter :test do |stub|
326
+ stub.post("/users") { |env| [200, {}, { :users => [{ :id => 1, :fullname => params(env)[:users][:fullname] }] }.to_json] }
327
+ end
328
+ end
329
+
330
+ context "to true" do
331
+ before do
332
+ spawn_model "Foo::User" do
333
+ include_root_in_json true
334
+ parse_root_in_json true, format: :json_api
335
+ custom_post :admins
336
+ end
337
+ end
338
+
339
+ it "wraps params in the element name in `to_params`" do
340
+ @new_user = Foo::User.new(:fullname => "Tobias Fünke")
341
+ @new_user.to_params.should == { :users => [{ :fullname => "Tobias Fünke" }] }
342
+ end
343
+
344
+ it "wraps params in the element name in `.where`" do
345
+ @new_user = Foo::User.where(:fullname => "Tobias Fünke").build
346
+ @new_user.fullname.should == "Tobias Fünke"
347
+ end
348
+ end
349
+ end
350
+
351
+ context 'when send_only_modified_attributes is set' do
352
+ before do
353
+ Her::API.setup :url => "https://api.example.com", :send_only_modified_attributes => true do |builder|
354
+ builder.use Her::Middleware::FirstLevelParseJSON
355
+ builder.use Faraday::Request::UrlEncoded
356
+ end
357
+
358
+ Her::API.default_api.connection.adapter :test do |stub|
359
+ stub.get("/users/1") { |env| [200, {}, { :id => 1, :first_name => "Gooby", :last_name => "Pls" }.to_json] }
360
+ end
361
+
362
+ spawn_model "Foo::User" do
363
+ include_root_in_json true
364
+ end
365
+ end
366
+
367
+ it 'only sends the attributes that were modified' do
368
+ user = Foo::User.find 1
369
+ user.first_name = 'Someone'
370
+ expect(user.to_params).to eql(:user => {:first_name => 'Someone'})
371
+ end
372
+ end
373
+ end
@@ -0,0 +1,341 @@
1
+ # encoding: utf-8
2
+ require File.join(File.dirname(__FILE__), "../spec_helper.rb")
3
+
4
+ describe Her::Model::Paths do
5
+ context "building request paths" do
6
+ context "simple model" do
7
+ before do
8
+ spawn_model "Foo::User"
9
+ end
10
+
11
+ describe "#build_request_path" do
12
+ it "builds paths with defaults" do
13
+ Foo::User.build_request_path(:id => "foo").should == "users/foo"
14
+ Foo::User.build_request_path(:id => nil).should == "users"
15
+ Foo::User.build_request_path.should == "users"
16
+ end
17
+
18
+ it "builds paths with custom collection path" do
19
+ Foo::User.collection_path "/utilisateurs"
20
+ Foo::User.build_request_path(:id => "foo").should == "/utilisateurs/foo"
21
+ Foo::User.build_request_path.should == "/utilisateurs"
22
+ end
23
+
24
+ it "builds paths with custom relative collection path" do
25
+ Foo::User.collection_path "utilisateurs"
26
+ Foo::User.build_request_path(:id => "foo").should == "utilisateurs/foo"
27
+ Foo::User.build_request_path.should == "utilisateurs"
28
+ end
29
+
30
+ it "builds paths with custom collection path with multiple variables" do
31
+ Foo::User.collection_path "/organizations/:organization_id/utilisateurs"
32
+
33
+ Foo::User.build_request_path(:id => "foo", :_organization_id => "acme").should == "/organizations/acme/utilisateurs/foo"
34
+ Foo::User.build_request_path(:_organization_id => "acme").should == "/organizations/acme/utilisateurs"
35
+
36
+ Foo::User.build_request_path(:id => "foo", :organization_id => "acme").should == "/organizations/acme/utilisateurs/foo"
37
+ Foo::User.build_request_path(:organization_id => "acme").should == "/organizations/acme/utilisateurs"
38
+ end
39
+
40
+ it "builds paths with custom relative collection path with multiple variables" do
41
+ Foo::User.collection_path "organizations/:organization_id/utilisateurs"
42
+
43
+ Foo::User.build_request_path(:id => "foo", :_organization_id => "acme").should == "organizations/acme/utilisateurs/foo"
44
+ Foo::User.build_request_path(:_organization_id => "acme").should == "organizations/acme/utilisateurs"
45
+
46
+ Foo::User.build_request_path(:id => "foo", :organization_id => "acme").should == "organizations/acme/utilisateurs/foo"
47
+ Foo::User.build_request_path(:organization_id => "acme").should == "organizations/acme/utilisateurs"
48
+ end
49
+
50
+ it "builds paths with custom item path" do
51
+ Foo::User.resource_path "/utilisateurs/:id"
52
+ Foo::User.build_request_path(:id => "foo").should == "/utilisateurs/foo"
53
+ Foo::User.build_request_path.should == "users"
54
+ end
55
+
56
+ it "builds paths with custom relative item path" do
57
+ Foo::User.resource_path "utilisateurs/:id"
58
+ Foo::User.build_request_path(:id => "foo").should == "utilisateurs/foo"
59
+ Foo::User.build_request_path.should == "users"
60
+ end
61
+
62
+ it "raises exceptions when building a path without required custom variables" do
63
+ Foo::User.collection_path "/organizations/:organization_id/utilisateurs"
64
+ expect { Foo::User.build_request_path(:id => "foo") }.to raise_error(Her::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `/organizations/:organization_id/utilisateurs/:id`. Parameters are `{:id=>\"foo\"}`.")
65
+ end
66
+ end
67
+ end
68
+
69
+ context "simple model with multiple words" do
70
+ before do
71
+ spawn_model "Foo::AdminUser"
72
+ end
73
+
74
+ describe "#build_request_path" do
75
+ it "builds paths with defaults" do
76
+ Foo::AdminUser.build_request_path(:id => "foo").should == "admin_users/foo"
77
+ Foo::AdminUser.build_request_path.should == "admin_users"
78
+ end
79
+
80
+ it "builds paths with custom collection path" do
81
+ Foo::AdminUser.collection_path "/users"
82
+ Foo::AdminUser.build_request_path(:id => "foo").should == "/users/foo"
83
+ Foo::AdminUser.build_request_path.should == "/users"
84
+ end
85
+
86
+ it "builds paths with custom relative collection path" do
87
+ Foo::AdminUser.collection_path "users"
88
+ Foo::AdminUser.build_request_path(:id => "foo").should == "users/foo"
89
+ Foo::AdminUser.build_request_path.should == "users"
90
+ end
91
+
92
+ it "builds paths with custom collection path with multiple variables" do
93
+ Foo::AdminUser.collection_path "/organizations/:organization_id/users"
94
+ Foo::AdminUser.build_request_path(:id => "foo", :_organization_id => "acme").should == "/organizations/acme/users/foo"
95
+ Foo::AdminUser.build_request_path(:_organization_id => "acme").should == "/organizations/acme/users"
96
+ end
97
+
98
+ it "builds paths with custom relative collection path with multiple variables" do
99
+ Foo::AdminUser.collection_path "organizations/:organization_id/users"
100
+ Foo::AdminUser.build_request_path(:id => "foo", :_organization_id => "acme").should == "organizations/acme/users/foo"
101
+ Foo::AdminUser.build_request_path(:_organization_id => "acme").should == "organizations/acme/users"
102
+ end
103
+
104
+ it "builds paths with custom item path" do
105
+ Foo::AdminUser.resource_path "/users/:id"
106
+ Foo::AdminUser.build_request_path(:id => "foo").should == "/users/foo"
107
+ Foo::AdminUser.build_request_path.should == "admin_users"
108
+ end
109
+
110
+ it "builds paths with custom relative item path" do
111
+ Foo::AdminUser.resource_path "users/:id"
112
+ Foo::AdminUser.build_request_path(:id => "foo").should == "users/foo"
113
+ Foo::AdminUser.build_request_path.should == "admin_users"
114
+ end
115
+
116
+ it "raises exceptions when building a path without required custom variables" do
117
+ Foo::AdminUser.collection_path "/organizations/:organization_id/users"
118
+ expect { Foo::AdminUser.build_request_path(:id => "foo") }.to raise_error(Her::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `/organizations/:organization_id/users/:id`. Parameters are `{:id=>\"foo\"}`.")
119
+ end
120
+
121
+ it "raises exceptions when building a relative path without required custom variables" do
122
+ Foo::AdminUser.collection_path "organizations/:organization_id/users"
123
+ expect { Foo::AdminUser.build_request_path(:id => "foo") }.to raise_error(Her::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `organizations/:organization_id/users/:id`. Parameters are `{:id=>\"foo\"}`.")
124
+ end
125
+ end
126
+ end
127
+
128
+ context "children model" do
129
+ before do
130
+ Her::API.setup :url => "https://api.example.com" do |builder|
131
+ builder.use Her::Middleware::FirstLevelParseJSON
132
+ builder.use Faraday::Request::UrlEncoded
133
+ builder.adapter :test do |stub|
134
+ stub.get("/users/foo") { |env| [200, {}, { :id => 'foo' }.to_json] }
135
+ end
136
+ end
137
+
138
+ spawn_model("Foo::Model") { include_root_in_json true }
139
+
140
+ class User < Foo::Model; end
141
+ @spawned_models << :User
142
+ end
143
+
144
+ it "builds path using the children model name" do
145
+ User.find('foo').id.should == 'foo'
146
+ User.find('foo').id.should == 'foo'
147
+ end
148
+ end
149
+
150
+ context "nested model" do
151
+ before do
152
+ spawn_model "Foo::User"
153
+ end
154
+
155
+ describe "#build_request_path" do
156
+ it "builds paths with defaults" do
157
+ Foo::User.build_request_path(:id => "foo").should == "users/foo"
158
+ Foo::User.build_request_path.should == "users"
159
+ end
160
+ end
161
+ end
162
+
163
+ context 'custom primary key' do
164
+ before do
165
+ spawn_model 'User' do
166
+ primary_key 'UserId'
167
+ resource_path 'users/:UserId'
168
+ end
169
+
170
+ spawn_model 'Customer' do
171
+ primary_key :customer_id
172
+ resource_path 'customers/:id'
173
+ end
174
+ end
175
+
176
+ describe '#build_request_path' do
177
+ it 'uses the correct primary key attribute' do
178
+ User.build_request_path(:UserId => 'foo').should == 'users/foo'
179
+ User.build_request_path(:id => 'foo').should == 'users'
180
+ end
181
+
182
+ it 'replaces :id with the appropriate primary key' do
183
+ Customer.build_request_path(:customer_id => 'joe').should == 'customers/joe'
184
+ Customer.build_request_path(:id => 'joe').should == 'customers'
185
+ end
186
+ end
187
+ end
188
+ end
189
+
190
+ context "making subdomain HTTP requests" do
191
+ before do
192
+ Her::API.setup :url => "https://api.example.com/" do |builder|
193
+ builder.use Her::Middleware::FirstLevelParseJSON
194
+ builder.use Faraday::Request::UrlEncoded
195
+ builder.adapter :test do |stub|
196
+ stub.get("organizations/2/users") { |env| [200, {}, [{ :id => 1, :fullname => "Tobias Fünke", :organization_id => 2 }, { :id => 2, :fullname => "Lindsay Fünke", :organization_id => 2 }].to_json] }
197
+ stub.post("organizations/2/users") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :organization_id => 2 }.to_json] }
198
+ stub.put("organizations/2/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke", :organization_id => 2 }.to_json] }
199
+ stub.get("organizations/2/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :organization_id => 2, :active => true }.to_json] }
200
+ stub.delete("organizations/2/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke", :organization_id => 2, :active => false }.to_json] }
201
+ end
202
+ end
203
+
204
+ spawn_model "Foo::User" do
205
+ collection_path "organizations/:organization_id/users"
206
+ end
207
+ end
208
+
209
+ describe "fetching a resource" do
210
+ it "maps a single resource to a Ruby object" do
211
+ @user = Foo::User.find(1, :_organization_id => 2)
212
+ @user.id.should == 1
213
+ @user.fullname.should == "Tobias Fünke"
214
+ end
215
+
216
+ it "maps a single resource using a scope to a Ruby object" do
217
+ Foo::User.scope :for_organization, lambda { |o| where(:organization_id => o) }
218
+ @user = Foo::User.for_organization(2).find(1)
219
+ @user.id.should == 1
220
+ @user.fullname.should == "Tobias Fünke"
221
+ end
222
+ end
223
+
224
+ describe "fetching a collection" do
225
+ it "maps a collection of resources to an array of Ruby objects" do
226
+ @users = Foo::User.where(:_organization_id => 2).all
227
+ @users.length.should == 2
228
+ @users.first.fullname.should == "Tobias Fünke"
229
+ end
230
+ end
231
+
232
+ describe "handling new resource" do
233
+ it "handles new resource" do
234
+ @new_user = Foo::User.new(:fullname => "Tobias Fünke", :organization_id => 2)
235
+ @new_user.new?.should be_truthy
236
+
237
+ @existing_user = Foo::User.find(1, :_organization_id => 2)
238
+ @existing_user.new?.should be_falsey
239
+ end
240
+ end
241
+
242
+ describe "creating resources" do
243
+ it "handle one-line resource creation" do
244
+ @user = Foo::User.create(:fullname => "Tobias Fünke", :organization_id => 2)
245
+ @user.id.should == 1
246
+ @user.fullname.should == "Tobias Fünke"
247
+ end
248
+
249
+ it "handle resource creation through Model.new + #save" do
250
+ @user = Foo::User.new(:fullname => "Tobias Fünke", :organization_id => 2)
251
+ @user.save
252
+ @user.fullname.should == "Tobias Fünke"
253
+ end
254
+ end
255
+
256
+ context "updating resources" do
257
+ it "handle resource data update without saving it" do
258
+ @user = Foo::User.find(1, :_organization_id => 2)
259
+ @user.fullname.should == "Tobias Fünke"
260
+ @user.fullname = "Kittie Sanchez"
261
+ @user.fullname.should == "Kittie Sanchez"
262
+ end
263
+
264
+ it "handle resource update through the .update class method" do
265
+ @user = Foo::User.save_existing(1, { :fullname => "Lindsay Fünke", :organization_id => 2 })
266
+ @user.fullname.should == "Lindsay Fünke"
267
+ end
268
+
269
+ it "handle resource update through #save on an existing resource" do
270
+ @user = Foo::User.find(1, :_organization_id => 2)
271
+ @user.fullname = "Lindsay Fünke"
272
+ @user.save
273
+ @user.fullname.should == "Lindsay Fünke"
274
+ end
275
+ end
276
+
277
+ context "deleting resources" do
278
+ it "handle resource deletion through the .destroy class method" do
279
+ @user = Foo::User.destroy_existing(1, :_organization_id => 2)
280
+ @user.active.should be_falsey
281
+ end
282
+
283
+ it "handle resource deletion through #destroy on an existing resource" do
284
+ @user = Foo::User.find(1, :_organization_id => 2)
285
+ @user.destroy
286
+ @user.active.should be_falsey
287
+ end
288
+ end
289
+ end
290
+
291
+ context "making path HTTP requests" do
292
+ before do
293
+ Her::API.setup :url => "https://example.com/api/" do |builder|
294
+ builder.use Her::Middleware::FirstLevelParseJSON
295
+ builder.use Faraday::Request::UrlEncoded
296
+ builder.adapter :test do |stub|
297
+ stub.get("/api/organizations/2/users") { |env| [200, {}, [{ :id => 1, :fullname => "Tobias Fünke", :organization_id => 2 }, { :id => 2, :fullname => "Lindsay Fünke", :organization_id => 2 }].to_json] }
298
+ stub.get("/api/organizations/2/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :organization_id => 2, :active => true }.to_json] }
299
+ end
300
+ end
301
+
302
+ spawn_model "Foo::User" do
303
+ collection_path "organizations/:organization_id/users"
304
+ end
305
+ end
306
+
307
+ describe "fetching a resource" do
308
+ it "maps a single resource to a Ruby object" do
309
+ @user = Foo::User.find(1, :_organization_id => 2)
310
+ @user.id.should == 1
311
+ @user.fullname.should == "Tobias Fünke"
312
+ end
313
+ end
314
+
315
+ describe "fetching a collection" do
316
+ it "maps a collection of resources to an array of Ruby objects" do
317
+ @users = Foo::User.where(:_organization_id => 2).all
318
+ @users.length.should == 2
319
+ @users.first.fullname.should == "Tobias Fünke"
320
+ end
321
+ end
322
+
323
+ describe "fetching a resource with absolute path" do
324
+ it "maps a single resource to a Ruby object" do
325
+ Foo::User.resource_path '/api/' + Foo::User.resource_path
326
+ @user = Foo::User.find(1, :_organization_id => 2)
327
+ @user.id.should == 1
328
+ @user.fullname.should == "Tobias Fünke"
329
+ end
330
+ end
331
+
332
+ describe "fetching a collection with absolute path" do
333
+ it "maps a collection of resources to an array of Ruby objects" do
334
+ Foo::User.collection_path '/api/' + Foo::User.collection_path
335
+ @users = Foo::User.where(:_organization_id => 2).all
336
+ @users.length.should == 2
337
+ @users.first.fullname.should == "Tobias Fünke"
338
+ end
339
+ end
340
+ end
341
+ end