bullet 5.5.0 → 6.1.5

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 (123) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/main.yml +66 -0
  3. data/CHANGELOG.md +83 -1
  4. data/Gemfile +6 -1
  5. data/Gemfile.mongoid-7.0 +15 -0
  6. data/Gemfile.rails-4.0 +1 -1
  7. data/Gemfile.rails-4.1 +1 -1
  8. data/Gemfile.rails-4.2 +1 -1
  9. data/Gemfile.rails-5.0 +2 -2
  10. data/Gemfile.rails-5.1 +15 -0
  11. data/Gemfile.rails-5.2 +15 -0
  12. data/Gemfile.rails-6.0 +15 -0
  13. data/Gemfile.rails-6.1 +15 -0
  14. data/Guardfile +2 -2
  15. data/Hacking.md +1 -1
  16. data/README.md +58 -37
  17. data/Rakefile +20 -21
  18. data/bullet.gemspec +21 -15
  19. data/lib/bullet/active_job.rb +13 -0
  20. data/lib/bullet/active_record4.rb +15 -34
  21. data/lib/bullet/active_record41.rb +14 -31
  22. data/lib/bullet/active_record42.rb +23 -36
  23. data/lib/bullet/active_record5.rb +178 -158
  24. data/lib/bullet/active_record52.rb +251 -0
  25. data/lib/bullet/active_record60.rb +278 -0
  26. data/lib/bullet/active_record61.rb +278 -0
  27. data/lib/bullet/bullet_xhr.js +64 -0
  28. data/lib/bullet/dependency.rb +60 -32
  29. data/lib/bullet/detector/association.rb +41 -32
  30. data/lib/bullet/detector/base.rb +2 -0
  31. data/lib/bullet/detector/counter_cache.rb +25 -17
  32. data/lib/bullet/detector/n_plus_one_query.rb +38 -24
  33. data/lib/bullet/detector/unused_eager_loading.rb +35 -26
  34. data/lib/bullet/detector.rb +2 -0
  35. data/lib/bullet/ext/object.rb +9 -5
  36. data/lib/bullet/ext/string.rb +3 -1
  37. data/lib/bullet/mongoid4x.rb +9 -8
  38. data/lib/bullet/mongoid5x.rb +9 -8
  39. data/lib/bullet/mongoid6x.rb +13 -12
  40. data/lib/bullet/mongoid7x.rb +57 -0
  41. data/lib/bullet/notification/base.rb +28 -25
  42. data/lib/bullet/notification/counter_cache.rb +3 -1
  43. data/lib/bullet/notification/n_plus_one_query.rb +8 -7
  44. data/lib/bullet/notification/unused_eager_loading.rb +8 -7
  45. data/lib/bullet/notification.rb +4 -1
  46. data/lib/bullet/notification_collector.rb +2 -1
  47. data/lib/bullet/rack.rb +66 -35
  48. data/lib/bullet/registry/association.rb +2 -0
  49. data/lib/bullet/registry/base.rb +2 -0
  50. data/lib/bullet/registry/object.rb +2 -0
  51. data/lib/bullet/registry.rb +2 -0
  52. data/lib/bullet/stack_trace_filter.rb +50 -20
  53. data/lib/bullet/version.rb +3 -2
  54. data/lib/bullet.rb +136 -55
  55. data/lib/generators/bullet/install_generator.rb +48 -0
  56. data/perf/benchmark.rb +19 -22
  57. data/rails/init.rb +2 -0
  58. data/spec/bullet/detector/association_spec.rb +6 -4
  59. data/spec/bullet/detector/base_spec.rb +2 -0
  60. data/spec/bullet/detector/counter_cache_spec.rb +17 -15
  61. data/spec/bullet/detector/n_plus_one_query_spec.rb +75 -46
  62. data/spec/bullet/detector/unused_eager_loading_spec.rb +45 -26
  63. data/spec/bullet/ext/object_spec.rb +18 -11
  64. data/spec/bullet/ext/string_spec.rb +7 -5
  65. data/spec/bullet/notification/base_spec.rb +38 -39
  66. data/spec/bullet/notification/counter_cache_spec.rb +5 -3
  67. data/spec/bullet/notification/n_plus_one_query_spec.rb +20 -5
  68. data/spec/bullet/notification/unused_eager_loading_spec.rb +9 -3
  69. data/spec/bullet/notification_collector_spec.rb +12 -10
  70. data/spec/bullet/rack_spec.rb +200 -48
  71. data/spec/bullet/registry/association_spec.rb +12 -10
  72. data/spec/bullet/registry/base_spec.rb +22 -20
  73. data/spec/bullet/registry/object_spec.rb +6 -4
  74. data/spec/bullet_spec.rb +64 -33
  75. data/spec/integration/{active_record4 → active_record}/association_spec.rb +238 -223
  76. data/spec/integration/counter_cache_spec.rb +26 -44
  77. data/spec/integration/mongoid/association_spec.rb +53 -65
  78. data/spec/models/address.rb +2 -0
  79. data/spec/models/attachment.rb +5 -0
  80. data/spec/models/author.rb +2 -0
  81. data/spec/models/base_user.rb +2 -0
  82. data/spec/models/category.rb +2 -0
  83. data/spec/models/city.rb +2 -0
  84. data/spec/models/client.rb +4 -0
  85. data/spec/models/comment.rb +3 -1
  86. data/spec/models/company.rb +2 -0
  87. data/spec/models/country.rb +2 -0
  88. data/spec/models/deal.rb +5 -0
  89. data/spec/models/document.rb +4 -2
  90. data/spec/models/entry.rb +2 -0
  91. data/spec/models/firm.rb +3 -0
  92. data/spec/models/folder.rb +2 -0
  93. data/spec/models/group.rb +4 -0
  94. data/spec/models/mongoid/address.rb +3 -1
  95. data/spec/models/mongoid/category.rb +4 -2
  96. data/spec/models/mongoid/comment.rb +3 -1
  97. data/spec/models/mongoid/company.rb +3 -1
  98. data/spec/models/mongoid/entry.rb +3 -1
  99. data/spec/models/mongoid/post.rb +6 -4
  100. data/spec/models/mongoid/user.rb +2 -0
  101. data/spec/models/newspaper.rb +3 -1
  102. data/spec/models/page.rb +2 -0
  103. data/spec/models/person.rb +2 -0
  104. data/spec/models/pet.rb +2 -0
  105. data/spec/models/post.rb +19 -0
  106. data/spec/models/relationship.rb +2 -0
  107. data/spec/models/reply.rb +2 -0
  108. data/spec/models/student.rb +2 -0
  109. data/spec/models/submission.rb +3 -0
  110. data/spec/models/teacher.rb +2 -0
  111. data/spec/models/user.rb +3 -0
  112. data/spec/models/writer.rb +2 -0
  113. data/spec/spec_helper.rb +19 -23
  114. data/spec/support/bullet_ext.rb +10 -9
  115. data/spec/support/mongo_seed.rb +38 -57
  116. data/spec/support/rack_double.rb +13 -19
  117. data/spec/support/sqlite_seed.rb +104 -76
  118. data/tasks/bullet_tasks.rake +4 -2
  119. data/test.sh +5 -0
  120. data/update.sh +3 -0
  121. metadata +34 -17
  122. data/.travis.yml +0 -16
  123. data/spec/integration/active_record5/association_spec.rb +0 -768
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bullet
4
+ module ActiveJob
5
+ def self.included(base)
6
+ base.class_eval do
7
+ around_perform do |_job, block|
8
+ Bullet.profile { block.call }
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module ActiveRecord
3
5
  def self.enable
4
6
  require 'active_record'
5
7
  ::ActiveRecord::Base.class_eval do
6
- class <<self
8
+ class << self
7
9
  alias_method :origin_find_by_sql, :find_by_sql
8
10
  def find_by_sql(sql, binds = [])
9
11
  result = origin_find_by_sql(sql, binds)
@@ -46,21 +48,10 @@ module Bullet
46
48
  end
47
49
 
48
50
  ::ActiveRecord::Persistence.class_eval do
49
- def save_with_bullet(*args, &proc)
50
- was_new_record = new_record?
51
- save_without_bullet(*args, &proc).tap do |result|
52
- Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
53
- end
54
- end
55
- alias_method_chain :save, :bullet
56
-
57
- def save_with_bullet!(*args, &proc)
58
- was_new_record = new_record?
59
- save_without_bullet!(*args, &proc).tap do |result|
60
- Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
61
- end
51
+ def _create_record_with_bullet(*args)
52
+ _create_record_without_bullet(*args).tap { Bullet::Detector::NPlusOneQuery.add_impossible_object(self) }
62
53
  end
63
- alias_method_chain :save!, :bullet
54
+ alias_method_chain :_create_record, :bullet
64
55
  end
65
56
 
66
57
  ::ActiveRecord::Associations::Preloader.class_eval do
@@ -69,12 +60,12 @@ module Bullet
69
60
  alias_method :origin_initialize, :initialize
70
61
  def initialize(records, associations, preload_scope = nil)
71
62
  origin_initialize(records, associations, preload_scope)
63
+
72
64
  if Bullet.start?
73
65
  records = [records].flatten.compact.uniq
74
66
  return if records.empty?
75
- records.each do |record|
76
- Bullet::Detector::Association.add_object_associations(record, associations)
77
- end
67
+
68
+ records.each { |record| Bullet::Detector::Association.add_object_associations(record, associations) }
78
69
  Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
79
70
  end
80
71
  end
@@ -87,9 +78,7 @@ module Bullet
87
78
  records = origin_find_with_associations
88
79
  if Bullet.start?
89
80
  associations = (eager_load_values + includes_values).uniq
90
- records.each do |record|
91
- Bullet::Detector::Association.add_object_associations(record, associations)
92
- end
81
+ records.each { |record| Bullet::Detector::Association.add_object_associations(record, associations) }
93
82
  Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
94
83
  end
95
84
  records
@@ -105,7 +94,7 @@ module Bullet
105
94
  records = origin_instantiate(rows)
106
95
 
107
96
  if Bullet.start?
108
- @bullet_eager_loadings.each do |klazz, eager_loadings_hash|
97
+ @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
109
98
  objects = eager_loadings_hash.keys
110
99
  Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
111
100
  end
@@ -134,17 +123,13 @@ module Bullet
134
123
  # call one to many associations
135
124
  alias_method :origin_load_target, :load_target
136
125
  def load_target
137
- if Bullet.start?
138
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
139
- end
126
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) if Bullet.start?
140
127
  origin_load_target
141
128
  end
142
129
 
143
130
  alias_method :origin_include?, :include?
144
131
  def include?(object)
145
- if Bullet.start?
146
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
147
- end
132
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) if Bullet.start?
148
133
  origin_include?(object)
149
134
  end
150
135
  end
@@ -162,9 +147,7 @@ module Bullet
162
147
  ::ActiveRecord::Associations::HasAndBelongsToManyAssociation.class_eval do
163
148
  alias_method :origin_empty?, :empty?
164
149
  def empty?
165
- if Bullet.start? && !loaded?
166
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
167
- end
150
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) if Bullet.start? && !loaded?
168
151
  origin_empty?
169
152
  end
170
153
  end
@@ -189,9 +172,7 @@ module Bullet
189
172
 
190
173
  def has_cached_counter?(reflection = reflection())
191
174
  result = origin_has_cached_counter?(reflection)
192
- if Bullet.start? && !result
193
- Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
194
- end
175
+ Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name) if Bullet.start? && !result
195
176
  result
196
177
  end
197
178
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module ActiveRecord
3
5
  def self.enable
4
6
  require 'active_record'
5
7
  ::ActiveRecord::Base.class_eval do
6
- class <<self
8
+ class << self
7
9
  alias_method :origin_find_by_sql, :find_by_sql
8
10
  def find_by_sql(sql, binds = [])
9
11
  result = origin_find_by_sql(sql, binds)
@@ -28,6 +30,7 @@ module Bullet
28
30
 
29
31
  ::ActiveRecord::Relation.class_eval do
30
32
  alias_method :origin_to_a, :to_a
33
+
31
34
  # if select a collection of objects, then these objects have possible to cause N+1 query.
32
35
  # if select only one object, then the only one object has impossible to cause N+1 query.
33
36
  def to_a
@@ -48,21 +51,10 @@ module Bullet
48
51
  end
49
52
 
50
53
  ::ActiveRecord::Persistence.class_eval do
51
- def save_with_bullet(*args, &proc)
52
- was_new_record = new_record?
53
- save_without_bullet(*args, &proc).tap do |result|
54
- Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
55
- end
56
- end
57
- alias_method_chain :save, :bullet
58
-
59
- def save_with_bullet!(*args, &proc)
60
- was_new_record = new_record?
61
- save_without_bullet!(*args, &proc).tap do |result|
62
- Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
63
- end
54
+ def _create_record_with_bullet(*args)
55
+ _create_record_without_bullet(*args).tap { Bullet::Detector::NPlusOneQuery.add_impossible_object(self) }
64
56
  end
65
- alias_method_chain :save!, :bullet
57
+ alias_method_chain :_create_record, :bullet
66
58
  end
67
59
 
68
60
  ::ActiveRecord::Associations::Preloader.class_eval do
@@ -72,9 +64,7 @@ module Bullet
72
64
  if Bullet.start?
73
65
  records.compact!
74
66
  if records.first.class.name !~ /^HABTM_/
75
- records.each do |record|
76
- Bullet::Detector::Association.add_object_associations(record, association)
77
- end
67
+ records.each { |record| Bullet::Detector::Association.add_object_associations(record, association) }
78
68
  Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
79
69
  end
80
70
  end
@@ -87,12 +77,11 @@ module Bullet
87
77
  alias_method :origin_find_with_associations, :find_with_associations
88
78
  def find_with_associations
89
79
  return origin_find_with_associations { |r| yield r } if block_given?
80
+
90
81
  records = origin_find_with_associations
91
82
  if Bullet.start?
92
83
  associations = (eager_load_values + includes_values).uniq
93
- records.each do |record|
94
- Bullet::Detector::Association.add_object_associations(record, associations)
95
- end
84
+ records.each { |record| Bullet::Detector::Association.add_object_associations(record, associations) }
96
85
  Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
97
86
  end
98
87
  records
@@ -108,7 +97,7 @@ module Bullet
108
97
  records = origin_instantiate(result_set, aliases)
109
98
 
110
99
  if Bullet.start?
111
- @bullet_eager_loadings.each do |klazz, eager_loadings_hash|
100
+ @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
112
101
  objects = eager_loadings_hash.keys
113
102
  Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
114
103
  end
@@ -137,9 +126,7 @@ module Bullet
137
126
  # call one to many associations
138
127
  alias_method :origin_load_target, :load_target
139
128
  def load_target
140
- if Bullet.start?
141
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
142
- end
129
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) if Bullet.start? && !@inversed
143
130
  origin_load_target
144
131
  end
145
132
 
@@ -153,9 +140,7 @@ module Bullet
153
140
 
154
141
  alias_method :origin_include?, :include?
155
142
  def include?(object)
156
- if Bullet.start?
157
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
158
- end
143
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) if Bullet.start?
159
144
  origin_include?(object)
160
145
  end
161
146
  end
@@ -179,9 +164,7 @@ module Bullet
179
164
  alias_method :origin_count_records, :count_records
180
165
  def count_records
181
166
  result = has_cached_counter?
182
- if Bullet.start? && !result
183
- Bullet::Detector::CounterCache.add_counter_cache(@owner, @reflection.name)
184
- end
167
+ Bullet::Detector::CounterCache.add_counter_cache(@owner, @reflection.name) if Bullet.start? && !result
185
168
  origin_count_records
186
169
  end
187
170
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module ActiveRecord
3
5
  def self.enable
4
6
  require 'active_record'
5
7
  ::ActiveRecord::Base.class_eval do
6
- class <<self
8
+ class << self
7
9
  alias_method :origin_find, :find
8
10
  def find(*args)
9
11
  result = origin_find(*args)
@@ -42,25 +44,15 @@ module Bullet
42
44
  end
43
45
 
44
46
  ::ActiveRecord::Persistence.class_eval do
45
- def save_with_bullet(*args, &proc)
46
- was_new_record = new_record?
47
- save_without_bullet(*args, &proc).tap do |result|
48
- Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
49
- end
50
- end
51
- alias_method_chain :save, :bullet
52
-
53
- def save_with_bullet!(*args, &proc)
54
- was_new_record = new_record?
55
- save_without_bullet!(*args, &proc).tap do |result|
56
- Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
57
- end
47
+ def _create_record_with_bullet(*args)
48
+ _create_record_without_bullet(*args).tap { Bullet::Detector::NPlusOneQuery.add_impossible_object(self) }
58
49
  end
59
- alias_method_chain :save!, :bullet
50
+ alias_method_chain :_create_record, :bullet
60
51
  end
61
52
 
62
53
  ::ActiveRecord::Relation.class_eval do
63
54
  alias_method :origin_to_a, :to_a
55
+
64
56
  # if select a collection of objects, then these objects have possible to cause N+1 query.
65
57
  # if select only one object, then the only one object has impossible to cause N+1 query.
66
58
  def to_a
@@ -87,9 +79,7 @@ module Bullet
87
79
  if Bullet.start?
88
80
  records.compact!
89
81
  if records.first.class.name !~ /^HABTM_/
90
- records.each do |record|
91
- Bullet::Detector::Association.add_object_associations(record, association)
92
- end
82
+ records.each { |record| Bullet::Detector::Association.add_object_associations(record, association) }
93
83
  Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
94
84
  end
95
85
  end
@@ -102,12 +92,11 @@ module Bullet
102
92
  alias_method :origin_find_with_associations, :find_with_associations
103
93
  def find_with_associations
104
94
  return origin_find_with_associations { |r| yield r } if block_given?
95
+
105
96
  records = origin_find_with_associations
106
97
  if Bullet.start?
107
98
  associations = (eager_load_values + includes_values).uniq
108
- records.each do |record|
109
- Bullet::Detector::Association.add_object_associations(record, associations)
110
- end
99
+ records.each { |record| Bullet::Detector::Association.add_object_associations(record, associations) }
111
100
  Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
112
101
  end
113
102
  records
@@ -124,7 +113,7 @@ module Bullet
124
113
  records = origin_instantiate(result_set, aliases)
125
114
 
126
115
  if Bullet.start?
127
- @bullet_eager_loadings.each do |klazz, eager_loadings_hash|
116
+ @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
128
117
  objects = eager_loadings_hash.keys
129
118
  Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
130
119
  end
@@ -138,14 +127,14 @@ module Bullet
138
127
  parent.children.each do |node|
139
128
  key = aliases.column_alias(node, node.primary_key)
140
129
  id = row[key]
141
- if id.nil?
142
- associations = node.reflection.name
143
- Bullet::Detector::Association.add_object_associations(ar_parent, associations)
144
- Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
145
- @bullet_eager_loadings[ar_parent.class] ||= {}
146
- @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
147
- @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
148
- end
130
+ next unless id.nil?
131
+
132
+ associations = node.reflection.name
133
+ Bullet::Detector::Association.add_object_associations(ar_parent, associations)
134
+ Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
135
+ @bullet_eager_loadings[ar_parent.class] ||= {}
136
+ @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
137
+ @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
149
138
  end
150
139
  end
151
140
  end
@@ -201,9 +190,7 @@ module Bullet
201
190
 
202
191
  alias_method :origin_include?, :include?
203
192
  def include?(object)
204
- if Bullet.start?
205
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
206
- end
193
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) if Bullet.start?
207
194
  origin_include?(object)
208
195
  end
209
196
  end
@@ -213,9 +200,11 @@ module Bullet
213
200
  alias_method :origin_reader, :reader
214
201
  def reader(force_reload = false)
215
202
  result = origin_reader(force_reload)
203
+
216
204
  if Bullet.start?
217
205
  if @owner.class.name !~ /^HABTM_/ && !@inversed
218
206
  Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
207
+
219
208
  if Bullet::Detector::NPlusOneQuery.impossible?(@owner)
220
209
  Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
221
210
  else
@@ -240,9 +229,7 @@ module Bullet
240
229
  alias_method :origin_count_records, :count_records
241
230
  def count_records
242
231
  result = has_cached_counter?
243
- if Bullet.start? && !result
244
- Bullet::Detector::CounterCache.add_counter_cache(@owner, @reflection.name)
245
- end
232
+ Bullet::Detector::CounterCache.add_counter_cache(@owner, @reflection.name) if Bullet.start? && !result
246
233
  origin_count_records
247
234
  end
248
235
  end