bullet 6.1.2 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +82 -0
  3. data/CHANGELOG.md +22 -0
  4. data/Gemfile.rails-7.0 +10 -0
  5. data/README.md +10 -7
  6. data/lib/bullet/active_record41.rb +1 -0
  7. data/lib/bullet/active_record42.rb +1 -0
  8. data/lib/bullet/active_record52.rb +22 -17
  9. data/lib/bullet/active_record60.rb +22 -17
  10. data/lib/bullet/active_record61.rb +22 -17
  11. data/lib/bullet/active_record70.rb +261 -0
  12. data/lib/bullet/bullet_xhr.js +1 -0
  13. data/lib/bullet/dependency.rb +10 -0
  14. data/lib/bullet/detector/base.rb +2 -1
  15. data/lib/bullet/detector/counter_cache.rb +1 -1
  16. data/lib/bullet/detector/n_plus_one_query.rb +3 -3
  17. data/lib/bullet/detector/unused_eager_loading.rb +1 -1
  18. data/lib/bullet/mongoid4x.rb +1 -1
  19. data/lib/bullet/mongoid5x.rb +1 -1
  20. data/lib/bullet/mongoid6x.rb +1 -1
  21. data/lib/bullet/mongoid7x.rb +24 -7
  22. data/lib/bullet/notification.rb +2 -1
  23. data/lib/bullet/rack.rb +5 -3
  24. data/lib/bullet/stack_trace_filter.rb +7 -9
  25. data/lib/bullet/version.rb +1 -1
  26. data/lib/bullet.rb +68 -22
  27. data/perf/benchmark.rb +4 -1
  28. data/spec/bullet/detector/unused_eager_loading_spec.rb +6 -2
  29. data/spec/bullet/ext/object_spec.rb +1 -1
  30. data/spec/bullet/rack_spec.rb +88 -17
  31. data/spec/bullet_spec.rb +39 -10
  32. data/spec/integration/active_record/association_spec.rb +53 -8
  33. data/spec/integration/counter_cache_spec.rb +4 -4
  34. data/spec/integration/mongoid/association_spec.rb +1 -1
  35. data/spec/models/attachment.rb +5 -0
  36. data/spec/models/deal.rb +5 -0
  37. data/spec/models/folder.rb +2 -1
  38. data/spec/models/group.rb +2 -1
  39. data/spec/models/page.rb +2 -1
  40. data/spec/models/post.rb +2 -0
  41. data/spec/models/submission.rb +1 -0
  42. data/spec/models/user.rb +1 -0
  43. data/spec/models/writer.rb +2 -1
  44. data/spec/spec_helper.rb +0 -2
  45. data/spec/support/mongo_seed.rb +1 -0
  46. data/spec/support/sqlite_seed.rb +20 -0
  47. data/test.sh +2 -0
  48. metadata +10 -4
  49. data/.travis.yml +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0363cc75d4facd4a8b4a318312a421040d5b21d7ca81c0b3af21daaf7c2b60cd
4
- data.tar.gz: ac0b0e4ed9c1641b5359b3d885ef854934b85b8ee21bc0486a4c3d2661e20853
3
+ metadata.gz: 6fbdfd6fe2f412c6632b877352fdf912b56fd54e255101dc16ae414a218fc2ee
4
+ data.tar.gz: 0a765e969ccae1ef685051350324052225fea00e80d7d37a6b8cce6d8ad1f28e
5
5
  SHA512:
6
- metadata.gz: 199558c8186455fb2ddac94461d611d1c40eb0fb83ac68e36647efe0a34927020967a803cb24645b7ae3d5d045aa0060c604b455476f8c6aad6b5329103a7ebe
7
- data.tar.gz: 86c4bcf9fcca52275229b7fa5e02919c39318d91a60c7fe956d4cab07442cbacfc3d9df134af1ee38bc7f153eeb2cd256faff45815ad83acd296c1831d1fc9ee
6
+ metadata.gz: 35c561cc6534f355a56d1aa0e880bb51941c126ce8a9c704bbc8c0f59c67cf5890a00d650919d5a458a913f4d6a125b17e57c92130d48baa9b6a90db190db763
7
+ data.tar.gz: 4f29d4316862401ad1d5d4edfdf21f6f8045a37e21acc4d12f3cdd80e528b12924a9f0a2ab5fdb366432ea5c8e656210887112fc338da8539a34586da88710e9
@@ -0,0 +1,82 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: CI
9
+
10
+ on:
11
+ push:
12
+ branches: [ master ]
13
+ pull_request:
14
+ branches: [ master ]
15
+
16
+ jobs:
17
+ test_rails_4:
18
+ runs-on: ubuntu-latest
19
+ strategy:
20
+ matrix:
21
+ gemfile: ['Gemfile.rails-4.0', 'Gemfile.rails-4.1', 'Gemfile.rails-4.2']
22
+ env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
23
+ BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}
24
+ steps:
25
+ - uses: actions/checkout@v2
26
+ - name: Set up Ruby
27
+ uses: ruby/setup-ruby@v1
28
+ with:
29
+ ruby-version: 2.3
30
+ bundler: 1
31
+ bundler-cache: true
32
+ - name: Run tests
33
+ run: bundle exec rake
34
+ test_rails_5:
35
+ runs-on: ubuntu-latest
36
+ strategy:
37
+ matrix:
38
+ gemfile: ['Gemfile.rails-5.0', 'Gemfile.rails-5.1', 'Gemfile.rails-5.2']
39
+ env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
40
+ BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}
41
+ steps:
42
+ - uses: actions/checkout@v2
43
+ - name: Set up Ruby
44
+ uses: ruby/setup-ruby@v1
45
+ with:
46
+ ruby-version: 2.5
47
+ bundler: 1
48
+ bundler-cache: true
49
+ - name: Run tests
50
+ run: bundle exec rake
51
+ test_rails_6:
52
+ runs-on: ubuntu-latest
53
+ strategy:
54
+ matrix:
55
+ gemfile: ['Gemfile.rails-6.0', 'Gemfile.rails-6.1']
56
+ env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
57
+ BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}
58
+ steps:
59
+ - uses: actions/checkout@v2
60
+ - name: Set up Ruby
61
+ uses: ruby/setup-ruby@v1
62
+ with:
63
+ ruby-version: 2.7
64
+ bundler-cache: true
65
+ - name: Run tests
66
+ run: bundle exec rake
67
+ test_rails_7:
68
+ runs-on: ubuntu-latest
69
+ strategy:
70
+ matrix:
71
+ gemfile: ['Gemfile.rails-7.0']
72
+ env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
73
+ BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}
74
+ steps:
75
+ - uses: actions/checkout@v2
76
+ - name: Set up Ruby
77
+ uses: ruby/setup-ruby@v1
78
+ with:
79
+ ruby-version: 3.0
80
+ bundler-cache: true
81
+ - name: Run tests
82
+ run: bundle exec rake
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  ## Next Release
2
2
 
3
+ ## 7.0.0 (12/18/2021)
4
+
5
+ * Support rails 7
6
+ * Fix Mongoid 7 view iteration
7
+ * Move CI from Travis to Github Actions
8
+
9
+ ## 6.1.5 (08/16/2021)
10
+
11
+ * Rename whitelist to safelist
12
+ * Fix onload called twice
13
+ * Support Rack::Files::Iterator responses
14
+ * Ensure HABTM associations are not incorrectly labeled n+1
15
+
16
+ ## 6.1.4 (02/26/2021)
17
+
18
+ * Added an option to stop adding HTTP headers to API requests
19
+
20
+ ## 6.1.3 (01/21/2021)
21
+
22
+ * Consider ThroughAssociation at SingularAssociation like CollectionAssociation
23
+ * Add xhr_script only when add_footer is enabled
24
+
3
25
  ## 6.1.2 (12/12/2020)
4
26
 
5
27
  * Revert "Make whitelist thread safe"
data/Gemfile.rails-7.0 ADDED
@@ -0,0 +1,10 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'rails', '~> 7.0.0'
6
+ gem 'sqlite3'
7
+ gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
+ gem 'activerecord-import'
9
+
10
+ gem "rspec"
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Bullet
2
2
 
3
+ ![Main workflow](https://github.com/flyerhzm/bullet/actions/workflows/main.yml/badge.svg)
3
4
  [![Gem Version](https://badge.fury.io/rb/bullet.svg)](http://badge.fury.io/rb/bullet)
4
- [![Build Status](https://secure.travis-ci.org/flyerhzm/bullet.svg)](http://travis-ci.org/flyerhzm/bullet)
5
5
  [![AwesomeCode Status for flyerhzm/bullet](https://awesomecode.io/projects/6755235b-e2c1-459e-bf92-b8b13d0c0472/status)](https://awesomecode.io/repos/flyerhzm/bullet)
6
6
  [![Coderwall Endorse](http://api.coderwall.com/flyerhzm/endorsecount.png)](http://coderwall.com/flyerhzm)
7
7
 
@@ -67,6 +67,7 @@ config.after_initialize do
67
67
  Bullet.rails_logger = true
68
68
  Bullet.honeybadger = true
69
69
  Bullet.bugsnag = true
70
+ Bullet.appsignal = true
70
71
  Bullet.airbrake = true
71
72
  Bullet.rollbar = true
72
73
  Bullet.add_footer = true
@@ -90,10 +91,12 @@ The code above will enable all of the Bullet notification systems:
90
91
  * `Bullet.honeybadger`: add notifications to Honeybadger
91
92
  * `Bullet.bugsnag`: add notifications to bugsnag
92
93
  * `Bullet.airbrake`: add notifications to airbrake
94
+ * `Bullet.appsignal`: add notifications to AppSignal
93
95
  * `Bullet.rollbar`: add notifications to rollbar
94
96
  * `Bullet.sentry`: add notifications to sentry
95
97
  * `Bullet.add_footer`: adds the details in the bottom left corner of the page. Double click the footer or use close button to hide footer.
96
- * `Bullet.skip_html_injection`: prevents Bullet from injecting XHR into the returned HTML. This must be false for receiving alerts or console logging.
98
+ * `Bullet.skip_html_injection`: prevents Bullet from injecting code into the returned HTML. This must be false for receiving alerts, showing the footer or console logging.
99
+ * `Bullet.skip_http_headers`: don't add headers to API requests, and remove the javascript that relies on them. Note that this prevents bullet from logging warnings to the browser console or updating the footer.
97
100
  * `Bullet.stacktrace_includes`: include paths with any of these substrings in the stack trace, even if they are not in your main app
98
101
  * `Bullet.stacktrace_excludes`: ignore paths with any of these substrings in the stack trace, even if they are not in your main app.
99
102
  Each item can be a string (match substring), a regex, or an array where the first item is a path to match, and the second
@@ -118,15 +121,15 @@ Bullet.unused_eager_loading_enable = false
118
121
  Bullet.counter_cache_enable = false
119
122
  ```
120
123
 
121
- ## Whitelist
124
+ ## Safe list
122
125
 
123
126
  Sometimes Bullet may notify you of query problems you don't care to fix, or
124
- which come from outside your code. You can whitelist these to ignore them:
127
+ which come from outside your code. You can add them to a safe list to ignore them:
125
128
 
126
129
  ```ruby
127
- Bullet.add_whitelist :type => :n_plus_one_query, :class_name => "Post", :association => :comments
128
- Bullet.add_whitelist :type => :unused_eager_loading, :class_name => "Post", :association => :comments
129
- Bullet.add_whitelist :type => :counter_cache, :class_name => "Country", :association => :cities
130
+ Bullet.add_safelist :type => :n_plus_one_query, :class_name => "Post", :association => :comments
131
+ Bullet.add_safelist :type => :unused_eager_loading, :class_name => "Post", :association => :comments
132
+ Bullet.add_safelist :type => :counter_cache, :class_name => "Country", :association => :cities
130
133
  ```
131
134
 
132
135
  If you want to skip bullet in some specific controller actions, you can
@@ -30,6 +30,7 @@ module Bullet
30
30
 
31
31
  ::ActiveRecord::Relation.class_eval do
32
32
  alias_method :origin_to_a, :to_a
33
+
33
34
  # if select a collection of objects, then these objects have possible to cause N+1 query.
34
35
  # if select only one object, then the only one object has impossible to cause N+1 query.
35
36
  def to_a
@@ -52,6 +52,7 @@ module Bullet
52
52
 
53
53
  ::ActiveRecord::Relation.class_eval do
54
54
  alias_method :origin_to_a, :to_a
55
+
55
56
  # if select a collection of objects, then these objects have possible to cause N+1 query.
56
57
  # if select only one object, then the only one object has impossible to cause N+1 query.
57
58
  def to_a
@@ -75,23 +75,6 @@ module Bullet
75
75
  end
76
76
  )
77
77
 
78
- ::ActiveRecord::FinderMethods.prepend(
79
- Module.new do
80
- # add includes in scope
81
- def find_with_associations
82
- return super { |r| yield r } if block_given?
83
-
84
- records = super
85
- if Bullet.start?
86
- associations = (eager_load_values + includes_values).uniq
87
- records.each { |record| Bullet::Detector::Association.add_object_associations(record, associations) }
88
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
89
- end
90
- records
91
- end
92
- end
93
- )
94
-
95
78
  ::ActiveRecord::Associations::JoinDependency.prepend(
96
79
  Module.new do
97
80
  def instantiate(result_set, &block)
@@ -149,6 +132,17 @@ module Bullet
149
132
  end
150
133
  )
151
134
 
135
+ ::ActiveRecord::Associations::Association.prepend(
136
+ Module.new do
137
+ def inversed_from(record)
138
+ if Bullet.start?
139
+ Bullet::Detector::NPlusOneQuery.add_inversed_object(owner, reflection.name)
140
+ end
141
+ super
142
+ end
143
+ end
144
+ )
145
+
152
146
  ::ActiveRecord::Associations::CollectionAssociation.prepend(
153
147
  Module.new do
154
148
  def load_target
@@ -202,6 +196,17 @@ module Bullet
202
196
 
203
197
  if Bullet.start?
204
198
  if owner.class.name !~ /^HABTM_/ && !@inversed
199
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
200
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
201
+ association = owner.association reflection.through_reflection.name
202
+ Array(association.target).each do |through_record|
203
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
204
+ end
205
+
206
+ if reflection.through_reflection != through_reflection
207
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
208
+ end
209
+ end
205
210
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
206
211
 
207
212
  if Bullet::Detector::NPlusOneQuery.impossible?(owner)
@@ -102,23 +102,6 @@ module Bullet
102
102
  end
103
103
  )
104
104
 
105
- ::ActiveRecord::FinderMethods.prepend(
106
- Module.new do
107
- # add includes in scope
108
- def find_with_associations
109
- return super { |r| yield r } if block_given?
110
-
111
- records = super
112
- if Bullet.start?
113
- associations = (eager_load_values + includes_values).uniq
114
- records.each { |record| Bullet::Detector::Association.add_object_associations(record, associations) }
115
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
116
- end
117
- records
118
- end
119
- end
120
- )
121
-
122
105
  ::ActiveRecord::Associations::JoinDependency.prepend(
123
106
  Module.new do
124
107
  def instantiate(result_set, &block)
@@ -176,6 +159,17 @@ module Bullet
176
159
  end
177
160
  )
178
161
 
162
+ ::ActiveRecord::Associations::Association.prepend(
163
+ Module.new do
164
+ def inversed_from(record)
165
+ if Bullet.start?
166
+ Bullet::Detector::NPlusOneQuery.add_inversed_object(owner, reflection.name)
167
+ end
168
+ super
169
+ end
170
+ end
171
+ )
172
+
179
173
  ::ActiveRecord::Associations::CollectionAssociation.prepend(
180
174
  Module.new do
181
175
  def load_target
@@ -229,6 +223,17 @@ module Bullet
229
223
 
230
224
  if Bullet.start?
231
225
  if owner.class.name !~ /^HABTM_/ && !@inversed
226
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
227
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
228
+ association = owner.association(reflection.through_reflection.name)
229
+ Array(association.target).each do |through_record|
230
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
231
+ end
232
+
233
+ if reflection.through_reflection != through_reflection
234
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
235
+ end
236
+ end
232
237
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
233
238
 
234
239
  if Bullet::Detector::NPlusOneQuery.impossible?(owner)
@@ -102,23 +102,6 @@ module Bullet
102
102
  end
103
103
  )
104
104
 
105
- ::ActiveRecord::FinderMethods.prepend(
106
- Module.new do
107
- # add includes in scope
108
- def find_with_associations
109
- return super { |r| yield r } if block_given?
110
-
111
- records = super
112
- if Bullet.start?
113
- associations = (eager_load_values + includes_values).uniq
114
- records.each { |record| Bullet::Detector::Association.add_object_associations(record, associations) }
115
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
116
- end
117
- records
118
- end
119
- end
120
- )
121
-
122
105
  ::ActiveRecord::Associations::JoinDependency.prepend(
123
106
  Module.new do
124
107
  def instantiate(result_set, strict_loading_value, &block)
@@ -176,6 +159,17 @@ module Bullet
176
159
  end
177
160
  )
178
161
 
162
+ ::ActiveRecord::Associations::Association.prepend(
163
+ Module.new do
164
+ def inversed_from(record)
165
+ if Bullet.start?
166
+ Bullet::Detector::NPlusOneQuery.add_inversed_object(owner, reflection.name)
167
+ end
168
+ super
169
+ end
170
+ end
171
+ )
172
+
179
173
  ::ActiveRecord::Associations::CollectionAssociation.prepend(
180
174
  Module.new do
181
175
  def load_target
@@ -229,6 +223,17 @@ module Bullet
229
223
 
230
224
  if Bullet.start?
231
225
  if owner.class.name !~ /^HABTM_/ && !@inversed
226
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
227
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
228
+ association = owner.association(reflection.through_reflection.name)
229
+ Array(association.target).each do |through_record|
230
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
231
+ end
232
+
233
+ if reflection.through_reflection != through_reflection
234
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
235
+ end
236
+ end
232
237
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
233
238
 
234
239
  if Bullet::Detector::NPlusOneQuery.impossible?(owner)
@@ -0,0 +1,261 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bullet
4
+ module SaveWithBulletSupport
5
+ def _create_record(*)
6
+ super do
7
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
8
+ yield(self) if block_given?
9
+ end
10
+ end
11
+ end
12
+
13
+ module ActiveRecord
14
+ def self.enable
15
+ require 'active_record'
16
+ ::ActiveRecord::Base.extend(
17
+ Module.new do
18
+ def find_by_sql(sql, binds = [], preparable: nil, &block)
19
+ result = super
20
+ if Bullet.start?
21
+ if result.is_a? Array
22
+ if result.size > 1
23
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
24
+ Bullet::Detector::CounterCache.add_possible_objects(result)
25
+ elsif result.size == 1
26
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
27
+ Bullet::Detector::CounterCache.add_impossible_object(result.first)
28
+ end
29
+ elsif result.is_a? ::ActiveRecord::Base
30
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
31
+ Bullet::Detector::CounterCache.add_impossible_object(result)
32
+ end
33
+ end
34
+ result
35
+ end
36
+ end
37
+ )
38
+
39
+ ::ActiveRecord::Base.prepend(SaveWithBulletSupport)
40
+
41
+ ::ActiveRecord::Relation.prepend(
42
+ Module.new do
43
+ # if select a collection of objects, then these objects have possible to cause N+1 query.
44
+ # if select only one object, then the only one object has impossible to cause N+1 query.
45
+ def records
46
+ result = super
47
+ if Bullet.start?
48
+ if result.first.class.name !~ /^HABTM_/
49
+ if result.size > 1
50
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
51
+ Bullet::Detector::CounterCache.add_possible_objects(result)
52
+ elsif result.size == 1
53
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
54
+ Bullet::Detector::CounterCache.add_impossible_object(result.first)
55
+ end
56
+ end
57
+ end
58
+ result
59
+ end
60
+ end
61
+ )
62
+
63
+ ::ActiveRecord::Associations::Preloader::Branch.prepend(
64
+ Module.new do
65
+ def preloaders_for_reflection(reflection, reflection_records)
66
+ if Bullet.start?
67
+ reflection_records.compact!
68
+ if reflection_records.first.class.name !~ /^HABTM_/
69
+ reflection_records.each { |record| Bullet::Detector::Association.add_object_associations(record, reflection.name) }
70
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(reflection_records, reflection.name)
71
+ end
72
+ end
73
+ super
74
+ end
75
+ end
76
+ )
77
+
78
+ ::ActiveRecord::Associations::Preloader::ThroughAssociation.prepend(
79
+ Module.new do
80
+ def preloaded_records
81
+ if Bullet.start? && !defined?(@preloaded_records)
82
+ source_preloaders.each do |source_preloader|
83
+ reflection_name = source_preloader.send(:reflection).name
84
+ source_preloader.send(:owners).each do |owner|
85
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection_name)
86
+ end
87
+ end
88
+ end
89
+ super
90
+ end
91
+ end
92
+ )
93
+
94
+ ::ActiveRecord::Associations::JoinDependency.prepend(
95
+ Module.new do
96
+ def instantiate(result_set, strict_loading_value, &block)
97
+ @bullet_eager_loadings = {}
98
+ records = super
99
+
100
+ if Bullet.start?
101
+ @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
102
+ objects = eager_loadings_hash.keys
103
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(
104
+ objects,
105
+ eager_loadings_hash[objects.first].to_a
106
+ )
107
+ end
108
+ end
109
+ records
110
+ end
111
+
112
+ def construct(ar_parent, parent, row, seen, model_cache, strict_loading_value)
113
+ if Bullet.start?
114
+ unless ar_parent.nil?
115
+ parent.children.each do |node|
116
+ key = aliases.column_alias(node, node.primary_key)
117
+ id = row[key]
118
+ next unless id.nil?
119
+
120
+ associations = node.reflection.name
121
+ Bullet::Detector::Association.add_object_associations(ar_parent, associations)
122
+ Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
123
+ @bullet_eager_loadings[ar_parent.class] ||= {}
124
+ @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
125
+ @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
126
+ end
127
+ end
128
+ end
129
+
130
+ super
131
+ end
132
+
133
+ # call join associations
134
+ def construct_model(record, node, row, model_cache, id, strict_loading_value)
135
+ result = super
136
+
137
+ if Bullet.start?
138
+ associations = node.reflection.name
139
+ Bullet::Detector::Association.add_object_associations(record, associations)
140
+ Bullet::Detector::NPlusOneQuery.call_association(record, associations)
141
+ @bullet_eager_loadings[record.class] ||= {}
142
+ @bullet_eager_loadings[record.class][record] ||= Set.new
143
+ @bullet_eager_loadings[record.class][record] << associations
144
+ end
145
+
146
+ result
147
+ end
148
+ end
149
+ )
150
+
151
+ ::ActiveRecord::Associations::Association.prepend(
152
+ Module.new do
153
+ def inversed_from(record)
154
+ if Bullet.start?
155
+ Bullet::Detector::NPlusOneQuery.add_inversed_object(owner, reflection.name)
156
+ end
157
+ super
158
+ end
159
+ end
160
+ )
161
+
162
+ ::ActiveRecord::Associations::CollectionAssociation.prepend(
163
+ Module.new do
164
+ def load_target
165
+ records = super
166
+
167
+ if Bullet.start?
168
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
169
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
170
+ association = owner.association(reflection.through_reflection.name)
171
+ Array(association.target).each do |through_record|
172
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
173
+ end
174
+
175
+ if reflection.through_reflection != through_reflection
176
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
177
+ end
178
+ end
179
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
180
+ if records.first.class.name !~ /^HABTM_/
181
+ if records.size > 1
182
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
183
+ Bullet::Detector::CounterCache.add_possible_objects(records)
184
+ elsif records.size == 1
185
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
186
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
187
+ end
188
+ end
189
+ end
190
+ records
191
+ end
192
+
193
+ def empty?
194
+ if Bullet.start? && !reflection.has_cached_counter?
195
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
196
+ end
197
+ super
198
+ end
199
+
200
+ def include?(object)
201
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) if Bullet.start?
202
+ super
203
+ end
204
+ end
205
+ )
206
+
207
+ ::ActiveRecord::Associations::SingularAssociation.prepend(
208
+ Module.new do
209
+ # call has_one and belongs_to associations
210
+ def reader
211
+ result = super
212
+
213
+ if Bullet.start?
214
+ if owner.class.name !~ /^HABTM_/
215
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
216
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
217
+ association = owner.association(reflection.through_reflection.name)
218
+ Array(association.target).each do |through_record|
219
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
220
+ end
221
+
222
+ if reflection.through_reflection != through_reflection
223
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
224
+ end
225
+ end
226
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
227
+
228
+ if Bullet::Detector::NPlusOneQuery.impossible?(owner)
229
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
230
+ else
231
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
232
+ end
233
+ end
234
+ end
235
+ result
236
+ end
237
+ end
238
+ )
239
+
240
+ ::ActiveRecord::Associations::HasManyAssociation.prepend(
241
+ Module.new do
242
+ def empty?
243
+ result = super
244
+ if Bullet.start? && !reflection.has_cached_counter?
245
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
246
+ end
247
+ result
248
+ end
249
+
250
+ def count_records
251
+ result = reflection.has_cached_counter?
252
+ if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
253
+ Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
254
+ end
255
+ super
256
+ end
257
+ end
258
+ )
259
+ end
260
+ end
261
+ end
@@ -20,6 +20,7 @@
20
20
  if (this.onload) {
21
21
  this._storedOnload = this.onload;
22
22
  }
23
+ this.onload = null
23
24
  this.addEventListener("load", bulletXHROnload);
24
25
  return Reflect.apply(oldSend, this, arguments);
25
26
  }
@@ -29,6 +29,8 @@ module Bullet
29
29
  'active_record60'
30
30
  elsif active_record61?
31
31
  'active_record61'
32
+ elsif active_record70?
33
+ 'active_record70'
32
34
  else
33
35
  raise "Bullet does not support active_record #{::ActiveRecord::VERSION::STRING} yet"
34
36
  end
@@ -64,6 +66,10 @@ module Bullet
64
66
  active_record? && ::ActiveRecord::VERSION::MAJOR == 6
65
67
  end
66
68
 
69
+ def active_record7?
70
+ active_record? && ::ActiveRecord::VERSION::MAJOR == 7
71
+ end
72
+
67
73
  def active_record40?
68
74
  active_record4? && ::ActiveRecord::VERSION::MINOR == 0
69
75
  end
@@ -96,6 +102,10 @@ module Bullet
96
102
  active_record6? && ::ActiveRecord::VERSION::MINOR == 1
97
103
  end
98
104
 
105
+ def active_record70?
106
+ active_record7? && ::ActiveRecord::VERSION::MINOR == 0
107
+ end
108
+
99
109
  def mongoid4x?
100
110
  mongoid? && ::Mongoid::VERSION =~ /\A4/
101
111
  end