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 +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile.rails-6.0 +1 -1
- data/Gemfile.rails-6.1 +15 -0
- data/README.md +7 -0
- data/lib/bullet.rb +2 -2
- data/lib/bullet/active_job.rb +5 -1
- data/lib/bullet/active_record61.rb +267 -0
- data/lib/bullet/bullet_xhr.js +17 -17
- data/lib/bullet/dependency.rb +6 -0
- data/lib/bullet/rack.rb +21 -15
- data/lib/bullet/version.rb +1 -1
- data/lib/generators/bullet/install_generator.rb +23 -25
- data/spec/bullet/detector/counter_cache_spec.rb +1 -1
- data/spec/bullet/detector/unused_eager_loading_spec.rb +8 -12
- data/spec/bullet/notification/n_plus_one_query_spec.rb +1 -3
- data/spec/bullet/rack_spec.rb +57 -1
- data/spec/integration/active_record/association_spec.rb +1 -2
- data/spec/integration/mongoid/association_spec.rb +3 -3
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0363cc75d4facd4a8b4a318312a421040d5b21d7ca81c0b3af21daaf7c2b60cd
|
4
|
+
data.tar.gz: ac0b0e4ed9c1641b5359b3d885ef854934b85b8ee21bc0486a4c3d2661e20853
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 199558c8186455fb2ddac94461d611d1c40eb0fb83ac68e36647efe0a34927020967a803cb24645b7ae3d5d045aa0060c604b455476f8c6aad6b5329103a7ebe
|
7
|
+
data.tar.gz: 86c4bcf9fcca52275229b7fa5e02919c39318d91a60c7fe956d4cab07442cbacfc3d9df134af1ee38bc7f153eeb2cd256faff45815ad83acd296c1831d1fc9ee
|
data/CHANGELOG.md
CHANGED
data/Gemfile.rails-6.0
CHANGED
data/Gemfile.rails-6.1
ADDED
@@ -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
|
|
data/lib/bullet.rb
CHANGED
@@ -240,8 +240,8 @@ module Bullet
|
|
240
240
|
UniformNotifier.active_notifiers.include?(UniformNotifier::JavascriptConsole)
|
241
241
|
end
|
242
242
|
|
243
|
-
def
|
244
|
-
|
243
|
+
def inject_into_page?
|
244
|
+
!@skip_html_injection && (console_enabled? || add_footer)
|
245
245
|
end
|
246
246
|
|
247
247
|
private
|
data/lib/bullet/active_job.rb
CHANGED
@@ -3,7 +3,11 @@
|
|
3
3
|
module Bullet
|
4
4
|
module ActiveJob
|
5
5
|
def self.included(base)
|
6
|
-
base.class_eval
|
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
|
data/lib/bullet/bullet_xhr.js
CHANGED
@@ -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 ==
|
13
|
+
return oldOpen.name == "bulletXHROpen" && oldSend.name == "bulletXHRSend";
|
14
14
|
}
|
15
15
|
function bulletXHROpen(_, url) {
|
16
16
|
this._storedUrl = url;
|
17
|
-
return
|
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(
|
24
|
-
return
|
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 +
|
29
|
-
!this._storedUrl.startsWith(
|
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(
|
31
|
+
var bulletFooterText = this.getResponseHeader("X-bullet-footer-text");
|
32
32
|
if (bulletFooterText) {
|
33
|
-
setTimeout(()
|
34
|
-
var oldHtml = document.
|
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.
|
39
|
+
document.querySelector("#bullet-footer").innerHTML = `${header}<br>${newHtml.join("<br>")}`;
|
40
40
|
}, 0);
|
41
41
|
}
|
42
|
-
var bulletConsoleText = this.getResponseHeader(
|
43
|
-
if (bulletConsoleText && typeof console !==
|
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(
|
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
|
58
|
+
return Reflect.apply(this._storedOnload, this, arguments);
|
59
59
|
}
|
60
60
|
}
|
61
61
|
window.XMLHttpRequest.prototype.open = bulletXHROpen;
|
data/lib/bullet/dependency.rb
CHANGED
@@ -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
|
data/lib/bullet/rack.rb
CHANGED
@@ -17,7 +17,7 @@ module Bullet
|
|
17
17
|
response_body = nil
|
18
18
|
|
19
19
|
if Bullet.notification?
|
20
|
-
if
|
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
|
-
"<
|
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 *
|
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
|
92
|
+
def details_attributes
|
92
93
|
<<~EOF
|
93
|
-
id="bullet-footer" data-is-bullet-footer
|
94
|
-
|
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
|
102
|
-
|
103
|
-
|
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
|
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
|
|
data/lib/bullet/version.rb
CHANGED
@@ -10,40 +10,38 @@ module Bullet
|
|
10
10
|
|
11
11
|
def enable_in_development
|
12
12
|
environment(nil, env: 'development') do
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
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
|
-
|
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
|
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(
|
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(
|
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(
|
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(
|
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(
|
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',
|
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(
|
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',
|
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
|
data/spec/bullet/rack_spec.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
77
|
-
|
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.
|
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:
|
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.
|
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:
|