sequel_mapper 0.0.1 → 0.0.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/.ruby-version +1 -1
- data/CODE_OF_CONDUCT.md +28 -0
- data/Gemfile.lock +32 -2
- data/MissingFeatures.md +64 -0
- data/README.md +141 -72
- data/Rakefile +29 -0
- data/TODO.md +16 -11
- data/features/env.rb +57 -0
- data/features/example.feature +121 -0
- data/features/step_definitions/example_steps.rb +46 -0
- data/lib/sequel_mapper.rb +6 -2
- data/lib/sequel_mapper/abstract_record.rb +53 -0
- data/lib/sequel_mapper/association_loaders.rb +52 -0
- data/lib/sequel_mapper/collection_mutability_proxy.rb +77 -0
- data/lib/sequel_mapper/configurations/conventional_association_configuration.rb +187 -0
- data/lib/sequel_mapper/configurations/conventional_configuration.rb +269 -0
- data/lib/sequel_mapper/dataset.rb +37 -0
- data/lib/sequel_mapper/deleted_record.rb +16 -0
- data/lib/sequel_mapper/dirty_map.rb +31 -0
- data/lib/sequel_mapper/graph_loader.rb +48 -0
- data/lib/sequel_mapper/graph_serializer.rb +107 -0
- data/lib/sequel_mapper/identity_map.rb +22 -0
- data/lib/sequel_mapper/lazy_object_proxy.rb +51 -0
- data/lib/sequel_mapper/many_to_many_association.rb +181 -0
- data/lib/sequel_mapper/many_to_one_association.rb +60 -0
- data/lib/sequel_mapper/mapper_facade.rb +180 -0
- data/lib/sequel_mapper/one_to_many_association.rb +51 -0
- data/lib/sequel_mapper/public_conveniencies.rb +27 -0
- data/lib/sequel_mapper/query_order.rb +32 -0
- data/lib/sequel_mapper/queryable_lazy_dataset_loader.rb +70 -0
- data/lib/sequel_mapper/relation_mapping.rb +35 -0
- data/lib/sequel_mapper/serializer.rb +18 -0
- data/lib/sequel_mapper/short_inspection_string.rb +18 -0
- data/lib/sequel_mapper/subset_queries_proxy.rb +11 -0
- data/lib/sequel_mapper/upserted_record.rb +15 -0
- data/lib/sequel_mapper/version.rb +1 -1
- data/sequel_mapper.gemspec +3 -0
- data/spec/config_override_spec.rb +167 -0
- data/spec/custom_serializers_spec.rb +77 -0
- data/spec/deletion_spec.rb +104 -0
- data/spec/graph_persistence_spec.rb +83 -88
- data/spec/graph_traversal_spec.rb +32 -31
- data/spec/new_graph_persistence_spec.rb +69 -0
- data/spec/object_identity_spec.rb +70 -0
- data/spec/ordered_association_spec.rb +46 -16
- data/spec/persistence_efficiency_spec.rb +186 -0
- data/spec/predefined_queries_spec.rb +73 -0
- data/spec/proxying_spec.rb +25 -19
- data/spec/querying_spec.rb +24 -27
- data/spec/readme_examples_spec.rb +35 -0
- data/spec/sequel_mapper/abstract_record_spec.rb +179 -0
- data/spec/sequel_mapper/{association_proxy_spec.rb → collection_mutability_proxy_spec.rb} +6 -6
- data/spec/sequel_mapper/deleted_record_spec.rb +59 -0
- data/spec/sequel_mapper/lazy_object_proxy_spec.rb +140 -0
- data/spec/sequel_mapper/public_conveniencies_spec.rb +49 -0
- data/spec/sequel_mapper/queryable_lazy_dataset_loader_spec.rb +103 -0
- data/spec/sequel_mapper/upserted_record_spec.rb +59 -0
- data/spec/spec_helper.rb +7 -10
- data/spec/support/blog_schema.rb +29 -0
- data/spec/support/have_persisted_matcher.rb +19 -0
- data/spec/support/mapper_setup.rb +234 -0
- data/spec/support/mock_sequel.rb +0 -1
- data/spec/support/object_graph_setup.rb +106 -0
- data/spec/support/seed_data_setup.rb +122 -0
- data/spec/support/sequel_persistence_setup.rb +19 -0
- data/spec/support/sequel_test_support.rb +159 -0
- metadata +121 -15
- data/lib/sequel_mapper/association_proxy.rb +0 -54
- data/lib/sequel_mapper/belongs_to_association_proxy.rb +0 -27
- data/lib/sequel_mapper/graph.rb +0 -174
- data/lib/sequel_mapper/queryable_association_proxy.rb +0 -23
- data/spec/sequel_mapper/belongs_to_association_proxy_spec.rb +0 -65
- data/spec/support/graph_fixture.rb +0 -331
- data/spec/support/query_counter.rb +0 -29
@@ -1,45 +1,46 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
+
require "support/mapper_setup"
|
4
|
+
require "support/sequel_persistence_setup"
|
5
|
+
require "support/seed_data_setup"
|
3
6
|
require "sequel_mapper"
|
4
|
-
require "support/graph_fixture"
|
5
7
|
|
6
8
|
RSpec.describe "Graph traversal" do
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
datastore: datastore,
|
14
|
-
relation_mappings: relation_mappings,
|
15
|
-
)
|
16
|
-
}
|
9
|
+
include_context "mapper setup"
|
10
|
+
include_context "sequel persistence setup"
|
11
|
+
include_context "seed data setup"
|
12
|
+
|
13
|
+
describe "associations" do
|
14
|
+
subject(:mapper) { user_mapper }
|
17
15
|
|
18
16
|
let(:user_query) {
|
19
|
-
|
17
|
+
mapper.where(id: "users/1")
|
20
18
|
}
|
21
19
|
|
20
|
+
let(:user) { user_query.first }
|
21
|
+
|
22
22
|
it "finds data via the storage adapter" do
|
23
|
-
expect(user_query.count).to
|
23
|
+
expect(user_query.count).to eq(1)
|
24
24
|
end
|
25
25
|
|
26
26
|
it "maps the raw data from the store into domain objects" do
|
27
|
-
expect(user_query.first.id).to eq("
|
28
|
-
expect(user_query.first.first_name).to eq("
|
27
|
+
expect(user_query.first.id).to eq("users/1")
|
28
|
+
expect(user_query.first.first_name).to eq("Hansel")
|
29
29
|
end
|
30
30
|
|
31
31
|
it "handles has_many associations" do
|
32
|
-
|
33
|
-
|
32
|
+
post = user.posts.first
|
33
|
+
|
34
|
+
expect(post.subject).to eq("Biscuits")
|
34
35
|
end
|
35
36
|
|
36
37
|
it "handles nested has_many associations" do
|
37
38
|
expect(
|
38
|
-
|
39
|
+
user
|
39
40
|
.posts.first
|
40
41
|
.comments.first
|
41
42
|
.body
|
42
|
-
).to eq("
|
43
|
+
).to eq("oh noes")
|
43
44
|
end
|
44
45
|
|
45
46
|
describe "lazy loading" do
|
@@ -53,33 +54,33 @@ RSpec.describe "Graph traversal" do
|
|
53
54
|
end
|
54
55
|
|
55
56
|
it "maps belongs to assocations" do
|
56
|
-
|
57
|
-
|
57
|
+
post = user.posts.first
|
58
|
+
comment = post.comments.first
|
59
|
+
|
60
|
+
expect(comment.commenter.id).to eq("users/1")
|
58
61
|
end
|
59
62
|
|
60
63
|
describe "identity map" do
|
61
64
|
it "always returns (a proxy of) the same object for a given id" do
|
62
|
-
|
63
|
-
|
65
|
+
post = user.posts.first
|
66
|
+
comment = post.comments.first
|
67
|
+
|
68
|
+
expect(comment.commenter.__getobj__)
|
69
|
+
.to be(user)
|
64
70
|
end
|
65
71
|
end
|
66
72
|
|
67
73
|
it "maps deeply nested belongs to assocations" do
|
68
74
|
expect(user_query.first.posts.first.comments.first.commenter.id)
|
69
|
-
.to eq("
|
75
|
+
.to eq("users/1")
|
70
76
|
end
|
71
77
|
|
72
78
|
it "maps has many to many associations as has many through" do
|
73
79
|
expect(user_query.first.posts.first.categories.map(&:id))
|
74
|
-
.to match_array(["
|
80
|
+
.to match_array(["categories/1", "categories/2"])
|
75
81
|
|
76
82
|
expect(user_query.first.posts.first.categories.to_a.last.posts.map(&:id))
|
77
|
-
.to match_array(["
|
78
|
-
end
|
79
|
-
|
80
|
-
xit "combines has many through associations" do
|
81
|
-
expect(user_query.first.categories_posted_in.map(&:id))
|
82
|
-
.to match_array(["category/1", "category/2"])
|
83
|
+
.to match_array(["posts/1", "posts/2"])
|
83
84
|
end
|
84
85
|
end
|
85
86
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "support/mapper_setup"
|
3
|
+
require "support/sequel_persistence_setup"
|
4
|
+
require "support/have_persisted_matcher"
|
5
|
+
|
6
|
+
RSpec.describe "Persist a new graph in empty datastore" do
|
7
|
+
include_context "mapper setup"
|
8
|
+
include_context "sequel persistence setup"
|
9
|
+
|
10
|
+
context "given a graph of new objects" do
|
11
|
+
it "persists the root node" do
|
12
|
+
user_mapper.save(hansel)
|
13
|
+
|
14
|
+
expect(datastore).to have_persisted(:users, {
|
15
|
+
id: hansel.id,
|
16
|
+
first_name: hansel.first_name,
|
17
|
+
last_name: hansel.last_name,
|
18
|
+
email: hansel.email,
|
19
|
+
})
|
20
|
+
end
|
21
|
+
|
22
|
+
it "persists one to many related nodes 1 level deep" do
|
23
|
+
user_mapper.save(hansel)
|
24
|
+
|
25
|
+
expect(datastore).to have_persisted(:posts, hash_including(
|
26
|
+
id: "posts/1",
|
27
|
+
subject: "Biscuits",
|
28
|
+
body: "I like them",
|
29
|
+
author_id: "users/1",
|
30
|
+
))
|
31
|
+
|
32
|
+
expect(datastore).to have_persisted(:posts, hash_including(
|
33
|
+
id: "posts/2",
|
34
|
+
subject: "Sleeping",
|
35
|
+
body: "I do it three times purrr day",
|
36
|
+
author_id: "users/1",
|
37
|
+
))
|
38
|
+
end
|
39
|
+
|
40
|
+
it "persists one to many related nodes 2 levels deep" do
|
41
|
+
user_mapper.save(hansel)
|
42
|
+
|
43
|
+
expect(datastore).to have_persisted(:comments, {
|
44
|
+
id: "comments/1",
|
45
|
+
body: "oh noes",
|
46
|
+
post_id: "posts/1",
|
47
|
+
commenter_id: "users/1",
|
48
|
+
})
|
49
|
+
end
|
50
|
+
|
51
|
+
it "persists many to many related nodes" do
|
52
|
+
user_mapper.save(hansel)
|
53
|
+
|
54
|
+
expect(datastore).to have_persisted(:categories, {
|
55
|
+
id: "categories/1",
|
56
|
+
name: "Cat biscuits",
|
57
|
+
})
|
58
|
+
end
|
59
|
+
|
60
|
+
it "persists a 'join table' to faciliate many to many" do
|
61
|
+
user_mapper.save(hansel)
|
62
|
+
|
63
|
+
expect(datastore).to have_persisted(:categories_to_posts, {
|
64
|
+
category_id: "categories/1",
|
65
|
+
post_id: "posts/1",
|
66
|
+
})
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "support/mapper_setup"
|
4
|
+
require "support/sequel_persistence_setup"
|
5
|
+
require "support/seed_data_setup"
|
6
|
+
require "sequel_mapper"
|
7
|
+
|
8
|
+
RSpec.describe "Object identity" do
|
9
|
+
include_context "mapper setup"
|
10
|
+
include_context "sequel persistence setup"
|
11
|
+
include_context "seed data setup"
|
12
|
+
|
13
|
+
subject(:mapper) { mappers.fetch(:users) }
|
14
|
+
|
15
|
+
let(:user) { mapper.where(id: "users/1").first }
|
16
|
+
let(:post) { user.posts.first }
|
17
|
+
|
18
|
+
context "when using arbitrary where query" do
|
19
|
+
it "returns the same object for a row's primary key" do
|
20
|
+
expect(
|
21
|
+
user.posts.where(id: post.id).first
|
22
|
+
).to be(post)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when traversing deep into the graph" do
|
27
|
+
context "via has many through" do
|
28
|
+
it "returns the same object for a row's primary key" do
|
29
|
+
expect(
|
30
|
+
user.posts.first.categories.first.posts
|
31
|
+
.find { |cat_post| cat_post.id == post.id }
|
32
|
+
).to be(post)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "via a belongs to" do
|
37
|
+
it "returns the same object for a row's primary once loaded" do
|
38
|
+
# TODO: Add another method to avoid using #__getobj__
|
39
|
+
expect(
|
40
|
+
user.posts.first.comments
|
41
|
+
.find { |comment| comment.commenter.id == user.id }
|
42
|
+
.commenter
|
43
|
+
.__getobj__
|
44
|
+
).to be(user)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when eager loading" do
|
49
|
+
let(:user_query) { mapper.where(id: "users/1") }
|
50
|
+
|
51
|
+
let(:eager_category) {
|
52
|
+
user_query
|
53
|
+
.eager_load(:posts => { :categories => { :posts => [] }})
|
54
|
+
.first
|
55
|
+
.posts
|
56
|
+
.first
|
57
|
+
.categories
|
58
|
+
.first
|
59
|
+
}
|
60
|
+
|
61
|
+
it "returns the same object for a row's primary once loaded" do
|
62
|
+
expect(
|
63
|
+
eager_category
|
64
|
+
.posts
|
65
|
+
.find { |cat_post| cat_post.id == post.id }
|
66
|
+
).to be(post)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -1,29 +1,59 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
+
require "support/mapper_setup"
|
4
|
+
require "support/sequel_persistence_setup"
|
5
|
+
require "support/seed_data_setup"
|
3
6
|
require "sequel_mapper"
|
4
|
-
require "
|
7
|
+
require "sequel_mapper/configurations/conventional_configuration"
|
5
8
|
|
6
9
|
RSpec.describe "Ordered associations" do
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
include_context "mapper setup"
|
11
|
+
include_context "sequel persistence setup"
|
12
|
+
include_context "seed data setup"
|
13
|
+
|
14
|
+
subject(:user_mapper) {
|
15
|
+
SequelMapper.mapper(
|
16
|
+
config: mapper_config,
|
17
|
+
name: :users,
|
18
|
+
datastore: datastore,
|
19
|
+
)
|
20
|
+
}
|
21
|
+
|
17
22
|
|
18
|
-
|
19
|
-
|
23
|
+
context "one to many association ordered by `created_at DESC`" do
|
24
|
+
let(:posts) { user_mapper.first.posts }
|
25
|
+
|
26
|
+
let(:mapper_config) {
|
27
|
+
SequelMapper::Configurations::ConventionalConfiguration.new(datastore)
|
28
|
+
.setup_mapping(:users) { |users|
|
29
|
+
users.has_many(:posts, foreign_key: :author_id, order_fields: [:created_at], order_direction: "DESC")
|
30
|
+
}
|
20
31
|
}
|
21
32
|
|
22
33
|
it "enumerates the objects in order specified in the config" do
|
23
|
-
|
34
|
+
expect(posts.map(&:id)).to eq(
|
35
|
+
posts.to_a.sort_by(&:created_at).reverse.map(&:id)
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "many to many associatin ordered by reverse alphabetical name" do
|
41
|
+
let(:mapper_config) {
|
42
|
+
SequelMapper::Configurations::ConventionalConfiguration.new(datastore)
|
43
|
+
.setup_mapping(:users) { |users|
|
44
|
+
users.has_many(:posts, foreign_key: :author_id)
|
45
|
+
}
|
46
|
+
.setup_mapping(:posts) { |posts|
|
47
|
+
posts.has_many_through(:categories, order_fields: [:name], order_direction: "DESC")
|
48
|
+
}
|
49
|
+
}
|
24
50
|
|
25
|
-
|
26
|
-
|
51
|
+
let(:categories) { user_mapper.first.posts.first.categories }
|
52
|
+
|
53
|
+
it "enumerates the objects in order specified in the config" do
|
54
|
+
expect(categories.map(&:id)).to eq(
|
55
|
+
categories.to_a.sort_by(&:name).reverse.map(&:id)
|
56
|
+
)
|
27
57
|
end
|
28
58
|
end
|
29
59
|
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "support/mapper_setup"
|
4
|
+
require "support/sequel_persistence_setup"
|
5
|
+
require "support/seed_data_setup"
|
6
|
+
require "sequel_mapper"
|
7
|
+
|
8
|
+
RSpec.describe "Graph persistence efficiency" do
|
9
|
+
include_context "mapper setup"
|
10
|
+
include_context "sequel persistence setup"
|
11
|
+
include_context "seed data setup"
|
12
|
+
|
13
|
+
let(:mapper) { user_mapper }
|
14
|
+
let(:user_query) { mapper.where(id: "users/1") }
|
15
|
+
let(:user) { user_query.first }
|
16
|
+
|
17
|
+
context "when modifying the root node" do
|
18
|
+
let(:modified_email) { "modified@example.com" }
|
19
|
+
|
20
|
+
context "and only the root node" do
|
21
|
+
before do
|
22
|
+
user.email = modified_email
|
23
|
+
end
|
24
|
+
|
25
|
+
it "performs 1 update" do
|
26
|
+
expect {
|
27
|
+
mapper.save(user)
|
28
|
+
}.to change { query_counter.update_count }.by(1)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "when modifying a directly associated (has many) object" do
|
34
|
+
let(:modified_post_subject) { "modified post subject" }
|
35
|
+
|
36
|
+
before do
|
37
|
+
user.posts.first.subject = modified_post_subject
|
38
|
+
end
|
39
|
+
|
40
|
+
it "performs 1 update" do
|
41
|
+
expect {
|
42
|
+
mapper.save(user)
|
43
|
+
}.to change { query_counter.update_count }.by(1)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "performs 0 deletes" do
|
47
|
+
expect {
|
48
|
+
mapper.save(user)
|
49
|
+
}.to change { query_counter.delete_count }.by(0)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "performs 0 additional reads" do
|
53
|
+
expect {
|
54
|
+
mapper.save(user)
|
55
|
+
}.to change { query_counter.read_count }.by(0)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "when loading many nodes of the graph" do
|
60
|
+
let(:post) {
|
61
|
+
user.posts.first
|
62
|
+
}
|
63
|
+
|
64
|
+
context "and modifying an intermediate node" do
|
65
|
+
before do
|
66
|
+
post.subject = "MODIFIED"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "performs 1 write" do
|
70
|
+
expect {
|
71
|
+
mapper.save(user)
|
72
|
+
}.to change { query_counter.update_count }.by(1)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "and modifying a leaf node" do
|
77
|
+
let(:comment) { post.comments.first }
|
78
|
+
|
79
|
+
before do
|
80
|
+
comment.body = "UPDATED!"
|
81
|
+
end
|
82
|
+
|
83
|
+
it "performs 1 update" do
|
84
|
+
expect {
|
85
|
+
mapper.save(user)
|
86
|
+
}.to change { query_counter.update_count }.by(1)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "and modifying both a leaf and intermediate node" do
|
91
|
+
let(:comment) { post.comments.first }
|
92
|
+
|
93
|
+
before do
|
94
|
+
comment.body = "UPDATED!"
|
95
|
+
post.subject = "MODIFIED"
|
96
|
+
end
|
97
|
+
|
98
|
+
it "performs 2 updates" do
|
99
|
+
expect {
|
100
|
+
mapper.save(user)
|
101
|
+
}.to change { query_counter.update_count }.by(2)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context "when modifying a many to many association" do
|
107
|
+
let(:post) { user.posts.first }
|
108
|
+
let(:category) { post.categories.first }
|
109
|
+
|
110
|
+
before do
|
111
|
+
category.name = "UPDATED"
|
112
|
+
end
|
113
|
+
|
114
|
+
it "performs 1 write" do
|
115
|
+
expect {
|
116
|
+
mapper.save(user)
|
117
|
+
}.to change { query_counter.update_count }.by(1)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context "eager loading" do
|
122
|
+
context "on root node" do
|
123
|
+
it "performs 1 read per table rather than n + 1" do
|
124
|
+
expect {
|
125
|
+
mapper.eager_load(:posts => []).all.map { |user|
|
126
|
+
[user.id, user.posts.map(&:id)]
|
127
|
+
}
|
128
|
+
}.to change { query_counter.read_count }.by(2)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# mapper.eager_load([:posts, [:comments, [:author]]])
|
133
|
+
|
134
|
+
context "with nested has many" do
|
135
|
+
it "performs 1 read per table rather than n + 1" do
|
136
|
+
expect {
|
137
|
+
user_query
|
138
|
+
.eager_load(:posts => { :comments => [] })
|
139
|
+
.first
|
140
|
+
.posts
|
141
|
+
.map { |post| post.comments.map(&:id) }
|
142
|
+
}.to change { query_counter.read_count }.by(3)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context "with has many and belongs to" do
|
147
|
+
it "performs 1 read per table rather than n + 1" do
|
148
|
+
expect {
|
149
|
+
user_query
|
150
|
+
.eager_load(:posts => { :comments => { :commenter => [] }})
|
151
|
+
.flat_map { |u| u.posts.to_a }
|
152
|
+
.flat_map { |p| p.comments.to_a }
|
153
|
+
.flat_map { |c| c.commenter.id }
|
154
|
+
}.to change { query_counter.read_count }.by(4)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "for has many to has many through" do
|
159
|
+
it "performs 1 read per table (including join table) rather than n + 1" do
|
160
|
+
expect {
|
161
|
+
user_query
|
162
|
+
.eager_load(:posts => { :categories => [] })
|
163
|
+
.flat_map { |u| u.posts.to_a }
|
164
|
+
.flat_map { |p| p.categories.to_a }
|
165
|
+
.flat_map { |c| c.id }
|
166
|
+
}.to change { query_counter.read_count }.by(4)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context "for has many through to has many" do
|
171
|
+
it "performs 1 read per table (includiing join table) rather than n + 1" do
|
172
|
+
expect {
|
173
|
+
user_query
|
174
|
+
.eager_load(:posts => { :categories => { :posts => [] }})
|
175
|
+
.flat_map { |u| u.posts.to_a }
|
176
|
+
.flat_map { |p| p.categories.to_a }
|
177
|
+
.flat_map { |c| c.posts.to_a }
|
178
|
+
}.to change { query_counter.read_count }.by(6)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
after do |example|
|
184
|
+
query_counter.show_queries if example.exception
|
185
|
+
end
|
186
|
+
end
|