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,345 @@
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
+
255
+ it "parse the data from the JSON root element after .create" do
256
+ @new_user = Foo::User.create(:fullname => "Lindsay Fünke")
257
+ @new_user.fullname.should == "Lindsay Fünke"
258
+ end
259
+
260
+ it "parse the data from the JSON root element after an arbitrary HTTP request" do
261
+ @new_user = Foo::User.admins
262
+ @new_user.first.fullname.should == "Lindsay Fünke"
263
+ end
264
+
265
+ it "parse the data from the JSON root element after .all" do
266
+ @users = Foo::User.all
267
+ @users.first.fullname.should == "Lindsay Fünke"
268
+ end
269
+
270
+ it "parse the data from the JSON root element after .find" do
271
+ @user = Foo::User.find(1)
272
+ @user.fullname.should == "Lindsay Fünke"
273
+ end
274
+
275
+ it "parse the data from the JSON root element after .save" do
276
+ @user = Foo::User.find(1)
277
+ @user.fullname = "Tobias Fünke"
278
+ @user.save
279
+ @user.fullname.should == "Tobias Fünke Jr."
280
+ end
281
+
282
+ it "parse the data from the JSON root element after new/save" do
283
+ @user = Foo::User.new
284
+ @user.fullname = "Lindsay Fünke (before save)"
285
+ @user.save
286
+ @user.fullname.should == "Lindsay Fünke"
287
+ end
288
+ end
289
+
290
+ context "when include_root_in_json set json_api" do
291
+ before do
292
+ Her::API.setup :url => "https://api.example.com" do |builder|
293
+ builder.use Her::Middleware::FirstLevelParseJSON
294
+ builder.use Faraday::Request::UrlEncoded
295
+ end
296
+
297
+ Her::API.default_api.connection.adapter :test do |stub|
298
+ stub.post("/users") { |env| [200, {}, { :users => [{ :id => 1, :fullname => params(env)[:users][:fullname] }] }.to_json] }
299
+ end
300
+ end
301
+
302
+ context "to true" do
303
+ before do
304
+ spawn_model "Foo::User" do
305
+ include_root_in_json true
306
+ parse_root_in_json true, format: :json_api
307
+ custom_post :admins
308
+ end
309
+ end
310
+
311
+ it "wraps params in the element name in `to_params`" do
312
+ @new_user = Foo::User.new(:fullname => "Tobias Fünke")
313
+ @new_user.to_params.should == { :users => [{ :fullname => "Tobias Fünke" }] }
314
+ end
315
+
316
+ it "wraps params in the element name in `.where`" do
317
+ @new_user = Foo::User.where(:fullname => "Tobias Fünke").build
318
+ @new_user.fullname.should == "Tobias Fünke"
319
+ end
320
+ end
321
+ end
322
+
323
+ context 'when send_only_modified_attributes is set' do
324
+ before do
325
+ Her::API.setup :url => "https://api.example.com", :send_only_modified_attributes => true do |builder|
326
+ builder.use Her::Middleware::FirstLevelParseJSON
327
+ builder.use Faraday::Request::UrlEncoded
328
+ end
329
+
330
+ Her::API.default_api.connection.adapter :test do |stub|
331
+ stub.get("/users/1") { |env| [200, {}, { :id => 1, :first_name => "Gooby", :last_name => "Pls" }.to_json] }
332
+ end
333
+
334
+ spawn_model "Foo::User" do
335
+ include_root_in_json true
336
+ end
337
+ end
338
+
339
+ it 'only sends the attributes that were modified' do
340
+ user = Foo::User.find 1
341
+ user.first_name = 'Someone'
342
+ expect(user.to_params).to eql(:user => {:first_name => 'Someone'})
343
+ end
344
+ end
345
+ end
@@ -0,0 +1,347 @@
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 "#request_path" do
12
+ it "builds paths with defaults" do
13
+ Foo::User.new(:id => "foo").request_path.should == "users/foo"
14
+ Foo::User.new(:id => nil).request_path.should == "users"
15
+ Foo::User.new().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.new(:id => "foo").request_path.should == "/utilisateurs/foo"
21
+ Foo::User.new().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.new(:id => "foo").request_path.should == "utilisateurs/foo"
27
+ Foo::User.new().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.new(:id => "foo").request_path(:_organization_id => "acme").should == "/organizations/acme/utilisateurs/foo"
34
+ Foo::User.new().request_path(:_organization_id => "acme").should == "/organizations/acme/utilisateurs"
35
+
36
+ Foo::User.new(:id => "foo", :organization_id => "acme").request_path.should == "/organizations/acme/utilisateurs/foo"
37
+ Foo::User.new(:organization_id => "acme").request_path.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.new(:id => "foo").request_path(:_organization_id => "acme").should == "organizations/acme/utilisateurs/foo"
44
+ Foo::User.new().request_path(:_organization_id => "acme").should == "organizations/acme/utilisateurs"
45
+
46
+ Foo::User.new(:id => "foo", :organization_id => "acme").request_path.should == "organizations/acme/utilisateurs/foo"
47
+ Foo::User.new(:organization_id => "acme").request_path.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.new(:id => "foo").request_path.should == "/utilisateurs/foo"
53
+ Foo::User.new().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.new(:id => "foo").request_path.should == "utilisateurs/foo"
59
+ Foo::User.new().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.new(:id => "foo").request_path }.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
+
67
+ it "escapes the variable values" do
68
+ Foo::User.collection_path "organizations/:organization_id/utilisateurs"
69
+ Foo::User.new(:id => "Привет").request_path(:_organization_id => 'лол').should == "organizations/%D0%BB%D0%BE%D0%BB/utilisateurs/%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82"
70
+ Foo::User.new(:organization_id => 'лол', :id => "Привет").request_path.should == "organizations/%D0%BB%D0%BE%D0%BB/utilisateurs/%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82"
71
+ end
72
+ end
73
+ end
74
+
75
+ context "simple model with multiple words" do
76
+ before do
77
+ spawn_model "Foo::AdminUser"
78
+ end
79
+
80
+ describe "#request_path" do
81
+ it "builds paths with defaults" do
82
+ Foo::AdminUser.new(:id => "foo").request_path.should == "admin_users/foo"
83
+ Foo::AdminUser.new().request_path.should == "admin_users"
84
+ end
85
+
86
+ it "builds paths with custom collection path" do
87
+ Foo::AdminUser.collection_path "/users"
88
+ Foo::AdminUser.new(:id => "foo").request_path.should == "/users/foo"
89
+ Foo::AdminUser.new().request_path.should == "/users"
90
+ end
91
+
92
+ it "builds paths with custom relative collection path" do
93
+ Foo::AdminUser.collection_path "users"
94
+ Foo::AdminUser.new(:id => "foo").request_path.should == "users/foo"
95
+ Foo::AdminUser.new().request_path.should == "users"
96
+ end
97
+
98
+ it "builds paths with custom collection path with multiple variables" do
99
+ Foo::AdminUser.collection_path "/organizations/:organization_id/users"
100
+ Foo::AdminUser.new(:id => "foo").request_path(:_organization_id => "acme").should == "/organizations/acme/users/foo"
101
+ Foo::AdminUser.new().request_path(:_organization_id => "acme").should == "/organizations/acme/users"
102
+ end
103
+
104
+ it "builds paths with custom relative collection path with multiple variables" do
105
+ Foo::AdminUser.collection_path "organizations/:organization_id/users"
106
+ Foo::AdminUser.new(:id => "foo").request_path(:_organization_id => "acme").should == "organizations/acme/users/foo"
107
+ Foo::AdminUser.new().request_path(:_organization_id => "acme").should == "organizations/acme/users"
108
+ end
109
+
110
+ it "builds paths with custom item path" do
111
+ Foo::AdminUser.resource_path "/users/:id"
112
+ Foo::AdminUser.new(:id => "foo").request_path.should == "/users/foo"
113
+ Foo::AdminUser.new().request_path.should == "admin_users"
114
+ end
115
+
116
+ it "builds paths with custom relative item path" do
117
+ Foo::AdminUser.resource_path "users/:id"
118
+ Foo::AdminUser.new(:id => "foo").request_path.should == "users/foo"
119
+ Foo::AdminUser.new().request_path.should == "admin_users"
120
+ end
121
+
122
+ it "raises exceptions when building a path without required custom variables" do
123
+ Foo::AdminUser.collection_path "/organizations/:organization_id/users"
124
+ expect { Foo::AdminUser.new(:id => "foo").request_path }.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\"}`.")
125
+ end
126
+
127
+ it "raises exceptions when building a relative path without required custom variables" do
128
+ Foo::AdminUser.collection_path "organizations/:organization_id/users"
129
+ expect { Foo::AdminUser.new(:id => "foo").request_path }.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\"}`.")
130
+ end
131
+ end
132
+ end
133
+
134
+ context "children model" do
135
+ before do
136
+ Her::API.setup :url => "https://api.example.com" do |builder|
137
+ builder.use Her::Middleware::FirstLevelParseJSON
138
+ builder.use Faraday::Request::UrlEncoded
139
+ builder.adapter :test do |stub|
140
+ stub.get("/users/foo") { |env| [200, {}, { :id => 'foo' }.to_json] }
141
+ end
142
+ end
143
+
144
+ spawn_model("Foo::Model") { include_root_in_json true }
145
+
146
+ class User < Foo::Model; end
147
+ @spawned_models << :User
148
+ end
149
+
150
+ it "builds path using the children model name" do
151
+ User.find('foo').id.should == 'foo'
152
+ User.find('foo').id.should == 'foo'
153
+ end
154
+ end
155
+
156
+ context "nested model" do
157
+ before do
158
+ spawn_model "Foo::User"
159
+ end
160
+
161
+ describe "#request_path" do
162
+ it "builds paths with defaults" do
163
+ Foo::User.new(:id => "foo").request_path.should == "users/foo"
164
+ Foo::User.new.request_path.should == "users"
165
+ end
166
+ end
167
+ end
168
+
169
+ context 'custom primary key' do
170
+ before do
171
+ spawn_model 'User' do
172
+ primary_key 'UserId'
173
+ resource_path 'users/:UserId'
174
+ end
175
+
176
+ spawn_model 'Customer' do
177
+ primary_key :customer_id
178
+ resource_path 'customers/:id'
179
+ end
180
+ end
181
+
182
+ describe '#request_path' do
183
+ it 'uses the correct primary key attribute' do
184
+ User.new(:UserId => 'foo').request_path.should == 'users/foo'
185
+ User.new(:id => 'foo').request_path.should == 'users'
186
+ end
187
+
188
+ it 'replaces :id with the appropriate primary key' do
189
+ Customer.new(:customer_id => 'joe').request_path.should == 'customers/joe'
190
+ Customer.new(:id => 'joe').request_path.should == 'customers'
191
+ end
192
+ end
193
+ end
194
+ end
195
+
196
+ context "making subdomain HTTP requests" do
197
+ before do
198
+ Her::API.setup :url => "https://api.example.com/" do |builder|
199
+ builder.use Her::Middleware::FirstLevelParseJSON
200
+ builder.use Faraday::Request::UrlEncoded
201
+ builder.adapter :test do |stub|
202
+ 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] }
203
+ stub.post("organizations/2/users") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :organization_id => 2 }.to_json] }
204
+ stub.put("organizations/2/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke", :organization_id => 2 }.to_json] }
205
+ stub.get("organizations/2/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :organization_id => 2, :active => true }.to_json] }
206
+ stub.delete("organizations/2/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke", :organization_id => 2, :active => false }.to_json] }
207
+ end
208
+ end
209
+
210
+ spawn_model "Foo::User" do
211
+ collection_path "organizations/:organization_id/users"
212
+ end
213
+ end
214
+
215
+ describe "fetching a resource" do
216
+ it "maps a single resource to a Ruby object" do
217
+ @user = Foo::User.find(1, :_organization_id => 2)
218
+ @user.id.should == 1
219
+ @user.fullname.should == "Tobias Fünke"
220
+ end
221
+
222
+ it "maps a single resource using a scope to a Ruby object" do
223
+ Foo::User.scope :for_organization, lambda { |o| where(:organization_id => o) }
224
+ @user = Foo::User.for_organization(2).find(1)
225
+ @user.id.should == 1
226
+ @user.fullname.should == "Tobias Fünke"
227
+ end
228
+ end
229
+
230
+ describe "fetching a collection" do
231
+ it "maps a collection of resources to an array of Ruby objects" do
232
+ @users = Foo::User.where(:_organization_id => 2).all
233
+ @users.length.should == 2
234
+ @users.first.fullname.should == "Tobias Fünke"
235
+ end
236
+ end
237
+
238
+ describe "handling new resource" do
239
+ it "handles new resource" do
240
+ @new_user = Foo::User.new(:fullname => "Tobias Fünke", :organization_id => 2)
241
+ @new_user.new?.should be_truthy
242
+
243
+ @existing_user = Foo::User.find(1, :_organization_id => 2)
244
+ @existing_user.new?.should be_falsey
245
+ end
246
+ end
247
+
248
+ describe "creating resources" do
249
+ it "handle one-line resource creation" do
250
+ @user = Foo::User.create(:fullname => "Tobias Fünke", :organization_id => 2)
251
+ @user.id.should == 1
252
+ @user.fullname.should == "Tobias Fünke"
253
+ end
254
+
255
+ it "handle resource creation through Model.new + #save" do
256
+ @user = Foo::User.new(:fullname => "Tobias Fünke", :organization_id => 2)
257
+ @user.save
258
+ @user.fullname.should == "Tobias Fünke"
259
+ end
260
+ end
261
+
262
+ context "updating resources" do
263
+ it "handle resource data update without saving it" do
264
+ @user = Foo::User.find(1, :_organization_id => 2)
265
+ @user.fullname.should == "Tobias Fünke"
266
+ @user.fullname = "Kittie Sanchez"
267
+ @user.fullname.should == "Kittie Sanchez"
268
+ end
269
+
270
+ it "handle resource update through the .update class method" do
271
+ @user = Foo::User.save_existing(1, { :fullname => "Lindsay Fünke", :organization_id => 2 })
272
+ @user.fullname.should == "Lindsay Fünke"
273
+ end
274
+
275
+ it "handle resource update through #save on an existing resource" do
276
+ @user = Foo::User.find(1, :_organization_id => 2)
277
+ @user.fullname = "Lindsay Fünke"
278
+ @user.save
279
+ @user.fullname.should == "Lindsay Fünke"
280
+ end
281
+ end
282
+
283
+ context "deleting resources" do
284
+ it "handle resource deletion through the .destroy class method" do
285
+ @user = Foo::User.destroy_existing(1, :_organization_id => 2)
286
+ @user.active.should be_falsey
287
+ end
288
+
289
+ it "handle resource deletion through #destroy on an existing resource" do
290
+ @user = Foo::User.find(1, :_organization_id => 2)
291
+ @user.destroy
292
+ @user.active.should be_falsey
293
+ end
294
+ end
295
+ end
296
+
297
+ context "making path HTTP requests" do
298
+ before do
299
+ Her::API.setup :url => "https://example.com/api/" do |builder|
300
+ builder.use Her::Middleware::FirstLevelParseJSON
301
+ builder.use Faraday::Request::UrlEncoded
302
+ builder.adapter :test do |stub|
303
+ 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] }
304
+ stub.get("/api/organizations/2/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :organization_id => 2, :active => true }.to_json] }
305
+ end
306
+ end
307
+
308
+ spawn_model "Foo::User" do
309
+ collection_path "organizations/:organization_id/users"
310
+ end
311
+ end
312
+
313
+ describe "fetching a resource" do
314
+ it "maps a single resource to a Ruby object" do
315
+ @user = Foo::User.find(1, :_organization_id => 2)
316
+ @user.id.should == 1
317
+ @user.fullname.should == "Tobias Fünke"
318
+ end
319
+ end
320
+
321
+ describe "fetching a collection" do
322
+ it "maps a collection of resources to an array of Ruby objects" do
323
+ @users = Foo::User.where(:_organization_id => 2).all
324
+ @users.length.should == 2
325
+ @users.first.fullname.should == "Tobias Fünke"
326
+ end
327
+ end
328
+
329
+ describe "fetching a resource with absolute path" do
330
+ it "maps a single resource to a Ruby object" do
331
+ Foo::User.resource_path '/api/' + Foo::User.resource_path
332
+ @user = Foo::User.find(1, :_organization_id => 2)
333
+ @user.id.should == 1
334
+ @user.fullname.should == "Tobias Fünke"
335
+ end
336
+ end
337
+
338
+ describe "fetching a collection with absolute path" do
339
+ it "maps a collection of resources to an array of Ruby objects" do
340
+ Foo::User.collection_path '/api/' + Foo::User.collection_path
341
+ @users = Foo::User.where(:_organization_id => 2).all
342
+ @users.length.should == 2
343
+ @users.first.fullname.should == "Tobias Fünke"
344
+ end
345
+ end
346
+ end
347
+ end