bullet 4.14.3 → 4.14.10

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +28 -1
  3. data/CHANGELOG.md +38 -2
  4. data/Gemfile.mongoid +0 -2
  5. data/Gemfile.mongoid-2.4 +0 -2
  6. data/Gemfile.mongoid-2.5 +0 -2
  7. data/Gemfile.mongoid-2.6 +0 -2
  8. data/Gemfile.mongoid-2.7 +0 -2
  9. data/Gemfile.mongoid-2.8 +0 -2
  10. data/Gemfile.mongoid-3.0 +0 -2
  11. data/Gemfile.mongoid-3.1 +0 -2
  12. data/Gemfile.mongoid-4.0 +0 -2
  13. data/Gemfile.mongoid-5.0 +17 -0
  14. data/Gemfile.rails-3.1 +0 -2
  15. data/Gemfile.rails-3.2 +0 -2
  16. data/Gemfile.rails-4.0 +0 -2
  17. data/Gemfile.rails-4.1 +0 -2
  18. data/Gemfile.rails-4.2 +0 -2
  19. data/README.md +27 -1
  20. data/bullet.gemspec +1 -1
  21. data/lib/bullet/active_record3.rb +65 -35
  22. data/lib/bullet/active_record3x.rb +54 -32
  23. data/lib/bullet/active_record4.rb +61 -31
  24. data/lib/bullet/active_record41.rb +63 -32
  25. data/lib/bullet/active_record42.rb +105 -43
  26. data/lib/bullet/dependency.rb +6 -0
  27. data/lib/bullet/detector/association.rb +16 -16
  28. data/lib/bullet/detector/counter_cache.rb +13 -13
  29. data/lib/bullet/detector/n_plus_one_query.rb +30 -27
  30. data/lib/bullet/detector/unused_eager_loading.rb +1 -1
  31. data/lib/bullet/ext/object.rb +3 -1
  32. data/lib/bullet/mongoid5x.rb +56 -0
  33. data/lib/bullet/notification/n_plus_one_query.rb +6 -0
  34. data/lib/bullet/rack.rb +4 -6
  35. data/lib/bullet/version.rb +1 -1
  36. data/lib/bullet.rb +23 -10
  37. data/spec/bullet/detector/counter_cache_spec.rb +8 -8
  38. data/spec/bullet/detector/n_plus_one_query_spec.rb +27 -27
  39. data/spec/bullet/ext/object_spec.rb +6 -0
  40. data/spec/bullet/notification/base_spec.rb +2 -2
  41. data/spec/bullet/rack_spec.rb +2 -2
  42. data/spec/bullet_spec.rb +47 -0
  43. data/spec/integration/active_record3/association_spec.rb +11 -2
  44. data/spec/integration/active_record4/association_spec.rb +32 -2
  45. data/spec/integration/counter_cache_spec.rb +8 -1
  46. data/spec/spec_helper.rb +1 -0
  47. data/spec/support/mongo_seed.rb +13 -0
  48. data/test.sh +1 -0
  49. data/update.sh +1 -0
  50. metadata +9 -7
@@ -2,19 +2,50 @@ module Bullet
2
2
  module ActiveRecord
3
3
  def self.enable
4
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
+
5
34
  ::ActiveRecord::Relation.class_eval do
6
35
  alias_method :origin_to_a, :to_a
7
36
  # if select a collection of objects, then these objects have possible to cause N+1 query.
8
37
  # if select only one object, then the only one object has impossible to cause N+1 query.
9
38
  def to_a
10
39
  records = origin_to_a
11
- if records.first.class.name !~ /^HABTM_/
12
- if records.size > 1
13
- Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
14
- Bullet::Detector::CounterCache.add_possible_objects(records)
15
- elsif records.size == 1
16
- Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
17
- Bullet::Detector::CounterCache.add_impossible_object(records.first)
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
18
49
  end
19
50
  end
20
51
  records
@@ -25,12 +56,14 @@ module Bullet
25
56
  alias_method :origin_preloaders_on, :preloaders_on
26
57
 
27
58
  def preloaders_on(association, records, scope)
28
- records.compact!
29
- if records.first.class.name !~ /^HABTM_/
30
- records.each do |record|
31
- Bullet::Detector::Association.add_object_associations(record, association)
59
+ if Bullet.start?
60
+ records.compact!
61
+ if records.first.class.name !~ /^HABTM_/
62
+ records.each do |record|
63
+ Bullet::Detector::Association.add_object_associations(record, association)
64
+ end
65
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
32
66
  end
33
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
34
67
  end
35
68
  origin_preloaders_on(association, records, scope)
36
69
  end
@@ -40,12 +73,15 @@ module Bullet
40
73
  # add includes in scope
41
74
  alias_method :origin_find_with_associations, :find_with_associations
42
75
  def find_with_associations
76
+ return origin_find_with_associations { |r| yield r } if block_given?
43
77
  records = origin_find_with_associations
44
- associations = (eager_load_values + includes_values).uniq
45
- records.each do |record|
46
- Bullet::Detector::Association.add_object_associations(record, associations)
78
+ if Bullet.start?
79
+ associations = (eager_load_values + includes_values).uniq
80
+ records.each do |record|
81
+ Bullet::Detector::Association.add_object_associations(record, associations)
82
+ end
83
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
47
84
  end
48
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
49
85
  records
50
86
  end
51
87
  end
@@ -58,9 +94,11 @@ module Bullet
58
94
  @bullet_eager_loadings = {}
59
95
  records = origin_instantiate(result_set, aliases)
60
96
 
61
- @bullet_eager_loadings.each do |klazz, eager_loadings_hash|
62
- objects = eager_loadings_hash.keys
63
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
97
+ if Bullet.start?
98
+ @bullet_eager_loadings.each do |klazz, eager_loadings_hash|
99
+ objects = eager_loadings_hash.keys
100
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
101
+ end
64
102
  end
65
103
  records
66
104
  end
@@ -69,12 +107,14 @@ module Bullet
69
107
  def construct_model(record, node, row, model_cache, id, aliases)
70
108
  result = origin_construct_model(record, node, row, model_cache, id, aliases)
71
109
 
72
- associations = node.reflection.name
73
- Bullet::Detector::Association.add_object_associations(record, associations)
74
- Bullet::Detector::NPlusOneQuery.call_association(record, associations)
75
- @bullet_eager_loadings[record.class] ||= {}
76
- @bullet_eager_loadings[record.class][record] ||= Set.new
77
- @bullet_eager_loadings[record.class][record] << associations
110
+ if Bullet.start?
111
+ associations = node.reflection.name
112
+ Bullet::Detector::Association.add_object_associations(record, associations)
113
+ Bullet::Detector::NPlusOneQuery.call_association(record, associations)
114
+ @bullet_eager_loadings[record.class] ||= {}
115
+ @bullet_eager_loadings[record.class][record] ||= Set.new
116
+ @bullet_eager_loadings[record.class][record] << associations
117
+ end
78
118
 
79
119
  result
80
120
  end
@@ -84,16 +124,18 @@ module Bullet
84
124
  # call one to many associations
85
125
  alias_method :origin_load_target, :load_target
86
126
  def load_target
87
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
88
127
  records = origin_load_target
89
128
 
90
- if records.first.class.name !~ /^HABTM_/
91
- if records.size > 1
92
- Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
93
- Bullet::Detector::CounterCache.add_possible_objects(records)
94
- elsif records.size == 1
95
- Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
96
- Bullet::Detector::CounterCache.add_impossible_object(records.first)
129
+ if Bullet.start?
130
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
131
+ if records.first.class.name !~ /^HABTM_/
132
+ if records.size > 1
133
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
134
+ Bullet::Detector::CounterCache.add_possible_objects(records)
135
+ elsif records.size == 1
136
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
137
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
138
+ end
97
139
  end
98
140
  end
99
141
  records
@@ -101,13 +143,17 @@ module Bullet
101
143
 
102
144
  alias_method :origin_empty?, :empty?
103
145
  def empty?
104
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
146
+ if Bullet.start?
147
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
148
+ end
105
149
  origin_empty?
106
150
  end
107
151
 
108
152
  alias_method :origin_include?, :include?
109
153
  def include?(object)
110
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
154
+ if Bullet.start?
155
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
156
+ end
111
157
  origin_include?(object)
112
158
  end
113
159
  end
@@ -117,12 +163,15 @@ module Bullet
117
163
  alias_method :origin_reader, :reader
118
164
  def reader(force_reload = false)
119
165
  result = origin_reader(force_reload)
120
- if @owner.class.name !~ /^HABTM_/ && !@inversed
121
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
122
- Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
123
- end
124
- if ::ActiveRecord::Reflection::HasOneReflection === @reflection && result
125
- Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
166
+ if Bullet.start?
167
+ if @owner.class.name !~ /^HABTM_/ && !@inversed
168
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
169
+ if Bullet::Detector::NPlusOneQuery.impossible?(@owner)
170
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
171
+ else
172
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
173
+ end
174
+ end
126
175
  end
127
176
  result
128
177
  end
@@ -134,14 +183,27 @@ module Bullet
134
183
  Thread.current[:bullet_collection_empty] = true
135
184
  result = origin_many_empty?
136
185
  Thread.current[:bullet_collection_empty] = nil
137
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
186
+ if Bullet.start?
187
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
188
+ end
138
189
  result
139
190
  end
140
191
 
141
192
  alias_method :origin_has_cached_counter?, :has_cached_counter?
142
193
  def has_cached_counter?(reflection = reflection())
143
194
  result = origin_has_cached_counter?(reflection)
144
- if !result && !Thread.current[:bullet_collection_empty]
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]
145
207
  Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
146
208
  end
147
209
  result
@@ -36,6 +36,8 @@ module Bullet
36
36
  'mongoid3x'
37
37
  elsif mongoid4x?
38
38
  'mongoid4x'
39
+ elsif mongoid5x?
40
+ 'mongoid5x'
39
41
  end
40
42
  end
41
43
  end
@@ -83,5 +85,9 @@ module Bullet
83
85
  def mongoid4x?
84
86
  mongoid? && ::Mongoid::VERSION =~ /\A4/
85
87
  end
88
+
89
+ def mongoid5x?
90
+ mongoid? && ::Mongoid::VERSION =~ /\A5/
91
+ end
86
92
  end
87
93
  end
@@ -20,6 +20,22 @@ module Bullet
20
20
  call_object_associations.add(object.bullet_key, associations)
21
21
  end
22
22
 
23
+ # possible_objects keep the class to object relationships
24
+ # that the objects may cause N+1 query.
25
+ # e.g. { Post => ["Post:1", "Post:2"] }
26
+ def possible_objects
27
+ Thread.current[:bullet_possible_objects]
28
+ end
29
+
30
+ # impossible_objects keep the class to objects relationships
31
+ # that the objects may not cause N+1 query.
32
+ # e.g. { Post => ["Post:1", "Post:2"] }
33
+ # if find collection returns only one object, then the object is impossible object,
34
+ # impossible_objects are used to avoid treating 1+1 query to N+1 query.
35
+ def impossible_objects
36
+ Thread.current[:bullet_impossible_objects]
37
+ end
38
+
23
39
  private
24
40
  # object_associations keep the object relationships
25
41
  # that the object has many associations.
@@ -38,22 +54,6 @@ module Bullet
38
54
  Thread.current[:bullet_call_object_associations]
39
55
  end
40
56
 
41
- # possible_objects keep the class to object relationships
42
- # that the objects may cause N+1 query.
43
- # e.g. { Post => ["Post:1", "Post:2"] }
44
- def possible_objects
45
- Thread.current[:bullet_possible_objects]
46
- end
47
-
48
- # impossible_objects keep the class to objects relationships
49
- # that the objects may not cause N+1 query.
50
- # e.g. { Post => ["Post:1", "Post:2"] }
51
- # if find collection returns only one object, then the object is impossible object,
52
- # impossible_objects are used to avoid treating 1+1 query to N+1 query.
53
- def impossible_objects
54
- Thread.current[:bullet_impossible_objects]
55
- end
56
-
57
57
  # inversed_objects keeps object relationships
58
58
  # that association is inversed.
59
59
  # e.g. { "Comment:1" => ["post"] }
@@ -8,7 +8,7 @@ module Bullet
8
8
  return unless object.primary_key_value
9
9
 
10
10
  Bullet.debug("Detector::CounterCache#add_counter_cache", "object: #{object.bullet_key}, associations: #{associations}")
11
- if conditions_met?(object.bullet_key, associations)
11
+ if conditions_met?(object, associations)
12
12
  create_notification object.class.to_s, associations
13
13
  end
14
14
  end
@@ -32,6 +32,18 @@ module Bullet
32
32
  impossible_objects.add object.bullet_key
33
33
  end
34
34
 
35
+ def conditions_met?(object, associations)
36
+ possible_objects.include?(object.bullet_key) && !impossible_objects.include?(object.bullet_key)
37
+ end
38
+
39
+ def possible_objects
40
+ Thread.current[:bullet_counter_possible_objects]
41
+ end
42
+
43
+ def impossible_objects
44
+ Thread.current[:bullet_counter_impossible_objects]
45
+ end
46
+
35
47
  private
36
48
  def create_notification(klazz, associations)
37
49
  notify_associations = Array(associations) - Bullet.get_whitelist_associations(:counter_cache, klazz)
@@ -41,18 +53,6 @@ module Bullet
41
53
  Bullet.notification_collector.add notice
42
54
  end
43
55
  end
44
-
45
- def possible_objects
46
- Thread.current[:bullet_counter_possible_objects]
47
- end
48
-
49
- def impossible_objects
50
- Thread.current[:bullet_counter_impossible_objects]
51
- end
52
-
53
- def conditions_met?(bullet_key, associations)
54
- possible_objects.include?(bullet_key) && !impossible_objects.include?(bullet_key)
55
- end
56
56
  end
57
57
  end
58
58
  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.bullet_key, associations)
18
+ if 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
@@ -49,6 +49,35 @@ module Bullet
49
49
  inversed_objects.add object.bullet_key, association
50
50
  end
51
51
 
52
+ # decide whether the object.associations is unpreloaded or not.
53
+ def conditions_met?(object, associations)
54
+ possible?(object) && !impossible?(object) && !association?(object, associations)
55
+ end
56
+
57
+ def possible?(object)
58
+ possible_objects.include? object.bullet_key
59
+ end
60
+
61
+ def impossible?(object)
62
+ impossible_objects.include? object.bullet_key
63
+ end
64
+
65
+ # check if object => associations already exists in object_associations.
66
+ def association?(object, associations)
67
+ value = object_associations[object.bullet_key]
68
+ if value
69
+ value.each do |v|
70
+ # associations == v comparision order is important here because
71
+ # v variable might be a squeel node where :== method is redefined,
72
+ # so it does not compare values at all and return unexpected results
73
+ result = v.is_a?(Hash) ? v.key?(associations) : associations == v
74
+ return true if result
75
+ end
76
+ end
77
+
78
+ false
79
+ end
80
+
52
81
  private
53
82
  def create_notification(callers, klazz, associations)
54
83
  notify_associations = Array(associations) - Bullet.get_whitelist_associations(:n_plus_one_query, klazz)
@@ -59,11 +88,6 @@ module Bullet
59
88
  end
60
89
  end
61
90
 
62
- # decide whether the object.associations is unpreloaded or not.
63
- def conditions_met?(bullet_key, associations)
64
- possible?(bullet_key) && !impossible?(bullet_key) && !association?(bullet_key, associations)
65
- end
66
-
67
91
  def caller_in_project
68
92
  app_root = rails? ? Rails.root.to_s : Dir.pwd
69
93
  vendor_root = app_root + "/vendor"
@@ -72,27 +96,6 @@ module Bullet
72
96
  Bullet.stacktrace_includes.any? { |include| c.include?(include) }
73
97
  end
74
98
  end
75
-
76
- def possible?(bullet_key)
77
- possible_objects.include? bullet_key
78
- end
79
-
80
- def impossible?(bullet_key)
81
- impossible_objects.include? bullet_key
82
- end
83
-
84
- # check if object => associations already exists in object_associations.
85
- def association?(bullet_key, associations)
86
- value = object_associations[bullet_key]
87
- if value
88
- value.each do |v|
89
- result = v.is_a?(Hash) ? v.has_key?(associations) : v == associations
90
- return true if result
91
- end
92
- end
93
-
94
- return false
95
- end
96
99
  end
97
100
  end
98
101
  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?
@@ -4,7 +4,9 @@ class Object
4
4
  end
5
5
 
6
6
  def primary_key_value
7
- if self.class.respond_to?(:primary_key) && self.class.primary_key
7
+ if self.class.respond_to?(:primary_keys) && self.class.primary_keys
8
+ self.class.primary_keys.map { |primary_key| self.send primary_key }.join(',')
9
+ elsif self.class.respond_to?(:primary_key) && self.class.primary_key
8
10
  self.send self.class.primary_key
9
11
  else
10
12
  self.id
@@ -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
@@ -15,6 +15,12 @@ module Bullet
15
15
  "N+1 Query #{@path ? "in #{@path}" : 'detected'}"
16
16
  end
17
17
 
18
+ def notification_data
19
+ super.merge(
20
+ :backtrace => @callers
21
+ )
22
+ end
23
+
18
24
  protected
19
25
  def call_stack_messages
20
26
  (['N+1 Query method call stack'] + @callers).join( "\n " )
data/lib/bullet/rack.rb CHANGED
@@ -10,18 +10,16 @@ module Bullet
10
10
  return @app.call(env) unless Bullet.enable?
11
11
  Bullet.start_request
12
12
  status, headers, response = @app.call(env)
13
- return [status, headers, response] if file?(headers) || sse?(response) || empty?(response)
14
13
 
15
14
  response_body = nil
16
15
  if Bullet.notification?
17
- if status == 200 && !response_body(response).frozen? && html_request?(headers, response)
16
+ if !file?(headers) && !sse?(headers) && !empty?(response) &&
17
+ status == 200 && !response_body(response).frozen? && html_request?(headers, response)
18
18
  response_body = response_body(response)
19
19
  append_to_html_body(response_body, footer_note) if Bullet.add_footer
20
20
  append_to_html_body(response_body, Bullet.gather_inline_notifications)
21
21
  headers['Content-Length'] = response_body.bytesize.to_s
22
22
  end
23
- end
24
- if Bullet.enable? && Bullet.notification?
25
23
  Bullet.perform_out_of_channel_notifications(env)
26
24
  end
27
25
  [status, headers, response_body ? [response_body] : response]
@@ -60,8 +58,8 @@ module Bullet
60
58
  headers["Content-Transfer-Encoding"] == "binary"
61
59
  end
62
60
 
63
- def sse?(response)
64
- response.respond_to?(:stream) && response.stream.is_a?(ActionController::Live::Buffer)
61
+ def sse?(headers)
62
+ headers["Content-Type"] == "text/event-stream"
65
63
  end
66
64
 
67
65
  def html_request?(headers, response)
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Bullet
3
- VERSION = "4.14.3"
3
+ VERSION = "4.14.10"
4
4
  end
data/lib/bullet.rb CHANGED
@@ -16,6 +16,9 @@ module Bullet
16
16
  autoload :Registry, 'bullet/registry'
17
17
  autoload :NotificationCollector, 'bullet/notification_collector'
18
18
 
19
+ BULLET_DEBUG = 'BULLET_DEBUG'.freeze
20
+ TRUE = 'true'.freeze
21
+
19
22
  if defined? Rails::Railtie
20
23
  class BulletRailtie < Rails::Railtie
21
24
  initializer "bullet.configure_rails_initialization" do |app|
@@ -29,7 +32,9 @@ module Bullet
29
32
  attr_reader :notification_collector, :whitelist
30
33
  attr_accessor :add_footer, :orm_pathches_applied
31
34
 
32
- delegate :alert=, :console=, :growl=, :rails_logger=, :xmpp=, :airbrake=, :bugsnag=, :to => UniformNotifier
35
+ available_notifiers = UniformNotifier::AVAILABLE_NOTIFIERS.map { |notifier| "#{notifier}=" }
36
+ available_notifiers << { :to => UniformNotifier }
37
+ delegate *available_notifiers
33
38
 
34
39
  def raise=(should_raise)
35
40
  UniformNotifier.raise=(should_raise ? Notification::UnoptimizedQueryError : false)
@@ -72,6 +77,7 @@ module Bullet
72
77
  end
73
78
 
74
79
  def add_whitelist(options)
80
+ reset_whitelist
75
81
  @whitelist[options[:type]][options[:class_name].classify] ||= []
76
82
  @whitelist[options[:type]][options[:class_name].classify] << options[:association].to_sym
77
83
  end
@@ -81,7 +87,11 @@ module Bullet
81
87
  end
82
88
 
83
89
  def reset_whitelist
84
- @whitelist = {:n_plus_one_query => {}, :unused_eager_loading => {}, :counter_cache => {}}
90
+ @whitelist ||= {:n_plus_one_query => {}, :unused_eager_loading => {}, :counter_cache => {}}
91
+ end
92
+
93
+ def clear_whitelist
94
+ @whitelist = nil
85
95
  end
86
96
 
87
97
  def bullet_logger=(active)
@@ -96,7 +106,7 @@ module Bullet
96
106
  end
97
107
 
98
108
  def debug(title, message)
99
- puts "[Bullet][#{title}] #{message}" if ENV['BULLET_DEBUG'] == 'true'
109
+ puts "[Bullet][#{title}] #{message}" if ENV[BULLET_DEBUG] == TRUE
100
110
  end
101
111
 
102
112
  def start_request
@@ -130,7 +140,7 @@ module Bullet
130
140
  end
131
141
 
132
142
  def start?
133
- Thread.current[:bullet_start]
143
+ enable? && Thread.current[:bullet_start]
134
144
  end
135
145
 
136
146
  def notification_collector
@@ -160,7 +170,7 @@ module Bullet
160
170
 
161
171
  def footer_info
162
172
  info = []
163
- for_each_active_notifier_with_notification do |notification|
173
+ notification_collector.collection.each do |notification|
164
174
  info << notification.short_notice
165
175
  end
166
176
  info
@@ -176,14 +186,17 @@ module Bullet
176
186
  end
177
187
 
178
188
  def profile
179
- Bullet.start_request if Bullet.enable?
189
+ if Bullet.enable?
190
+ begin
191
+ Bullet.start_request
180
192
 
181
- yield
193
+ yield
182
194
 
183
- if Bullet.enable? && Bullet.notification?
184
- Bullet.perform_out_of_channel_notifications
195
+ Bullet.perform_out_of_channel_notifications if Bullet.notification?
196
+ ensure
197
+ Bullet.end_request
198
+ end
185
199
  end
186
- Bullet.end_request if Bullet.enable?
187
200
  end
188
201
 
189
202
  private