bullet 5.5.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/.travis.yml +1 -5
- data/CHANGELOG.md +31 -1
- data/Gemfile +6 -1
- data/Gemfile.rails-5.0 +1 -1
- data/Gemfile.rails-5.1 +15 -0
- data/Gemfile.rails-5.2 +15 -0
- data/Guardfile +2 -2
- data/Hacking.md +1 -1
- data/README.md +20 -23
- data/Rakefile +20 -21
- data/bullet.gemspec +20 -15
- 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 +102 -91
- data/lib/bullet/active_record52.rb +232 -0
- data/lib/bullet/dependency.rb +9 -1
- data/lib/bullet/detector/association.rb +17 -14
- data/lib/bullet/detector/base.rb +2 -0
- data/lib/bullet/detector/counter_cache.rb +14 -10
- data/lib/bullet/detector/n_plus_one_query.rb +21 -17
- data/lib/bullet/detector/unused_eager_loading.rb +30 -24
- 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 +26 -19
- data/lib/bullet/notification/counter_cache.rb +3 -1
- data/lib/bullet/notification/n_plus_one_query.rb +7 -4
- data/lib/bullet/notification/unused_eager_loading.rb +7 -4
- data/lib/bullet/notification.rb +2 -0
- data/lib/bullet/notification_collector.rb +2 -1
- data/lib/bullet/rack.rb +22 -18
- 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 -18
- data/lib/bullet/version.rb +3 -2
- data/lib/bullet.rb +43 -33
- data/lib/generators/bullet/install_generator.rb +48 -0
- data/perf/benchmark.rb +12 -12
- data/rails/init.rb +2 -0
- data/spec/bullet/detector/association_spec.rb +6 -4
- data/spec/bullet/detector/base_spec.rb +2 -0
- data/spec/bullet/detector/counter_cache_spec.rb +13 -11
- data/spec/bullet/detector/n_plus_one_query_spec.rb +71 -46
- data/spec/bullet/detector/unused_eager_loading_spec.rb +21 -19
- data/spec/bullet/ext/object_spec.rb +10 -8
- data/spec/bullet/ext/string_spec.rb +7 -5
- data/spec/bullet/notification/base_spec.rb +39 -38
- data/spec/bullet/notification/counter_cache_spec.rb +5 -3
- data/spec/bullet/notification/n_plus_one_query_spec.rb +5 -3
- data/spec/bullet/notification/unused_eager_loading_spec.rb +4 -2
- data/spec/bullet/notification_collector_spec.rb +12 -10
- data/spec/bullet/rack_spec.rb +48 -47
- data/spec/bullet/registry/association_spec.rb +12 -10
- data/spec/bullet/registry/base_spec.rb +22 -20
- data/spec/bullet/registry/object_spec.rb +6 -4
- data/spec/bullet_spec.rb +24 -3
- data/spec/integration/{active_record4 → active_record}/association_spec.rb +176 -114
- data/spec/integration/counter_cache_spec.rb +15 -13
- data/spec/integration/mongoid/association_spec.rb +35 -33
- 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 +3 -1
- data/spec/models/company.rb +2 -0
- data/spec/models/country.rb +2 -0
- data/spec/models/document.rb +4 -2
- 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 +3 -1
- 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 +15 -13
- data/spec/support/bullet_ext.rb +2 -0
- data/spec/support/mongo_seed.rb +36 -42
- data/spec/support/rack_double.rb +13 -19
- data/spec/support/sqlite_seed.rb +84 -76
- data/tasks/bullet_tasks.rake +4 -2
- data/test.sh +2 -0
- data/update.sh +2 -0
- metadata +17 -11
- data/spec/integration/active_record5/association_spec.rb +0 -768
data/spec/bullet/rack_spec.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require 'spec_helper'
|
|
3
4
|
|
|
4
5
|
module Bullet
|
|
@@ -6,120 +7,120 @@ module Bullet
|
|
|
6
7
|
let(:middleware) { Bullet::Rack.new app }
|
|
7
8
|
let(:app) { Support::AppDouble.new }
|
|
8
9
|
|
|
9
|
-
context
|
|
10
|
-
it
|
|
11
|
-
headers = {
|
|
12
|
-
response = double(:
|
|
10
|
+
context '#html_request?' do
|
|
11
|
+
it 'should be true if Content-Type is text/html and http body contains html tag' do
|
|
12
|
+
headers = { 'Content-Type' => 'text/html' }
|
|
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
|
-
it
|
|
17
|
-
headers = {
|
|
18
|
-
response = double(:
|
|
17
|
+
it 'should be true if Content-Type is text/html and http body contains html tag with attributes' do
|
|
18
|
+
headers = { 'Content-Type' => 'text/html' }
|
|
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
|
-
it
|
|
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
|
-
it
|
|
29
|
-
headers = {
|
|
30
|
-
response = double(:
|
|
29
|
+
it 'should be false if Content-Type is javascript' do
|
|
30
|
+
headers = { 'Content-Type' => 'text/javascript' }
|
|
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
|
-
headers = {
|
|
36
|
-
response = double(:
|
|
36
|
+
headers = { 'Content-Type' => 'text/html' }
|
|
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
|
-
context
|
|
42
|
-
it
|
|
43
|
-
response = double(:
|
|
42
|
+
context 'empty?' do
|
|
43
|
+
it 'should be false if response is a string and not empty' do
|
|
44
|
+
response = double(body: '<html><head></head><body></body></html>')
|
|
44
45
|
expect(middleware).not_to be_empty(response)
|
|
45
46
|
end
|
|
46
47
|
|
|
47
|
-
it
|
|
48
|
-
response = [
|
|
48
|
+
it 'should be true if response is not found' do
|
|
49
|
+
response = ['Not Found']
|
|
49
50
|
expect(middleware).to be_empty(response)
|
|
50
51
|
end
|
|
51
52
|
|
|
52
|
-
it
|
|
53
|
-
response = double(:
|
|
53
|
+
it 'should be true if response body is empty' do
|
|
54
|
+
response = double(body: '')
|
|
54
55
|
expect(middleware).to be_empty(response)
|
|
55
56
|
end
|
|
56
57
|
end
|
|
57
58
|
|
|
58
|
-
context
|
|
59
|
-
context
|
|
60
|
-
it
|
|
61
|
-
expected_response = Support::ResponseDouble.new
|
|
59
|
+
context '#call' do
|
|
60
|
+
context 'when Bullet is enabled' do
|
|
61
|
+
it 'should return original response body' do
|
|
62
|
+
expected_response = Support::ResponseDouble.new 'Actual body'
|
|
62
63
|
app.response = expected_response
|
|
63
64
|
_, _, response = middleware.call({})
|
|
64
65
|
expect(response).to eq(expected_response)
|
|
65
66
|
end
|
|
66
67
|
|
|
67
|
-
it
|
|
68
|
+
it 'should change response body if notification is active' do
|
|
68
69
|
expect(Bullet).to receive(:notification?).and_return(true)
|
|
69
|
-
expect(Bullet).to receive(:gather_inline_notifications).and_return(
|
|
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
|
-
expect(headers[
|
|
73
|
-
expect(response).to eq([
|
|
72
|
+
status, headers, response = middleware.call('Content-Type' => 'text/html')
|
|
73
|
+
expect(headers['Content-Length']).to eq('56')
|
|
74
|
+
expect(response).to eq(['<html><head></head><body><bullet></bullet></body></html>'])
|
|
74
75
|
end
|
|
75
76
|
|
|
76
|
-
it
|
|
77
|
+
it 'should set the right Content-Length if response body contains accents' do
|
|
77
78
|
response = Support::ResponseDouble.new
|
|
78
|
-
response.body =
|
|
79
|
+
response.body = '<html><head></head><body>é</body></html>'
|
|
79
80
|
app.response = response
|
|
80
81
|
expect(Bullet).to receive(:notification?).and_return(true)
|
|
81
|
-
expect(Bullet).to receive(:gather_inline_notifications).and_return(
|
|
82
|
-
status, headers, response = middleware.call(
|
|
83
|
-
expect(headers[
|
|
82
|
+
expect(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
|
|
83
|
+
status, headers, response = middleware.call('Content-Type' => 'text/html')
|
|
84
|
+
expect(headers['Content-Length']).to eq('58')
|
|
84
85
|
end
|
|
85
86
|
end
|
|
86
87
|
|
|
87
|
-
context
|
|
88
|
+
context 'when Bullet is disabled' do
|
|
88
89
|
before(:each) { allow(Bullet).to receive(:enable?).and_return(false) }
|
|
89
90
|
|
|
90
|
-
it
|
|
91
|
+
it 'should not call Bullet.start_request' do
|
|
91
92
|
expect(Bullet).not_to receive(:start_request)
|
|
92
93
|
middleware.call({})
|
|
93
94
|
end
|
|
94
95
|
end
|
|
95
96
|
end
|
|
96
97
|
|
|
97
|
-
describe
|
|
98
|
+
describe '#response_body' do
|
|
98
99
|
let(:response) { double }
|
|
99
|
-
let(:body_string) {
|
|
100
|
+
let(:body_string) { '<html><body>My Body</body></html>' }
|
|
100
101
|
|
|
101
|
-
context
|
|
102
|
+
context 'when `response` responds to `body`' do
|
|
102
103
|
before { allow(response).to receive(:body).and_return(body) }
|
|
103
104
|
|
|
104
|
-
context
|
|
105
|
+
context 'when `body` returns an Array' do
|
|
105
106
|
let(:body) { [body_string, 'random string'] }
|
|
106
|
-
it
|
|
107
|
+
it 'should return the plain body string' do
|
|
107
108
|
expect(middleware.response_body(response)).to eq body_string
|
|
108
109
|
end
|
|
109
110
|
end
|
|
110
111
|
|
|
111
|
-
context
|
|
112
|
+
context 'when `body` does not return an Array' do
|
|
112
113
|
let(:body) { body_string }
|
|
113
|
-
it
|
|
114
|
+
it 'should return the plain body string' do
|
|
114
115
|
expect(middleware.response_body(response)).to eq body_string
|
|
115
116
|
end
|
|
116
117
|
end
|
|
117
118
|
end
|
|
118
119
|
|
|
119
|
-
context
|
|
120
|
+
context 'when `response` does not respond to `body`' do
|
|
120
121
|
before { allow(response).to receive(:first).and_return(body_string) }
|
|
121
122
|
|
|
122
|
-
it
|
|
123
|
+
it 'should return the plain body string' do
|
|
123
124
|
expect(middleware.response_body(response)).to eq body_string
|
|
124
125
|
end
|
|
125
126
|
end
|
|
@@ -1,24 +1,26 @@
|
|
|
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
|
-
context
|
|
9
|
-
it
|
|
10
|
-
subject.merge(
|
|
11
|
-
expect(subject[
|
|
10
|
+
context '#merge' do
|
|
11
|
+
it 'should merge key/value' do
|
|
12
|
+
subject.merge('key0', 'value0')
|
|
13
|
+
expect(subject['key0']).to be_include('value0')
|
|
12
14
|
end
|
|
13
15
|
end
|
|
14
16
|
|
|
15
|
-
context
|
|
16
|
-
it
|
|
17
|
-
expect(subject.similarly_associated(
|
|
17
|
+
context '#similarly_associated' do
|
|
18
|
+
it 'should return similarly associated keys' do
|
|
19
|
+
expect(subject.similarly_associated('key1', Set.new(['value']))).to eq(%w[key1 key2])
|
|
18
20
|
end
|
|
19
21
|
|
|
20
|
-
it
|
|
21
|
-
expect(subject.similarly_associated(
|
|
22
|
+
it 'should return empty if key does not exist' do
|
|
23
|
+
expect(subject.similarly_associated('key3', Set.new(['value']))).to be_empty
|
|
22
24
|
end
|
|
23
25
|
end
|
|
24
26
|
end
|
|
@@ -1,42 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
module Bullet
|
|
4
6
|
module Registry
|
|
5
7
|
describe Base do
|
|
6
|
-
subject { Base.new.tap { |base| base.add(
|
|
8
|
+
subject { Base.new.tap { |base| base.add('key', 'value') } }
|
|
7
9
|
|
|
8
|
-
context
|
|
9
|
-
it
|
|
10
|
-
expect(subject[
|
|
10
|
+
context '#[]' do
|
|
11
|
+
it 'should get value by key' do
|
|
12
|
+
expect(subject['key']).to eq(Set.new(['value']))
|
|
11
13
|
end
|
|
12
14
|
end
|
|
13
15
|
|
|
14
|
-
context
|
|
15
|
-
it
|
|
16
|
-
subject.delete(
|
|
17
|
-
expect(subject[
|
|
16
|
+
context '#delete' do
|
|
17
|
+
it 'should delete key' do
|
|
18
|
+
subject.delete('key')
|
|
19
|
+
expect(subject['key']).to be_nil
|
|
18
20
|
end
|
|
19
21
|
end
|
|
20
22
|
|
|
21
|
-
context
|
|
22
|
-
it
|
|
23
|
-
subject.add(
|
|
24
|
-
expect(subject[
|
|
23
|
+
context '#add' do
|
|
24
|
+
it 'should add value with string' do
|
|
25
|
+
subject.add('key', 'new_value')
|
|
26
|
+
expect(subject['key']).to eq(Set.new(%w[value new_value]))
|
|
25
27
|
end
|
|
26
28
|
|
|
27
|
-
it
|
|
28
|
-
subject.add(
|
|
29
|
-
expect(subject[
|
|
29
|
+
it 'should add value with array' do
|
|
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
|
|
|
33
|
-
context
|
|
34
|
-
it
|
|
35
|
-
expect(subject.include?(
|
|
35
|
+
context '#include?' do
|
|
36
|
+
it 'should include key/value' do
|
|
37
|
+
expect(subject.include?('key', 'value')).to eq true
|
|
36
38
|
end
|
|
37
39
|
|
|
38
|
-
it
|
|
39
|
-
expect(subject.include?(
|
|
40
|
+
it 'should not include wrong key/value' do
|
|
41
|
+
expect(subject.include?('key', 'val')).to eq false
|
|
40
42
|
end
|
|
41
43
|
end
|
|
42
44
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
module Bullet
|
|
@@ -7,14 +9,14 @@ module Bullet
|
|
|
7
9
|
let(:another_post) { Post.last }
|
|
8
10
|
subject { Object.new.tap { |object| object.add(post.bullet_key) } }
|
|
9
11
|
|
|
10
|
-
context
|
|
11
|
-
it
|
|
12
|
+
context '#include?' do
|
|
13
|
+
it 'should include the object' do
|
|
12
14
|
expect(subject).to be_include(post.bullet_key)
|
|
13
15
|
end
|
|
14
16
|
end
|
|
15
17
|
|
|
16
|
-
context
|
|
17
|
-
it
|
|
18
|
+
context '#add' do
|
|
19
|
+
it 'should add an object' do
|
|
18
20
|
subject.add(another_post.bullet_key)
|
|
19
21
|
expect(subject).to be_include(another_post.bullet_key)
|
|
20
22
|
end
|
data/spec/bullet_spec.rb
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
describe Bullet, focused: true do
|
|
4
6
|
subject { Bullet }
|
|
5
7
|
|
|
6
8
|
describe '#enable' do
|
|
7
|
-
|
|
8
9
|
context 'enable Bullet' do
|
|
9
10
|
before do
|
|
10
11
|
# Bullet.enable
|
|
@@ -89,8 +90,28 @@ describe Bullet, focused: true do
|
|
|
89
90
|
describe '#add_whitelist' do
|
|
90
91
|
context "for 'special' class names" do
|
|
91
92
|
it 'is added to the whitelist successfully' do
|
|
92
|
-
Bullet.add_whitelist(:
|
|
93
|
+
Bullet.add_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
|
|
94
|
+
expect(Bullet.get_whitelist_associations(:n_plus_one_query, 'Klass')).to include :department
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
describe '#delete_whitelist' do
|
|
100
|
+
context "for 'special' class names" do
|
|
101
|
+
it 'is deleted from the whitelist successfully' do
|
|
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)
|
|
104
|
+
expect(Bullet.whitelist[:n_plus_one_query]).to eq({})
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
context 'when exists multiple definitions' do
|
|
109
|
+
it 'is deleted from the whitelist successfully' do
|
|
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)
|
|
93
113
|
expect(Bullet.get_whitelist_associations(:n_plus_one_query, 'Klass')).to include :department
|
|
114
|
+
expect(Bullet.get_whitelist_associations(:n_plus_one_query, 'Klass')).to_not include :team
|
|
94
115
|
end
|
|
95
116
|
end
|
|
96
117
|
end
|
|
@@ -108,7 +129,7 @@ describe Bullet, focused: true do
|
|
|
108
129
|
{
|
|
109
130
|
'REQUEST_METHOD' => 'GET',
|
|
110
131
|
'PATH_INFO' => '/path',
|
|
111
|
-
'QUERY_STRING' => 'foo=bar'
|
|
132
|
+
'QUERY_STRING' => 'foo=bar'
|
|
112
133
|
}
|
|
113
134
|
}
|
|
114
135
|
|