bullet 6.1.0 → 7.0.7
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/.github/workflows/main.yml +82 -0
- data/CHANGELOG.md +66 -0
- data/Gemfile.rails-6.0 +1 -1
- data/Gemfile.rails-6.1 +15 -0
- data/Gemfile.rails-7.0 +10 -0
- data/MIT-LICENSE +1 -1
- data/README.md +41 -27
- data/lib/bullet/active_job.rb +5 -1
- data/lib/bullet/active_record41.rb +1 -0
- data/lib/bullet/active_record42.rb +1 -0
- data/lib/bullet/active_record5.rb +10 -8
- data/lib/bullet/active_record52.rb +32 -25
- data/lib/bullet/active_record60.rb +30 -23
- data/lib/bullet/active_record61.rb +274 -0
- data/lib/bullet/active_record70.rb +284 -0
- data/lib/bullet/bullet_xhr.js +18 -17
- data/lib/bullet/dependency.rb +16 -0
- data/lib/bullet/detector/association.rb +8 -0
- data/lib/bullet/detector/base.rb +2 -1
- data/lib/bullet/detector/counter_cache.rb +2 -2
- data/lib/bullet/detector/n_plus_one_query.rb +24 -13
- data/lib/bullet/detector/unused_eager_loading.rb +3 -3
- 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 +32 -17
- data/lib/bullet/notification.rb +2 -1
- data/lib/bullet/rack.rb +64 -23
- data/lib/bullet/registry/call_stack.rb +12 -0
- data/lib/bullet/registry.rb +1 -0
- data/lib/bullet/stack_trace_filter.rb +15 -15
- data/lib/bullet/version.rb +1 -1
- data/lib/bullet.rb +35 -29
- data/lib/generators/bullet/install_generator.rb +22 -25
- data/perf/benchmark.rb +4 -1
- data/spec/bullet/detector/counter_cache_spec.rb +1 -1
- data/spec/bullet/detector/n_plus_one_query_spec.rb +1 -33
- data/spec/bullet/detector/unused_eager_loading_spec.rb +15 -10
- data/spec/bullet/ext/object_spec.rb +1 -1
- data/spec/bullet/notification/base_spec.rb +4 -4
- data/spec/bullet/notification/n_plus_one_query_spec.rb +1 -3
- data/spec/bullet/rack_spec.rb +152 -9
- data/spec/bullet/stack_trace_filter_spec.rb +26 -0
- data/spec/bullet_spec.rb +15 -15
- data/spec/integration/active_record/association_spec.rb +95 -12
- data/spec/integration/counter_cache_spec.rb +4 -4
- data/spec/integration/mongoid/association_spec.rb +4 -4
- data/spec/models/attachment.rb +5 -0
- data/spec/models/deal.rb +5 -0
- data/spec/models/folder.rb +2 -1
- data/spec/models/group.rb +2 -1
- data/spec/models/page.rb +2 -1
- data/spec/models/post.rb +2 -0
- data/spec/models/role.rb +7 -0
- data/spec/models/submission.rb +1 -0
- data/spec/models/user.rb +2 -0
- data/spec/models/writer.rb +2 -1
- data/spec/spec_helper.rb +0 -2
- data/spec/support/mongo_seed.rb +1 -0
- data/spec/support/sqlite_seed.rb +38 -0
- data/test.sh +2 -0
- metadata +20 -7
- data/.travis.yml +0 -33
data/lib/bullet/detector/base.rb
CHANGED
@@ -20,7 +20,7 @@ module Bullet
|
|
20
20
|
return unless Bullet.start?
|
21
21
|
return unless Bullet.counter_cache_enable?
|
22
22
|
|
23
|
-
objects = Array(object_or_objects)
|
23
|
+
objects = Array.wrap(object_or_objects)
|
24
24
|
return if objects.map(&:bullet_primary_key_value).compact.empty?
|
25
25
|
|
26
26
|
Bullet.debug(
|
@@ -54,7 +54,7 @@ module Bullet
|
|
54
54
|
private
|
55
55
|
|
56
56
|
def create_notification(klazz, associations)
|
57
|
-
notify_associations = Array(associations) - Bullet.
|
57
|
+
notify_associations = Array.wrap(associations) - Bullet.get_safelist_associations(:counter_cache, klazz)
|
58
58
|
|
59
59
|
if notify_associations.present?
|
60
60
|
notice = Bullet::Notification::CounterCache.new klazz, notify_associations
|
@@ -7,7 +7,7 @@ module Bullet
|
|
7
7
|
extend StackTraceFilter
|
8
8
|
|
9
9
|
class << self
|
10
|
-
# executed when object.
|
10
|
+
# executed when object.associations is called.
|
11
11
|
# first, it keeps this method call for object.association.
|
12
12
|
# then, it checks if this associations call is unpreload.
|
13
13
|
# if it is, keeps this unpreload associations and caller.
|
@@ -25,7 +25,7 @@ module Bullet
|
|
25
25
|
)
|
26
26
|
if !excluded_stacktrace_path? && conditions_met?(object, associations)
|
27
27
|
Bullet.debug('detect n + 1 query', "object: #{object.bullet_key}, associations: #{associations}")
|
28
|
-
create_notification caller_in_project, object.class.to_s, associations
|
28
|
+
create_notification caller_in_project(object.bullet_key), object.class.to_s, associations
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
@@ -33,14 +33,26 @@ module Bullet
|
|
33
33
|
return unless Bullet.start?
|
34
34
|
return unless Bullet.n_plus_one_query_enable?
|
35
35
|
|
36
|
-
objects = Array(object_or_objects)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
36
|
+
objects = Array.wrap(object_or_objects)
|
37
|
+
class_names_match_regex = true
|
38
|
+
primary_key_values_are_empty = true
|
39
|
+
keys_joined = ""
|
40
|
+
objects.each do |obj|
|
41
|
+
unless obj.class.name =~ /^HABTM_/
|
42
|
+
class_names_match_regex = false
|
43
|
+
end
|
44
|
+
unless obj.bullet_primary_key_value.nil?
|
45
|
+
primary_key_values_are_empty = false
|
46
|
+
end
|
47
|
+
keys_joined += "#{(keys_joined.empty?? '' : ', ')}#{obj.bullet_key}"
|
48
|
+
end
|
49
|
+
unless class_names_match_regex || primary_key_values_are_empty
|
50
|
+
Bullet.debug(
|
51
|
+
'Detector::NPlusOneQuery#add_possible_objects',
|
52
|
+
"objects: #{keys_joined}"
|
53
|
+
)
|
54
|
+
objects.each { |object| possible_objects.add object.bullet_key }
|
55
|
+
end
|
44
56
|
end
|
45
57
|
|
46
58
|
def add_impossible_object(object)
|
@@ -84,8 +96,7 @@ module Bullet
|
|
84
96
|
# associations == v comparison order is important here because
|
85
97
|
# v variable might be a squeel node where :== method is redefined,
|
86
98
|
# so it does not compare values at all and return unexpected results
|
87
|
-
result =
|
88
|
-
v.is_a?(Hash) ? v.key?(associations) : associations == v
|
99
|
+
result = v.is_a?(Hash) ? v.key?(associations) : associations == v
|
89
100
|
return true if result
|
90
101
|
end
|
91
102
|
|
@@ -95,7 +106,7 @@ module Bullet
|
|
95
106
|
private
|
96
107
|
|
97
108
|
def create_notification(callers, klazz, associations)
|
98
|
-
notify_associations = Array(associations) - Bullet.
|
109
|
+
notify_associations = Array.wrap(associations) - Bullet.get_safelist_associations(:n_plus_one_query, klazz)
|
99
110
|
|
100
111
|
if notify_associations.present?
|
101
112
|
notice = Bullet::Notification::NPlusOneQuery.new(callers, klazz, notify_associations)
|
@@ -10,7 +10,7 @@ module Bullet
|
|
10
10
|
# check if there are unused preload associations.
|
11
11
|
# get related_objects from eager_loadings associated with object and associations
|
12
12
|
# get call_object_association from associations of call_object_associations whose object is in related_objects
|
13
|
-
# if association not in call_object_association, then the object => association - call_object_association is ununsed preload
|
13
|
+
# if association not in call_object_association, then the object => association - call_object_association is ununsed preload associations
|
14
14
|
def check_unused_preload_associations
|
15
15
|
return unless Bullet.start?
|
16
16
|
return unless Bullet.unused_eager_loading_enable?
|
@@ -20,7 +20,7 @@ module Bullet
|
|
20
20
|
next if object_association_diff.empty?
|
21
21
|
|
22
22
|
Bullet.debug('detect unused preload', "object: #{bullet_key}, associations: #{object_association_diff}")
|
23
|
-
create_notification(caller_in_project, bullet_key.bullet_class_name, object_association_diff)
|
23
|
+
create_notification(caller_in_project(bullet_key), bullet_key.bullet_class_name, object_association_diff)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -65,7 +65,7 @@ module Bullet
|
|
65
65
|
private
|
66
66
|
|
67
67
|
def create_notification(callers, klazz, associations)
|
68
|
-
notify_associations = Array(associations) - Bullet.
|
68
|
+
notify_associations = Array.wrap(associations) - Bullet.get_safelist_associations(:unused_eager_loading, klazz)
|
69
69
|
|
70
70
|
if notify_associations.present?
|
71
71
|
notice = Bullet::Notification::UnusedEagerLoading.new(callers, klazz, notify_associations)
|
data/lib/bullet/mongoid4x.rb
CHANGED
data/lib/bullet/mongoid5x.rb
CHANGED
data/lib/bullet/mongoid6x.rb
CHANGED
data/lib/bullet/mongoid7x.rb
CHANGED
@@ -4,35 +4,50 @@ module Bullet
|
|
4
4
|
module Mongoid
|
5
5
|
def self.enable
|
6
6
|
require 'mongoid'
|
7
|
+
require 'rubygems'
|
7
8
|
::Mongoid::Contextual::Mongo.class_eval do
|
8
9
|
alias_method :origin_first, :first
|
9
10
|
alias_method :origin_last, :last
|
10
11
|
alias_method :origin_each, :each
|
11
12
|
alias_method :origin_eager_load, :eager_load
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
result = origin_last(opts)
|
21
|
-
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
22
|
-
result
|
14
|
+
%i[first last].each do |context|
|
15
|
+
default = Gem::Version.new(::Mongoid::VERSION) >= Gem::Version.new('7.5') ? nil : {}
|
16
|
+
define_method(context) do |opts = default|
|
17
|
+
result = send(:"origin_#{context}", opts)
|
18
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
19
|
+
result
|
20
|
+
end
|
23
21
|
end
|
24
22
|
|
25
23
|
def each(&block)
|
26
24
|
return to_enum unless block_given?
|
27
25
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
26
|
+
first_document = nil
|
27
|
+
document_count = 0
|
28
|
+
|
29
|
+
origin_each do |document|
|
30
|
+
document_count += 1
|
31
|
+
|
32
|
+
if document_count == 1
|
33
|
+
first_document = document
|
34
|
+
elsif document_count == 2
|
35
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects([first_document, document])
|
36
|
+
yield(first_document)
|
37
|
+
first_document = nil
|
38
|
+
yield(document)
|
39
|
+
else
|
40
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(document)
|
41
|
+
yield(document)
|
42
|
+
end
|
34
43
|
end
|
35
|
-
|
44
|
+
|
45
|
+
if document_count == 1
|
46
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(first_document)
|
47
|
+
yield(first_document)
|
48
|
+
end
|
49
|
+
|
50
|
+
self
|
36
51
|
end
|
37
52
|
|
38
53
|
def eager_load(docs)
|
data/lib/bullet/notification.rb
CHANGED
data/lib/bullet/rack.rb
CHANGED
@@ -4,6 +4,8 @@ module Bullet
|
|
4
4
|
class Rack
|
5
5
|
include Dependency
|
6
6
|
|
7
|
+
NONCE_MATCHER = /script-src .*'nonce-(?<nonce>[A-Za-z0-9+\/]+={0,2})'/
|
8
|
+
|
7
9
|
def initialize(app)
|
8
10
|
@app = app
|
9
11
|
end
|
@@ -17,14 +19,20 @@ module Bullet
|
|
17
19
|
response_body = nil
|
18
20
|
|
19
21
|
if Bullet.notification?
|
20
|
-
if
|
22
|
+
if Bullet.inject_into_page? && !file?(headers) && !sse?(headers) && !empty?(response) && status == 200
|
21
23
|
if html_request?(headers, response)
|
22
24
|
response_body = response_body(response)
|
23
|
-
|
24
|
-
|
25
|
-
|
25
|
+
|
26
|
+
with_security_policy_nonce(headers) do |nonce|
|
27
|
+
response_body = append_to_html_body(response_body, footer_note) if Bullet.add_footer
|
28
|
+
response_body = append_to_html_body(response_body, Bullet.gather_inline_notifications)
|
29
|
+
if Bullet.add_footer && !Bullet.skip_http_headers
|
30
|
+
response_body = append_to_html_body(response_body, xhr_script(nonce))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
26
34
|
headers['Content-Length'] = response_body.bytesize.to_s
|
27
|
-
|
35
|
+
elsif !Bullet.skip_http_headers
|
28
36
|
set_header(headers, 'X-bullet-footer-text', Bullet.footer_info.uniq) if Bullet.add_footer
|
29
37
|
set_header(headers, 'X-bullet-console-text', Bullet.text_notifications) if Bullet.console_enabled?
|
30
38
|
end
|
@@ -40,12 +48,14 @@ module Bullet
|
|
40
48
|
def empty?(response)
|
41
49
|
# response may be ["Not Found"], ["Move Permanently"], etc, but
|
42
50
|
# those should not happen if the status is 200
|
51
|
+
return true if !response.respond_to?(:body) && !response.respond_to?(:first)
|
43
52
|
body = response_body(response)
|
44
53
|
body.nil? || body.empty?
|
45
54
|
end
|
46
55
|
|
47
56
|
def append_to_html_body(response_body, content)
|
48
57
|
body = response_body.dup
|
58
|
+
content = content.html_safe if content.respond_to?(:html_safe)
|
49
59
|
if body.include?('</body>')
|
50
60
|
position = body.rindex('</body>')
|
51
61
|
body.insert(position, content)
|
@@ -55,14 +65,14 @@ module Bullet
|
|
55
65
|
end
|
56
66
|
|
57
67
|
def footer_note
|
58
|
-
"<
|
68
|
+
"<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
69
|
end
|
60
70
|
|
61
71
|
def set_header(headers, header_name, header_array)
|
62
72
|
# Many proxy applications such as Nginx and AWS ELB limit
|
63
73
|
# the size a header to 8KB, so truncate the list of reports to
|
64
74
|
# be under that limit
|
65
|
-
header_array.pop while header_array.to_json.length > 8 *
|
75
|
+
header_array.pop while header_array.to_json.length > 8 * 1024
|
66
76
|
headers[header_name] = header_array.to_json
|
67
77
|
end
|
68
78
|
|
@@ -75,42 +85,73 @@ module Bullet
|
|
75
85
|
end
|
76
86
|
|
77
87
|
def html_request?(headers, response)
|
78
|
-
headers['Content-Type']&.include?('text/html')
|
88
|
+
headers['Content-Type']&.include?('text/html')
|
79
89
|
end
|
80
90
|
|
81
91
|
def response_body(response)
|
82
92
|
if response.respond_to?(:body)
|
83
93
|
Array === response.body ? response.body.first : response.body
|
84
|
-
|
94
|
+
elsif response.respond_to?(:first)
|
85
95
|
response.first
|
86
96
|
end
|
87
97
|
end
|
88
98
|
|
89
99
|
private
|
90
100
|
|
91
|
-
def
|
101
|
+
def details_attributes
|
102
|
+
<<~EOF
|
103
|
+
id="bullet-footer" data-is-bullet-footer
|
104
|
+
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;"
|
105
|
+
EOF
|
106
|
+
end
|
107
|
+
|
108
|
+
def summary_attributes
|
109
|
+
<<~EOF
|
110
|
+
style="font-weight: 600; padding: 2px 8px"
|
111
|
+
EOF
|
112
|
+
end
|
113
|
+
|
114
|
+
def footer_content_attributes
|
92
115
|
<<~EOF
|
93
|
-
|
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;"
|
116
|
+
style="padding: 8px; border-top: 1px solid #9b1c1c;"
|
98
117
|
EOF
|
99
118
|
end
|
100
119
|
|
101
|
-
def
|
102
|
-
cancel_button =
|
103
|
-
"<span onclick='this.parentNode.remove()' style='position:absolute; right: 10px; top: 0px; font-weight: bold; color: #333;'>×</span>"
|
120
|
+
def footer_console_message
|
104
121
|
if Bullet.console_enabled?
|
105
|
-
"<span>See 'Uniform Notifier' in JS Console for Stacktrace</span
|
106
|
-
else
|
107
|
-
cancel_button
|
122
|
+
"<br/><span style='font-style: italic;'>See 'Uniform Notifier' in JS Console for Stacktrace</span>"
|
108
123
|
end
|
109
124
|
end
|
110
125
|
|
111
126
|
# Make footer work for XHR requests by appending data to the footer
|
112
|
-
def xhr_script
|
113
|
-
|
127
|
+
def xhr_script(nonce = nil)
|
128
|
+
script = File.read("#{__dir__}/bullet_xhr.js")
|
129
|
+
|
130
|
+
if nonce
|
131
|
+
"<script type='text/javascript' nonce='#{nonce}'>#{script}</script>"
|
132
|
+
else
|
133
|
+
"<script type='text/javascript'>#{script}</script>"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def with_security_policy_nonce(headers)
|
138
|
+
matched = (headers['Content-Security-Policy'] || '').match(NONCE_MATCHER)
|
139
|
+
nonce = matched[:nonce] if matched
|
140
|
+
|
141
|
+
if nonce
|
142
|
+
console_enabled = UniformNotifier.console
|
143
|
+
alert_enabled = UniformNotifier.alert
|
144
|
+
|
145
|
+
UniformNotifier.console = { attributes: { nonce: nonce } } if console_enabled
|
146
|
+
UniformNotifier.alert = { attributes: { nonce: nonce } } if alert_enabled
|
147
|
+
|
148
|
+
yield nonce
|
149
|
+
|
150
|
+
UniformNotifier.console = console_enabled
|
151
|
+
UniformNotifier.alert = alert_enabled
|
152
|
+
else
|
153
|
+
yield
|
154
|
+
end
|
114
155
|
end
|
115
156
|
end
|
116
157
|
end
|
data/lib/bullet/registry.rb
CHANGED
@@ -1,17 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require "bundler"
|
2
3
|
|
3
4
|
module Bullet
|
4
5
|
module StackTraceFilter
|
5
6
|
VENDOR_PATH = '/vendor'
|
7
|
+
IS_RUBY_19 = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0')
|
6
8
|
|
7
|
-
|
9
|
+
# @param bullet_key[String] - use this to get stored call stack from call_stacks object.
|
10
|
+
def caller_in_project(bullet_key = nil)
|
8
11
|
vendor_root = Bullet.app_root + VENDOR_PATH
|
9
12
|
bundler_path = Bundler.bundle_path.to_s
|
10
|
-
select_caller_locations do |location|
|
13
|
+
select_caller_locations(bullet_key) do |location|
|
11
14
|
caller_path = location_as_path(location)
|
12
15
|
caller_path.include?(Bullet.app_root) && !caller_path.include?(vendor_root) &&
|
13
|
-
!caller_path.include?(bundler_path) ||
|
14
|
-
|
16
|
+
!caller_path.include?(bundler_path) || Bullet.stacktrace_includes.any? { |include_pattern|
|
17
|
+
pattern_matches?(location, include_pattern)
|
18
|
+
}
|
15
19
|
end
|
16
20
|
end
|
17
21
|
|
@@ -47,20 +51,16 @@ module Bullet
|
|
47
51
|
end
|
48
52
|
|
49
53
|
def location_as_path(location)
|
50
|
-
|
51
|
-
end
|
54
|
+
return location if location.is_a?(String)
|
52
55
|
|
53
|
-
|
54
|
-
if ruby_19?
|
55
|
-
caller.select { |caller_path| yield caller_path }
|
56
|
-
else
|
57
|
-
caller_locations.select { |location| yield location }
|
58
|
-
end
|
56
|
+
IS_RUBY_19 ? location : location.absolute_path.to_s
|
59
57
|
end
|
60
58
|
|
61
|
-
def
|
62
|
-
|
63
|
-
|
59
|
+
def select_caller_locations(bullet_key = nil)
|
60
|
+
return caller.select { |caller_path| yield caller_path } if IS_RUBY_19
|
61
|
+
|
62
|
+
call_stack = bullet_key ? call_stacks[bullet_key] : caller_locations
|
63
|
+
call_stack.select { |location| yield location }
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
data/lib/bullet/version.rb
CHANGED
data/lib/bullet.rb
CHANGED
@@ -20,13 +20,14 @@ module Bullet
|
|
20
20
|
autoload :Registry, 'bullet/registry'
|
21
21
|
autoload :NotificationCollector, 'bullet/notification_collector'
|
22
22
|
|
23
|
-
BULLET_DEBUG = 'BULLET_DEBUG'
|
24
|
-
TRUE = 'true'
|
25
|
-
|
26
23
|
if defined?(Rails::Railtie)
|
27
24
|
class BulletRailtie < Rails::Railtie
|
28
25
|
initializer 'bullet.configure_rails_initialization' do |app|
|
29
|
-
|
26
|
+
if defined?(ActionDispatch::ContentSecurityPolicy::Middleware) && Rails.application.config.content_security_policy
|
27
|
+
app.middleware.insert_before ActionDispatch::ContentSecurityPolicy::Middleware, Bullet::Rack
|
28
|
+
else
|
29
|
+
app.middleware.use Bullet::Rack
|
30
|
+
end
|
30
31
|
end
|
31
32
|
end
|
32
33
|
end
|
@@ -38,10 +39,11 @@ module Bullet
|
|
38
39
|
:stacktrace_includes,
|
39
40
|
:stacktrace_excludes,
|
40
41
|
:skip_html_injection
|
41
|
-
attr_reader :
|
42
|
-
attr_accessor :add_footer, :orm_patches_applied
|
42
|
+
attr_reader :safelist
|
43
|
+
attr_accessor :add_footer, :orm_patches_applied, :skip_http_headers
|
43
44
|
|
44
|
-
available_notifiers =
|
45
|
+
available_notifiers =
|
46
|
+
UniformNotifier::AVAILABLE_NOTIFIERS.select { |notifier| notifier != :raise }.map { |notifier| "#{notifier}=" }
|
45
47
|
available_notifiers_options = { to: UniformNotifier }
|
46
48
|
delegate(*available_notifiers, **available_notifiers_options)
|
47
49
|
|
@@ -59,7 +61,7 @@ module Bullet
|
|
59
61
|
@enable = @n_plus_one_query_enable = @unused_eager_loading_enable = @counter_cache_enable = enable
|
60
62
|
|
61
63
|
if enable?
|
62
|
-
|
64
|
+
reset_safelist
|
63
65
|
unless orm_patches_applied
|
64
66
|
self.orm_patches_applied = true
|
65
67
|
Bullet::Mongoid.enable if mongoid?
|
@@ -72,8 +74,9 @@ module Bullet
|
|
72
74
|
!!@enable
|
73
75
|
end
|
74
76
|
|
77
|
+
# Rails.root might be nil if `railties` is a dependency on a project that does not use Rails
|
75
78
|
def app_root
|
76
|
-
(defined?(::Rails.root) ? Rails.root.to_s : Dir.pwd).to_s
|
79
|
+
@app_root ||= (defined?(::Rails.root) && !::Rails.root.nil? ? Rails.root.to_s : Dir.pwd).to_s
|
77
80
|
end
|
78
81
|
|
79
82
|
def n_plus_one_query_enable?
|
@@ -89,36 +92,36 @@ module Bullet
|
|
89
92
|
end
|
90
93
|
|
91
94
|
def stacktrace_includes
|
92
|
-
@stacktrace_includes
|
95
|
+
@stacktrace_includes ||= []
|
93
96
|
end
|
94
97
|
|
95
98
|
def stacktrace_excludes
|
96
|
-
@stacktrace_excludes
|
99
|
+
@stacktrace_excludes ||= []
|
97
100
|
end
|
98
101
|
|
99
|
-
def
|
100
|
-
|
101
|
-
@
|
102
|
-
@
|
102
|
+
def add_safelist(options)
|
103
|
+
reset_safelist
|
104
|
+
@safelist[options[:type]][options[:class_name]] ||= []
|
105
|
+
@safelist[options[:type]][options[:class_name]] << options[:association].to_sym
|
103
106
|
end
|
104
107
|
|
105
|
-
def
|
106
|
-
|
107
|
-
@
|
108
|
-
@
|
109
|
-
@
|
108
|
+
def delete_safelist(options)
|
109
|
+
reset_safelist
|
110
|
+
@safelist[options[:type]][options[:class_name]] ||= []
|
111
|
+
@safelist[options[:type]][options[:class_name]].delete(options[:association].to_sym)
|
112
|
+
@safelist[options[:type]].delete_if { |_key, val| val.empty? }
|
110
113
|
end
|
111
114
|
|
112
|
-
def
|
113
|
-
Array(@
|
115
|
+
def get_safelist_associations(type, class_name)
|
116
|
+
Array.wrap(@safelist[type][class_name])
|
114
117
|
end
|
115
118
|
|
116
|
-
def
|
117
|
-
@
|
119
|
+
def reset_safelist
|
120
|
+
@safelist ||= { n_plus_one_query: {}, unused_eager_loading: {}, counter_cache: {} }
|
118
121
|
end
|
119
122
|
|
120
|
-
def
|
121
|
-
@
|
123
|
+
def clear_safelist
|
124
|
+
@safelist = nil
|
122
125
|
end
|
123
126
|
|
124
127
|
def bullet_logger=(active)
|
@@ -132,7 +135,7 @@ module Bullet
|
|
132
135
|
end
|
133
136
|
|
134
137
|
def debug(title, message)
|
135
|
-
puts "[Bullet][#{title}] #{message}" if ENV[BULLET_DEBUG] ==
|
138
|
+
puts "[Bullet][#{title}] #{message}" if ENV['BULLET_DEBUG'] == 'true'
|
136
139
|
end
|
137
140
|
|
138
141
|
def start_request
|
@@ -145,6 +148,7 @@ module Bullet
|
|
145
148
|
Thread.current[:bullet_impossible_objects] = Bullet::Registry::Object.new
|
146
149
|
Thread.current[:bullet_inversed_objects] = Bullet::Registry::Base.new
|
147
150
|
Thread.current[:bullet_eager_loadings] = Bullet::Registry::Association.new
|
151
|
+
Thread.current[:bullet_call_stacks] = Bullet::Registry::CallStack.new
|
148
152
|
|
149
153
|
Thread.current[:bullet_counter_possible_objects] ||= Bullet::Registry::Object.new
|
150
154
|
Thread.current[:bullet_counter_impossible_objects] ||= Bullet::Registry::Object.new
|
@@ -240,8 +244,10 @@ module Bullet
|
|
240
244
|
UniformNotifier.active_notifiers.include?(UniformNotifier::JavascriptConsole)
|
241
245
|
end
|
242
246
|
|
243
|
-
def
|
244
|
-
@skip_html_injection
|
247
|
+
def inject_into_page?
|
248
|
+
return false if defined?(@skip_html_injection) && @skip_html_injection
|
249
|
+
|
250
|
+
console_enabled? || add_footer
|
245
251
|
end
|
246
252
|
|
247
253
|
private
|