bullet 4.4.0 → 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -30,18 +30,19 @@ module Bullet
30
30
  end
31
31
 
32
32
  class <<self
33
- attr_accessor :enable
34
- attr_reader :notification_collector
33
+ attr_writer :enable, :n_plus_one_query_enable, :unused_eager_loading_enable, :counter_cache_enable
34
+ attr_reader :notification_collector, :whitelist
35
35
 
36
36
  delegate :alert=, :console=, :growl=, :rails_logger=, :xmpp=, :airbrake=, :to => UniformNotifier
37
37
 
38
38
  DETECTORS = [ Bullet::Detector::NPlusOneQuery,
39
- Bullet::Detector::UnusedEagerAssociation,
40
- Bullet::Detector::Counter ]
39
+ Bullet::Detector::UnusedEagerLoading,
40
+ Bullet::Detector::CounterCache ]
41
41
 
42
42
  def enable=(enable)
43
- @enable = enable
43
+ @enable = @n_plus_one_query_enable = @unused_eager_loading_enable = @counter_cache_enable = enable
44
44
  if enable?
45
+ reset_whitelist
45
46
  if mongoid?
46
47
  Bullet::Mongoid.enable
47
48
  end
@@ -53,7 +54,32 @@ module Bullet
53
54
  end
54
55
 
55
56
  def enable?
56
- @enable == true
57
+ !!@enable
58
+ end
59
+
60
+ def n_plus_one_query_enable?
61
+ self.enable? && !!@n_plus_one_query_enable
62
+ end
63
+
64
+ def unused_eager_loading_enable?
65
+ self.enable? && !!@unused_eager_loading_enable
66
+ end
67
+
68
+ def counter_cache_enable?
69
+ self.enable? && !!@counter_cache_enable
70
+ end
71
+
72
+ def add_whitelist(options)
73
+ @whitelist[options[:type]][options[:class_name].classify] ||= []
74
+ @whitelist[options[:type]][options[:class_name].classify] << options[:association].to_s.tableize.to_sym
75
+ end
76
+
77
+ def get_whitelist_associations(type, class_name)
78
+ Array(@whitelist[type][class_name])
79
+ end
80
+
81
+ def reset_whitelist
82
+ @whitelist = {:n_plus_one_query => {}, :unused_eager_loading => {}, :counter_cache => {}}
57
83
  end
58
84
 
59
85
  def bullet_logger=(active)
@@ -82,7 +108,7 @@ module Bullet
82
108
  end
83
109
 
84
110
  def notification?
85
- Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
111
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
86
112
  notification_collector.notifications_present?
87
113
  end
88
114
 
@@ -101,6 +127,15 @@ module Bullet
101
127
  end
102
128
  end
103
129
 
130
+ def warnings
131
+ notification_collector.collection.inject({}) do |warnings, notification|
132
+ warning_type = notification.class.to_s.split(':').last.tableize
133
+ warnings[warning_type] ||= []
134
+ warnings[warning_type] << notification
135
+ warnings
136
+ end
137
+ end
138
+
104
139
  private
105
140
  def for_each_active_notifier_with_notification
106
141
  UniformNotifier.active_notifiers.each do |notifier|
@@ -12,11 +12,11 @@ module Bullet
12
12
 
13
13
  if records
14
14
  if records.size > 1
15
- Bullet::Detector::Association.add_possible_objects(records)
16
- Bullet::Detector::Counter.add_possible_objects(records)
15
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
16
+ Bullet::Detector::CounterCache.add_possible_objects(records)
17
17
  elsif records.size == 1
18
- Bullet::Detector::Association.add_impossible_object(records.first)
19
- Bullet::Detector::Counter.add_impossible_object(records.first)
18
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
19
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
20
20
  end
21
21
  end
22
22
 
@@ -35,7 +35,7 @@ module Bullet
35
35
  records.each do |record|
36
36
  Bullet::Detector::Association.add_object_associations(record, associations)
37
37
  end
38
- Bullet::Detector::Association.add_eager_loadings(records, associations)
38
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
39
39
  origin_preload_associations(records, associations, preload_options={})
40
40
  end
41
41
  end
@@ -50,7 +50,7 @@ module Bullet
50
50
  Bullet::Detector::Association.add_object_associations(record, associations)
51
51
  Bullet::Detector::NPlusOneQuery.call_association(record, associations)
52
52
  end
53
- Bullet::Detector::Association.add_eager_loadings(records, associations)
53
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
54
54
  records
55
55
  end
56
56
  end
@@ -94,7 +94,7 @@ module Bullet
94
94
  # avoid stack level too deep
95
95
  result = origin_load_target
96
96
  Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless caller.any? {|c| c.include?("load_target") }
97
- Bullet::Detector::Association.add_possible_objects(result)
97
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
98
98
  result
99
99
  end
100
100
  end
@@ -103,7 +103,7 @@ module Bullet
103
103
  alias_method :origin_has_cached_counter?, :has_cached_counter?
104
104
  def has_cached_counter?
105
105
  result = origin_has_cached_counter?
106
- Bullet::Detector::Counter.add_counter_cache(@owner, @reflection.name) unless result
106
+ Bullet::Detector::CounterCache.add_counter_cache(@owner, @reflection.name) unless result
107
107
  result
108
108
  end
109
109
  end
@@ -112,7 +112,7 @@ module Bullet
112
112
  alias_method :origin_has_cached_counter?, :has_cached_counter?
113
113
  def has_cached_counter?
114
114
  result = origin_has_cached_counter?
115
- Bullet::Detector::Counter.add_counter_cache(@owner, @reflection.name) unless result
115
+ Bullet::Detector::CounterCache.add_counter_cache(@owner, @reflection.name) unless result
116
116
  result
117
117
  end
118
118
  end
@@ -9,11 +9,11 @@ module Bullet
9
9
  def to_a
10
10
  records = origin_to_a
11
11
  if records.size > 1
12
- Bullet::Detector::Association.add_possible_objects(records)
13
- Bullet::Detector::Counter.add_possible_objects(records)
12
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
13
+ Bullet::Detector::CounterCache.add_possible_objects(records)
14
14
  elsif records.size == 1
15
- Bullet::Detector::Association.add_impossible_object(records.first)
16
- Bullet::Detector::Counter.add_impossible_object(records.first)
15
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
16
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
17
17
  end
18
18
  records
19
19
  end
@@ -29,7 +29,7 @@ module Bullet
29
29
  records.each do |record|
30
30
  Bullet::Detector::Association.add_object_associations(record, associations)
31
31
  end
32
- Bullet::Detector::Association.add_eager_loadings(records, associations)
32
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
33
33
  origin_preload_associations(records, associations, preload_options={})
34
34
  end
35
35
  end
@@ -44,7 +44,7 @@ module Bullet
44
44
  Bullet::Detector::Association.add_object_associations(record, associations)
45
45
  Bullet::Detector::NPlusOneQuery.call_association(record, associations)
46
46
  end
47
- Bullet::Detector::Association.add_eager_loadings(records, associations)
47
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
48
48
  records
49
49
  end
50
50
  end
@@ -88,7 +88,7 @@ module Bullet
88
88
  # avoid stack level too deep
89
89
  result = origin_load_target
90
90
  Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless caller.any? { |c| c.include?("load_target") }
91
- Bullet::Detector::Association.add_possible_objects(result)
91
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
92
92
  result
93
93
  end
94
94
  end
@@ -98,7 +98,7 @@ module Bullet
98
98
 
99
99
  def has_cached_counter?
100
100
  result = origin_has_cached_counter?
101
- Bullet::Detector::Counter.add_counter_cache(@owner, @reflection.name) unless result
101
+ Bullet::Detector::CounterCache.add_counter_cache(@owner, @reflection.name) unless result
102
102
  result
103
103
  end
104
104
  end
@@ -107,7 +107,7 @@ module Bullet
107
107
  alias_method :origin_has_cached_counter?, :has_cached_counter?
108
108
  def has_cached_counter?
109
109
  result = origin_has_cached_counter?
110
- Bullet::Detector::Counter.add_counter_cache(@owner, @reflection.name) unless result
110
+ Bullet::Detector::CounterCache.add_counter_cache(@owner, @reflection.name) unless result
111
111
  result
112
112
  end
113
113
  end
@@ -9,11 +9,11 @@ module Bullet
9
9
  def to_a
10
10
  records = origin_to_a
11
11
  if records.size > 1
12
- Bullet::Detector::Association.add_possible_objects(records)
13
- Bullet::Detector::Counter.add_possible_objects(records)
12
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
13
+ Bullet::Detector::CounterCache.add_possible_objects(records)
14
14
  elsif records.size == 1
15
- Bullet::Detector::Association.add_impossible_object(records.first)
16
- Bullet::Detector::Counter.add_impossible_object(records.first)
15
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
16
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
17
17
  end
18
18
  records
19
19
  end
@@ -30,7 +30,7 @@ module Bullet
30
30
  records.each do |record|
31
31
  Bullet::Detector::Association.add_object_associations(record, associations)
32
32
  end
33
- Bullet::Detector::Association.add_eager_loadings(records, associations)
33
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
34
34
  end
35
35
  end
36
36
 
@@ -44,7 +44,7 @@ module Bullet
44
44
  Bullet::Detector::Association.add_object_associations(record, associations)
45
45
  Bullet::Detector::NPlusOneQuery.call_association(record, associations)
46
46
  end
47
- Bullet::Detector::Association.add_eager_loadings(records, associations)
47
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
48
48
  records
49
49
  end
50
50
  end
@@ -75,7 +75,7 @@ module Bullet
75
75
  def reader(force_reload = false)
76
76
  result = origin_reader(force_reload)
77
77
  Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
78
- Bullet::Detector::Association.add_possible_objects(result)
78
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
79
79
  result
80
80
  end
81
81
  end
@@ -85,7 +85,7 @@ module Bullet
85
85
 
86
86
  def has_cached_counter?(reflection = reflection)
87
87
  result = origin_has_cached_counter?(reflection)
88
- Bullet::Detector::Counter.add_counter_cache(owner, reflection.name) unless result
88
+ Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name) unless result
89
89
  result
90
90
  end
91
91
  end
@@ -9,11 +9,11 @@ module Bullet
9
9
  def to_a
10
10
  records = origin_to_a
11
11
  if records.size > 1
12
- Bullet::Detector::Association.add_possible_objects(records)
13
- Bullet::Detector::Counter.add_possible_objects(records)
12
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
13
+ Bullet::Detector::CounterCache.add_possible_objects(records)
14
14
  elsif records.size == 1
15
- Bullet::Detector::Association.add_impossible_object(records.first)
16
- Bullet::Detector::Counter.add_impossible_object(records.first)
15
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
16
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
17
17
  end
18
18
  records
19
19
  end
@@ -30,7 +30,7 @@ module Bullet
30
30
  records.each do |record|
31
31
  Bullet::Detector::Association.add_object_associations(record, associations)
32
32
  end
33
- Bullet::Detector::Association.add_eager_loadings(records, associations)
33
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
34
34
  end
35
35
  end
36
36
 
@@ -44,7 +44,7 @@ module Bullet
44
44
  Bullet::Detector::Association.add_object_associations(record, associations)
45
45
  Bullet::Detector::NPlusOneQuery.call_association(record, associations)
46
46
  end
47
- Bullet::Detector::Association.add_eager_loadings(records, associations)
47
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
48
48
  records
49
49
  end
50
50
  end
@@ -75,7 +75,7 @@ module Bullet
75
75
  def reader(force_reload = false)
76
76
  result = origin_reader(force_reload)
77
77
  Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
78
- Bullet::Detector::Association.add_possible_objects(result)
78
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
79
79
  result
80
80
  end
81
81
  end
@@ -85,7 +85,7 @@ module Bullet
85
85
 
86
86
  def has_cached_counter?(reflection = reflection)
87
87
  result = origin_has_cached_counter?(reflection)
88
- Bullet::Detector::Counter.add_counter_cache(owner, reflection.name) unless result
88
+ Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name) unless result
89
89
  result
90
90
  end
91
91
  end
@@ -3,7 +3,7 @@ module Bullet
3
3
  autoload :Base, 'bullet/detector/base'
4
4
  autoload :Association, 'bullet/detector/association'
5
5
  autoload :NPlusOneQuery, 'bullet/detector/n_plus_one_query'
6
- autoload :UnusedEagerAssociation, 'bullet/detector/unused_eager_association'
7
- autoload :Counter, 'bullet/detector/counter'
6
+ autoload :UnusedEagerLoading, 'bullet/detector/unused_eager_loading'
7
+ autoload :CounterCache, 'bullet/detector/counter_cache'
8
8
  end
9
9
  end
@@ -20,56 +20,15 @@ module Bullet
20
20
  end
21
21
 
22
22
  def add_object_associations(object, associations)
23
+ return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
23
24
  object_associations.add(object.bullet_ar_key, associations) if object.id
24
25
  end
25
26
 
26
27
  def add_call_object_associations(object, associations)
28
+ return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
27
29
  call_object_associations.add(object.bullet_ar_key, associations) if object.id
28
30
  end
29
31
 
30
- def add_possible_objects(object_or_objects)
31
- return unless object_or_objects
32
- if object_or_objects.is_a? Array
33
- object_or_objects.each { |object| possible_objects.add object.bullet_ar_key }
34
- else
35
- possible_objects.add object_or_objects.bullet_ar_key if object_or_objects.id
36
- end
37
- end
38
-
39
- def add_impossible_object(object)
40
- impossible_objects.add object.bullet_ar_key if object.id
41
- end
42
-
43
- def add_eager_loadings(objects, associations)
44
- bullet_ar_keys = objects.map(&:bullet_ar_key)
45
-
46
- to_add = nil
47
- to_merge, to_delete = [], []
48
- eager_loadings.each do |k, v|
49
- key_objects_overlap = k & bullet_ar_keys
50
-
51
- next if key_objects_overlap.empty?
52
-
53
- if key_objects_overlap == k
54
- to_add = [k, associations]
55
- break
56
- else
57
- to_merge << [key_objects_overlap, ( eager_loadings[k].dup << associations )]
58
-
59
- keys_without_objects = k - bullet_ar_keys
60
- to_merge << [keys_without_objects, eager_loadings[k]]
61
- to_delete << k
62
- bullet_ar_keys = bullet_ar_keys - k
63
- end
64
- end
65
-
66
- eager_loadings.add *to_add if to_add
67
- to_merge.each { |k,val| eager_loadings.merge k, val }
68
- to_delete.each { |k| eager_loadings.delete k }
69
-
70
- eager_loadings.add bullet_ar_keys, associations unless bullet_ar_keys.empty?
71
- end
72
-
73
32
  private
74
33
  # object_associations keep the object relationships
75
34
  # that the object has many associations.
@@ -1,6 +1,6 @@
1
1
  module Bullet
2
2
  module Detector
3
- class Counter < Base
3
+ class CounterCache < Base
4
4
  class <<self
5
5
  def clear
6
6
  @@possible_objects = nil
@@ -8,12 +8,16 @@ module Bullet
8
8
  end
9
9
 
10
10
  def add_counter_cache(object, associations)
11
+ return unless Bullet.counter_cache_enable?
12
+
11
13
  if conditions_met?(object.bullet_ar_key, associations)
12
14
  create_notification object.class.to_s, associations
13
15
  end
14
16
  end
15
17
 
16
18
  def add_possible_objects(object_or_objects)
19
+ return unless Bullet.counter_cache_enable?
20
+
17
21
  if object_or_objects.is_a? Array
18
22
  object_or_objects.each { |object| possible_objects.add object.bullet_ar_key }
19
23
  else
@@ -22,13 +26,19 @@ module Bullet
22
26
  end
23
27
 
24
28
  def add_impossible_object(object)
29
+ return unless Bullet.counter_cache_enable?
30
+
25
31
  impossible_objects.add object.bullet_ar_key
26
32
  end
27
33
 
28
34
  private
29
35
  def create_notification(klazz, associations)
30
- notice = Bullet::Notification::CounterCache.new klazz, associations
31
- Bullet.notification_collector.add notice
36
+ notify_associations = Array(associations) - Bullet.get_whitelist_associations(:counter_cache, klazz)
37
+
38
+ if notify_associations.present?
39
+ notice = Bullet::Notification::CounterCache.new klazz, notify_associations
40
+ Bullet.notification_collector.add notice
41
+ end
32
42
  end
33
43
 
34
44
  def possible_objects
@@ -15,10 +15,29 @@ module Bullet
15
15
  end
16
16
  end
17
17
 
18
+ def add_possible_objects(object_or_objects)
19
+ return unless Bullet.n_plus_one_query_enable?
20
+ return if object_or_objects.blank?
21
+ if object_or_objects.is_a? Array
22
+ object_or_objects.each { |object| possible_objects.add object.bullet_ar_key }
23
+ else
24
+ possible_objects.add object_or_objects.bullet_ar_key if object_or_objects.id
25
+ end
26
+ end
27
+
28
+ def add_impossible_object(object)
29
+ return unless Bullet.n_plus_one_query_enable?
30
+ impossible_objects.add object.bullet_ar_key if object.id
31
+ end
32
+
18
33
  private
19
34
  def create_notification(callers, klazz, associations)
20
- notice = Bullet::Notification::NPlusOneQuery.new(callers, klazz, associations)
21
- Bullet.notification_collector.add(notice)
35
+ notify_associations = Array(associations) - Bullet.get_whitelist_associations(:n_plus_one_query, klazz)
36
+
37
+ if notify_associations.present?
38
+ notice = Bullet::Notification::NPlusOneQuery.new(callers, klazz, notify_associations)
39
+ Bullet.notification_collector.add(notice)
40
+ end
22
41
  end
23
42
 
24
43
  # decide whether the object.associations is unpreloaded or not.