bullet 4.14.0 → 4.14.10

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/.travis.yml +28 -1
  3. data/CHANGELOG.md +49 -0
  4. data/Gemfile.mongoid +0 -2
  5. data/Gemfile.mongoid-2.4 +2 -4
  6. data/Gemfile.mongoid-2.5 +2 -4
  7. data/Gemfile.mongoid-2.6 +1 -3
  8. data/Gemfile.mongoid-2.7 +2 -4
  9. data/Gemfile.mongoid-2.8 +2 -4
  10. data/Gemfile.mongoid-3.0 +2 -4
  11. data/Gemfile.mongoid-3.1 +2 -4
  12. data/Gemfile.mongoid-4.0 +1 -3
  13. data/Gemfile.mongoid-5.0 +17 -0
  14. data/Gemfile.rails-3.0 +1 -3
  15. data/Gemfile.rails-3.1 +1 -3
  16. data/Gemfile.rails-3.2 +1 -3
  17. data/Gemfile.rails-4.0 +1 -3
  18. data/Gemfile.rails-4.1 +1 -3
  19. data/Gemfile.rails-4.2 +1 -3
  20. data/README.md +29 -2
  21. data/bullet.gemspec +1 -1
  22. data/lib/bullet/active_record3.rb +65 -35
  23. data/lib/bullet/active_record3x.rb +54 -32
  24. data/lib/bullet/active_record4.rb +62 -30
  25. data/lib/bullet/active_record41.rb +63 -32
  26. data/lib/bullet/active_record42.rb +105 -40
  27. data/lib/bullet/dependency.rb +6 -0
  28. data/lib/bullet/detector/association.rb +16 -16
  29. data/lib/bullet/detector/counter_cache.rb +13 -13
  30. data/lib/bullet/detector/n_plus_one_query.rb +30 -27
  31. data/lib/bullet/detector/unused_eager_loading.rb +1 -1
  32. data/lib/bullet/ext/object.rb +3 -1
  33. data/lib/bullet/mongoid5x.rb +56 -0
  34. data/lib/bullet/notification/base.rb +1 -1
  35. data/lib/bullet/notification/n_plus_one_query.rb +6 -0
  36. data/lib/bullet/rack.rb +19 -11
  37. data/lib/bullet/version.rb +1 -1
  38. data/lib/bullet.rb +23 -10
  39. data/spec/bullet/detector/counter_cache_spec.rb +8 -8
  40. data/spec/bullet/detector/n_plus_one_query_spec.rb +27 -27
  41. data/spec/bullet/ext/object_spec.rb +6 -0
  42. data/spec/bullet/notification/base_spec.rb +2 -2
  43. data/spec/bullet/notification/n_plus_one_query_spec.rb +3 -3
  44. data/spec/bullet/notification/unused_eager_loading_spec.rb +1 -1
  45. data/spec/bullet/rack_spec.rb +3 -3
  46. data/spec/bullet_spec.rb +47 -0
  47. data/spec/integration/active_record3/association_spec.rb +11 -2
  48. data/spec/integration/active_record4/association_spec.rb +58 -3
  49. data/spec/integration/counter_cache_spec.rb +8 -1
  50. data/spec/models/category.rb +4 -1
  51. data/spec/models/comment.rb +2 -0
  52. data/spec/models/post.rb +7 -2
  53. data/spec/models/reply.rb +3 -0
  54. data/spec/models/submission.rb +1 -1
  55. data/spec/spec_helper.rb +3 -2
  56. data/spec/support/mongo_seed.rb +13 -0
  57. data/spec/support/sqlite_seed.rb +14 -6
  58. data/test.sh +1 -0
  59. data/update.sh +15 -0
  60. metadata +12 -7
data/lib/bullet.rb CHANGED
@@ -16,6 +16,9 @@ module Bullet
16
16
  autoload :Registry, 'bullet/registry'
17
17
  autoload :NotificationCollector, 'bullet/notification_collector'
18
18
 
19
+ BULLET_DEBUG = 'BULLET_DEBUG'.freeze
20
+ TRUE = 'true'.freeze
21
+
19
22
  if defined? Rails::Railtie
20
23
  class BulletRailtie < Rails::Railtie
21
24
  initializer "bullet.configure_rails_initialization" do |app|
@@ -29,7 +32,9 @@ module Bullet
29
32
  attr_reader :notification_collector, :whitelist
30
33
  attr_accessor :add_footer, :orm_pathches_applied
31
34
 
32
- delegate :alert=, :console=, :growl=, :rails_logger=, :xmpp=, :airbrake=, :bugsnag=, :to => UniformNotifier
35
+ available_notifiers = UniformNotifier::AVAILABLE_NOTIFIERS.map { |notifier| "#{notifier}=" }
36
+ available_notifiers << { :to => UniformNotifier }
37
+ delegate *available_notifiers
33
38
 
34
39
  def raise=(should_raise)
35
40
  UniformNotifier.raise=(should_raise ? Notification::UnoptimizedQueryError : false)
@@ -72,6 +77,7 @@ module Bullet
72
77
  end
73
78
 
74
79
  def add_whitelist(options)
80
+ reset_whitelist
75
81
  @whitelist[options[:type]][options[:class_name].classify] ||= []
76
82
  @whitelist[options[:type]][options[:class_name].classify] << options[:association].to_sym
77
83
  end
@@ -81,7 +87,11 @@ module Bullet
81
87
  end
82
88
 
83
89
  def reset_whitelist
84
- @whitelist = {:n_plus_one_query => {}, :unused_eager_loading => {}, :counter_cache => {}}
90
+ @whitelist ||= {:n_plus_one_query => {}, :unused_eager_loading => {}, :counter_cache => {}}
91
+ end
92
+
93
+ def clear_whitelist
94
+ @whitelist = nil
85
95
  end
86
96
 
87
97
  def bullet_logger=(active)
@@ -96,7 +106,7 @@ module Bullet
96
106
  end
97
107
 
98
108
  def debug(title, message)
99
- puts "[Bullet][#{title}] #{message}" if ENV['BULLET_DEBUG'] == 'true'
109
+ puts "[Bullet][#{title}] #{message}" if ENV[BULLET_DEBUG] == TRUE
100
110
  end
101
111
 
102
112
  def start_request
@@ -130,7 +140,7 @@ module Bullet
130
140
  end
131
141
 
132
142
  def start?
133
- Thread.current[:bullet_start]
143
+ enable? && Thread.current[:bullet_start]
134
144
  end
135
145
 
136
146
  def notification_collector
@@ -160,7 +170,7 @@ module Bullet
160
170
 
161
171
  def footer_info
162
172
  info = []
163
- for_each_active_notifier_with_notification do |notification|
173
+ notification_collector.collection.each do |notification|
164
174
  info << notification.short_notice
165
175
  end
166
176
  info
@@ -176,14 +186,17 @@ module Bullet
176
186
  end
177
187
 
178
188
  def profile
179
- Bullet.start_request if Bullet.enable?
189
+ if Bullet.enable?
190
+ begin
191
+ Bullet.start_request
180
192
 
181
- yield
193
+ yield
182
194
 
183
- if Bullet.enable? && Bullet.notification?
184
- Bullet.perform_out_of_channel_notifications
195
+ Bullet.perform_out_of_channel_notifications if Bullet.notification?
196
+ ensure
197
+ Bullet.end_request
198
+ end
185
199
  end
186
- Bullet.end_request if Bullet.enable?
187
200
  end
188
201
 
189
202
  private
@@ -10,13 +10,13 @@ module Bullet
10
10
 
11
11
  context ".add_counter_cache" do
12
12
  it "should create notification if conditions met" do
13
- expect(CounterCache).to receive(:conditions_met?).with(@post1.bullet_key, [:comments]).and_return(true)
13
+ expect(CounterCache).to receive(:conditions_met?).with(@post1, [:comments]).and_return(true)
14
14
  expect(CounterCache).to receive(:create_notification).with("Post", [:comments])
15
15
  CounterCache.add_counter_cache(@post1, [:comments])
16
16
  end
17
17
 
18
18
  it "should not create notification if conditions not met" do
19
- expect(CounterCache).to receive(:conditions_met?).with(@post1.bullet_key, [:comments]).and_return(false)
19
+ expect(CounterCache).to receive(:conditions_met?).with(@post1, [:comments]).and_return(false)
20
20
  expect(CounterCache).to receive(:create_notification).never
21
21
  CounterCache.add_counter_cache(@post1, [:comments])
22
22
  end
@@ -25,30 +25,30 @@ module Bullet
25
25
  context ".add_possible_objects" do
26
26
  it "should add possible objects" do
27
27
  CounterCache.add_possible_objects([@post1, @post2])
28
- expect(CounterCache.send(:possible_objects)).to be_include(@post1.bullet_key)
29
- expect(CounterCache.send(:possible_objects)).to be_include(@post2.bullet_key)
28
+ expect(CounterCache.possible_objects).to be_include(@post1.bullet_key)
29
+ expect(CounterCache.possible_objects).to be_include(@post2.bullet_key)
30
30
  end
31
31
 
32
32
  it "should add impossible object" do
33
33
  CounterCache.add_impossible_object(@post1)
34
- expect(CounterCache.send(:impossible_objects)).to be_include(@post1.bullet_key)
34
+ expect(CounterCache.impossible_objects).to be_include(@post1.bullet_key)
35
35
  end
36
36
  end
37
37
 
38
38
  context ".conditions_met?" do
39
39
  it "should be true when object is possible, not impossible" do
40
40
  CounterCache.add_possible_objects(@post1)
41
- expect(CounterCache.send(:conditions_met?, @post1.bullet_key, :associations)).to eq true
41
+ expect(CounterCache.conditions_met?(@post1, :associations)).to eq true
42
42
  end
43
43
 
44
44
  it "should be false when object is not possible" do
45
- expect(CounterCache.send(:conditions_met?, @post1.bullet_key, :associations)).to eq false
45
+ expect(CounterCache.conditions_met?(@post1, :associations)).to eq false
46
46
  end
47
47
 
48
48
  it "should be true when object is possible, and impossible" do
49
49
  CounterCache.add_possible_objects(@post1)
50
50
  CounterCache.add_impossible_object(@post1)
51
- expect(CounterCache.send(:conditions_met?, @post1.bullet_key, :associations)).to eq false
51
+ expect(CounterCache.conditions_met?(@post1, :associations)).to eq false
52
52
  end
53
53
  end
54
54
  end
@@ -18,69 +18,69 @@ module Bullet
18
18
  context ".possible?" do
19
19
  it "should be true if possible_objects contain" do
20
20
  NPlusOneQuery.add_possible_objects(@post)
21
- expect(NPlusOneQuery.send(:possible?, @post.bullet_key)).to eq true
21
+ expect(NPlusOneQuery.possible?(@post)).to eq true
22
22
  end
23
23
  end
24
24
 
25
25
  context ".impossible?" do
26
26
  it "should be true if impossible_objects contain" do
27
27
  NPlusOneQuery.add_impossible_object(@post)
28
- expect(NPlusOneQuery.send(:impossible?, @post.bullet_key)).to eq true
28
+ expect(NPlusOneQuery.impossible?(@post)).to eq true
29
29
  end
30
30
  end
31
31
 
32
32
  context ".association?" do
33
33
  it "should be true if object, associations pair is already existed" do
34
34
  NPlusOneQuery.add_object_associations(@post, :association)
35
- expect(NPlusOneQuery.send(:association?, @post.bullet_key, :association)).to eq true
35
+ expect(NPlusOneQuery.association?(@post, :association)).to eq true
36
36
  end
37
37
 
38
38
  it "should be false if object, association pair is not existed" do
39
39
  NPlusOneQuery.add_object_associations(@post, :association1)
40
- expect(NPlusOneQuery.send(:association?, @post.bullet_key, :associatio2)).to eq false
40
+ expect(NPlusOneQuery.association?(@post, :associatio2)).to eq false
41
41
  end
42
42
  end
43
43
 
44
44
  context ".conditions_met?" do
45
45
  it "should be true if object is possible, not impossible and object, associations pair is not already existed" do
46
- allow(NPlusOneQuery).to receive(:possible?).with(@post.bullet_key).and_return(true)
47
- allow(NPlusOneQuery).to receive(:impossible?).with(@post.bullet_key).and_return(false)
48
- allow(NPlusOneQuery).to receive(:association?).with(@post.bullet_key, :associations).and_return(false)
49
- expect(NPlusOneQuery.send(:conditions_met?, @post.bullet_key, :associations)).to eq true
46
+ allow(NPlusOneQuery).to receive(:possible?).with(@post).and_return(true)
47
+ allow(NPlusOneQuery).to receive(:impossible?).with(@post).and_return(false)
48
+ allow(NPlusOneQuery).to receive(:association?).with(@post, :associations).and_return(false)
49
+ expect(NPlusOneQuery.conditions_met?(@post, :associations)).to eq true
50
50
  end
51
51
 
52
52
  it "should be false if object is not possible, not impossible and object, associations pair is not already existed" do
53
- allow(NPlusOneQuery).to receive(:possible?).with(@post.bullet_key).and_return(false)
54
- allow(NPlusOneQuery).to receive(:impossible?).with(@post.bullet_key).and_return(false)
55
- allow(NPlusOneQuery).to receive(:association?).with(@post.bullet_key, :associations).and_return(false)
56
- expect(NPlusOneQuery.send(:conditions_met?, @post.bullet_key, :associations)).to eq false
53
+ allow(NPlusOneQuery).to receive(:possible?).with(@post).and_return(false)
54
+ allow(NPlusOneQuery).to receive(:impossible?).with(@post).and_return(false)
55
+ allow(NPlusOneQuery).to receive(:association?).with(@post, :associations).and_return(false)
56
+ expect(NPlusOneQuery.conditions_met?(@post, :associations)).to eq false
57
57
  end
58
58
 
59
59
  it "should be false if object is possible, but impossible and object, associations pair is not already existed" do
60
- allow(NPlusOneQuery).to receive(:possible?).with(@post.bullet_key).and_return(true)
61
- allow(NPlusOneQuery).to receive(:impossible?).with(@post.bullet_key).and_return(true)
62
- allow(NPlusOneQuery).to receive(:association?).with(@post.bullet_key, :associations).and_return(false)
63
- expect(NPlusOneQuery.send(:conditions_met?, @post.bullet_key, :associations)).to eq false
60
+ allow(NPlusOneQuery).to receive(:possible?).with(@post).and_return(true)
61
+ allow(NPlusOneQuery).to receive(:impossible?).with(@post).and_return(true)
62
+ allow(NPlusOneQuery).to receive(:association?).with(@post, :associations).and_return(false)
63
+ expect(NPlusOneQuery.conditions_met?(@post, :associations)).to eq false
64
64
  end
65
65
 
66
66
  it "should be false if object is possible, not impossible and object, associations pair is already existed" do
67
- allow(NPlusOneQuery).to receive(:possible?).with(@post.bullet_key).and_return(true)
68
- allow(NPlusOneQuery).to receive(:impossible?).with(@post.bullet_key).and_return(false)
69
- allow(NPlusOneQuery).to receive(:association?).with(@post.bullet_key, :associations).and_return(true)
70
- expect(NPlusOneQuery.send(:conditions_met?, @post.bullet_key, :associations)).to eq false
67
+ allow(NPlusOneQuery).to receive(:possible?).with(@post).and_return(true)
68
+ allow(NPlusOneQuery).to receive(:impossible?).with(@post).and_return(false)
69
+ allow(NPlusOneQuery).to receive(:association?).with(@post, :associations).and_return(true)
70
+ expect(NPlusOneQuery.conditions_met?(@post, :associations)).to eq false
71
71
  end
72
72
  end
73
73
 
74
74
  context ".call_association" do
75
75
  it "should create notification if conditions met" do
76
- expect(NPlusOneQuery).to receive(:conditions_met?).with(@post.bullet_key, :association).and_return(true)
76
+ expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
77
77
  expect(NPlusOneQuery).to receive(:caller_in_project).and_return(["caller"])
78
78
  expect(NPlusOneQuery).to receive(:create_notification).with(["caller"], "Post", :association)
79
79
  NPlusOneQuery.call_association(@post, :association)
80
80
  end
81
81
 
82
82
  it "should not create notification if conditions not met" do
83
- expect(NPlusOneQuery).to receive(:conditions_met?).with(@post.bullet_key, :association).and_return(false)
83
+ expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(false)
84
84
  expect(NPlusOneQuery).not_to receive(:caller_in_project!)
85
85
  expect(NPlusOneQuery).not_to receive(:create_notification).with("Post", :association)
86
86
  NPlusOneQuery.call_association(@post, :association)
@@ -93,7 +93,7 @@ module Bullet
93
93
  not_in_project = '/def/def.rb'
94
94
 
95
95
  expect(NPlusOneQuery).to receive(:caller).and_return([in_project, not_in_project])
96
- expect(NPlusOneQuery).to receive(:conditions_met?).with(@post.bullet_key, :association).and_return(true)
96
+ expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
97
97
  expect(NPlusOneQuery).to receive(:create_notification).with([in_project], "Post", :association)
98
98
  NPlusOneQuery.call_association(@post, :association)
99
99
  end
@@ -108,7 +108,7 @@ module Bullet
108
108
  excluded_gem = '/ghi/ghi.rb'
109
109
 
110
110
  expect(NPlusOneQuery).to receive(:caller).and_return([in_project, included_gem, excluded_gem])
111
- expect(NPlusOneQuery).to receive(:conditions_met?).with(@post.bullet_key, :association).and_return(true)
111
+ expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
112
112
  expect(NPlusOneQuery).to receive(:create_notification).with([in_project, included_gem], "Post", :association)
113
113
  NPlusOneQuery.call_association(@post, :association)
114
114
  end
@@ -118,8 +118,8 @@ module Bullet
118
118
  context ".add_possible_objects" do
119
119
  it "should add possible objects" do
120
120
  NPlusOneQuery.add_possible_objects([@post, @post2])
121
- expect(NPlusOneQuery.send(:possible_objects)).to be_include(@post.bullet_key)
122
- expect(NPlusOneQuery.send(:possible_objects)).to be_include(@post2.bullet_key)
121
+ expect(NPlusOneQuery.possible_objects).to be_include(@post.bullet_key)
122
+ expect(NPlusOneQuery.possible_objects).to be_include(@post2.bullet_key)
123
123
  end
124
124
 
125
125
  it "should not raise error if object is nil" do
@@ -130,7 +130,7 @@ module Bullet
130
130
  context ".add_impossible_object" do
131
131
  it "should add impossible object" do
132
132
  NPlusOneQuery.add_impossible_object(@post)
133
- expect(NPlusOneQuery.send(:impossible_objects)).to be_include(@post.bullet_key)
133
+ expect(NPlusOneQuery.impossible_objects).to be_include(@post.bullet_key)
134
134
  end
135
135
  end
136
136
  end
@@ -27,5 +27,11 @@ describe Object do
27
27
  expect(post.primary_key_value).to eq(post.name)
28
28
  Post.primary_key = 'id'
29
29
  end
30
+
31
+ it "should return value for multiple primary keys" do
32
+ post = Post.first
33
+ allow(Post).to receive(:primary_keys).and_return([:category_id, :writer_id])
34
+ expect(post.primary_key_value).to eq("#{post.category_id},#{post.writer_id}")
35
+ end
30
36
  end
31
37
  end
@@ -30,14 +30,14 @@ module Bullet
30
30
  end
31
31
 
32
32
  it "should return blank if no user available" do
33
- temp_env_variable("USER","") do
33
+ temp_env_variable("USER", "") do
34
34
  expect(subject).to receive(:`).with("whoami").and_return("")
35
35
  expect(subject.whoami).to eq("")
36
36
  end
37
37
  end
38
38
 
39
39
  it "should return blank if whoami is not available" do
40
- temp_env_variable("USER","") do
40
+ temp_env_variable("USER", "") do
41
41
  expect(subject).to receive(:`).with("whoami").and_raise(Errno::ENOENT)
42
42
  expect(subject.whoami).to eq("")
43
43
  end
@@ -5,9 +5,9 @@ module Bullet
5
5
  describe NPlusOneQuery do
6
6
  subject { NPlusOneQuery.new([["caller1", "caller2"]], Post, [:comments, :votes], "path") }
7
7
 
8
- it { expect(subject.body_with_caller).to eq(" Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2\n") }
9
- it { expect([ subject.body_with_caller, subject.body_with_caller]).to eq([ " Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2\n", " Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2\n" ]) }
10
- it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]") }
8
+ it { expect(subject.body_with_caller).to eq(" Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2\n") }
9
+ it { expect([subject.body_with_caller, subject.body_with_caller]).to eq([ " Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2\n", " Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2\n" ]) }
10
+ it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]") }
11
11
  it { expect(subject.title).to eq("N+1 Query in path") }
12
12
  end
13
13
  end
@@ -5,7 +5,7 @@ module Bullet
5
5
  describe UnusedEagerLoading do
6
6
  subject { UnusedEagerLoading.new(Post, [:comments, :votes], "path") }
7
7
 
8
- it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Remove from your finder: :include => [:comments, :votes]") }
8
+ it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Remove from your finder: :includes => [:comments, :votes]") }
9
9
  it { expect(subject.title).to eq("Unused Eager Loading in path") }
10
10
  end
11
11
  end
@@ -65,19 +65,19 @@ module Bullet
65
65
  end
66
66
 
67
67
  it "should change response body if notification is active" do
68
- expect(Bullet).to receive(:notification?).and_return(true).twice
68
+ expect(Bullet).to receive(:notification?).and_return(true)
69
69
  expect(Bullet).to receive(:gather_inline_notifications).and_return("<bullet></bullet>")
70
70
  expect(Bullet).to receive(:perform_out_of_channel_notifications)
71
71
  status, headers, response = middleware.call([200, {"Content-Type" => "text/html"}])
72
72
  expect(headers["Content-Length"]).to eq("56")
73
- expect(response).to eq(["<html><head></head><body></body></html><bullet></bullet>"])
73
+ expect(response).to eq(["<html><head></head><body><bullet></bullet></body></html>"])
74
74
  end
75
75
 
76
76
  it "should set the right Content-Length if response body contains accents" do
77
77
  response = Support::ResponseDouble.new
78
78
  response.body = "<html><head></head><body>é</body></html>"
79
79
  app.response = response
80
- expect(Bullet).to receive(:notification?).and_return(true).twice
80
+ expect(Bullet).to receive(:notification?).and_return(true)
81
81
  expect(Bullet).to receive(:gather_inline_notifications).and_return("<bullet></bullet>")
82
82
  status, headers, response = middleware.call([200, {"Content-Type" => "text/html"}])
83
83
  expect(headers["Content-Length"]).to eq("58")
data/spec/bullet_spec.rb CHANGED
@@ -38,4 +38,51 @@ describe Bullet, focused: true do
38
38
  end
39
39
  end
40
40
  end
41
+
42
+ describe '#start?' do
43
+ context 'when bullet is disabled' do
44
+ before(:each) do
45
+ Bullet.enable = false
46
+ end
47
+
48
+ it 'should not be started' do
49
+ expect(Bullet).not_to be_start
50
+ end
51
+ end
52
+ end
53
+
54
+ describe '#debug' do
55
+ before(:each) do
56
+ $stdout = StringIO.new
57
+ end
58
+
59
+ after(:each) do
60
+ $stdout = STDOUT
61
+ end
62
+
63
+ context 'when debug is enabled' do
64
+ before(:each) do
65
+ ENV['BULLET_DEBUG'] = 'true'
66
+ end
67
+
68
+ after(:each) do
69
+ ENV['BULLET_DEBUG'] = 'false'
70
+ end
71
+
72
+ it 'should output debug information' do
73
+ Bullet.debug('debug_message', 'this is helpful information')
74
+
75
+ expect($stdout.string)
76
+ .to eq("[Bullet][debug_message] this is helpful information\n")
77
+ end
78
+ end
79
+
80
+ context 'when debug is disabled' do
81
+ it 'should output debug information' do
82
+ Bullet.debug('debug_message', 'this is helpful information')
83
+
84
+ expect($stdout.string).to be_empty
85
+ end
86
+ end
87
+ end
41
88
  end
@@ -335,6 +335,15 @@ if !mongoid? && active_record3?
335
335
  expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Comment, :post)
336
336
  end
337
337
 
338
+ it "should not detect non preload association with only one comment" do
339
+ Comment.first.post.category.name
340
+
341
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
342
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
343
+
344
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
345
+ end
346
+
338
347
  it "should detect non preload association with post => category" do
339
348
  Comment.includes(:post).each do |comment|
340
349
  comment.post.category.name
@@ -615,7 +624,7 @@ if !mongoid? && active_record3?
615
624
 
616
625
  context "whitelist n plus one query" do
617
626
  before { Bullet.add_whitelist :type => :n_plus_one_query, :class_name => "Post", :association => :comments }
618
- after { Bullet.reset_whitelist }
627
+ after { Bullet.clear_whitelist }
619
628
 
620
629
  it "should not detect n plus one query" do
621
630
  Post.all.each do |post|
@@ -638,7 +647,7 @@ if !mongoid? && active_record3?
638
647
 
639
648
  context "whitelist unused eager loading" do
640
649
  before { Bullet.add_whitelist :type => :unused_eager_loading, :class_name => "Post", :association => :comments }
641
- after { Bullet.reset_whitelist }
650
+ after { Bullet.clear_whitelist }
642
651
 
643
652
  it "should not detect unused eager loading" do
644
653
  Post.includes(:comments).map(&:name)
@@ -219,6 +219,20 @@ if !mongoid? && active_record4?
219
219
 
220
220
  expect(Bullet::Detector::Association).to be_completely_preloading_associations
221
221
  end
222
+
223
+ it "should not detect unused preload with category => posts" do
224
+ category = Category.first
225
+ category.draft_post.destroy!
226
+ post = category.draft_post
227
+ post.update_attributes!(link: true)
228
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
229
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
230
+
231
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
232
+
233
+ Support::SqliteSeed.setup_db
234
+ Support::SqliteSeed.seed_db
235
+ end
222
236
  end
223
237
 
224
238
  context "category => posts => writer" do
@@ -313,7 +327,7 @@ if !mongoid? && active_record4?
313
327
  expect(Bullet::Detector::Association).to be_completely_preloading_associations
314
328
  end
315
329
 
316
- it "should detect unused preload with comments => post" do
330
+ it "should detect unused preload with comment => post" do
317
331
  Comment.includes(:post).map(&:name)
318
332
  Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
319
333
  expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Comment, :post)
@@ -333,6 +347,15 @@ if !mongoid? && active_record4?
333
347
  expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Comment, :post)
334
348
  end
335
349
 
350
+ it "should not detect non preload association with only one comment" do
351
+ Comment.first.post.category.name
352
+
353
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
354
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
355
+
356
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
357
+ end
358
+
336
359
  it "should detect non preload association with post => category" do
337
360
  Comment.includes(:post).each do |comment|
338
361
  comment.post.category.name
@@ -515,6 +538,17 @@ if !mongoid? && active_record4?
515
538
  end
516
539
  end
517
540
 
541
+ describe Bullet::Detector::Association, "has_one => has_many" do
542
+ it "should not detect preload association" do
543
+ user = User.first
544
+ user.submission.replies.map(&:name)
545
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
546
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
547
+
548
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
549
+ end
550
+ end
551
+
518
552
  describe Bullet::Detector::Association, "call one association that in possible objects" do
519
553
  it "should not detect preload association" do
520
554
  Post.all
@@ -526,6 +560,27 @@ if !mongoid? && active_record4?
526
560
  end
527
561
  end
528
562
 
563
+ describe Bullet::Detector::Association, "query immediately after creation" do
564
+ context "document => children" do
565
+ it 'should not detect non preload associations' do
566
+ document1 = Document.new
567
+ document1.children.build
568
+ document1.save
569
+
570
+ document2 = Document.new(parent: document1)
571
+ document2.save
572
+ document2.parent
573
+
574
+ document1.children.each.first
575
+
576
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
577
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
578
+
579
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
580
+ end
581
+ end
582
+ end
583
+
529
584
  describe Bullet::Detector::Association, "STI" do
530
585
  context "page => author" do
531
586
  it "should detect non preload associations" do
@@ -613,7 +668,7 @@ if !mongoid? && active_record4?
613
668
 
614
669
  context "whitelist n plus one query" do
615
670
  before { Bullet.add_whitelist :type => :n_plus_one_query, :class_name => "Post", :association => :comments }
616
- after { Bullet.reset_whitelist }
671
+ after { Bullet.clear_whitelist }
617
672
 
618
673
  it "should not detect n plus one query" do
619
674
  Post.all.each do |post|
@@ -636,7 +691,7 @@ if !mongoid? && active_record4?
636
691
 
637
692
  context "whitelist unused eager loading" do
638
693
  before { Bullet.add_whitelist :type => :unused_eager_loading, :class_name => "Post", :association => :comments }
639
- after { Bullet.reset_whitelist }
694
+ after { Bullet.clear_whitelist }
640
695
 
641
696
  it "should not detect unused eager loading" do
642
697
  Post.includes(:comments).map(&:name)
@@ -29,6 +29,13 @@ if !mongoid? && active_record?
29
29
  expect(Bullet.collected_counter_cache_notifications).to be_empty
30
30
  end
31
31
 
32
+ it "should need counter cache for has_many through" do
33
+ Client.all.each do |client|
34
+ client.firms.size
35
+ end
36
+ expect(Bullet.collected_counter_cache_notifications).not_to be_empty
37
+ end
38
+
32
39
  it "should not need counter cache with part of cities" do
33
40
  Country.all.each do |country|
34
41
  country.cities.where(:name => 'first').size
@@ -50,7 +57,7 @@ if !mongoid? && active_record?
50
57
 
51
58
  context "whitelist" do
52
59
  before { Bullet.add_whitelist :type => :counter_cache, :class_name => "Country", :association => :cities }
53
- after { Bullet.reset_whitelist }
60
+ after { Bullet.clear_whitelist }
54
61
 
55
62
  it "should not detect counter cache" do
56
63
  Country.all.each do |country|
@@ -2,6 +2,9 @@ class Category < ActiveRecord::Base
2
2
  has_many :posts, inverse_of: :category
3
3
  has_many :entries
4
4
 
5
- has_many :submissions
6
5
  has_many :users
6
+
7
+ def draft_post
8
+ posts.draft.first_or_create
9
+ end
7
10
  end
@@ -1,4 +1,6 @@
1
1
  class Comment < ActiveRecord::Base
2
2
  belongs_to :post, inverse_of: :comments
3
3
  belongs_to :author, class_name: "BaseUser"
4
+
5
+ validates :post, presence: true
4
6
  end
data/spec/models/post.rb CHANGED
@@ -1,10 +1,15 @@
1
1
  class Post < ActiveRecord::Base
2
- extend Bullet::Dependency
3
-
4
2
  belongs_to :category, inverse_of: :posts
5
3
  belongs_to :writer
6
4
  has_many :comments, inverse_of: :post
7
5
 
6
+ validates :category, presence: true
7
+
8
8
  scope :preload_comments, -> { includes(:comments) }
9
9
  scope :in_category_name, ->(name) { where(['categories.name = ?', name]).includes(:category) }
10
+ scope :draft, -> { where(active: false) }
11
+
12
+ def link=(*)
13
+ comments.new
14
+ end
10
15
  end
@@ -0,0 +1,3 @@
1
+ class Reply < ActiveRecord::Base
2
+ belongs_to :submission
3
+ end
@@ -1,4 +1,4 @@
1
1
  class Submission < ActiveRecord::Base
2
- belongs_to :category
3
2
  belongs_to :user
3
+ has_many :replies
4
4
  end
data/spec/spec_helper.rb CHANGED
@@ -56,11 +56,12 @@ if active_record?
56
56
  Support::SqliteSeed.seed_db
57
57
  end
58
58
 
59
- config.before(:each) do
59
+ config.before(:example) do
60
60
  Bullet.start_request
61
+ Bullet.enable = true
61
62
  end
62
63
 
63
- config.after(:each) do
64
+ config.after(:example) do
64
65
  Bullet.end_request
65
66
  end
66
67
  end
@@ -52,6 +52,19 @@ module Support
52
52
  }
53
53
  )
54
54
  end
55
+ elsif Mongoid::VERSION =~ /\A5/
56
+ Mongoid.configure do |config|
57
+ config.load_configuration(
58
+ clients: {
59
+ default: {
60
+ database: "bullet",
61
+ hosts: [ "localhost:27017" ]
62
+ }
63
+ }
64
+ )
65
+ end
66
+ # Increase the level from DEBUG in order to avoid excessive logging to the screen
67
+ Mongo::Logger.logger.level = Logger::WARN
55
68
  end
56
69
  end
57
70