bullet 6.1.0 → 6.1.4
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 +18 -0
- data/Gemfile.rails-6.0 +1 -1
- data/Gemfile.rails-6.1 +15 -0
- data/README.md +9 -1
- data/lib/bullet.rb +9 -7
- data/lib/bullet/active_job.rb +5 -1
- data/lib/bullet/active_record52.rb +11 -0
- data/lib/bullet/active_record60.rb +11 -0
- data/lib/bullet/active_record61.rb +278 -0
- data/lib/bullet/bullet_xhr.js +17 -17
- data/lib/bullet/dependency.rb +6 -0
- data/lib/bullet/mongoid4x.rb +1 -1
- data/lib/bullet/mongoid5x.rb +1 -1
- data/lib/bullet/mongoid6x.rb +1 -1
- data/lib/bullet/mongoid7x.rb +1 -1
- data/lib/bullet/rack.rb +23 -17
- data/lib/bullet/stack_trace_filter.rb +3 -7
- 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 +114 -3
- data/spec/integration/active_record/association_spec.rb +37 -2
- data/spec/integration/mongoid/association_spec.rb +3 -3
- data/spec/models/attachment.rb +5 -0
- data/spec/models/submission.rb +1 -0
- data/spec/models/user.rb +1 -0
- data/spec/support/sqlite_seed.rb +8 -0
- metadata +10 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 74ea4e863bfe254dde17af73253ffe7041cd2b2dabee2dad05d1eccb6904f229
|
4
|
+
data.tar.gz: 284dcd1a516922384bdaf1b3dd9e6a5e6776c213c3ecbc9ca41b223c5b92d36b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 26c43bfdac9582f059d067d4f56d4d85ed28416654ff5fd4686bf9977cbfbc8d92f887079954662e904fbe3d1b861d02482ac2fefac30a859fbdad8c3833e225
|
7
|
+
data.tar.gz: 48decfa9d28b9936f5c1f39c669b6e8061fae649f9036c29d4b28138083a43db8739f3b73bb9c390e9f6baea62aa1f66f79e8ced69cf13e789b8f116c101f25f
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
## Next Release
|
2
2
|
|
3
|
+
## 6.1.4 (02/26/2021)
|
4
|
+
|
5
|
+
* Added an option to stop adding HTTP headers to API requests
|
6
|
+
|
7
|
+
## 6.1.3 (01/21/2021)
|
8
|
+
|
9
|
+
* Consider ThroughAssociation at SingularAssociation like CollectionAssociation
|
10
|
+
* Add xhr_script only when add_footer is enabled
|
11
|
+
|
12
|
+
## 6.1.2 (12/12/2020)
|
13
|
+
|
14
|
+
* Revert "Make whitelist thread safe"
|
15
|
+
|
16
|
+
## 6.1.1 (12/12/2020)
|
17
|
+
|
18
|
+
* Add support Rails 6.1
|
19
|
+
* Make whitelist thread safe
|
20
|
+
|
3
21
|
## 6.1.0 (12/28/2019)
|
4
22
|
|
5
23
|
* Add skip_html_injection flag
|
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
|
|
@@ -86,7 +93,8 @@ The code above will enable all of the Bullet notification systems:
|
|
86
93
|
* `Bullet.rollbar`: add notifications to rollbar
|
87
94
|
* `Bullet.sentry`: add notifications to sentry
|
88
95
|
* `Bullet.add_footer`: adds the details in the bottom left corner of the page. Double click the footer or use close button to hide footer.
|
89
|
-
* `Bullet.skip_html_injection`: prevents Bullet from injecting
|
96
|
+
* `Bullet.skip_html_injection`: prevents Bullet from injecting code into the returned HTML. This must be false for receiving alerts, showing the footer or console logging.
|
97
|
+
* `Bullet.skip_http_headers`: don't add headers to API requests, and remove the javascript that relies on them. Note that this prevents bullet from logging warnings to the browser console or updating the footer.
|
90
98
|
* `Bullet.stacktrace_includes`: include paths with any of these substrings in the stack trace, even if they are not in your main app
|
91
99
|
* `Bullet.stacktrace_excludes`: ignore paths with any of these substrings in the stack trace, even if they are not in your main app.
|
92
100
|
Each item can be a string (match substring), a regex, or an array where the first item is a path to match, and the second
|
data/lib/bullet.rb
CHANGED
@@ -39,9 +39,9 @@ module Bullet
|
|
39
39
|
:stacktrace_excludes,
|
40
40
|
:skip_html_injection
|
41
41
|
attr_reader :whitelist
|
42
|
-
attr_accessor :add_footer, :orm_patches_applied
|
42
|
+
attr_accessor :add_footer, :orm_patches_applied, :skip_http_headers
|
43
43
|
|
44
|
-
available_notifiers = UniformNotifier::AVAILABLE_NOTIFIERS.map { |notifier| "#{notifier}=" }
|
44
|
+
available_notifiers = UniformNotifier::AVAILABLE_NOTIFIERS.select { |notifier| notifier != :raise }.map { |notifier| "#{notifier}=" }
|
45
45
|
available_notifiers_options = { to: UniformNotifier }
|
46
46
|
delegate(*available_notifiers, **available_notifiers_options)
|
47
47
|
|
@@ -73,7 +73,7 @@ module Bullet
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def app_root
|
76
|
-
(defined?(::Rails.root) ? Rails.root.to_s : Dir.pwd).to_s
|
76
|
+
@app_root ||= (defined?(::Rails.root) ? Rails.root.to_s : Dir.pwd).to_s
|
77
77
|
end
|
78
78
|
|
79
79
|
def n_plus_one_query_enable?
|
@@ -89,11 +89,11 @@ module Bullet
|
|
89
89
|
end
|
90
90
|
|
91
91
|
def stacktrace_includes
|
92
|
-
@stacktrace_includes
|
92
|
+
@stacktrace_includes ||= []
|
93
93
|
end
|
94
94
|
|
95
95
|
def stacktrace_excludes
|
96
|
-
@stacktrace_excludes
|
96
|
+
@stacktrace_excludes ||= []
|
97
97
|
end
|
98
98
|
|
99
99
|
def add_whitelist(options)
|
@@ -240,8 +240,10 @@ module Bullet
|
|
240
240
|
UniformNotifier.active_notifiers.include?(UniformNotifier::JavascriptConsole)
|
241
241
|
end
|
242
242
|
|
243
|
-
def
|
244
|
-
@skip_html_injection
|
243
|
+
def inject_into_page?
|
244
|
+
return false if defined?(@skip_html_injection) && @skip_html_injection
|
245
|
+
|
246
|
+
console_enabled? || add_footer
|
245
247
|
end
|
246
248
|
|
247
249
|
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
|
@@ -202,6 +202,17 @@ module Bullet
|
|
202
202
|
|
203
203
|
if Bullet.start?
|
204
204
|
if owner.class.name !~ /^HABTM_/ && !@inversed
|
205
|
+
if is_a? ::ActiveRecord::Associations::ThroughAssociation
|
206
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
|
207
|
+
association = owner.association reflection.through_reflection.name
|
208
|
+
Array(association.target).each do |through_record|
|
209
|
+
Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
|
210
|
+
end
|
211
|
+
|
212
|
+
if reflection.through_reflection != through_reflection
|
213
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
|
214
|
+
end
|
215
|
+
end
|
205
216
|
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
|
206
217
|
|
207
218
|
if Bullet::Detector::NPlusOneQuery.impossible?(owner)
|
@@ -229,6 +229,17 @@ module Bullet
|
|
229
229
|
|
230
230
|
if Bullet.start?
|
231
231
|
if owner.class.name !~ /^HABTM_/ && !@inversed
|
232
|
+
if is_a? ::ActiveRecord::Associations::ThroughAssociation
|
233
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
|
234
|
+
association = owner.association(reflection.through_reflection.name)
|
235
|
+
Array(association.target).each do |through_record|
|
236
|
+
Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
|
237
|
+
end
|
238
|
+
|
239
|
+
if reflection.through_reflection != through_reflection
|
240
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
|
241
|
+
end
|
242
|
+
end
|
232
243
|
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
|
233
244
|
|
234
245
|
if Bullet::Detector::NPlusOneQuery.impossible?(owner)
|
@@ -0,0 +1,278 @@
|
|
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
|
+
if is_a? ::ActiveRecord::Associations::ThroughAssociation
|
233
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
|
234
|
+
association = owner.association(reflection.through_reflection.name)
|
235
|
+
Array(association.target).each do |through_record|
|
236
|
+
Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
|
237
|
+
end
|
238
|
+
|
239
|
+
if reflection.through_reflection != through_reflection
|
240
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
|
244
|
+
|
245
|
+
if Bullet::Detector::NPlusOneQuery.impossible?(owner)
|
246
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
247
|
+
else
|
248
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
result
|
253
|
+
end
|
254
|
+
end
|
255
|
+
)
|
256
|
+
|
257
|
+
::ActiveRecord::Associations::HasManyAssociation.prepend(
|
258
|
+
Module.new do
|
259
|
+
def empty?
|
260
|
+
result = super
|
261
|
+
if Bullet.start? && !reflection.has_cached_counter?
|
262
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
|
263
|
+
end
|
264
|
+
result
|
265
|
+
end
|
266
|
+
|
267
|
+
def count_records
|
268
|
+
result = reflection.has_cached_counter?
|
269
|
+
if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
|
270
|
+
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
271
|
+
end
|
272
|
+
super
|
273
|
+
end
|
274
|
+
end
|
275
|
+
)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
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/mongoid4x.rb
CHANGED
data/lib/bullet/mongoid5x.rb
CHANGED
data/lib/bullet/mongoid6x.rb
CHANGED
data/lib/bullet/mongoid7x.rb
CHANGED
data/lib/bullet/rack.rb
CHANGED
@@ -17,14 +17,14 @@ 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
|
24
24
|
response_body = append_to_html_body(response_body, Bullet.gather_inline_notifications)
|
25
|
-
response_body = append_to_html_body(response_body, xhr_script)
|
25
|
+
response_body = append_to_html_body(response_body, xhr_script) if Bullet.add_footer && !Bullet.skip_http_headers
|
26
26
|
headers['Content-Length'] = response_body.bytesize.to_s
|
27
|
-
|
27
|
+
elsif !Bullet.skip_http_headers
|
28
28
|
set_header(headers, 'X-bullet-footer-text', Bullet.footer_info.uniq) if Bullet.add_footer
|
29
29
|
set_header(headers, 'X-bullet-console-text', Bullet.text_notifications) if Bullet.console_enabled?
|
30
30
|
end
|
@@ -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
|
|
@@ -3,6 +3,7 @@
|
|
3
3
|
module Bullet
|
4
4
|
module StackTraceFilter
|
5
5
|
VENDOR_PATH = '/vendor'
|
6
|
+
IS_RUBY_19 = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0')
|
6
7
|
|
7
8
|
def caller_in_project
|
8
9
|
vendor_root = Bullet.app_root + VENDOR_PATH
|
@@ -47,20 +48,15 @@ module Bullet
|
|
47
48
|
end
|
48
49
|
|
49
50
|
def location_as_path(location)
|
50
|
-
|
51
|
+
IS_RUBY_19 ? location : location.absolute_path.to_s
|
51
52
|
end
|
52
53
|
|
53
54
|
def select_caller_locations
|
54
|
-
if
|
55
|
+
if IS_RUBY_19
|
55
56
|
caller.select { |caller_path| yield caller_path }
|
56
57
|
else
|
57
58
|
caller_locations.select { |location| yield location }
|
58
59
|
end
|
59
60
|
end
|
60
|
-
|
61
|
-
def ruby_19?
|
62
|
-
@ruby_19 = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0') if @ruby_19.nil?
|
63
|
-
@ruby_19
|
64
|
-
end
|
65
61
|
end
|
66
62
|
end
|
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,9 +67,8 @@ 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
|
-
expect(middleware).to receive(:xhr_script).and_return('')
|
73
72
|
expect(Bullet).to receive(:perform_out_of_channel_notifications)
|
74
73
|
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
75
74
|
expect(headers['Content-Length']).to eq('56')
|
@@ -81,9 +80,121 @@ module Bullet
|
|
81
80
|
response.body = '<html><head></head><body>é</body></html>'
|
82
81
|
app.response = response
|
83
82
|
expect(Bullet).to receive(:notification?).and_return(true)
|
83
|
+
allow(Bullet).to receive(:console_enabled?).and_return(true)
|
84
84
|
expect(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
|
85
85
|
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
86
|
-
expect(headers['Content-Length']).to eq(
|
86
|
+
expect(headers['Content-Length']).to eq('58')
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'with injection notifiers' do
|
90
|
+
before do
|
91
|
+
expect(Bullet).to receive(:notification?).and_return(true)
|
92
|
+
allow(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
|
93
|
+
allow(middleware).to receive(:xhr_script).and_return('<script></script>')
|
94
|
+
allow(middleware).to receive(:footer_note).and_return('footer')
|
95
|
+
expect(Bullet).to receive(:perform_out_of_channel_notifications)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should change response body if add_footer is true' do
|
99
|
+
expect(Bullet).to receive(:add_footer).exactly(3).times.and_return(true)
|
100
|
+
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
101
|
+
|
102
|
+
expect(headers['Content-Length']).to eq((73 + middleware.send(:footer_note).length).to_s)
|
103
|
+
expect(response).to eq(%w[<html><head></head><body>footer<bullet></bullet><script></script></body></html>])
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should change response body for html safe string if add_footer is true' do
|
107
|
+
expect(Bullet).to receive(:add_footer).exactly(3).times.and_return(true)
|
108
|
+
app.response = Support::ResponseDouble.new.tap do |response|
|
109
|
+
response.body = ActiveSupport::SafeBuffer.new('<html><head></head><body></body></html>')
|
110
|
+
end
|
111
|
+
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
112
|
+
|
113
|
+
expect(headers['Content-Length']).to eq((73 + middleware.send(:footer_note).length).to_s)
|
114
|
+
expect(response).to eq(%w[<html><head></head><body>footer<bullet></bullet><script></script></body></html>])
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should add the footer-text header for non-html requests when add_footer is true' do
|
118
|
+
allow(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
|
119
|
+
allow(Bullet).to receive(:footer_info).and_return(['footer text'])
|
120
|
+
app.headers = {'Content-Type' => 'application/json'}
|
121
|
+
_, headers, _response = middleware.call({})
|
122
|
+
expect(headers).to include('X-bullet-footer-text' => '["footer text"]')
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should change response body if console_enabled is true' do
|
126
|
+
expect(Bullet).to receive(:console_enabled?).and_return(true)
|
127
|
+
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
128
|
+
expect(headers['Content-Length']).to eq('56')
|
129
|
+
expect(response).to eq(%w[<html><head></head><body><bullet></bullet></body></html>])
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'should change response body for html safe string if console_enabled is true' do
|
133
|
+
expect(Bullet).to receive(:console_enabled?).and_return(true)
|
134
|
+
app.response = Support::ResponseDouble.new.tap do |response|
|
135
|
+
response.body = ActiveSupport::SafeBuffer.new('<html><head></head><body></body></html>')
|
136
|
+
end
|
137
|
+
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
138
|
+
expect(headers['Content-Length']).to eq('56')
|
139
|
+
expect(response).to eq(%w[<html><head></head><body><bullet></bullet></body></html>])
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'should add headers for non-html requests when console_enabled is true' do
|
143
|
+
allow(Bullet).to receive(:console_enabled?).at_least(:once).and_return(true)
|
144
|
+
allow(Bullet).to receive(:text_notifications).and_return(['text notifications'])
|
145
|
+
app.headers = {'Content-Type' => 'application/json'}
|
146
|
+
_, headers, _response = middleware.call({})
|
147
|
+
expect(headers).to include('X-bullet-console-text' => '["text notifications"]')
|
148
|
+
end
|
149
|
+
|
150
|
+
it "shouldn't change response body unnecessarily" do
|
151
|
+
expected_response = Support::ResponseDouble.new 'Actual body'
|
152
|
+
app.response = expected_response
|
153
|
+
_, _, response = middleware.call({})
|
154
|
+
expect(response).to eq(expected_response)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "shouldn't add headers unnecessarily" do
|
158
|
+
app.headers = {'Content-Type' => 'application/json'}
|
159
|
+
_, headers, _response = middleware.call({})
|
160
|
+
expect(headers).not_to include('X-bullet-footer-text')
|
161
|
+
expect(headers).not_to include('X-bullet-console-text')
|
162
|
+
end
|
163
|
+
|
164
|
+
context "when skip_http_headers is enabled" do
|
165
|
+
before do
|
166
|
+
allow(Bullet).to receive(:skip_http_headers).and_return(true)
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'should include the footer but not the xhr script tag if add_footer is true' do
|
170
|
+
expect(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
|
171
|
+
_, headers, response = middleware.call({})
|
172
|
+
|
173
|
+
expect(headers['Content-Length']).to eq((56 + middleware.send(:footer_note).length).to_s)
|
174
|
+
expect(response).to eq(%w[<html><head></head><body>footer<bullet></bullet></body></html>])
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'should not include the xhr script tag if console_enabled is true' do
|
178
|
+
expect(Bullet).to receive(:console_enabled?).and_return(true)
|
179
|
+
_, headers, response = middleware.call({})
|
180
|
+
expect(headers['Content-Length']).to eq('56')
|
181
|
+
expect(response).to eq(%w[<html><head></head><body><bullet></bullet></body></html>])
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'should not add the footer-text header for non-html requests when add_footer is true' do
|
185
|
+
allow(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
|
186
|
+
app.headers = {'Content-Type' => 'application/json'}
|
187
|
+
_, headers, _response = middleware.call({})
|
188
|
+
expect(headers).not_to include('X-bullet-footer-text')
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'should not add headers for non-html requests when console_enabled is true' do
|
192
|
+
allow(Bullet).to receive(:console_enabled?).at_least(:once).and_return(true)
|
193
|
+
app.headers = {'Content-Type' => 'application/json'}
|
194
|
+
_, headers, _response = middleware.call({})
|
195
|
+
expect(headers).not_to include('X-bullet-console-text')
|
196
|
+
end
|
197
|
+
end
|
87
198
|
end
|
88
199
|
|
89
200
|
context 'when skip_html_injection is enabled' do
|
@@ -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
|
|
@@ -562,6 +561,42 @@ if active_record?
|
|
562
561
|
end
|
563
562
|
end
|
564
563
|
|
564
|
+
describe Bullet::Detector::Association, 'has_one :through' do
|
565
|
+
context 'user => attachment' do
|
566
|
+
it 'should detect non preload associations' do
|
567
|
+
User.all.each { |user| user.submission_attachment.file_name }
|
568
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
569
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
570
|
+
|
571
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(User, :submission_attachment)
|
572
|
+
end
|
573
|
+
|
574
|
+
it 'should detect preload associations' do
|
575
|
+
User.includes(:submission_attachment).each { |user| user.submission_attachment.file_name }
|
576
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
577
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
578
|
+
|
579
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
580
|
+
end
|
581
|
+
|
582
|
+
it 'should not detect preload associations' do
|
583
|
+
User.all.map(&:name)
|
584
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
585
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
586
|
+
|
587
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
588
|
+
end
|
589
|
+
|
590
|
+
it 'should detect unused preload associations' do
|
591
|
+
User.includes(:submission_attachment).map(&:name)
|
592
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
593
|
+
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(User, :submission_attachment)
|
594
|
+
|
595
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
596
|
+
end
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
565
600
|
describe Bullet::Detector::Association, 'call one association that in possible objects' do
|
566
601
|
it 'should not detect preload association' do
|
567
602
|
Post.all
|
@@ -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
|
|
data/spec/models/submission.rb
CHANGED
data/spec/models/user.rb
CHANGED
data/spec/support/sqlite_seed.rb
CHANGED
@@ -95,6 +95,9 @@ module Support
|
|
95
95
|
submission1.replies.create(name: 'reply2')
|
96
96
|
submission2.replies.create(name: 'reply3')
|
97
97
|
submission2.replies.create(name: 'reply4')
|
98
|
+
|
99
|
+
submission1.create_attachment(file_name: 'submission1 file')
|
100
|
+
submission2.create_attachment(file_name: 'submission2 file')
|
98
101
|
end
|
99
102
|
|
100
103
|
def setup_db
|
@@ -240,6 +243,11 @@ module Support
|
|
240
243
|
t.column :name, :string
|
241
244
|
t.column :category_id, :integer
|
242
245
|
end
|
246
|
+
|
247
|
+
create_table :attachments do |t|
|
248
|
+
t.column :file_name, :string
|
249
|
+
t.column :submission_id, :integer
|
250
|
+
end
|
243
251
|
end
|
244
252
|
end
|
245
253
|
end
|
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.4
|
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: 2021-02-26 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
|
@@ -127,6 +129,7 @@ files:
|
|
127
129
|
- spec/integration/counter_cache_spec.rb
|
128
130
|
- spec/integration/mongoid/association_spec.rb
|
129
131
|
- spec/models/address.rb
|
132
|
+
- spec/models/attachment.rb
|
130
133
|
- spec/models/author.rb
|
131
134
|
- spec/models/base_user.rb
|
132
135
|
- spec/models/category.rb
|
@@ -173,7 +176,7 @@ licenses:
|
|
173
176
|
metadata:
|
174
177
|
changelog_uri: https://github.com/flyerhzm/bullet/blob/master/CHANGELOG.md
|
175
178
|
source_code_uri: https://github.com/flyerhzm/bullet
|
176
|
-
post_install_message:
|
179
|
+
post_install_message:
|
177
180
|
rdoc_options: []
|
178
181
|
require_paths:
|
179
182
|
- lib
|
@@ -188,8 +191,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
188
191
|
- !ruby/object:Gem::Version
|
189
192
|
version: 1.3.6
|
190
193
|
requirements: []
|
191
|
-
rubygems_version: 3.
|
192
|
-
signing_key:
|
194
|
+
rubygems_version: 3.1.4
|
195
|
+
signing_key:
|
193
196
|
specification_version: 4
|
194
197
|
summary: help to kill N+1 queries and unused eager loading.
|
195
198
|
test_files:
|
@@ -214,6 +217,7 @@ test_files:
|
|
214
217
|
- spec/integration/counter_cache_spec.rb
|
215
218
|
- spec/integration/mongoid/association_spec.rb
|
216
219
|
- spec/models/address.rb
|
220
|
+
- spec/models/attachment.rb
|
217
221
|
- spec/models/author.rb
|
218
222
|
- spec/models/base_user.rb
|
219
223
|
- spec/models/category.rb
|