bullet 6.0.2 → 7.0.3
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 +52 -0
- data/Gemfile.rails-6.0 +1 -1
- data/Gemfile.rails-6.1 +15 -0
- data/Gemfile.rails-7.0 +10 -0
- data/README.md +39 -25
- data/lib/bullet/active_job.rb +1 -3
- data/lib/bullet/active_record4.rb +9 -23
- data/lib/bullet/active_record41.rb +8 -19
- data/lib/bullet/active_record42.rb +9 -16
- data/lib/bullet/active_record5.rb +188 -170
- data/lib/bullet/active_record52.rb +182 -162
- data/lib/bullet/active_record60.rb +191 -178
- data/lib/bullet/active_record61.rb +272 -0
- data/lib/bullet/active_record70.rb +275 -0
- data/lib/bullet/bullet_xhr.js +18 -23
- data/lib/bullet/dependency.rb +52 -34
- data/lib/bullet/detector/association.rb +24 -18
- data/lib/bullet/detector/counter_cache.rb +12 -8
- data/lib/bullet/detector/n_plus_one_query.rb +20 -10
- data/lib/bullet/detector/unused_eager_loading.rb +7 -4
- data/lib/bullet/mongoid4x.rb +3 -7
- data/lib/bullet/mongoid5x.rb +3 -7
- data/lib/bullet/mongoid6x.rb +3 -7
- data/lib/bullet/mongoid7x.rb +26 -13
- data/lib/bullet/notification/base.rb +14 -18
- data/lib/bullet/notification/n_plus_one_query.rb +2 -4
- data/lib/bullet/notification/unused_eager_loading.rb +2 -4
- data/lib/bullet/notification.rb +2 -1
- data/lib/bullet/rack.rb +28 -17
- data/lib/bullet/stack_trace_filter.rb +10 -17
- data/lib/bullet/version.rb +1 -1
- data/lib/bullet.rb +52 -42
- data/lib/generators/bullet/install_generator.rb +22 -23
- data/perf/benchmark.rb +11 -14
- data/spec/bullet/detector/counter_cache_spec.rb +6 -6
- data/spec/bullet/detector/n_plus_one_query_spec.rb +8 -4
- data/spec/bullet/detector/unused_eager_loading_spec.rb +25 -8
- data/spec/bullet/ext/object_spec.rb +1 -1
- data/spec/bullet/notification/base_spec.rb +5 -7
- data/spec/bullet/notification/n_plus_one_query_spec.rb +16 -3
- data/spec/bullet/notification/unused_eager_loading_spec.rb +5 -1
- data/spec/bullet/rack_spec.rb +154 -13
- data/spec/bullet/registry/association_spec.rb +2 -2
- data/spec/bullet/registry/base_spec.rb +1 -1
- data/spec/bullet_spec.rb +25 -44
- data/spec/integration/active_record/association_spec.rb +104 -130
- data/spec/integration/counter_cache_spec.rb +14 -34
- data/spec/integration/mongoid/association_spec.rb +19 -33
- data/spec/models/attachment.rb +5 -0
- data/spec/models/deal.rb +5 -0
- 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/spec_helper.rb +4 -10
- data/spec/support/bullet_ext.rb +8 -9
- data/spec/support/mongo_seed.rb +3 -16
- data/spec/support/sqlite_seed.rb +38 -0
- data/test.sh +2 -0
- metadata +17 -7
- data/.travis.yml +0 -31
data/lib/bullet/rack.rb
CHANGED
@@ -15,15 +15,18 @@ module Bullet
|
|
15
15
|
status, headers, response = @app.call(env)
|
16
16
|
|
17
17
|
response_body = nil
|
18
|
+
|
18
19
|
if Bullet.notification?
|
19
|
-
if !file?(headers) && !sse?(headers) && !empty?(response) && status == 200
|
20
|
+
if Bullet.inject_into_page? && !file?(headers) && !sse?(headers) && !empty?(response) && status == 200
|
20
21
|
if html_request?(headers, response)
|
21
22
|
response_body = response_body(response)
|
22
23
|
response_body = append_to_html_body(response_body, footer_note) if Bullet.add_footer
|
23
24
|
response_body = append_to_html_body(response_body, Bullet.gather_inline_notifications)
|
24
|
-
|
25
|
+
if Bullet.add_footer && !Bullet.skip_http_headers
|
26
|
+
response_body = append_to_html_body(response_body, xhr_script)
|
27
|
+
end
|
25
28
|
headers['Content-Length'] = response_body.bytesize.to_s
|
26
|
-
|
29
|
+
elsif !Bullet.skip_http_headers
|
27
30
|
set_header(headers, 'X-bullet-footer-text', Bullet.footer_info.uniq) if Bullet.add_footer
|
28
31
|
set_header(headers, 'X-bullet-console-text', Bullet.text_notifications) if Bullet.console_enabled?
|
29
32
|
end
|
@@ -39,12 +42,14 @@ module Bullet
|
|
39
42
|
def empty?(response)
|
40
43
|
# response may be ["Not Found"], ["Move Permanently"], etc, but
|
41
44
|
# those should not happen if the status is 200
|
45
|
+
return true if !response.respond_to?(:body) && !response.respond_to?(:first)
|
42
46
|
body = response_body(response)
|
43
47
|
body.nil? || body.empty?
|
44
48
|
end
|
45
49
|
|
46
50
|
def append_to_html_body(response_body, content)
|
47
51
|
body = response_body.dup
|
52
|
+
content = content.html_safe if content.respond_to?(:html_safe)
|
48
53
|
if body.include?('</body>')
|
49
54
|
position = body.rindex('</body>')
|
50
55
|
body.insert(position, content)
|
@@ -54,7 +59,7 @@ module Bullet
|
|
54
59
|
end
|
55
60
|
|
56
61
|
def footer_note
|
57
|
-
"<
|
62
|
+
"<details #{details_attributes}><summary #{summary_attributes}>Bullet Warnings</summary><div #{footer_content_attributes}>#{Bullet.footer_info.uniq.join('<br>')}#{footer_console_message}</div></details>"
|
58
63
|
end
|
59
64
|
|
60
65
|
def set_header(headers, header_name, header_array)
|
@@ -74,35 +79,41 @@ module Bullet
|
|
74
79
|
end
|
75
80
|
|
76
81
|
def html_request?(headers, response)
|
77
|
-
headers['Content-Type']&.include?('text/html')
|
82
|
+
headers['Content-Type']&.include?('text/html')
|
78
83
|
end
|
79
84
|
|
80
85
|
def response_body(response)
|
81
86
|
if response.respond_to?(:body)
|
82
87
|
Array === response.body ? response.body.first : response.body
|
83
|
-
|
88
|
+
elsif response.respond_to?(:first)
|
84
89
|
response.first
|
85
90
|
end
|
86
91
|
end
|
87
92
|
|
88
93
|
private
|
89
94
|
|
90
|
-
def
|
95
|
+
def details_attributes
|
96
|
+
<<~EOF
|
97
|
+
id="bullet-footer" data-is-bullet-footer
|
98
|
+
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;"
|
99
|
+
EOF
|
100
|
+
end
|
101
|
+
|
102
|
+
def summary_attributes
|
91
103
|
<<~EOF
|
92
|
-
|
93
|
-
-moz-border-top-colors: none; -moz-border-right-colors: none; -moz-border-bottom-colors: none;
|
94
|
-
-moz-border-left-colors: none; -moz-border-image: none; border-width: 2pt 2pt 0px 0px;
|
95
|
-
padding: 3px 5px; border-radius: 0pt 10pt 0pt 0px; background: none repeat scroll 0% 0% rgba(200, 200, 200, 0.8);
|
96
|
-
color: rgb(119, 119, 119); font-size: 16px; font-family: 'Arial', sans-serif; z-index:9999;"
|
104
|
+
style="font-weight: 600; padding: 2px 8px"
|
97
105
|
EOF
|
98
106
|
end
|
99
107
|
|
100
|
-
def
|
101
|
-
|
108
|
+
def footer_content_attributes
|
109
|
+
<<~EOF
|
110
|
+
style="padding: 8px; border-top: 1px solid #9b1c1c;"
|
111
|
+
EOF
|
112
|
+
end
|
113
|
+
|
114
|
+
def footer_console_message
|
102
115
|
if Bullet.console_enabled?
|
103
|
-
"<span>See 'Uniform Notifier' in JS Console for Stacktrace</span
|
104
|
-
else
|
105
|
-
cancel_button
|
116
|
+
"<br/><span style='font-style: italic;'>See 'Uniform Notifier' in JS Console for Stacktrace</span>"
|
106
117
|
end
|
107
118
|
end
|
108
119
|
|
@@ -1,16 +1,20 @@
|
|
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
|
def caller_in_project
|
8
10
|
vendor_root = Bullet.app_root + VENDOR_PATH
|
9
11
|
bundler_path = Bundler.bundle_path.to_s
|
10
12
|
select_caller_locations do |location|
|
11
13
|
caller_path = location_as_path(location)
|
12
|
-
caller_path.include?(Bullet.app_root) && !caller_path.include?(vendor_root) &&
|
13
|
-
Bullet.stacktrace_includes.any? { |include_pattern|
|
14
|
+
caller_path.include?(Bullet.app_root) && !caller_path.include?(vendor_root) &&
|
15
|
+
!caller_path.include?(bundler_path) || Bullet.stacktrace_includes.any? { |include_pattern|
|
16
|
+
pattern_matches?(location, include_pattern)
|
17
|
+
}
|
14
18
|
end
|
15
19
|
end
|
16
20
|
|
@@ -46,26 +50,15 @@ module Bullet
|
|
46
50
|
end
|
47
51
|
|
48
52
|
def location_as_path(location)
|
49
|
-
|
53
|
+
IS_RUBY_19 ? location : location.absolute_path.to_s
|
50
54
|
end
|
51
55
|
|
52
56
|
def select_caller_locations
|
53
|
-
if
|
54
|
-
caller.select
|
55
|
-
yield caller_path
|
56
|
-
end
|
57
|
+
if IS_RUBY_19
|
58
|
+
caller.select { |caller_path| yield caller_path }
|
57
59
|
else
|
58
|
-
caller_locations.select
|
59
|
-
yield location
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def ruby_19?
|
65
|
-
if @ruby_19.nil?
|
66
|
-
@ruby_19 = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0')
|
60
|
+
caller_locations.select { |location| yield location }
|
67
61
|
end
|
68
|
-
@ruby_19
|
69
62
|
end
|
70
63
|
end
|
71
64
|
end
|
data/lib/bullet/version.rb
CHANGED
data/lib/bullet.rb
CHANGED
@@ -20,10 +20,7 @@ module Bullet
|
|
20
20
|
autoload :Registry, 'bullet/registry'
|
21
21
|
autoload :NotificationCollector, 'bullet/notification_collector'
|
22
22
|
|
23
|
-
|
24
|
-
TRUE = 'true'
|
25
|
-
|
26
|
-
if defined? Rails::Railtie
|
23
|
+
if defined?(Rails::Railtie)
|
27
24
|
class BulletRailtie < Rails::Railtie
|
28
25
|
initializer 'bullet.configure_rails_initialization' do |app|
|
29
26
|
app.middleware.use Bullet::Rack
|
@@ -32,28 +29,37 @@ module Bullet
|
|
32
29
|
end
|
33
30
|
|
34
31
|
class << self
|
35
|
-
attr_writer :n_plus_one_query_enable,
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
32
|
+
attr_writer :n_plus_one_query_enable,
|
33
|
+
:unused_eager_loading_enable,
|
34
|
+
:counter_cache_enable,
|
35
|
+
:stacktrace_includes,
|
36
|
+
:stacktrace_excludes,
|
37
|
+
:skip_html_injection
|
38
|
+
attr_reader :safelist
|
39
|
+
attr_accessor :add_footer, :orm_patches_applied, :skip_http_headers
|
40
|
+
|
41
|
+
available_notifiers =
|
42
|
+
UniformNotifier::AVAILABLE_NOTIFIERS.select { |notifier| notifier != :raise }.map { |notifier| "#{notifier}=" }
|
43
|
+
available_notifiers_options = { to: UniformNotifier }
|
44
|
+
delegate(*available_notifiers, **available_notifiers_options)
|
42
45
|
|
43
46
|
def raise=(should_raise)
|
44
47
|
UniformNotifier.raise = (should_raise ? Notification::UnoptimizedQueryError : false)
|
45
48
|
end
|
46
49
|
|
47
|
-
DETECTORS = [
|
48
|
-
|
49
|
-
|
50
|
+
DETECTORS = [
|
51
|
+
Bullet::Detector::NPlusOneQuery,
|
52
|
+
Bullet::Detector::UnusedEagerLoading,
|
53
|
+
Bullet::Detector::CounterCache
|
54
|
+
].freeze
|
50
55
|
|
51
56
|
def enable=(enable)
|
52
57
|
@enable = @n_plus_one_query_enable = @unused_eager_loading_enable = @counter_cache_enable = enable
|
58
|
+
|
53
59
|
if enable?
|
54
|
-
|
55
|
-
unless
|
56
|
-
self.
|
60
|
+
reset_safelist
|
61
|
+
unless orm_patches_applied
|
62
|
+
self.orm_patches_applied = true
|
57
63
|
Bullet::Mongoid.enable if mongoid?
|
58
64
|
Bullet::ActiveRecord.enable if active_record?
|
59
65
|
end
|
@@ -64,8 +70,9 @@ module Bullet
|
|
64
70
|
!!@enable
|
65
71
|
end
|
66
72
|
|
73
|
+
# Rails.root might be nil if `railties` is a dependency on a project that does not use Rails
|
67
74
|
def app_root
|
68
|
-
(defined?(::Rails.root) ? Rails.root.to_s : Dir.pwd).to_s
|
75
|
+
@app_root ||= (defined?(::Rails.root) && !::Rails.root.nil? ? Rails.root.to_s : Dir.pwd).to_s
|
69
76
|
end
|
70
77
|
|
71
78
|
def n_plus_one_query_enable?
|
@@ -81,36 +88,36 @@ module Bullet
|
|
81
88
|
end
|
82
89
|
|
83
90
|
def stacktrace_includes
|
84
|
-
@stacktrace_includes
|
91
|
+
@stacktrace_includes ||= []
|
85
92
|
end
|
86
93
|
|
87
94
|
def stacktrace_excludes
|
88
|
-
@stacktrace_excludes
|
95
|
+
@stacktrace_excludes ||= []
|
89
96
|
end
|
90
97
|
|
91
|
-
def
|
92
|
-
|
93
|
-
@
|
94
|
-
@
|
98
|
+
def add_safelist(options)
|
99
|
+
reset_safelist
|
100
|
+
@safelist[options[:type]][options[:class_name]] ||= []
|
101
|
+
@safelist[options[:type]][options[:class_name]] << options[:association].to_sym
|
95
102
|
end
|
96
103
|
|
97
|
-
def
|
98
|
-
|
99
|
-
@
|
100
|
-
@
|
101
|
-
@
|
104
|
+
def delete_safelist(options)
|
105
|
+
reset_safelist
|
106
|
+
@safelist[options[:type]][options[:class_name]] ||= []
|
107
|
+
@safelist[options[:type]][options[:class_name]].delete(options[:association].to_sym)
|
108
|
+
@safelist[options[:type]].delete_if { |_key, val| val.empty? }
|
102
109
|
end
|
103
110
|
|
104
|
-
def
|
105
|
-
Array(@
|
111
|
+
def get_safelist_associations(type, class_name)
|
112
|
+
Array.wrap(@safelist[type][class_name])
|
106
113
|
end
|
107
114
|
|
108
|
-
def
|
109
|
-
@
|
115
|
+
def reset_safelist
|
116
|
+
@safelist ||= { n_plus_one_query: {}, unused_eager_loading: {}, counter_cache: {} }
|
110
117
|
end
|
111
118
|
|
112
|
-
def
|
113
|
-
@
|
119
|
+
def clear_safelist
|
120
|
+
@safelist = nil
|
114
121
|
end
|
115
122
|
|
116
123
|
def bullet_logger=(active)
|
@@ -124,7 +131,7 @@ module Bullet
|
|
124
131
|
end
|
125
132
|
|
126
133
|
def debug(title, message)
|
127
|
-
puts "[Bullet][#{title}] #{message}" if ENV[BULLET_DEBUG] ==
|
134
|
+
puts "[Bullet][#{title}] #{message}" if ENV['BULLET_DEBUG'] == 'true'
|
128
135
|
end
|
129
136
|
|
130
137
|
def start_request
|
@@ -174,9 +181,7 @@ module Bullet
|
|
174
181
|
|
175
182
|
def gather_inline_notifications
|
176
183
|
responses = []
|
177
|
-
for_each_active_notifier_with_notification
|
178
|
-
responses << notification.notify_inline
|
179
|
-
end
|
184
|
+
for_each_active_notifier_with_notification { |notification| responses << notification.notify_inline }
|
180
185
|
responses.join("\n")
|
181
186
|
end
|
182
187
|
|
@@ -190,9 +195,7 @@ module Bullet
|
|
190
195
|
|
191
196
|
def footer_info
|
192
197
|
info = []
|
193
|
-
notification_collector.collection.each
|
194
|
-
info << notification.short_notice
|
195
|
-
end
|
198
|
+
notification_collector.collection.each { |notification| info << notification.short_notice }
|
196
199
|
info
|
197
200
|
end
|
198
201
|
|
@@ -214,6 +217,7 @@ module Bullet
|
|
214
217
|
|
215
218
|
def profile
|
216
219
|
return_value = nil
|
220
|
+
|
217
221
|
if Bullet.enable?
|
218
222
|
begin
|
219
223
|
Bullet.start_request
|
@@ -235,6 +239,12 @@ module Bullet
|
|
235
239
|
UniformNotifier.active_notifiers.include?(UniformNotifier::JavascriptConsole)
|
236
240
|
end
|
237
241
|
|
242
|
+
def inject_into_page?
|
243
|
+
return false if defined?(@skip_html_injection) && @skip_html_injection
|
244
|
+
|
245
|
+
console_enabled? || add_footer
|
246
|
+
end
|
247
|
+
|
238
248
|
private
|
239
249
|
|
240
250
|
def for_each_active_notifier_with_notification
|
@@ -10,17 +10,16 @@ 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
|
-
end
|
13
|
+
<<~FILE
|
14
|
+
config.after_initialize do
|
15
|
+
Bullet.enable = true
|
16
|
+
Bullet.alert = true
|
17
|
+
Bullet.bullet_logger = true
|
18
|
+
Bullet.console = true
|
19
|
+
Bullet.rails_logger = true
|
20
|
+
Bullet.add_footer = true
|
21
|
+
end
|
22
|
+
|
24
23
|
FILE
|
25
24
|
end
|
26
25
|
|
@@ -28,20 +27,20 @@ module Bullet
|
|
28
27
|
end
|
29
28
|
|
30
29
|
def enable_in_test
|
31
|
-
|
32
|
-
environment(nil, env: 'test') do
|
33
|
-
<<-"FILE".strip
|
34
|
-
|
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
|
-
FILE
|
41
|
-
end
|
30
|
+
return unless yes?('Would you like to enable bullet in test environment? (y/n)')
|
42
31
|
|
43
|
-
|
32
|
+
environment(nil, env: 'test') do
|
33
|
+
<<~FILE
|
34
|
+
config.after_initialize do
|
35
|
+
Bullet.enable = true
|
36
|
+
Bullet.bullet_logger = true
|
37
|
+
Bullet.raise = true # raise an error if n+1 query occurs
|
38
|
+
end
|
39
|
+
|
40
|
+
FILE
|
44
41
|
end
|
42
|
+
|
43
|
+
say 'Enabled bullet in config/environments/test.rb'
|
45
44
|
end
|
46
45
|
end
|
47
46
|
end
|
data/perf/benchmark.rb
CHANGED
@@ -29,11 +29,14 @@ class User < ActiveRecord::Base
|
|
29
29
|
end
|
30
30
|
|
31
31
|
# create database bullet_benchmark;
|
32
|
-
ActiveRecord::Base.establish_connection(
|
32
|
+
ActiveRecord::Base.establish_connection(
|
33
|
+
adapter: 'mysql2',
|
34
|
+
database: 'bullet_benchmark',
|
35
|
+
server: '/tmp/mysql.socket',
|
36
|
+
username: 'root'
|
37
|
+
)
|
33
38
|
|
34
|
-
ActiveRecord::Base.connection.tables.each
|
35
|
-
ActiveRecord::Base.connection.drop_table(table)
|
36
|
-
end
|
39
|
+
ActiveRecord::Base.connection.tables.each { |table| ActiveRecord::Base.connection.drop_table(table) }
|
37
40
|
|
38
41
|
ActiveRecord::Schema.define(version: 1) do
|
39
42
|
create_table :posts do |t|
|
@@ -54,26 +57,20 @@ ActiveRecord::Schema.define(version: 1) do
|
|
54
57
|
end
|
55
58
|
|
56
59
|
users_size = 100
|
57
|
-
posts_size =
|
60
|
+
posts_size = 1_000
|
58
61
|
comments_size = 10_000
|
59
62
|
users = []
|
60
|
-
users_size.times
|
61
|
-
users << User.new(name: "user#{i}")
|
62
|
-
end
|
63
|
+
users_size.times { |i| users << User.new(name: "user#{i}") }
|
63
64
|
User.import users
|
64
65
|
users = User.all
|
65
66
|
|
66
67
|
posts = []
|
67
|
-
posts_size.times
|
68
|
-
posts << Post.new(title: "Title #{i}", body: "Body #{i}", user: users[i % 100])
|
69
|
-
end
|
68
|
+
posts_size.times { |i| posts << Post.new(title: "Title #{i}", body: "Body #{i}", user: users[i % 100]) }
|
70
69
|
Post.import posts
|
71
70
|
posts = Post.all
|
72
71
|
|
73
72
|
comments = []
|
74
|
-
comments_size.times
|
75
|
-
comments << Comment.new(body: "Comment #{i}", post: posts[i % 1000], user: users[i % 100])
|
76
|
-
end
|
73
|
+
comments_size.times { |i| comments << Comment.new(body: "Comment #{i}", post: posts[i % 1_000], user: users[i % 100]) }
|
77
74
|
Comment.import comments
|
78
75
|
|
79
76
|
puts 'Start benchmarking...'
|
@@ -12,15 +12,15 @@ module Bullet
|
|
12
12
|
|
13
13
|
context '.add_counter_cache' do
|
14
14
|
it 'should create notification if conditions met' do
|
15
|
-
expect(CounterCache).to receive(:conditions_met?).with(@post1, [
|
16
|
-
expect(CounterCache).to receive(:create_notification).with('Post', [
|
17
|
-
CounterCache.add_counter_cache(@post1, [
|
15
|
+
expect(CounterCache).to receive(:conditions_met?).with(@post1, %i[comments]).and_return(true)
|
16
|
+
expect(CounterCache).to receive(:create_notification).with('Post', %i[comments])
|
17
|
+
CounterCache.add_counter_cache(@post1, %i[comments])
|
18
18
|
end
|
19
19
|
|
20
20
|
it 'should not create notification if conditions not met' do
|
21
|
-
expect(CounterCache).to receive(:conditions_met?).with(@post1, [
|
21
|
+
expect(CounterCache).to receive(:conditions_met?).with(@post1, %i[comments]).and_return(false)
|
22
22
|
expect(CounterCache).to receive(:create_notification).never
|
23
|
-
CounterCache.add_counter_cache(@post1, [
|
23
|
+
CounterCache.add_counter_cache(@post1, %i[comments])
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -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
|
@@ -39,7 +39,7 @@ module Bullet
|
|
39
39
|
|
40
40
|
it 'should be false if object, association pair is not existed' do
|
41
41
|
NPlusOneQuery.add_object_associations(@post, :association1)
|
42
|
-
expect(NPlusOneQuery.association?(@post, :
|
42
|
+
expect(NPlusOneQuery.association?(@post, :association2)).to eq false
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
@@ -76,8 +76,8 @@ module Bullet
|
|
76
76
|
context '.call_association' do
|
77
77
|
it 'should create notification if conditions met' do
|
78
78
|
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
|
79
|
-
expect(NPlusOneQuery).to receive(:caller_in_project).and_return([
|
80
|
-
expect(NPlusOneQuery).to receive(:create_notification).with([
|
79
|
+
expect(NPlusOneQuery).to receive(:caller_in_project).and_return(%w[caller])
|
80
|
+
expect(NPlusOneQuery).to receive(:create_notification).with(%w[caller], 'Post', :association)
|
81
81
|
NPlusOneQuery.call_association(@post, :association)
|
82
82
|
end
|
83
83
|
|
@@ -149,7 +149,11 @@ module Bullet
|
|
149
149
|
|
150
150
|
expect(NPlusOneQuery).to receive(:caller_locations).and_return([in_project, *included_gems, excluded_gem])
|
151
151
|
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
|
152
|
-
expect(NPlusOneQuery).to receive(:create_notification).with(
|
152
|
+
expect(NPlusOneQuery).to receive(:create_notification).with(
|
153
|
+
[in_project, *included_gems],
|
154
|
+
'Post',
|
155
|
+
:association
|
156
|
+
)
|
153
157
|
NPlusOneQuery.call_association(@post, :association)
|
154
158
|
end
|
155
159
|
end
|
@@ -19,7 +19,9 @@ module Bullet
|
|
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([:association]))).to eq(
|
22
|
+
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to eq(
|
23
|
+
[:association]
|
24
|
+
)
|
23
25
|
end
|
24
26
|
|
25
27
|
it 'should not get call associations if not exist in call_object_associations' do
|
@@ -30,18 +32,22 @@ module Bullet
|
|
30
32
|
|
31
33
|
context '.diff_object_associations' do
|
32
34
|
it 'should return associations not exist in call_association' do
|
33
|
-
expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to eq(
|
35
|
+
expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to eq(
|
36
|
+
[:association]
|
37
|
+
)
|
34
38
|
end
|
35
39
|
|
36
40
|
it 'should return empty if associations exist in call_association' do
|
37
41
|
UnusedEagerLoading.add_eager_loadings([@post], :association)
|
38
42
|
UnusedEagerLoading.add_call_object_associations(@post, :association)
|
39
|
-
expect(
|
43
|
+
expect(
|
44
|
+
UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))
|
45
|
+
).to be_empty
|
40
46
|
end
|
41
47
|
end
|
42
48
|
|
43
49
|
context '.check_unused_preload_associations' do
|
44
|
-
let(:paths) { [
|
50
|
+
let(:paths) { %w[/dir1 /dir1/subdir] }
|
45
51
|
it 'should create notification if object_association_diff is not empty' do
|
46
52
|
UnusedEagerLoading.add_object_associations(@post, :association)
|
47
53
|
allow(UnusedEagerLoading).to receive(:caller_in_project).and_return(paths)
|
@@ -53,7 +59,9 @@ module Bullet
|
|
53
59
|
UnusedEagerLoading.add_object_associations(@post, :association)
|
54
60
|
UnusedEagerLoading.add_eager_loadings([@post], :association)
|
55
61
|
UnusedEagerLoading.add_call_object_associations(@post, :association)
|
56
|
-
expect(
|
62
|
+
expect(
|
63
|
+
UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))
|
64
|
+
).to be_empty
|
57
65
|
expect(UnusedEagerLoading).not_to receive(:create_notification).with('Post', [:association])
|
58
66
|
UnusedEagerLoading.check_unused_preload_associations
|
59
67
|
end
|
@@ -62,14 +70,23 @@ module Bullet
|
|
62
70
|
context '.add_eager_loadings' do
|
63
71
|
it 'should add objects, associations pair when eager_loadings are empty' do
|
64
72
|
UnusedEagerLoading.add_eager_loadings([@post, @post2], :associations)
|
65
|
-
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include(
|
73
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include(
|
74
|
+
[@post.bullet_key, @post2.bullet_key],
|
75
|
+
:associations
|
76
|
+
)
|
66
77
|
end
|
67
78
|
|
68
79
|
it 'should add objects, associations pair for existing eager_loadings' do
|
69
80
|
UnusedEagerLoading.add_eager_loadings([@post, @post2], :association1)
|
70
81
|
UnusedEagerLoading.add_eager_loadings([@post, @post2], :association2)
|
71
|
-
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include(
|
72
|
-
|
82
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include(
|
83
|
+
[@post.bullet_key, @post2.bullet_key],
|
84
|
+
:association1
|
85
|
+
)
|
86
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include(
|
87
|
+
[@post.bullet_key, @post2.bullet_key],
|
88
|
+
:association2
|
89
|
+
)
|
73
90
|
end
|
74
91
|
|
75
92
|
it 'should merge objects, associations pair for existing eager_loadings' do
|
@@ -10,7 +10,7 @@ describe Object do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
if mongoid?
|
13
|
-
it 'should return class with
|
13
|
+
it 'should return class with namespace and id composition' do
|
14
14
|
post = Mongoid::Post.first
|
15
15
|
expect(post.bullet_key).to eq("Mongoid::Post:#{post.id}")
|
16
16
|
end
|
@@ -26,9 +26,7 @@ module Bullet
|
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'should leverage ENV parameter' do
|
29
|
-
temp_env_variable('USER', 'bogus')
|
30
|
-
expect(subject.whoami).to eq('user: bogus')
|
31
|
-
end
|
29
|
+
temp_env_variable('USER', 'bogus') { expect(subject.whoami).to eq('user: bogus') }
|
32
30
|
end
|
33
31
|
|
34
32
|
it 'should return blank if no user available' do
|
@@ -76,8 +74,8 @@ module Bullet
|
|
76
74
|
it 'should send full_notice to notifier' do
|
77
75
|
notifier = double
|
78
76
|
allow(subject).to receive(:notifier).and_return(notifier)
|
79
|
-
allow(subject).to receive(:notification_data).and_return(foo: :bar)
|
80
|
-
expect(notifier).to receive(:inline_notify).with(foo: :bar)
|
77
|
+
allow(subject).to receive(:notification_data).and_return({ foo: :bar })
|
78
|
+
expect(notifier).to receive(:inline_notify).with({ foo: :bar })
|
81
79
|
subject.notify_inline
|
82
80
|
end
|
83
81
|
end
|
@@ -86,8 +84,8 @@ module Bullet
|
|
86
84
|
it 'should send full_out_of_channel to notifier' do
|
87
85
|
notifier = double
|
88
86
|
allow(subject).to receive(:notifier).and_return(notifier)
|
89
|
-
allow(subject).to receive(:notification_data).and_return(foo: :bar)
|
90
|
-
expect(notifier).to receive(:out_of_channel_notify).with(foo: :bar)
|
87
|
+
allow(subject).to receive(:notification_data).and_return({ foo: :bar })
|
88
|
+
expect(notifier).to receive(:out_of_channel_notify).with({ foo: :bar })
|
91
89
|
subject.notify_out_of_channel
|
92
90
|
end
|
93
91
|
end
|
@@ -7,9 +7,22 @@ module Bullet
|
|
7
7
|
describe NPlusOneQuery do
|
8
8
|
subject { NPlusOneQuery.new([%w[caller1 caller2]], Post, %i[comments votes], 'path') }
|
9
9
|
|
10
|
-
it
|
11
|
-
|
12
|
-
|
10
|
+
it do
|
11
|
+
expect(subject.body_with_caller).to eq(
|
12
|
+
" Post => [:comments, :votes]\n Add to your query: .includes([:comments, :votes])\nCall stack\n caller1\n caller2\n"
|
13
|
+
)
|
14
|
+
end
|
15
|
+
it do
|
16
|
+
expect([subject.body_with_caller, subject.body_with_caller]).to eq(
|
17
|
+
[
|
18
|
+
" Post => [:comments, :votes]\n Add to your query: .includes([:comments, :votes])\nCall stack\n caller1\n caller2\n",
|
19
|
+
" Post => [:comments, :votes]\n Add to your query: .includes([:comments, :votes])\nCall stack\n caller1\n caller2\n"
|
20
|
+
]
|
21
|
+
)
|
22
|
+
end
|
23
|
+
it do
|
24
|
+
expect(subject.body).to eq(" Post => [:comments, :votes]\n Add to your query: .includes([:comments, :votes])")
|
25
|
+
end
|
13
26
|
it { expect(subject.title).to eq('USE eager loading in path') }
|
14
27
|
end
|
15
28
|
end
|