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.
- checksums.yaml +4 -4
- data/.travis.yml +28 -1
- data/CHANGELOG.md +49 -0
- data/Gemfile.mongoid +0 -2
- data/Gemfile.mongoid-2.4 +2 -4
- data/Gemfile.mongoid-2.5 +2 -4
- data/Gemfile.mongoid-2.6 +1 -3
- data/Gemfile.mongoid-2.7 +2 -4
- data/Gemfile.mongoid-2.8 +2 -4
- data/Gemfile.mongoid-3.0 +2 -4
- data/Gemfile.mongoid-3.1 +2 -4
- data/Gemfile.mongoid-4.0 +1 -3
- data/Gemfile.mongoid-5.0 +17 -0
- data/Gemfile.rails-3.0 +1 -3
- data/Gemfile.rails-3.1 +1 -3
- data/Gemfile.rails-3.2 +1 -3
- data/Gemfile.rails-4.0 +1 -3
- data/Gemfile.rails-4.1 +1 -3
- data/Gemfile.rails-4.2 +1 -3
- data/README.md +29 -2
- data/bullet.gemspec +1 -1
- data/lib/bullet/active_record3.rb +65 -35
- data/lib/bullet/active_record3x.rb +54 -32
- data/lib/bullet/active_record4.rb +62 -30
- data/lib/bullet/active_record41.rb +63 -32
- data/lib/bullet/active_record42.rb +105 -40
- data/lib/bullet/dependency.rb +6 -0
- data/lib/bullet/detector/association.rb +16 -16
- data/lib/bullet/detector/counter_cache.rb +13 -13
- data/lib/bullet/detector/n_plus_one_query.rb +30 -27
- data/lib/bullet/detector/unused_eager_loading.rb +1 -1
- data/lib/bullet/ext/object.rb +3 -1
- data/lib/bullet/mongoid5x.rb +56 -0
- data/lib/bullet/notification/base.rb +1 -1
- data/lib/bullet/notification/n_plus_one_query.rb +6 -0
- data/lib/bullet/rack.rb +19 -11
- data/lib/bullet/version.rb +1 -1
- data/lib/bullet.rb +23 -10
- data/spec/bullet/detector/counter_cache_spec.rb +8 -8
- data/spec/bullet/detector/n_plus_one_query_spec.rb +27 -27
- data/spec/bullet/ext/object_spec.rb +6 -0
- data/spec/bullet/notification/base_spec.rb +2 -2
- data/spec/bullet/notification/n_plus_one_query_spec.rb +3 -3
- data/spec/bullet/notification/unused_eager_loading_spec.rb +1 -1
- data/spec/bullet/rack_spec.rb +3 -3
- data/spec/bullet_spec.rb +47 -0
- data/spec/integration/active_record3/association_spec.rb +11 -2
- data/spec/integration/active_record4/association_spec.rb +58 -3
- data/spec/integration/counter_cache_spec.rb +8 -1
- data/spec/models/category.rb +4 -1
- data/spec/models/comment.rb +2 -0
- data/spec/models/post.rb +7 -2
- data/spec/models/reply.rb +3 -0
- data/spec/models/submission.rb +1 -1
- data/spec/spec_helper.rb +3 -2
- data/spec/support/mongo_seed.rb +13 -0
- data/spec/support/sqlite_seed.rb +14 -6
- data/test.sh +1 -0
- data/update.sh +15 -0
- 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
|
-
|
|
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
|
|
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[
|
|
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
|
-
|
|
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
|
-
|
|
189
|
+
if Bullet.enable?
|
|
190
|
+
begin
|
|
191
|
+
Bullet.start_request
|
|
180
192
|
|
|
181
|
-
|
|
193
|
+
yield
|
|
182
194
|
|
|
183
|
-
|
|
184
|
-
|
|
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
|
|
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
|
|
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.
|
|
29
|
-
expect(CounterCache.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
47
|
-
allow(NPlusOneQuery).to receive(:impossible?).with(@post
|
|
48
|
-
allow(NPlusOneQuery).to receive(:association?).with(@post
|
|
49
|
-
expect(NPlusOneQuery.
|
|
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
|
|
54
|
-
allow(NPlusOneQuery).to receive(:impossible?).with(@post
|
|
55
|
-
allow(NPlusOneQuery).to receive(:association?).with(@post
|
|
56
|
-
expect(NPlusOneQuery.
|
|
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
|
|
61
|
-
allow(NPlusOneQuery).to receive(:impossible?).with(@post
|
|
62
|
-
allow(NPlusOneQuery).to receive(:association?).with(@post
|
|
63
|
-
expect(NPlusOneQuery.
|
|
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
|
|
68
|
-
allow(NPlusOneQuery).to receive(:impossible?).with(@post
|
|
69
|
-
allow(NPlusOneQuery).to receive(:association?).with(@post
|
|
70
|
-
expect(NPlusOneQuery.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
122
|
-
expect(NPlusOneQuery.
|
|
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.
|
|
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: :
|
|
9
|
-
it { expect([
|
|
10
|
-
it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Add to your finder: :
|
|
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: :
|
|
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
|
data/spec/bullet/rack_spec.rb
CHANGED
|
@@ -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)
|
|
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
|
|
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)
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
60
|
+
after { Bullet.clear_whitelist }
|
|
54
61
|
|
|
55
62
|
it "should not detect counter cache" do
|
|
56
63
|
Country.all.each do |country|
|
data/spec/models/category.rb
CHANGED
data/spec/models/comment.rb
CHANGED
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
|
data/spec/models/submission.rb
CHANGED
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(:
|
|
59
|
+
config.before(:example) do
|
|
60
60
|
Bullet.start_request
|
|
61
|
+
Bullet.enable = true
|
|
61
62
|
end
|
|
62
63
|
|
|
63
|
-
config.after(:
|
|
64
|
+
config.after(:example) do
|
|
64
65
|
Bullet.end_request
|
|
65
66
|
end
|
|
66
67
|
end
|
data/spec/support/mongo_seed.rb
CHANGED
|
@@ -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
|
|