bullet 6.1.0 → 7.0.4

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +82 -0
  3. data/CHANGELOG.md +50 -0
  4. data/Gemfile.rails-6.0 +1 -1
  5. data/Gemfile.rails-6.1 +15 -0
  6. data/Gemfile.rails-7.0 +10 -0
  7. data/MIT-LICENSE +1 -1
  8. data/README.md +38 -26
  9. data/lib/bullet/active_job.rb +5 -1
  10. data/lib/bullet/active_record41.rb +1 -0
  11. data/lib/bullet/active_record42.rb +1 -0
  12. data/lib/bullet/active_record5.rb +6 -4
  13. data/lib/bullet/active_record52.rb +28 -21
  14. data/lib/bullet/active_record60.rb +27 -20
  15. data/lib/bullet/active_record61.rb +274 -0
  16. data/lib/bullet/active_record70.rb +277 -0
  17. data/lib/bullet/bullet_xhr.js +18 -17
  18. data/lib/bullet/dependency.rb +16 -0
  19. data/lib/bullet/detector/base.rb +2 -1
  20. data/lib/bullet/detector/counter_cache.rb +2 -2
  21. data/lib/bullet/detector/n_plus_one_query.rb +5 -5
  22. data/lib/bullet/detector/unused_eager_loading.rb +2 -2
  23. data/lib/bullet/mongoid4x.rb +1 -1
  24. data/lib/bullet/mongoid5x.rb +1 -1
  25. data/lib/bullet/mongoid6x.rb +1 -1
  26. data/lib/bullet/mongoid7x.rb +32 -17
  27. data/lib/bullet/notification.rb +2 -1
  28. data/lib/bullet/rack.rb +28 -19
  29. data/lib/bullet/stack_trace_filter.rb +7 -9
  30. data/lib/bullet/version.rb +1 -1
  31. data/lib/bullet.rb +29 -28
  32. data/lib/generators/bullet/install_generator.rb +22 -25
  33. data/perf/benchmark.rb +4 -1
  34. data/spec/bullet/detector/counter_cache_spec.rb +1 -1
  35. data/spec/bullet/detector/n_plus_one_query_spec.rb +1 -1
  36. data/spec/bullet/detector/unused_eager_loading_spec.rb +10 -10
  37. data/spec/bullet/ext/object_spec.rb +1 -1
  38. data/spec/bullet/notification/base_spec.rb +4 -4
  39. data/spec/bullet/notification/n_plus_one_query_spec.rb +1 -3
  40. data/spec/bullet/rack_spec.rb +135 -9
  41. data/spec/bullet_spec.rb +15 -15
  42. data/spec/integration/active_record/association_spec.rb +73 -11
  43. data/spec/integration/counter_cache_spec.rb +4 -4
  44. data/spec/integration/mongoid/association_spec.rb +4 -4
  45. data/spec/models/attachment.rb +5 -0
  46. data/spec/models/deal.rb +5 -0
  47. data/spec/models/folder.rb +2 -1
  48. data/spec/models/group.rb +2 -1
  49. data/spec/models/page.rb +2 -1
  50. data/spec/models/post.rb +2 -0
  51. data/spec/models/role.rb +7 -0
  52. data/spec/models/submission.rb +1 -0
  53. data/spec/models/user.rb +2 -0
  54. data/spec/models/writer.rb +2 -1
  55. data/spec/spec_helper.rb +0 -2
  56. data/spec/support/mongo_seed.rb +1 -0
  57. data/spec/support/sqlite_seed.rb +38 -0
  58. data/test.sh +2 -0
  59. metadata +17 -7
  60. data/.travis.yml +0 -33
@@ -31,12 +31,6 @@ module Bullet
31
31
  response = double(body: '<html><head></head><body></body></html>')
32
32
  expect(middleware).not_to be_html_request(headers, response)
33
33
  end
34
-
35
- it "should be false if response body doesn't contain html tag" do
36
- headers = { 'Content-Type' => 'text/html' }
37
- response = double(body: '<div>Partial</div>')
38
- expect(middleware).not_to be_html_request(headers, response)
39
- end
40
34
  end
41
35
 
42
36
  context 'empty?' do
@@ -54,6 +48,11 @@ module Bullet
54
48
  response = double(body: '')
55
49
  expect(middleware).to be_empty(response)
56
50
  end
51
+
52
+ it 'should be true if no response body' do
53
+ response = double()
54
+ expect(middleware).to be_empty(response)
55
+ end
57
56
  end
58
57
 
59
58
  context '#call' do
@@ -67,9 +66,8 @@ module Bullet
67
66
 
68
67
  it 'should change response body if notification is active' do
69
68
  expect(Bullet).to receive(:notification?).and_return(true)
70
- allow(Bullet).to receive(:skip_html_injection?).and_return(false)
69
+ expect(Bullet).to receive(:console_enabled?).and_return(true)
71
70
  expect(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
72
- expect(middleware).to receive(:xhr_script).and_return('')
73
71
  expect(Bullet).to receive(:perform_out_of_channel_notifications)
74
72
  _, headers, response = middleware.call('Content-Type' => 'text/html')
75
73
  expect(headers['Content-Length']).to eq('56')
@@ -81,9 +79,123 @@ module Bullet
81
79
  response.body = '<html><head></head><body>é</body></html>'
82
80
  app.response = response
83
81
  expect(Bullet).to receive(:notification?).and_return(true)
82
+ allow(Bullet).to receive(:console_enabled?).and_return(true)
84
83
  expect(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
85
84
  _, headers, response = middleware.call('Content-Type' => 'text/html')
86
- expect(headers['Content-Length']).to eq((58 + middleware.send(:xhr_script).length).to_s)
85
+ expect(headers['Content-Length']).to eq('58')
86
+ end
87
+
88
+ context 'with injection notifiers' do
89
+ before do
90
+ expect(Bullet).to receive(:notification?).and_return(true)
91
+ allow(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
92
+ allow(middleware).to receive(:xhr_script).and_return('<script></script>')
93
+ allow(middleware).to receive(:footer_note).and_return('footer')
94
+ expect(Bullet).to receive(:perform_out_of_channel_notifications)
95
+ end
96
+
97
+ it 'should change response body if add_footer is true' do
98
+ expect(Bullet).to receive(:add_footer).exactly(3).times.and_return(true)
99
+ _, headers, response = middleware.call('Content-Type' => 'text/html')
100
+
101
+ expect(headers['Content-Length']).to eq((73 + middleware.send(:footer_note).length).to_s)
102
+ expect(response).to eq(%w[<html><head></head><body>footer<bullet></bullet><script></script></body></html>])
103
+ end
104
+
105
+ it 'should change response body for html safe string if add_footer is true' do
106
+ expect(Bullet).to receive(:add_footer).exactly(3).times.and_return(true)
107
+ app.response =
108
+ Support::ResponseDouble.new.tap do |response|
109
+ response.body = ActiveSupport::SafeBuffer.new('<html><head></head><body></body></html>')
110
+ end
111
+ _, headers, response = middleware.call('Content-Type' => 'text/html')
112
+
113
+ expect(headers['Content-Length']).to eq((73 + middleware.send(:footer_note).length).to_s)
114
+ expect(response).to eq(%w[<html><head></head><body>footer<bullet></bullet><script></script></body></html>])
115
+ end
116
+
117
+ it 'should add the footer-text header for non-html requests when add_footer is true' do
118
+ allow(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
119
+ allow(Bullet).to receive(:footer_info).and_return(['footer text'])
120
+ app.headers = { 'Content-Type' => 'application/json' }
121
+ _, headers, _response = middleware.call({})
122
+ expect(headers).to include('X-bullet-footer-text' => '["footer text"]')
123
+ end
124
+
125
+ it 'should change response body if console_enabled is true' do
126
+ expect(Bullet).to receive(:console_enabled?).and_return(true)
127
+ _, headers, response = middleware.call('Content-Type' => 'text/html')
128
+ expect(headers['Content-Length']).to eq('56')
129
+ expect(response).to eq(%w[<html><head></head><body><bullet></bullet></body></html>])
130
+ end
131
+
132
+ it 'should change response body for html safe string if console_enabled is true' do
133
+ expect(Bullet).to receive(:console_enabled?).and_return(true)
134
+ app.response =
135
+ Support::ResponseDouble.new.tap do |response|
136
+ response.body = ActiveSupport::SafeBuffer.new('<html><head></head><body></body></html>')
137
+ end
138
+ _, headers, response = middleware.call('Content-Type' => 'text/html')
139
+ expect(headers['Content-Length']).to eq('56')
140
+ expect(response).to eq(%w[<html><head></head><body><bullet></bullet></body></html>])
141
+ end
142
+
143
+ it 'should add headers for non-html requests when console_enabled is true' do
144
+ allow(Bullet).to receive(:console_enabled?).at_least(:once).and_return(true)
145
+ allow(Bullet).to receive(:text_notifications).and_return(['text notifications'])
146
+ app.headers = { 'Content-Type' => 'application/json' }
147
+ _, headers, _response = middleware.call({})
148
+ expect(headers).to include('X-bullet-console-text' => '["text notifications"]')
149
+ end
150
+
151
+ it "shouldn't change response body unnecessarily" do
152
+ expected_response = Support::ResponseDouble.new 'Actual body'
153
+ app.response = expected_response
154
+ _, _, response = middleware.call({})
155
+ expect(response).to eq(expected_response)
156
+ end
157
+
158
+ it "shouldn't add headers unnecessarily" do
159
+ app.headers = { 'Content-Type' => 'application/json' }
160
+ _, headers, _response = middleware.call({})
161
+ expect(headers).not_to include('X-bullet-footer-text')
162
+ expect(headers).not_to include('X-bullet-console-text')
163
+ end
164
+
165
+ context 'when skip_http_headers is enabled' do
166
+ before do
167
+ allow(Bullet).to receive(:skip_http_headers).and_return(true)
168
+ end
169
+
170
+ it 'should include the footer but not the xhr script tag if add_footer is true' do
171
+ expect(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
172
+ _, headers, response = middleware.call({})
173
+
174
+ expect(headers['Content-Length']).to eq((56 + middleware.send(:footer_note).length).to_s)
175
+ expect(response).to eq(%w[<html><head></head><body>footer<bullet></bullet></body></html>])
176
+ end
177
+
178
+ it 'should not include the xhr script tag if console_enabled is true' do
179
+ expect(Bullet).to receive(:console_enabled?).and_return(true)
180
+ _, headers, response = middleware.call({})
181
+ expect(headers['Content-Length']).to eq('56')
182
+ expect(response).to eq(%w[<html><head></head><body><bullet></bullet></body></html>])
183
+ end
184
+
185
+ it 'should not add the footer-text header for non-html requests when add_footer is true' do
186
+ allow(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
187
+ app.headers = { 'Content-Type' => 'application/json' }
188
+ _, headers, _response = middleware.call({})
189
+ expect(headers).not_to include('X-bullet-footer-text')
190
+ end
191
+
192
+ it 'should not add headers for non-html requests when console_enabled is true' do
193
+ allow(Bullet).to receive(:console_enabled?).at_least(:once).and_return(true)
194
+ app.headers = { 'Content-Type' => 'application/json' }
195
+ _, headers, _response = middleware.call({})
196
+ expect(headers).not_to include('X-bullet-console-text')
197
+ end
198
+ end
87
199
  end
88
200
 
89
201
  context 'when skip_html_injection is enabled' do
@@ -148,6 +260,20 @@ module Bullet
148
260
  expect(middleware.response_body(response)).to eq body_string
149
261
  end
150
262
  end
263
+
264
+ begin
265
+ require 'rack/files'
266
+
267
+ context 'when `response` is a Rack::Files::Iterator' do
268
+ let(:response) { instance_double(::Rack::Files::Iterator) }
269
+ before { allow(response).to receive(:is_a?).with(::Rack::Files::Iterator) { true } }
270
+
271
+ it 'should return nil' do
272
+ expect(middleware.response_body(response)).to be_nil
273
+ end
274
+ end
275
+ rescue LoadError
276
+ end
151
277
  end
152
278
  end
153
279
  end
data/spec/bullet_spec.rb CHANGED
@@ -74,31 +74,31 @@ describe Bullet, focused: true do
74
74
  end
75
75
  end
76
76
 
77
- describe '#add_whitelist' do
77
+ describe '#add_safelist' do
78
78
  context "for 'special' class names" do
79
- it 'is added to the whitelist successfully' do
80
- Bullet.add_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
81
- expect(Bullet.get_whitelist_associations(:n_plus_one_query, 'Klass')).to include :department
79
+ it 'is added to the safelist successfully' do
80
+ Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
81
+ expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to include :department
82
82
  end
83
83
  end
84
84
  end
85
85
 
86
- describe '#delete_whitelist' do
86
+ describe '#delete_safelist' do
87
87
  context "for 'special' class names" do
88
- it 'is deleted from the whitelist successfully' do
89
- Bullet.add_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
90
- Bullet.delete_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
91
- expect(Bullet.whitelist[:n_plus_one_query]).to eq({})
88
+ it 'is deleted from the safelist successfully' do
89
+ Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
90
+ Bullet.delete_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
91
+ expect(Bullet.safelist[:n_plus_one_query]).to eq({})
92
92
  end
93
93
  end
94
94
 
95
95
  context 'when exists multiple definitions' do
96
- it 'is deleted from the whitelist successfully' do
97
- Bullet.add_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
98
- Bullet.add_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
99
- Bullet.delete_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
100
- expect(Bullet.get_whitelist_associations(:n_plus_one_query, 'Klass')).to include :department
101
- expect(Bullet.get_whitelist_associations(:n_plus_one_query, 'Klass')).to_not include :team
96
+ it 'is deleted from the safelist successfully' do
97
+ Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
98
+ Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
99
+ Bullet.delete_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
100
+ expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to include :department
101
+ expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to_not include :team
102
102
  end
103
103
  end
104
104
  end
@@ -129,7 +129,7 @@ if active_record?
129
129
  expect(Bullet::Detector::Association).to be_completely_preloading_associations
130
130
  end
131
131
 
132
- it 'should detect unused preload with post => commnets, no category => posts' do
132
+ it 'should detect unused preload with post => comments, no category => posts' do
133
133
  Category.includes(posts: :comments).each { |category| category.posts.map(&:name) }
134
134
  Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
135
135
  expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Post, :comments)
@@ -202,7 +202,7 @@ if active_record?
202
202
  expect(Bullet::Detector::Association).to be_completely_preloading_associations
203
203
  end
204
204
 
205
- it 'should detect preload with post => commnets' do
205
+ it 'should detect preload with post => comments' do
206
206
  Post.first.comments.map(&:name)
207
207
  Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
208
208
  expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
@@ -377,8 +377,7 @@ if active_record?
377
377
  it 'should detect unused preload with comment => author' do
378
378
  Comment.includes([:author, { post: :writer }]).where(['base_users.id = ?', BaseUser.first]).references(
379
379
  :base_users
380
- )
381
- .each { |comment| comment.post.writer.name }
380
+ ).each { |comment| comment.post.writer.name }
382
381
  Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
383
382
  expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
384
383
 
@@ -402,6 +401,15 @@ if active_record?
402
401
  end
403
402
 
404
403
  describe Bullet::Detector::Association, 'has_and_belongs_to_many' do
404
+ context 'posts <=> deals' do
405
+ it 'should detect preload associations with join tables that have identifier' do
406
+ Post.includes(:deals).each { |post| post.deals.map(&:name) }
407
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
408
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
409
+
410
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
411
+ end
412
+ end
405
413
  context 'students <=> teachers' do
406
414
  it 'should detect non preload associations' do
407
415
  Student.all.each { |student| student.teachers.map(&:name) }
@@ -443,6 +451,16 @@ if active_record?
443
451
  expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Student, :teachers)
444
452
  end
445
453
  end
454
+
455
+ context 'user => roles' do
456
+ it 'should detect preload associations' do
457
+ User.first.roles.includes(:resource).each { |role| role.resource }
458
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
459
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
460
+
461
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
462
+ end
463
+ end
446
464
  end
447
465
 
448
466
  describe Bullet::Detector::Association, 'has_many :through' do
@@ -456,7 +474,15 @@ if active_record?
456
474
  end
457
475
 
458
476
  it 'should detect preload associations' do
459
- Firm.includes(:clients).each { |firm| firm.clients.map(&:name) }
477
+ Firm.preload(:clients).each { |firm| firm.clients.map(&:name) }
478
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
479
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
480
+
481
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
482
+ end
483
+
484
+ it 'should detect eager load association' do
485
+ Firm.eager_load(:clients).each { |firm| firm.clients.map(&:name) }
460
486
  Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
461
487
  expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
462
488
 
@@ -562,6 +588,42 @@ if active_record?
562
588
  end
563
589
  end
564
590
 
591
+ describe Bullet::Detector::Association, 'has_one :through' do
592
+ context 'user => attachment' do
593
+ it 'should detect non preload associations' do
594
+ User.all.each { |user| user.submission_attachment.file_name }
595
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
596
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
597
+
598
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(User, :submission_attachment)
599
+ end
600
+
601
+ it 'should detect preload associations' do
602
+ User.includes(:submission_attachment).each { |user| user.submission_attachment.file_name }
603
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
604
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
605
+
606
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
607
+ end
608
+
609
+ it 'should not detect preload associations' do
610
+ User.all.map(&:name)
611
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
612
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
613
+
614
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
615
+ end
616
+
617
+ it 'should detect unused preload associations' do
618
+ User.includes(:submission_attachment).map(&:name)
619
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
620
+ expect(Bullet::Detector::Association).to be_unused_preload_associations_for(User, :submission_attachment)
621
+
622
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
623
+ end
624
+ end
625
+ end
626
+
565
627
  describe Bullet::Detector::Association, 'call one association that in possible objects' do
566
628
  it 'should not detect preload association' do
567
629
  Post.all
@@ -694,9 +756,9 @@ if active_record?
694
756
  end
695
757
  end
696
758
 
697
- context 'whitelist n plus one query' do
698
- before { Bullet.add_whitelist type: :n_plus_one_query, class_name: 'Post', association: :comments }
699
- after { Bullet.clear_whitelist }
759
+ context 'add n plus one query to safelist' do
760
+ before { Bullet.add_safelist type: :n_plus_one_query, class_name: 'Post', association: :comments }
761
+ after { Bullet.clear_safelist }
700
762
 
701
763
  it 'should not detect n plus one query' do
702
764
  Post.all.each { |post| post.comments.map(&:name) }
@@ -715,9 +777,9 @@ if active_record?
715
777
  end
716
778
  end
717
779
 
718
- context 'whitelist unused eager loading' do
719
- before { Bullet.add_whitelist type: :unused_eager_loading, class_name: 'Post', association: :comments }
720
- after { Bullet.clear_whitelist }
780
+ context 'add unused eager loading to safelist' do
781
+ before { Bullet.add_safelist type: :unused_eager_loading, class_name: 'Post', association: :comments }
782
+ after { Bullet.clear_safelist }
721
783
 
722
784
  it 'should not detect unused eager loading' do
723
785
  Post.includes(:comments).map(&:name)
@@ -28,7 +28,7 @@ if !mongoid? && active_record?
28
28
  expect(Bullet.collected_counter_cache_notifications).to be_empty
29
29
  end
30
30
 
31
- if active_record5? || active_record6?
31
+ if ActiveRecord::VERSION::MAJOR > 4
32
32
  it 'should not need counter cache for has_many through' do
33
33
  Client.all.each { |client| client.firms.size }
34
34
  expect(Bullet.collected_counter_cache_notifications).to be_empty
@@ -55,9 +55,9 @@ if !mongoid? && active_record?
55
55
  end
56
56
  end
57
57
 
58
- context 'whitelist' do
59
- before { Bullet.add_whitelist type: :counter_cache, class_name: 'Country', association: :cities }
60
- after { Bullet.clear_whitelist }
58
+ context 'safelist' do
59
+ before { Bullet.add_safelist type: :counter_cache, class_name: 'Country', association: :cities }
60
+ after { Bullet.clear_safelist }
61
61
 
62
62
  it 'should not detect counter cache' do
63
63
  Country.all.each { |country| country.cities.size }
@@ -73,9 +73,9 @@ if mongoid?
73
73
  expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
74
74
 
75
75
  expect(Bullet::Detector::Association).not_to be_detecting_unpreloaded_association_for(
76
- Mongoid::Category,
77
- :posts
78
- )
76
+ Mongoid::Category,
77
+ :posts
78
+ )
79
79
  expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Mongoid::Category, :entries)
80
80
  end
81
81
 
@@ -118,7 +118,7 @@ if mongoid?
118
118
  expect(Bullet::Detector::Association).to be_completely_preloading_associations
119
119
  end
120
120
 
121
- it 'should detect preload with post => commnets' do
121
+ it 'should detect preload with post => comments' do
122
122
  Mongoid::Post.first.comments.map(&:name)
123
123
  Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
124
124
  expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Attachment < ActiveRecord::Base
4
+ belongs_to :submission
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Deal < ActiveRecord::Base
4
+ has_and_belongs_to_many :posts
5
+ end
@@ -1,3 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Folder < Document; end
3
+ class Folder < Document
4
+ end
data/spec/models/group.rb CHANGED
@@ -1,3 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Group < ActiveRecord::Base; end
3
+ class Group < ActiveRecord::Base
4
+ end
data/spec/models/page.rb CHANGED
@@ -1,3 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Page < Document; end
3
+ class Page < Document
4
+ end
data/spec/models/post.rb CHANGED
@@ -4,6 +4,7 @@ class Post < ActiveRecord::Base
4
4
  belongs_to :category, inverse_of: :posts
5
5
  belongs_to :writer
6
6
  has_many :comments, inverse_of: :post
7
+ has_and_belongs_to_many :deals
7
8
 
8
9
  validates :category, presence: true
9
10
 
@@ -21,6 +22,7 @@ class Post < ActiveRecord::Base
21
22
  next unless trigger_after_save
22
23
 
23
24
  temp_comment = Comment.new(post: self)
25
+
24
26
  # this triggers self to be "possible", even though it's
25
27
  # not saved yet
26
28
  temp_comment.post
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Role < ActiveRecord::Base
4
+ has_and_belongs_to_many :users
5
+
6
+ belongs_to :resource, polymorphic: true
7
+ end
@@ -3,4 +3,5 @@
3
3
  class Submission < ActiveRecord::Base
4
4
  belongs_to :user
5
5
  has_many :replies
6
+ has_one :attachment
6
7
  end
data/spec/models/user.rb CHANGED
@@ -2,5 +2,7 @@
2
2
 
3
3
  class User < ActiveRecord::Base
4
4
  has_one :submission
5
+ has_one :submission_attachment, through: :submission, source: :attachment, class_name: 'Attachment'
5
6
  belongs_to :category
7
+ has_and_belongs_to_many :roles
6
8
  end
@@ -1,3 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Writer < BaseUser; end
3
+ class Writer < BaseUser
4
+ end
data/spec/spec_helper.rb CHANGED
@@ -4,12 +4,10 @@ require 'rspec'
4
4
  begin
5
5
  require 'active_record'
6
6
  rescue LoadError
7
-
8
7
  end
9
8
  begin
10
9
  require 'mongoid'
11
10
  rescue LoadError
12
-
13
11
  end
14
12
 
15
13
  module Rails
@@ -45,6 +45,7 @@ module Support
45
45
  Mongoid.configure do |config|
46
46
  config.load_configuration(clients: { default: { database: 'bullet', hosts: %w[localhost:27017] } })
47
47
  end
48
+
48
49
  # Increase the level from DEBUG in order to avoid excessive logging to the screen
49
50
  Mongo::Logger.logger.level = Logger::WARN
50
51
  end
@@ -21,6 +21,13 @@ module Support
21
21
  post2 = category2.posts.create(name: 'second', writer: writer2)
22
22
  post3 = category2.posts.create(name: 'third', writer: writer2)
23
23
 
24
+ deal1 = Deal.new(name: 'Deal 1')
25
+ deal1.posts << post1
26
+ deal1.posts << post2
27
+ deal2 = Deal.new(name: 'Deal 2')
28
+ post1.deals << deal1
29
+ post1.deals << deal2
30
+
24
31
  comment1 = post1.comments.create(name: 'first', author: writer1)
25
32
  comment2 = post1.comments.create(name: 'first2', author: writer1)
26
33
  comment3 = post1.comments.create(name: 'first3', author: writer1)
@@ -85,9 +92,16 @@ module Support
85
92
  page3 = Page.create(name: 'page3', parent_id: folder2.id, author_id: author2.id)
86
93
  page4 = Page.create(name: 'page4', parent_id: folder2.id, author_id: author2.id)
87
94
 
95
+ role1 = Role.create(name: 'Admin')
96
+ role2 = Role.create(name: 'User')
97
+
88
98
  user1 = User.create(name: 'user1', category: category1)
89
99
  user2 = User.create(name: 'user2', category: category1)
90
100
 
101
+ user1.roles << role1
102
+ user1.roles << role2
103
+ user2.roles << role2
104
+
91
105
  submission1 = user1.create_submission(name: 'submission1')
92
106
  submission2 = user2.create_submission(name: 'submission2')
93
107
 
@@ -95,6 +109,9 @@ module Support
95
109
  submission1.replies.create(name: 'reply2')
96
110
  submission2.replies.create(name: 'reply3')
97
111
  submission2.replies.create(name: 'reply4')
112
+
113
+ submission1.create_attachment(file_name: 'submission1 file')
114
+ submission2.create_attachment(file_name: 'submission2 file')
98
115
  end
99
116
 
100
117
  def setup_db
@@ -153,6 +170,11 @@ module Support
153
170
  t.column :hotel_id, :integer
154
171
  end
155
172
 
173
+ create_table :deals_posts do |t|
174
+ t.column :deal_id, :integer
175
+ t.column :post_id, :integer
176
+ end
177
+
156
178
  create_table :documents do |t|
157
179
  t.string :name
158
180
  t.string :type
@@ -231,6 +253,17 @@ module Support
231
253
  t.column :submission_id, :integer
232
254
  end
233
255
 
256
+ create_table :roles do |t|
257
+ t.column :name, :string
258
+ t.column :resource_id, :integer
259
+ t.column :resource_type, :string
260
+ end
261
+
262
+ create_table :roles_users do |t|
263
+ t.column :role_id, :integer
264
+ t.column :user_id, :integer
265
+ end
266
+
234
267
  create_table :submissions do |t|
235
268
  t.column :name, :string
236
269
  t.column :user_id, :integer
@@ -240,6 +273,11 @@ module Support
240
273
  t.column :name, :string
241
274
  t.column :category_id, :integer
242
275
  end
276
+
277
+ create_table :attachments do |t|
278
+ t.column :file_name, :string
279
+ t.column :submission_id, :integer
280
+ end
243
281
  end
244
282
  end
245
283
  end
data/test.sh CHANGED
@@ -1,5 +1,7 @@
1
1
  #bundle update rails && bundle exec rspec spec
2
2
  #BUNDLE_GEMFILE=Gemfile.mongoid bundle update mongoid && BUNDLE_GEMFILE=Gemfile.mongoid bundle exec rspec spec
3
+ BUNDLE_GEMFILE=Gemfile.rails-7.0 bundle && BUNDLE_GEMFILE=Gemfile.rails-7.0 bundle exec rspec spec
4
+ BUNDLE_GEMFILE=Gemfile.rails-6.1 bundle && BUNDLE_GEMFILE=Gemfile.rails-6.1 bundle exec rspec spec
3
5
  BUNDLE_GEMFILE=Gemfile.rails-6.0 bundle && BUNDLE_GEMFILE=Gemfile.rails-6.0 bundle exec rspec spec
4
6
  BUNDLE_GEMFILE=Gemfile.rails-5.2 bundle && BUNDLE_GEMFILE=Gemfile.rails-5.2 bundle exec rspec spec
5
7
  BUNDLE_GEMFILE=Gemfile.rails-5.1 bundle && BUNDLE_GEMFILE=Gemfile.rails-5.1 bundle exec rspec spec