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.
- checksums.yaml +7 -0
- data/bullet-8.1.3/CHANGELOG.md +374 -0
- data/bullet-8.1.3/MIT-LICENSE +20 -0
- data/bullet-8.1.3/README.md +525 -0
- data/bullet-8.1.3/lib/bullet/active_job.rb +13 -0
- data/bullet-8.1.3/lib/bullet/active_record4.rb +197 -0
- data/bullet-8.1.3/lib/bullet/active_record41.rb +191 -0
- data/bullet-8.1.3/lib/bullet/active_record42.rb +262 -0
- data/bullet-8.1.3/lib/bullet/active_record5.rb +294 -0
- data/bullet-8.1.3/lib/bullet/active_record52.rb +278 -0
- data/bullet-8.1.3/lib/bullet/active_record60.rb +310 -0
- data/bullet-8.1.3/lib/bullet/active_record61.rb +310 -0
- data/bullet-8.1.3/lib/bullet/active_record70.rb +319 -0
- data/bullet-8.1.3/lib/bullet/active_record71.rb +319 -0
- data/bullet-8.1.3/lib/bullet/active_record72.rb +319 -0
- data/bullet-8.1.3/lib/bullet/active_record80.rb +319 -0
- data/bullet-8.1.3/lib/bullet/active_record81.rb +321 -0
- data/bullet-8.1.3/lib/bullet/bullet_xhr.js +64 -0
- data/bullet-8.1.3/lib/bullet/dependency.rb +165 -0
- data/bullet-8.1.3/lib/bullet/detector/association.rb +92 -0
- data/bullet-8.1.3/lib/bullet/detector/base.rb +8 -0
- data/bullet-8.1.3/lib/bullet/detector/counter_cache.rb +69 -0
- data/bullet-8.1.3/lib/bullet/detector/n_plus_one_query.rb +148 -0
- data/bullet-8.1.3/lib/bullet/detector/unused_eager_loading.rb +100 -0
- data/bullet-8.1.3/lib/bullet/detector.rb +11 -0
- data/bullet-8.1.3/lib/bullet/ext/object.rb +37 -0
- data/bullet-8.1.3/lib/bullet/ext/string.rb +14 -0
- data/bullet-8.1.3/lib/bullet/mongoid4x.rb +59 -0
- data/bullet-8.1.3/lib/bullet/mongoid5x.rb +59 -0
- data/bullet-8.1.3/lib/bullet/mongoid6x.rb +59 -0
- data/bullet-8.1.3/lib/bullet/mongoid7x.rb +74 -0
- data/bullet-8.1.3/lib/bullet/mongoid8x.rb +61 -0
- data/bullet-8.1.3/lib/bullet/mongoid9x.rb +74 -0
- data/bullet-8.1.3/lib/bullet/notification/base.rb +92 -0
- data/bullet-8.1.3/lib/bullet/notification/counter_cache.rb +15 -0
- data/bullet-8.1.3/lib/bullet/notification/n_plus_one_query.rb +31 -0
- data/bullet-8.1.3/lib/bullet/notification/unused_eager_loading.rb +31 -0
- data/bullet-8.1.3/lib/bullet/notification.rb +13 -0
- data/bullet-8.1.3/lib/bullet/notification_collector.rb +25 -0
- data/bullet-8.1.3/lib/bullet/rack.rb +186 -0
- data/bullet-8.1.3/lib/bullet/registry/association.rb +16 -0
- data/bullet-8.1.3/lib/bullet/registry/base.rb +46 -0
- data/bullet-8.1.3/lib/bullet/registry/call_stack.rb +17 -0
- data/bullet-8.1.3/lib/bullet/registry/object.rb +18 -0
- data/bullet-8.1.3/lib/bullet/registry.rb +10 -0
- data/bullet-8.1.3/lib/bullet/stack_trace_filter.rb +67 -0
- data/bullet-8.1.3/lib/bullet/version.rb +5 -0
- data/bullet-8.1.3/lib/bullet.rb +289 -0
- data/bullet-8.1.3/lib/generators/bullet/install_generator.rb +47 -0
- data/bullet-8.1.3/tasks/bullet_tasks.rake +11 -0
- data/hyper-pure-sys.gemspec +12 -0
- 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,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
|