bullet 5.7.0 → 5.8.0
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 +16 -1
- data/Gemfile.rails-5.2 +1 -1
- data/Guardfile +1 -1
- data/README.md +9 -4
- data/Rakefile +5 -5
- data/bullet.gemspec +11 -6
- data/lib/bullet/active_record4.rb +18 -14
- data/lib/bullet/active_record41.rb +16 -14
- data/lib/bullet/active_record42.rb +24 -22
- data/lib/bullet/active_record5.rb +78 -49
- data/lib/bullet/active_record52.rb +47 -40
- data/lib/bullet/dependency.rb +3 -1
- data/lib/bullet/detector/association.rb +16 -14
- data/lib/bullet/detector/base.rb +2 -0
- data/lib/bullet/detector/counter_cache.rb +10 -7
- data/lib/bullet/detector/n_plus_one_query.rb +17 -15
- data/lib/bullet/detector/unused_eager_loading.rb +26 -21
- data/lib/bullet/detector.rb +2 -0
- data/lib/bullet/ext/object.rb +6 -4
- data/lib/bullet/ext/string.rb +3 -1
- data/lib/bullet/mongoid4x.rb +7 -2
- data/lib/bullet/mongoid5x.rb +7 -2
- data/lib/bullet/mongoid6x.rb +11 -6
- data/lib/bullet/notification/base.rb +21 -15
- data/lib/bullet/notification/counter_cache.rb +2 -0
- data/lib/bullet/notification/n_plus_one_query.rb +6 -4
- data/lib/bullet/notification/unused_eager_loading.rb +6 -4
- data/lib/bullet/notification.rb +2 -0
- data/lib/bullet/notification_collector.rb +2 -1
- data/lib/bullet/rack.rb +19 -15
- data/lib/bullet/registry/association.rb +2 -0
- data/lib/bullet/registry/base.rb +2 -0
- data/lib/bullet/registry/object.rb +2 -0
- data/lib/bullet/registry.rb +2 -0
- data/lib/bullet/stack_trace_filter.rb +56 -19
- data/lib/bullet/version.rb +2 -1
- data/lib/bullet.rb +31 -29
- data/lib/generators/bullet/install_generator.rb +5 -3
- data/perf/benchmark.rb +9 -7
- data/rails/init.rb +2 -0
- data/spec/bullet/detector/association_spec.rb +2 -0
- data/spec/bullet/detector/base_spec.rb +2 -0
- data/spec/bullet/detector/counter_cache_spec.rb +2 -0
- data/spec/bullet/detector/n_plus_one_query_spec.rb +33 -8
- data/spec/bullet/detector/unused_eager_loading_spec.rb +2 -0
- data/spec/bullet/ext/object_spec.rb +3 -1
- data/spec/bullet/ext/string_spec.rb +2 -0
- data/spec/bullet/notification/base_spec.rb +8 -6
- data/spec/bullet/notification/counter_cache_spec.rb +3 -1
- data/spec/bullet/notification/n_plus_one_query_spec.rb +3 -1
- data/spec/bullet/notification/unused_eager_loading_spec.rb +3 -1
- data/spec/bullet/notification_collector_spec.rb +2 -0
- data/spec/bullet/rack_spec.rb +10 -9
- data/spec/bullet/registry/association_spec.rb +4 -2
- data/spec/bullet/registry/base_spec.rb +5 -3
- data/spec/bullet/registry/object_spec.rb +2 -0
- data/spec/bullet_spec.rb +9 -7
- data/spec/integration/active_record/association_spec.rb +79 -17
- data/spec/integration/counter_cache_spec.rb +4 -2
- data/spec/integration/mongoid/association_spec.rb +2 -0
- data/spec/models/address.rb +2 -0
- data/spec/models/author.rb +2 -0
- data/spec/models/base_user.rb +2 -0
- data/spec/models/category.rb +2 -0
- data/spec/models/city.rb +2 -0
- data/spec/models/client.rb +4 -0
- data/spec/models/comment.rb +2 -0
- data/spec/models/company.rb +2 -0
- data/spec/models/country.rb +2 -0
- data/spec/models/document.rb +2 -0
- data/spec/models/entry.rb +2 -0
- data/spec/models/firm.rb +3 -0
- data/spec/models/folder.rb +2 -0
- data/spec/models/group.rb +4 -0
- data/spec/models/mongoid/address.rb +3 -1
- data/spec/models/mongoid/category.rb +4 -2
- data/spec/models/mongoid/comment.rb +3 -1
- data/spec/models/mongoid/company.rb +3 -1
- data/spec/models/mongoid/entry.rb +3 -1
- data/spec/models/mongoid/post.rb +6 -4
- data/spec/models/mongoid/user.rb +2 -0
- data/spec/models/newspaper.rb +2 -0
- data/spec/models/page.rb +2 -0
- data/spec/models/person.rb +2 -0
- data/spec/models/pet.rb +2 -0
- data/spec/models/post.rb +17 -0
- data/spec/models/relationship.rb +2 -0
- data/spec/models/reply.rb +2 -0
- data/spec/models/student.rb +2 -0
- data/spec/models/submission.rb +2 -0
- data/spec/models/teacher.rb +2 -0
- data/spec/models/user.rb +2 -0
- data/spec/models/writer.rb +2 -0
- data/spec/spec_helper.rb +3 -1
- data/spec/support/bullet_ext.rb +2 -0
- data/spec/support/mongo_seed.rb +32 -30
- data/spec/support/rack_double.rb +9 -16
- data/spec/support/sqlite_seed.rb +84 -75
- data/tasks/bullet_tasks.rake +2 -0
- metadata +11 -7
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
module Bullet
|
|
@@ -91,21 +93,44 @@ module Bullet
|
|
|
91
93
|
after { Bullet.stacktrace_excludes = nil }
|
|
92
94
|
|
|
93
95
|
it 'should not create notification when stacktrace contains paths that are in the exclude list' do
|
|
94
|
-
in_project = OpenStruct.new(:
|
|
95
|
-
included_path = OpenStruct.new(:
|
|
96
|
-
excluded_path = OpenStruct.new(:
|
|
96
|
+
in_project = OpenStruct.new(absolute_path: File.join(Dir.pwd, 'abc', 'abc.rb'))
|
|
97
|
+
included_path = OpenStruct.new(absolute_path: '/ghi/ghi.rb')
|
|
98
|
+
excluded_path = OpenStruct.new(absolute_path: '/def/def.rb')
|
|
97
99
|
|
|
98
100
|
expect(NPlusOneQuery).to receive(:caller_locations).and_return([in_project, included_path, excluded_path])
|
|
99
101
|
expect(NPlusOneQuery).to_not receive(:create_notification)
|
|
100
102
|
NPlusOneQuery.call_association(@post, :association)
|
|
101
103
|
end
|
|
104
|
+
|
|
105
|
+
# just a sanity spec to make sure the following spec works correctly
|
|
106
|
+
it "should create notification when stacktrace contains methods that aren't in the exclude list" do
|
|
107
|
+
method = NPlusOneQuery.method(:excluded_stacktrace_path?).source_location
|
|
108
|
+
in_project = OpenStruct.new(absolute_path: File.join(Dir.pwd, 'abc', 'abc.rb'))
|
|
109
|
+
excluded_path = OpenStruct.new(absolute_path: method.first, lineno: method.last)
|
|
110
|
+
|
|
111
|
+
expect(NPlusOneQuery).to receive(:caller_locations).at_least(1).and_return([in_project, excluded_path])
|
|
112
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).and_return(true)
|
|
113
|
+
expect(NPlusOneQuery).to receive(:create_notification)
|
|
114
|
+
NPlusOneQuery.call_association(@post, :association)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'should not create notification when stacktrace contains methods that are in the exclude list' do
|
|
118
|
+
method = NPlusOneQuery.method(:excluded_stacktrace_path?).source_location
|
|
119
|
+
Bullet.stacktrace_excludes = [method]
|
|
120
|
+
in_project = OpenStruct.new(absolute_path: File.join(Dir.pwd, 'abc', 'abc.rb'))
|
|
121
|
+
excluded_path = OpenStruct.new(absolute_path: method.first, lineno: method.last)
|
|
122
|
+
|
|
123
|
+
expect(NPlusOneQuery).to receive(:caller_locations).and_return([in_project, excluded_path])
|
|
124
|
+
expect(NPlusOneQuery).to_not receive(:create_notification)
|
|
125
|
+
NPlusOneQuery.call_association(@post, :association)
|
|
126
|
+
end
|
|
102
127
|
end
|
|
103
128
|
end
|
|
104
129
|
|
|
105
130
|
context '.caller_in_project' do
|
|
106
131
|
it 'should include only paths that are in the project' do
|
|
107
|
-
in_project = OpenStruct.new(:
|
|
108
|
-
not_in_project = OpenStruct.new(:
|
|
132
|
+
in_project = OpenStruct.new(absolute_path: File.join(Dir.pwd, 'abc', 'abc.rb'))
|
|
133
|
+
not_in_project = OpenStruct.new(absolute_path: '/def/def.rb')
|
|
109
134
|
|
|
110
135
|
expect(NPlusOneQuery).to receive(:caller_locations).and_return([in_project, not_in_project])
|
|
111
136
|
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
|
|
@@ -118,9 +143,9 @@ module Bullet
|
|
|
118
143
|
after { Bullet.stacktrace_includes = nil }
|
|
119
144
|
|
|
120
145
|
it 'should include paths that are in the stacktrace_include list' do
|
|
121
|
-
in_project = OpenStruct.new(:
|
|
122
|
-
included_gems = [OpenStruct.new(:
|
|
123
|
-
excluded_gem = OpenStruct.new(:
|
|
146
|
+
in_project = OpenStruct.new(absolute_path: File.join(Dir.pwd, 'abc', 'abc.rb'))
|
|
147
|
+
included_gems = [OpenStruct.new(absolute_path: '/def/def.rb'), OpenStruct.new(absolute_path: 'xyz/xyz.rb')]
|
|
148
|
+
excluded_gem = OpenStruct.new(absolute_path: '/ghi/ghi.rb')
|
|
124
149
|
|
|
125
150
|
expect(NPlusOneQuery).to receive(:caller_locations).and_return([in_project, *included_gems, excluded_gem])
|
|
126
151
|
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
describe Object do
|
|
@@ -30,7 +32,7 @@ describe Object do
|
|
|
30
32
|
|
|
31
33
|
it 'should return value for multiple primary keys' do
|
|
32
34
|
post = Post.first
|
|
33
|
-
allow(Post).to receive(:primary_keys).and_return([
|
|
35
|
+
allow(Post).to receive(:primary_keys).and_return(%i[category_id writer_id])
|
|
34
36
|
expect(post.primary_key_value).to eq("#{post.category_id},#{post.writer_id}")
|
|
35
37
|
end
|
|
36
38
|
end
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
module Bullet
|
|
4
6
|
module Notification
|
|
5
7
|
describe Base do
|
|
6
|
-
subject { Base.new(Post, [
|
|
8
|
+
subject { Base.new(Post, %i[comments votes]) }
|
|
7
9
|
|
|
8
10
|
context '#title' do
|
|
9
11
|
it 'should raise NoMethodError' do
|
|
@@ -66,7 +68,7 @@ module Bullet
|
|
|
66
68
|
allow(subject).to receive(:url).and_return('url')
|
|
67
69
|
allow(subject).to receive(:title).and_return('title')
|
|
68
70
|
allow(subject).to receive(:body_with_caller).and_return('body_with_caller')
|
|
69
|
-
expect(subject.notification_data).to eq(:
|
|
71
|
+
expect(subject.notification_data).to eq(user: 'whoami', url: 'url', title: 'title', body: 'body_with_caller')
|
|
70
72
|
end
|
|
71
73
|
end
|
|
72
74
|
|
|
@@ -74,8 +76,8 @@ module Bullet
|
|
|
74
76
|
it 'should send full_notice to notifier' do
|
|
75
77
|
notifier = double
|
|
76
78
|
allow(subject).to receive(:notifier).and_return(notifier)
|
|
77
|
-
allow(subject).to receive(:notification_data).and_return(:
|
|
78
|
-
expect(notifier).to receive(:inline_notify).with(:
|
|
79
|
+
allow(subject).to receive(:notification_data).and_return(foo: :bar)
|
|
80
|
+
expect(notifier).to receive(:inline_notify).with(foo: :bar)
|
|
79
81
|
subject.notify_inline
|
|
80
82
|
end
|
|
81
83
|
end
|
|
@@ -84,8 +86,8 @@ module Bullet
|
|
|
84
86
|
it 'should send full_out_of_channel to notifier' do
|
|
85
87
|
notifier = double
|
|
86
88
|
allow(subject).to receive(:notifier).and_return(notifier)
|
|
87
|
-
allow(subject).to receive(:notification_data).and_return(:
|
|
88
|
-
expect(notifier).to receive(:out_of_channel_notify).with(:
|
|
89
|
+
allow(subject).to receive(:notification_data).and_return(foo: :bar)
|
|
90
|
+
expect(notifier).to receive(:out_of_channel_notify).with(foo: :bar)
|
|
89
91
|
subject.notify_out_of_channel
|
|
90
92
|
end
|
|
91
93
|
end
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
module Bullet
|
|
4
6
|
module Notification
|
|
5
7
|
describe CounterCache do
|
|
6
|
-
subject { CounterCache.new(Post, [
|
|
8
|
+
subject { CounterCache.new(Post, %i[comments votes]) }
|
|
7
9
|
|
|
8
10
|
it { expect(subject.body).to eq(' Post => [:comments, :votes]') }
|
|
9
11
|
it { expect(subject.title).to eq('Need Counter Cache') }
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
module Bullet
|
|
4
6
|
module Notification
|
|
5
7
|
describe NPlusOneQuery do
|
|
6
|
-
subject { NPlusOneQuery.new([[
|
|
8
|
+
subject { NPlusOneQuery.new([%w[caller1 caller2]], Post, %i[comments votes], 'path') }
|
|
7
9
|
|
|
8
10
|
it { expect(subject.body_with_caller).to eq(" Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nCall stack\n caller1\n caller2\n") }
|
|
9
11
|
it { expect([subject.body_with_caller, subject.body_with_caller]).to eq([" Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nCall stack\n caller1\n caller2\n", " Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nCall stack\n caller1\n caller2\n"]) }
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
module Bullet
|
|
4
6
|
module Notification
|
|
5
7
|
describe UnusedEagerLoading do
|
|
6
|
-
subject { UnusedEagerLoading.new([''], Post, [
|
|
8
|
+
subject { UnusedEagerLoading.new([''], Post, %i[comments votes], 'path') }
|
|
7
9
|
|
|
8
10
|
it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Remove from your finder: :includes => [:comments, :votes]") }
|
|
9
11
|
it { expect(subject.title).to eq('AVOID eager loading in path') }
|
data/spec/bullet/rack_spec.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
|
|
2
3
|
require 'spec_helper'
|
|
3
4
|
|
|
@@ -9,38 +10,38 @@ module Bullet
|
|
|
9
10
|
context '#html_request?' do
|
|
10
11
|
it 'should be true if Content-Type is text/html and http body contains html tag' do
|
|
11
12
|
headers = { 'Content-Type' => 'text/html' }
|
|
12
|
-
response = double(:
|
|
13
|
+
response = double(body: '<html><head></head><body></body></html>')
|
|
13
14
|
expect(middleware).to be_html_request(headers, response)
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
it 'should be true if Content-Type is text/html and http body contains html tag with attributes' do
|
|
17
18
|
headers = { 'Content-Type' => 'text/html' }
|
|
18
|
-
response = double(:
|
|
19
|
+
response = double(body: "<html attr='hello'><head></head><body></body></html>")
|
|
19
20
|
expect(middleware).to be_html_request(headers, response)
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
it 'should be false if there is no Content-Type header' do
|
|
23
24
|
headers = {}
|
|
24
|
-
response = double(:
|
|
25
|
+
response = double(body: '<html><head></head><body></body></html>')
|
|
25
26
|
expect(middleware).not_to be_html_request(headers, response)
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
it 'should be false if Content-Type is javascript' do
|
|
29
30
|
headers = { 'Content-Type' => 'text/javascript' }
|
|
30
|
-
response = double(:
|
|
31
|
+
response = double(body: '<html><head></head><body></body></html>')
|
|
31
32
|
expect(middleware).not_to be_html_request(headers, response)
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
it "should be false if response body doesn't contain html tag" do
|
|
35
36
|
headers = { 'Content-Type' => 'text/html' }
|
|
36
|
-
response = double(:
|
|
37
|
+
response = double(body: '<div>Partial</div>')
|
|
37
38
|
expect(middleware).not_to be_html_request(headers, response)
|
|
38
39
|
end
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
context 'empty?' do
|
|
42
43
|
it 'should be false if response is a string and not empty' do
|
|
43
|
-
response = double(:
|
|
44
|
+
response = double(body: '<html><head></head><body></body></html>')
|
|
44
45
|
expect(middleware).not_to be_empty(response)
|
|
45
46
|
end
|
|
46
47
|
|
|
@@ -50,7 +51,7 @@ module Bullet
|
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
it 'should be true if response body is empty' do
|
|
53
|
-
response = double(:
|
|
54
|
+
response = double(body: '')
|
|
54
55
|
expect(middleware).to be_empty(response)
|
|
55
56
|
end
|
|
56
57
|
end
|
|
@@ -68,7 +69,7 @@ module Bullet
|
|
|
68
69
|
expect(Bullet).to receive(:notification?).and_return(true)
|
|
69
70
|
expect(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
|
|
70
71
|
expect(Bullet).to receive(:perform_out_of_channel_notifications)
|
|
71
|
-
status, headers, response = middleware.call(
|
|
72
|
+
status, headers, response = middleware.call('Content-Type' => 'text/html')
|
|
72
73
|
expect(headers['Content-Length']).to eq('56')
|
|
73
74
|
expect(response).to eq(['<html><head></head><body><bullet></bullet></body></html>'])
|
|
74
75
|
end
|
|
@@ -79,7 +80,7 @@ module Bullet
|
|
|
79
80
|
app.response = response
|
|
80
81
|
expect(Bullet).to receive(:notification?).and_return(true)
|
|
81
82
|
expect(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
|
|
82
|
-
status, headers, response = middleware.call(
|
|
83
|
+
status, headers, response = middleware.call('Content-Type' => 'text/html')
|
|
83
84
|
expect(headers['Content-Length']).to eq('58')
|
|
84
85
|
end
|
|
85
86
|
end
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
module Bullet
|
|
4
6
|
module Registry
|
|
5
7
|
describe Association do
|
|
6
|
-
subject { Association.new.tap { |association| association.add([
|
|
8
|
+
subject { Association.new.tap { |association| association.add(%w[key1 key2], 'value') } }
|
|
7
9
|
|
|
8
10
|
context '#merge' do
|
|
9
11
|
it 'should merge key/value' do
|
|
@@ -14,7 +16,7 @@ module Bullet
|
|
|
14
16
|
|
|
15
17
|
context '#similarly_associated' do
|
|
16
18
|
it 'should return similarly associated keys' do
|
|
17
|
-
expect(subject.similarly_associated('key1', Set.new(['value']))).to eq([
|
|
19
|
+
expect(subject.similarly_associated('key1', Set.new(['value']))).to eq(%w[key1 key2])
|
|
18
20
|
end
|
|
19
21
|
|
|
20
22
|
it 'should return empty if key does not exist' do
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
module Bullet
|
|
@@ -21,12 +23,12 @@ module Bullet
|
|
|
21
23
|
context '#add' do
|
|
22
24
|
it 'should add value with string' do
|
|
23
25
|
subject.add('key', 'new_value')
|
|
24
|
-
expect(subject['key']).to eq(Set.new([
|
|
26
|
+
expect(subject['key']).to eq(Set.new(%w[value new_value]))
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
it 'should add value with array' do
|
|
28
|
-
subject.add('key', [
|
|
29
|
-
expect(subject['key']).to eq(Set.new([
|
|
30
|
+
subject.add('key', %w[value1 value2])
|
|
31
|
+
expect(subject['key']).to eq(Set.new(%w[value value1 value2]))
|
|
30
32
|
end
|
|
31
33
|
end
|
|
32
34
|
|
data/spec/bullet_spec.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
describe Bullet, focused: true do
|
|
@@ -88,7 +90,7 @@ describe Bullet, focused: true do
|
|
|
88
90
|
describe '#add_whitelist' do
|
|
89
91
|
context "for 'special' class names" do
|
|
90
92
|
it 'is added to the whitelist successfully' do
|
|
91
|
-
Bullet.add_whitelist(:
|
|
93
|
+
Bullet.add_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
|
|
92
94
|
expect(Bullet.get_whitelist_associations(:n_plus_one_query, 'Klass')).to include :department
|
|
93
95
|
end
|
|
94
96
|
end
|
|
@@ -97,17 +99,17 @@ describe Bullet, focused: true do
|
|
|
97
99
|
describe '#delete_whitelist' do
|
|
98
100
|
context "for 'special' class names" do
|
|
99
101
|
it 'is deleted from the whitelist successfully' do
|
|
100
|
-
Bullet.add_whitelist(:
|
|
101
|
-
Bullet.delete_whitelist(:
|
|
102
|
+
Bullet.add_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
|
|
103
|
+
Bullet.delete_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
|
|
102
104
|
expect(Bullet.whitelist[:n_plus_one_query]).to eq({})
|
|
103
105
|
end
|
|
104
106
|
end
|
|
105
107
|
|
|
106
108
|
context 'when exists multiple definitions' do
|
|
107
109
|
it 'is deleted from the whitelist successfully' do
|
|
108
|
-
Bullet.add_whitelist(:
|
|
109
|
-
Bullet.add_whitelist(:
|
|
110
|
-
Bullet.delete_whitelist(:
|
|
110
|
+
Bullet.add_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
|
|
111
|
+
Bullet.add_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
|
|
112
|
+
Bullet.delete_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
|
|
111
113
|
expect(Bullet.get_whitelist_associations(:n_plus_one_query, 'Klass')).to include :department
|
|
112
114
|
expect(Bullet.get_whitelist_associations(:n_plus_one_query, 'Klass')).to_not include :team
|
|
113
115
|
end
|
|
@@ -127,7 +129,7 @@ describe Bullet, focused: true do
|
|
|
127
129
|
{
|
|
128
130
|
'REQUEST_METHOD' => 'GET',
|
|
129
131
|
'PATH_INFO' => '/path',
|
|
130
|
-
'QUERY_STRING' => 'foo=bar'
|
|
132
|
+
'QUERY_STRING' => 'foo=bar'
|
|
131
133
|
}
|
|
132
134
|
}
|
|
133
135
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
if active_record?
|
|
@@ -122,7 +124,7 @@ if active_record?
|
|
|
122
124
|
end
|
|
123
125
|
|
|
124
126
|
it 'should detect preload with category => posts => comments' do
|
|
125
|
-
Category.includes(
|
|
127
|
+
Category.includes(posts: :comments).each do |category|
|
|
126
128
|
category.posts.each do |post|
|
|
127
129
|
post.comments.map(&:name)
|
|
128
130
|
end
|
|
@@ -134,7 +136,7 @@ if active_record?
|
|
|
134
136
|
end
|
|
135
137
|
|
|
136
138
|
it 'should detect preload with category => posts => comments with posts.id > 0' do
|
|
137
|
-
Category.includes(
|
|
139
|
+
Category.includes(posts: :comments).where('posts.id > 0').references(:posts).each do |category|
|
|
138
140
|
category.posts.each do |post|
|
|
139
141
|
post.comments.map(&:name)
|
|
140
142
|
end
|
|
@@ -146,7 +148,7 @@ if active_record?
|
|
|
146
148
|
end
|
|
147
149
|
|
|
148
150
|
it 'should detect unused preload with category => posts => comments' do
|
|
149
|
-
Category.includes(
|
|
151
|
+
Category.includes(posts: :comments).map(&:name)
|
|
150
152
|
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
|
151
153
|
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Post, :comments)
|
|
152
154
|
|
|
@@ -154,7 +156,7 @@ if active_record?
|
|
|
154
156
|
end
|
|
155
157
|
|
|
156
158
|
it 'should detect unused preload with post => commnets, no category => posts' do
|
|
157
|
-
Category.includes(
|
|
159
|
+
Category.includes(posts: :comments).each do |category|
|
|
158
160
|
category.posts.map(&:name)
|
|
159
161
|
end
|
|
160
162
|
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
|
@@ -190,7 +192,7 @@ if active_record?
|
|
|
190
192
|
end
|
|
191
193
|
|
|
192
194
|
it 'should detect preload with category => [posts, entries]' do
|
|
193
|
-
Category.includes([
|
|
195
|
+
Category.includes(%i[posts entries]).each do |category|
|
|
194
196
|
category.posts.map(&:name)
|
|
195
197
|
category.entries.map(&:name)
|
|
196
198
|
end
|
|
@@ -201,7 +203,7 @@ if active_record?
|
|
|
201
203
|
end
|
|
202
204
|
|
|
203
205
|
it 'should detect unused preload with category => [posts, entries]' do
|
|
204
|
-
Category.includes([
|
|
206
|
+
Category.includes(%i[posts entries]).map(&:name)
|
|
205
207
|
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
|
206
208
|
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Category, :posts)
|
|
207
209
|
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Category, :entries)
|
|
@@ -210,7 +212,7 @@ if active_record?
|
|
|
210
212
|
end
|
|
211
213
|
|
|
212
214
|
it 'should detect unused preload with category => entries, but not with category => posts' do
|
|
213
|
-
Category.includes([
|
|
215
|
+
Category.includes(%i[posts entries]).each do |category|
|
|
214
216
|
category.posts.map(&:name)
|
|
215
217
|
end
|
|
216
218
|
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
|
@@ -224,7 +226,7 @@ if active_record?
|
|
|
224
226
|
context 'post => comment' do
|
|
225
227
|
it 'should detect unused preload with post => comments' do
|
|
226
228
|
Post.includes(:comments).each do |post|
|
|
227
|
-
post.comments.first
|
|
229
|
+
post.comments.first&.name
|
|
228
230
|
end
|
|
229
231
|
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
|
230
232
|
expect(Bullet::Detector::Association).not_to be_unused_preload_associations_for(Post, :comments)
|
|
@@ -257,7 +259,7 @@ if active_record?
|
|
|
257
259
|
|
|
258
260
|
context 'category => posts => writer' do
|
|
259
261
|
it 'should not detect unused preload associations' do
|
|
260
|
-
category = Category.includes(
|
|
262
|
+
category = Category.includes(posts: :writer).order('id DESC').find_by_name('first')
|
|
261
263
|
category.posts.map do |post|
|
|
262
264
|
post.name
|
|
263
265
|
post.writer.name
|
|
@@ -354,6 +356,28 @@ if active_record?
|
|
|
354
356
|
|
|
355
357
|
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
|
356
358
|
end
|
|
359
|
+
|
|
360
|
+
it 'should not detect newly assigned object in an after_save' do
|
|
361
|
+
new_post = Post.new(category: Category.first)
|
|
362
|
+
|
|
363
|
+
new_post.trigger_after_save = true
|
|
364
|
+
new_post.save!
|
|
365
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
|
366
|
+
|
|
367
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
it 'should not detect "manual" preload' do
|
|
371
|
+
comment = Comment.all.to_a.first
|
|
372
|
+
post = Post.find(comment.post_id)
|
|
373
|
+
# "manually" preload with out-of-band data
|
|
374
|
+
comment.post = post
|
|
375
|
+
# loading it should not trigger anything
|
|
376
|
+
comment.post
|
|
377
|
+
|
|
378
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
|
379
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
|
380
|
+
end
|
|
357
381
|
end
|
|
358
382
|
|
|
359
383
|
context 'comment => post => category' do
|
|
@@ -387,7 +411,7 @@ if active_record?
|
|
|
387
411
|
end
|
|
388
412
|
|
|
389
413
|
it 'should not detect unpreload association' do
|
|
390
|
-
Comment.includes(:
|
|
414
|
+
Comment.includes(post: :category).each do |comment|
|
|
391
415
|
comment.post.category.name
|
|
392
416
|
end
|
|
393
417
|
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
|
@@ -399,7 +423,7 @@ if active_record?
|
|
|
399
423
|
|
|
400
424
|
context 'comment => author, post => writer' do
|
|
401
425
|
it 'should detect non preloaded writer' do
|
|
402
|
-
Comment.includes([
|
|
426
|
+
Comment.includes(%i[author post]).where(['base_users.id = ?', BaseUser.first]).references(:base_users).each do |comment|
|
|
403
427
|
comment.post.writer.name
|
|
404
428
|
end
|
|
405
429
|
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
|
@@ -409,7 +433,7 @@ if active_record?
|
|
|
409
433
|
end
|
|
410
434
|
|
|
411
435
|
it 'should detect unused preload with comment => author' do
|
|
412
|
-
Comment.includes([:author, { :
|
|
436
|
+
Comment.includes([:author, { post: :writer }]).where(['base_users.id = ?', BaseUser.first]).references(:base_users).each do |comment|
|
|
413
437
|
comment.post.writer.name
|
|
414
438
|
end
|
|
415
439
|
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
|
@@ -419,7 +443,7 @@ if active_record?
|
|
|
419
443
|
end
|
|
420
444
|
|
|
421
445
|
it 'should detect non preloading with writer => newspaper' do
|
|
422
|
-
Comment.includes(:
|
|
446
|
+
Comment.includes(post: :writer).where("posts.name like '%first%'").references(:posts).each do |comment|
|
|
423
447
|
comment.post.writer.newspaper.name
|
|
424
448
|
end
|
|
425
449
|
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
|
@@ -430,10 +454,10 @@ if active_record?
|
|
|
430
454
|
|
|
431
455
|
it 'should not raise a stack error from posts to category' do
|
|
432
456
|
expect {
|
|
433
|
-
Comment.includes(
|
|
457
|
+
Comment.includes(post: :category).each do |com|
|
|
434
458
|
com.post.category
|
|
435
459
|
end
|
|
436
|
-
}.not_to raise_error
|
|
460
|
+
}.not_to raise_error
|
|
437
461
|
end
|
|
438
462
|
end
|
|
439
463
|
end
|
|
@@ -526,6 +550,44 @@ if active_record?
|
|
|
526
550
|
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
|
527
551
|
end
|
|
528
552
|
end
|
|
553
|
+
|
|
554
|
+
context 'firm => clients => groups' do
|
|
555
|
+
it 'should detect non preload associations' do
|
|
556
|
+
Firm.all.each do |firm|
|
|
557
|
+
firm.groups.map(&:name)
|
|
558
|
+
end
|
|
559
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
|
560
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
|
561
|
+
|
|
562
|
+
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Firm, :groups)
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
it 'should detect preload associations' do
|
|
566
|
+
Firm.includes(:groups).each do |firm|
|
|
567
|
+
firm.groups.map(&:name)
|
|
568
|
+
end
|
|
569
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
|
570
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
|
571
|
+
|
|
572
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
it 'should not detect preload associations' do
|
|
576
|
+
Firm.all.map(&:name)
|
|
577
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
|
578
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
|
579
|
+
|
|
580
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
it 'should detect unused preload associations' do
|
|
584
|
+
Firm.includes(:groups).map(&:name)
|
|
585
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
|
586
|
+
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Firm, :groups)
|
|
587
|
+
|
|
588
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
|
589
|
+
end
|
|
590
|
+
end
|
|
529
591
|
end
|
|
530
592
|
|
|
531
593
|
describe Bullet::Detector::Association, 'has_one' do
|
|
@@ -720,7 +782,7 @@ if active_record?
|
|
|
720
782
|
end
|
|
721
783
|
|
|
722
784
|
context 'whitelist n plus one query' do
|
|
723
|
-
before { Bullet.add_whitelist :
|
|
785
|
+
before { Bullet.add_whitelist type: :n_plus_one_query, class_name: 'Post', association: :comments }
|
|
724
786
|
after { Bullet.clear_whitelist }
|
|
725
787
|
|
|
726
788
|
it 'should not detect n plus one query' do
|
|
@@ -743,7 +805,7 @@ if active_record?
|
|
|
743
805
|
end
|
|
744
806
|
|
|
745
807
|
context 'whitelist unused eager loading' do
|
|
746
|
-
before { Bullet.add_whitelist :
|
|
808
|
+
before { Bullet.add_whitelist type: :unused_eager_loading, class_name: 'Post', association: :comments }
|
|
747
809
|
after { Bullet.clear_whitelist }
|
|
748
810
|
|
|
749
811
|
it 'should not detect unused eager loading' do
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
if !mongoid? && active_record?
|
|
@@ -54,7 +56,7 @@ if !mongoid? && active_record?
|
|
|
54
56
|
|
|
55
57
|
it 'should not need counter cache with part of cities' do
|
|
56
58
|
Country.all.each do |country|
|
|
57
|
-
country.cities.where(:
|
|
59
|
+
country.cities.where(name: 'first').size
|
|
58
60
|
end
|
|
59
61
|
expect(Bullet.collected_counter_cache_notifications).to be_empty
|
|
60
62
|
end
|
|
@@ -72,7 +74,7 @@ if !mongoid? && active_record?
|
|
|
72
74
|
end
|
|
73
75
|
|
|
74
76
|
context 'whitelist' do
|
|
75
|
-
before { Bullet.add_whitelist :
|
|
77
|
+
before { Bullet.add_whitelist type: :counter_cache, class_name: 'Country', association: :cities }
|
|
76
78
|
after { Bullet.clear_whitelist }
|
|
77
79
|
|
|
78
80
|
it 'should not detect counter cache' do
|
data/spec/models/address.rb
CHANGED
data/spec/models/author.rb
CHANGED
data/spec/models/base_user.rb
CHANGED