bullet 6.1.0 → 6.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd668fdfd675ea8437eee0242974d7cb1cfacc0479be4cc5f3f58ee7d05a6d63
4
- data.tar.gz: 3d841fb8471fe18b66135fa087ee3d1874f412c114d09ba05ba150e2daaf9775
3
+ metadata.gz: 0363cc75d4facd4a8b4a318312a421040d5b21d7ca81c0b3af21daaf7c2b60cd
4
+ data.tar.gz: ac0b0e4ed9c1641b5359b3d885ef854934b85b8ee21bc0486a4c3d2661e20853
5
5
  SHA512:
6
- metadata.gz: 714c614f2cf44f332f4f9996638c10dca565c92a2279316509256fefba2ca824fc9c71d2a3c3e4ca28edfc45849f76b61e521158e0051c365f5b8a81f41e9d8d
7
- data.tar.gz: a3b1a7e58b6e5554a641aa4b8edf16dbb4983b2f7a072c5b7275ee04a7251e4d095fed37f82d2945ef759603c8aeaf8a394d9e7fcf9f70a723e6235ab5f3e646
6
+ metadata.gz: 199558c8186455fb2ddac94461d611d1c40eb0fb83ac68e36647efe0a34927020967a803cb24645b7ae3d5d045aa0060c604b455476f8c6aad6b5329103a7ebe
7
+ data.tar.gz: 86c4bcf9fcca52275229b7fa5e02919c39318d91a60c7fe956d4cab07442cbacfc3d9df134af1ee38bc7f153eeb2cd256faff45815ad83acd296c1831d1fc9ee
@@ -1,5 +1,14 @@
1
1
  ## Next Release
2
2
 
3
+ ## 6.1.2 (12/12/2020)
4
+
5
+ * Revert "Make whitelist thread safe"
6
+
7
+ ## 6.1.1 (12/12/2020)
8
+
9
+ * Add support Rails 6.1
10
+ * Make whitelist thread safe
11
+
3
12
  ## 6.1.0 (12/28/2019)
4
13
 
5
14
  * Add skip_html_injection flag
@@ -2,7 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rails', '6.0.0'
5
+ gem 'rails', '~> 6.0.0'
6
6
  gem 'sqlite3'
7
7
  gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
8
  gem 'activerecord-import'
@@ -0,0 +1,15 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'rails', '~> 6.1.0'
6
+ gem 'sqlite3'
7
+ gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
+ gem 'activerecord-import'
9
+
10
+ gem "rspec"
11
+
12
+ platforms :rbx do
13
+ gem 'rubysl', '~> 2.0'
14
+ gem 'rubinius-developer_tools'
15
+ end
data/README.md CHANGED
@@ -37,6 +37,13 @@ or add it into a Gemfile (Bundler):
37
37
  gem 'bullet', group: 'development'
38
38
  ```
39
39
 
40
+ enable the Bullet gem with generate command
41
+
42
+ ```ruby
43
+ bundle exec rails g bullet:install
44
+ ```
45
+ The generate command will auto generate the default configuration and may ask to include in the test environment as well. See below for custom configuration.
46
+
40
47
  **Note**: make sure `bullet` gem is added after activerecord (rails) and
41
48
  mongoid.
42
49
 
@@ -240,8 +240,8 @@ module Bullet
240
240
  UniformNotifier.active_notifiers.include?(UniformNotifier::JavascriptConsole)
241
241
  end
242
242
 
243
- def skip_html_injection?
244
- @skip_html_injection || false
243
+ def inject_into_page?
244
+ !@skip_html_injection && (console_enabled? || add_footer)
245
245
  end
246
246
 
247
247
  private
@@ -3,7 +3,11 @@
3
3
  module Bullet
4
4
  module ActiveJob
5
5
  def self.included(base)
6
- base.class_eval { around_perform { |_job, block| Bullet.profile { block.call } } }
6
+ base.class_eval do
7
+ around_perform do |_job, block|
8
+ Bullet.profile { block.call }
9
+ end
10
+ end
7
11
  end
8
12
  end
9
13
  end
@@ -0,0 +1,267 @@
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.prepend(
64
+ Module.new do
65
+ def preloaders_for_one(association, records, scope, polymorphic_parent)
66
+ if Bullet.start?
67
+ records.compact!
68
+ if records.first.class.name !~ /^HABTM_/
69
+ records.each { |record| Bullet::Detector::Association.add_object_associations(record, association) }
70
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
71
+ end
72
+ end
73
+ super
74
+ end
75
+
76
+ def preloaders_for_reflection(reflection, records, scope)
77
+ if Bullet.start?
78
+ records.compact!
79
+ if records.first.class.name !~ /^HABTM_/
80
+ records.each { |record| Bullet::Detector::Association.add_object_associations(record, reflection.name) }
81
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, reflection.name)
82
+ end
83
+ end
84
+ super
85
+ end
86
+ end
87
+ )
88
+
89
+ ::ActiveRecord::Associations::Preloader::ThroughAssociation.prepend(
90
+ Module.new do
91
+ def preloaded_records
92
+ if Bullet.start? && !defined?(@preloaded_records)
93
+ source_preloaders.each do |source_preloader|
94
+ reflection_name = source_preloader.send(:reflection).name
95
+ source_preloader.send(:owners).each do |owner|
96
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection_name)
97
+ end
98
+ end
99
+ end
100
+ super
101
+ end
102
+ end
103
+ )
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
+ ::ActiveRecord::Associations::JoinDependency.prepend(
123
+ Module.new do
124
+ def instantiate(result_set, strict_loading_value, &block)
125
+ @bullet_eager_loadings = {}
126
+ records = super
127
+
128
+ if Bullet.start?
129
+ @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
130
+ objects = eager_loadings_hash.keys
131
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(
132
+ objects,
133
+ eager_loadings_hash[objects.first].to_a
134
+ )
135
+ end
136
+ end
137
+ records
138
+ end
139
+
140
+ def construct(ar_parent, parent, row, seen, model_cache, strict_loading_value)
141
+ if Bullet.start?
142
+ unless ar_parent.nil?
143
+ parent.children.each do |node|
144
+ key = aliases.column_alias(node, node.primary_key)
145
+ id = row[key]
146
+ next unless id.nil?
147
+
148
+ associations = node.reflection.name
149
+ Bullet::Detector::Association.add_object_associations(ar_parent, associations)
150
+ Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
151
+ @bullet_eager_loadings[ar_parent.class] ||= {}
152
+ @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
153
+ @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
154
+ end
155
+ end
156
+ end
157
+
158
+ super
159
+ end
160
+
161
+ # call join associations
162
+ def construct_model(record, node, row, model_cache, id, strict_loading_value)
163
+ result = super
164
+
165
+ if Bullet.start?
166
+ associations = node.reflection.name
167
+ Bullet::Detector::Association.add_object_associations(record, associations)
168
+ Bullet::Detector::NPlusOneQuery.call_association(record, associations)
169
+ @bullet_eager_loadings[record.class] ||= {}
170
+ @bullet_eager_loadings[record.class][record] ||= Set.new
171
+ @bullet_eager_loadings[record.class][record] << associations
172
+ end
173
+
174
+ result
175
+ end
176
+ end
177
+ )
178
+
179
+ ::ActiveRecord::Associations::CollectionAssociation.prepend(
180
+ Module.new do
181
+ def load_target
182
+ records = super
183
+
184
+ if Bullet.start?
185
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
186
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
187
+ association = owner.association(reflection.through_reflection.name)
188
+ Array(association.target).each do |through_record|
189
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
190
+ end
191
+
192
+ if reflection.through_reflection != through_reflection
193
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
194
+ end
195
+ end
196
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
197
+ if records.first.class.name !~ /^HABTM_/
198
+ if records.size > 1
199
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
200
+ Bullet::Detector::CounterCache.add_possible_objects(records)
201
+ elsif records.size == 1
202
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
203
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
204
+ end
205
+ end
206
+ end
207
+ records
208
+ end
209
+
210
+ def empty?
211
+ if Bullet.start? && !reflection.has_cached_counter?
212
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
213
+ end
214
+ super
215
+ end
216
+
217
+ def include?(object)
218
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) if Bullet.start?
219
+ super
220
+ end
221
+ end
222
+ )
223
+
224
+ ::ActiveRecord::Associations::SingularAssociation.prepend(
225
+ Module.new do
226
+ # call has_one and belongs_to associations
227
+ def target
228
+ result = super()
229
+
230
+ if Bullet.start?
231
+ if owner.class.name !~ /^HABTM_/ && !@inversed
232
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
233
+
234
+ if Bullet::Detector::NPlusOneQuery.impossible?(owner)
235
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
236
+ else
237
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
238
+ end
239
+ end
240
+ end
241
+ result
242
+ end
243
+ end
244
+ )
245
+
246
+ ::ActiveRecord::Associations::HasManyAssociation.prepend(
247
+ Module.new do
248
+ def empty?
249
+ result = super
250
+ if Bullet.start? && !reflection.has_cached_counter?
251
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
252
+ end
253
+ result
254
+ end
255
+
256
+ def count_records
257
+ result = reflection.has_cached_counter?
258
+ if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
259
+ Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
260
+ end
261
+ super
262
+ end
263
+ end
264
+ )
265
+ end
266
+ end
267
+ end
@@ -1,4 +1,4 @@
1
- (function() {
1
+ (function () {
2
2
  var oldOpen = window.XMLHttpRequest.prototype.open;
3
3
  var oldSend = window.XMLHttpRequest.prototype.send;
4
4
 
@@ -10,41 +10,41 @@
10
10
  if (isBulletInitiated()) return;
11
11
 
12
12
  function isBulletInitiated() {
13
- return oldOpen.name == 'bulletXHROpen' && oldSend.name == 'bulletXHRSend';
13
+ return oldOpen.name == "bulletXHROpen" && oldSend.name == "bulletXHRSend";
14
14
  }
15
15
  function bulletXHROpen(_, url) {
16
16
  this._storedUrl = url;
17
- return oldOpen.apply(this, arguments);
17
+ return Reflect.apply(oldOpen, this, arguments);
18
18
  }
19
19
  function bulletXHRSend() {
20
20
  if (this.onload) {
21
21
  this._storedOnload = this.onload;
22
22
  }
23
- this.addEventListener('load', bulletXHROnload);
24
- return oldSend.apply(this, arguments);
23
+ this.addEventListener("load", bulletXHROnload);
24
+ return Reflect.apply(oldSend, this, arguments);
25
25
  }
26
26
  function bulletXHROnload() {
27
27
  if (
28
- this._storedUrl.startsWith(window.location.protocol + '//' + window.location.host) ||
29
- !this._storedUrl.startsWith('http') // For relative paths
28
+ this._storedUrl.startsWith(window.location.protocol + "//" + window.location.host) ||
29
+ !this._storedUrl.startsWith("http") // For relative paths
30
30
  ) {
31
- var bulletFooterText = this.getResponseHeader('X-bullet-footer-text');
31
+ var bulletFooterText = this.getResponseHeader("X-bullet-footer-text");
32
32
  if (bulletFooterText) {
33
- setTimeout(() => {
34
- var oldHtml = document.getElementById('bullet-footer').innerHTML.split('<br>');
33
+ setTimeout(function() {
34
+ var oldHtml = document.querySelector("#bullet-footer").innerHTML.split("<br>");
35
35
  var header = oldHtml[0];
36
36
  oldHtml = oldHtml.slice(1, oldHtml.length);
37
37
  var newHtml = oldHtml.concat(JSON.parse(bulletFooterText));
38
38
  newHtml = newHtml.slice(newHtml.length - 10, newHtml.length); // rotate through 10 most recent
39
- document.getElementById('bullet-footer').innerHTML = `${header}<br>${newHtml.join('<br>')}`;
39
+ document.querySelector("#bullet-footer").innerHTML = `${header}<br>${newHtml.join("<br>")}`;
40
40
  }, 0);
41
41
  }
42
- var bulletConsoleText = this.getResponseHeader('X-bullet-console-text');
43
- if (bulletConsoleText && typeof console !== 'undefined' && console.log) {
44
- setTimeout(() => {
45
- JSON.parse(bulletConsoleText).forEach(message => {
42
+ var bulletConsoleText = this.getResponseHeader("X-bullet-console-text");
43
+ if (bulletConsoleText && typeof console !== "undefined" && console.log) {
44
+ setTimeout(function() {
45
+ JSON.parse(bulletConsoleText).forEach((message) => {
46
46
  if (console.groupCollapsed && console.groupEnd) {
47
- console.groupCollapsed('Uniform Notifier');
47
+ console.groupCollapsed("Uniform Notifier");
48
48
  console.log(message);
49
49
  console.groupEnd();
50
50
  } else {
@@ -55,7 +55,7 @@
55
55
  }
56
56
  }
57
57
  if (this._storedOnload) {
58
- return this._storedOnload.apply(this, arguments);
58
+ return Reflect.apply(this._storedOnload, this, arguments);
59
59
  }
60
60
  }
61
61
  window.XMLHttpRequest.prototype.open = bulletXHROpen;
@@ -27,6 +27,8 @@ module Bullet
27
27
  'active_record52'
28
28
  elsif active_record60?
29
29
  'active_record60'
30
+ elsif active_record61?
31
+ 'active_record61'
30
32
  else
31
33
  raise "Bullet does not support active_record #{::ActiveRecord::VERSION::STRING} yet"
32
34
  end
@@ -90,6 +92,10 @@ module Bullet
90
92
  active_record6? && ::ActiveRecord::VERSION::MINOR == 0
91
93
  end
92
94
 
95
+ def active_record61?
96
+ active_record6? && ::ActiveRecord::VERSION::MINOR == 1
97
+ end
98
+
93
99
  def mongoid4x?
94
100
  mongoid? && ::Mongoid::VERSION =~ /\A4/
95
101
  end
@@ -17,7 +17,7 @@ module Bullet
17
17
  response_body = nil
18
18
 
19
19
  if Bullet.notification?
20
- if !Bullet.skip_html_injection? && !file?(headers) && !sse?(headers) && !empty?(response) && status == 200
20
+ if Bullet.inject_into_page? && !file?(headers) && !sse?(headers) && !empty?(response) && status == 200
21
21
  if html_request?(headers, response)
22
22
  response_body = response_body(response)
23
23
  response_body = append_to_html_body(response_body, footer_note) if Bullet.add_footer
@@ -46,6 +46,7 @@ module Bullet
46
46
 
47
47
  def append_to_html_body(response_body, content)
48
48
  body = response_body.dup
49
+ content = content.html_safe if content.respond_to?(:html_safe)
49
50
  if body.include?('</body>')
50
51
  position = body.rindex('</body>')
51
52
  body.insert(position, content)
@@ -55,14 +56,14 @@ module Bullet
55
56
  end
56
57
 
57
58
  def footer_note
58
- "<div #{footer_div_attributes}>" + footer_header + '<br>' + Bullet.footer_info.uniq.join('<br>') + '</div>'
59
+ "<details #{details_attributes}><summary #{summary_attributes}>Bullet Warnings</summary><div #{footer_content_attributes}>#{Bullet.footer_info.uniq.join('<br>')}#{footer_console_message}</div></details>"
59
60
  end
60
61
 
61
62
  def set_header(headers, header_name, header_array)
62
63
  # Many proxy applications such as Nginx and AWS ELB limit
63
64
  # the size a header to 8KB, so truncate the list of reports to
64
65
  # be under that limit
65
- header_array.pop while header_array.to_json.length > 8 * 1_024
66
+ header_array.pop while header_array.to_json.length > 8 * 1024
66
67
  headers[header_name] = header_array.to_json
67
68
  end
68
69
 
@@ -88,23 +89,28 @@ module Bullet
88
89
 
89
90
  private
90
91
 
91
- def footer_div_attributes
92
+ def details_attributes
92
93
  <<~EOF
93
- id="bullet-footer" data-is-bullet-footer ondblclick="this.parentNode.removeChild(this);" style="position: fixed; bottom: 0pt; left: 0pt; cursor: pointer; border-style: solid; border-color: rgb(153, 153, 153);
94
- -moz-border-top-colors: none; -moz-border-right-colors: none; -moz-border-bottom-colors: none;
95
- -moz-border-left-colors: none; -moz-border-image: none; border-width: 2pt 2pt 0px 0px;
96
- padding: 3px 5px; border-radius: 0pt 10pt 0pt 0px; background: none repeat scroll 0% 0% rgba(200, 200, 200, 0.8);
97
- color: rgb(119, 119, 119); font-size: 16px; font-family: 'Arial', sans-serif; z-index:9999;"
94
+ id="bullet-footer" data-is-bullet-footer
95
+ style="cursor: pointer; position: fixed; left: 0px; bottom: 0px; z-index: 9999; background: #fdf2f2; color: #9b1c1c; font-size: 12px; border-radius: 0px 8px 0px 0px; border: 1px solid #9b1c1c;"
98
96
  EOF
99
97
  end
100
98
 
101
- def footer_header
102
- cancel_button =
103
- "<span onclick='this.parentNode.remove()' style='position:absolute; right: 10px; top: 0px; font-weight: bold; color: #333;'>&times;</span>"
99
+ def summary_attributes
100
+ <<~EOF
101
+ style="font-weight: 600; padding: 2px 8px"
102
+ EOF
103
+ end
104
+
105
+ def footer_content_attributes
106
+ <<~EOF
107
+ style="padding: 8px; border-top: 1px solid #9b1c1c;"
108
+ EOF
109
+ end
110
+
111
+ def footer_console_message
104
112
  if Bullet.console_enabled?
105
- "<span>See 'Uniform Notifier' in JS Console for Stacktrace</span>#{cancel_button}"
106
- else
107
- cancel_button
113
+ "<br/><span style='font-style: italic;'>See 'Uniform Notifier' in JS Console for Stacktrace</span>"
108
114
  end
109
115
  end
110
116
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bullet
4
- VERSION = '6.1.0'
4
+ VERSION = '6.1.2'
5
5
  end
@@ -10,40 +10,38 @@ module Bullet
10
10
 
11
11
  def enable_in_development
12
12
  environment(nil, env: 'development') do
13
- <<-"FILE"
14
-
15
- config.after_initialize do
16
- Bullet.enable = true
17
- Bullet.alert = true
18
- Bullet.bullet_logger = true
19
- Bullet.console = true
20
- # Bullet.growl = true
21
- Bullet.rails_logger = true
22
- Bullet.add_footer = true
23
- end
13
+ <<~FILE
14
+ config.after_initialize do
15
+ Bullet.enable = true
16
+ Bullet.alert = true
17
+ Bullet.bullet_logger = true
18
+ Bullet.console = true
19
+ # Bullet.growl = true
20
+ Bullet.rails_logger = true
21
+ Bullet.add_footer = true
22
+ end
23
+
24
24
  FILE
25
- .strip
26
25
  end
27
26
 
28
27
  say 'Enabled bullet in config/environments/development.rb'
29
28
  end
30
29
 
31
30
  def enable_in_test
32
- if yes?('Would you like to enable bullet in test environment? (y/n)')
33
- environment(nil, env: 'test') do
34
- <<-"FILE"
35
-
36
- config.after_initialize do
37
- Bullet.enable = true
38
- Bullet.bullet_logger = true
39
- Bullet.raise = true # raise an error if n+1 query occurs
40
- end
41
- FILE
42
- .strip
43
- end
31
+ return unless yes?('Would you like to enable bullet in test environment? (y/n)')
44
32
 
45
- say 'Enabled bullet in config/environments/test.rb'
33
+ environment(nil, env: 'test') do
34
+ <<~FILE
35
+ config.after_initialize do
36
+ Bullet.enable = true
37
+ Bullet.bullet_logger = true
38
+ Bullet.raise = true # raise an error if n+1 query occurs
39
+ end
40
+
41
+ FILE
46
42
  end
43
+
44
+ say 'Enabled bullet in config/environments/test.rb'
47
45
  end
48
46
  end
49
47
  end
@@ -47,7 +47,7 @@ module Bullet
47
47
  expect(CounterCache.conditions_met?(@post1, :associations)).to eq false
48
48
  end
49
49
 
50
- it 'should be true when object is possible, and impossible' do
50
+ it 'should be false when object is possible, and impossible' do
51
51
  CounterCache.add_possible_objects(@post1)
52
52
  CounterCache.add_impossible_object(@post1)
53
53
  expect(CounterCache.conditions_met?(@post1, :associations)).to eq false
@@ -13,35 +13,31 @@ module Bullet
13
13
 
14
14
  context '.call_associations' do
15
15
  it 'should get empty array if eager_loadings' do
16
- expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new(%i[association]))).to be_empty
16
+ expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to be_empty
17
17
  end
18
18
 
19
19
  it 'should get call associations if object and association are both in eager_loadings and call_object_associations' do
20
20
  UnusedEagerLoading.add_eager_loadings([@post], :association)
21
21
  UnusedEagerLoading.add_call_object_associations(@post, :association)
22
- expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new(%i[association]))).to eq(
23
- %i[association]
24
- )
22
+ expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to eq([:association])
25
23
  end
26
24
 
27
25
  it 'should not get call associations if not exist in call_object_associations' do
28
26
  UnusedEagerLoading.add_eager_loadings([@post], :association)
29
- expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new(%i[association]))).to be_empty
27
+ expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to be_empty
30
28
  end
31
29
  end
32
30
 
33
31
  context '.diff_object_associations' do
34
32
  it 'should return associations not exist in call_association' do
35
- expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new(%i[association]))).to eq(
36
- %i[association]
37
- )
33
+ expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to eq([:association])
38
34
  end
39
35
 
40
36
  it 'should return empty if associations exist in call_association' do
41
37
  UnusedEagerLoading.add_eager_loadings([@post], :association)
42
38
  UnusedEagerLoading.add_call_object_associations(@post, :association)
43
39
  expect(
44
- UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new(%i[association]))
40
+ UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))
45
41
  ).to be_empty
46
42
  end
47
43
  end
@@ -51,7 +47,7 @@ module Bullet
51
47
  it 'should create notification if object_association_diff is not empty' do
52
48
  UnusedEagerLoading.add_object_associations(@post, :association)
53
49
  allow(UnusedEagerLoading).to receive(:caller_in_project).and_return(paths)
54
- expect(UnusedEagerLoading).to receive(:create_notification).with(paths, 'Post', %i[association])
50
+ expect(UnusedEagerLoading).to receive(:create_notification).with(paths, 'Post', [:association])
55
51
  UnusedEagerLoading.check_unused_preload_associations
56
52
  end
57
53
 
@@ -60,9 +56,9 @@ module Bullet
60
56
  UnusedEagerLoading.add_eager_loadings([@post], :association)
61
57
  UnusedEagerLoading.add_call_object_associations(@post, :association)
62
58
  expect(
63
- UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new(%i[association]))
59
+ UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))
64
60
  ).to be_empty
65
- expect(UnusedEagerLoading).not_to receive(:create_notification).with('Post', %i[association])
61
+ expect(UnusedEagerLoading).not_to receive(:create_notification).with('Post', [:association])
66
62
  UnusedEagerLoading.check_unused_preload_associations
67
63
  end
68
64
  end
@@ -21,9 +21,7 @@ module Bullet
21
21
  )
22
22
  end
23
23
  it do
24
- expect(subject.body).to eq(
25
- " Post => [:comments, :votes]\n Add to your query: .includes([:comments, :votes])"
26
- )
24
+ expect(subject.body).to eq(" Post => [:comments, :votes]\n Add to your query: .includes([:comments, :votes])")
27
25
  end
28
26
  it { expect(subject.title).to eq('USE eager loading in path') }
29
27
  end
@@ -67,7 +67,7 @@ module Bullet
67
67
 
68
68
  it 'should change response body if notification is active' do
69
69
  expect(Bullet).to receive(:notification?).and_return(true)
70
- allow(Bullet).to receive(:skip_html_injection?).and_return(false)
70
+ expect(Bullet).to receive(:console_enabled?).and_return(true)
71
71
  expect(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
72
72
  expect(middleware).to receive(:xhr_script).and_return('')
73
73
  expect(Bullet).to receive(:perform_out_of_channel_notifications)
@@ -81,11 +81,67 @@ module Bullet
81
81
  response.body = '<html><head></head><body>é</body></html>'
82
82
  app.response = response
83
83
  expect(Bullet).to receive(:notification?).and_return(true)
84
+ allow(Bullet).to receive(:console_enabled?).and_return(true)
84
85
  expect(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
85
86
  _, headers, response = middleware.call('Content-Type' => 'text/html')
86
87
  expect(headers['Content-Length']).to eq((58 + middleware.send(:xhr_script).length).to_s)
87
88
  end
88
89
 
90
+ context 'with injection notifiers' do
91
+ before do
92
+ expect(Bullet).to receive(:notification?).and_return(true)
93
+ allow(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
94
+ allow(middleware).to receive(:xhr_script).and_return('')
95
+ allow(middleware).to receive(:footer_note).and_return('footer')
96
+ expect(Bullet).to receive(:perform_out_of_channel_notifications)
97
+ end
98
+
99
+ it 'should change response body if add_footer is true' do
100
+ expect(Bullet).to receive(:add_footer).twice.and_return(true)
101
+ _, headers, response = middleware.call('Content-Type' => 'text/html')
102
+
103
+ expect(headers['Content-Length']).to eq((56 + middleware.send(:footer_note).length).to_s)
104
+ expect(response.first).to start_with('<html><head></head><body>')
105
+ expect(response.first).to include('<bullet></bullet><')
106
+ end
107
+
108
+ it 'should change response body for html safe string if add_footer is true' do
109
+ expect(Bullet).to receive(:add_footer).twice.and_return(true)
110
+ app.response = Support::ResponseDouble.new.tap do |response|
111
+ response.body = ActiveSupport::SafeBuffer.new('<html><head></head><body></body></html>')
112
+ end
113
+ _, headers, response = middleware.call('Content-Type' => 'text/html')
114
+
115
+ expect(headers['Content-Length']).to eq((56 + middleware.send(:footer_note).length).to_s)
116
+ expect(response.first).to start_with('<html><head></head><body>')
117
+ expect(response.first).to include('<bullet></bullet><')
118
+ end
119
+
120
+ it 'should change response body if console_enabled is true' do
121
+ expect(Bullet).to receive(:console_enabled?).and_return(true)
122
+ _, headers, response = middleware.call('Content-Type' => 'text/html')
123
+ expect(headers['Content-Length']).to eq('56')
124
+ expect(response).to eq(%w[<html><head></head><body><bullet></bullet></body></html>])
125
+ end
126
+
127
+ it 'should change response body for html safe string if console_enabled is true' do
128
+ expect(Bullet).to receive(:console_enabled?).and_return(true)
129
+ app.response = Support::ResponseDouble.new.tap do |response|
130
+ response.body = ActiveSupport::SafeBuffer.new('<html><head></head><body></body></html>')
131
+ end
132
+ _, headers, response = middleware.call('Content-Type' => 'text/html')
133
+ expect(headers['Content-Length']).to eq('56')
134
+ expect(response).to eq(%w[<html><head></head><body><bullet></bullet></body></html>])
135
+ end
136
+
137
+ it "shouldn't change response body unnecessarily" do
138
+ expected_response = Support::ResponseDouble.new 'Actual body'
139
+ app.response = expected_response
140
+ _, _, response = middleware.call({})
141
+ expect(response).to eq(expected_response)
142
+ end
143
+ end
144
+
89
145
  context 'when skip_html_injection is enabled' do
90
146
  it 'should not try to inject html' do
91
147
  expected_response = Support::ResponseDouble.new 'Actual body'
@@ -377,8 +377,7 @@ if active_record?
377
377
  it 'should detect unused preload with comment => author' do
378
378
  Comment.includes([:author, { post: :writer }]).where(['base_users.id = ?', BaseUser.first]).references(
379
379
  :base_users
380
- )
381
- .each { |comment| comment.post.writer.name }
380
+ ).each { |comment| comment.post.writer.name }
382
381
  Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
383
382
  expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
384
383
 
@@ -73,9 +73,9 @@ if mongoid?
73
73
  expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
74
74
 
75
75
  expect(Bullet::Detector::Association).not_to be_detecting_unpreloaded_association_for(
76
- Mongoid::Category,
77
- :posts
78
- )
76
+ Mongoid::Category,
77
+ :posts
78
+ )
79
79
  expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Mongoid::Category, :entries)
80
80
  end
81
81
 
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.0
4
+ version: 6.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-28 00:00:00.000000000 Z
11
+ date: 2020-12-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -62,6 +62,7 @@ files:
62
62
  - Gemfile.rails-5.1
63
63
  - Gemfile.rails-5.2
64
64
  - Gemfile.rails-6.0
65
+ - Gemfile.rails-6.1
65
66
  - Guardfile
66
67
  - Hacking.md
67
68
  - MIT-LICENSE
@@ -76,6 +77,7 @@ files:
76
77
  - lib/bullet/active_record5.rb
77
78
  - lib/bullet/active_record52.rb
78
79
  - lib/bullet/active_record60.rb
80
+ - lib/bullet/active_record61.rb
79
81
  - lib/bullet/bullet_xhr.js
80
82
  - lib/bullet/dependency.rb
81
83
  - lib/bullet/detector.rb
@@ -173,7 +175,7 @@ licenses:
173
175
  metadata:
174
176
  changelog_uri: https://github.com/flyerhzm/bullet/blob/master/CHANGELOG.md
175
177
  source_code_uri: https://github.com/flyerhzm/bullet
176
- post_install_message:
178
+ post_install_message:
177
179
  rdoc_options: []
178
180
  require_paths:
179
181
  - lib
@@ -188,8 +190,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
188
190
  - !ruby/object:Gem::Version
189
191
  version: 1.3.6
190
192
  requirements: []
191
- rubygems_version: 3.0.3
192
- signing_key:
193
+ rubygems_version: 3.1.4
194
+ signing_key:
193
195
  specification_version: 4
194
196
  summary: help to kill N+1 queries and unused eager loading.
195
197
  test_files: