her 0.8.2 → 0.10.4
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.
- checksums.yaml +4 -4
- data/.rspec +1 -1
- data/.rubocop.yml +1291 -0
- data/.travis.yml +6 -1
- data/README.md +29 -11
- data/her.gemspec +3 -5
- data/lib/her/api.rb +16 -9
- data/lib/her/middleware/json_api_parser.rb +1 -1
- data/lib/her/model/associations/association.rb +32 -5
- data/lib/her/model/associations/association_proxy.rb +1 -1
- data/lib/her/model/associations/belongs_to_association.rb +1 -1
- data/lib/her/model/associations/has_many_association.rb +3 -3
- data/lib/her/model/attributes.rb +105 -75
- data/lib/her/model/http.rb +3 -3
- data/lib/her/model/introspection.rb +1 -1
- data/lib/her/model/orm.rb +96 -19
- data/lib/her/model/parse.rb +27 -17
- data/lib/her/model/relation.rb +46 -2
- data/lib/her/version.rb +1 -1
- data/spec/api_spec.rb +34 -31
- data/spec/collection_spec.rb +25 -10
- data/spec/json_api/model_spec.rb +75 -72
- data/spec/middleware/accept_json_spec.rb +1 -1
- data/spec/middleware/first_level_parse_json_spec.rb +20 -20
- data/spec/middleware/json_api_parser_spec.rb +26 -7
- data/spec/middleware/second_level_parse_json_spec.rb +8 -9
- data/spec/model/associations/association_proxy_spec.rb +2 -5
- data/spec/model/associations_spec.rb +617 -295
- data/spec/model/attributes_spec.rb +114 -107
- data/spec/model/callbacks_spec.rb +59 -27
- data/spec/model/dirty_spec.rb +70 -29
- data/spec/model/http_spec.rb +67 -35
- data/spec/model/introspection_spec.rb +26 -22
- data/spec/model/nested_attributes_spec.rb +31 -31
- data/spec/model/orm_spec.rb +332 -157
- data/spec/model/parse_spec.rb +250 -77
- data/spec/model/paths_spec.rb +109 -109
- data/spec/model/relation_spec.rb +89 -69
- data/spec/model/validations_spec.rb +6 -6
- data/spec/model_spec.rb +17 -17
- data/spec/spec_helper.rb +2 -3
- data/spec/support/macros/model_macros.rb +2 -2
- metadata +36 -63
@@ -4,124 +4,279 @@ require File.join(File.dirname(__FILE__), "../spec_helper.rb")
|
|
4
4
|
describe Her::Model::Associations do
|
5
5
|
context "setting associations without details" do
|
6
6
|
before { spawn_model "Foo::User" }
|
7
|
-
subject { Foo::User.associations }
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
subject(:associations) { Foo::User.associations }
|
8
|
+
|
9
|
+
describe "has_many associations" do
|
10
|
+
subject { associations[:has_many] }
|
11
|
+
|
12
|
+
context "single" do
|
13
|
+
let(:comments_association) do
|
14
|
+
{
|
15
|
+
name: :comments,
|
16
|
+
data_key: :comments,
|
17
|
+
default: [],
|
18
|
+
class_name: "Comment",
|
19
|
+
path: "/comments",
|
20
|
+
inverse_of: nil
|
21
|
+
}
|
22
|
+
end
|
23
|
+
before { Foo::User.has_many :comments }
|
13
24
|
|
14
|
-
|
15
|
-
before do
|
16
|
-
Foo::User.has_many :comments
|
17
|
-
Foo::User.has_many :posts
|
25
|
+
it { is_expected.to eql [comments_association] }
|
18
26
|
end
|
19
27
|
|
20
|
-
|
21
|
-
|
28
|
+
context "multiple" do
|
29
|
+
let(:comments_association) do
|
30
|
+
{
|
31
|
+
name: :comments,
|
32
|
+
data_key: :comments,
|
33
|
+
default: [],
|
34
|
+
class_name: "Comment",
|
35
|
+
path: "/comments",
|
36
|
+
inverse_of: nil
|
37
|
+
}
|
38
|
+
end
|
39
|
+
let(:posts_association) do
|
40
|
+
{
|
41
|
+
name: :posts,
|
42
|
+
data_key: :posts,
|
43
|
+
default: [],
|
44
|
+
class_name: "Post",
|
45
|
+
path: "/posts",
|
46
|
+
inverse_of: nil
|
47
|
+
}
|
48
|
+
end
|
49
|
+
before do
|
50
|
+
Foo::User.has_many :comments
|
51
|
+
Foo::User.has_many :posts
|
52
|
+
end
|
22
53
|
|
23
|
-
|
24
|
-
|
25
|
-
its([:has_one]) { should eql [{ :name => :category, :data_key => :category, :default => nil, :class_name => "Category", :path => "/category" }] }
|
54
|
+
it { is_expected.to eql [comments_association, posts_association] }
|
55
|
+
end
|
26
56
|
end
|
27
57
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
58
|
+
describe "has_one associations" do
|
59
|
+
subject { associations[:has_one] }
|
60
|
+
|
61
|
+
context "single" do
|
62
|
+
let(:category_association) do
|
63
|
+
{
|
64
|
+
name: :category,
|
65
|
+
data_key: :category,
|
66
|
+
default: nil,
|
67
|
+
class_name: "Category",
|
68
|
+
path: "/category"
|
69
|
+
}
|
70
|
+
end
|
71
|
+
before { Foo::User.has_one :category }
|
72
|
+
|
73
|
+
it { is_expected.to eql [category_association] }
|
32
74
|
end
|
33
75
|
|
34
|
-
|
35
|
-
|
76
|
+
context "multiple" do
|
77
|
+
let(:category_association) do
|
78
|
+
{
|
79
|
+
name: :category,
|
80
|
+
data_key: :category,
|
81
|
+
default: nil,
|
82
|
+
class_name: "Category",
|
83
|
+
path: "/category"
|
84
|
+
}
|
85
|
+
end
|
86
|
+
let(:role_association) do
|
87
|
+
{
|
88
|
+
name: :role,
|
89
|
+
data_key: :role,
|
90
|
+
default: nil,
|
91
|
+
class_name: "Role",
|
92
|
+
path: "/role"
|
93
|
+
}
|
94
|
+
end
|
95
|
+
before do
|
96
|
+
Foo::User.has_one :category
|
97
|
+
Foo::User.has_one :role
|
98
|
+
end
|
36
99
|
|
37
|
-
|
38
|
-
|
39
|
-
its([:belongs_to]) { should eql [{ :name => :organization, :data_key => :organization, :default => nil, :class_name => "Organization", :foreign_key => "organization_id", :path => "/organizations/:id" }] }
|
100
|
+
it { is_expected.to eql [category_association, role_association] }
|
101
|
+
end
|
40
102
|
end
|
41
103
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
104
|
+
describe "belongs_to associations" do
|
105
|
+
subject { associations[:belongs_to] }
|
106
|
+
|
107
|
+
context "single" do
|
108
|
+
let(:organization_association) do
|
109
|
+
{
|
110
|
+
name: :organization,
|
111
|
+
data_key: :organization,
|
112
|
+
default: nil,
|
113
|
+
class_name: "Organization",
|
114
|
+
foreign_key: "organization_id",
|
115
|
+
path: "/organizations/:id"
|
116
|
+
}
|
117
|
+
end
|
118
|
+
before { Foo::User.belongs_to :organization }
|
119
|
+
|
120
|
+
it { is_expected.to eql [organization_association] }
|
46
121
|
end
|
47
122
|
|
48
|
-
|
123
|
+
context "multiple" do
|
124
|
+
let(:organization_association) do
|
125
|
+
{
|
126
|
+
name: :organization,
|
127
|
+
data_key: :organization,
|
128
|
+
default: nil,
|
129
|
+
class_name: "Organization",
|
130
|
+
foreign_key: "organization_id",
|
131
|
+
path: "/organizations/:id"
|
132
|
+
}
|
133
|
+
end
|
134
|
+
let(:family_association) do
|
135
|
+
{
|
136
|
+
name: :family,
|
137
|
+
data_key: :family,
|
138
|
+
default: nil,
|
139
|
+
class_name: "Family",
|
140
|
+
foreign_key: "family_id",
|
141
|
+
path: "/families/:id"
|
142
|
+
}
|
143
|
+
end
|
144
|
+
before do
|
145
|
+
Foo::User.belongs_to :organization
|
146
|
+
Foo::User.belongs_to :family
|
147
|
+
end
|
148
|
+
|
149
|
+
it { is_expected.to eql [organization_association, family_association] }
|
150
|
+
end
|
49
151
|
end
|
50
152
|
end
|
51
153
|
|
52
154
|
context "setting associations with details" do
|
53
155
|
before { spawn_model "Foo::User" }
|
54
|
-
subject { Foo::User.associations }
|
156
|
+
subject(:associations) { Foo::User.associations }
|
55
157
|
|
56
158
|
context "in base class" do
|
57
|
-
|
58
|
-
|
59
|
-
|
159
|
+
describe "has_many associations" do
|
160
|
+
subject { associations[:has_many] }
|
161
|
+
|
162
|
+
context "single" do
|
163
|
+
let(:comments_association) do
|
164
|
+
{
|
165
|
+
name: :comments,
|
166
|
+
data_key: :user_comments,
|
167
|
+
default: {},
|
168
|
+
class_name: "Post",
|
169
|
+
path: "/comments",
|
170
|
+
inverse_of: :admin
|
171
|
+
}
|
172
|
+
end
|
173
|
+
before do
|
174
|
+
Foo::User.has_many :comments, class_name: "Post",
|
175
|
+
inverse_of: :admin,
|
176
|
+
data_key: :user_comments,
|
177
|
+
default: {}
|
178
|
+
end
|
179
|
+
|
180
|
+
it { is_expected.to eql [comments_association] }
|
181
|
+
end
|
60
182
|
end
|
61
183
|
|
62
|
-
|
63
|
-
|
64
|
-
|
184
|
+
describe "has_one associations" do
|
185
|
+
subject { associations[:has_one] }
|
186
|
+
|
187
|
+
context "single" do
|
188
|
+
let(:category_association) do
|
189
|
+
{
|
190
|
+
name: :category,
|
191
|
+
data_key: :topic,
|
192
|
+
default: nil,
|
193
|
+
class_name: "Topic",
|
194
|
+
foreign_key: "topic_id",
|
195
|
+
path: "/category"
|
196
|
+
}
|
197
|
+
end
|
198
|
+
before do
|
199
|
+
Foo::User.has_one :category, class_name: "Topic",
|
200
|
+
foreign_key: "topic_id",
|
201
|
+
data_key: :topic, default: nil
|
202
|
+
end
|
203
|
+
|
204
|
+
it { is_expected.to eql [category_association] }
|
205
|
+
end
|
65
206
|
end
|
66
207
|
|
67
|
-
|
68
|
-
|
69
|
-
|
208
|
+
describe "belongs_to associations" do
|
209
|
+
subject { associations[:belongs_to] }
|
210
|
+
|
211
|
+
context "single" do
|
212
|
+
let(:organization_association) do
|
213
|
+
{
|
214
|
+
name: :organization,
|
215
|
+
data_key: :org,
|
216
|
+
default: true,
|
217
|
+
class_name: "Business",
|
218
|
+
foreign_key: "org_id",
|
219
|
+
path: "/organizations/:id"
|
220
|
+
}
|
221
|
+
end
|
222
|
+
before do
|
223
|
+
Foo::User.belongs_to :organization, class_name: "Business",
|
224
|
+
foreign_key: "org_id",
|
225
|
+
data_key: :org,
|
226
|
+
default: true
|
227
|
+
end
|
228
|
+
|
229
|
+
it { is_expected.to eql [organization_association] }
|
230
|
+
end
|
70
231
|
end
|
71
232
|
end
|
72
233
|
|
73
234
|
context "in parent class" do
|
74
|
-
before { Foo::User.has_many :comments, :
|
235
|
+
before { Foo::User.has_many :comments, class_name: "Post" }
|
75
236
|
|
76
237
|
describe "associations accessor" do
|
77
|
-
subject { Class.new(Foo::User).associations }
|
78
|
-
|
79
|
-
|
238
|
+
subject(:associations) { Class.new(Foo::User).associations }
|
239
|
+
|
240
|
+
describe "#object_id" do
|
241
|
+
subject { associations.object_id }
|
242
|
+
it { is_expected.not_to eql Foo::User.associations.object_id }
|
243
|
+
end
|
244
|
+
|
245
|
+
describe "[:has_many]" do
|
246
|
+
subject { associations[:has_many] }
|
247
|
+
let(:association) do
|
248
|
+
{
|
249
|
+
name: :comments,
|
250
|
+
data_key: :comments,
|
251
|
+
default: [],
|
252
|
+
class_name: "Post",
|
253
|
+
path: "/comments",
|
254
|
+
inverse_of: nil
|
255
|
+
}
|
256
|
+
end
|
257
|
+
|
258
|
+
it { is_expected.to eql [association] }
|
259
|
+
end
|
80
260
|
end
|
81
261
|
end
|
82
262
|
end
|
83
263
|
|
84
264
|
context "handling associations without details" do
|
85
265
|
before do
|
86
|
-
Her::API.setup :url => "https://api.example.com" do |builder|
|
87
|
-
builder.use Her::Middleware::FirstLevelParseJSON
|
88
|
-
builder.use Faraday::Request::UrlEncoded
|
89
|
-
builder.adapter :test do |stub|
|
90
|
-
stub.get("/users/1") { |env| [200, {}, { :id => 1, :name => "Tobias Fünke", :comments => [{ :comment => { :id => 2, :body => "Tobias, you blow hard!", :user_id => 1 } }, { :comment => { :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] }
|
91
|
-
stub.get("/users/2") { |env| [200, {}, { :id => 2, :name => "Lindsay Fünke", :organization_id => 2 }.to_json] }
|
92
|
-
stub.get("/users/1/comments") { |env| [200, {}, [{ :comment => { :id => 4, :body => "They're having a FIRESALE?" } }].to_json] }
|
93
|
-
stub.get("/users/2/comments") { |env| [200, {}, [{ :comment => { :id => 4, :body => "They're having a FIRESALE?" } }, { :comment => { :id => 5, :body => "Is this the tiny town from Footloose?" } }].to_json] }
|
94
|
-
stub.get("/users/2/comments/5") { |env| [200, {}, { :comment => { :id => 5, :body => "Is this the tiny town from Footloose?" } }.to_json] }
|
95
|
-
stub.get("/users/2/role") { |env| [200, {}, { :id => 2, :body => "User" }.to_json] }
|
96
|
-
stub.get("/users/1/role") { |env| [200, {}, { :id => 3, :body => "User" }.to_json] }
|
97
|
-
stub.get("/users/1/posts") { |env| [200, {}, [{:id => 1, :body => 'blogging stuff', :admin_id => 1 }].to_json] }
|
98
|
-
stub.get("/organizations/1") { |env| [200, {}, { :organization => { :id => 1, :name => "Bluth Company Foo" } }.to_json] }
|
99
|
-
stub.post("/users") { |env| [200, {}, { :id => 5, :name => "Mr. Krabs", :comments => [{ :comment => { :id => 99, :body => "Rodríguez, nasibisibusi?", :user_id => 5 } }], :role => { :id => 1, :body => "Admin" }, :organization => { :id => 3, :name => "Krusty Krab" }, :organization_id => 3 }.to_json] }
|
100
|
-
stub.put("/users/5") { |env| [200, {}, { :id => 5, :name => "Clancy Brown", :comments => [{ :comment => { :id => 99, :body => "Rodríguez, nasibisibusi?", :user_id => 5 } }], :role => { :id => 1, :body => "Admin" }, :organization => { :id => 3, :name => "Krusty Krab" }, :organization_id => 3 }.to_json] }
|
101
|
-
stub.delete("/users/5") { |env| [200, {}, { :id => 5, :name => "Clancy Brown", :comments => [{ :comment => { :id => 99, :body => "Rodríguez, nasibisibusi?", :user_id => 5 } }], :role => { :id => 1, :body => "Admin" }, :organization => { :id => 3, :name => "Krusty Krab" }, :organization_id => 3 }.to_json] }
|
102
|
-
|
103
|
-
stub.get("/organizations/2") do |env|
|
104
|
-
if env[:params]["admin"] == "true"
|
105
|
-
[200, {}, { :organization => { :id => 2, :name => "Bluth Company (admin)" } }.to_json]
|
106
|
-
else
|
107
|
-
[200, {}, { :organization => { :id => 2, :name => "Bluth Company" } }.to_json]
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
266
|
spawn_model "Foo::User" do
|
114
267
|
has_many :comments, class_name: "Foo::Comment"
|
115
|
-
has_one :role
|
116
|
-
belongs_to :organization
|
117
|
-
has_many :posts, :
|
268
|
+
has_one :role, class_name: "Foo::Role"
|
269
|
+
belongs_to :organization, class_name: "Foo::Organization"
|
270
|
+
has_many :posts, inverse_of: :admin
|
118
271
|
end
|
272
|
+
|
119
273
|
spawn_model "Foo::Comment" do
|
120
274
|
belongs_to :user
|
121
275
|
parse_root_in_json true
|
122
276
|
end
|
277
|
+
|
123
278
|
spawn_model "Foo::Post" do
|
124
|
-
belongs_to :admin, :
|
279
|
+
belongs_to :admin, class_name: "Foo::User"
|
125
280
|
end
|
126
281
|
|
127
282
|
spawn_model "Foo::Organization" do
|
@@ -129,143 +284,243 @@ describe Her::Model::Associations do
|
|
129
284
|
end
|
130
285
|
|
131
286
|
spawn_model "Foo::Role"
|
132
|
-
|
133
|
-
@user_with_included_data = Foo::User.find(1)
|
134
|
-
@user_without_included_data = Foo::User.find(2)
|
135
|
-
@user_without_organization_and_not_persisted = Foo::User.new(organization_id: nil, name: "Katlin Fünke")
|
136
287
|
end
|
137
288
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
289
|
+
context "with included data" do
|
290
|
+
before(:context) do
|
291
|
+
Her::API.setup url: "https://api.example.com" do |builder|
|
292
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
293
|
+
builder.use Faraday::Request::UrlEncoded
|
294
|
+
builder.adapter :test do |stub|
|
295
|
+
stub.get("/users/1") { [200, {}, { id: 1, name: "Tobias Fünke", comments: [{ comment: { id: 2, body: "Tobias, you blow hard!", user_id: 1 } }, { comment: { 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] }
|
296
|
+
stub.get("/users/1/comments") { [200, {}, [{ comment: { id: 4, body: "They're having a FIRESALE?" } }].to_json] }
|
297
|
+
stub.get("/users/1/role") { [200, {}, { id: 3, body: "User" }.to_json] }
|
298
|
+
stub.get("/users/1/posts") { [200, {}, [{ id: 1, body: "blogging stuff", admin_id: 1 }].to_json] }
|
299
|
+
stub.get("/organizations/1") { [200, {}, { organization: { id: 1, name: "Bluth Company Foo" } }.to_json] }
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
142
303
|
|
143
|
-
|
144
|
-
|
145
|
-
@user_with_included_data.comments.length.should == 2
|
146
|
-
@user_with_included_data.comments.first.id.should == 2
|
147
|
-
@user_with_included_data.comments.first.body.should == "Tobias, you blow hard!"
|
148
|
-
end
|
304
|
+
let(:user) { Foo::User.find(1) }
|
305
|
+
let(:user_params) { user.to_params }
|
149
306
|
|
150
|
-
|
151
|
-
|
152
|
-
|
307
|
+
it "maps an array of included data through has_many" do
|
308
|
+
expect(user.comments.first).to be_a(Foo::Comment)
|
309
|
+
expect(user.comments.length).to eq(2)
|
310
|
+
expect(user.comments.first.id).to eq(2)
|
311
|
+
expect(user.comments.first.body).to eq("Tobias, you blow hard!")
|
312
|
+
end
|
153
313
|
|
154
|
-
|
155
|
-
|
156
|
-
|
314
|
+
it "does not refetch the parents models data if they have been fetched before" do
|
315
|
+
expect(user.comments.first.user.object_id).to eq(user.object_id)
|
316
|
+
end
|
157
317
|
|
158
|
-
|
159
|
-
|
160
|
-
|
318
|
+
it "uses the given inverse_of key to set the parent model" do
|
319
|
+
expect(user.posts.first.admin.object_id).to eq(user.object_id)
|
320
|
+
end
|
161
321
|
|
162
|
-
|
163
|
-
|
164
|
-
|
322
|
+
it "fetches has_many data even if it was included, only if called with parameters" do
|
323
|
+
expect(user.comments.where(foo_id: 1).length).to eq(1)
|
324
|
+
end
|
165
325
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
326
|
+
it "maps an array of included data through has_one" do
|
327
|
+
expect(user.role).to be_a(Foo::Role)
|
328
|
+
expect(user.role.object_id).to eq(user.role.object_id)
|
329
|
+
expect(user.role.id).to eq(1)
|
330
|
+
expect(user.role.body).to eq("Admin")
|
331
|
+
end
|
172
332
|
|
173
|
-
|
174
|
-
|
175
|
-
|
333
|
+
it "fetches has_one data even if it was included, only if called with parameters" do
|
334
|
+
expect(user.role.where(foo_id: 2).id).to eq(3)
|
335
|
+
end
|
176
336
|
|
177
|
-
|
178
|
-
|
179
|
-
|
337
|
+
it "maps an array of included data through belongs_to" do
|
338
|
+
expect(user.organization).to be_a(Foo::Organization)
|
339
|
+
expect(user.organization.id).to eq(1)
|
340
|
+
expect(user.organization.name).to eq("Bluth Company")
|
341
|
+
end
|
180
342
|
|
181
|
-
|
182
|
-
|
183
|
-
|
343
|
+
it "fetches belongs_to data even if it was included, only if called with parameters" do
|
344
|
+
expect(user.organization.where(foo_id: 1).name).to eq("Bluth Company Foo")
|
345
|
+
end
|
184
346
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
@user_with_included_data.role.body.should == "Admin"
|
190
|
-
end
|
347
|
+
it "includes has_many relationships in params by default" do
|
348
|
+
expect(user_params[:comments]).to be_kind_of(Array)
|
349
|
+
expect(user_params[:comments].length).to eq(2)
|
350
|
+
end
|
191
351
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
end
|
352
|
+
it "includes has_one relationship in params by default" do
|
353
|
+
expect(user_params[:role]).to be_kind_of(Hash)
|
354
|
+
expect(user_params[:role]).not_to be_empty
|
355
|
+
end
|
197
356
|
|
198
|
-
|
199
|
-
|
357
|
+
it "includes belongs_to relationship in params by default" do
|
358
|
+
expect(user_params[:organization]).to be_kind_of(Hash)
|
359
|
+
expect(user_params[:organization]).not_to be_empty
|
360
|
+
end
|
200
361
|
end
|
201
362
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
363
|
+
context "without included data" do
|
364
|
+
before(:context) do
|
365
|
+
Her::API.setup url: "https://api.example.com" do |builder|
|
366
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
367
|
+
builder.use Faraday::Request::UrlEncoded
|
368
|
+
builder.adapter :test do |stub|
|
369
|
+
stub.get("/users/2") { [200, {}, { id: 2, name: "Lindsay Fünke", organization_id: 2 }.to_json] }
|
370
|
+
stub.get("/users/2/comments") { [200, {}, [{ comment: { id: 4, body: "They're having a FIRESALE?" } }, { comment: { id: 5, body: "Is this the tiny town from Footloose?" } }].to_json] }
|
371
|
+
stub.get("/users/2/comments/5") { [200, {}, { comment: { id: 5, body: "Is this the tiny town from Footloose?" } }.to_json] }
|
372
|
+
stub.get("/users/2/role") { [200, {}, { id: 2, body: "User" }.to_json] }
|
373
|
+
stub.get("/organizations/2") do |env|
|
374
|
+
if env[:params]["admin"] == "true"
|
375
|
+
[200, {}, { organization: { id: 2, name: "Bluth Company (admin)" } }.to_json]
|
376
|
+
else
|
377
|
+
[200, {}, { organization: { id: 2, name: "Bluth Company" } }.to_json]
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|
207
383
|
|
208
|
-
|
209
|
-
@user_without_included_data.organization.should be_a(Foo::Organization)
|
210
|
-
@user_without_included_data.organization.id.should == 2
|
211
|
-
@user_without_included_data.organization.name.should == "Bluth Company"
|
212
|
-
end
|
384
|
+
let(:user) { Foo::User.find(2) }
|
213
385
|
|
214
|
-
|
215
|
-
|
216
|
-
|
386
|
+
it "fetches data that was not included through has_many" do
|
387
|
+
expect(user.comments.first).to be_a(Foo::Comment)
|
388
|
+
expect(user.comments.length).to eq(2)
|
389
|
+
expect(user.comments.first.id).to eq(4)
|
390
|
+
expect(user.comments.first.body).to eq("They're having a FIRESALE?")
|
391
|
+
end
|
217
392
|
|
218
|
-
|
219
|
-
|
220
|
-
|
393
|
+
it "fetches data that was not included through has_many only once" do
|
394
|
+
expect(user.comments.first.object_id).to eq(user.comments.first.object_id)
|
395
|
+
end
|
221
396
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
end
|
397
|
+
it "fetches data that was cached through has_many if called with parameters" do
|
398
|
+
expect(user.comments.first.object_id).not_to eq(user.comments.where(foo_id: 1).first.object_id)
|
399
|
+
end
|
226
400
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
end
|
401
|
+
it "fetches data again after being reloaded" do
|
402
|
+
expect { user.comments.reload }.to change { user.comments.first.object_id }
|
403
|
+
end
|
231
404
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
405
|
+
it "fetches data that was not included through has_one" do
|
406
|
+
expect(user.role).to be_a(Foo::Role)
|
407
|
+
expect(user.role.id).to eq(2)
|
408
|
+
expect(user.role.body).to eq("User")
|
409
|
+
end
|
410
|
+
|
411
|
+
it "fetches data that was not included through belongs_to" do
|
412
|
+
expect(user.organization).to be_a(Foo::Organization)
|
413
|
+
expect(user.organization.id).to eq(2)
|
414
|
+
expect(user.organization.name).to eq("Bluth Company")
|
415
|
+
end
|
416
|
+
|
417
|
+
it "can tell if it has a association" do
|
418
|
+
expect(user.has_association?(:unknown_association)).to be false
|
419
|
+
expect(user.has_association?(:organization)).to be true
|
420
|
+
end
|
421
|
+
|
422
|
+
it "fetches the resource corresponding to a named association" do
|
423
|
+
expect(user.get_association(:unknown_association)).to be_nil
|
424
|
+
expect(user.get_association(:organization).name).to eq("Bluth Company")
|
425
|
+
end
|
426
|
+
|
427
|
+
it "pass query string parameters when additional arguments are passed" do
|
428
|
+
expect(user.organization.where(admin: true).name).to eq("Bluth Company (admin)")
|
429
|
+
expect(user.organization.name).to eq("Bluth Company")
|
430
|
+
end
|
431
|
+
|
432
|
+
it "fetches data with the specified id when calling find" do
|
433
|
+
comment = user.comments.find(5)
|
434
|
+
expect(comment).to be_a(Foo::Comment)
|
435
|
+
expect(comment.id).to eq(5)
|
436
|
+
end
|
236
437
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
438
|
+
it "'s associations responds to #empty?" do
|
439
|
+
expect(user.organization.respond_to?(:empty?)).to be_truthy
|
440
|
+
expect(user.organization).not_to be_empty
|
441
|
+
end
|
241
442
|
end
|
242
443
|
|
243
|
-
|
244
|
-
|
245
|
-
|
444
|
+
context "without included parent data" do
|
445
|
+
before(:context) do
|
446
|
+
Her::API.setup url: "https://api.example.com" do |builder|
|
447
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
448
|
+
builder.use Faraday::Request::UrlEncoded
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
let(:comment) { Foo::Comment.new(id: 7, user_id: 1) }
|
453
|
+
|
454
|
+
it "does fetch the parent models data only once" do
|
455
|
+
expect(comment.user.object_id).to eq(comment.user.object_id)
|
456
|
+
end
|
457
|
+
|
458
|
+
it "does fetch the parent models data that was cached if called with parameters" do
|
459
|
+
expect(comment.user.object_id).not_to eq(comment.user.where(a: 2).object_id)
|
460
|
+
end
|
246
461
|
end
|
247
462
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
463
|
+
context "when resource is new" do
|
464
|
+
let(:new_user) { Foo::User.new }
|
465
|
+
|
466
|
+
it "doesn't attempt to fetch association data" do
|
467
|
+
expect(new_user.comments).to eq([])
|
468
|
+
expect(new_user.role).to be_nil
|
469
|
+
expect(new_user.organization).to be_nil
|
470
|
+
end
|
252
471
|
end
|
253
472
|
|
254
|
-
|
255
|
-
|
256
|
-
|
473
|
+
context "when foreign_key is nil" do
|
474
|
+
before do
|
475
|
+
spawn_model "Foo::User" do
|
476
|
+
belongs_to :organization, class_name: "Foo::Organization"
|
477
|
+
end
|
478
|
+
|
479
|
+
spawn_model "Foo::Organization" do
|
480
|
+
parse_root_in_json true
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
let(:user) { Foo::User.new(organization_id: nil, name: "Katlin Fünke") }
|
485
|
+
|
486
|
+
it "returns nil" do
|
487
|
+
expect(user.organization).to be_nil
|
488
|
+
end
|
489
|
+
end
|
257
490
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
491
|
+
context "after" do
|
492
|
+
before(:context) do
|
493
|
+
Her::API.setup url: "https://api.example.com" do |builder|
|
494
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
495
|
+
builder.use Faraday::Request::UrlEncoded
|
496
|
+
builder.adapter :test do |stub|
|
497
|
+
stub.post("/users") { [200, {}, { id: 5, name: "Mr. Krabs", comments: [{ comment: { id: 99, body: "Rodríguez, nasibisibusi?", user_id: 5 } }], role: { id: 1, body: "Admin" }, organization: { id: 3, name: "Krusty Krab" }, organization_id: 3 }.to_json] }
|
498
|
+
stub.put("/users/5") { [200, {}, { id: 5, name: "Clancy Brown", comments: [{ comment: { id: 99, body: "Rodríguez, nasibisibusi?", user_id: 5 } }], role: { id: 1, body: "Admin" }, organization: { id: 3, name: "Krusty Krab" }, organization_id: 3 }.to_json] }
|
499
|
+
stub.delete("/users/5") { [200, {}, { id: 5, name: "Clancy Brown", comments: [{ comment: { id: 99, body: "Rodríguez, nasibisibusi?", user_id: 5 } }], role: { id: 1, body: "Admin" }, organization: { id: 3, name: "Krusty Krab" }, organization_id: 3 }.to_json] }
|
500
|
+
end
|
263
501
|
end
|
502
|
+
end
|
503
|
+
|
504
|
+
let(:user_after_create) { Foo::User.create }
|
505
|
+
let(:user_after_save_existing) { Foo::User.save_existing(5, name: "Clancy Brown") }
|
506
|
+
let(:user_after_destroy) { Foo::User.new(id: 5).destroy }
|
507
|
+
|
508
|
+
[:create, :save_existing, :destroy].each do |type|
|
509
|
+
context "after #{type}" do
|
510
|
+
let(:subject) { send("user_after_#{type}") }
|
264
511
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
512
|
+
it "maps an array of included data through has_many" do
|
513
|
+
expect(subject.comments.first).to be_a(Foo::Comment)
|
514
|
+
expect(subject.comments.length).to eq(1)
|
515
|
+
expect(subject.comments.first.id).to eq(99)
|
516
|
+
expect(subject.comments.first.body).to eq("Rodríguez, nasibisibusi?")
|
517
|
+
end
|
518
|
+
|
519
|
+
it "maps an array of included data through has_one" do
|
520
|
+
expect(subject.role).to be_a(Foo::Role)
|
521
|
+
expect(subject.role.id).to eq(1)
|
522
|
+
expect(subject.role.body).to eq("Admin")
|
523
|
+
end
|
269
524
|
end
|
270
525
|
end
|
271
526
|
end
|
@@ -273,130 +528,188 @@ describe Her::Model::Associations do
|
|
273
528
|
|
274
529
|
context "handling associations with details in active_model_serializers format" do
|
275
530
|
before do
|
276
|
-
Her::API.setup :url => "https://api.example.com" do |builder|
|
277
|
-
builder.use Her::Middleware::FirstLevelParseJSON
|
278
|
-
builder.use Faraday::Request::UrlEncoded
|
279
|
-
builder.adapter :test do |stub|
|
280
|
-
stub.get("/users/1") { |env| [200, {}, { :user => { :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] }
|
281
|
-
stub.get("/users/2") { |env| [200, {}, { :user => { :id => 2, :name => "Lindsay Fünke", :organization_id => 1 } }.to_json] }
|
282
|
-
stub.get("/users/1/comments") { |env| [200, {}, { :comments => [{ :id => 4, :body => "They're having a FIRESALE?" }] }.to_json] }
|
283
|
-
stub.get("/users/2/comments") { |env| [200, {}, { :comments => [{ :id => 4, :body => "They're having a FIRESALE?" }, { :id => 5, :body => "Is this the tiny town from Footloose?" }] }.to_json] }
|
284
|
-
stub.get("/users/2/comments/5") { |env| [200, {}, { :comment => { :id => 5, :body => "Is this the tiny town from Footloose?" } }.to_json] }
|
285
|
-
stub.get("/organizations/1") { |env| [200, {}, { :organization => { :id => 1, :name => "Bluth Company Foo" } }.to_json] }
|
286
|
-
end
|
287
|
-
end
|
288
531
|
spawn_model "Foo::User" do
|
289
|
-
parse_root_in_json true, :
|
532
|
+
parse_root_in_json true, format: :active_model_serializers
|
290
533
|
has_many :comments, class_name: "Foo::Comment"
|
291
|
-
|
534
|
+
has_one :role, class_name: "Foo::Role"
|
535
|
+
belongs_to :organization, class_name: "Foo::Organization"
|
536
|
+
end
|
537
|
+
|
538
|
+
spawn_model "Foo::Role" do
|
539
|
+
belongs_to :user
|
540
|
+
parse_root_in_json true, format: :active_model_serializers
|
292
541
|
end
|
542
|
+
|
293
543
|
spawn_model "Foo::Comment" do
|
294
544
|
belongs_to :user
|
295
|
-
parse_root_in_json true, :
|
545
|
+
parse_root_in_json true, format: :active_model_serializers
|
296
546
|
end
|
547
|
+
|
297
548
|
spawn_model "Foo::Organization" do
|
298
|
-
parse_root_in_json true, :
|
549
|
+
parse_root_in_json true, format: :active_model_serializers
|
299
550
|
end
|
300
|
-
|
301
|
-
@user_with_included_data = Foo::User.find(1)
|
302
|
-
@user_without_included_data = Foo::User.find(2)
|
303
551
|
end
|
304
552
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
553
|
+
context "with included data" do
|
554
|
+
before(:context) do
|
555
|
+
Her::API.setup url: "https://api.example.com" do |builder|
|
556
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
557
|
+
builder.use Faraday::Request::UrlEncoded
|
558
|
+
builder.adapter :test do |stub|
|
559
|
+
stub.get("/users/1") { [200, {}, { user: { 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] }
|
560
|
+
stub.get("/users/1/comments") { [200, {}, { comments: [{ id: 4, body: "They're having a FIRESALE?" }] }.to_json] }
|
561
|
+
stub.get("/organizations/1") { [200, {}, { organization: { id: 1, name: "Bluth Company Foo" } }.to_json] }
|
562
|
+
end
|
563
|
+
end
|
564
|
+
end
|
311
565
|
|
312
|
-
|
313
|
-
|
314
|
-
end
|
566
|
+
let(:user) { Foo::User.find(1) }
|
567
|
+
let(:user_params) { user.to_params }
|
315
568
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
569
|
+
it "maps an array of included data through has_many" do
|
570
|
+
expect(user.comments.first).to be_a(Foo::Comment)
|
571
|
+
expect(user.comments.length).to eq(2)
|
572
|
+
expect(user.comments.first.id).to eq(2)
|
573
|
+
expect(user.comments.first.body).to eq("Tobias, you blow hard!")
|
574
|
+
end
|
322
575
|
|
323
|
-
|
324
|
-
|
325
|
-
|
576
|
+
it "does not refetch the parents models data if they have been fetched before" do
|
577
|
+
expect(user.comments.first.user.object_id).to eq(user.object_id)
|
578
|
+
end
|
326
579
|
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
@user_with_included_data.organization.name.should == "Bluth Company"
|
331
|
-
end
|
580
|
+
it "fetches has_many data even if it was included, only if called with parameters" do
|
581
|
+
expect(user.comments.where(foo_id: 1).length).to eq(1)
|
582
|
+
end
|
332
583
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
584
|
+
it "maps an array of included data through belongs_to" do
|
585
|
+
expect(user.organization).to be_a(Foo::Organization)
|
586
|
+
expect(user.organization.id).to eq(1)
|
587
|
+
expect(user.organization.name).to eq("Bluth Company")
|
588
|
+
end
|
338
589
|
|
339
|
-
|
340
|
-
|
341
|
-
|
590
|
+
it "fetches belongs_to data even if it was included, only if called with parameters" do
|
591
|
+
expect(user.organization.where(foo_id: 1).name).to eq("Bluth Company Foo")
|
592
|
+
end
|
593
|
+
|
594
|
+
it "includes has_many relationships in params by default" do
|
595
|
+
expect(user_params[:comments]).to be_kind_of(Array)
|
596
|
+
expect(user_params[:comments].length).to eq(2)
|
597
|
+
end
|
342
598
|
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
599
|
+
it "includes has_one relationships in params by default" do
|
600
|
+
expect(user_params[:role]).to be_kind_of(Hash)
|
601
|
+
expect(user_params[:role]).not_to be_empty
|
602
|
+
end
|
603
|
+
|
604
|
+
it "includes belongs_to relationship in params by default" do
|
605
|
+
expect(user_params[:organization]).to be_kind_of(Hash)
|
606
|
+
expect(user_params[:organization]).not_to be_empty
|
607
|
+
end
|
347
608
|
end
|
348
609
|
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
610
|
+
context "without included data" do
|
611
|
+
before(:context) do
|
612
|
+
Her::API.setup url: "https://api.example.com" do |builder|
|
613
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
614
|
+
builder.use Faraday::Request::UrlEncoded
|
615
|
+
builder.adapter :test do |stub|
|
616
|
+
stub.get("/users/2") { [200, {}, { user: { id: 2, name: "Lindsay Fünke", organization_id: 1 } }.to_json] }
|
617
|
+
stub.get("/users/2/comments") { [200, {}, { comments: [{ id: 4, body: "They're having a FIRESALE?" }, { id: 5, body: "Is this the tiny town from Footloose?" }] }.to_json] }
|
618
|
+
stub.get("/users/2/comments/5") { [200, {}, { comment: { id: 5, body: "Is this the tiny town from Footloose?" } }.to_json] }
|
619
|
+
stub.get("/organizations/1") { [200, {}, { organization: { id: 1, name: "Bluth Company Foo" } }.to_json] }
|
620
|
+
end
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
let(:user) { Foo::User.find(2) }
|
625
|
+
|
626
|
+
it "fetches data that was not included through has_many" do
|
627
|
+
expect(user.comments.first).to be_a(Foo::Comment)
|
628
|
+
expect(user.comments.length).to eq(2)
|
629
|
+
expect(user.comments.first.id).to eq(4)
|
630
|
+
expect(user.comments.first.body).to eq("They're having a FIRESALE?")
|
631
|
+
end
|
632
|
+
|
633
|
+
it "fetches data that was not included through belongs_to" do
|
634
|
+
expect(user.organization).to be_a(Foo::Organization)
|
635
|
+
expect(user.organization.id).to eq(1)
|
636
|
+
expect(user.organization.name).to eq("Bluth Company Foo")
|
637
|
+
end
|
638
|
+
|
639
|
+
it "fetches data with the specified id when calling find" do
|
640
|
+
comment = user.comments.find(5)
|
641
|
+
expect(comment).to be_a(Foo::Comment)
|
642
|
+
expect(comment.id).to eq(5)
|
643
|
+
end
|
353
644
|
end
|
354
645
|
end
|
355
646
|
|
356
647
|
context "handling associations with details" do
|
357
648
|
before do
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
649
|
+
spawn_model "Foo::User" do
|
650
|
+
belongs_to :company, path: "/organizations/:id", foreign_key: :organization_id, data_key: :organization
|
651
|
+
end
|
652
|
+
|
653
|
+
spawn_model "Foo::Company"
|
654
|
+
end
|
655
|
+
|
656
|
+
context "with included data" do
|
657
|
+
before(:context) do
|
658
|
+
Her::API.setup url: "https://api.example.com" do |builder|
|
659
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
660
|
+
builder.use Faraday::Request::UrlEncoded
|
661
|
+
builder.adapter :test do |stub|
|
662
|
+
stub.get("/users/1") { [200, {}, { id: 1, name: "Tobias Fünke", organization: { id: 1, name: "Bluth Company Inc." }, organization_id: 1 }.to_json] }
|
663
|
+
stub.get("/users/4") { [200, {}, { id: 1, name: "Tobias Fünke", organization: { id: 1, name: "Bluth Company Inc." } }.to_json] }
|
664
|
+
stub.get("/users/3") { [200, {}, { id: 2, name: "Lindsay Fünke", organization: nil }.to_json] }
|
665
|
+
end
|
367
666
|
end
|
368
667
|
end
|
369
668
|
|
370
|
-
|
371
|
-
|
669
|
+
let(:user) { Foo::User.find(1) }
|
670
|
+
|
671
|
+
it "maps an array of included data through belongs_to" do
|
672
|
+
expect(user.company).to be_a(Foo::Company)
|
673
|
+
expect(user.company.id).to eq(1)
|
674
|
+
expect(user.company.name).to eq("Bluth Company Inc.")
|
372
675
|
end
|
373
676
|
|
374
|
-
|
677
|
+
context "when included data is nil" do
|
678
|
+
let(:user) { Foo::User.find(3) }
|
375
679
|
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
end
|
680
|
+
it "does not map included data" do
|
681
|
+
expect(user.company).to be_nil
|
682
|
+
end
|
683
|
+
end
|
381
684
|
|
382
|
-
|
383
|
-
|
384
|
-
@user_with_included_data.company.id.should == 1
|
385
|
-
@user_with_included_data.company.name.should == "Bluth Company Inc."
|
386
|
-
end
|
685
|
+
context "when included data has no foreign_key" do
|
686
|
+
let(:user) { Foo::User.find(4) }
|
387
687
|
|
388
|
-
|
389
|
-
|
688
|
+
it "maps included data anyway" do
|
689
|
+
expect(user.company.name).to eq("Bluth Company Inc.")
|
690
|
+
end
|
691
|
+
end
|
390
692
|
end
|
391
693
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
694
|
+
context "without included data" do
|
695
|
+
before(:context) do
|
696
|
+
Her::API.setup url: "https://api.example.com" do |builder|
|
697
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
698
|
+
builder.use Faraday::Request::UrlEncoded
|
699
|
+
builder.adapter :test do |stub|
|
700
|
+
stub.get("/users/2") { [200, {}, { id: 2, name: "Lindsay Fünke", organization_id: 1 }.to_json] }
|
701
|
+
stub.get("/organizations/1") { [200, {}, { id: 1, name: "Bluth Company" }.to_json] }
|
702
|
+
end
|
703
|
+
end
|
704
|
+
end
|
397
705
|
|
398
|
-
|
399
|
-
|
706
|
+
let(:user) { Foo::User.find(2) }
|
707
|
+
|
708
|
+
it "fetches data that was not included through belongs_to" do
|
709
|
+
expect(user.company).to be_a(Foo::Company)
|
710
|
+
expect(user.company.id).to eq(1)
|
711
|
+
expect(user.company.name).to eq("Bluth Company")
|
712
|
+
end
|
400
713
|
end
|
401
714
|
end
|
402
715
|
|
@@ -420,30 +733,30 @@ describe Her::Model::Associations do
|
|
420
733
|
subject { user_with_role.role }
|
421
734
|
|
422
735
|
it "doesnt mask the object's basic methods" do
|
423
|
-
subject.class.
|
736
|
+
expect(subject.class).to eq(Foo::Role)
|
424
737
|
end
|
425
738
|
|
426
739
|
it "doesnt mask core methods like extend" do
|
427
740
|
committer = Module.new
|
428
|
-
subject.extend
|
429
|
-
associated_value.
|
741
|
+
subject.extend committer
|
742
|
+
expect(associated_value).to be_kind_of committer
|
430
743
|
end
|
431
744
|
|
432
745
|
it "can return the association object" do
|
433
|
-
subject.association.
|
746
|
+
expect(subject.association).to be_kind_of Her::Model::Associations::Association
|
434
747
|
end
|
435
748
|
|
436
749
|
it "still can call fetch via the association" do
|
437
|
-
subject.association.fetch.
|
750
|
+
expect(subject.association.fetch).to eq associated_value
|
438
751
|
end
|
439
752
|
|
440
753
|
it "calls missing methods on associated value" do
|
441
|
-
subject.present
|
754
|
+
expect(subject.present?).to eq("of_course")
|
442
755
|
end
|
443
756
|
|
444
757
|
it "can use association methods like where" do
|
445
|
-
subject.where(role:
|
446
|
-
params.
|
758
|
+
expect(subject.where(role: "committer").association
|
759
|
+
.params).to include :role
|
447
760
|
end
|
448
761
|
end
|
449
762
|
|
@@ -456,21 +769,25 @@ describe Her::Model::Associations do
|
|
456
769
|
end
|
457
770
|
|
458
771
|
context "with #build" do
|
772
|
+
let(:comment) { Foo::User.new(id: 10).comments.build(body: "Hello!") }
|
773
|
+
|
459
774
|
it "takes the parent primary key" do
|
460
|
-
|
461
|
-
|
462
|
-
@comment.user_id.should == 10
|
775
|
+
expect(comment.body).to eq("Hello!")
|
776
|
+
expect(comment.user_id).to eq(10)
|
463
777
|
end
|
464
778
|
end
|
465
779
|
|
466
780
|
context "with #create" do
|
781
|
+
let(:user) { Foo::User.find(10) }
|
782
|
+
let(:comment) { user.comments.create(body: "Hello!") }
|
783
|
+
|
467
784
|
before do
|
468
|
-
Her::API.setup :
|
785
|
+
Her::API.setup url: "https://api.example.com" do |builder|
|
469
786
|
builder.use Her::Middleware::FirstLevelParseJSON
|
470
787
|
builder.use Faraday::Request::UrlEncoded
|
471
788
|
builder.adapter :test do |stub|
|
472
|
-
stub.get("/users/10") {
|
473
|
-
stub.post("/comments") { |env| [200, {}, { :
|
789
|
+
stub.get("/users/10") { [200, {}, { id: 10 }.to_json] }
|
790
|
+
stub.post("/comments") { |env| [200, {}, { id: 1, body: Faraday::Utils.parse_query(env[:body])["body"], user_id: Faraday::Utils.parse_query(env[:body])["user_id"].to_i }.to_json] }
|
474
791
|
end
|
475
792
|
end
|
476
793
|
|
@@ -479,25 +796,30 @@ describe Her::Model::Associations do
|
|
479
796
|
end
|
480
797
|
|
481
798
|
it "takes the parent primary key and saves the resource" do
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
@comment.user_id.should == 10
|
487
|
-
@user.comments.should == [@comment]
|
799
|
+
expect(comment.id).to eq(1)
|
800
|
+
expect(comment.body).to eq("Hello!")
|
801
|
+
expect(comment.user_id).to eq(10)
|
802
|
+
expect(user.comments).to eq([comment])
|
488
803
|
end
|
489
804
|
end
|
490
805
|
|
491
806
|
context "with #new" do
|
492
|
-
|
493
|
-
|
494
|
-
|
807
|
+
let(:user) { Foo::User.new(name: "vic", comments: [comment]) }
|
808
|
+
|
809
|
+
context "using hash attributes" do
|
810
|
+
let(:comment) { { text: "hello" } }
|
811
|
+
|
812
|
+
it "assigns nested models" do
|
813
|
+
expect(user.comments.first.text).to eq("hello")
|
814
|
+
end
|
495
815
|
end
|
496
816
|
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
817
|
+
context "using constructed objects" do
|
818
|
+
let(:comment) { Foo::Comment.new(text: "goodbye") }
|
819
|
+
|
820
|
+
it "assigns nested models" do
|
821
|
+
expect(user.comments.first.text).to eq("goodbye")
|
822
|
+
end
|
501
823
|
end
|
502
824
|
end
|
503
825
|
end
|