her 0.8.2 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +1 -1
- data/.rubocop.yml +1291 -0
- data/.travis.yml +2 -0
- data/README.md +23 -3
- data/her.gemspec +1 -3
- data/lib/her/middleware/json_api_parser.rb +1 -1
- data/lib/her/model/associations/association.rb +31 -0
- data/lib/her/model/associations/association_proxy.rb +1 -1
- data/lib/her/model/attributes.rb +2 -0
- data/lib/her/model/orm.rb +79 -6
- data/lib/her/model/parse.rb +8 -12
- data/lib/her/model/relation.rb +45 -1
- 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 +248 -161
- data/spec/model/attributes_spec.rb +106 -99
- data/spec/model/callbacks_spec.rb +58 -26
- data/spec/model/dirty_spec.rb +30 -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 +312 -155
- data/spec/model/parse_spec.rb +77 -77
- data/spec/model/paths_spec.rb +109 -109
- data/spec/model/relation_spec.rb +76 -68
- 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 +32 -59
@@ -8,7 +8,11 @@ describe Her::Model::Associations do
|
|
8
8
|
|
9
9
|
context "single has_many association" do
|
10
10
|
before { Foo::User.has_many :comments }
|
11
|
-
|
11
|
+
|
12
|
+
describe "[:has_many]" do
|
13
|
+
subject { super()[:has_many] }
|
14
|
+
it { is_expected.to eql [{ name: :comments, data_key: :comments, default: [], class_name: "Comment", path: "/comments", inverse_of: nil }] }
|
15
|
+
end
|
12
16
|
end
|
13
17
|
|
14
18
|
context "multiple has_many associations" do
|
@@ -17,12 +21,19 @@ describe Her::Model::Associations do
|
|
17
21
|
Foo::User.has_many :posts
|
18
22
|
end
|
19
23
|
|
20
|
-
|
24
|
+
describe "[:has_many]" do
|
25
|
+
subject { super()[:has_many] }
|
26
|
+
it { is_expected.to eql [{ name: :comments, data_key: :comments, default: [], class_name: "Comment", path: "/comments", inverse_of: nil }, { name: :posts, data_key: :posts, default: [], class_name: "Post", path: "/posts", inverse_of: nil }] }
|
27
|
+
end
|
21
28
|
end
|
22
29
|
|
23
30
|
context "single has_one association" do
|
24
31
|
before { Foo::User.has_one :category }
|
25
|
-
|
32
|
+
|
33
|
+
describe "[:has_one]" do
|
34
|
+
subject { super()[:has_one] }
|
35
|
+
it { is_expected.to eql [{ name: :category, data_key: :category, default: nil, class_name: "Category", path: "/category" }] }
|
36
|
+
end
|
26
37
|
end
|
27
38
|
|
28
39
|
context "multiple has_one associations" do
|
@@ -31,12 +42,19 @@ describe Her::Model::Associations do
|
|
31
42
|
Foo::User.has_one :role
|
32
43
|
end
|
33
44
|
|
34
|
-
|
45
|
+
describe "[:has_one]" do
|
46
|
+
subject { super()[:has_one] }
|
47
|
+
it { is_expected.to eql [{ name: :category, data_key: :category, default: nil, class_name: "Category", path: "/category" }, { name: :role, data_key: :role, default: nil, class_name: "Role", path: "/role" }] }
|
48
|
+
end
|
35
49
|
end
|
36
50
|
|
37
51
|
context "single belongs_to association" do
|
38
52
|
before { Foo::User.belongs_to :organization }
|
39
|
-
|
53
|
+
|
54
|
+
describe "[:belongs_to]" do
|
55
|
+
subject { super()[:belongs_to] }
|
56
|
+
it { is_expected.to eql [{ name: :organization, data_key: :organization, default: nil, class_name: "Organization", foreign_key: "organization_id", path: "/organizations/:id" }] }
|
57
|
+
end
|
40
58
|
end
|
41
59
|
|
42
60
|
context "multiple belongs_to association" do
|
@@ -45,7 +63,10 @@ describe Her::Model::Associations do
|
|
45
63
|
Foo::User.belongs_to :family
|
46
64
|
end
|
47
65
|
|
48
|
-
|
66
|
+
describe "[:belongs_to]" do
|
67
|
+
subject { super()[:belongs_to] }
|
68
|
+
it { is_expected.to eql [{ name: :organization, data_key: :organization, default: nil, class_name: "Organization", foreign_key: "organization_id", path: "/organizations/:id" }, { name: :family, data_key: :family, default: nil, class_name: "Family", foreign_key: "family_id", path: "/families/:id" }] }
|
69
|
+
end
|
49
70
|
end
|
50
71
|
end
|
51
72
|
|
@@ -55,56 +76,76 @@ describe Her::Model::Associations do
|
|
55
76
|
|
56
77
|
context "in base class" do
|
57
78
|
context "single has_many association" do
|
58
|
-
before { Foo::User.has_many :comments, :
|
59
|
-
|
79
|
+
before { Foo::User.has_many :comments, class_name: "Post", inverse_of: :admin, data_key: :user_comments, default: {} }
|
80
|
+
|
81
|
+
describe "[:has_many]" do
|
82
|
+
subject { super()[:has_many] }
|
83
|
+
it { is_expected.to eql [{ name: :comments, data_key: :user_comments, default: {}, class_name: "Post", path: "/comments", inverse_of: :admin }] }
|
84
|
+
end
|
60
85
|
end
|
61
86
|
|
62
87
|
context "single has_one association" do
|
63
|
-
before { Foo::User.has_one :category, :
|
64
|
-
|
88
|
+
before { Foo::User.has_one :category, class_name: "Topic", foreign_key: "topic_id", data_key: :topic, default: nil }
|
89
|
+
|
90
|
+
describe "[:has_one]" do
|
91
|
+
subject { super()[:has_one] }
|
92
|
+
it { is_expected.to eql [{ name: :category, data_key: :topic, default: nil, class_name: "Topic", foreign_key: "topic_id", path: "/category" }] }
|
93
|
+
end
|
65
94
|
end
|
66
95
|
|
67
96
|
context "single belongs_to association" do
|
68
|
-
before { Foo::User.belongs_to :organization, :
|
69
|
-
|
97
|
+
before { Foo::User.belongs_to :organization, class_name: "Business", foreign_key: "org_id", data_key: :org, default: true }
|
98
|
+
|
99
|
+
describe "[:belongs_to]" do
|
100
|
+
subject { super()[:belongs_to] }
|
101
|
+
it { is_expected.to eql [{ name: :organization, data_key: :org, default: true, class_name: "Business", foreign_key: "org_id", path: "/organizations/:id" }] }
|
102
|
+
end
|
70
103
|
end
|
71
104
|
end
|
72
105
|
|
73
106
|
context "in parent class" do
|
74
|
-
before { Foo::User.has_many :comments, :
|
107
|
+
before { Foo::User.has_many :comments, class_name: "Post" }
|
75
108
|
|
76
109
|
describe "associations accessor" do
|
77
110
|
subject { Class.new(Foo::User).associations }
|
78
|
-
|
79
|
-
|
111
|
+
|
112
|
+
describe "#object_id" do
|
113
|
+
subject { super().object_id }
|
114
|
+
it { is_expected.not_to eql Foo::User.associations.object_id }
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "[:has_many]" do
|
118
|
+
subject { super()[:has_many] }
|
119
|
+
it { is_expected.to eql [{ name: :comments, data_key: :comments, default: [], class_name: "Post", path: "/comments", inverse_of: nil }] }
|
120
|
+
end
|
80
121
|
end
|
81
122
|
end
|
82
123
|
end
|
83
124
|
|
84
125
|
context "handling associations without details" do
|
85
126
|
before do
|
86
|
-
Her::API.setup :
|
127
|
+
Her::API.setup url: "https://api.example.com" do |builder|
|
87
128
|
builder.use Her::Middleware::FirstLevelParseJSON
|
88
129
|
builder.use Faraday::Request::UrlEncoded
|
89
130
|
builder.adapter :test do |stub|
|
90
|
-
stub.get("/users/1") {
|
91
|
-
stub.get("/users/2") {
|
92
|
-
stub.get("/users/1/comments") {
|
93
|
-
stub.get("/users/2/comments") {
|
94
|
-
stub.get("/users/2/comments/5") {
|
95
|
-
stub.get("/users/2/role") {
|
96
|
-
stub.get("/users/1/role") {
|
97
|
-
stub.get("/users/1/posts") {
|
98
|
-
stub.get("/organizations/1") {
|
99
|
-
stub.post("/users") {
|
100
|
-
stub.put("/users/5") {
|
101
|
-
stub.delete("/users/5") {
|
131
|
+
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] }
|
132
|
+
stub.get("/users/2") { [200, {}, { id: 2, name: "Lindsay Fünke", organization_id: 2 }.to_json] }
|
133
|
+
stub.get("/users/1/comments") { [200, {}, [{ comment: { id: 4, body: "They're having a FIRESALE?" } }].to_json] }
|
134
|
+
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] }
|
135
|
+
stub.get("/users/2/comments/5") { [200, {}, { comment: { id: 5, body: "Is this the tiny town from Footloose?" } }.to_json] }
|
136
|
+
stub.get("/users/2/role") { [200, {}, { id: 2, body: "User" }.to_json] }
|
137
|
+
stub.get("/users/1/role") { [200, {}, { id: 3, body: "User" }.to_json] }
|
138
|
+
stub.get("/users/1/posts") { [200, {}, [{ id: 1, body: "blogging stuff", admin_id: 1 }].to_json] }
|
139
|
+
stub.get("/organizations/1") { [200, {}, { organization: { id: 1, name: "Bluth Company Foo" } }.to_json] }
|
140
|
+
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] }
|
141
|
+
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] }
|
142
|
+
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] }
|
102
143
|
|
103
144
|
stub.get("/organizations/2") do |env|
|
104
145
|
if env[:params]["admin"] == "true"
|
105
|
-
[200, {}, { :
|
146
|
+
[200, {}, { organization: { id: 2, name: "Bluth Company (admin)" } }.to_json]
|
106
147
|
else
|
107
|
-
[200, {}, { :
|
148
|
+
[200, {}, { organization: { id: 2, name: "Bluth Company" } }.to_json]
|
108
149
|
end
|
109
150
|
end
|
110
151
|
end
|
@@ -112,16 +153,18 @@ describe Her::Model::Associations do
|
|
112
153
|
|
113
154
|
spawn_model "Foo::User" do
|
114
155
|
has_many :comments, class_name: "Foo::Comment"
|
115
|
-
has_one :role
|
116
|
-
belongs_to :organization
|
117
|
-
has_many :posts, :
|
156
|
+
has_one :role, class_name: "Foo::Role"
|
157
|
+
belongs_to :organization, class_name: "Foo::Organization"
|
158
|
+
has_many :posts, inverse_of: :admin
|
118
159
|
end
|
160
|
+
|
119
161
|
spawn_model "Foo::Comment" do
|
120
162
|
belongs_to :user
|
121
163
|
parse_root_in_json true
|
122
164
|
end
|
165
|
+
|
123
166
|
spawn_model "Foo::Post" do
|
124
|
-
belongs_to :admin, :
|
167
|
+
belongs_to :admin, class_name: "Foo::User"
|
125
168
|
end
|
126
169
|
|
127
170
|
spawn_model "Foo::Organization" do
|
@@ -136,136 +179,159 @@ describe Her::Model::Associations do
|
|
136
179
|
end
|
137
180
|
|
138
181
|
let(:user_with_included_data_after_create) { Foo::User.create }
|
139
|
-
let(:user_with_included_data_after_save_existing) { Foo::User.save_existing(5, :
|
140
|
-
let(:user_with_included_data_after_destroy) { Foo::User.new(:
|
141
|
-
let(:comment_without_included_parent_data) { Foo::Comment.new(:
|
182
|
+
let(:user_with_included_data_after_save_existing) { Foo::User.save_existing(5, name: "Clancy Brown") }
|
183
|
+
let(:user_with_included_data_after_destroy) { Foo::User.new(id: 5).destroy }
|
184
|
+
let(:comment_without_included_parent_data) { Foo::Comment.new(id: 7, user_id: 1) }
|
185
|
+
let(:new_user) { Foo::User.new }
|
142
186
|
|
143
187
|
it "maps an array of included data through has_many" do
|
144
|
-
@user_with_included_data.comments.first.
|
145
|
-
@user_with_included_data.comments.length.
|
146
|
-
@user_with_included_data.comments.first.id.
|
147
|
-
@user_with_included_data.comments.first.body.
|
188
|
+
expect(@user_with_included_data.comments.first).to be_a(Foo::Comment)
|
189
|
+
expect(@user_with_included_data.comments.length).to eq(2)
|
190
|
+
expect(@user_with_included_data.comments.first.id).to eq(2)
|
191
|
+
expect(@user_with_included_data.comments.first.body).to eq("Tobias, you blow hard!")
|
148
192
|
end
|
149
193
|
|
150
194
|
it "does not refetch the parents models data if they have been fetched before" do
|
151
|
-
@user_with_included_data.comments.first.user.object_id.
|
195
|
+
expect(@user_with_included_data.comments.first.user.object_id).to eq(@user_with_included_data.object_id)
|
152
196
|
end
|
153
197
|
|
154
198
|
it "does fetch the parent models data only once" do
|
155
|
-
comment_without_included_parent_data.user.object_id.
|
199
|
+
expect(comment_without_included_parent_data.user.object_id).to eq(comment_without_included_parent_data.user.object_id)
|
156
200
|
end
|
157
201
|
|
158
202
|
it "does fetch the parent models data that was cached if called with parameters" do
|
159
|
-
comment_without_included_parent_data.user.object_id.
|
203
|
+
expect(comment_without_included_parent_data.user.object_id).not_to eq(comment_without_included_parent_data.user.where(a: 2).object_id)
|
160
204
|
end
|
161
205
|
|
162
206
|
it "uses the given inverse_of key to set the parent model" do
|
163
|
-
@user_with_included_data.posts.first.admin.object_id.
|
207
|
+
expect(@user_with_included_data.posts.first.admin.object_id).to eq(@user_with_included_data.object_id)
|
208
|
+
end
|
209
|
+
|
210
|
+
it "doesn't attempt to fetch association data for a new resource" do
|
211
|
+
expect(new_user.comments).to eq([])
|
212
|
+
expect(new_user.role).to be_nil
|
213
|
+
expect(new_user.organization).to be_nil
|
164
214
|
end
|
165
215
|
|
166
216
|
it "fetches data that was not included through has_many" do
|
167
|
-
@user_without_included_data.comments.first.
|
168
|
-
@user_without_included_data.comments.length.
|
169
|
-
@user_without_included_data.comments.first.id.
|
170
|
-
@user_without_included_data.comments.first.body.
|
217
|
+
expect(@user_without_included_data.comments.first).to be_a(Foo::Comment)
|
218
|
+
expect(@user_without_included_data.comments.length).to eq(2)
|
219
|
+
expect(@user_without_included_data.comments.first.id).to eq(4)
|
220
|
+
expect(@user_without_included_data.comments.first.body).to eq("They're having a FIRESALE?")
|
171
221
|
end
|
172
222
|
|
173
223
|
it "fetches has_many data even if it was included, only if called with parameters" do
|
174
|
-
@user_with_included_data.comments.where(:
|
224
|
+
expect(@user_with_included_data.comments.where(foo_id: 1).length).to eq(1)
|
175
225
|
end
|
176
226
|
|
177
227
|
it "fetches data that was not included through has_many only once" do
|
178
|
-
@user_without_included_data.comments.first.object_id.
|
228
|
+
expect(@user_without_included_data.comments.first.object_id).to eq(@user_without_included_data.comments.first.object_id)
|
179
229
|
end
|
180
230
|
|
181
231
|
it "fetches data that was cached through has_many if called with parameters" do
|
182
|
-
@user_without_included_data.comments.first.object_id.
|
232
|
+
expect(@user_without_included_data.comments.first.object_id).not_to eq(@user_without_included_data.comments.where(foo_id: 1).first.object_id)
|
233
|
+
end
|
234
|
+
|
235
|
+
it "fetches data again after being reloaded" do
|
236
|
+
expect { @user_without_included_data.comments.reload }.to change { @user_without_included_data.comments.first.object_id }
|
183
237
|
end
|
184
238
|
|
185
239
|
it "maps an array of included data through has_one" do
|
186
|
-
@user_with_included_data.role.
|
187
|
-
@user_with_included_data.role.object_id.
|
188
|
-
@user_with_included_data.role.id.
|
189
|
-
@user_with_included_data.role.body.
|
240
|
+
expect(@user_with_included_data.role).to be_a(Foo::Role)
|
241
|
+
expect(@user_with_included_data.role.object_id).to eq(@user_with_included_data.role.object_id)
|
242
|
+
expect(@user_with_included_data.role.id).to eq(1)
|
243
|
+
expect(@user_with_included_data.role.body).to eq("Admin")
|
190
244
|
end
|
191
245
|
|
192
246
|
it "fetches data that was not included through has_one" do
|
193
|
-
@user_without_included_data.role.
|
194
|
-
@user_without_included_data.role.id.
|
195
|
-
@user_without_included_data.role.body.
|
247
|
+
expect(@user_without_included_data.role).to be_a(Foo::Role)
|
248
|
+
expect(@user_without_included_data.role.id).to eq(2)
|
249
|
+
expect(@user_without_included_data.role.body).to eq("User")
|
196
250
|
end
|
197
251
|
|
198
252
|
it "fetches has_one data even if it was included, only if called with parameters" do
|
199
|
-
@user_with_included_data.role.where(:
|
253
|
+
expect(@user_with_included_data.role.where(foo_id: 2).id).to eq(3)
|
200
254
|
end
|
201
255
|
|
202
256
|
it "maps an array of included data through belongs_to" do
|
203
|
-
@user_with_included_data.organization.
|
204
|
-
@user_with_included_data.organization.id.
|
205
|
-
@user_with_included_data.organization.name.
|
257
|
+
expect(@user_with_included_data.organization).to be_a(Foo::Organization)
|
258
|
+
expect(@user_with_included_data.organization.id).to eq(1)
|
259
|
+
expect(@user_with_included_data.organization.name).to eq("Bluth Company")
|
206
260
|
end
|
207
261
|
|
208
262
|
it "fetches data that was not included through belongs_to" do
|
209
|
-
@user_without_included_data.organization.
|
210
|
-
@user_without_included_data.organization.id.
|
211
|
-
@user_without_included_data.organization.name.
|
263
|
+
expect(@user_without_included_data.organization).to be_a(Foo::Organization)
|
264
|
+
expect(@user_without_included_data.organization.id).to eq(2)
|
265
|
+
expect(@user_without_included_data.organization.name).to eq("Bluth Company")
|
212
266
|
end
|
213
267
|
|
214
268
|
it "returns nil if the foreign key is nil" do
|
215
|
-
@user_without_organization_and_not_persisted.organization.
|
269
|
+
expect(@user_without_organization_and_not_persisted.organization).to be_nil
|
216
270
|
end
|
217
271
|
|
218
272
|
it "fetches belongs_to data even if it was included, only if called with parameters" do
|
219
|
-
@user_with_included_data.organization.where(:
|
273
|
+
expect(@user_with_included_data.organization.where(foo_id: 1).name).to eq("Bluth Company Foo")
|
220
274
|
end
|
221
275
|
|
222
276
|
it "can tell if it has a association" do
|
223
|
-
@user_without_included_data.has_association?(:unknown_association).
|
224
|
-
@user_without_included_data.has_association?(:organization).
|
277
|
+
expect(@user_without_included_data.has_association?(:unknown_association)).to be false
|
278
|
+
expect(@user_without_included_data.has_association?(:organization)).to be true
|
225
279
|
end
|
226
280
|
|
227
281
|
it "fetches the resource corresponding to a named association" do
|
228
|
-
@user_without_included_data.get_association(:unknown_association).
|
229
|
-
@user_without_included_data.get_association(:organization).name.
|
282
|
+
expect(@user_without_included_data.get_association(:unknown_association)).to be_nil
|
283
|
+
expect(@user_without_included_data.get_association(:organization).name).to eq("Bluth Company")
|
230
284
|
end
|
231
285
|
|
232
286
|
it "pass query string parameters when additional arguments are passed" do
|
233
|
-
@user_without_included_data.organization.where(:
|
234
|
-
@user_without_included_data.organization.name.
|
287
|
+
expect(@user_without_included_data.organization.where(admin: true).name).to eq("Bluth Company (admin)")
|
288
|
+
expect(@user_without_included_data.organization.name).to eq("Bluth Company")
|
235
289
|
end
|
236
290
|
|
237
291
|
it "fetches data with the specified id when calling find" do
|
238
292
|
comment = @user_without_included_data.comments.find(5)
|
239
|
-
comment.
|
240
|
-
comment.id.
|
293
|
+
expect(comment).to be_a(Foo::Comment)
|
294
|
+
expect(comment.id).to eq(5)
|
241
295
|
end
|
242
296
|
|
243
297
|
it "'s associations responds to #empty?" do
|
244
|
-
@user_without_included_data.organization.respond_to?(:empty?).
|
245
|
-
@user_without_included_data.organization.
|
298
|
+
expect(@user_without_included_data.organization.respond_to?(:empty?)).to be_truthy
|
299
|
+
expect(@user_without_included_data.organization).not_to be_empty
|
300
|
+
end
|
301
|
+
|
302
|
+
it "includes has_many relationships in params by default" do
|
303
|
+
params = @user_with_included_data.to_params
|
304
|
+
expect(params[:comments]).to be_kind_of(Array)
|
305
|
+
expect(params[:comments].length).to eq(2)
|
246
306
|
end
|
247
307
|
|
248
|
-
it
|
308
|
+
it "includes has_one relationship in params by default" do
|
249
309
|
params = @user_with_included_data.to_params
|
250
|
-
params[:
|
251
|
-
params[:
|
310
|
+
expect(params[:role]).to be_kind_of(Hash)
|
311
|
+
expect(params[:role]).not_to be_empty
|
312
|
+
end
|
313
|
+
|
314
|
+
it "includes belongs_to relationship in params by default" do
|
315
|
+
params = @user_with_included_data.to_params
|
316
|
+
expect(params[:organization]).to be_kind_of(Hash)
|
317
|
+
expect(params[:organization]).not_to be_empty
|
252
318
|
end
|
253
319
|
|
254
320
|
[:create, :save_existing, :destroy].each do |type|
|
255
321
|
context "after #{type}" do
|
256
|
-
let(:subject) {
|
322
|
+
let(:subject) { send("user_with_included_data_after_#{type}") }
|
257
323
|
|
258
324
|
it "maps an array of included data through has_many" do
|
259
|
-
subject.comments.first.
|
260
|
-
subject.comments.length.
|
261
|
-
subject.comments.first.id.
|
262
|
-
subject.comments.first.body.
|
325
|
+
expect(subject.comments.first).to be_a(Foo::Comment)
|
326
|
+
expect(subject.comments.length).to eq(1)
|
327
|
+
expect(subject.comments.first.id).to eq(99)
|
328
|
+
expect(subject.comments.first.body).to eq("Rodríguez, nasibisibusi?")
|
263
329
|
end
|
264
330
|
|
265
331
|
it "maps an array of included data through has_one" do
|
266
|
-
subject.role.
|
267
|
-
subject.role.id.
|
268
|
-
subject.role.body.
|
332
|
+
expect(subject.role).to be_a(Foo::Role)
|
333
|
+
expect(subject.role.id).to eq(1)
|
334
|
+
expect(subject.role.body).to eq("Admin")
|
269
335
|
end
|
270
336
|
end
|
271
337
|
end
|
@@ -273,29 +339,38 @@ describe Her::Model::Associations do
|
|
273
339
|
|
274
340
|
context "handling associations with details in active_model_serializers format" do
|
275
341
|
before do
|
276
|
-
Her::API.setup :
|
342
|
+
Her::API.setup url: "https://api.example.com" do |builder|
|
277
343
|
builder.use Her::Middleware::FirstLevelParseJSON
|
278
344
|
builder.use Faraday::Request::UrlEncoded
|
279
345
|
builder.adapter :test do |stub|
|
280
|
-
stub.get("/users/1") {
|
281
|
-
stub.get("/users/2") {
|
282
|
-
stub.get("/users/1/comments") {
|
283
|
-
stub.get("/users/2/comments") {
|
284
|
-
stub.get("/users/2/comments/5") {
|
285
|
-
stub.get("/organizations/1") {
|
346
|
+
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] }
|
347
|
+
stub.get("/users/2") { [200, {}, { user: { id: 2, name: "Lindsay Fünke", organization_id: 1 } }.to_json] }
|
348
|
+
stub.get("/users/1/comments") { [200, {}, { comments: [{ id: 4, body: "They're having a FIRESALE?" }] }.to_json] }
|
349
|
+
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] }
|
350
|
+
stub.get("/users/2/comments/5") { [200, {}, { comment: { id: 5, body: "Is this the tiny town from Footloose?" } }.to_json] }
|
351
|
+
stub.get("/organizations/1") { [200, {}, { organization: { id: 1, name: "Bluth Company Foo" } }.to_json] }
|
286
352
|
end
|
287
353
|
end
|
354
|
+
|
288
355
|
spawn_model "Foo::User" do
|
289
|
-
parse_root_in_json true, :
|
356
|
+
parse_root_in_json true, format: :active_model_serializers
|
290
357
|
has_many :comments, class_name: "Foo::Comment"
|
291
|
-
|
358
|
+
has_one :role, class_name: "Foo::Role"
|
359
|
+
belongs_to :organization, class_name: "Foo::Organization"
|
360
|
+
end
|
361
|
+
|
362
|
+
spawn_model "Foo::Role" do
|
363
|
+
belongs_to :user
|
364
|
+
parse_root_in_json true, format: :active_model_serializers
|
292
365
|
end
|
366
|
+
|
293
367
|
spawn_model "Foo::Comment" do
|
294
368
|
belongs_to :user
|
295
|
-
parse_root_in_json true, :
|
369
|
+
parse_root_in_json true, format: :active_model_serializers
|
296
370
|
end
|
371
|
+
|
297
372
|
spawn_model "Foo::Organization" do
|
298
|
-
parse_root_in_json true, :
|
373
|
+
parse_root_in_json true, format: :active_model_serializers
|
299
374
|
end
|
300
375
|
|
301
376
|
@user_with_included_data = Foo::User.find(1)
|
@@ -303,72 +378,84 @@ describe Her::Model::Associations do
|
|
303
378
|
end
|
304
379
|
|
305
380
|
it "maps an array of included data through has_many" do
|
306
|
-
@user_with_included_data.comments.first.
|
307
|
-
@user_with_included_data.comments.length.
|
308
|
-
@user_with_included_data.comments.first.id.
|
309
|
-
@user_with_included_data.comments.first.body.
|
381
|
+
expect(@user_with_included_data.comments.first).to be_a(Foo::Comment)
|
382
|
+
expect(@user_with_included_data.comments.length).to eq(2)
|
383
|
+
expect(@user_with_included_data.comments.first.id).to eq(2)
|
384
|
+
expect(@user_with_included_data.comments.first.body).to eq("Tobias, you blow hard!")
|
310
385
|
end
|
311
386
|
|
312
387
|
it "does not refetch the parents models data if they have been fetched before" do
|
313
|
-
@user_with_included_data.comments.first.user.object_id.
|
388
|
+
expect(@user_with_included_data.comments.first.user.object_id).to eq(@user_with_included_data.object_id)
|
314
389
|
end
|
315
390
|
|
316
391
|
it "fetches data that was not included through has_many" do
|
317
|
-
@user_without_included_data.comments.first.
|
318
|
-
@user_without_included_data.comments.length.
|
319
|
-
@user_without_included_data.comments.first.id.
|
320
|
-
@user_without_included_data.comments.first.body.
|
392
|
+
expect(@user_without_included_data.comments.first).to be_a(Foo::Comment)
|
393
|
+
expect(@user_without_included_data.comments.length).to eq(2)
|
394
|
+
expect(@user_without_included_data.comments.first.id).to eq(4)
|
395
|
+
expect(@user_without_included_data.comments.first.body).to eq("They're having a FIRESALE?")
|
321
396
|
end
|
322
397
|
|
323
398
|
it "fetches has_many data even if it was included, only if called with parameters" do
|
324
|
-
@user_with_included_data.comments.where(:
|
399
|
+
expect(@user_with_included_data.comments.where(foo_id: 1).length).to eq(1)
|
325
400
|
end
|
326
401
|
|
327
402
|
it "maps an array of included data through belongs_to" do
|
328
|
-
@user_with_included_data.organization.
|
329
|
-
@user_with_included_data.organization.id.
|
330
|
-
@user_with_included_data.organization.name.
|
403
|
+
expect(@user_with_included_data.organization).to be_a(Foo::Organization)
|
404
|
+
expect(@user_with_included_data.organization.id).to eq(1)
|
405
|
+
expect(@user_with_included_data.organization.name).to eq("Bluth Company")
|
331
406
|
end
|
332
407
|
|
333
408
|
it "fetches data that was not included through belongs_to" do
|
334
|
-
@user_without_included_data.organization.
|
335
|
-
@user_without_included_data.organization.id.
|
336
|
-
@user_without_included_data.organization.name.
|
409
|
+
expect(@user_without_included_data.organization).to be_a(Foo::Organization)
|
410
|
+
expect(@user_without_included_data.organization.id).to eq(1)
|
411
|
+
expect(@user_without_included_data.organization.name).to eq("Bluth Company Foo")
|
337
412
|
end
|
338
413
|
|
339
414
|
it "fetches belongs_to data even if it was included, only if called with parameters" do
|
340
|
-
@user_with_included_data.organization.where(:
|
415
|
+
expect(@user_with_included_data.organization.where(foo_id: 1).name).to eq("Bluth Company Foo")
|
341
416
|
end
|
342
417
|
|
343
418
|
it "fetches data with the specified id when calling find" do
|
344
419
|
comment = @user_without_included_data.comments.find(5)
|
345
|
-
comment.
|
346
|
-
comment.id.
|
420
|
+
expect(comment).to be_a(Foo::Comment)
|
421
|
+
expect(comment.id).to eq(5)
|
422
|
+
end
|
423
|
+
|
424
|
+
it "includes has_many relationships in params by default" do
|
425
|
+
params = @user_with_included_data.to_params
|
426
|
+
expect(params[:comments]).to be_kind_of(Array)
|
427
|
+
expect(params[:comments].length).to eq(2)
|
428
|
+
end
|
429
|
+
|
430
|
+
it "includes has_one relationships in params by default" do
|
431
|
+
params = @user_with_included_data.to_params
|
432
|
+
expect(params[:role]).to be_kind_of(Hash)
|
433
|
+
expect(params[:role]).not_to be_empty
|
347
434
|
end
|
348
435
|
|
349
|
-
it
|
436
|
+
it "includes belongs_to relationship in params by default" do
|
350
437
|
params = @user_with_included_data.to_params
|
351
|
-
params[:
|
352
|
-
params[:
|
438
|
+
expect(params[:organization]).to be_kind_of(Hash)
|
439
|
+
expect(params[:organization]).not_to be_empty
|
353
440
|
end
|
354
441
|
end
|
355
442
|
|
356
443
|
context "handling associations with details" do
|
357
444
|
before do
|
358
|
-
Her::API.setup :
|
445
|
+
Her::API.setup url: "https://api.example.com" do |builder|
|
359
446
|
builder.use Her::Middleware::FirstLevelParseJSON
|
360
447
|
builder.use Faraday::Request::UrlEncoded
|
361
448
|
builder.adapter :test do |stub|
|
362
|
-
stub.get("/users/1") {
|
363
|
-
stub.get("/users/4") {
|
364
|
-
stub.get("/users/2") {
|
365
|
-
stub.get("/users/3") {
|
366
|
-
stub.get("/companies/1") {
|
449
|
+
stub.get("/users/1") { [200, {}, { id: 1, name: "Tobias Fünke", organization: { id: 1, name: "Bluth Company Inc." }, organization_id: 1 }.to_json] }
|
450
|
+
stub.get("/users/4") { [200, {}, { id: 1, name: "Tobias Fünke", organization: { id: 1, name: "Bluth Company Inc." } }.to_json] }
|
451
|
+
stub.get("/users/2") { [200, {}, { id: 2, name: "Lindsay Fünke", organization_id: 1 }.to_json] }
|
452
|
+
stub.get("/users/3") { [200, {}, { id: 2, name: "Lindsay Fünke", company: nil }.to_json] }
|
453
|
+
stub.get("/companies/1") { [200, {}, { id: 1, name: "Bluth Company" }.to_json] }
|
367
454
|
end
|
368
455
|
end
|
369
456
|
|
370
457
|
spawn_model "Foo::User" do
|
371
|
-
belongs_to :company, :
|
458
|
+
belongs_to :company, path: "/organizations/:id", foreign_key: :organization_id, data_key: :organization
|
372
459
|
end
|
373
460
|
|
374
461
|
spawn_model "Foo::Company"
|
@@ -380,23 +467,23 @@ describe Her::Model::Associations do
|
|
380
467
|
end
|
381
468
|
|
382
469
|
it "maps an array of included data through belongs_to" do
|
383
|
-
@user_with_included_data.company.
|
384
|
-
@user_with_included_data.company.id.
|
385
|
-
@user_with_included_data.company.name.
|
470
|
+
expect(@user_with_included_data.company).to be_a(Foo::Company)
|
471
|
+
expect(@user_with_included_data.company.id).to eq(1)
|
472
|
+
expect(@user_with_included_data.company.name).to eq("Bluth Company Inc.")
|
386
473
|
end
|
387
474
|
|
388
475
|
it "does not map included data if it’s nil" do
|
389
|
-
@user_with_included_nil_data.company.
|
476
|
+
expect(@user_with_included_nil_data.company).to be_nil
|
390
477
|
end
|
391
478
|
|
392
479
|
it "fetches data that was not included through belongs_to" do
|
393
|
-
@user_without_included_data.company.
|
394
|
-
@user_without_included_data.company.id.
|
395
|
-
@user_without_included_data.company.name.
|
480
|
+
expect(@user_without_included_data.company).to be_a(Foo::Company)
|
481
|
+
expect(@user_without_included_data.company.id).to eq(1)
|
482
|
+
expect(@user_without_included_data.company.name).to eq("Bluth Company")
|
396
483
|
end
|
397
484
|
|
398
485
|
it "does not require foreugn key to have nested object" do
|
399
|
-
@user_with_included_data_but_no_fk.company.name.
|
486
|
+
expect(@user_with_included_data_but_no_fk.company.name).to eq("Bluth Company Inc.")
|
400
487
|
end
|
401
488
|
end
|
402
489
|
|
@@ -420,30 +507,30 @@ describe Her::Model::Associations do
|
|
420
507
|
subject { user_with_role.role }
|
421
508
|
|
422
509
|
it "doesnt mask the object's basic methods" do
|
423
|
-
subject.class.
|
510
|
+
expect(subject.class).to eq(Foo::Role)
|
424
511
|
end
|
425
512
|
|
426
513
|
it "doesnt mask core methods like extend" do
|
427
514
|
committer = Module.new
|
428
|
-
subject.extend
|
429
|
-
associated_value.
|
515
|
+
subject.extend committer
|
516
|
+
expect(associated_value).to be_kind_of committer
|
430
517
|
end
|
431
518
|
|
432
519
|
it "can return the association object" do
|
433
|
-
subject.association.
|
520
|
+
expect(subject.association).to be_kind_of Her::Model::Associations::Association
|
434
521
|
end
|
435
522
|
|
436
523
|
it "still can call fetch via the association" do
|
437
|
-
subject.association.fetch.
|
524
|
+
expect(subject.association.fetch).to eq associated_value
|
438
525
|
end
|
439
526
|
|
440
527
|
it "calls missing methods on associated value" do
|
441
|
-
subject.present
|
528
|
+
expect(subject.present?).to eq("of_course")
|
442
529
|
end
|
443
530
|
|
444
531
|
it "can use association methods like where" do
|
445
|
-
subject.where(role:
|
446
|
-
params.
|
532
|
+
expect(subject.where(role: "committer").association
|
533
|
+
.params).to include :role
|
447
534
|
end
|
448
535
|
end
|
449
536
|
|
@@ -457,20 +544,20 @@ describe Her::Model::Associations do
|
|
457
544
|
|
458
545
|
context "with #build" do
|
459
546
|
it "takes the parent primary key" do
|
460
|
-
@comment = Foo::User.new(:
|
461
|
-
@comment.body.
|
462
|
-
@comment.user_id.
|
547
|
+
@comment = Foo::User.new(id: 10).comments.build(body: "Hello!")
|
548
|
+
expect(@comment.body).to eq("Hello!")
|
549
|
+
expect(@comment.user_id).to eq(10)
|
463
550
|
end
|
464
551
|
end
|
465
552
|
|
466
553
|
context "with #create" do
|
467
554
|
before do
|
468
|
-
Her::API.setup :
|
555
|
+
Her::API.setup url: "https://api.example.com" do |builder|
|
469
556
|
builder.use Her::Middleware::FirstLevelParseJSON
|
470
557
|
builder.use Faraday::Request::UrlEncoded
|
471
558
|
builder.adapter :test do |stub|
|
472
|
-
stub.get("/users/10") {
|
473
|
-
stub.post("/comments") { |env| [200, {}, { :
|
559
|
+
stub.get("/users/10") { [200, {}, { id: 10 }.to_json] }
|
560
|
+
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
561
|
end
|
475
562
|
end
|
476
563
|
|
@@ -480,24 +567,24 @@ describe Her::Model::Associations do
|
|
480
567
|
|
481
568
|
it "takes the parent primary key and saves the resource" do
|
482
569
|
@user = Foo::User.find(10)
|
483
|
-
@comment = @user.comments.create(:
|
484
|
-
@comment.id.
|
485
|
-
@comment.body.
|
486
|
-
@comment.user_id.
|
487
|
-
@user.comments.
|
570
|
+
@comment = @user.comments.create(body: "Hello!")
|
571
|
+
expect(@comment.id).to eq(1)
|
572
|
+
expect(@comment.body).to eq("Hello!")
|
573
|
+
expect(@comment.user_id).to eq(10)
|
574
|
+
expect(@user.comments).to eq([@comment])
|
488
575
|
end
|
489
576
|
end
|
490
577
|
|
491
578
|
context "with #new" do
|
492
579
|
it "creates nested models from hash attibutes" do
|
493
|
-
user = Foo::User.new(:
|
494
|
-
user.comments.first.text.
|
580
|
+
user = Foo::User.new(name: "vic", comments: [{ text: "hello" }])
|
581
|
+
expect(user.comments.first.text).to eq("hello")
|
495
582
|
end
|
496
583
|
|
497
584
|
it "assigns nested models if given as already constructed objects" do
|
498
|
-
bye = Foo::Comment.new(:
|
499
|
-
user = Foo::User.new(:
|
500
|
-
user.comments.first.text.
|
585
|
+
bye = Foo::Comment.new(text: "goodbye")
|
586
|
+
user = Foo::User.new(name: "vic", comments: [bye])
|
587
|
+
expect(user.comments.first.text).to eq("goodbye")
|
501
588
|
end
|
502
589
|
end
|
503
590
|
end
|