hyper-pure-sys 0.0.1

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 (52) hide show
  1. checksums.yaml +7 -0
  2. data/bullet-8.1.3/CHANGELOG.md +374 -0
  3. data/bullet-8.1.3/MIT-LICENSE +20 -0
  4. data/bullet-8.1.3/README.md +525 -0
  5. data/bullet-8.1.3/lib/bullet/active_job.rb +13 -0
  6. data/bullet-8.1.3/lib/bullet/active_record4.rb +197 -0
  7. data/bullet-8.1.3/lib/bullet/active_record41.rb +191 -0
  8. data/bullet-8.1.3/lib/bullet/active_record42.rb +262 -0
  9. data/bullet-8.1.3/lib/bullet/active_record5.rb +294 -0
  10. data/bullet-8.1.3/lib/bullet/active_record52.rb +278 -0
  11. data/bullet-8.1.3/lib/bullet/active_record60.rb +310 -0
  12. data/bullet-8.1.3/lib/bullet/active_record61.rb +310 -0
  13. data/bullet-8.1.3/lib/bullet/active_record70.rb +319 -0
  14. data/bullet-8.1.3/lib/bullet/active_record71.rb +319 -0
  15. data/bullet-8.1.3/lib/bullet/active_record72.rb +319 -0
  16. data/bullet-8.1.3/lib/bullet/active_record80.rb +319 -0
  17. data/bullet-8.1.3/lib/bullet/active_record81.rb +321 -0
  18. data/bullet-8.1.3/lib/bullet/bullet_xhr.js +64 -0
  19. data/bullet-8.1.3/lib/bullet/dependency.rb +165 -0
  20. data/bullet-8.1.3/lib/bullet/detector/association.rb +92 -0
  21. data/bullet-8.1.3/lib/bullet/detector/base.rb +8 -0
  22. data/bullet-8.1.3/lib/bullet/detector/counter_cache.rb +69 -0
  23. data/bullet-8.1.3/lib/bullet/detector/n_plus_one_query.rb +148 -0
  24. data/bullet-8.1.3/lib/bullet/detector/unused_eager_loading.rb +100 -0
  25. data/bullet-8.1.3/lib/bullet/detector.rb +11 -0
  26. data/bullet-8.1.3/lib/bullet/ext/object.rb +37 -0
  27. data/bullet-8.1.3/lib/bullet/ext/string.rb +14 -0
  28. data/bullet-8.1.3/lib/bullet/mongoid4x.rb +59 -0
  29. data/bullet-8.1.3/lib/bullet/mongoid5x.rb +59 -0
  30. data/bullet-8.1.3/lib/bullet/mongoid6x.rb +59 -0
  31. data/bullet-8.1.3/lib/bullet/mongoid7x.rb +74 -0
  32. data/bullet-8.1.3/lib/bullet/mongoid8x.rb +61 -0
  33. data/bullet-8.1.3/lib/bullet/mongoid9x.rb +74 -0
  34. data/bullet-8.1.3/lib/bullet/notification/base.rb +92 -0
  35. data/bullet-8.1.3/lib/bullet/notification/counter_cache.rb +15 -0
  36. data/bullet-8.1.3/lib/bullet/notification/n_plus_one_query.rb +31 -0
  37. data/bullet-8.1.3/lib/bullet/notification/unused_eager_loading.rb +31 -0
  38. data/bullet-8.1.3/lib/bullet/notification.rb +13 -0
  39. data/bullet-8.1.3/lib/bullet/notification_collector.rb +25 -0
  40. data/bullet-8.1.3/lib/bullet/rack.rb +186 -0
  41. data/bullet-8.1.3/lib/bullet/registry/association.rb +16 -0
  42. data/bullet-8.1.3/lib/bullet/registry/base.rb +46 -0
  43. data/bullet-8.1.3/lib/bullet/registry/call_stack.rb +17 -0
  44. data/bullet-8.1.3/lib/bullet/registry/object.rb +18 -0
  45. data/bullet-8.1.3/lib/bullet/registry.rb +10 -0
  46. data/bullet-8.1.3/lib/bullet/stack_trace_filter.rb +67 -0
  47. data/bullet-8.1.3/lib/bullet/version.rb +5 -0
  48. data/bullet-8.1.3/lib/bullet.rb +289 -0
  49. data/bullet-8.1.3/lib/generators/bullet/install_generator.rb +47 -0
  50. data/bullet-8.1.3/tasks/bullet_tasks.rake +11 -0
  51. data/hyper-pure-sys.gemspec +12 -0
  52. metadata +91 -0
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ using Bullet::Ext::Object
4
+
5
+ module Bullet
6
+ module Detector
7
+ class CounterCache < Base
8
+ class << self
9
+ def add_counter_cache(object, associations)
10
+ return unless Bullet.start?
11
+ return unless Bullet.counter_cache_enable?
12
+ return unless object.bullet_primary_key_value
13
+
14
+ Bullet.debug(
15
+ 'Detector::CounterCache#add_counter_cache',
16
+ "object: #{object.bullet_key}, associations: #{associations}"
17
+ )
18
+ create_notification object.class.to_s, associations if conditions_met?(object, associations)
19
+ end
20
+
21
+ def add_possible_objects(object_or_objects)
22
+ return unless Bullet.start?
23
+ return unless Bullet.counter_cache_enable?
24
+
25
+ objects = Array.wrap(object_or_objects)
26
+ return if objects.map(&:bullet_primary_key_value).compact.empty?
27
+
28
+ Bullet.debug(
29
+ 'Detector::CounterCache#add_possible_objects',
30
+ "objects: #{objects.map(&:bullet_key).join(', ')}"
31
+ )
32
+ objects.each { |object| possible_objects.add object.bullet_key }
33
+ end
34
+
35
+ def add_impossible_object(object)
36
+ return unless Bullet.start?
37
+ return unless Bullet.counter_cache_enable?
38
+ return unless object.bullet_primary_key_value
39
+
40
+ Bullet.debug('Detector::CounterCache#add_impossible_object', "object: #{object.bullet_key}")
41
+ impossible_objects.add object.bullet_key
42
+ end
43
+
44
+ def conditions_met?(object, _associations)
45
+ possible_objects.include?(object.bullet_key) && !impossible_objects.include?(object.bullet_key)
46
+ end
47
+
48
+ def possible_objects
49
+ Thread.current.thread_variable_get(:bullet_counter_possible_objects)
50
+ end
51
+
52
+ def impossible_objects
53
+ Thread.current.thread_variable_get(:bullet_counter_impossible_objects)
54
+ end
55
+
56
+ private
57
+
58
+ def create_notification(klazz, associations)
59
+ notify_associations = Array.wrap(associations) - Bullet.get_safelist_associations(:counter_cache, klazz)
60
+
61
+ if notify_associations.present?
62
+ notice = Bullet::Notification::CounterCache.new klazz, notify_associations
63
+ Bullet.notification_collector.add notice
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ using Bullet::Ext::Object
4
+
5
+ module Bullet
6
+ module Detector
7
+ class NPlusOneQuery < Association
8
+ extend Dependency
9
+ extend StackTraceFilter
10
+
11
+ class << self
12
+ # executed when object.associations is called.
13
+ # first, it keeps this method call for object.association.
14
+ # then, it checks if this associations call is unpreload.
15
+ # if it is, keeps this unpreload associations and caller.
16
+ def call_association(object, associations, caller_stack = nil, inversed: false)
17
+ return unless Bullet.start?
18
+ return unless Bullet.n_plus_one_query_enable?
19
+ return unless object.bullet_primary_key_value
20
+
21
+ # Record before early-returns so legitimate reads that bypass SQL
22
+ # (inversed, nil-polymorphic) aren't flagged as unused preloads.
23
+ add_call_object_associations(object, associations)
24
+ call_stacks.add(object.bullet_key, caller_stack) if caller_stack
25
+
26
+ return if inversed
27
+ return if inversed_objects.include?(object.bullet_key, associations)
28
+ return if optional_polymorphic_belongs_to_with_nil_type?(object, associations)
29
+
30
+ Bullet.debug(
31
+ 'Detector::NPlusOneQuery#call_association',
32
+ "object: #{object.bullet_key}, associations: #{associations}"
33
+ )
34
+ if !excluded_stacktrace_path? && conditions_met?(object, associations)
35
+ Bullet.debug('detect n + 1 query', "object: #{object.bullet_key}, associations: #{associations}")
36
+ create_notification(caller_in_project(object.bullet_key), object.class.to_s, associations)
37
+ end
38
+ end
39
+
40
+ def add_possible_objects(object_or_objects)
41
+ return unless Bullet.start?
42
+ return unless Bullet.n_plus_one_query_enable?
43
+
44
+ objects = Array.wrap(object_or_objects)
45
+ class_names_match_regex = true
46
+ primary_key_values_are_empty = true
47
+
48
+ keys_joined = objects.map do |obj|
49
+ unless obj.class.name =~ /^HABTM_/
50
+ class_names_match_regex = false
51
+ end
52
+ unless obj.bullet_primary_key_value.nil?
53
+ primary_key_values_are_empty = false
54
+ end
55
+ obj.bullet_key
56
+ end.join(", ")
57
+
58
+ unless class_names_match_regex || primary_key_values_are_empty
59
+ Bullet.debug('Detector::NPlusOneQuery#add_possible_objects', "objects: #{keys_joined}")
60
+ objects.each { |object| possible_objects.add object.bullet_key }
61
+ end
62
+ end
63
+
64
+ def add_impossible_object(object)
65
+ return unless Bullet.start?
66
+ return unless Bullet.n_plus_one_query_enable?
67
+ return unless object.bullet_primary_key_value
68
+
69
+ Bullet.debug('Detector::NPlusOneQuery#add_impossible_object', "object: #{object.bullet_key}")
70
+ impossible_objects.add object.bullet_key
71
+ end
72
+
73
+ def add_inversed_object(object, association)
74
+ return unless Bullet.start?
75
+ return unless Bullet.n_plus_one_query_enable?
76
+
77
+ object_key = object.bullet_primary_key_value ? object.bullet_key : object.object_id
78
+ Bullet.debug(
79
+ 'Detector::NPlusOneQuery#add_inversed_object',
80
+ "object: #{object_key}, association: #{association}"
81
+ )
82
+ inversed_objects.add object_key, association
83
+ end
84
+
85
+ def update_inversed_object(object)
86
+ if inversed_objects&.key?(object.object_id)
87
+ Bullet.debug(
88
+ 'Detector::NPlusOneQuery#update_inversed_object',
89
+ "object from #{object.object_id} to #{object.bullet_key}"
90
+ )
91
+ inversed_objects.add(object.bullet_key, inversed_objects[object.object_id].to_a)
92
+ end
93
+ end
94
+
95
+ # decide whether the object.associations is unpreloaded or not.
96
+ def conditions_met?(object, associations)
97
+ possible?(object) && !impossible?(object) && !association?(object, associations)
98
+ end
99
+
100
+ def possible?(object)
101
+ possible_objects.include? object.bullet_key
102
+ end
103
+
104
+ def impossible?(object)
105
+ impossible_objects.include? object.bullet_key
106
+ end
107
+
108
+ # check if object => associations already exists in object_associations.
109
+ def association?(object, associations)
110
+ value = object_associations[object.bullet_key]
111
+ value&.each do |v|
112
+ # associations == v comparison order is important here because
113
+ # v variable might be a squeel node where :== method is redefined,
114
+ # so it does not compare values at all and return unexpected results
115
+ result = v.is_a?(Hash) ? v.key?(associations) : associations == v
116
+ return true if result
117
+ end
118
+
119
+ false
120
+ end
121
+
122
+ private
123
+
124
+ def optional_polymorphic_belongs_to_with_nil_type?(object, associations)
125
+ return false unless associations.is_a?(Symbol) || associations.is_a?(String)
126
+ return false unless object.class.respond_to?(:reflect_on_association)
127
+
128
+ reflection = object.class.reflect_on_association(associations.to_sym)
129
+ return false unless reflection
130
+ return false unless reflection.respond_to?(:polymorphic?) && reflection.polymorphic?
131
+ return false unless reflection.respond_to?(:foreign_type)
132
+
133
+ foreign_type = reflection.foreign_type
134
+ foreign_type && object.respond_to?(:[]) && object[foreign_type].nil?
135
+ end
136
+
137
+ def create_notification(callers, klazz, associations)
138
+ notify_associations = Array.wrap(associations) - Bullet.get_safelist_associations(:n_plus_one_query, klazz)
139
+
140
+ if notify_associations.present?
141
+ notice = Bullet::Notification::NPlusOneQuery.new(callers, klazz, notify_associations)
142
+ Bullet.notification_collector.add(notice)
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ using Bullet::Ext::Object
4
+ using Bullet::Ext::String
5
+
6
+ module Bullet
7
+ module Detector
8
+ class UnusedEagerLoading < Association
9
+ extend Dependency
10
+ extend StackTraceFilter
11
+
12
+ class << self
13
+ # check if there are unused preload associations.
14
+ # get related_objects from eager_loadings associated with object and associations
15
+ # get call_object_association from associations of call_object_associations whose object is in related_objects
16
+ # if association not in call_object_association, then the object => association - call_object_association is unused preload associations
17
+ def check_unused_preload_associations
18
+ return unless Bullet.start?
19
+ return unless Bullet.unused_eager_loading_enable?
20
+
21
+ object_associations.each do |bullet_key, associations|
22
+ object_association_diff = diff_object_associations bullet_key, associations
23
+ next if object_association_diff.empty?
24
+
25
+ Bullet.debug('detect unused preload', "object: #{bullet_key}, associations: #{object_association_diff}")
26
+ create_notification(caller_in_project(bullet_key), bullet_key.bullet_class_name, object_association_diff)
27
+ end
28
+ end
29
+
30
+ def add_eager_loadings(objects, associations)
31
+ return unless Bullet.start?
32
+ return unless Bullet.unused_eager_loading_enable?
33
+ return if objects.map(&:bullet_primary_key_value).compact.empty?
34
+
35
+ Bullet.debug(
36
+ 'Detector::UnusedEagerLoading#add_eager_loadings',
37
+ "objects: #{objects.map(&:bullet_key).join(', ')}, associations: #{associations}"
38
+ )
39
+ bullet_keys = objects.map(&:bullet_key)
40
+
41
+ to_add = []
42
+ to_merge = []
43
+ to_delete = []
44
+ eager_loadings.each do |k, _v|
45
+ key_objects_overlap = k & bullet_keys
46
+
47
+ next if key_objects_overlap.empty?
48
+
49
+ bullet_keys -= k
50
+ if key_objects_overlap == k
51
+ to_add << [k, associations]
52
+ else
53
+ to_merge << [key_objects_overlap, eager_loadings[k].dup.merge(Array.wrap(associations))]
54
+
55
+ keys_without_objects = k - key_objects_overlap
56
+ to_merge << [keys_without_objects, eager_loadings[k]]
57
+ to_delete << k
58
+ end
59
+ end
60
+
61
+ to_add.each { |k, val| eager_loadings.add k, val }
62
+ to_merge.each { |k, val| eager_loadings.merge k, val }
63
+ to_delete.each { |k| eager_loadings.delete k }
64
+
65
+ eager_loadings.add bullet_keys, associations unless bullet_keys.empty?
66
+ end
67
+
68
+ private
69
+
70
+ def create_notification(callers, klazz, associations)
71
+ notify_associations = Array.wrap(associations) - Bullet.get_safelist_associations(
72
+ :unused_eager_loading,
73
+ klazz
74
+ )
75
+
76
+ if notify_associations.present?
77
+ notice = Bullet::Notification::UnusedEagerLoading.new(callers, klazz, notify_associations)
78
+ Bullet.notification_collector.add(notice)
79
+ end
80
+ end
81
+
82
+ def call_associations(bullet_key, associations)
83
+ all = Set.new
84
+ eager_loadings.similarly_associated(bullet_key, associations).each do |related_bullet_key|
85
+ coa = call_object_associations[related_bullet_key]
86
+ next if coa.nil?
87
+
88
+ all.merge coa
89
+ end
90
+ all.to_a
91
+ end
92
+
93
+ def diff_object_associations(bullet_key, associations)
94
+ potential_associations = associations - call_associations(bullet_key, associations)
95
+ potential_associations.reject { |a| a.is_a?(Hash) }
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bullet
4
+ module Detector
5
+ autoload :Base, 'bullet/detector/base'
6
+ autoload :Association, 'bullet/detector/association'
7
+ autoload :NPlusOneQuery, 'bullet/detector/n_plus_one_query'
8
+ autoload :UnusedEagerLoading, 'bullet/detector/unused_eager_loading'
9
+ autoload :CounterCache, 'bullet/detector/counter_cache'
10
+ end
11
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bullet
4
+ module Ext
5
+ module Object
6
+ refine ::Object do
7
+ attr_writer :bullet_key, :bullet_primary_key_value
8
+
9
+ def bullet_key
10
+ return "#{self.class}:" if respond_to?(:persisted?) && !persisted?
11
+
12
+ @bullet_key ||= "#{self.class}:#{bullet_primary_key_value}"
13
+ end
14
+
15
+ def bullet_primary_key_value
16
+ return if respond_to?(:persisted?) && !persisted?
17
+
18
+ @bullet_primary_key_value ||=
19
+ begin
20
+ primary_key = self.class.try(:primary_keys) || self.class.try(:primary_key) || :id
21
+
22
+ bullet_join_potential_composite_primary_key(primary_key)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def bullet_join_potential_composite_primary_key(primary_keys)
29
+ return read_attribute(primary_keys) unless primary_keys.is_a?(Enumerable)
30
+
31
+ primary_keys.map { |primary_key| read_attribute primary_key }
32
+ .compact.join(',')
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bullet
4
+ module Ext
5
+ module String
6
+ refine ::String do
7
+ def bullet_class_name
8
+ last_colon = self.rindex(':')
9
+ last_colon ? self[0...last_colon].dup : self.dup
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bullet
4
+ module Mongoid
5
+ def self.enable
6
+ require 'mongoid'
7
+ ::Mongoid::Contextual::Mongo.class_eval do
8
+ alias_method :origin_first, :first
9
+ alias_method :origin_last, :last
10
+ alias_method :origin_each, :each
11
+ alias_method :origin_eager_load, :eager_load
12
+
13
+ def first
14
+ result = origin_first
15
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
16
+ result
17
+ end
18
+
19
+ def last
20
+ result = origin_last
21
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
22
+ result
23
+ end
24
+
25
+ def each(&block)
26
+ return to_enum unless block
27
+
28
+ records = []
29
+ origin_each { |record| records << record }
30
+ if records.length > 1
31
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
32
+ elsif records.size == 1
33
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
34
+ end
35
+ records.each(&block)
36
+ end
37
+
38
+ def eager_load(docs)
39
+ associations = criteria.inclusions.map(&:name)
40
+ docs.each { |doc| Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations) }
41
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
42
+ origin_eager_load(docs)
43
+ end
44
+ end
45
+
46
+ ::Mongoid::Relations::Accessors.class_eval do
47
+ alias_method :origin_get_relation, :get_relation
48
+
49
+ private
50
+
51
+ def get_relation(name, metadata, object, reload = false)
52
+ result = origin_get_relation(name, metadata, object, reload)
53
+ Bullet::Detector::NPlusOneQuery.call_association(self, name) if metadata.macro !~ /embed/
54
+ result
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bullet
4
+ module Mongoid
5
+ def self.enable
6
+ require 'mongoid'
7
+ ::Mongoid::Contextual::Mongo.class_eval do
8
+ alias_method :origin_first, :first
9
+ alias_method :origin_last, :last
10
+ alias_method :origin_each, :each
11
+ alias_method :origin_eager_load, :eager_load
12
+
13
+ def first
14
+ result = origin_first
15
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
16
+ result
17
+ end
18
+
19
+ def last
20
+ result = origin_last
21
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
22
+ result
23
+ end
24
+
25
+ def each(&block)
26
+ return to_enum unless block
27
+
28
+ records = []
29
+ origin_each { |record| records << record }
30
+ if records.length > 1
31
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
32
+ elsif records.size == 1
33
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
34
+ end
35
+ records.each(&block)
36
+ end
37
+
38
+ def eager_load(docs)
39
+ associations = criteria.inclusions.map(&:name)
40
+ docs.each { |doc| Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations) }
41
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
42
+ origin_eager_load(docs)
43
+ end
44
+ end
45
+
46
+ ::Mongoid::Relations::Accessors.class_eval do
47
+ alias_method :origin_get_relation, :get_relation
48
+
49
+ private
50
+
51
+ def get_relation(name, metadata, object, reload = false)
52
+ result = origin_get_relation(name, metadata, object, reload)
53
+ Bullet::Detector::NPlusOneQuery.call_association(self, name) if metadata.macro !~ /embed/
54
+ result
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bullet
4
+ module Mongoid
5
+ def self.enable
6
+ require 'mongoid'
7
+ ::Mongoid::Contextual::Mongo.class_eval do
8
+ alias_method :origin_first, :first
9
+ alias_method :origin_last, :last
10
+ alias_method :origin_each, :each
11
+ alias_method :origin_eager_load, :eager_load
12
+
13
+ def first(opt = {})
14
+ result = origin_first(opt)
15
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
16
+ result
17
+ end
18
+
19
+ def last(opt = {})
20
+ result = origin_last(opt)
21
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
22
+ result
23
+ end
24
+
25
+ def each(&block)
26
+ return to_enum unless block
27
+
28
+ records = []
29
+ origin_each { |record| records << record }
30
+ if records.length > 1
31
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
32
+ elsif records.size == 1
33
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
34
+ end
35
+ records.each(&block)
36
+ end
37
+
38
+ def eager_load(docs)
39
+ associations = criteria.inclusions.map(&:name)
40
+ docs.each { |doc| Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations) }
41
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
42
+ origin_eager_load(docs)
43
+ end
44
+ end
45
+
46
+ ::Mongoid::Relations::Accessors.class_eval do
47
+ alias_method :origin_get_relation, :get_relation
48
+
49
+ private
50
+
51
+ def get_relation(name, metadata, object, reload = false)
52
+ result = origin_get_relation(name, metadata, object, reload)
53
+ Bullet::Detector::NPlusOneQuery.call_association(self, name) if metadata.macro !~ /embed/
54
+ result
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bullet
4
+ module Mongoid
5
+ def self.enable
6
+ require 'mongoid'
7
+ require 'rubygems'
8
+ ::Mongoid::Contextual::Mongo.class_eval do
9
+ alias_method :origin_first, :first
10
+ alias_method :origin_last, :last
11
+ alias_method :origin_each, :each
12
+ alias_method :origin_eager_load, :eager_load
13
+
14
+ %i[first last].each do |context|
15
+ default = Gem::Version.new(::Mongoid::VERSION) >= Gem::Version.new('7.5') ? nil : {}
16
+ define_method(context) do |opts = default|
17
+ result = send(:"origin_#{context}", opts)
18
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
19
+ result
20
+ end
21
+ end
22
+
23
+ def each(&block)
24
+ return to_enum unless block_given?
25
+
26
+ first_document = nil
27
+ document_count = 0
28
+
29
+ origin_each do |document|
30
+ document_count += 1
31
+
32
+ if document_count == 1
33
+ first_document = document
34
+ elsif document_count == 2
35
+ Bullet::Detector::NPlusOneQuery.add_possible_objects([first_document, document])
36
+ yield(first_document)
37
+ first_document = nil
38
+ yield(document)
39
+ else
40
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(document)
41
+ yield(document)
42
+ end
43
+ end
44
+
45
+ if document_count == 1
46
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(first_document)
47
+ yield(first_document)
48
+ end
49
+
50
+ self
51
+ end
52
+
53
+ def eager_load(docs)
54
+ associations = criteria.inclusions.map(&:name)
55
+ docs.each { |doc| Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations) }
56
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
57
+ origin_eager_load(docs)
58
+ end
59
+ end
60
+
61
+ ::Mongoid::Association::Accessors.class_eval do
62
+ alias_method :origin_get_relation, :get_relation
63
+
64
+ private
65
+
66
+ def get_relation(name, association, object, reload = false)
67
+ result = origin_get_relation(name, association, object, reload)
68
+ Bullet::Detector::NPlusOneQuery.call_association(self, name) unless association.embedded?
69
+ result
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end