bullet 4.14.8 → 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 +30 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile.mongoid-5.0 +17 -0
- data/Gemfile.rails-5.0 +17 -0
- data/README.md +5 -0
- data/lib/bullet/active_record3.rb +7 -5
- data/lib/bullet/active_record3x.rb +3 -3
- data/lib/bullet/active_record4.rb +12 -2
- data/lib/bullet/active_record41.rb +12 -2
- data/lib/bullet/active_record42.rb +23 -4
- data/lib/bullet/active_record5.rb +217 -0
- data/lib/bullet/dependency.rb +16 -0
- data/lib/bullet/detector/n_plus_one_query.rb +8 -2
- data/lib/bullet/detector/unused_eager_loading.rb +1 -1
- data/lib/bullet/mongoid5x.rb +56 -0
- data/lib/bullet/version.rb +1 -1
- data/lib/bullet.rb +16 -4
- data/spec/bullet/detector/n_plus_one_query_spec.rb +15 -0
- data/spec/bullet/notification/base_spec.rb +2 -2
- data/spec/bullet_spec.rb +47 -0
- data/spec/integration/active_record3/association_spec.rb +2 -2
- data/spec/integration/active_record4/association_spec.rb +23 -2
- data/spec/integration/active_record5/association_spec.rb +715 -0
- data/spec/integration/counter_cache_spec.rb +17 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/support/mongo_seed.rb +13 -0
- data/test.sh +1 -0
- data/update.sh +1 -0
- metadata +9 -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
|
@@ -1,15 +1,19 @@
|
|
|
1
|
+
sudo: false
|
|
1
2
|
language: ruby
|
|
2
3
|
rvm:
|
|
3
4
|
- 2.0
|
|
4
5
|
- 2.1
|
|
5
6
|
- 2.2
|
|
7
|
+
- 2.2.3
|
|
6
8
|
gemfile:
|
|
9
|
+
- Gemfile.rails-5.0
|
|
7
10
|
- Gemfile.rails-4.2
|
|
8
11
|
- Gemfile.rails-4.1
|
|
9
12
|
- Gemfile.rails-4.0
|
|
10
13
|
- Gemfile.rails-3.2
|
|
11
14
|
- Gemfile.rails-3.1
|
|
12
15
|
- Gemfile.rails-3.0
|
|
16
|
+
- Gemfile.mongoid-5.0
|
|
13
17
|
- Gemfile.mongoid-4.0
|
|
14
18
|
- Gemfile.mongoid-3.1
|
|
15
19
|
- Gemfile.mongoid-3.0
|
|
@@ -24,6 +28,12 @@ services:
|
|
|
24
28
|
- mongodb
|
|
25
29
|
matrix:
|
|
26
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
|
|
27
37
|
- rvm: 2.2
|
|
28
38
|
gemfile: Gemfile.rails-3.0
|
|
29
39
|
- rvm: 2.2
|
|
@@ -44,3 +54,23 @@ matrix:
|
|
|
44
54
|
gemfile: Gemfile.mongoid-2.5
|
|
45
55
|
- rvm: 2.2
|
|
46
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
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Next Release
|
|
2
2
|
|
|
3
|
+
## 5.0.0 (01/06/2015)
|
|
4
|
+
|
|
5
|
+
* Support Rails 5.0.0.beta1
|
|
6
|
+
|
|
7
|
+
## 4.14.10
|
|
8
|
+
|
|
9
|
+
* Fix `has_many :through` infinite loop issue
|
|
10
|
+
|
|
11
|
+
## 4.14.9
|
|
12
|
+
|
|
13
|
+
* Support mongoid 5.0.0
|
|
14
|
+
* Do not report association queries immediately after object creation to
|
|
15
|
+
require a preload
|
|
16
|
+
* Detect `counter_cache` for `has_many :through` association
|
|
17
|
+
|
|
3
18
|
## 4.14.8
|
|
4
19
|
|
|
5
20
|
* compatible with `composite_primary_keys` gem
|
data/Gemfile.mongoid-5.0
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
source "https://rubygems.org"
|
|
2
|
+
|
|
3
|
+
gemspec
|
|
4
|
+
|
|
5
|
+
gem 'rails', '~> 4.0.0'
|
|
6
|
+
gem 'sqlite3', platforms: [:ruby]
|
|
7
|
+
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
|
|
8
|
+
gem 'mongoid', '~> 5.0.0.beta', github: 'mongoid'
|
|
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/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
|
@@ -36,6 +36,9 @@ or add it into a Gemfile (Bundler):
|
|
|
36
36
|
gem "bullet", :group => "development"
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
**Note**: make sure `bullet` gem is added after activerecord (rails) and
|
|
40
|
+
mongoid.
|
|
41
|
+
|
|
39
42
|
## Configuration
|
|
40
43
|
|
|
41
44
|
Bullet won't do ANYTHING unless you tell it to explicitly. Append to
|
|
@@ -59,6 +62,7 @@ config.after_initialize do
|
|
|
59
62
|
Bullet.rollbar = true
|
|
60
63
|
Bullet.add_footer = true
|
|
61
64
|
Bullet.stacktrace_includes = [ 'your_gem', 'your_middleware' ]
|
|
65
|
+
Bullet.stacktrace_excludes = [ 'their_gem', 'their_middleware' ]
|
|
62
66
|
Bullet.slack = { webhook_url: 'http://some.slack.url', foo: 'bar' }
|
|
63
67
|
end
|
|
64
68
|
```
|
|
@@ -80,6 +84,7 @@ The code above will enable all seven of the Bullet notification systems:
|
|
|
80
84
|
* `Bullet.raise`: raise errors, useful for making your specs fail unless they have optimized queries
|
|
81
85
|
* `Bullet.add_footer`: adds the details in the bottom left corner of the page
|
|
82
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.
|
|
83
88
|
* `Bullet.slack`: add notifications to slack
|
|
84
89
|
|
|
85
90
|
Bullet also allows you to disable any of its detectors.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module Bullet
|
|
2
2
|
module ActiveRecord
|
|
3
|
+
LOAD_TARGET = 'load_target'.freeze
|
|
4
|
+
|
|
3
5
|
def self.enable
|
|
4
6
|
require 'active_record'
|
|
5
7
|
::ActiveRecord::Relation.class_eval do
|
|
@@ -138,7 +140,7 @@ module Bullet
|
|
|
138
140
|
# avoid stack level too deep
|
|
139
141
|
result = origin_load_target
|
|
140
142
|
if Bullet.start?
|
|
141
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless caller.any? { |c| c.include?(
|
|
143
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless caller.any? { |c| c.include?(LOAD_TARGET) }
|
|
142
144
|
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
|
143
145
|
end
|
|
144
146
|
result
|
|
@@ -160,8 +162,8 @@ module Bullet
|
|
|
160
162
|
|
|
161
163
|
def has_cached_counter?
|
|
162
164
|
result = origin_has_cached_counter?
|
|
163
|
-
if Bullet.start?
|
|
164
|
-
Bullet::Detector::CounterCache.add_counter_cache(@owner, @reflection.name)
|
|
165
|
+
if Bullet.start? && !result
|
|
166
|
+
Bullet::Detector::CounterCache.add_counter_cache(@owner, @reflection.name)
|
|
165
167
|
end
|
|
166
168
|
result
|
|
167
169
|
end
|
|
@@ -171,8 +173,8 @@ module Bullet
|
|
|
171
173
|
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
|
172
174
|
def has_cached_counter?
|
|
173
175
|
result = origin_has_cached_counter?
|
|
174
|
-
if Bullet.start?
|
|
175
|
-
Bullet::Detector::CounterCache.add_counter_cache(@owner, @reflection.name)
|
|
176
|
+
if Bullet.start? && !result
|
|
177
|
+
Bullet::Detector::CounterCache.add_counter_cache(@owner, @reflection.name)
|
|
176
178
|
end
|
|
177
179
|
result
|
|
178
180
|
end
|
|
@@ -143,10 +143,10 @@ module Bullet
|
|
|
143
143
|
::ActiveRecord::Associations::HasManyAssociation.class_eval do
|
|
144
144
|
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
|
145
145
|
|
|
146
|
-
def has_cached_counter?(reflection = reflection)
|
|
146
|
+
def has_cached_counter?(reflection = reflection())
|
|
147
147
|
result = origin_has_cached_counter?(reflection)
|
|
148
|
-
if Bullet.start?
|
|
149
|
-
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
148
|
+
if Bullet.start? && !result
|
|
149
|
+
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
150
150
|
end
|
|
151
151
|
result
|
|
152
152
|
end
|
|
@@ -21,6 +21,16 @@ module Bullet
|
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
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
|
+
|
|
24
34
|
::ActiveRecord::Associations::Preloader.class_eval do
|
|
25
35
|
# include query for one to many associations.
|
|
26
36
|
# keep this eager loadings.
|
|
@@ -135,8 +145,8 @@ module Bullet
|
|
|
135
145
|
|
|
136
146
|
def has_cached_counter?(reflection = reflection())
|
|
137
147
|
result = origin_has_cached_counter?(reflection)
|
|
138
|
-
if Bullet.start?
|
|
139
|
-
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
148
|
+
if Bullet.start? && !result
|
|
149
|
+
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
140
150
|
end
|
|
141
151
|
result
|
|
142
152
|
end
|
|
@@ -23,6 +23,16 @@ module Bullet
|
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
::ActiveRecord::Persistence.class_eval do
|
|
27
|
+
def save_with_bullet(*args, &proc)
|
|
28
|
+
was_new_record = new_record?
|
|
29
|
+
save_without_bullet(*args, &proc).tap do |result|
|
|
30
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
alias_method_chain :save, :bullet
|
|
34
|
+
end
|
|
35
|
+
|
|
26
36
|
::ActiveRecord::Associations::Preloader.class_eval do
|
|
27
37
|
alias_method :origin_preloaders_on, :preloaders_on
|
|
28
38
|
|
|
@@ -138,8 +148,8 @@ module Bullet
|
|
|
138
148
|
|
|
139
149
|
def has_cached_counter?(reflection = reflection())
|
|
140
150
|
result = origin_has_cached_counter?(reflection)
|
|
141
|
-
if Bullet.start?
|
|
142
|
-
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
151
|
+
if Bullet.start? && !result
|
|
152
|
+
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
143
153
|
end
|
|
144
154
|
result
|
|
145
155
|
end
|
|
@@ -21,6 +21,16 @@ module Bullet
|
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
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
|
+
|
|
24
34
|
::ActiveRecord::Relation.class_eval do
|
|
25
35
|
alias_method :origin_to_a, :to_a
|
|
26
36
|
# if select a collection of objects, then these objects have possible to cause N+1 query.
|
|
@@ -182,10 +192,19 @@ module Bullet
|
|
|
182
192
|
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
|
183
193
|
def has_cached_counter?(reflection = reflection())
|
|
184
194
|
result = origin_has_cached_counter?(reflection)
|
|
185
|
-
if Bullet.start?
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
195
|
+
if Bullet.start? && !result && !Thread.current[:bullet_collection_empty]
|
|
196
|
+
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
197
|
+
end
|
|
198
|
+
result
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
::ActiveRecord::Associations::HasManyThroughAssociation.class_eval do
|
|
203
|
+
alias_method :origin_hmt_has_cached_counter?, :has_cached_counter?
|
|
204
|
+
def has_cached_counter?(reflection = reflection())
|
|
205
|
+
result = origin_hmt_has_cached_counter?(reflection)
|
|
206
|
+
if Bullet.start? && !result && !Thread.current[:bullet_collection_empty]
|
|
207
|
+
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
189
208
|
end
|
|
190
209
|
result
|
|
191
210
|
end
|
|
@@ -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
|
|
@@ -36,6 +38,8 @@ module Bullet
|
|
|
36
38
|
'mongoid3x'
|
|
37
39
|
elsif mongoid4x?
|
|
38
40
|
'mongoid4x'
|
|
41
|
+
elsif mongoid5x?
|
|
42
|
+
'mongoid5x'
|
|
39
43
|
end
|
|
40
44
|
end
|
|
41
45
|
end
|
|
@@ -48,6 +52,10 @@ module Bullet
|
|
|
48
52
|
active_record? && ::ActiveRecord::VERSION::MAJOR == 4
|
|
49
53
|
end
|
|
50
54
|
|
|
55
|
+
def active_record5?
|
|
56
|
+
active_record? && ::ActiveRecord::VERSION::MAJOR == 5
|
|
57
|
+
end
|
|
58
|
+
|
|
51
59
|
def active_record30?
|
|
52
60
|
active_record3? && ::ActiveRecord::VERSION::MINOR == 0
|
|
53
61
|
end
|
|
@@ -72,6 +80,10 @@ module Bullet
|
|
|
72
80
|
active_record4? && ::ActiveRecord::VERSION::MINOR == 2
|
|
73
81
|
end
|
|
74
82
|
|
|
83
|
+
def active_record50?
|
|
84
|
+
active_record5? && ::ActiveRecord::VERSION::MINOR == 0
|
|
85
|
+
end
|
|
86
|
+
|
|
75
87
|
def mongoid2x?
|
|
76
88
|
mongoid? && ::Mongoid::VERSION =~ /\A2\.[4-8]/
|
|
77
89
|
end
|
|
@@ -83,5 +95,9 @@ module Bullet
|
|
|
83
95
|
def mongoid4x?
|
|
84
96
|
mongoid? && ::Mongoid::VERSION =~ /\A4/
|
|
85
97
|
end
|
|
98
|
+
|
|
99
|
+
def mongoid5x?
|
|
100
|
+
mongoid? && ::Mongoid::VERSION =~ /\A5/
|
|
101
|
+
end
|
|
86
102
|
end
|
|
87
103
|
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
|
|
@@ -70,7 +70,7 @@ module Bullet
|
|
|
70
70
|
# associations == v comparision order is important here because
|
|
71
71
|
# v variable might be a squeel node where :== method is redefined,
|
|
72
72
|
# so it does not compare values at all and return unexpected results
|
|
73
|
-
result = v.is_a?(Hash) ? v.
|
|
73
|
+
result = v.is_a?(Hash) ? v.key?(associations) : associations == v
|
|
74
74
|
return true if result
|
|
75
75
|
end
|
|
76
76
|
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
|
|
@@ -48,7 +48,7 @@ module Bullet
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
eager_loadings.add(*to_add) if to_add
|
|
51
|
-
to_merge.each { |k,val| eager_loadings.merge k, val }
|
|
51
|
+
to_merge.each { |k, val| eager_loadings.merge k, val }
|
|
52
52
|
to_delete.each { |k| eager_loadings.delete k }
|
|
53
53
|
|
|
54
54
|
eager_loadings.add bullet_keys, associations unless bullet_keys.empty?
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Bullet
|
|
2
|
+
module Mongoid
|
|
3
|
+
def self.enable
|
|
4
|
+
require 'mongoid'
|
|
5
|
+
::Mongoid::Contextual::Mongo.class_eval do
|
|
6
|
+
alias_method :origin_first, :first
|
|
7
|
+
alias_method :origin_last, :last
|
|
8
|
+
alias_method :origin_each, :each
|
|
9
|
+
alias_method :origin_eager_load, :eager_load
|
|
10
|
+
|
|
11
|
+
def first
|
|
12
|
+
result = origin_first
|
|
13
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
|
14
|
+
result
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def last
|
|
18
|
+
result = origin_last
|
|
19
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
|
20
|
+
result
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def each(&block)
|
|
24
|
+
records = view.map{ |doc| ::Mongoid::Factory.from_db(klass, doc) }
|
|
25
|
+
if records.length > 1
|
|
26
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
27
|
+
elsif records.size == 1
|
|
28
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
29
|
+
end
|
|
30
|
+
origin_each(&block)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def eager_load(docs)
|
|
34
|
+
associations = criteria.inclusions.map(&:name)
|
|
35
|
+
docs.each do |doc|
|
|
36
|
+
Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations)
|
|
37
|
+
end
|
|
38
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
|
|
39
|
+
origin_eager_load(docs)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
::Mongoid::Relations::Accessors.class_eval do
|
|
44
|
+
alias_method :origin_get_relation, :get_relation
|
|
45
|
+
|
|
46
|
+
def get_relation(name, metadata, object, reload = false)
|
|
47
|
+
result = origin_get_relation(name, metadata, object, reload)
|
|
48
|
+
if metadata.macro !~ /embed/
|
|
49
|
+
Bullet::Detector::NPlusOneQuery.call_association(self, name)
|
|
50
|
+
end
|
|
51
|
+
result
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/bullet/version.rb
CHANGED