bullet 4.14.10 → 5.0.0
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/.travis.yml +28 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile.rails-5.0 +17 -0
- data/README.md +2 -0
- data/lib/bullet.rb +5 -1
- data/lib/bullet/active_record5.rb +217 -0
- data/lib/bullet/dependency.rb +10 -0
- data/lib/bullet/detector/n_plus_one_query.rb +7 -1
- data/lib/bullet/version.rb +1 -1
- data/spec/bullet/detector/n_plus_one_query_spec.rb +15 -0
- data/spec/integration/active_record5/association_spec.rb +715 -0
- data/spec/integration/counter_cache_spec.rb +13 -4
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5439116cdedcd96dfad6dfd315eaf47ee815153
|
4
|
+
data.tar.gz: 9542679b93fa670fbf8b014fb0cbd3adb13a30d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4704ead627526344ab065d5d0a9bd2f3516165f664d9ab97aca3dd576c55ecc75450636daceebe21d100562be45c5e9352296ea0cd5856b0a4205be2120751cb
|
7
|
+
data.tar.gz: 6b3d52be604e5188920592e6b3952d2a683364631739c097b619b151f9131b911454452eae824e13493a4866ff767eb9ba61da3a98097aa3042e498faf74bdd6
|
data/.travis.yml
CHANGED
@@ -4,7 +4,9 @@ rvm:
|
|
4
4
|
- 2.0
|
5
5
|
- 2.1
|
6
6
|
- 2.2
|
7
|
+
- 2.2.3
|
7
8
|
gemfile:
|
9
|
+
- Gemfile.rails-5.0
|
8
10
|
- Gemfile.rails-4.2
|
9
11
|
- Gemfile.rails-4.1
|
10
12
|
- Gemfile.rails-4.0
|
@@ -26,6 +28,12 @@ services:
|
|
26
28
|
- mongodb
|
27
29
|
matrix:
|
28
30
|
exclude:
|
31
|
+
- rvm: 2.0
|
32
|
+
gemfile: Gemfile.rails-5.0
|
33
|
+
- rvm: 2.1
|
34
|
+
gemfile: Gemfile.rails-5.0
|
35
|
+
- rvm: 2.2
|
36
|
+
gemfile: Gemfile.rails-5.0
|
29
37
|
- rvm: 2.2
|
30
38
|
gemfile: Gemfile.rails-3.0
|
31
39
|
- rvm: 2.2
|
@@ -46,3 +54,23 @@ matrix:
|
|
46
54
|
gemfile: Gemfile.mongoid-2.5
|
47
55
|
- rvm: 2.2
|
48
56
|
gemfile: Gemfile.mongoid-2.4
|
57
|
+
- rvm: 2.2.3
|
58
|
+
gemfile: Gemfile.rails-3.0
|
59
|
+
- rvm: 2.2.3
|
60
|
+
gemfile: Gemfile.rails-3.1
|
61
|
+
- rvm: 2.2.3
|
62
|
+
gemfile: Gemfile.rails-3.2
|
63
|
+
- rvm: 2.2.3
|
64
|
+
gemfile: Gemfile.mongoid-3.1
|
65
|
+
- rvm: 2.2.3
|
66
|
+
gemfile: Gemfile.mongoid-3.0
|
67
|
+
- rvm: 2.2.3
|
68
|
+
gemfile: Gemfile.mongoid-2.8
|
69
|
+
- rvm: 2.2.3
|
70
|
+
gemfile: Gemfile.mongoid-2.7
|
71
|
+
- rvm: 2.2.3
|
72
|
+
gemfile: Gemfile.mongoid-2.6
|
73
|
+
- rvm: 2.2.3
|
74
|
+
gemfile: Gemfile.mongoid-2.5
|
75
|
+
- rvm: 2.2.3
|
76
|
+
gemfile: Gemfile.mongoid-2.4
|
data/CHANGELOG.md
CHANGED
data/Gemfile.rails-5.0
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
gem 'rails', '5.0.0.beta1'
|
6
|
+
gem 'sqlite3'
|
7
|
+
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
|
8
|
+
gem 'activerecord-import'
|
9
|
+
|
10
|
+
gem "rspec"
|
11
|
+
|
12
|
+
gem 'coveralls', require: false
|
13
|
+
|
14
|
+
platforms :rbx do
|
15
|
+
gem 'rubysl', '~> 2.0'
|
16
|
+
gem 'rubinius-developer_tools'
|
17
|
+
end
|
data/README.md
CHANGED
@@ -62,6 +62,7 @@ config.after_initialize do
|
|
62
62
|
Bullet.rollbar = true
|
63
63
|
Bullet.add_footer = true
|
64
64
|
Bullet.stacktrace_includes = [ 'your_gem', 'your_middleware' ]
|
65
|
+
Bullet.stacktrace_excludes = [ 'their_gem', 'their_middleware' ]
|
65
66
|
Bullet.slack = { webhook_url: 'http://some.slack.url', foo: 'bar' }
|
66
67
|
end
|
67
68
|
```
|
@@ -83,6 +84,7 @@ The code above will enable all seven of the Bullet notification systems:
|
|
83
84
|
* `Bullet.raise`: raise errors, useful for making your specs fail unless they have optimized queries
|
84
85
|
* `Bullet.add_footer`: adds the details in the bottom left corner of the page
|
85
86
|
* `Bullet.stacktrace_includes`: include paths with any of these substrings in the stack trace, even if they are not in your main app
|
87
|
+
* `Bullet.stacktrace_excludes`: ignore paths with any of these substrings in the stack trace, even if they are not in your main app.
|
86
88
|
* `Bullet.slack`: add notifications to slack
|
87
89
|
|
88
90
|
Bullet also allows you to disable any of its detectors.
|
data/lib/bullet.rb
CHANGED
@@ -28,7 +28,7 @@ module Bullet
|
|
28
28
|
end
|
29
29
|
|
30
30
|
class << self
|
31
|
-
attr_writer :enable, :n_plus_one_query_enable, :unused_eager_loading_enable, :counter_cache_enable, :stacktrace_includes
|
31
|
+
attr_writer :enable, :n_plus_one_query_enable, :unused_eager_loading_enable, :counter_cache_enable, :stacktrace_includes, :stacktrace_excludes
|
32
32
|
attr_reader :notification_collector, :whitelist
|
33
33
|
attr_accessor :add_footer, :orm_pathches_applied
|
34
34
|
|
@@ -76,6 +76,10 @@ module Bullet
|
|
76
76
|
@stacktrace_includes || []
|
77
77
|
end
|
78
78
|
|
79
|
+
def stacktrace_excludes
|
80
|
+
@stacktrace_excludes || []
|
81
|
+
end
|
82
|
+
|
79
83
|
def add_whitelist(options)
|
80
84
|
reset_whitelist
|
81
85
|
@whitelist[options[:type]][options[:class_name].classify] ||= []
|
@@ -0,0 +1,217 @@
|
|
1
|
+
module Bullet
|
2
|
+
module ActiveRecord
|
3
|
+
def self.enable
|
4
|
+
require 'active_record'
|
5
|
+
::ActiveRecord::Base.class_eval do
|
6
|
+
class <<self
|
7
|
+
alias_method :origin_find, :find
|
8
|
+
def find(*args)
|
9
|
+
result = origin_find(*args)
|
10
|
+
if Bullet.start?
|
11
|
+
if result.is_a? Array
|
12
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
13
|
+
Bullet::Detector::CounterCache.add_possible_objects(result)
|
14
|
+
elsif result.is_a? ::ActiveRecord::Base
|
15
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
|
16
|
+
Bullet::Detector::CounterCache.add_impossible_object(result)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
result
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
::ActiveRecord::Persistence.class_eval do
|
25
|
+
def save_with_bullet(*args, &proc)
|
26
|
+
was_new_record = new_record?
|
27
|
+
save_without_bullet(*args, &proc).tap do |result|
|
28
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
|
29
|
+
end
|
30
|
+
end
|
31
|
+
alias_method_chain :save, :bullet
|
32
|
+
end
|
33
|
+
|
34
|
+
::ActiveRecord::Relation.class_eval do
|
35
|
+
alias_method :origin_to_a, :to_a
|
36
|
+
# if select a collection of objects, then these objects have possible to cause N+1 query.
|
37
|
+
# if select only one object, then the only one object has impossible to cause N+1 query.
|
38
|
+
def to_a
|
39
|
+
records = origin_to_a
|
40
|
+
if Bullet.start?
|
41
|
+
if records.first.class.name !~ /^HABTM_/
|
42
|
+
if records.size > 1
|
43
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
44
|
+
Bullet::Detector::CounterCache.add_possible_objects(records)
|
45
|
+
elsif records.size == 1
|
46
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
47
|
+
Bullet::Detector::CounterCache.add_impossible_object(records.first)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
records
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
::ActiveRecord::Associations::Association.class_eval do
|
56
|
+
alias_method :origin_initialize, :initialize
|
57
|
+
def initialize(owner, reflection)
|
58
|
+
origin_initialize(owner, reflection)
|
59
|
+
reflection.set_owner owner
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
::ActiveRecord::Associations::Preloader.class_eval do
|
64
|
+
alias_method :origin_preloaders_on, :preloaders_on
|
65
|
+
|
66
|
+
def preloaders_on(association, records, scope)
|
67
|
+
if Bullet.start?
|
68
|
+
records.compact!
|
69
|
+
if records.first.class.name !~ /^HABTM_/
|
70
|
+
records.each do |record|
|
71
|
+
Bullet::Detector::Association.add_object_associations(record, association)
|
72
|
+
end
|
73
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
origin_preloaders_on(association, records, scope)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
::ActiveRecord::FinderMethods.class_eval do
|
81
|
+
# add includes in scope
|
82
|
+
alias_method :origin_find_with_associations, :find_with_associations
|
83
|
+
def find_with_associations
|
84
|
+
return origin_find_with_associations { |r| yield r } if block_given?
|
85
|
+
records = origin_find_with_associations
|
86
|
+
if Bullet.start?
|
87
|
+
associations = (eager_load_values + includes_values).uniq
|
88
|
+
records.each do |record|
|
89
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
90
|
+
end
|
91
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
92
|
+
end
|
93
|
+
records
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
::ActiveRecord::Associations::JoinDependency.class_eval do
|
98
|
+
alias_method :origin_instantiate, :instantiate
|
99
|
+
alias_method :origin_construct_model, :construct_model
|
100
|
+
|
101
|
+
def instantiate(result_set, aliases)
|
102
|
+
@bullet_eager_loadings = {}
|
103
|
+
records = origin_instantiate(result_set, aliases)
|
104
|
+
|
105
|
+
if Bullet.start?
|
106
|
+
@bullet_eager_loadings.each do |klazz, eager_loadings_hash|
|
107
|
+
objects = eager_loadings_hash.keys
|
108
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
records
|
112
|
+
end
|
113
|
+
|
114
|
+
# call join associations
|
115
|
+
def construct_model(record, node, row, model_cache, id, aliases)
|
116
|
+
result = origin_construct_model(record, node, row, model_cache, id, aliases)
|
117
|
+
|
118
|
+
if Bullet.start?
|
119
|
+
associations = node.reflection.name
|
120
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
121
|
+
Bullet::Detector::NPlusOneQuery.call_association(record, associations)
|
122
|
+
@bullet_eager_loadings[record.class] ||= {}
|
123
|
+
@bullet_eager_loadings[record.class][record] ||= Set.new
|
124
|
+
@bullet_eager_loadings[record.class][record] << associations
|
125
|
+
end
|
126
|
+
|
127
|
+
result
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
::ActiveRecord::Associations::CollectionAssociation.class_eval do
|
132
|
+
# call one to many associations
|
133
|
+
alias_method :origin_load_target, :load_target
|
134
|
+
def load_target
|
135
|
+
records = origin_load_target
|
136
|
+
|
137
|
+
if Bullet.start?
|
138
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
|
139
|
+
if records.first.class.name !~ /^HABTM_/
|
140
|
+
if records.size > 1
|
141
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
142
|
+
Bullet::Detector::CounterCache.add_possible_objects(records)
|
143
|
+
elsif records.size == 1
|
144
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
145
|
+
Bullet::Detector::CounterCache.add_impossible_object(records.first)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
records
|
150
|
+
end
|
151
|
+
|
152
|
+
alias_method :origin_empty?, :empty?
|
153
|
+
def empty?
|
154
|
+
if Bullet.start?
|
155
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
156
|
+
end
|
157
|
+
origin_empty?
|
158
|
+
end
|
159
|
+
|
160
|
+
alias_method :origin_include?, :include?
|
161
|
+
def include?(object)
|
162
|
+
if Bullet.start?
|
163
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
164
|
+
end
|
165
|
+
origin_include?(object)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
::ActiveRecord::Associations::SingularAssociation.class_eval do
|
170
|
+
# call has_one and belongs_to associations
|
171
|
+
alias_method :origin_reader, :reader
|
172
|
+
def reader(force_reload = false)
|
173
|
+
result = origin_reader(force_reload)
|
174
|
+
if Bullet.start?
|
175
|
+
if @owner.class.name !~ /^HABTM_/ && !@inversed
|
176
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
177
|
+
if Bullet::Detector::NPlusOneQuery.impossible?(@owner)
|
178
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
179
|
+
else
|
180
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
result
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
::ActiveRecord::Associations::HasManyAssociation.class_eval do
|
189
|
+
alias_method :origin_many_empty?, :empty?
|
190
|
+
def empty?
|
191
|
+
Thread.current[:bullet_collection_empty] = true
|
192
|
+
result = origin_many_empty?
|
193
|
+
Thread.current[:bullet_collection_empty] = nil
|
194
|
+
if Bullet.start?
|
195
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
196
|
+
end
|
197
|
+
result
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
::ActiveRecord::Reflection::AbstractReflection.class_eval do
|
202
|
+
def set_owner(owner)
|
203
|
+
@owner = owner
|
204
|
+
end
|
205
|
+
|
206
|
+
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
207
|
+
def has_cached_counter?
|
208
|
+
result = origin_has_cached_counter?
|
209
|
+
if Bullet.start? && !result && !Thread.current[:bullet_collection_empty]
|
210
|
+
Bullet::Detector::CounterCache.add_counter_cache(@owner, @name)
|
211
|
+
end
|
212
|
+
result
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
data/lib/bullet/dependency.rb
CHANGED
@@ -24,6 +24,8 @@ module Bullet
|
|
24
24
|
'active_record41'
|
25
25
|
elsif active_record42?
|
26
26
|
'active_record42'
|
27
|
+
elsif active_record50?
|
28
|
+
'active_record5'
|
27
29
|
end
|
28
30
|
end
|
29
31
|
end
|
@@ -50,6 +52,10 @@ module Bullet
|
|
50
52
|
active_record? && ::ActiveRecord::VERSION::MAJOR == 4
|
51
53
|
end
|
52
54
|
|
55
|
+
def active_record5?
|
56
|
+
active_record? && ::ActiveRecord::VERSION::MAJOR == 5
|
57
|
+
end
|
58
|
+
|
53
59
|
def active_record30?
|
54
60
|
active_record3? && ::ActiveRecord::VERSION::MINOR == 0
|
55
61
|
end
|
@@ -74,6 +80,10 @@ module Bullet
|
|
74
80
|
active_record4? && ::ActiveRecord::VERSION::MINOR == 2
|
75
81
|
end
|
76
82
|
|
83
|
+
def active_record50?
|
84
|
+
active_record5? && ::ActiveRecord::VERSION::MINOR == 0
|
85
|
+
end
|
86
|
+
|
77
87
|
def mongoid2x?
|
78
88
|
mongoid? && ::Mongoid::VERSION =~ /\A2\.[4-8]/
|
79
89
|
end
|
@@ -15,7 +15,7 @@ module Bullet
|
|
15
15
|
add_call_object_associations(object, associations)
|
16
16
|
|
17
17
|
Bullet.debug("Detector::NPlusOneQuery#call_association", "object: #{object.bullet_key}, associations: #{associations}")
|
18
|
-
if conditions_met?(object, associations)
|
18
|
+
if !excluded_stacktrace_path? && conditions_met?(object, associations)
|
19
19
|
Bullet.debug("detect n + 1 query", "object: #{object.bullet_key}, associations: #{associations}")
|
20
20
|
create_notification caller_in_project, object.class.to_s, associations
|
21
21
|
end
|
@@ -96,6 +96,12 @@ module Bullet
|
|
96
96
|
Bullet.stacktrace_includes.any? { |include| c.include?(include) }
|
97
97
|
end
|
98
98
|
end
|
99
|
+
|
100
|
+
def excluded_stacktrace_path?
|
101
|
+
Bullet.stacktrace_excludes.any? do |excluded_path|
|
102
|
+
caller_in_project.any? { |c| c.include?(excluded_path) }
|
103
|
+
end
|
104
|
+
end
|
99
105
|
end
|
100
106
|
end
|
101
107
|
end
|
data/lib/bullet/version.rb
CHANGED
@@ -85,6 +85,21 @@ module Bullet
|
|
85
85
|
expect(NPlusOneQuery).not_to receive(:create_notification).with("Post", :association)
|
86
86
|
NPlusOneQuery.call_association(@post, :association)
|
87
87
|
end
|
88
|
+
|
89
|
+
context "stacktrace_excludes" do
|
90
|
+
before { Bullet.stacktrace_excludes = [ 'def' ] }
|
91
|
+
after { Bullet.stacktrace_excludes = nil }
|
92
|
+
|
93
|
+
it "should not create notification when stacktrace contains paths that are in the exclude list" do
|
94
|
+
in_project = File.join(Dir.pwd, 'abc', 'abc.rb')
|
95
|
+
included_path = '/ghi/ghi.rb'
|
96
|
+
excluded_path = '/def/def.rb'
|
97
|
+
|
98
|
+
expect(NPlusOneQuery).to receive(:caller).and_return([in_project, included_path, excluded_path])
|
99
|
+
expect(NPlusOneQuery).to_not receive(:create_notification)
|
100
|
+
NPlusOneQuery.call_association(@post, :association)
|
101
|
+
end
|
102
|
+
end
|
88
103
|
end
|
89
104
|
|
90
105
|
context ".caller_in_project" do
|
@@ -0,0 +1,715 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
if !mongoid? && active_record5?
|
4
|
+
describe Bullet::Detector::Association, 'has_many' do
|
5
|
+
context "post => comments" do
|
6
|
+
it "should detect non preload post => comments" do
|
7
|
+
Post.all.each do |post|
|
8
|
+
post.comments.map(&:name)
|
9
|
+
end
|
10
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
11
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
12
|
+
|
13
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should detect preload with post => comments" do
|
17
|
+
Post.includes(:comments).each do |post|
|
18
|
+
post.comments.map(&:name)
|
19
|
+
end
|
20
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
21
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
22
|
+
|
23
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should detect unused preload post => comments" do
|
27
|
+
Post.includes(:comments).map(&:name)
|
28
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
29
|
+
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Post, :comments)
|
30
|
+
|
31
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should not detect unused preload post => comments" do
|
35
|
+
Post.all.map(&:name)
|
36
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
37
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
38
|
+
|
39
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should detect non preload comment => post with inverse_of" do
|
43
|
+
Post.includes(:comments).each do |post|
|
44
|
+
post.comments.each do |comment|
|
45
|
+
comment.name
|
46
|
+
comment.post.name
|
47
|
+
end
|
48
|
+
end
|
49
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
50
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
51
|
+
|
52
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should detect non preload post => comments with empty?" do
|
56
|
+
Post.all.each do |post|
|
57
|
+
post.comments.empty?
|
58
|
+
end
|
59
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
60
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
61
|
+
|
62
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should detect non preload post => comments with include?" do
|
66
|
+
comment = Comment.last
|
67
|
+
Post.all.each do |post|
|
68
|
+
post.comments.include?(comment)
|
69
|
+
end
|
70
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
71
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
72
|
+
|
73
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "category => posts => comments" do
|
78
|
+
it "should detect non preload category => posts => comments" do
|
79
|
+
Category.all.each do |category|
|
80
|
+
category.posts.each do |post|
|
81
|
+
post.comments.map(&:name)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
85
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
86
|
+
|
87
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Category, :posts)
|
88
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should detect preload category => posts, but no post => comments" do
|
92
|
+
Category.includes(:posts).each do |category|
|
93
|
+
category.posts.each do |post|
|
94
|
+
post.comments.map(&:name)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
98
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
99
|
+
|
100
|
+
expect(Bullet::Detector::Association).not_to be_detecting_unpreloaded_association_for(Category, :posts)
|
101
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should detect preload with category => posts => comments" do
|
105
|
+
Category.includes({:posts => :comments}).each do |category|
|
106
|
+
category.posts.each do |post|
|
107
|
+
post.comments.map(&:name)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
111
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
112
|
+
|
113
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should detect preload with category => posts => comments with posts.id > 0" do
|
117
|
+
Category.includes({:posts => :comments}).where('posts.id > 0').references(:posts).each do |category|
|
118
|
+
category.posts.each do |post|
|
119
|
+
post.comments.map(&:name)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
123
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
124
|
+
|
125
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should detect unused preload with category => posts => comments" do
|
129
|
+
Category.includes({:posts => :comments}).map(&:name)
|
130
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
131
|
+
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Post, :comments)
|
132
|
+
|
133
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should detect unused preload with post => commnets, no category => posts" do
|
137
|
+
Category.includes({:posts => :comments}).each do |category|
|
138
|
+
category.posts.map(&:name)
|
139
|
+
end
|
140
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
141
|
+
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Post, :comments)
|
142
|
+
|
143
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
context "category => posts, category => entries" do
|
148
|
+
it "should detect non preload with category => [posts, entries]" do
|
149
|
+
Category.all.each do |category|
|
150
|
+
category.posts.map(&:name)
|
151
|
+
category.entries.map(&:name)
|
152
|
+
end
|
153
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
154
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
155
|
+
|
156
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Category, :posts)
|
157
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Category, :entries)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should detect preload with category => posts, but not with category => entries" do
|
161
|
+
Category.includes(:posts).each do |category|
|
162
|
+
category.posts.map(&:name)
|
163
|
+
category.entries.map(&:name)
|
164
|
+
end
|
165
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
166
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
167
|
+
|
168
|
+
expect(Bullet::Detector::Association).not_to be_detecting_unpreloaded_association_for(Category, :posts)
|
169
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Category, :entries)
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should detect preload with category => [posts, entries]" do
|
173
|
+
Category.includes([:posts, :entries]).each do |category|
|
174
|
+
category.posts.map(&:name)
|
175
|
+
category.entries.map(&:name)
|
176
|
+
end
|
177
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
178
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
179
|
+
|
180
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should detect unused preload with category => [posts, entries]" do
|
184
|
+
Category.includes([:posts, :entries]).map(&:name)
|
185
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
186
|
+
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Category, :posts)
|
187
|
+
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Category, :entries)
|
188
|
+
|
189
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should detect unused preload with category => entries, but not with category => posts" do
|
193
|
+
Category.includes([:posts, :entries]).each do |category|
|
194
|
+
category.posts.map(&:name)
|
195
|
+
end
|
196
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
197
|
+
expect(Bullet::Detector::Association).not_to be_unused_preload_associations_for(Category, :posts)
|
198
|
+
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Category, :entries)
|
199
|
+
|
200
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
context "post => comment" do
|
205
|
+
it "should detect unused preload with post => comments" do
|
206
|
+
Post.includes(:comments).each do |post|
|
207
|
+
post.comments.first.name
|
208
|
+
end
|
209
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
210
|
+
expect(Bullet::Detector::Association).not_to be_unused_preload_associations_for(Post, :comments)
|
211
|
+
|
212
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should detect preload with post => commnets" do
|
216
|
+
Post.first.comments.map(&:name)
|
217
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
218
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
219
|
+
|
220
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should not detect unused preload with category => posts" do
|
224
|
+
category = Category.first
|
225
|
+
category.draft_post.destroy!
|
226
|
+
post = category.draft_post
|
227
|
+
post.update_attributes!(link: true)
|
228
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
229
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
230
|
+
|
231
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
232
|
+
|
233
|
+
Support::SqliteSeed.setup_db
|
234
|
+
Support::SqliteSeed.seed_db
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
context "category => posts => writer" do
|
239
|
+
it "should not detect unused preload associations" do
|
240
|
+
category = Category.includes({:posts => :writer}).order("id DESC").find_by_name('first')
|
241
|
+
category.posts.map do |post|
|
242
|
+
post.name
|
243
|
+
post.writer.name
|
244
|
+
end
|
245
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
246
|
+
expect(Bullet::Detector::Association).not_to be_unused_preload_associations_for(Category, :posts)
|
247
|
+
expect(Bullet::Detector::Association).not_to be_unused_preload_associations_for(Post, :writer)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
context "scope for_category_name" do
|
252
|
+
it "should detect preload with post => category" do
|
253
|
+
Post.in_category_name('first').references(:categories).each do |post|
|
254
|
+
post.category.name
|
255
|
+
end
|
256
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
257
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
258
|
+
|
259
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
260
|
+
end
|
261
|
+
|
262
|
+
it "should not be unused preload post => category" do
|
263
|
+
Post.in_category_name('first').references(:categories).map(&:name)
|
264
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
265
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
266
|
+
|
267
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
context "scope preload_comments" do
|
272
|
+
it "should detect preload post => comments with scope" do
|
273
|
+
Post.preload_comments.each do |post|
|
274
|
+
post.comments.map(&:name)
|
275
|
+
end
|
276
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
277
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
278
|
+
|
279
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
280
|
+
end
|
281
|
+
|
282
|
+
it "should detect unused preload with scope" do
|
283
|
+
Post.preload_comments.map(&:name)
|
284
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
285
|
+
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Post, :comments)
|
286
|
+
|
287
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
describe Bullet::Detector::Association, 'belongs_to' do
|
293
|
+
context "comment => post" do
|
294
|
+
it "should detect non preload with comment => post" do
|
295
|
+
Comment.all.each do |comment|
|
296
|
+
comment.post.name
|
297
|
+
end
|
298
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
299
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
300
|
+
|
301
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Comment, :post)
|
302
|
+
end
|
303
|
+
|
304
|
+
it "should detect preload with one comment => post" do
|
305
|
+
Comment.first.post.name
|
306
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
307
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
308
|
+
|
309
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
310
|
+
end
|
311
|
+
|
312
|
+
it "should dtect preload with comment => post" do
|
313
|
+
Comment.includes(:post).each do |comment|
|
314
|
+
comment.post.name
|
315
|
+
end
|
316
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
317
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
318
|
+
|
319
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
320
|
+
end
|
321
|
+
|
322
|
+
it "should not detect preload with comment => post" do
|
323
|
+
Comment.all.map(&:name)
|
324
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
325
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
326
|
+
|
327
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
328
|
+
end
|
329
|
+
|
330
|
+
it "should detect unused preload with comment => post" do
|
331
|
+
Comment.includes(:post).map(&:name)
|
332
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
333
|
+
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Comment, :post)
|
334
|
+
|
335
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
context "comment => post => category" do
|
340
|
+
it "should detect non preload association with comment => post" do
|
341
|
+
Comment.all.each do |comment|
|
342
|
+
comment.post.category.name
|
343
|
+
end
|
344
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
345
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
346
|
+
|
347
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Comment, :post)
|
348
|
+
end
|
349
|
+
|
350
|
+
it "should not detect non preload association with only one comment" do
|
351
|
+
Comment.first.post.category.name
|
352
|
+
|
353
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
354
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
355
|
+
|
356
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
357
|
+
end
|
358
|
+
|
359
|
+
it "should detect non preload association with post => category" do
|
360
|
+
Comment.includes(:post).each do |comment|
|
361
|
+
comment.post.category.name
|
362
|
+
end
|
363
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
364
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
365
|
+
|
366
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :category)
|
367
|
+
end
|
368
|
+
|
369
|
+
it "should not detect unpreload association" do
|
370
|
+
Comment.includes(:post => :category).each do |comment|
|
371
|
+
comment.post.category.name
|
372
|
+
end
|
373
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
374
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
375
|
+
|
376
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
context "comment => author, post => writer" do
|
381
|
+
it "should detect non preloaded writer" do
|
382
|
+
Comment.includes([:author, :post]).where(["base_users.id = ?", BaseUser.first]).references(:base_users).each do |comment|
|
383
|
+
comment.post.writer.name
|
384
|
+
end
|
385
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
386
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
387
|
+
|
388
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :writer)
|
389
|
+
end
|
390
|
+
|
391
|
+
it "should detect unused preload with comment => author" do
|
392
|
+
Comment.includes([:author, {:post => :writer}]).where(["base_users.id = ?", BaseUser.first]).references(:base_users).each do |comment|
|
393
|
+
comment.post.writer.name
|
394
|
+
end
|
395
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
396
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
397
|
+
|
398
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
399
|
+
end
|
400
|
+
|
401
|
+
it "should detect non preloading with writer => newspaper" do
|
402
|
+
Comment.includes(:post => :writer).where("posts.name like '%first%'").references(:posts).each do |comment|
|
403
|
+
comment.post.writer.newspaper.name
|
404
|
+
end
|
405
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
406
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
407
|
+
|
408
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Writer, :newspaper)
|
409
|
+
end
|
410
|
+
|
411
|
+
it "should not raise a stack error from posts to category" do
|
412
|
+
expect {
|
413
|
+
Comment.includes({:post => :category}).each do |com|
|
414
|
+
com.post.category
|
415
|
+
end
|
416
|
+
}.not_to raise_error()
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
describe Bullet::Detector::Association, 'has_and_belongs_to_many' do
|
422
|
+
context "students <=> teachers" do
|
423
|
+
it "should detect non preload associations" do
|
424
|
+
Student.all.each do |student|
|
425
|
+
student.teachers.map(&:name)
|
426
|
+
end
|
427
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
428
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
429
|
+
|
430
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Student, :teachers)
|
431
|
+
end
|
432
|
+
|
433
|
+
it "should detect preload associations" do
|
434
|
+
Student.includes(:teachers).each do |student|
|
435
|
+
student.teachers.map(&:name)
|
436
|
+
end
|
437
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
438
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
439
|
+
|
440
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
441
|
+
end
|
442
|
+
|
443
|
+
it "should detect unused preload associations" do
|
444
|
+
Student.includes(:teachers).map(&:name)
|
445
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
446
|
+
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Student, :teachers)
|
447
|
+
|
448
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
449
|
+
end
|
450
|
+
|
451
|
+
it "should detect no unused preload associations" do
|
452
|
+
Student.all.map(&:name)
|
453
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
454
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
455
|
+
|
456
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
457
|
+
end
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
describe Bullet::Detector::Association, 'has_many :through' do
|
462
|
+
context "firm => clients" do
|
463
|
+
it "should detect non preload associations" do
|
464
|
+
Firm.all.each do |firm|
|
465
|
+
firm.clients.map(&:name)
|
466
|
+
end
|
467
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
468
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
469
|
+
|
470
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Firm, :clients)
|
471
|
+
end
|
472
|
+
|
473
|
+
it "should detect preload associations" do
|
474
|
+
Firm.includes(:clients).each do |firm|
|
475
|
+
firm.clients.map(&:name)
|
476
|
+
end
|
477
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
478
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
479
|
+
|
480
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
481
|
+
end
|
482
|
+
|
483
|
+
it "should not detect preload associations" do
|
484
|
+
Firm.all.map(&:name)
|
485
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
486
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
487
|
+
|
488
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
489
|
+
end
|
490
|
+
|
491
|
+
it "should detect unused preload associations" do
|
492
|
+
Firm.includes(:clients).map(&:name)
|
493
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
494
|
+
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Firm, :clients)
|
495
|
+
|
496
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
describe Bullet::Detector::Association, "has_one" do
|
502
|
+
context "company => address" do
|
503
|
+
it "should detect non preload association" do
|
504
|
+
Company.all.each do |company|
|
505
|
+
company.address.name
|
506
|
+
end
|
507
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
508
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
509
|
+
|
510
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Company, :address)
|
511
|
+
end
|
512
|
+
|
513
|
+
it "should detect preload association" do
|
514
|
+
Company.includes(:address).each do |company|
|
515
|
+
company.address.name
|
516
|
+
end
|
517
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
518
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
519
|
+
|
520
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
521
|
+
end
|
522
|
+
|
523
|
+
it "should not detect preload association" do
|
524
|
+
Company.all.map(&:name)
|
525
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
526
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
527
|
+
|
528
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
529
|
+
end
|
530
|
+
|
531
|
+
it "should detect unused preload association" do
|
532
|
+
Company.includes(:address).map(&:name)
|
533
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
534
|
+
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Company, :address)
|
535
|
+
|
536
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
537
|
+
end
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
describe Bullet::Detector::Association, "has_one => has_many" do
|
542
|
+
it "should not detect preload association" do
|
543
|
+
user = User.first
|
544
|
+
user.submission.replies.map(&:name)
|
545
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
546
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
547
|
+
|
548
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
describe Bullet::Detector::Association, "call one association that in possible objects" do
|
553
|
+
it "should not detect preload association" do
|
554
|
+
Post.all
|
555
|
+
Post.first.comments.map(&:name)
|
556
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
557
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
558
|
+
|
559
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
describe Bullet::Detector::Association, "query immediately after creation" do
|
564
|
+
context "document => children" do
|
565
|
+
it 'should not detect non preload associations' do
|
566
|
+
document1 = Document.new
|
567
|
+
document1.children.build
|
568
|
+
document1.save
|
569
|
+
|
570
|
+
document2 = Document.new(parent: document1)
|
571
|
+
document2.save
|
572
|
+
document2.parent
|
573
|
+
|
574
|
+
document1.children.each.first
|
575
|
+
|
576
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
577
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
578
|
+
|
579
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
580
|
+
end
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
describe Bullet::Detector::Association, "STI" do
|
585
|
+
context "page => author" do
|
586
|
+
it "should detect non preload associations" do
|
587
|
+
Page.all.each do |page|
|
588
|
+
page.author.name
|
589
|
+
end
|
590
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
591
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
592
|
+
|
593
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Page, :author)
|
594
|
+
end
|
595
|
+
|
596
|
+
it "should detect preload associations" do
|
597
|
+
Page.includes(:author).each do |page|
|
598
|
+
page.author.name
|
599
|
+
end
|
600
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
601
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
602
|
+
|
603
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
604
|
+
end
|
605
|
+
|
606
|
+
it "should detect unused preload associations" do
|
607
|
+
Page.includes(:author).map(&:name)
|
608
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
609
|
+
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Page, :author)
|
610
|
+
|
611
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
612
|
+
end
|
613
|
+
|
614
|
+
it "should not detect preload associations" do
|
615
|
+
Page.all.map(&:name)
|
616
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
617
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
618
|
+
|
619
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
620
|
+
end
|
621
|
+
end
|
622
|
+
|
623
|
+
context "disable n plus one query" do
|
624
|
+
before { Bullet.n_plus_one_query_enable = false }
|
625
|
+
after { Bullet.n_plus_one_query_enable = true }
|
626
|
+
|
627
|
+
it "should not detect n plus one query" do
|
628
|
+
Post.all.each do |post|
|
629
|
+
post.comments.map(&:name)
|
630
|
+
end
|
631
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
632
|
+
|
633
|
+
expect(Bullet::Detector::Association).not_to be_detecting_unpreloaded_association_for(Post, :comments)
|
634
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
635
|
+
end
|
636
|
+
|
637
|
+
it "should still detect unused eager loading" do
|
638
|
+
Post.includes(:comments).map(&:name)
|
639
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
640
|
+
|
641
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
642
|
+
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Post, :comments)
|
643
|
+
end
|
644
|
+
end
|
645
|
+
|
646
|
+
context "disable unused eager loading" do
|
647
|
+
before { Bullet.unused_eager_loading_enable = false }
|
648
|
+
after { Bullet.unused_eager_loading_enable = true }
|
649
|
+
|
650
|
+
it "should not detect unused eager loading" do
|
651
|
+
Post.includes(:comments).map(&:name)
|
652
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
653
|
+
|
654
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
655
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
656
|
+
end
|
657
|
+
|
658
|
+
it "should still detect n plus one query" do
|
659
|
+
Post.all.each do |post|
|
660
|
+
post.comments.map(&:name)
|
661
|
+
end
|
662
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
663
|
+
|
664
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
|
665
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
666
|
+
end
|
667
|
+
end
|
668
|
+
|
669
|
+
context "whitelist n plus one query" do
|
670
|
+
before { Bullet.add_whitelist :type => :n_plus_one_query, :class_name => "Post", :association => :comments }
|
671
|
+
after { Bullet.clear_whitelist }
|
672
|
+
|
673
|
+
it "should not detect n plus one query" do
|
674
|
+
Post.all.each do |post|
|
675
|
+
post.comments.map(&:name)
|
676
|
+
end
|
677
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
678
|
+
|
679
|
+
expect(Bullet::Detector::Association).not_to be_detecting_unpreloaded_association_for(Post, :comments)
|
680
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
681
|
+
end
|
682
|
+
|
683
|
+
it "should still detect unused eager loading" do
|
684
|
+
Post.includes(:comments).map(&:name)
|
685
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
686
|
+
|
687
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
688
|
+
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Post, :comments)
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
context "whitelist unused eager loading" do
|
693
|
+
before { Bullet.add_whitelist :type => :unused_eager_loading, :class_name => "Post", :association => :comments }
|
694
|
+
after { Bullet.clear_whitelist }
|
695
|
+
|
696
|
+
it "should not detect unused eager loading" do
|
697
|
+
Post.includes(:comments).map(&:name)
|
698
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
699
|
+
|
700
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
701
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
702
|
+
end
|
703
|
+
|
704
|
+
it "should still detect n plus one query" do
|
705
|
+
Post.all.each do |post|
|
706
|
+
post.comments.map(&:name)
|
707
|
+
end
|
708
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
709
|
+
|
710
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
|
711
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
712
|
+
end
|
713
|
+
end
|
714
|
+
end
|
715
|
+
end
|
@@ -29,11 +29,20 @@ if !mongoid? && active_record?
|
|
29
29
|
expect(Bullet.collected_counter_cache_notifications).to be_empty
|
30
30
|
end
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
if active_record5?
|
33
|
+
it "should not need counter cache for has_many through" do
|
34
|
+
Client.all.each do |client|
|
35
|
+
client.firms.size
|
36
|
+
end
|
37
|
+
expect(Bullet.collected_counter_cache_notifications).to be_empty
|
38
|
+
end
|
39
|
+
else
|
40
|
+
it "should need counter cache for has_many through" do
|
41
|
+
Client.all.each do |client|
|
42
|
+
client.firms.size
|
43
|
+
end
|
44
|
+
expect(Bullet.collected_counter_cache_notifications).not_to be_empty
|
35
45
|
end
|
36
|
-
expect(Bullet.collected_counter_cache_notifications).not_to be_empty
|
37
46
|
end
|
38
47
|
|
39
48
|
it "should not need counter cache with part of cities" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bullet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 5.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Huang
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -66,6 +66,7 @@ files:
|
|
66
66
|
- Gemfile.rails-4.0
|
67
67
|
- Gemfile.rails-4.1
|
68
68
|
- Gemfile.rails-4.2
|
69
|
+
- Gemfile.rails-5.0
|
69
70
|
- Guardfile
|
70
71
|
- Hacking.md
|
71
72
|
- MIT-LICENSE
|
@@ -78,6 +79,7 @@ files:
|
|
78
79
|
- lib/bullet/active_record4.rb
|
79
80
|
- lib/bullet/active_record41.rb
|
80
81
|
- lib/bullet/active_record42.rb
|
82
|
+
- lib/bullet/active_record5.rb
|
81
83
|
- lib/bullet/dependency.rb
|
82
84
|
- lib/bullet/detector.rb
|
83
85
|
- lib/bullet/detector/association.rb
|
@@ -124,6 +126,7 @@ files:
|
|
124
126
|
- spec/bullet_spec.rb
|
125
127
|
- spec/integration/active_record3/association_spec.rb
|
126
128
|
- spec/integration/active_record4/association_spec.rb
|
129
|
+
- spec/integration/active_record5/association_spec.rb
|
127
130
|
- spec/integration/counter_cache_spec.rb
|
128
131
|
- spec/integration/mongoid/association_spec.rb
|
129
132
|
- spec/models/address.rb
|
@@ -186,7 +189,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
186
189
|
version: 1.3.6
|
187
190
|
requirements: []
|
188
191
|
rubyforge_project:
|
189
|
-
rubygems_version: 2.
|
192
|
+
rubygems_version: 2.5.1
|
190
193
|
signing_key:
|
191
194
|
specification_version: 4
|
192
195
|
summary: help to kill N+1 queries and unused eager loading.
|
@@ -210,6 +213,7 @@ test_files:
|
|
210
213
|
- spec/bullet_spec.rb
|
211
214
|
- spec/integration/active_record3/association_spec.rb
|
212
215
|
- spec/integration/active_record4/association_spec.rb
|
216
|
+
- spec/integration/active_record5/association_spec.rb
|
213
217
|
- spec/integration/counter_cache_spec.rb
|
214
218
|
- spec/integration/mongoid/association_spec.rb
|
215
219
|
- spec/models/address.rb
|