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.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -5
  3. data/CHANGELOG.md +31 -1
  4. data/Gemfile +6 -1
  5. data/Gemfile.rails-5.0 +1 -1
  6. data/Gemfile.rails-5.1 +15 -0
  7. data/Gemfile.rails-5.2 +15 -0
  8. data/Guardfile +2 -2
  9. data/Hacking.md +1 -1
  10. data/README.md +20 -23
  11. data/Rakefile +20 -21
  12. data/bullet.gemspec +20 -15
  13. data/lib/bullet/active_record4.rb +18 -14
  14. data/lib/bullet/active_record41.rb +16 -14
  15. data/lib/bullet/active_record42.rb +24 -22
  16. data/lib/bullet/active_record5.rb +102 -91
  17. data/lib/bullet/active_record52.rb +232 -0
  18. data/lib/bullet/dependency.rb +9 -1
  19. data/lib/bullet/detector/association.rb +17 -14
  20. data/lib/bullet/detector/base.rb +2 -0
  21. data/lib/bullet/detector/counter_cache.rb +14 -10
  22. data/lib/bullet/detector/n_plus_one_query.rb +21 -17
  23. data/lib/bullet/detector/unused_eager_loading.rb +30 -24
  24. data/lib/bullet/detector.rb +2 -0
  25. data/lib/bullet/ext/object.rb +6 -4
  26. data/lib/bullet/ext/string.rb +3 -1
  27. data/lib/bullet/mongoid4x.rb +7 -2
  28. data/lib/bullet/mongoid5x.rb +7 -2
  29. data/lib/bullet/mongoid6x.rb +11 -6
  30. data/lib/bullet/notification/base.rb +26 -19
  31. data/lib/bullet/notification/counter_cache.rb +3 -1
  32. data/lib/bullet/notification/n_plus_one_query.rb +7 -4
  33. data/lib/bullet/notification/unused_eager_loading.rb +7 -4
  34. data/lib/bullet/notification.rb +2 -0
  35. data/lib/bullet/notification_collector.rb +2 -1
  36. data/lib/bullet/rack.rb +22 -18
  37. data/lib/bullet/registry/association.rb +2 -0
  38. data/lib/bullet/registry/base.rb +2 -0
  39. data/lib/bullet/registry/object.rb +2 -0
  40. data/lib/bullet/registry.rb +2 -0
  41. data/lib/bullet/stack_trace_filter.rb +56 -18
  42. data/lib/bullet/version.rb +3 -2
  43. data/lib/bullet.rb +43 -33
  44. data/lib/generators/bullet/install_generator.rb +48 -0
  45. data/perf/benchmark.rb +12 -12
  46. data/rails/init.rb +2 -0
  47. data/spec/bullet/detector/association_spec.rb +6 -4
  48. data/spec/bullet/detector/base_spec.rb +2 -0
  49. data/spec/bullet/detector/counter_cache_spec.rb +13 -11
  50. data/spec/bullet/detector/n_plus_one_query_spec.rb +71 -46
  51. data/spec/bullet/detector/unused_eager_loading_spec.rb +21 -19
  52. data/spec/bullet/ext/object_spec.rb +10 -8
  53. data/spec/bullet/ext/string_spec.rb +7 -5
  54. data/spec/bullet/notification/base_spec.rb +39 -38
  55. data/spec/bullet/notification/counter_cache_spec.rb +5 -3
  56. data/spec/bullet/notification/n_plus_one_query_spec.rb +5 -3
  57. data/spec/bullet/notification/unused_eager_loading_spec.rb +4 -2
  58. data/spec/bullet/notification_collector_spec.rb +12 -10
  59. data/spec/bullet/rack_spec.rb +48 -47
  60. data/spec/bullet/registry/association_spec.rb +12 -10
  61. data/spec/bullet/registry/base_spec.rb +22 -20
  62. data/spec/bullet/registry/object_spec.rb +6 -4
  63. data/spec/bullet_spec.rb +24 -3
  64. data/spec/integration/{active_record4 → active_record}/association_spec.rb +176 -114
  65. data/spec/integration/counter_cache_spec.rb +15 -13
  66. data/spec/integration/mongoid/association_spec.rb +35 -33
  67. data/spec/models/address.rb +2 -0
  68. data/spec/models/author.rb +2 -0
  69. data/spec/models/base_user.rb +2 -0
  70. data/spec/models/category.rb +2 -0
  71. data/spec/models/city.rb +2 -0
  72. data/spec/models/client.rb +4 -0
  73. data/spec/models/comment.rb +3 -1
  74. data/spec/models/company.rb +2 -0
  75. data/spec/models/country.rb +2 -0
  76. data/spec/models/document.rb +4 -2
  77. data/spec/models/entry.rb +2 -0
  78. data/spec/models/firm.rb +3 -0
  79. data/spec/models/folder.rb +2 -0
  80. data/spec/models/group.rb +4 -0
  81. data/spec/models/mongoid/address.rb +3 -1
  82. data/spec/models/mongoid/category.rb +4 -2
  83. data/spec/models/mongoid/comment.rb +3 -1
  84. data/spec/models/mongoid/company.rb +3 -1
  85. data/spec/models/mongoid/entry.rb +3 -1
  86. data/spec/models/mongoid/post.rb +6 -4
  87. data/spec/models/mongoid/user.rb +2 -0
  88. data/spec/models/newspaper.rb +3 -1
  89. data/spec/models/page.rb +2 -0
  90. data/spec/models/person.rb +2 -0
  91. data/spec/models/pet.rb +2 -0
  92. data/spec/models/post.rb +17 -0
  93. data/spec/models/relationship.rb +2 -0
  94. data/spec/models/reply.rb +2 -0
  95. data/spec/models/student.rb +2 -0
  96. data/spec/models/submission.rb +2 -0
  97. data/spec/models/teacher.rb +2 -0
  98. data/spec/models/user.rb +2 -0
  99. data/spec/models/writer.rb +2 -0
  100. data/spec/spec_helper.rb +15 -13
  101. data/spec/support/bullet_ext.rb +2 -0
  102. data/spec/support/mongo_seed.rb +36 -42
  103. data/spec/support/rack_double.rb +13 -19
  104. data/spec/support/sqlite_seed.rb +84 -76
  105. data/tasks/bullet_tasks.rake +4 -2
  106. data/test.sh +2 -0
  107. data/update.sh +2 -0
  108. metadata +17 -11
  109. data/spec/integration/active_record5/association_spec.rb +0 -768
@@ -1,4 +1,5 @@
1
- # encoding: utf-8
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 "#html_request?" do
10
- it "should be true if Content-Type is text/html and http body contains html tag" do
11
- headers = {"Content-Type" => "text/html"}
12
- response = double(:body => "<html><head></head><body></body></html>")
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 "should be true if Content-Type is text/html and http body contains html tag with attributes" do
17
- headers = {"Content-Type" => "text/html"}
18
- response = double(:body => "<html attr='hello'><head></head><body></body></html>")
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 "should be false if there is no Content-Type header" do
23
+ it 'should be false if there is no Content-Type header' do
23
24
  headers = {}
24
- response = double(:body => "<html><head></head><body></body></html>")
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 "should be false if Content-Type is javascript" do
29
- headers = {"Content-Type" => "text/javascript"}
30
- response = double(:body => "<html><head></head><body></body></html>")
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 = {"Content-Type" => "text/html"}
36
- response = double(:body => "<div>Partial</div>")
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 "empty?" do
42
- it "should be false if response is a string and not empty" do
43
- response = double(:body => "<html><head></head><body></body></html>")
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 "should be true if response is not found" do
48
- response = ["Not Found"]
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 "should be true if response body is empty" do
53
- response = double(:body => "")
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 "#call" do
59
- context "when Bullet is enabled" do
60
- it "should return original response body" do
61
- expected_response = Support::ResponseDouble.new "Actual body"
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 "should change response body if notification is active" do
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("<bullet></bullet>")
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({"Content-Type" => "text/html"})
72
- expect(headers["Content-Length"]).to eq("56")
73
- expect(response).to eq(["<html><head></head><body><bullet></bullet></body></html>"])
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 "should set the right Content-Length if response body contains accents" do
77
+ it 'should set the right Content-Length if response body contains accents' do
77
78
  response = Support::ResponseDouble.new
78
- response.body = "<html><head></head><body>é</body></html>"
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("<bullet></bullet>")
82
- status, headers, response = middleware.call({"Content-Type" => "text/html"})
83
- expect(headers["Content-Length"]).to eq("58")
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 "when Bullet is disabled" do
88
+ context 'when Bullet is disabled' do
88
89
  before(:each) { allow(Bullet).to receive(:enable?).and_return(false) }
89
90
 
90
- it "should not call Bullet.start_request" do
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 "#response_body" do
98
+ describe '#response_body' do
98
99
  let(:response) { double }
99
- let(:body_string) { "<html><body>My Body</body></html>" }
100
+ let(:body_string) { '<html><body>My Body</body></html>' }
100
101
 
101
- context "when `response` responds to `body`" do
102
+ context 'when `response` responds to `body`' do
102
103
  before { allow(response).to receive(:body).and_return(body) }
103
104
 
104
- context "when `body` returns an Array" do
105
+ context 'when `body` returns an Array' do
105
106
  let(:body) { [body_string, 'random string'] }
106
- it "should return the plain body string" do
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 "when `body` does not return an Array" do
112
+ context 'when `body` does not return an Array' do
112
113
  let(:body) { body_string }
113
- it "should return the plain body string" do
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 "when `response` does not respond to `body`" do
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 "should return the plain body string" do
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(["key1", "key2"], "value") } }
8
+ subject { Association.new.tap { |association| association.add(%w[key1 key2], 'value') } }
7
9
 
8
- context "#merge" do
9
- it "should merge key/value" do
10
- subject.merge("key0", "value0")
11
- expect(subject["key0"]).to be_include("value0")
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 "#similarly_associated" do
16
- it "should return similarly associated keys" do
17
- expect(subject.similarly_associated("key1", Set.new(["value"]))).to eq(["key1", "key2"])
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 "should return empty if key does not exist" do
21
- expect(subject.similarly_associated("key3", Set.new(["value"]))).to be_empty
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("key", "value") } }
8
+ subject { Base.new.tap { |base| base.add('key', 'value') } }
7
9
 
8
- context "#[]" do
9
- it "should get value by key" do
10
- expect(subject["key"]).to eq(Set.new(["value"]))
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 "#delete" do
15
- it "should delete key" do
16
- subject.delete("key")
17
- expect(subject["key"]).to be_nil
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 "#add" do
22
- it "should add value with string" do
23
- subject.add("key", "new_value")
24
- expect(subject["key"]).to eq(Set.new(["value", "new_value"]))
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 "should add value with array" do
28
- subject.add("key", ["value1", "value2"])
29
- expect(subject["key"]).to eq(Set.new(["value", "value1", "value2"]))
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 "#include?" do
34
- it "should include key/value" do
35
- expect(subject.include?("key", "value")).to eq true
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 "should not include wrong key/value" do
39
- expect(subject.include?("key", "val")).to eq false
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 "#include?" do
11
- it "should include the object" do
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 "#add" do
17
- it "should add an object" do
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(:type => :n_plus_one_query, :class_name => 'Klass', :association => :department)
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