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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e402a50f9b2a4c75edafcf12bc2bdb32e3298860
4
- data.tar.gz: 73102589e922894098fe54684ac483c8ef6b44b7
3
+ metadata.gz: d5439116cdedcd96dfad6dfd315eaf47ee815153
4
+ data.tar.gz: 9542679b93fa670fbf8b014fb0cbd3adb13a30d8
5
5
  SHA512:
6
- metadata.gz: 03191a02a885f763bbe24986fdad49f5f9e6d19f6f9a4501ff0c5915b1bb3eaf4cebde6b611be5c0a99982eef12ea32e1e3c51ad6a7a8d3029a1d38ea285d7c8
7
- data.tar.gz: dffc316bd5cbacaee45d5ef161bfb63f159a24006d09baf7a5572cfed72a21f970a668c55799fceb2e48705625074fa257bdd0d608a5391c54a5e13e1b354577
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
@@ -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?("load_target") }
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) unless result
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) unless result
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) unless result
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) unless result
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) unless result
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
- if !result && !Thread.current[:bullet_collection_empty]
187
- Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
188
- end
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
@@ -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.has_key?(associations) : associations == 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
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Bullet
3
- VERSION = "4.14.8"
3
+ VERSION = "5.0.0"
4
4
  end