extended_her 0.5
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.
- data/.gitignore +8 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/CONTRIBUTING.md +26 -0
- data/Gemfile +2 -0
- data/LICENSE +7 -0
- data/README.md +723 -0
- data/Rakefile +11 -0
- data/UPGRADE.md +32 -0
- data/examples/twitter-oauth/Gemfile +13 -0
- data/examples/twitter-oauth/app.rb +50 -0
- data/examples/twitter-oauth/config.ru +5 -0
- data/examples/twitter-oauth/views/index.haml +9 -0
- data/examples/twitter-search/Gemfile +12 -0
- data/examples/twitter-search/app.rb +55 -0
- data/examples/twitter-search/config.ru +5 -0
- data/examples/twitter-search/views/index.haml +9 -0
- data/extended_her.gemspec +27 -0
- data/lib/her.rb +23 -0
- data/lib/her/api.rb +108 -0
- data/lib/her/base.rb +17 -0
- data/lib/her/collection.rb +12 -0
- data/lib/her/errors.rb +5 -0
- data/lib/her/exceptions/exception.rb +4 -0
- data/lib/her/exceptions/record_invalid.rb +8 -0
- data/lib/her/exceptions/record_not_found.rb +13 -0
- data/lib/her/middleware.rb +9 -0
- data/lib/her/middleware/accept_json.rb +15 -0
- data/lib/her/middleware/first_level_parse_json.rb +34 -0
- data/lib/her/middleware/second_level_parse_json.rb +28 -0
- data/lib/her/model.rb +69 -0
- data/lib/her/model/base.rb +7 -0
- data/lib/her/model/hooks.rb +114 -0
- data/lib/her/model/http.rb +284 -0
- data/lib/her/model/introspection.rb +57 -0
- data/lib/her/model/orm.rb +191 -0
- data/lib/her/model/orm/comparison_methods.rb +20 -0
- data/lib/her/model/orm/create_methods.rb +29 -0
- data/lib/her/model/orm/destroy_methods.rb +53 -0
- data/lib/her/model/orm/error_methods.rb +19 -0
- data/lib/her/model/orm/fields_definition.rb +15 -0
- data/lib/her/model/orm/find_methods.rb +46 -0
- data/lib/her/model/orm/persistance_methods.rb +22 -0
- data/lib/her/model/orm/relation_mapper.rb +21 -0
- data/lib/her/model/orm/save_methods.rb +58 -0
- data/lib/her/model/orm/serialization_methods.rb +28 -0
- data/lib/her/model/orm/update_methods.rb +31 -0
- data/lib/her/model/paths.rb +82 -0
- data/lib/her/model/relationships.rb +191 -0
- data/lib/her/paginated_collection.rb +20 -0
- data/lib/her/relation.rb +94 -0
- data/lib/her/version.rb +3 -0
- data/spec/api_spec.rb +131 -0
- data/spec/collection_spec.rb +26 -0
- data/spec/middleware/accept_json_spec.rb +10 -0
- data/spec/middleware/first_level_parse_json_spec.rb +42 -0
- data/spec/middleware/second_level_parse_json_spec.rb +25 -0
- data/spec/model/hooks_spec.rb +406 -0
- data/spec/model/http_spec.rb +184 -0
- data/spec/model/introspection_spec.rb +59 -0
- data/spec/model/orm_spec.rb +552 -0
- data/spec/model/paths_spec.rb +286 -0
- data/spec/model/relationships_spec.rb +222 -0
- data/spec/model_spec.rb +31 -0
- data/spec/spec_helper.rb +46 -0
- metadata +222 -0
@@ -0,0 +1,286 @@
|
|
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)
|
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)
|
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)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context "nested model" do
|
129
|
+
before do
|
130
|
+
spawn_model "Foo::User"
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "#build_request_path" do
|
134
|
+
it "builds paths with defaults" do
|
135
|
+
Foo::User.build_request_path(:id => "foo").should == "users/foo"
|
136
|
+
Foo::User.build_request_path.should == "users"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context "making subdomain HTTP requests" do
|
143
|
+
before do
|
144
|
+
Her::API.setup :url => "https://api.example.com/" do |builder|
|
145
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
146
|
+
builder.use Faraday::Request::UrlEncoded
|
147
|
+
builder.adapter :test do |stub|
|
148
|
+
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] }
|
149
|
+
stub.post("organizations/2/users") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :organization_id => 2 }.to_json] }
|
150
|
+
stub.put("organizations/2/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke", :organization_id => 2 }.to_json] }
|
151
|
+
stub.get("organizations/2/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :organization_id => 2, :active => true }.to_json] }
|
152
|
+
stub.delete("organizations/2/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke", :organization_id => 2, :active => false }.to_json] }
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
spawn_model "Foo::User" do
|
157
|
+
collection_path "organizations/:organization_id/users"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe "fetching a resource" do
|
162
|
+
it "maps a single resource to a Ruby object" do
|
163
|
+
@user = Foo::User.find(1, :_organization_id => 2)
|
164
|
+
@user.id.should == 1
|
165
|
+
@user.fullname.should == "Tobias Fünke"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe "fetching a collection" do
|
170
|
+
it "maps a collection of resources to an array of Ruby objects" do
|
171
|
+
@users = Foo::User.all(:_organization_id => 2)
|
172
|
+
@users.length.should == 2
|
173
|
+
@users.first.fullname.should == "Tobias Fünke"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "handling new resource" do
|
178
|
+
it "handles new resource" do
|
179
|
+
@new_user = Foo::User.new(:fullname => "Tobias Fünke", :organization_id => 2)
|
180
|
+
@new_user.new?.should be_true
|
181
|
+
|
182
|
+
@existing_user = Foo::User.find(1, :_organization_id => 2)
|
183
|
+
@existing_user.new?.should be_false
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe "creating resources" do
|
188
|
+
it "handle one-line resource creation" do
|
189
|
+
@user = Foo::User.create(:fullname => "Tobias Fünke", :organization_id => 2)
|
190
|
+
@user.id.should == 1
|
191
|
+
@user.fullname.should == "Tobias Fünke"
|
192
|
+
end
|
193
|
+
|
194
|
+
it "handle resource creation through Model.new + #save" do
|
195
|
+
@user = Foo::User.new(:fullname => "Tobias Fünke", :organization_id => 2)
|
196
|
+
@user.save
|
197
|
+
@user.fullname.should == "Tobias Fünke"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context "updating resources" do
|
202
|
+
it "handle resource data update without saving it" do
|
203
|
+
@user = Foo::User.find(1, :_organization_id => 2)
|
204
|
+
@user.fullname.should == "Tobias Fünke"
|
205
|
+
@user.fullname = "Kittie Sanchez"
|
206
|
+
@user.fullname.should == "Kittie Sanchez"
|
207
|
+
end
|
208
|
+
|
209
|
+
it "handle resource update through the .update class method" do
|
210
|
+
@user = Foo::User.save_existing(1, { :fullname => "Lindsay Fünke", :organization_id => 2 })
|
211
|
+
@user.fullname.should == "Lindsay Fünke"
|
212
|
+
end
|
213
|
+
|
214
|
+
it "handle resource update through #save on an existing resource" do
|
215
|
+
@user = Foo::User.find(1, :_organization_id => 2)
|
216
|
+
@user.fullname = "Lindsay Fünke"
|
217
|
+
@user.save
|
218
|
+
@user.fullname.should == "Lindsay Fünke"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
context "deleting resources" do
|
223
|
+
it "handle resource deletion through the .destroy class method" do
|
224
|
+
@user = Foo::User.destroy_existing(1, :_organization_id => 2)
|
225
|
+
@user.active.should be_false
|
226
|
+
end
|
227
|
+
|
228
|
+
it "handle resource deletion through #destroy on an existing resource" do
|
229
|
+
@user = Foo::User.find(1, :_organization_id => 2)
|
230
|
+
@user.destroy
|
231
|
+
@user.active.should be_false
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
context "making path HTTP requests" do
|
237
|
+
before do
|
238
|
+
Her::API.setup :url => "https://example.com/api/" do |builder|
|
239
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
240
|
+
builder.use Faraday::Request::UrlEncoded
|
241
|
+
builder.adapter :test do |stub|
|
242
|
+
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] }
|
243
|
+
stub.get("/api/organizations/2/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :organization_id => 2, :active => true }.to_json] }
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
spawn_model "Foo::User" do
|
248
|
+
collection_path "organizations/:organization_id/users"
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
describe "fetching a resource" do
|
253
|
+
it "maps a single resource to a Ruby object" do
|
254
|
+
@user = Foo::User.find(1, :_organization_id => 2)
|
255
|
+
@user.id.should == 1
|
256
|
+
@user.fullname.should == "Tobias Fünke"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
describe "fetching a collection" do
|
261
|
+
it "maps a collection of resources to an array of Ruby objects" do
|
262
|
+
@users = Foo::User.all(:_organization_id => 2)
|
263
|
+
@users.length.should == 2
|
264
|
+
@users.first.fullname.should == "Tobias Fünke"
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
describe "fetching a resource with absolute path" do
|
269
|
+
it "maps a single resource to a Ruby object" do
|
270
|
+
Foo::User.resource_path '/api/' + Foo::User.resource_path
|
271
|
+
@user = Foo::User.find(1, :_organization_id => 2)
|
272
|
+
@user.id.should == 1
|
273
|
+
@user.fullname.should == "Tobias Fünke"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
describe "fetching a collection with absolute path" do
|
278
|
+
it "maps a collection of resources to an array of Ruby objects" do
|
279
|
+
Foo::User.collection_path '/api/' + Foo::User.collection_path
|
280
|
+
@users = Foo::User.all(:_organization_id => 2)
|
281
|
+
@users.length.should == 2
|
282
|
+
@users.first.fullname.should == "Tobias Fünke"
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), "../spec_helper.rb")
|
3
|
+
|
4
|
+
describe Her::Model::Relationships do
|
5
|
+
context "setting relationships without details" do
|
6
|
+
before do
|
7
|
+
spawn_model "Foo::User"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "handles a single 'has_many' relationship" do
|
11
|
+
Foo::User.has_many :comments
|
12
|
+
Foo::User.relationships[:has_many].should == [{ :name => :comments, :class_name => "Comment", :path => "/comments", :inverse_of => nil }]
|
13
|
+
end
|
14
|
+
|
15
|
+
it "handles multiples 'has_many' relationship" do
|
16
|
+
Foo::User.has_many :comments
|
17
|
+
Foo::User.has_many :posts
|
18
|
+
Foo::User.relationships[:has_many].should == [{ :name => :comments, :class_name => "Comment", :path => "/comments", :inverse_of => nil }, { :name => :posts, :class_name => "Post", :path => "/posts", :inverse_of => nil }]
|
19
|
+
end
|
20
|
+
|
21
|
+
it "handles a single 'has_one' relationship" do
|
22
|
+
Foo::User.has_one :category
|
23
|
+
Foo::User.relationships[:has_one].should == [{ :name => :category, :class_name => "Category", :path => "/category" }]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "handles multiples 'has_one' relationship" do
|
27
|
+
Foo::User.has_one :category
|
28
|
+
Foo::User.has_one :role
|
29
|
+
Foo::User.relationships[:has_one].should == [{ :name => :category, :class_name => "Category", :path => "/category" }, { :name => :role, :class_name => "Role", :path => "/role" }]
|
30
|
+
end
|
31
|
+
|
32
|
+
it "handles a single belongs_to relationship" do
|
33
|
+
Foo::User.belongs_to :organization
|
34
|
+
Foo::User.relationships[:belongs_to].should == [{ :name => :organization, :class_name => "Organization", :foreign_key => "organization_id", :path => "/organizations/:id" }]
|
35
|
+
end
|
36
|
+
|
37
|
+
it "handles multiples 'belongs_to' relationship" do
|
38
|
+
Foo::User.belongs_to :organization
|
39
|
+
Foo::User.belongs_to :family
|
40
|
+
Foo::User.relationships[:belongs_to].should == [{ :name => :organization, :class_name => "Organization", :foreign_key => "organization_id", :path => "/organizations/:id" }, { :name => :family, :class_name => "Family", :foreign_key => "family_id", :path => "/families/:id" }]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "setting relationships with details" do
|
45
|
+
before do
|
46
|
+
spawn_model "Foo::User"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "handles a single 'has_many' relationship" do
|
50
|
+
Foo::User.has_many :comments, :class_name => "Post", :inverse_of => :admin
|
51
|
+
Foo::User.relationships[:has_many].should == [{ :name => :comments, :class_name => "Post", :path => "/comments", :inverse_of => :admin }]
|
52
|
+
end
|
53
|
+
|
54
|
+
it "handles a single 'has_one' relationship" do
|
55
|
+
Foo::User.has_one :category, :class_name => "Topic", :foreign_key => "topic_id"
|
56
|
+
Foo::User.relationships[:has_one].should == [{ :name => :category, :class_name => "Topic", :foreign_key => "topic_id", :path => "/category" }]
|
57
|
+
end
|
58
|
+
|
59
|
+
it "handles a single belongs_to relationship" do
|
60
|
+
Foo::User.belongs_to :organization, :class_name => "Business", :foreign_key => "org_id"
|
61
|
+
Foo::User.relationships[:belongs_to].should == [{ :name => :organization, :class_name => "Business", :foreign_key => "org_id", :path => "/organizations/:id" }]
|
62
|
+
end
|
63
|
+
|
64
|
+
context "inheriting relationships from a superclass" do
|
65
|
+
it "copies relationships to the subclass" do
|
66
|
+
Foo::User.has_many :comments, :class_name => "Post"
|
67
|
+
subclass = Class.new(Foo::User)
|
68
|
+
subclass.relationships.object_id.should_not == Foo::User.relationships.object_id
|
69
|
+
subclass.relationships[:has_many].length.should == 1
|
70
|
+
subclass.relationships[:has_many].first[:class_name].should == "Post"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "handling relationships without details" do
|
76
|
+
before do
|
77
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
78
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
79
|
+
builder.use Faraday::Request::UrlEncoded
|
80
|
+
builder.adapter :test do |stub|
|
81
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1, :name => "Tobias Fünke", :comments => [{ :id => 2, :body => "Tobias, you blow hard!", :user_id => 1 }, { :id => 3, :body => "I wouldn't mind kissing that man between the cheeks, so to speak", :user_id => 1 }], :role => { :id => 1, :body => "Admin" }, :organization => { :id => 1, :name => "Bluth Company" }, :organization_id => 1 }.to_json] }
|
82
|
+
stub.get("/users/2") { |env| [200, {}, { :id => 2, :name => "Lindsay Fünke", :organization_id => 2 }.to_json] }
|
83
|
+
stub.get("/users/1/comments") { |env| [200, {}, [{ :id => 4, :body => "They're having a FIRESALE?" }].to_json] }
|
84
|
+
stub.get("/users/2/comments") { |env| [200, {}, [{ :id => 4, :body => "They're having a FIRESALE?" }, { :id => 5, :body => "Is this the tiny town from Footloose?" }].to_json] }
|
85
|
+
stub.get("/users/2/role") { |env| [200, {}, { :id => 2, :body => "User" }.to_json] }
|
86
|
+
stub.get("/users/1/role") { |env| [200, {}, { :id => 3, :body => "User" }.to_json] }
|
87
|
+
stub.get("/users/1/posts") { |env| [200, {}, {:id => 1, :body => 'blogging stuff', :admin_id => 1 }.to_json] }
|
88
|
+
stub.get("/organizations/1") { |env| [200, {}, { :id => 1, :name => "Bluth Company Foo" }.to_json] }
|
89
|
+
stub.get("/organizations/2") { |env| [200, {}, { :id => 2, :name => "Bluth Company" }.to_json] }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
spawn_model "Foo::User" do
|
94
|
+
has_many :comments
|
95
|
+
has_one :role
|
96
|
+
belongs_to :organization
|
97
|
+
has_many :posts, :inverse_of => :admin
|
98
|
+
end
|
99
|
+
spawn_model "Foo::Comment" do
|
100
|
+
belongs_to :user
|
101
|
+
end
|
102
|
+
spawn_model "Foo::Post" do
|
103
|
+
belongs_to :admin, :class_name => 'Foo::User'
|
104
|
+
end
|
105
|
+
|
106
|
+
spawn_model "Foo::Organization"
|
107
|
+
spawn_model "Foo::Role"
|
108
|
+
|
109
|
+
@user_with_included_data = Foo::User.find(1)
|
110
|
+
@user_without_included_data = Foo::User.find(2)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "maps an array of included data through has_many" do
|
114
|
+
@user_with_included_data.comments.first.should be_a(Foo::Comment)
|
115
|
+
@user_with_included_data.comments.length.should == 2
|
116
|
+
@user_with_included_data.comments.first.id.should == 2
|
117
|
+
@user_with_included_data.comments.first.body.should == "Tobias, you blow hard!"
|
118
|
+
end
|
119
|
+
|
120
|
+
it "does not refetch the parents models data if they have been fetched before" do
|
121
|
+
@user_with_included_data.comments.first.user.object_id.should == @user_with_included_data.object_id
|
122
|
+
end
|
123
|
+
|
124
|
+
it "uses the given inverse_of key to set the parent model" do
|
125
|
+
@user_with_included_data.posts.first.admin.object_id.should == @user_with_included_data.object_id
|
126
|
+
end
|
127
|
+
|
128
|
+
it "fetches data that was not included through has_many" do
|
129
|
+
@user_without_included_data.comments.first.should be_a(Foo::Comment)
|
130
|
+
@user_without_included_data.comments.length.should == 2
|
131
|
+
@user_without_included_data.comments.first.id.should == 4
|
132
|
+
@user_without_included_data.comments.first.body.should == "They're having a FIRESALE?"
|
133
|
+
end
|
134
|
+
|
135
|
+
it "fetches has_many data even if it was included, only if called with parameters" do
|
136
|
+
@user_with_included_data.comments(:foo_id => 1).length.should == 1
|
137
|
+
end
|
138
|
+
|
139
|
+
it "maps an array of included data through has_one" do
|
140
|
+
@user_with_included_data.role.should be_a(Foo::Role)
|
141
|
+
@user_with_included_data.role.id.should == 1
|
142
|
+
@user_with_included_data.role.body.should == "Admin"
|
143
|
+
end
|
144
|
+
|
145
|
+
it "fetches data that was not included through has_one" do
|
146
|
+
@user_without_included_data.role.should be_a(Foo::Role)
|
147
|
+
@user_without_included_data.role.id.should == 2
|
148
|
+
@user_without_included_data.role.body.should == "User"
|
149
|
+
end
|
150
|
+
|
151
|
+
it "fetches has_one data even if it was included, only if called with parameters" do
|
152
|
+
@user_with_included_data.role(:foo_id => 2).id.should == 3
|
153
|
+
end
|
154
|
+
|
155
|
+
it "maps an array of included data through belongs_to" do
|
156
|
+
@user_with_included_data.organization.should be_a(Foo::Organization)
|
157
|
+
@user_with_included_data.organization.id.should == 1
|
158
|
+
@user_with_included_data.organization.name.should == "Bluth Company"
|
159
|
+
end
|
160
|
+
|
161
|
+
it "fetches data that was not included through belongs_to" do
|
162
|
+
@user_without_included_data.organization.should be_a(Foo::Organization)
|
163
|
+
@user_without_included_data.organization.id.should == 2
|
164
|
+
@user_without_included_data.organization.name.should == "Bluth Company"
|
165
|
+
end
|
166
|
+
|
167
|
+
it "fetches belongs_to data even if it was included, only if called with parameters" do
|
168
|
+
@user_with_included_data.organization(:foo_id => 1).name.should == "Bluth Company Foo"
|
169
|
+
end
|
170
|
+
|
171
|
+
it "can tell if it has a relationship" do
|
172
|
+
@user_without_included_data.has_relationship?(:unknown_relationship).should be_false
|
173
|
+
@user_without_included_data.has_relationship?(:organization).should be_true
|
174
|
+
end
|
175
|
+
|
176
|
+
it "fetches the resource corresponding to a named relationship" do
|
177
|
+
@user_without_included_data.get_relationship(:unknown_relationship).should be_nil
|
178
|
+
@user_without_included_data.get_relationship(:organization).name.should == "Bluth Company"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
context "handling relationships with details" do
|
183
|
+
before do
|
184
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
185
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
186
|
+
builder.use Faraday::Request::UrlEncoded
|
187
|
+
builder.adapter :test do |stub|
|
188
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1, :name => "Tobias Fünke", :organization => { :id => 1, :name => "Bluth Company" }, :organization_id => 1 }.to_json] }
|
189
|
+
stub.get("/users/2") { |env| [200, {}, { :id => 2, :name => "Lindsay Fünke", :organization_id => 1 }.to_json] }
|
190
|
+
stub.get("/users/3") { |env| [200, {}, { :id => 2, :name => "Lindsay Fünke", :organization => nil }.to_json] }
|
191
|
+
stub.get("/companies/1") { |env| [200, {}, { :id => 1, :name => "Bluth Company" }.to_json] }
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
spawn_model "Foo::User" do
|
196
|
+
belongs_to :company, :path => "/organizations/:id", :foreign_key => :organization_id
|
197
|
+
end
|
198
|
+
|
199
|
+
spawn_model "Foo::Company"
|
200
|
+
|
201
|
+
@user_with_included_data = Foo::User.find(1)
|
202
|
+
@user_without_included_data = Foo::User.find(2)
|
203
|
+
@user_with_included_nil_data = Foo::User.find(3)
|
204
|
+
end
|
205
|
+
|
206
|
+
it "maps an array of included data through belongs_to" do
|
207
|
+
@user_with_included_data.company.should be_a(Foo::Company)
|
208
|
+
@user_with_included_data.company.id.should == 1
|
209
|
+
@user_with_included_data.company.name.should == "Bluth Company"
|
210
|
+
end
|
211
|
+
|
212
|
+
it "does not map included data if it’s nil" do
|
213
|
+
@user_with_included_nil_data.organization.should be_nil
|
214
|
+
end
|
215
|
+
|
216
|
+
it "fetches data that was not included through belongs_to" do
|
217
|
+
@user_without_included_data.company.should be_a(Foo::Company)
|
218
|
+
@user_without_included_data.company.id.should == 1
|
219
|
+
@user_without_included_data.company.name.should == "Bluth Company"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|