bullet_instructure 4.0.2

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 (118) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +20 -0
  6. data/CHANGELOG.md +75 -0
  7. data/Gemfile +19 -0
  8. data/Gemfile.mongoid +14 -0
  9. data/Gemfile.mongoid-2.4 +19 -0
  10. data/Gemfile.mongoid-2.5 +19 -0
  11. data/Gemfile.mongoid-2.6 +19 -0
  12. data/Gemfile.mongoid-2.7 +19 -0
  13. data/Gemfile.mongoid-2.8 +19 -0
  14. data/Gemfile.mongoid-3.0 +19 -0
  15. data/Gemfile.mongoid-3.1 +19 -0
  16. data/Gemfile.mongoid-4.0 +19 -0
  17. data/Gemfile.rails-3.0 +19 -0
  18. data/Gemfile.rails-3.1 +19 -0
  19. data/Gemfile.rails-3.2 +19 -0
  20. data/Gemfile.rails-4.0 +19 -0
  21. data/Gemfile.rails-4.1 +19 -0
  22. data/Guardfile +8 -0
  23. data/Hacking.md +74 -0
  24. data/MIT-LICENSE +20 -0
  25. data/README.md +428 -0
  26. data/Rakefile +52 -0
  27. data/bullet_instructure.gemspec +27 -0
  28. data/lib/bullet.rb +196 -0
  29. data/lib/bullet/active_record3.rb +148 -0
  30. data/lib/bullet/active_record3x.rb +128 -0
  31. data/lib/bullet/active_record4.rb +128 -0
  32. data/lib/bullet/active_record41.rb +121 -0
  33. data/lib/bullet/dependency.rb +81 -0
  34. data/lib/bullet/detector.rb +9 -0
  35. data/lib/bullet/detector/association.rb +67 -0
  36. data/lib/bullet/detector/base.rb +6 -0
  37. data/lib/bullet/detector/counter_cache.rb +59 -0
  38. data/lib/bullet/detector/n_plus_one_query.rb +89 -0
  39. data/lib/bullet/detector/unused_eager_loading.rb +84 -0
  40. data/lib/bullet/ext/object.rb +9 -0
  41. data/lib/bullet/ext/string.rb +5 -0
  42. data/lib/bullet/mongoid2x.rb +56 -0
  43. data/lib/bullet/mongoid3x.rb +56 -0
  44. data/lib/bullet/mongoid4x.rb +56 -0
  45. data/lib/bullet/notification.rb +10 -0
  46. data/lib/bullet/notification/base.rb +97 -0
  47. data/lib/bullet/notification/counter_cache.rb +13 -0
  48. data/lib/bullet/notification/n_plus_one_query.rb +28 -0
  49. data/lib/bullet/notification/unused_eager_loading.rb +13 -0
  50. data/lib/bullet/notification_collector.rb +24 -0
  51. data/lib/bullet/rack.rb +81 -0
  52. data/lib/bullet/registry.rb +7 -0
  53. data/lib/bullet/registry/association.rb +13 -0
  54. data/lib/bullet/registry/base.rb +40 -0
  55. data/lib/bullet/registry/object.rb +13 -0
  56. data/lib/bullet/version.rb +4 -0
  57. data/perf/benchmark.rb +121 -0
  58. data/rails/init.rb +1 -0
  59. data/spec/bullet/detector/association_spec.rb +26 -0
  60. data/spec/bullet/detector/base_spec.rb +8 -0
  61. data/spec/bullet/detector/counter_cache_spec.rb +56 -0
  62. data/spec/bullet/detector/n_plus_one_query_spec.rb +138 -0
  63. data/spec/bullet/detector/unused_eager_loading_spec.rb +88 -0
  64. data/spec/bullet/ext/object_spec.rb +17 -0
  65. data/spec/bullet/ext/string_spec.rb +13 -0
  66. data/spec/bullet/notification/base_spec.rb +83 -0
  67. data/spec/bullet/notification/counter_cache_spec.rb +12 -0
  68. data/spec/bullet/notification/n_plus_one_query_spec.rb +14 -0
  69. data/spec/bullet/notification/unused_eager_loading_spec.rb +12 -0
  70. data/spec/bullet/notification_collector_spec.rb +32 -0
  71. data/spec/bullet/rack_spec.rb +97 -0
  72. data/spec/bullet/registry/association_spec.rb +26 -0
  73. data/spec/bullet/registry/base_spec.rb +44 -0
  74. data/spec/bullet/registry/object_spec.rb +24 -0
  75. data/spec/bullet_spec.rb +41 -0
  76. data/spec/integration/active_record3/association_spec.rb +651 -0
  77. data/spec/integration/active_record4/association_spec.rb +649 -0
  78. data/spec/integration/counter_cache_spec.rb +63 -0
  79. data/spec/integration/mongoid/association_spec.rb +258 -0
  80. data/spec/models/address.rb +3 -0
  81. data/spec/models/author.rb +3 -0
  82. data/spec/models/base_user.rb +5 -0
  83. data/spec/models/category.rb +7 -0
  84. data/spec/models/city.rb +3 -0
  85. data/spec/models/client.rb +4 -0
  86. data/spec/models/comment.rb +4 -0
  87. data/spec/models/company.rb +3 -0
  88. data/spec/models/country.rb +3 -0
  89. data/spec/models/document.rb +5 -0
  90. data/spec/models/entry.rb +3 -0
  91. data/spec/models/firm.rb +4 -0
  92. data/spec/models/folder.rb +2 -0
  93. data/spec/models/mongoid/address.rb +7 -0
  94. data/spec/models/mongoid/category.rb +8 -0
  95. data/spec/models/mongoid/comment.rb +7 -0
  96. data/spec/models/mongoid/company.rb +7 -0
  97. data/spec/models/mongoid/entry.rb +7 -0
  98. data/spec/models/mongoid/post.rb +12 -0
  99. data/spec/models/mongoid/user.rb +5 -0
  100. data/spec/models/newspaper.rb +3 -0
  101. data/spec/models/page.rb +2 -0
  102. data/spec/models/person.rb +3 -0
  103. data/spec/models/pet.rb +3 -0
  104. data/spec/models/post.rb +10 -0
  105. data/spec/models/relationship.rb +4 -0
  106. data/spec/models/student.rb +3 -0
  107. data/spec/models/submission.rb +4 -0
  108. data/spec/models/teacher.rb +3 -0
  109. data/spec/models/user.rb +4 -0
  110. data/spec/models/writer.rb +2 -0
  111. data/spec/spec_helper.rb +103 -0
  112. data/spec/support/bullet_ext.rb +55 -0
  113. data/spec/support/mongo_seed.rb +65 -0
  114. data/spec/support/rack_double.rb +55 -0
  115. data/spec/support/sqlite_seed.rb +229 -0
  116. data/tasks/bullet_tasks.rake +9 -0
  117. data/test.sh +15 -0
  118. metadata +246 -0
@@ -0,0 +1,128 @@
1
+ module Bullet
2
+ module ActiveRecord
3
+ def self.enable
4
+ require 'active_record'
5
+ ::ActiveRecord::Relation.class_eval do
6
+ alias_method :origin_to_a, :to_a
7
+ # if select a collection of objects, then these objects have possible to cause N+1 query.
8
+ # if select only one object, then the only one object has impossible to cause N+1 query.
9
+ def to_a
10
+ records = origin_to_a
11
+ if records.size > 1
12
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
13
+ Bullet::Detector::CounterCache.add_possible_objects(records)
14
+ elsif records.size == 1
15
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
16
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
17
+ end
18
+ records
19
+ end
20
+ end
21
+
22
+ ::ActiveRecord::Associations::Preloader.class_eval do
23
+ # include query for one to many associations.
24
+ # keep this eager loadings.
25
+ alias_method :origin_initialize, :initialize
26
+ def initialize(records, associations, preload_scope = nil)
27
+ origin_initialize(records, associations, preload_scope)
28
+ records = [records].flatten.compact.uniq
29
+ return if records.empty?
30
+ records.each do |record|
31
+ Bullet::Detector::Association.add_object_associations(record, associations)
32
+ end
33
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
34
+ end
35
+ end
36
+
37
+ ::ActiveRecord::FinderMethods.class_eval do
38
+ # add includes in scope
39
+ alias_method :origin_find_with_associations, :find_with_associations
40
+ def find_with_associations
41
+ records = origin_find_with_associations
42
+ associations = (eager_load_values + includes_values).uniq
43
+ records.each do |record|
44
+ Bullet::Detector::Association.add_object_associations(record, associations)
45
+ end
46
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
47
+ records
48
+ end
49
+ end
50
+
51
+ ::ActiveRecord::Associations::JoinDependency.class_eval do
52
+ alias_method :origin_instantiate, :instantiate
53
+ alias_method :origin_construct_association, :construct_association
54
+
55
+ def instantiate(rows)
56
+ @bullet_eager_loadings = {}
57
+ records = origin_instantiate(rows)
58
+
59
+ @bullet_eager_loadings.each do |klazz, eager_loadings_hash|
60
+ objects = eager_loadings_hash.keys
61
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
62
+ end
63
+ records
64
+ end
65
+
66
+ # call join associations
67
+ def construct_association(record, join, row)
68
+ result = origin_construct_association(record, join, row)
69
+
70
+ associations = join.reflection.name
71
+ Bullet::Detector::Association.add_object_associations(record, associations)
72
+ Bullet::Detector::NPlusOneQuery.call_association(record, associations)
73
+ @bullet_eager_loadings[record.class] ||= {}
74
+ @bullet_eager_loadings[record.class][record] ||= Set.new
75
+ @bullet_eager_loadings[record.class][record] << associations
76
+
77
+ result
78
+ end
79
+ end
80
+
81
+ ::ActiveRecord::Associations::CollectionAssociation.class_eval do
82
+ # call one to many associations
83
+ alias_method :origin_load_target, :load_target
84
+ def load_target
85
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
86
+ origin_load_target
87
+ end
88
+
89
+ alias_method :origin_empty?, :empty?
90
+ def empty?
91
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
92
+ origin_empty?
93
+ end
94
+ end
95
+
96
+ ::ActiveRecord::Associations::SingularAssociation.class_eval do
97
+ # call has_one and belongs_to associations
98
+ alias_method :origin_reader, :reader
99
+ def reader(force_reload = false)
100
+ result = origin_reader(force_reload)
101
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
102
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
103
+ result
104
+ end
105
+ end
106
+
107
+ ::ActiveRecord::Associations::Association.class_eval do
108
+ alias_method :origin_set_inverse_instance, :set_inverse_instance
109
+ def set_inverse_instance(record)
110
+ if record && invertible_for?(record)
111
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(record)
112
+ end
113
+ origin_set_inverse_instance(record)
114
+ end
115
+ end
116
+
117
+ ::ActiveRecord::Associations::HasManyAssociation.class_eval do
118
+ alias_method :origin_has_cached_counter?, :has_cached_counter?
119
+
120
+ def has_cached_counter?(reflection = reflection())
121
+ result = origin_has_cached_counter?(reflection)
122
+ Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name) unless result
123
+ result
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,121 @@
1
+ module Bullet
2
+ module ActiveRecord
3
+ def self.enable
4
+ require 'active_record'
5
+ ::ActiveRecord::Relation.class_eval do
6
+ alias_method :origin_to_a, :to_a
7
+ # if select a collection of objects, then these objects have possible to cause N+1 query.
8
+ # if select only one object, then the only one object has impossible to cause N+1 query.
9
+ def to_a
10
+ 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)
18
+ end
19
+ end
20
+ records
21
+ end
22
+ end
23
+
24
+ ::ActiveRecord::Associations::Preloader.class_eval do
25
+ alias_method :origin_preloaders_on, :preloaders_on
26
+
27
+ def preloaders_on(association, records, scope)
28
+ if records.first.class.name !~ /^HABTM_/
29
+ records.each do |record|
30
+ Bullet::Detector::Association.add_object_associations(record, association)
31
+ end
32
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
33
+ end
34
+ origin_preloaders_on(association, records, scope)
35
+ end
36
+ end
37
+
38
+ ::ActiveRecord::FinderMethods.class_eval do
39
+ # add includes in scope
40
+ alias_method :origin_find_with_associations, :find_with_associations
41
+ def find_with_associations
42
+ records = origin_find_with_associations
43
+ associations = (eager_load_values + includes_values).uniq
44
+ records.each do |record|
45
+ Bullet::Detector::Association.add_object_associations(record, associations)
46
+ end
47
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
48
+ records
49
+ end
50
+ end
51
+
52
+ ::ActiveRecord::Associations::JoinDependency.class_eval do
53
+ alias_method :origin_instantiate, :instantiate
54
+ alias_method :origin_construct_model, :construct_model
55
+
56
+ def instantiate(result_set, aliases)
57
+ @bullet_eager_loadings = {}
58
+ records = origin_instantiate(result_set, aliases)
59
+
60
+ @bullet_eager_loadings.each do |klazz, eager_loadings_hash|
61
+ objects = eager_loadings_hash.keys
62
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
63
+ end
64
+ records
65
+ end
66
+
67
+ # call join associations
68
+ def construct_model(record, node, row, model_cache, id, aliases)
69
+ result = origin_construct_model(record, node, row, model_cache, id, aliases)
70
+
71
+ associations = node.reflection.name
72
+ Bullet::Detector::Association.add_object_associations(record, associations)
73
+ Bullet::Detector::NPlusOneQuery.call_association(record, associations)
74
+ @bullet_eager_loadings[record.class] ||= {}
75
+ @bullet_eager_loadings[record.class][record] ||= Set.new
76
+ @bullet_eager_loadings[record.class][record] << associations
77
+
78
+ result
79
+ end
80
+ end
81
+
82
+ ::ActiveRecord::Associations::CollectionAssociation.class_eval do
83
+ # call one to many associations
84
+ alias_method :origin_load_target, :load_target
85
+ def load_target
86
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
87
+ origin_load_target
88
+ end
89
+
90
+ alias_method :origin_empty?, :empty?
91
+ def empty?
92
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
93
+ origin_empty?
94
+ end
95
+ end
96
+
97
+ ::ActiveRecord::Associations::SingularAssociation.class_eval do
98
+ # call has_one and belongs_to associations
99
+ alias_method :origin_reader, :reader
100
+ def reader(force_reload = false)
101
+ result = origin_reader(force_reload)
102
+ if @owner.class.name !~ /^HABTM_/
103
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
104
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
105
+ end
106
+ result
107
+ end
108
+ end
109
+
110
+ ::ActiveRecord::Associations::HasManyAssociation.class_eval do
111
+ alias_method :origin_has_cached_counter?, :has_cached_counter?
112
+
113
+ def has_cached_counter?(reflection = reflection())
114
+ result = origin_has_cached_counter?(reflection)
115
+ Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name) unless result
116
+ result
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,81 @@
1
+ module Bullet
2
+ module Dependency
3
+ def mongoid?
4
+ @mongoid ||= defined? ::Mongoid
5
+ end
6
+
7
+ def active_record?
8
+ @active_record ||= defined? ::ActiveRecord
9
+ end
10
+
11
+ def rails?
12
+ @rails ||= defined? ::Rails
13
+ end
14
+
15
+ def active_record_version
16
+ @active_record_version ||= begin
17
+ if active_record30?
18
+ 'active_record3'
19
+ elsif active_record31? || active_record32?
20
+ 'active_record3x'
21
+ elsif active_record40?
22
+ 'active_record4'
23
+ elsif active_record41?
24
+ 'active_record41'
25
+ end
26
+ end
27
+ end
28
+
29
+ def mongoid_version
30
+ @mongoid_version ||= begin
31
+ if mongoid2x?
32
+ 'mongoid2x'
33
+ elsif mongoid3x?
34
+ 'mongoid3x'
35
+ elsif mongoid4x?
36
+ 'mongoid4x'
37
+ end
38
+ end
39
+ end
40
+
41
+ def active_record3?
42
+ active_record? && ::ActiveRecord::VERSION::MAJOR == 3
43
+ end
44
+
45
+ def active_record4?
46
+ active_record? && ::ActiveRecord::VERSION::MAJOR == 4
47
+ end
48
+
49
+ def active_record30?
50
+ active_record3? && ::ActiveRecord::VERSION::MINOR == 0
51
+ end
52
+
53
+ def active_record31?
54
+ active_record3? && ::ActiveRecord::VERSION::MINOR == 1
55
+ end
56
+
57
+ def active_record32?
58
+ active_record3? && ::ActiveRecord::VERSION::MINOR == 2
59
+ end
60
+
61
+ def active_record40?
62
+ active_record4? && ::ActiveRecord::VERSION::MINOR == 0
63
+ end
64
+
65
+ def active_record41?
66
+ active_record4? && ::ActiveRecord::VERSION::MINOR == 1
67
+ end
68
+
69
+ def mongoid2x?
70
+ mongoid? && ::Mongoid::VERSION =~ /\A2\.[4-8]/
71
+ end
72
+
73
+ def mongoid3x?
74
+ mongoid? && ::Mongoid::VERSION =~ /\A3/
75
+ end
76
+
77
+ def mongoid4x?
78
+ mongoid? && ::Mongoid::VERSION =~ /\A4/
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,9 @@
1
+ module Bullet
2
+ module Detector
3
+ autoload :Base, 'bullet/detector/base'
4
+ autoload :Association, 'bullet/detector/association'
5
+ autoload :NPlusOneQuery, 'bullet/detector/n_plus_one_query'
6
+ autoload :UnusedEagerLoading, 'bullet/detector/unused_eager_loading'
7
+ autoload :CounterCache, 'bullet/detector/counter_cache'
8
+ end
9
+ end
@@ -0,0 +1,67 @@
1
+ module Bullet
2
+ module Detector
3
+ class Association < Base
4
+ class <<self
5
+ def add_object_associations(object, associations)
6
+ return unless Bullet.start?
7
+ return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
8
+ return unless object.id
9
+
10
+ Bullet.debug("Detector::Association#add_object_associations", "object: #{object.bullet_key}, associations: #{associations}")
11
+ object_associations.add(object.bullet_key, associations)
12
+ end
13
+
14
+ def add_call_object_associations(object, associations)
15
+ return unless Bullet.start?
16
+ return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
17
+ return unless object.id
18
+
19
+ Bullet.debug("Detector::Association#add_call_object_associations", "object: #{object.bullet_key}, associations: #{associations}")
20
+ call_object_associations.add(object.bullet_key, associations)
21
+ end
22
+
23
+ private
24
+ # object_associations keep the object relationships
25
+ # that the object has many associations.
26
+ # e.g. { "Post:1" => [:comments] }
27
+ # the object_associations keep all associations that may be or may no be
28
+ # unpreload associations or unused preload associations.
29
+ def object_associations
30
+ Thread.current[:bullet_object_associations]
31
+ end
32
+
33
+ # call_object_associations keep the object relationships
34
+ # that object.associations is called.
35
+ # e.g. { "Post:1" => [:comments] }
36
+ # they are used to detect unused preload associations.
37
+ def call_object_associations
38
+ Thread.current[:bullet_call_object_associations]
39
+ end
40
+
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
+ # Notice: impossible_objects are not accurate,
52
+ # if find collection returns only one object, then the object is impossible object,
53
+ # impossible_objects are used to avoid treating 1+1 query to N+1 query.
54
+ def impossible_objects
55
+ Thread.current[:bullet_impossible_objects]
56
+ end
57
+
58
+ # eager_loadings keep the object relationships
59
+ # that the associations are preloaded by find :include.
60
+ # e.g. { ["Post:1", "Post:2"] => [:comments, :user] }
61
+ def eager_loadings
62
+ Thread.current[:bullet_eager_loadings]
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,6 @@
1
+ module Bullet
2
+ module Detector
3
+ class Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,59 @@
1
+ module Bullet
2
+ module Detector
3
+ class CounterCache < Base
4
+ class <<self
5
+ def add_counter_cache(object, associations)
6
+ return unless Bullet.start?
7
+ return unless Bullet.counter_cache_enable?
8
+ return unless object.id
9
+
10
+ Bullet.debug("Detector::CounterCache#add_counter_cache", "object: #{object.bullet_key}, associations: #{associations}")
11
+ if conditions_met?(object.bullet_key, associations)
12
+ create_notification object.class.to_s, associations
13
+ end
14
+ end
15
+
16
+ def add_possible_objects(object_or_objects)
17
+ return unless Bullet.start?
18
+ return unless Bullet.counter_cache_enable?
19
+ objects = Array(object_or_objects)
20
+ return if objects.map(&:id).compact.empty?
21
+
22
+ Bullet.debug("Detector::CounterCache#add_possible_objects", "objects: #{objects.map(&:bullet_key).join(', ')}")
23
+ objects.each { |object| possible_objects.add object.bullet_key }
24
+ end
25
+
26
+ def add_impossible_object(object)
27
+ return unless Bullet.start?
28
+ return unless Bullet.counter_cache_enable?
29
+ return unless object.id
30
+
31
+ Bullet.debug("Detector::CounterCache#add_impossible_object", "object: #{object.bullet_key}")
32
+ impossible_objects.add object.bullet_key
33
+ end
34
+
35
+ private
36
+ def create_notification(klazz, associations)
37
+ notify_associations = Array(associations) - Bullet.get_whitelist_associations(:counter_cache, klazz)
38
+
39
+ if notify_associations.present?
40
+ notice = Bullet::Notification::CounterCache.new klazz, notify_associations
41
+ Bullet.notification_collector.add notice
42
+ end
43
+ 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
+ end
57
+ end
58
+ end
59
+ end