her 0.8.1 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +1 -1
- data/.rubocop.yml +1291 -0
- data/.travis.yml +1 -0
- data/README.md +6 -0
- data/UPGRADE.md +1 -1
- data/her.gemspec +3 -5
- data/lib/her/model/associations/association_proxy.rb +1 -2
- data/lib/her/model/orm.rb +7 -3
- 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 +6 -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 +199 -158
- data/spec/model/attributes_spec.rb +98 -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 +183 -146
- data/spec/model/parse_spec.rb +77 -77
- data/spec/model/paths_spec.rb +109 -109
- data/spec/model/relation_spec.rb +68 -68
- data/spec/model/validations_spec.rb +6 -6
- data/spec/model_spec.rb +24 -11
- data/spec/spec_helper.rb +2 -3
- data/spec/support/macros/model_macros.rb +2 -2
- metadata +10 -37
@@ -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
|
@@ -114,14 +155,14 @@ describe Her::Model::Associations do
|
|
114
155
|
has_many :comments, class_name: "Foo::Comment"
|
115
156
|
has_one :role
|
116
157
|
belongs_to :organization
|
117
|
-
has_many :posts, :
|
158
|
+
has_many :posts, inverse_of: :admin
|
118
159
|
end
|
119
160
|
spawn_model "Foo::Comment" do
|
120
161
|
belongs_to :user
|
121
162
|
parse_root_in_json true
|
122
163
|
end
|
123
164
|
spawn_model "Foo::Post" do
|
124
|
-
belongs_to :admin, :
|
165
|
+
belongs_to :admin, class_name: "Foo::User"
|
125
166
|
end
|
126
167
|
|
127
168
|
spawn_model "Foo::Organization" do
|
@@ -136,136 +177,136 @@ describe Her::Model::Associations do
|
|
136
177
|
end
|
137
178
|
|
138
179
|
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(:
|
180
|
+
let(:user_with_included_data_after_save_existing) { Foo::User.save_existing(5, name: "Clancy Brown") }
|
181
|
+
let(:user_with_included_data_after_destroy) { Foo::User.new(id: 5).destroy }
|
182
|
+
let(:comment_without_included_parent_data) { Foo::Comment.new(id: 7, user_id: 1) }
|
142
183
|
|
143
184
|
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.
|
185
|
+
expect(@user_with_included_data.comments.first).to be_a(Foo::Comment)
|
186
|
+
expect(@user_with_included_data.comments.length).to eq(2)
|
187
|
+
expect(@user_with_included_data.comments.first.id).to eq(2)
|
188
|
+
expect(@user_with_included_data.comments.first.body).to eq("Tobias, you blow hard!")
|
148
189
|
end
|
149
190
|
|
150
191
|
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.
|
192
|
+
expect(@user_with_included_data.comments.first.user.object_id).to eq(@user_with_included_data.object_id)
|
152
193
|
end
|
153
194
|
|
154
195
|
it "does fetch the parent models data only once" do
|
155
|
-
comment_without_included_parent_data.user.object_id.
|
196
|
+
expect(comment_without_included_parent_data.user.object_id).to eq(comment_without_included_parent_data.user.object_id)
|
156
197
|
end
|
157
198
|
|
158
199
|
it "does fetch the parent models data that was cached if called with parameters" do
|
159
|
-
comment_without_included_parent_data.user.object_id.
|
200
|
+
expect(comment_without_included_parent_data.user.object_id).not_to eq(comment_without_included_parent_data.user.where(a: 2).object_id)
|
160
201
|
end
|
161
202
|
|
162
203
|
it "uses the given inverse_of key to set the parent model" do
|
163
|
-
@user_with_included_data.posts.first.admin.object_id.
|
204
|
+
expect(@user_with_included_data.posts.first.admin.object_id).to eq(@user_with_included_data.object_id)
|
164
205
|
end
|
165
206
|
|
166
207
|
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.
|
208
|
+
expect(@user_without_included_data.comments.first).to be_a(Foo::Comment)
|
209
|
+
expect(@user_without_included_data.comments.length).to eq(2)
|
210
|
+
expect(@user_without_included_data.comments.first.id).to eq(4)
|
211
|
+
expect(@user_without_included_data.comments.first.body).to eq("They're having a FIRESALE?")
|
171
212
|
end
|
172
213
|
|
173
214
|
it "fetches has_many data even if it was included, only if called with parameters" do
|
174
|
-
@user_with_included_data.comments.where(:
|
215
|
+
expect(@user_with_included_data.comments.where(foo_id: 1).length).to eq(1)
|
175
216
|
end
|
176
217
|
|
177
218
|
it "fetches data that was not included through has_many only once" do
|
178
|
-
@user_without_included_data.comments.first.object_id.
|
219
|
+
expect(@user_without_included_data.comments.first.object_id).to eq(@user_without_included_data.comments.first.object_id)
|
179
220
|
end
|
180
221
|
|
181
222
|
it "fetches data that was cached through has_many if called with parameters" do
|
182
|
-
@user_without_included_data.comments.first.object_id.
|
223
|
+
expect(@user_without_included_data.comments.first.object_id).not_to eq(@user_without_included_data.comments.where(foo_id: 1).first.object_id)
|
183
224
|
end
|
184
225
|
|
185
226
|
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.
|
227
|
+
expect(@user_with_included_data.role).to be_a(Foo::Role)
|
228
|
+
expect(@user_with_included_data.role.object_id).to eq(@user_with_included_data.role.object_id)
|
229
|
+
expect(@user_with_included_data.role.id).to eq(1)
|
230
|
+
expect(@user_with_included_data.role.body).to eq("Admin")
|
190
231
|
end
|
191
232
|
|
192
233
|
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.
|
234
|
+
expect(@user_without_included_data.role).to be_a(Foo::Role)
|
235
|
+
expect(@user_without_included_data.role.id).to eq(2)
|
236
|
+
expect(@user_without_included_data.role.body).to eq("User")
|
196
237
|
end
|
197
238
|
|
198
239
|
it "fetches has_one data even if it was included, only if called with parameters" do
|
199
|
-
@user_with_included_data.role.where(:
|
240
|
+
expect(@user_with_included_data.role.where(foo_id: 2).id).to eq(3)
|
200
241
|
end
|
201
242
|
|
202
243
|
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.
|
244
|
+
expect(@user_with_included_data.organization).to be_a(Foo::Organization)
|
245
|
+
expect(@user_with_included_data.organization.id).to eq(1)
|
246
|
+
expect(@user_with_included_data.organization.name).to eq("Bluth Company")
|
206
247
|
end
|
207
248
|
|
208
249
|
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.
|
250
|
+
expect(@user_without_included_data.organization).to be_a(Foo::Organization)
|
251
|
+
expect(@user_without_included_data.organization.id).to eq(2)
|
252
|
+
expect(@user_without_included_data.organization.name).to eq("Bluth Company")
|
212
253
|
end
|
213
254
|
|
214
255
|
it "returns nil if the foreign key is nil" do
|
215
|
-
@user_without_organization_and_not_persisted.organization.
|
256
|
+
expect(@user_without_organization_and_not_persisted.organization).to be_nil
|
216
257
|
end
|
217
258
|
|
218
259
|
it "fetches belongs_to data even if it was included, only if called with parameters" do
|
219
|
-
@user_with_included_data.organization.where(:
|
260
|
+
expect(@user_with_included_data.organization.where(foo_id: 1).name).to eq("Bluth Company Foo")
|
220
261
|
end
|
221
262
|
|
222
263
|
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).
|
264
|
+
expect(@user_without_included_data.has_association?(:unknown_association)).to be false
|
265
|
+
expect(@user_without_included_data.has_association?(:organization)).to be true
|
225
266
|
end
|
226
267
|
|
227
268
|
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.
|
269
|
+
expect(@user_without_included_data.get_association(:unknown_association)).to be_nil
|
270
|
+
expect(@user_without_included_data.get_association(:organization).name).to eq("Bluth Company")
|
230
271
|
end
|
231
272
|
|
232
273
|
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.
|
274
|
+
expect(@user_without_included_data.organization.where(admin: true).name).to eq("Bluth Company (admin)")
|
275
|
+
expect(@user_without_included_data.organization.name).to eq("Bluth Company")
|
235
276
|
end
|
236
277
|
|
237
278
|
it "fetches data with the specified id when calling find" do
|
238
279
|
comment = @user_without_included_data.comments.find(5)
|
239
|
-
comment.
|
240
|
-
comment.id.
|
280
|
+
expect(comment).to be_a(Foo::Comment)
|
281
|
+
expect(comment.id).to eq(5)
|
241
282
|
end
|
242
283
|
|
243
284
|
it "'s associations responds to #empty?" do
|
244
|
-
@user_without_included_data.organization.respond_to?(:empty?).
|
245
|
-
@user_without_included_data.organization.
|
285
|
+
expect(@user_without_included_data.organization.respond_to?(:empty?)).to be_truthy
|
286
|
+
expect(@user_without_included_data.organization).not_to be_empty
|
246
287
|
end
|
247
288
|
|
248
|
-
it
|
289
|
+
it "includes has_many relationships in params by default" do
|
249
290
|
params = @user_with_included_data.to_params
|
250
|
-
params[:comments].
|
251
|
-
params[:comments].length.
|
291
|
+
expect(params[:comments]).to be_kind_of(Array)
|
292
|
+
expect(params[:comments].length).to eq(2)
|
252
293
|
end
|
253
294
|
|
254
295
|
[:create, :save_existing, :destroy].each do |type|
|
255
296
|
context "after #{type}" do
|
256
|
-
let(:subject) {
|
297
|
+
let(:subject) { send("user_with_included_data_after_#{type}") }
|
257
298
|
|
258
299
|
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.
|
300
|
+
expect(subject.comments.first).to be_a(Foo::Comment)
|
301
|
+
expect(subject.comments.length).to eq(1)
|
302
|
+
expect(subject.comments.first.id).to eq(99)
|
303
|
+
expect(subject.comments.first.body).to eq("Rodríguez, nasibisibusi?")
|
263
304
|
end
|
264
305
|
|
265
306
|
it "maps an array of included data through has_one" do
|
266
|
-
subject.role.
|
267
|
-
subject.role.id.
|
268
|
-
subject.role.body.
|
307
|
+
expect(subject.role).to be_a(Foo::Role)
|
308
|
+
expect(subject.role.id).to eq(1)
|
309
|
+
expect(subject.role.body).to eq("Admin")
|
269
310
|
end
|
270
311
|
end
|
271
312
|
end
|
@@ -273,29 +314,29 @@ describe Her::Model::Associations do
|
|
273
314
|
|
274
315
|
context "handling associations with details in active_model_serializers format" do
|
275
316
|
before do
|
276
|
-
Her::API.setup :
|
317
|
+
Her::API.setup url: "https://api.example.com" do |builder|
|
277
318
|
builder.use Her::Middleware::FirstLevelParseJSON
|
278
319
|
builder.use Faraday::Request::UrlEncoded
|
279
320
|
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") {
|
321
|
+
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] }
|
322
|
+
stub.get("/users/2") { [200, {}, { user: { id: 2, name: "Lindsay Fünke", organization_id: 1 } }.to_json] }
|
323
|
+
stub.get("/users/1/comments") { [200, {}, { comments: [{ id: 4, body: "They're having a FIRESALE?" }] }.to_json] }
|
324
|
+
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] }
|
325
|
+
stub.get("/users/2/comments/5") { [200, {}, { comment: { id: 5, body: "Is this the tiny town from Footloose?" } }.to_json] }
|
326
|
+
stub.get("/organizations/1") { [200, {}, { organization: { id: 1, name: "Bluth Company Foo" } }.to_json] }
|
286
327
|
end
|
287
328
|
end
|
288
329
|
spawn_model "Foo::User" do
|
289
|
-
parse_root_in_json true, :
|
330
|
+
parse_root_in_json true, format: :active_model_serializers
|
290
331
|
has_many :comments, class_name: "Foo::Comment"
|
291
332
|
belongs_to :organization
|
292
333
|
end
|
293
334
|
spawn_model "Foo::Comment" do
|
294
335
|
belongs_to :user
|
295
|
-
parse_root_in_json true, :
|
336
|
+
parse_root_in_json true, format: :active_model_serializers
|
296
337
|
end
|
297
338
|
spawn_model "Foo::Organization" do
|
298
|
-
parse_root_in_json true, :
|
339
|
+
parse_root_in_json true, format: :active_model_serializers
|
299
340
|
end
|
300
341
|
|
301
342
|
@user_with_included_data = Foo::User.find(1)
|
@@ -303,72 +344,72 @@ describe Her::Model::Associations do
|
|
303
344
|
end
|
304
345
|
|
305
346
|
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.
|
347
|
+
expect(@user_with_included_data.comments.first).to be_a(Foo::Comment)
|
348
|
+
expect(@user_with_included_data.comments.length).to eq(2)
|
349
|
+
expect(@user_with_included_data.comments.first.id).to eq(2)
|
350
|
+
expect(@user_with_included_data.comments.first.body).to eq("Tobias, you blow hard!")
|
310
351
|
end
|
311
352
|
|
312
353
|
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.
|
354
|
+
expect(@user_with_included_data.comments.first.user.object_id).to eq(@user_with_included_data.object_id)
|
314
355
|
end
|
315
356
|
|
316
357
|
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.
|
358
|
+
expect(@user_without_included_data.comments.first).to be_a(Foo::Comment)
|
359
|
+
expect(@user_without_included_data.comments.length).to eq(2)
|
360
|
+
expect(@user_without_included_data.comments.first.id).to eq(4)
|
361
|
+
expect(@user_without_included_data.comments.first.body).to eq("They're having a FIRESALE?")
|
321
362
|
end
|
322
363
|
|
323
364
|
it "fetches has_many data even if it was included, only if called with parameters" do
|
324
|
-
@user_with_included_data.comments.where(:
|
365
|
+
expect(@user_with_included_data.comments.where(foo_id: 1).length).to eq(1)
|
325
366
|
end
|
326
367
|
|
327
368
|
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.
|
369
|
+
expect(@user_with_included_data.organization).to be_a(Foo::Organization)
|
370
|
+
expect(@user_with_included_data.organization.id).to eq(1)
|
371
|
+
expect(@user_with_included_data.organization.name).to eq("Bluth Company")
|
331
372
|
end
|
332
373
|
|
333
374
|
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.
|
375
|
+
expect(@user_without_included_data.organization).to be_a(Foo::Organization)
|
376
|
+
expect(@user_without_included_data.organization.id).to eq(1)
|
377
|
+
expect(@user_without_included_data.organization.name).to eq("Bluth Company Foo")
|
337
378
|
end
|
338
379
|
|
339
380
|
it "fetches belongs_to data even if it was included, only if called with parameters" do
|
340
|
-
@user_with_included_data.organization.where(:
|
381
|
+
expect(@user_with_included_data.organization.where(foo_id: 1).name).to eq("Bluth Company Foo")
|
341
382
|
end
|
342
383
|
|
343
384
|
it "fetches data with the specified id when calling find" do
|
344
385
|
comment = @user_without_included_data.comments.find(5)
|
345
|
-
comment.
|
346
|
-
comment.id.
|
386
|
+
expect(comment).to be_a(Foo::Comment)
|
387
|
+
expect(comment.id).to eq(5)
|
347
388
|
end
|
348
389
|
|
349
|
-
it
|
390
|
+
it "includes has_many relationships in params by default" do
|
350
391
|
params = @user_with_included_data.to_params
|
351
|
-
params[:comments].
|
352
|
-
params[:comments].length.
|
392
|
+
expect(params[:comments]).to be_kind_of(Array)
|
393
|
+
expect(params[:comments].length).to eq(2)
|
353
394
|
end
|
354
395
|
end
|
355
396
|
|
356
397
|
context "handling associations with details" do
|
357
398
|
before do
|
358
|
-
Her::API.setup :
|
399
|
+
Her::API.setup url: "https://api.example.com" do |builder|
|
359
400
|
builder.use Her::Middleware::FirstLevelParseJSON
|
360
401
|
builder.use Faraday::Request::UrlEncoded
|
361
402
|
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") {
|
403
|
+
stub.get("/users/1") { [200, {}, { id: 1, name: "Tobias Fünke", organization: { id: 1, name: "Bluth Company Inc." }, organization_id: 1 }.to_json] }
|
404
|
+
stub.get("/users/4") { [200, {}, { id: 1, name: "Tobias Fünke", organization: { id: 1, name: "Bluth Company Inc." } }.to_json] }
|
405
|
+
stub.get("/users/2") { [200, {}, { id: 2, name: "Lindsay Fünke", organization_id: 1 }.to_json] }
|
406
|
+
stub.get("/users/3") { [200, {}, { id: 2, name: "Lindsay Fünke", company: nil }.to_json] }
|
407
|
+
stub.get("/companies/1") { [200, {}, { id: 1, name: "Bluth Company" }.to_json] }
|
367
408
|
end
|
368
409
|
end
|
369
410
|
|
370
411
|
spawn_model "Foo::User" do
|
371
|
-
belongs_to :company, :
|
412
|
+
belongs_to :company, path: "/organizations/:id", foreign_key: :organization_id, data_key: :organization
|
372
413
|
end
|
373
414
|
|
374
415
|
spawn_model "Foo::Company"
|
@@ -380,23 +421,23 @@ describe Her::Model::Associations do
|
|
380
421
|
end
|
381
422
|
|
382
423
|
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.
|
424
|
+
expect(@user_with_included_data.company).to be_a(Foo::Company)
|
425
|
+
expect(@user_with_included_data.company.id).to eq(1)
|
426
|
+
expect(@user_with_included_data.company.name).to eq("Bluth Company Inc.")
|
386
427
|
end
|
387
428
|
|
388
429
|
it "does not map included data if it’s nil" do
|
389
|
-
@user_with_included_nil_data.company.
|
430
|
+
expect(@user_with_included_nil_data.company).to be_nil
|
390
431
|
end
|
391
432
|
|
392
433
|
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.
|
434
|
+
expect(@user_without_included_data.company).to be_a(Foo::Company)
|
435
|
+
expect(@user_without_included_data.company.id).to eq(1)
|
436
|
+
expect(@user_without_included_data.company.name).to eq("Bluth Company")
|
396
437
|
end
|
397
438
|
|
398
439
|
it "does not require foreugn key to have nested object" do
|
399
|
-
@user_with_included_data_but_no_fk.company.name.
|
440
|
+
expect(@user_with_included_data_but_no_fk.company.name).to eq("Bluth Company Inc.")
|
400
441
|
end
|
401
442
|
end
|
402
443
|
|
@@ -420,30 +461,30 @@ describe Her::Model::Associations do
|
|
420
461
|
subject { user_with_role.role }
|
421
462
|
|
422
463
|
it "doesnt mask the object's basic methods" do
|
423
|
-
subject.class.
|
464
|
+
expect(subject.class).to eq(Foo::Role)
|
424
465
|
end
|
425
466
|
|
426
467
|
it "doesnt mask core methods like extend" do
|
427
468
|
committer = Module.new
|
428
|
-
subject.extend
|
429
|
-
associated_value.
|
469
|
+
subject.extend committer
|
470
|
+
expect(associated_value).to be_kind_of committer
|
430
471
|
end
|
431
472
|
|
432
473
|
it "can return the association object" do
|
433
|
-
subject.association.
|
474
|
+
expect(subject.association).to be_kind_of Her::Model::Associations::Association
|
434
475
|
end
|
435
476
|
|
436
477
|
it "still can call fetch via the association" do
|
437
|
-
subject.association.fetch.
|
478
|
+
expect(subject.association.fetch).to eq associated_value
|
438
479
|
end
|
439
480
|
|
440
481
|
it "calls missing methods on associated value" do
|
441
|
-
subject.present
|
482
|
+
expect(subject.present?).to eq("of_course")
|
442
483
|
end
|
443
484
|
|
444
485
|
it "can use association methods like where" do
|
445
|
-
subject.where(role:
|
446
|
-
params.
|
486
|
+
expect(subject.where(role: "committer").association
|
487
|
+
.params).to include :role
|
447
488
|
end
|
448
489
|
end
|
449
490
|
|
@@ -457,20 +498,20 @@ describe Her::Model::Associations do
|
|
457
498
|
|
458
499
|
context "with #build" do
|
459
500
|
it "takes the parent primary key" do
|
460
|
-
@comment = Foo::User.new(:
|
461
|
-
@comment.body.
|
462
|
-
@comment.user_id.
|
501
|
+
@comment = Foo::User.new(id: 10).comments.build(body: "Hello!")
|
502
|
+
expect(@comment.body).to eq("Hello!")
|
503
|
+
expect(@comment.user_id).to eq(10)
|
463
504
|
end
|
464
505
|
end
|
465
506
|
|
466
507
|
context "with #create" do
|
467
508
|
before do
|
468
|
-
Her::API.setup :
|
509
|
+
Her::API.setup url: "https://api.example.com" do |builder|
|
469
510
|
builder.use Her::Middleware::FirstLevelParseJSON
|
470
511
|
builder.use Faraday::Request::UrlEncoded
|
471
512
|
builder.adapter :test do |stub|
|
472
|
-
stub.get("/users/10") {
|
473
|
-
stub.post("/comments") { |env| [200, {}, { :
|
513
|
+
stub.get("/users/10") { [200, {}, { id: 10 }.to_json] }
|
514
|
+
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
515
|
end
|
475
516
|
end
|
476
517
|
|
@@ -480,24 +521,24 @@ describe Her::Model::Associations do
|
|
480
521
|
|
481
522
|
it "takes the parent primary key and saves the resource" do
|
482
523
|
@user = Foo::User.find(10)
|
483
|
-
@comment = @user.comments.create(:
|
484
|
-
@comment.id.
|
485
|
-
@comment.body.
|
486
|
-
@comment.user_id.
|
487
|
-
@user.comments.
|
524
|
+
@comment = @user.comments.create(body: "Hello!")
|
525
|
+
expect(@comment.id).to eq(1)
|
526
|
+
expect(@comment.body).to eq("Hello!")
|
527
|
+
expect(@comment.user_id).to eq(10)
|
528
|
+
expect(@user.comments).to eq([@comment])
|
488
529
|
end
|
489
530
|
end
|
490
531
|
|
491
532
|
context "with #new" do
|
492
533
|
it "creates nested models from hash attibutes" do
|
493
|
-
user = Foo::User.new(:
|
494
|
-
user.comments.first.text.
|
534
|
+
user = Foo::User.new(name: "vic", comments: [{ text: "hello" }])
|
535
|
+
expect(user.comments.first.text).to eq("hello")
|
495
536
|
end
|
496
537
|
|
497
538
|
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.
|
539
|
+
bye = Foo::Comment.new(text: "goodbye")
|
540
|
+
user = Foo::User.new(name: "vic", comments: [bye])
|
541
|
+
expect(user.comments.first.text).to eq("goodbye")
|
501
542
|
end
|
502
543
|
end
|
503
544
|
end
|