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.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -1
  3. data/Gemfile.rails-5.2 +1 -1
  4. data/Guardfile +1 -1
  5. data/README.md +9 -4
  6. data/Rakefile +5 -5
  7. data/bullet.gemspec +11 -6
  8. data/lib/bullet/active_record4.rb +18 -14
  9. data/lib/bullet/active_record41.rb +16 -14
  10. data/lib/bullet/active_record42.rb +24 -22
  11. data/lib/bullet/active_record5.rb +78 -49
  12. data/lib/bullet/active_record52.rb +47 -40
  13. data/lib/bullet/dependency.rb +3 -1
  14. data/lib/bullet/detector/association.rb +16 -14
  15. data/lib/bullet/detector/base.rb +2 -0
  16. data/lib/bullet/detector/counter_cache.rb +10 -7
  17. data/lib/bullet/detector/n_plus_one_query.rb +17 -15
  18. data/lib/bullet/detector/unused_eager_loading.rb +26 -21
  19. data/lib/bullet/detector.rb +2 -0
  20. data/lib/bullet/ext/object.rb +6 -4
  21. data/lib/bullet/ext/string.rb +3 -1
  22. data/lib/bullet/mongoid4x.rb +7 -2
  23. data/lib/bullet/mongoid5x.rb +7 -2
  24. data/lib/bullet/mongoid6x.rb +11 -6
  25. data/lib/bullet/notification/base.rb +21 -15
  26. data/lib/bullet/notification/counter_cache.rb +2 -0
  27. data/lib/bullet/notification/n_plus_one_query.rb +6 -4
  28. data/lib/bullet/notification/unused_eager_loading.rb +6 -4
  29. data/lib/bullet/notification.rb +2 -0
  30. data/lib/bullet/notification_collector.rb +2 -1
  31. data/lib/bullet/rack.rb +19 -15
  32. data/lib/bullet/registry/association.rb +2 -0
  33. data/lib/bullet/registry/base.rb +2 -0
  34. data/lib/bullet/registry/object.rb +2 -0
  35. data/lib/bullet/registry.rb +2 -0
  36. data/lib/bullet/stack_trace_filter.rb +56 -19
  37. data/lib/bullet/version.rb +2 -1
  38. data/lib/bullet.rb +31 -29
  39. data/lib/generators/bullet/install_generator.rb +5 -3
  40. data/perf/benchmark.rb +9 -7
  41. data/rails/init.rb +2 -0
  42. data/spec/bullet/detector/association_spec.rb +2 -0
  43. data/spec/bullet/detector/base_spec.rb +2 -0
  44. data/spec/bullet/detector/counter_cache_spec.rb +2 -0
  45. data/spec/bullet/detector/n_plus_one_query_spec.rb +33 -8
  46. data/spec/bullet/detector/unused_eager_loading_spec.rb +2 -0
  47. data/spec/bullet/ext/object_spec.rb +3 -1
  48. data/spec/bullet/ext/string_spec.rb +2 -0
  49. data/spec/bullet/notification/base_spec.rb +8 -6
  50. data/spec/bullet/notification/counter_cache_spec.rb +3 -1
  51. data/spec/bullet/notification/n_plus_one_query_spec.rb +3 -1
  52. data/spec/bullet/notification/unused_eager_loading_spec.rb +3 -1
  53. data/spec/bullet/notification_collector_spec.rb +2 -0
  54. data/spec/bullet/rack_spec.rb +10 -9
  55. data/spec/bullet/registry/association_spec.rb +4 -2
  56. data/spec/bullet/registry/base_spec.rb +5 -3
  57. data/spec/bullet/registry/object_spec.rb +2 -0
  58. data/spec/bullet_spec.rb +9 -7
  59. data/spec/integration/active_record/association_spec.rb +79 -17
  60. data/spec/integration/counter_cache_spec.rb +4 -2
  61. data/spec/integration/mongoid/association_spec.rb +2 -0
  62. data/spec/models/address.rb +2 -0
  63. data/spec/models/author.rb +2 -0
  64. data/spec/models/base_user.rb +2 -0
  65. data/spec/models/category.rb +2 -0
  66. data/spec/models/city.rb +2 -0
  67. data/spec/models/client.rb +4 -0
  68. data/spec/models/comment.rb +2 -0
  69. data/spec/models/company.rb +2 -0
  70. data/spec/models/country.rb +2 -0
  71. data/spec/models/document.rb +2 -0
  72. data/spec/models/entry.rb +2 -0
  73. data/spec/models/firm.rb +3 -0
  74. data/spec/models/folder.rb +2 -0
  75. data/spec/models/group.rb +4 -0
  76. data/spec/models/mongoid/address.rb +3 -1
  77. data/spec/models/mongoid/category.rb +4 -2
  78. data/spec/models/mongoid/comment.rb +3 -1
  79. data/spec/models/mongoid/company.rb +3 -1
  80. data/spec/models/mongoid/entry.rb +3 -1
  81. data/spec/models/mongoid/post.rb +6 -4
  82. data/spec/models/mongoid/user.rb +2 -0
  83. data/spec/models/newspaper.rb +2 -0
  84. data/spec/models/page.rb +2 -0
  85. data/spec/models/person.rb +2 -0
  86. data/spec/models/pet.rb +2 -0
  87. data/spec/models/post.rb +17 -0
  88. data/spec/models/relationship.rb +2 -0
  89. data/spec/models/reply.rb +2 -0
  90. data/spec/models/student.rb +2 -0
  91. data/spec/models/submission.rb +2 -0
  92. data/spec/models/teacher.rb +2 -0
  93. data/spec/models/user.rb +2 -0
  94. data/spec/models/writer.rb +2 -0
  95. data/spec/spec_helper.rb +3 -1
  96. data/spec/support/bullet_ext.rb +2 -0
  97. data/spec/support/mongo_seed.rb +32 -30
  98. data/spec/support/rack_double.rb +9 -16
  99. data/spec/support/sqlite_seed.rb +84 -75
  100. data/tasks/bullet_tasks.rake +2 -0
  101. 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(:absolute_path => File.join(Dir.pwd, 'abc', 'abc.rb'))
95
- included_path = OpenStruct.new(:absolute_path => '/ghi/ghi.rb')
96
- excluded_path = OpenStruct.new(:absolute_path => '/def/def.rb')
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(:absolute_path => File.join(Dir.pwd, 'abc', 'abc.rb'))
108
- not_in_project = OpenStruct.new(:absolute_path => '/def/def.rb')
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(:absolute_path => File.join(Dir.pwd, 'abc', 'abc.rb'))
122
- included_gems = [OpenStruct.new(:absolute_path => '/def/def.rb'), OpenStruct.new(:absolute_path => 'xyz/xyz.rb')]
123
- excluded_gem = OpenStruct.new(:absolute_path => '/ghi/ghi.rb')
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
  module Bullet
@@ -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([:category_id, :writer_id])
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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe String do
@@ -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, [:comments, :votes]) }
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(:user => 'whoami', :url => 'url', :title => 'title', :body => 'body_with_caller')
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(:foo => :bar)
78
- expect(notifier).to receive(:inline_notify).with(:foo => :bar)
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(:foo => :bar)
88
- expect(notifier).to receive(:out_of_channel_notify).with(:foo => :bar)
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, [:comments, :votes]) }
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([['caller1', 'caller2']], Post, [:comments, :votes], 'path') }
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, [:comments, :votes], 'path') }
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') }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  module Bullet
@@ -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(:body => '<html><head></head><body></body></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
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(:body => "<html attr='hello'><head></head><body></body></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
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
29
  it 'should be false if Content-Type is javascript' do
29
30
  headers = { 'Content-Type' => 'text/javascript' }
30
- response = double(:body => '<html><head></head><body></body></html>')
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(:body => '<div>Partial</div>')
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(:body => '<html><head></head><body></body></html>')
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(:body => '')
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({ 'Content-Type' => 'text/html' })
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({ 'Content-Type' => 'text/html' })
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(['key1', 'key2'], 'value') } }
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(['key1', 'key2'])
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(['value', 'new_value']))
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', ['value1', 'value2'])
29
- expect(subject['key']).to eq(Set.new(['value', 'value1', 'value2']))
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
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  module Bullet
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(:type => :n_plus_one_query, :class_name => 'Klass', :association => :department)
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(:type => :n_plus_one_query, :class_name => 'Klass', :association => :department)
101
- Bullet.delete_whitelist(:type => :n_plus_one_query, :class_name => 'Klass', :association => :department)
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(:type => :n_plus_one_query, :class_name => 'Klass', :association => :department)
109
- Bullet.add_whitelist(:type => :n_plus_one_query, :class_name => 'Klass', :association => :team)
110
- Bullet.delete_whitelist(:type => :n_plus_one_query, :class_name => 'Klass', :association => :team)
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({ :posts => :comments }).each do |category|
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({ :posts => :comments }).where('posts.id > 0').references(:posts).each do |category|
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({ :posts => :comments }).map(&:name)
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({ :posts => :comments }).each do |category|
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([:posts, :entries]).each do |category|
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([:posts, :entries]).map(&:name)
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([:posts, :entries]).each do |category|
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.name if 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({ :posts => :writer }).order('id DESC').find_by_name('first')
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(:post => :category).each do |comment|
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([:author, :post]).where(['base_users.id = ?', BaseUser.first]).references(:base_users).each do |comment|
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, { :post => :writer }]).where(['base_users.id = ?', BaseUser.first]).references(:base_users).each do |comment|
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(:post => :writer).where("posts.name like '%first%'").references(:posts).each do |comment|
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({ :post => :category }).each do |com|
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 :type => :n_plus_one_query, :class_name => 'Post', :association => :comments }
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 :type => :unused_eager_loading, :class_name => 'Post', :association => :comments }
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(:name => 'first').size
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 :type => :counter_cache, :class_name => 'Country', :association => :cities }
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  if mongoid?
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Address < ActiveRecord::Base
2
4
  belongs_to :company
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Author < ActiveRecord::Base
2
4
  has_many :documents
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class BaseUser < ActiveRecord::Base
2
4
  has_many :comments
3
5
  has_many :posts
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Category < ActiveRecord::Base
2
4
  has_many :posts, inverse_of: :category
3
5
  has_many :entries