bullet 6.1.5 → 7.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b21c2c4ca3caf6c41961a7182a5c8d37f6b89aa78b8dfb7badfb940f11a23191
4
- data.tar.gz: 0f432034f9b4cb2fe6c6572481c797aefcfa69373839fbd0985a7efd2a0bbea9
3
+ metadata.gz: 9190b7377e0184967fec123b7926cc2287755989ceea1a4c90a949721d29f411
4
+ data.tar.gz: 9ae038298d2367a5862eeb6480565f9ac9d104887b97f417e390266e0bfbdb8e
5
5
  SHA512:
6
- metadata.gz: d2457987f11f034fa457030cf7161d7508fb26230cf9e88a2571c54a934ad9a1330b48dd3d38eede3033bd7ef6c920e564dcbfa43a15354bf72d14ed799aefe2
7
- data.tar.gz: ed6690fcb3bd778d6d5a27538a0ee78d298c749be1e445c305b9884448ca13f97e5a62c76ac48229a250041bb09ba609d6b154b12cf42a0f3dca66fbb9c83021
6
+ metadata.gz: 5b8679cc1801319b7527c472be94d75a5df7a3e03a16f2ef444d3708de584f1f8985ceedd079c35c43cc9ffb39ee5a1664b5c12d38d3d194a686692495873612
7
+ data.tar.gz: 6309d7925415f5a6339ab4e180a6ef0cd3d27c3308f3a39f6bbfa120c4cb0825f6c2728365942f71e810cb967504b72ad6fa2893334adb4ce6597299cbc4abbd
@@ -64,3 +64,19 @@ jobs:
64
64
  bundler-cache: true
65
65
  - name: Run tests
66
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.1
80
+ bundler-cache: true
81
+ - name: Run tests
82
+ run: bundle exec rake
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  ## Next Release
2
2
 
3
+ ## 7.0.2 (05/31/2022)
4
+
5
+ * Drop growl support
6
+ * Do not check html tag in Bullet::Rack anymore
7
+
8
+ ## 7.0.1 (01/15/2022)
9
+
10
+ * Get rid of *_whitelist methods
11
+ * Hack ActiveRecord::Associations::Preloader::Batch in rails 7
12
+
13
+ ## 7.0.0 (12/18/2021)
14
+
15
+ * Support rails 7
16
+ * Fix Mongoid 7 view iteration
17
+ * Move CI from Travis to Github Actions
18
+
3
19
  ## 6.1.5 (08/16/2021)
4
20
 
5
21
  * Rename whitelist to safelist
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
@@ -49,7 +49,7 @@ mongoid.
49
49
 
50
50
  ## Configuration
51
51
 
52
- Bullet won't do ANYTHING unless you tell it to explicitly. Append to
52
+ Bullet won't enable any notification systems unless you tell it to explicitly. Append to
53
53
  `config/environments/development.rb` initializer with the following code:
54
54
 
55
55
  ```ruby
@@ -59,7 +59,6 @@ config.after_initialize do
59
59
  Bullet.alert = true
60
60
  Bullet.bullet_logger = true
61
61
  Bullet.console = true
62
- Bullet.growl = true
63
62
  Bullet.xmpp = { :account => 'bullets_account@jabber.org',
64
63
  :password => 'bullets_password_for_jabber',
65
64
  :receiver => 'your_account@jabber.org',
@@ -85,7 +84,6 @@ The code above will enable all of the Bullet notification systems:
85
84
  * `Bullet.alert`: pop up a JavaScript alert in the browser
86
85
  * `Bullet.bullet_logger`: log to the Bullet log file (Rails.root/log/bullet.log)
87
86
  * `Bullet.console`: log warnings to your browser's console.log (Safari/Webkit browsers or Firefox w/Firebug installed)
88
- * `Bullet.growl`: pop up Growl warnings if your system has Growl installed. Requires a little bit of configuration
89
87
  * `Bullet.xmpp`: send XMPP/Jabber notifications to the receiver indicated. Note that the code will currently not handle the adding of contacts, so you will need to make both accounts indicated know each other manually before you will receive any notifications. If you restart the development server frequently, the 'coming online' sound for the Bullet account may start to annoy - in this case set :show_online_status to false; you will still get notifications, but the Bullet account won't announce it's online status anymore.
90
88
  * `Bullet.rails_logger`: add warnings directly to the Rails log
91
89
  * `Bullet.honeybadger`: add notifications to Honeybadger
@@ -156,25 +154,26 @@ The Bullet log `log/bullet.log` will look something like this:
156
154
  * N+1 Query:
157
155
 
158
156
  ```
159
- 2009-08-25 20:40:17[INFO] N+1 Query: PATH_INFO: /posts; model: Post => associations: [comments]·
160
- Add to your finder: :include => [:comments]
161
- 2009-08-25 20:40:17[INFO] N+1 Query: method call stack:·
162
- /Users/richard/Downloads/test/app/views/posts/index.html.erb:11:in `_run_erb_app47views47posts47index46html46erb'
163
- /Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `each'
164
- /Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `_run_erb_app47views47posts47index46html46erb'
165
- /Users/richard/Downloads/test/app/controllers/posts_controller.rb:7:in `index'
157
+ 2009-08-25 20:40:17[INFO] USE eager loading detected:
158
+ Post => [:comments]·
159
+ Add to your query: .includes([:comments])
160
+ 2009-08-25 20:40:17[INFO] Call stack
161
+ /Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `each'
162
+ /Users/richard/Downloads/test/app/controllers/posts_controller.rb:7:in `index'
166
163
  ```
167
164
 
168
- The first two lines are notifications that N+1 queries have been encountered. The remaining lines are stack traces so you can find exactly where the queries were invoked in your code, and fix them.
165
+ The first log entry is a notification that N+1 queries have been encountered. The remaining entry is a stack trace so you can find exactly where the queries were invoked in your code, and fix them.
169
166
 
170
167
  * Unused eager loading:
171
168
 
172
169
  ```
173
- 2009-08-25 20:53:56[INFO] Unused eager loadings: PATH_INFO: /posts; model: Post => associations: [comments]·
174
- Remove from your finder: :include => [:comments]
170
+ 2009-08-25 20:53:56[INFO] AVOID eager loading detected
171
+ Post => [:comments]·
172
+ Remove from your query: .includes([:comments])
173
+ 2009-08-25 20:53:56[INFO] Call stack
175
174
  ```
176
175
 
177
- These two lines are notifications that unused eager loadings have been encountered.
176
+ These lines are notifications that unused eager loadings have been encountered.
178
177
 
179
178
  * Need counter cache:
180
179
 
@@ -183,10 +182,14 @@ These two lines are notifications that unused eager loadings have been encounter
183
182
  Post => [:comments]
184
183
  ```
185
184
 
186
- ## Growl, XMPP/Jabber and Airbrake Support
185
+ ## XMPP/Jabber and Airbrake Support
187
186
 
188
187
  see [https://github.com/flyerhzm/uniform_notifier](https://github.com/flyerhzm/uniform_notifier)
189
188
 
189
+ ## Growl Support
190
+
191
+ Growl support is dropped from uniform_notifier 1.16.0, if you still want it, please use uniform_notifier 1.15.0.
192
+
190
193
  ## Important
191
194
 
192
195
  If you find Bullet does not work for you, *please disable your browser's cache*.
@@ -270,8 +273,7 @@ Bullet outputs some details info, to enable debug mode, set
270
273
  ## Demo
271
274
 
272
275
  Bullet is designed to function as you browse through your application in development. To see it in action,
273
- you can visit [https://github.com/flyerhzm/bullet_test](https://github.com/flyerhzm/bullet_test) or
274
- follow these steps to create, detect, and fix example query problems.
276
+ you can follow these steps to create, detect, and fix example query problems.
275
277
 
276
278
  1\. Create an example application
277
279
 
@@ -482,4 +484,4 @@ Meanwhile, there's a line appended to `log/bullet.log`
482
484
  Post => [:comments]
483
485
  ```
484
486
 
485
- Copyright (c) 2009 - 2019 Richard Huang (flyerhzm@gmail.com), released under the MIT license
487
+ Copyright (c) 2009 - 2022 Richard Huang (flyerhzm@gmail.com), released under the MIT license
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,275 @@
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::Batch.prepend(
64
+ Module.new do
65
+ def call
66
+ if Bullet.start?
67
+ @preloaders.each do |preloader|
68
+ preloader.records.each { |record| Bullet::Detector::Association.add_object_associations(record, preloader.associations) }
69
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(preloader.records, preloader.associations)
70
+ end
71
+ end
72
+ super
73
+ end
74
+ end
75
+ )
76
+
77
+ ::ActiveRecord::Associations::Preloader::Branch.prepend(
78
+ Module.new do
79
+ def preloaders_for_reflection(reflection, reflection_records)
80
+ if Bullet.start?
81
+ reflection_records.compact!
82
+ if reflection_records.first.class.name !~ /^HABTM_/
83
+ reflection_records.each { |record| Bullet::Detector::Association.add_object_associations(record, reflection.name) }
84
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(reflection_records, reflection.name)
85
+ end
86
+ end
87
+ super
88
+ end
89
+ end
90
+ )
91
+
92
+ ::ActiveRecord::Associations::Preloader::ThroughAssociation.prepend(
93
+ Module.new do
94
+ def preloaded_records
95
+ if Bullet.start? && !defined?(@preloaded_records)
96
+ source_preloaders.each do |source_preloader|
97
+ reflection_name = source_preloader.send(:reflection).name
98
+ source_preloader.send(:owners).each do |owner|
99
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection_name)
100
+ end
101
+ end
102
+ end
103
+ super
104
+ end
105
+ end
106
+ )
107
+
108
+ ::ActiveRecord::Associations::JoinDependency.prepend(
109
+ Module.new do
110
+ def instantiate(result_set, strict_loading_value, &block)
111
+ @bullet_eager_loadings = {}
112
+ records = super
113
+
114
+ if Bullet.start?
115
+ @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
116
+ objects = eager_loadings_hash.keys
117
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(
118
+ objects,
119
+ eager_loadings_hash[objects.first].to_a
120
+ )
121
+ end
122
+ end
123
+ records
124
+ end
125
+
126
+ def construct(ar_parent, parent, row, seen, model_cache, strict_loading_value)
127
+ if Bullet.start?
128
+ unless ar_parent.nil?
129
+ parent.children.each do |node|
130
+ key = aliases.column_alias(node, node.primary_key)
131
+ id = row[key]
132
+ next unless id.nil?
133
+
134
+ associations = node.reflection.name
135
+ Bullet::Detector::Association.add_object_associations(ar_parent, associations)
136
+ Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
137
+ @bullet_eager_loadings[ar_parent.class] ||= {}
138
+ @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
139
+ @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
140
+ end
141
+ end
142
+ end
143
+
144
+ super
145
+ end
146
+
147
+ # call join associations
148
+ def construct_model(record, node, row, model_cache, id, strict_loading_value)
149
+ result = super
150
+
151
+ if Bullet.start?
152
+ associations = node.reflection.name
153
+ Bullet::Detector::Association.add_object_associations(record, associations)
154
+ Bullet::Detector::NPlusOneQuery.call_association(record, associations)
155
+ @bullet_eager_loadings[record.class] ||= {}
156
+ @bullet_eager_loadings[record.class][record] ||= Set.new
157
+ @bullet_eager_loadings[record.class][record] << associations
158
+ end
159
+
160
+ result
161
+ end
162
+ end
163
+ )
164
+
165
+ ::ActiveRecord::Associations::Association.prepend(
166
+ Module.new do
167
+ def inversed_from(record)
168
+ if Bullet.start?
169
+ Bullet::Detector::NPlusOneQuery.add_inversed_object(owner, reflection.name)
170
+ end
171
+ super
172
+ end
173
+ end
174
+ )
175
+
176
+ ::ActiveRecord::Associations::CollectionAssociation.prepend(
177
+ Module.new do
178
+ def load_target
179
+ records = super
180
+
181
+ if Bullet.start?
182
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
183
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
184
+ association = owner.association(reflection.through_reflection.name)
185
+ Array(association.target).each do |through_record|
186
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
187
+ end
188
+
189
+ if reflection.through_reflection != through_reflection
190
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
191
+ end
192
+ end
193
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
194
+ if records.first.class.name !~ /^HABTM_/
195
+ if records.size > 1
196
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
197
+ Bullet::Detector::CounterCache.add_possible_objects(records)
198
+ elsif records.size == 1
199
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
200
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
201
+ end
202
+ end
203
+ end
204
+ records
205
+ end
206
+
207
+ def empty?
208
+ if Bullet.start? && !reflection.has_cached_counter?
209
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
210
+ end
211
+ super
212
+ end
213
+
214
+ def include?(object)
215
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) if Bullet.start?
216
+ super
217
+ end
218
+ end
219
+ )
220
+
221
+ ::ActiveRecord::Associations::SingularAssociation.prepend(
222
+ Module.new do
223
+ # call has_one and belongs_to associations
224
+ def reader
225
+ result = super
226
+
227
+ if Bullet.start?
228
+ if owner.class.name !~ /^HABTM_/
229
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
230
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
231
+ association = owner.association(reflection.through_reflection.name)
232
+ Array(association.target).each do |through_record|
233
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
234
+ end
235
+
236
+ if reflection.through_reflection != through_reflection
237
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
238
+ end
239
+ end
240
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
241
+
242
+ if Bullet::Detector::NPlusOneQuery.impossible?(owner)
243
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
244
+ else
245
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
246
+ end
247
+ end
248
+ end
249
+ result
250
+ end
251
+ end
252
+ )
253
+
254
+ ::ActiveRecord::Associations::HasManyAssociation.prepend(
255
+ Module.new do
256
+ def empty?
257
+ result = super
258
+ if Bullet.start? && !reflection.has_cached_counter?
259
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
260
+ end
261
+ result
262
+ end
263
+
264
+ def count_records
265
+ result = reflection.has_cached_counter?
266
+ if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
267
+ Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
268
+ end
269
+ super
270
+ end
271
+ end
272
+ )
273
+ end
274
+ end
275
+ end
@@ -20,7 +20,7 @@
20
20
  if (this.onload) {
21
21
  this._storedOnload = this.onload;
22
22
  }
23
- this.onload = null
23
+ this.onload = null;
24
24
  this.addEventListener("load", bulletXHROnload);
25
25
  return Reflect.apply(oldSend, this, arguments);
26
26
  }
@@ -31,7 +31,7 @@
31
31
  ) {
32
32
  var bulletFooterText = this.getResponseHeader("X-bullet-footer-text");
33
33
  if (bulletFooterText) {
34
- setTimeout(function() {
34
+ setTimeout(function () {
35
35
  var oldHtml = document.querySelector("#bullet-footer").innerHTML.split("<br>");
36
36
  var header = oldHtml[0];
37
37
  oldHtml = oldHtml.slice(1, oldHtml.length);
@@ -42,7 +42,7 @@
42
42
  }
43
43
  var bulletConsoleText = this.getResponseHeader("X-bullet-console-text");
44
44
  if (bulletConsoleText && typeof console !== "undefined" && console.log) {
45
- setTimeout(function() {
45
+ setTimeout(function () {
46
46
  JSON.parse(bulletConsoleText).forEach((message) => {
47
47
  if (console.groupCollapsed && console.groupEnd) {
48
48
  console.groupCollapsed("Uniform Notifier");
@@ -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
@@ -23,16 +23,33 @@ module Bullet
23
23
  end
24
24
 
25
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)
26
+ return to_enum unless block_given?
27
+
28
+ first_document = nil
29
+ document_count = 0
30
+
31
+ origin_each do |document|
32
+ document_count += 1
33
+
34
+ if document_count == 1
35
+ first_document = document
36
+ elsif document_count == 2
37
+ Bullet::Detector::NPlusOneQuery.add_possible_objects([first_document, document])
38
+ yield(first_document)
39
+ first_document = nil
40
+ yield(document)
41
+ else
42
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(document)
43
+ yield(document)
44
+ end
34
45
  end
35
- records.each(&block)
46
+
47
+ if document_count == 1
48
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(first_document)
49
+ yield(first_document)
50
+ end
51
+
52
+ self
36
53
  end
37
54
 
38
55
  def eager_load(docs)
data/lib/bullet/rack.rb CHANGED
@@ -42,6 +42,7 @@ module Bullet
42
42
  def empty?(response)
43
43
  # response may be ["Not Found"], ["Move Permanently"], etc, but
44
44
  # those should not happen if the status is 200
45
+ return true if !response.respond_to?(:body) && !response.respond_to?(:first)
45
46
  body = response_body(response)
46
47
  body.nil? || body.empty?
47
48
  end
@@ -78,7 +79,7 @@ module Bullet
78
79
  end
79
80
 
80
81
  def html_request?(headers, response)
81
- headers['Content-Type']&.include?('text/html') && response_body(response).include?('<html')
82
+ headers['Content-Type']&.include?('text/html')
82
83
  end
83
84
 
84
85
  def response_body(response)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bullet
4
- VERSION = '6.1.5'
4
+ VERSION = '7.0.2'
5
5
  end
data/lib/bullet.rb CHANGED
@@ -120,51 +120,6 @@ module Bullet
120
120
  @safelist = nil
121
121
  end
122
122
 
123
- def add_whitelist(options)
124
- ActiveSupport::Deprecation.warn(<<~WARN.strip
125
- add_whitelist is deprecated in favor of add_safelist. It will be removed from the next major release.
126
- WARN
127
- )
128
-
129
- add_safelist(options)
130
- end
131
-
132
- def delete_whitelist(options)
133
- ActiveSupport::Deprecation.warn(<<~WARN.strip
134
- delete_whitelist is deprecated in favor of delete_safelist. It will be removed from the next major release.
135
- WARN
136
- )
137
-
138
- delete_safelist(options)
139
- end
140
-
141
- def get_whitelist_associations(type, class_name)
142
- ActiveSupport::Deprecation.warn(<<~WARN.strip
143
- get_whitelist_associations is deprecated in favor of get_safelist_associations. It will be removed from the next major release.
144
- WARN
145
- )
146
-
147
- get_safelist_associations(type, class_name)
148
- end
149
-
150
- def reset_whitelist
151
- ActiveSupport::Deprecation.warn(<<~WARN.strip
152
- reset_whitelist is deprecated in favor of reset_safelist. It will be removed from the next major release.
153
- WARN
154
- )
155
-
156
- reset_safelist
157
- end
158
-
159
- def clear_whitelist
160
- ActiveSupport::Deprecation.warn(<<~WARN.strip
161
- clear_whitelist is deprecated in favor of clear_safelist. It will be removed from the next major release.
162
- WARN
163
- )
164
-
165
- clear_safelist
166
- end
167
-
168
123
  def bullet_logger=(active)
169
124
  if active
170
125
  require 'fileutils'
@@ -16,7 +16,6 @@ module Bullet
16
16
  Bullet.alert = true
17
17
  Bullet.bullet_logger = true
18
18
  Bullet.console = true
19
- # Bullet.growl = true
20
19
  Bullet.rails_logger = true
21
20
  Bullet.add_footer = true
22
21
  end
@@ -74,8 +74,8 @@ module Bullet
74
74
  it 'should send full_notice to notifier' do
75
75
  notifier = double
76
76
  allow(subject).to receive(:notifier).and_return(notifier)
77
- allow(subject).to receive(:notification_data).and_return(foo: :bar)
78
- expect(notifier).to receive(:inline_notify).with(foo: :bar)
77
+ allow(subject).to receive(:notification_data).and_return({ foo: :bar })
78
+ expect(notifier).to receive(:inline_notify).with({ foo: :bar })
79
79
  subject.notify_inline
80
80
  end
81
81
  end
@@ -84,8 +84,8 @@ module Bullet
84
84
  it 'should send full_out_of_channel to notifier' do
85
85
  notifier = double
86
86
  allow(subject).to receive(:notifier).and_return(notifier)
87
- allow(subject).to receive(:notification_data).and_return(foo: :bar)
88
- expect(notifier).to receive(:out_of_channel_notify).with(foo: :bar)
87
+ allow(subject).to receive(:notification_data).and_return({ foo: :bar })
88
+ expect(notifier).to receive(:out_of_channel_notify).with({ foo: :bar })
89
89
  subject.notify_out_of_channel
90
90
  end
91
91
  end
@@ -31,12 +31,6 @@ module Bullet
31
31
  response = double(body: '<html><head></head><body></body></html>')
32
32
  expect(middleware).not_to be_html_request(headers, response)
33
33
  end
34
-
35
- it "should be false if response body doesn't contain html tag" do
36
- headers = { 'Content-Type' => 'text/html' }
37
- response = double(body: '<div>Partial</div>')
38
- expect(middleware).not_to be_html_request(headers, response)
39
- end
40
34
  end
41
35
 
42
36
  context 'empty?' do
@@ -54,6 +48,11 @@ module Bullet
54
48
  response = double(body: '')
55
49
  expect(middleware).to be_empty(response)
56
50
  end
51
+
52
+ it 'should be true if no response body' do
53
+ response = double()
54
+ expect(middleware).to be_empty(response)
55
+ end
57
56
  end
58
57
 
59
58
  context '#call' do
data/spec/bullet_spec.rb CHANGED
@@ -83,15 +83,6 @@ describe Bullet, focused: true do
83
83
  end
84
84
  end
85
85
 
86
- describe '#add_whitelist' do
87
- context "for 'special' class names" do
88
- it 'is added to the safelist successfully' do
89
- Bullet.add_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
90
- expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to include :department
91
- end
92
- end
93
- end
94
-
95
86
  describe '#delete_safelist' do
96
87
  context "for 'special' class names" do
97
88
  it 'is deleted from the safelist successfully' do
@@ -112,26 +103,6 @@ describe Bullet, focused: true do
112
103
  end
113
104
  end
114
105
 
115
- describe '#delete_whitelist' do
116
- context "for 'special' class names" do
117
- it 'is deleted from the safelist successfully' do
118
- Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
119
- Bullet.delete_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
120
- expect(Bullet.safelist[:n_plus_one_query]).to eq({})
121
- end
122
- end
123
-
124
- context 'when exists multiple definitions' do
125
- it 'is deleted from the safelist successfully' do
126
- Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
127
- Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
128
- Bullet.delete_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
129
- expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to include :department
130
- expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to_not include :team
131
- end
132
- end
133
- end
134
-
135
106
  describe '#perform_out_of_channel_notifications' do
136
107
  let(:notification) { double }
137
108
 
@@ -451,6 +451,16 @@ if active_record?
451
451
  expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Student, :teachers)
452
452
  end
453
453
  end
454
+
455
+ context 'user => roles' do
456
+ it 'should detect preload associations' do
457
+ User.first.roles.includes(:resource).each { |role| role.resource }
458
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
459
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
460
+
461
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
462
+ end
463
+ end
454
464
  end
455
465
 
456
466
  describe Bullet::Detector::Association, 'has_many :through' do
@@ -28,7 +28,7 @@ if !mongoid? && active_record?
28
28
  expect(Bullet.collected_counter_cache_notifications).to be_empty
29
29
  end
30
30
 
31
- if active_record5? || active_record6?
31
+ if ActiveRecord::VERSION::MAJOR > 4
32
32
  it 'should not need counter cache for has_many through' do
33
33
  Client.all.each { |client| client.firms.size }
34
34
  expect(Bullet.collected_counter_cache_notifications).to be_empty
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Role < ActiveRecord::Base
4
+ has_and_belongs_to_many :users
5
+
6
+ belongs_to :resource, polymorphic: true
7
+ end
data/spec/models/user.rb CHANGED
@@ -4,4 +4,5 @@ class User < ActiveRecord::Base
4
4
  has_one :submission
5
5
  has_one :submission_attachment, through: :submission, source: :attachment, class_name: 'Attachment'
6
6
  belongs_to :category
7
+ has_and_belongs_to_many :roles
7
8
  end
@@ -92,9 +92,16 @@ module Support
92
92
  page3 = Page.create(name: 'page3', parent_id: folder2.id, author_id: author2.id)
93
93
  page4 = Page.create(name: 'page4', parent_id: folder2.id, author_id: author2.id)
94
94
 
95
+ role1 = Role.create(name: 'Amdin')
96
+ role2 = Role.create(name: 'User')
97
+
95
98
  user1 = User.create(name: 'user1', category: category1)
96
99
  user2 = User.create(name: 'user2', category: category1)
97
100
 
101
+ user1.roles << role1
102
+ user1.roles << role2
103
+ user2.roles << role2
104
+
98
105
  submission1 = user1.create_submission(name: 'submission1')
99
106
  submission2 = user2.create_submission(name: 'submission2')
100
107
 
@@ -246,6 +253,17 @@ module Support
246
253
  t.column :submission_id, :integer
247
254
  end
248
255
 
256
+ create_table :roles do |t|
257
+ t.column :name, :string
258
+ t.column :resource_id, :integer
259
+ t.column :resource_type, :string
260
+ end
261
+
262
+ create_table :roles_users do |t|
263
+ t.column :role_id, :integer
264
+ t.column :user_id, :integer
265
+ end
266
+
249
267
  create_table :submissions do |t|
250
268
  t.column :name, :string
251
269
  t.column :user_id, :integer
data/test.sh CHANGED
@@ -1,5 +1,6 @@
1
1
  #bundle update rails && bundle exec rspec spec
2
2
  #BUNDLE_GEMFILE=Gemfile.mongoid bundle update mongoid && BUNDLE_GEMFILE=Gemfile.mongoid bundle exec rspec spec
3
+ BUNDLE_GEMFILE=Gemfile.rails-7.0 bundle && BUNDLE_GEMFILE=Gemfile.rails-7.0 bundle exec rspec spec
3
4
  BUNDLE_GEMFILE=Gemfile.rails-6.1 bundle && BUNDLE_GEMFILE=Gemfile.rails-6.1 bundle exec rspec spec
4
5
  BUNDLE_GEMFILE=Gemfile.rails-6.0 bundle && BUNDLE_GEMFILE=Gemfile.rails-6.0 bundle exec rspec spec
5
6
  BUNDLE_GEMFILE=Gemfile.rails-5.2 bundle && BUNDLE_GEMFILE=Gemfile.rails-5.2 bundle exec rspec spec
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bullet
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.1.5
4
+ version: 7.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-16 00:00:00.000000000 Z
11
+ date: 2022-05-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -63,6 +63,7 @@ files:
63
63
  - Gemfile.rails-5.2
64
64
  - Gemfile.rails-6.0
65
65
  - Gemfile.rails-6.1
66
+ - Gemfile.rails-7.0
66
67
  - Guardfile
67
68
  - Hacking.md
68
69
  - MIT-LICENSE
@@ -78,6 +79,7 @@ files:
78
79
  - lib/bullet/active_record52.rb
79
80
  - lib/bullet/active_record60.rb
80
81
  - lib/bullet/active_record61.rb
82
+ - lib/bullet/active_record70.rb
81
83
  - lib/bullet/bullet_xhr.js
82
84
  - lib/bullet/dependency.rb
83
85
  - lib/bullet/detector.rb
@@ -158,6 +160,7 @@ files:
158
160
  - spec/models/post.rb
159
161
  - spec/models/relationship.rb
160
162
  - spec/models/reply.rb
163
+ - spec/models/role.rb
161
164
  - spec/models/student.rb
162
165
  - spec/models/submission.rb
163
166
  - spec/models/teacher.rb
@@ -192,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
192
195
  - !ruby/object:Gem::Version
193
196
  version: 1.3.6
194
197
  requirements: []
195
- rubygems_version: 3.2.22
198
+ rubygems_version: 3.3.7
196
199
  signing_key:
197
200
  specification_version: 4
198
201
  summary: help to kill N+1 queries and unused eager loading.
@@ -247,6 +250,7 @@ test_files:
247
250
  - spec/models/post.rb
248
251
  - spec/models/relationship.rb
249
252
  - spec/models/reply.rb
253
+ - spec/models/role.rb
250
254
  - spec/models/student.rb
251
255
  - spec/models/submission.rb
252
256
  - spec/models/teacher.rb