jit_preloader 0.2.2 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: a2439886049ccbcd7845882f8fa47524e500ffc96d8b9620711288853c47c131
4
- data.tar.gz: 189adc93418cf777c6eb33617934d92426cfbdc105a05a10c639f9dd387736f8
2
+ SHA1:
3
+ metadata.gz: c2c802d6683cb058781abe0360bec58b0857c020
4
+ data.tar.gz: 3576db9546592974cc8383c26fa395b939971876
5
5
  SHA512:
6
- metadata.gz: 932327300aea76f21906abf912ba85d655fbeb04b6cf940c1c650e21d39a72682d4fb55d529c389897ed7d5b45d9a41cec411a5c7eb932f2216df03b91fef47e
7
- data.tar.gz: 591bc82f9d763b963a78d6869da2f6346c7c3d49b044e48d0a12529fd61c94b104c3e0cc8e7e08a8a8fd9f4feee4ce73e66fbc72cf36ef11477852949d012802
6
+ metadata.gz: 4cf99a2440af7e4ef861cf2a2ea679f6ff3fac90494aab0fddb5722a1712b9611357d03c15d6cf1a1ced43f698231228c276c517b362fb05937f8e3510a7a8e4
7
+ data.tar.gz: ba29dd13180a27824ce4e1ad1ec88124c1200e6025a5341a88aff891b706cb1f899a6ba6d59c208c2ae042cc8112df0ea9c4fed90b3e2a7b6d428145b39b5f1b
data/README.md CHANGED
@@ -215,7 +215,7 @@ end
215
215
 
216
216
  ## Contributing
217
217
 
218
- 1. Fork it ( https://github.com/[my-github-username]/jit_preloader/fork )
218
+ 1. Fork it ( https://github.com/clio/jit_preloader/fork )
219
219
  2. Create your feature branch (`git checkout -b my-new-feature`)
220
220
  3. Commit your changes (`git commit -am 'Add some feature'`)
221
221
  4. Push to the branch (`git push origin my-new-feature`)
@@ -41,11 +41,25 @@ module JitPreloadExtension
41
41
  association_scope = klass.all.merge(association(assoc).scope).unscope(where: aggregate_association.foreign_key)
42
42
  association_scope = association_scope.instance_exec(&reflection.scope).reorder(nil) if reflection.scope
43
43
 
44
- conditions[aggregate_association.table_name] = { aggregate_association.foreign_key => primary_ids }
44
+ # If the query uses an alias for the association, use that instead of the table name
45
+ table_alias_name = association_scope.references_values.first
46
+ table_reference = table_alias_name || aggregate_association.table_name
47
+
48
+ conditions[table_reference] = { aggregate_association.foreign_key => primary_ids }
49
+
50
+ # If the association is a STI child model, specify its type in the condition so that it
51
+ # doesn't include results from other child models
52
+ parent_is_base_class = aggregate_association.klass.superclass.abstract_class? || aggregate_association.klass.superclass == ActiveRecord::Base
53
+ has_type_column = aggregate_association.klass.column_names.include?(aggregate_association.klass.inheritance_column)
54
+ is_child_sti_model = !parent_is_base_class && has_type_column
55
+ if is_child_sti_model
56
+ conditions[table_reference].merge!({ aggregate_association.klass.inheritance_column => aggregate_association.klass.sti_name })
57
+ end
58
+
45
59
  if reflection.type.present?
46
60
  conditions[reflection.type] = self.class.name
47
61
  end
48
- group_by = "#{aggregate_association.table_name}.#{aggregate_association.foreign_key}"
62
+ group_by = "#{table_reference}.#{aggregate_association.foreign_key}"
49
63
 
50
64
  preloaded_data = Hash[association_scope
51
65
  .where(conditions)
@@ -1,3 +1,3 @@
1
1
  module JitPreloader
2
- VERSION = "0.2.2"
2
+ VERSION = "0.2.3"
3
3
  end
@@ -1,7 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe JitPreloader::Preloader do
4
-
5
4
  let!(:contact1) do
6
5
  addresses = [
7
6
  Address.new(street: "123 Fake st", country: canada),
@@ -49,6 +48,66 @@ RSpec.describe JitPreloader::Preloader do
49
48
  ->(event, data){ source_map[data[:source]] << data[:association] }
50
49
  end
51
50
 
51
+ context "for single table inheritance" do
52
+ context "when preloading an aggregate for a child model" do
53
+ let!(:contact_book) { ContactBook.create(name: "The Yellow Pages") }
54
+ let!(:company1) { Company.create(name: "Company1", contact_book: contact_book) }
55
+ let!(:company2) { Company.create(name: "Company2", contact_book: contact_book) }
56
+
57
+ it "can handle queries" do
58
+ contact_books = ContactBook.jit_preload.to_a
59
+ expect(contact_books.first.companies_count).to eq 2
60
+ end
61
+ end
62
+
63
+ context "when preloading an aggregate of a child model through its base model" do
64
+ let!(:contact_book) { ContactBook.create(name: "The Yellow Pages") }
65
+ let!(:contact) { Contact.create(name: "Contact", contact_book: contact_book) }
66
+ let!(:company1) { Company.create(name: "Company1", contact_book: contact_book) }
67
+ let!(:company2) { Company.create(name: "Company2", contact_book: contact_book) }
68
+ let!(:contact_employee1) { Employee.create(name: "Contact Employee1", contact: contact) }
69
+ let!(:contact_employee2) { Employee.create(name: "Contact Employee2", contact: contact) }
70
+ let!(:company_employee1) { Employee.create(name: "Company Employee1", contact: company1) }
71
+ let!(:company_employee2) { Employee.create(name: "Company Employee2", contact: company2) }
72
+
73
+ it "can handle queries" do
74
+ contact_books = ContactBook.jit_preload.to_a
75
+ expect(contact_books.first.employees_count).to eq 4
76
+ end
77
+ end
78
+
79
+ context "when preloading an aggregate of a nested child model through another child model" do
80
+ let!(:contact_book) { ContactBook.create(name: "The Yellow Pages") }
81
+ let!(:contact) { Contact.create(name: "Contact", contact_book: contact_book) }
82
+ let!(:company1) { Company.create(name: "Company1", contact_book: contact_book) }
83
+ let!(:company2) { Company.create(name: "Company2", contact_book: contact_book) }
84
+ let!(:contact_employee1) { Employee.create(name: "Contact Employee1", contact: contact) }
85
+ let!(:contact_employee2) { Employee.create(name: "Contact Employee2", contact: contact) }
86
+ let!(:company_employee1) { Employee.create(name: "Company Employee1", contact: company1) }
87
+ let!(:company_employee2) { Employee.create(name: "Company Employee2", contact: company2) }
88
+
89
+ it "can handle queries" do
90
+ contact_books = ContactBook.jit_preload.to_a
91
+ expect(contact_books.first.company_employees_count).to eq 2
92
+ end
93
+ end
94
+
95
+ context "when preloading an aggregate of a nested child model through a many-to-many relationship with another child model" do
96
+ let!(:contact_book) { ContactBook.create(name: "The Yellow Pages") }
97
+ let!(:child1) { Child.create(name: "Child1") }
98
+ let!(:child2) { Child.create(name: "Child2") }
99
+ let!(:child3) { Child.create(name: "Child3") }
100
+ let!(:parent1) { Parent.create(name: "Parent1", contact_book: contact_book, children: [child1, child2]) }
101
+ let!(:parent2) { Parent.create(name: "Parent2", contact_book: contact_book, children: [child2, child3]) }
102
+
103
+ it "can handle queries" do
104
+ contact_books = ContactBook.jit_preload.to_a
105
+ expect(contact_books.first.children_count).to eq 4
106
+ expect(contact_books.first.children).to include(child1, child2, child3)
107
+ end
108
+ end
109
+ end
110
+
52
111
  context "when preloading an aggregate as polymorphic" do
53
112
  let(:contact_owner_counts) { [2] }
54
113
 
@@ -1,12 +1,14 @@
1
1
  class Database
2
2
  def self.tables
3
3
  [
4
- "CREATE TABLE contacts (id INTEGER NOT NULL PRIMARY KEY, name VARCHAR(255), contact_owner_id INTEGER, contact_owner_type VARCHAR(255))",
4
+ "CREATE TABLE contact_books (id INTEGER NOT NULL PRIMARY KEY, name VARCHAR(255))",
5
+ "CREATE TABLE contacts (id INTEGER NOT NULL PRIMARY KEY, type VARCHAR(255), contact_book_id INTEGER, contact_id INTEGER, name VARCHAR(255), contact_owner_id INTEGER, contact_owner_type VARCHAR(255))",
5
6
  "CREATE TABLE contact_owners (id INTEGER NOT NULL PRIMARY KEY, name VARCHAR(255))",
6
7
  "CREATE TABLE addresses (id INTEGER NOT NULL PRIMARY KEY, contact_id INTEGER NOT NULL, country_id INTEGER NOT NULL, street VARCHAR(255))",
7
8
  "CREATE TABLE email_addresses (id INTEGER NOT NULL PRIMARY KEY, contact_id INTEGER NOT NULL, address VARCHAR(255))",
8
9
  "CREATE TABLE phone_numbers (id INTEGER NOT NULL PRIMARY KEY, contact_id INTEGER NOT NULL, phone VARCHAR(10))",
9
10
  "CREATE TABLE countries (id INTEGER NOT NULL PRIMARY KEY, name VARCHAR(255))",
11
+ "CREATE TABLE parents_children (id INTEGER NOT NULL PRIMARY KEY, parent_id INTEGER, child_id INTEGER)",
10
12
  ]
11
13
  end
12
14
 
@@ -1,15 +1,55 @@
1
+ class ContactBook < ActiveRecord::Base
2
+ has_many :contacts
3
+ has_many :employees, through: :contacts
4
+
5
+ has_many :companies
6
+ has_many :company_employees, through: :companies, source: :employees
7
+
8
+ has_many :parents
9
+ has_many :children, through: :parents
10
+
11
+ has_many_aggregate :companies, :count, :count, "*"
12
+ has_many_aggregate :employees, :count, :count, "*"
13
+ has_many_aggregate :company_employees, :count, :count, "*"
14
+ has_many_aggregate :children, :count, :count, "*"
15
+ end
16
+
1
17
  class Contact < ActiveRecord::Base
18
+ belongs_to :contact_book
2
19
  belongs_to :contact_owner, polymorphic: true
3
20
 
4
21
  has_many :addresses
5
22
  has_many :phone_numbers
6
23
  has_one :email_address
24
+ has_many :employees
7
25
 
8
26
  has_many_aggregate :addresses, :max_street_length, :maximum, "LENGTH(street)"
9
27
  has_many_aggregate :phone_numbers, :count, :count, "id"
10
28
  has_many_aggregate :addresses, :count, :count, "*"
11
29
  end
12
30
 
31
+ class Company < Contact
32
+ end
33
+
34
+ class Employee < Contact
35
+ belongs_to :contact
36
+ end
37
+
38
+ class ParentsChild < ActiveRecord::Base
39
+ belongs_to :parent
40
+ belongs_to :child
41
+ end
42
+
43
+ class Parent < Contact
44
+ has_many :parents_child
45
+ has_many :children, through: :parents_child
46
+ end
47
+
48
+ class Child < Contact
49
+ has_many :parents_child
50
+ has_many :parents, through: :parents_child
51
+ end
52
+
13
53
  class Address < ActiveRecord::Base
14
54
  belongs_to :contact
15
55
  belongs_to :country
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jit_preloader
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kyle d'Oliveira
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-16 00:00:00.000000000 Z
11
+ date: 2019-11-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -178,7 +178,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
178
  - !ruby/object:Gem::Version
179
179
  version: '0'
180
180
  requirements: []
181
- rubygems_version: 3.0.3
181
+ rubyforge_project:
182
+ rubygems_version: 2.5.2.1
182
183
  signing_key:
183
184
  specification_version: 4
184
185
  summary: Tool to understand N+1 queries and to remove them