bullet 6.1.0 → 7.0.4

Sign up to get free protection for your applications and to get access to all the features.
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