bullet 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/.travis.yml +4 -1
- data/Gemfile +3 -2
- data/Gemfile.lock +85 -69
- data/Guardfile +8 -0
- data/README.textile +2 -2
- data/lib/bullet/action_controller2.rb +4 -4
- data/lib/bullet/active_record2.rb +5 -6
- data/lib/bullet/active_record3.rb +2 -3
- data/lib/bullet/active_record31.rb +10 -12
- data/lib/bullet/detector/association.rb +27 -53
- data/lib/bullet/detector/counter.rb +34 -29
- data/lib/bullet/detector/n_plus_one_query.rb +47 -28
- data/lib/bullet/detector/unused_eager_association.rb +31 -30
- data/lib/bullet/notification/base.rb +14 -12
- data/lib/bullet/notification/n_plus_one_query.rb +6 -10
- data/lib/bullet/notification/unused_eager_loading.rb +1 -2
- data/lib/bullet/notification_collector.rb +1 -2
- data/lib/bullet/rack.rb +6 -3
- data/lib/bullet/registry/association.rb +4 -6
- data/lib/bullet/registry/base.rb +10 -7
- data/lib/bullet/registry/object.rb +6 -6
- data/lib/bullet/version.rb +1 -1
- data/lib/bullet.rb +13 -8
- data/perf/benchmark.rb +30 -12
- data/spec/bullet/detector/association_spec.rb +90 -0
- data/spec/bullet/detector/base_spec.rb +14 -0
- data/spec/bullet/detector/counter_spec.rb +65 -0
- data/spec/bullet/detector/n_plus_one_query_spec.rb +94 -0
- data/spec/bullet/detector/unused_eager_association_spec.rb +62 -0
- data/spec/bullet/notification/base_spec.rb +67 -0
- data/spec/bullet/notification/counter_cache_spec.rb +12 -0
- data/spec/bullet/notification/n_plus_one_query_spec.rb +13 -0
- data/spec/bullet/notification/unused_eager_loading_spec.rb +12 -0
- data/spec/bullet/notification_collector_spec.rb +32 -0
- data/spec/bullet/rack_spec.rb +80 -24
- data/spec/bullet/registry/association_spec.rb +26 -0
- data/spec/bullet/registry/base_spec.rb +44 -0
- data/spec/bullet/registry/object_spec.rb +25 -0
- data/spec/integration/association_for_chris_spec.rb +37 -0
- data/spec/integration/association_for_peschkaj_spec.rb +26 -0
- data/spec/{bullet → integration}/association_spec.rb +1 -359
- data/spec/integration/counter_spec.rb +37 -0
- data/spec/models/address.rb +3 -0
- data/spec/models/author.rb +3 -0
- data/spec/models/base_user.rb +5 -0
- data/spec/models/category.rb +7 -0
- data/spec/models/city.rb +3 -0
- data/spec/models/client.rb +4 -0
- data/spec/models/comment.rb +4 -0
- data/spec/models/company.rb +3 -0
- data/spec/models/contact.rb +3 -0
- data/spec/models/country.rb +3 -0
- data/spec/models/deal.rb +4 -0
- data/spec/models/document.rb +5 -0
- data/spec/models/email.rb +3 -0
- data/spec/models/entry.rb +3 -0
- data/spec/models/firm.rb +4 -0
- data/spec/models/folder.rb +2 -0
- data/spec/models/hotel.rb +4 -0
- data/spec/models/location.rb +3 -0
- data/spec/models/newspaper.rb +3 -0
- data/spec/models/page.rb +2 -0
- data/spec/models/person.rb +3 -0
- data/spec/models/pet.rb +3 -0
- data/spec/models/post.rb +11 -0
- data/spec/models/relationship.rb +4 -0
- data/spec/models/student.rb +3 -0
- data/spec/models/submission.rb +4 -0
- data/spec/models/teacher.rb +3 -0
- data/spec/models/user.rb +4 -0
- data/spec/models/writer.rb +2 -0
- data/spec/spec_helper.rb +16 -106
- data/spec/support/bullet_ext.rb +55 -0
- data/spec/support/rack_double.rb +55 -0
- data/spec/support/seed.rb +256 -0
- metadata +104 -14
- data/.watchr +0 -24
- data/spec/bullet/association_for_chris_spec.rb +0 -96
- data/spec/bullet/association_for_peschkaj_spec.rb +0 -86
- data/spec/bullet/counter_spec.rb +0 -136
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Bullet
|
|
4
|
+
module Detector
|
|
5
|
+
describe Counter do
|
|
6
|
+
before :all do
|
|
7
|
+
@post1 = Post.first
|
|
8
|
+
@post2 = Post.last
|
|
9
|
+
end
|
|
10
|
+
before(:each) { Counter.clear }
|
|
11
|
+
|
|
12
|
+
context ".clear" do
|
|
13
|
+
it "should clear all class variables" do
|
|
14
|
+
Counter.clear
|
|
15
|
+
Counter.class_variable_get(:@@possible_objects).should be_nil
|
|
16
|
+
Counter.class_variable_get(:@@impossible_objects).should be_nil
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context ".add_counter_cache" do
|
|
21
|
+
it "should create notification if conditions met" do
|
|
22
|
+
Counter.should_receive(:conditions_met?).with(@post1.ar_key, [:comments]).and_return(true)
|
|
23
|
+
Counter.should_receive(:create_notification).with("Post", [:comments])
|
|
24
|
+
Counter.add_counter_cache(@post1, [:comments])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should not create notification if conditions not met" do
|
|
28
|
+
Counter.should_receive(:conditions_met?).with(@post1.ar_key, [:comments]).and_return(false)
|
|
29
|
+
Counter.should_receive(:create_notification).never
|
|
30
|
+
Counter.add_counter_cache(@post1, [:comments])
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context ".add_possible_objects" do
|
|
35
|
+
it "should add possible objects" do
|
|
36
|
+
Counter.add_possible_objects([@post1, @post2])
|
|
37
|
+
Counter.send(:possible_objects).should be_include(@post1.ar_key)
|
|
38
|
+
Counter.send(:possible_objects).should be_include(@post2.ar_key)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should add impossible object" do
|
|
42
|
+
Counter.add_impossible_object(@post1)
|
|
43
|
+
Counter.send(:impossible_objects).should be_include(@post1.ar_key)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
context ".conditions_met?" do
|
|
48
|
+
it "should be true when object is possible, not impossible" do
|
|
49
|
+
Counter.add_possible_objects(@post1)
|
|
50
|
+
Counter.send(:conditions_met?, @post1.ar_key, :associations).should be_true
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should be false when object is not possible" do
|
|
54
|
+
Counter.send(:conditions_met?, @post1.ar_key, :associations).should be_false
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "should be true when object is possible, and impossible" do
|
|
58
|
+
Counter.add_possible_objects(@post1)
|
|
59
|
+
Counter.add_impossible_object(@post1)
|
|
60
|
+
Counter.send(:conditions_met?, @post1.ar_key, :associations).should be_false
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Bullet
|
|
4
|
+
module Detector
|
|
5
|
+
describe NPlusOneQuery do
|
|
6
|
+
before(:all) { @post = Post.first }
|
|
7
|
+
before(:each) { NPlusOneQuery.clear }
|
|
8
|
+
|
|
9
|
+
context ".call_association" do
|
|
10
|
+
it "should set @@checked to true" do
|
|
11
|
+
NPlusOneQuery.call_association(@post, :associations)
|
|
12
|
+
NPlusOneQuery.class_variable_get(:@@checked).should be_true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should add call_object_associations" do
|
|
16
|
+
NPlusOneQuery.should_receive(:add_call_object_associations).with(@post, :associations)
|
|
17
|
+
NPlusOneQuery.call_association(@post, :associations)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
context ".possible?" do
|
|
22
|
+
it "should be true if possible_objects contain" do
|
|
23
|
+
NPlusOneQuery.add_possible_objects(@post)
|
|
24
|
+
NPlusOneQuery.send(:possible?, @post.ar_key).should be_true
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
context ".impossible?" do
|
|
29
|
+
it "should be true if impossible_objects contain" do
|
|
30
|
+
NPlusOneQuery.add_impossible_object(@post)
|
|
31
|
+
NPlusOneQuery.send(:impossible?, @post.ar_key).should be_true
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
context ".association?" do
|
|
36
|
+
it "should be true if object, associations pair is already existed" do
|
|
37
|
+
NPlusOneQuery.add_object_associations(@post, :association)
|
|
38
|
+
NPlusOneQuery.send(:association?, @post.ar_key, :association).should be_true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should be false if object, association pair is not existed" do
|
|
42
|
+
NPlusOneQuery.add_object_associations(@post, :association1)
|
|
43
|
+
NPlusOneQuery.send(:association?, @post.ar_key, :associatio2).should be_false
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
context ".conditions_met?" do
|
|
48
|
+
it "should be true if object is possible, not impossible and object, associations pair is not already existed" do
|
|
49
|
+
NPlusOneQuery.stub(:possible?).with(@post.ar_key).and_return(true)
|
|
50
|
+
NPlusOneQuery.stub(:impossible?).with(@post.ar_key).and_return(false)
|
|
51
|
+
NPlusOneQuery.stub(:association?).with(@post.ar_key, :associations).and_return(false)
|
|
52
|
+
NPlusOneQuery.send(:conditions_met?, @post.ar_key, :associations).should be_true
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "should be false if object is not possible, not impossible and object, associations pair is not already existed" do
|
|
56
|
+
NPlusOneQuery.stub(:possible?).with(@post.ar_key).and_return(false)
|
|
57
|
+
NPlusOneQuery.stub(:impossible?).with(@post.ar_key).and_return(false)
|
|
58
|
+
NPlusOneQuery.stub(:association?).with(@post.ar_key, :associations).and_return(false)
|
|
59
|
+
NPlusOneQuery.send(:conditions_met?, @post.ar_key, :associations).should be_false
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "should be false if object is possible, but impossible and object, associations pair is not already existed" do
|
|
63
|
+
NPlusOneQuery.stub(:possible?).with(@post.ar_key).and_return(true)
|
|
64
|
+
NPlusOneQuery.stub(:impossible?).with(@post.ar_key).and_return(true)
|
|
65
|
+
NPlusOneQuery.stub(:association?).with(@post.ar_key, :associations).and_return(false)
|
|
66
|
+
NPlusOneQuery.send(:conditions_met?, @post.ar_key, :associations).should be_false
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "should be false if object is possible, not impossible and object, associations pair is already existed" do
|
|
70
|
+
NPlusOneQuery.stub(:possible?).with(@post.ar_key).and_return(true)
|
|
71
|
+
NPlusOneQuery.stub(:impossible?).with(@post.ar_key).and_return(false)
|
|
72
|
+
NPlusOneQuery.stub(:association?).with(@post.ar_key, :associations).and_return(true)
|
|
73
|
+
NPlusOneQuery.send(:conditions_met?, @post.ar_key, :associations).should be_false
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
context ".call_association" do
|
|
78
|
+
it "should create notification if conditions met" do
|
|
79
|
+
NPlusOneQuery.should_receive(:conditions_met?).with(@post.ar_key, :association).and_return(true)
|
|
80
|
+
NPlusOneQuery.should_receive(:caller_in_project).and_return(["caller"])
|
|
81
|
+
NPlusOneQuery.should_receive(:create_notification).with(["caller"], "Post", :association)
|
|
82
|
+
NPlusOneQuery.call_association(@post, :association)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "should not create notification if conditions not met" do
|
|
86
|
+
NPlusOneQuery.should_receive(:conditions_met?).with(@post.ar_key, :association).and_return(false)
|
|
87
|
+
NPlusOneQuery.should_not_receive(:caller_in_project!)
|
|
88
|
+
NPlusOneQuery.should_not_receive(:create_notification).with("Post", :association)
|
|
89
|
+
NPlusOneQuery.call_association(@post, :association)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Bullet
|
|
4
|
+
module Detector
|
|
5
|
+
describe UnusedEagerAssociation do
|
|
6
|
+
before(:all) { @post = Post.first }
|
|
7
|
+
before(:each) { UnusedEagerAssociation.clear }
|
|
8
|
+
|
|
9
|
+
context ".call_associations" do
|
|
10
|
+
it "should get empty array if eager_loadgins" do
|
|
11
|
+
UnusedEagerAssociation.send(:call_associations, @post.ar_key, Set.new([:association])).should be_empty
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "should get call associations if object and association are both in eager_loadings and call_object_associations" do
|
|
15
|
+
UnusedEagerAssociation.add_eager_loadings(@post, :association)
|
|
16
|
+
UnusedEagerAssociation.add_call_object_associations(@post, :association)
|
|
17
|
+
UnusedEagerAssociation.send(:call_associations, @post.ar_key, Set.new([:association])).should == [:association]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should not get call associations if not exist in call_object_associations" do
|
|
21
|
+
UnusedEagerAssociation.add_eager_loadings(@post, :association)
|
|
22
|
+
UnusedEagerAssociation.send(:call_associations, @post.ar_key, Set.new([:association])).should be_empty
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
context ".diff_object_association" do
|
|
27
|
+
it "should return associations not exist in call_association" do
|
|
28
|
+
UnusedEagerAssociation.send(:diff_object_association, @post.ar_key, Set.new([:association])).should == [:association]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should return empty if associations exist in call_association" do
|
|
32
|
+
UnusedEagerAssociation.add_eager_loadings(@post, :association)
|
|
33
|
+
UnusedEagerAssociation.add_call_object_associations(@post, :association)
|
|
34
|
+
UnusedEagerAssociation.send(:diff_object_association, @post.ar_key, Set.new([:association])).should be_empty
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
context ".check_unused_preload_associations" do
|
|
39
|
+
it "should set @@checked to true" do
|
|
40
|
+
UnusedEagerAssociation.check_unused_preload_associations
|
|
41
|
+
UnusedEagerAssociation.class_variable_get(:@@checked).should be_true
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "should create notification if object_association_diff is not empty" do
|
|
45
|
+
UnusedEagerAssociation.add_object_associations(@post, :association)
|
|
46
|
+
UnusedEagerAssociation.should_receive(:create_notification).with("Post", [:association])
|
|
47
|
+
UnusedEagerAssociation.check_unused_preload_associations
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "should not create notification if object_association_diff is empty" do
|
|
51
|
+
UnusedEagerAssociation.clear
|
|
52
|
+
UnusedEagerAssociation.add_object_associations(@post, :association)
|
|
53
|
+
UnusedEagerAssociation.add_eager_loadings(@post, :association)
|
|
54
|
+
UnusedEagerAssociation.add_call_object_associations(@post, :association)
|
|
55
|
+
UnusedEagerAssociation.send(:diff_object_association, @post.ar_key, Set.new([:association])).should be_empty
|
|
56
|
+
UnusedEagerAssociation.should_not_receive(:create_notification).with("Post", [:association])
|
|
57
|
+
UnusedEagerAssociation.check_unused_preload_associations
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Bullet
|
|
4
|
+
module Notification
|
|
5
|
+
describe Base do
|
|
6
|
+
subject { Base.new(Post, [:comments, :votes]) }
|
|
7
|
+
|
|
8
|
+
context "#title" do
|
|
9
|
+
it "should raise NoMethodError" do
|
|
10
|
+
lambda { subject.title }.should raise_error(NoMethodError)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context "#body" do
|
|
15
|
+
it "should raise NoMethodError" do
|
|
16
|
+
lambda { subject.body }.should raise_error(NoMethodError)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context "#whoami" do
|
|
21
|
+
it "should display user name" do
|
|
22
|
+
user = `whoami`.chomp
|
|
23
|
+
subject.whoami.should == "user: #{user}"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context "#body_with_caller" do
|
|
28
|
+
it "should return body" do
|
|
29
|
+
subject.stub(:body => "body")
|
|
30
|
+
subject.body_with_caller.should == "body"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context "#standard_notice" do
|
|
35
|
+
it "should return title + body" do
|
|
36
|
+
subject.stub(:title => "title", :body => "body")
|
|
37
|
+
subject.standard_notice.should == "title\nbody"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
context "#full_notice" do
|
|
42
|
+
it "should return whoami + url + title + body_with_caller" do
|
|
43
|
+
subject.stub(:whoami => "whoami", :url => "url", :title => "title", :body_with_caller => "body_with_caller")
|
|
44
|
+
subject.full_notice.should == "whoami\nurl\ntitle\nbody_with_caller"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
context "#notify_inline" do
|
|
49
|
+
it "should send full_notice to notifier" do
|
|
50
|
+
notifier = stub
|
|
51
|
+
subject.stub(:notifier => notifier, :full_notice => "full_notice")
|
|
52
|
+
notifier.should_receive(:inline_notify).with("full_notice")
|
|
53
|
+
subject.notify_inline
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
context "#notify_out_of_channel" do
|
|
58
|
+
it "should send full_out_of_channel to notifier" do
|
|
59
|
+
notifier = stub
|
|
60
|
+
subject.stub(:notifier => notifier, :full_notice => "full_notice")
|
|
61
|
+
notifier.should_receive(:out_of_channel_notify).with("full_notice")
|
|
62
|
+
subject.notify_out_of_channel
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Bullet
|
|
4
|
+
module Notification
|
|
5
|
+
describe CounterCache do
|
|
6
|
+
subject { CounterCache.new(Post, [:comments, :votes]) }
|
|
7
|
+
|
|
8
|
+
its(:body) { should == " Post => [:comments, :votes]" }
|
|
9
|
+
its(:title) { should == "Need Counter Cache" }
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Bullet
|
|
4
|
+
module Notification
|
|
5
|
+
describe NPlusOneQuery do
|
|
6
|
+
subject { NPlusOneQuery.new([["caller1", "caller2"]], Post, [:comments, :votes], "path") }
|
|
7
|
+
|
|
8
|
+
its(:body_with_caller) { should == " Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2" }
|
|
9
|
+
its(:body) { should == " Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]" }
|
|
10
|
+
its(:title) { should == "N+1 Query in path" }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Bullet
|
|
4
|
+
module Notification
|
|
5
|
+
describe UnusedEagerLoading do
|
|
6
|
+
subject { UnusedEagerLoading.new(Post, [:comments, :votes], "path") }
|
|
7
|
+
|
|
8
|
+
its(:body) { should == " Post => [:comments, :votes]\n Remove from your finder: :include => [:comments, :votes]" }
|
|
9
|
+
its(:title) { should == "Unused Eager Loading in path" }
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Bullet
|
|
4
|
+
describe NotificationCollector do
|
|
5
|
+
subject { NotificationCollector.new.tap { |collector| collector.add("value") } }
|
|
6
|
+
|
|
7
|
+
context "#add" do
|
|
8
|
+
it "should add a value" do
|
|
9
|
+
subject.add("value1")
|
|
10
|
+
subject.collection.should be_include("value1")
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context "#reset" do
|
|
15
|
+
it "should reset collector" do
|
|
16
|
+
subject.reset
|
|
17
|
+
subject.collection.should be_empty
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
context "#notifications_present?" do
|
|
22
|
+
it "should be true if collection is not empty" do
|
|
23
|
+
subject.should be_notifications_present
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should be false if collection is empty" do
|
|
27
|
+
subject.reset
|
|
28
|
+
subject.should_not be_notifications_present
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/spec/bullet/rack_spec.rb
CHANGED
|
@@ -1,39 +1,95 @@
|
|
|
1
|
-
require
|
|
1
|
+
require 'spec_helper'
|
|
2
2
|
|
|
3
|
+
module Bullet
|
|
4
|
+
describe Rack do
|
|
5
|
+
let(:middleware) { Bullet::Rack.new app }
|
|
6
|
+
let(:app) { Support::AppDouble.new }
|
|
3
7
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
8
|
+
context "#no_browser_cache" do
|
|
9
|
+
it "should add no cache meta in http headers" do
|
|
10
|
+
headers = {}
|
|
11
|
+
middleware.no_browser_cache(headers)
|
|
12
|
+
headers["Cache-Control"].should == "no-cache, no-store, max-age=0, must-revalidate"
|
|
13
|
+
headers["Pragma"].should == "no-cache"
|
|
14
|
+
headers["Expires"].should == "Wed, 09 Sep 2009 09:09:09 GMT"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
context "#html_request?" do
|
|
19
|
+
it "should be true if Content-Type is text/html and http body contains html tag" do
|
|
20
|
+
headers = {"Content-Type" => "text/html"}
|
|
21
|
+
response = stub(:body => "<html><head></head><body></body></html>")
|
|
22
|
+
middleware.should be_html_request(headers, response)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should be false if there is no Content-Type header" do
|
|
26
|
+
headers = {}
|
|
27
|
+
response = stub(:body => "<html><head></head><body></body></html>")
|
|
28
|
+
middleware.should_not be_html_request(headers, response)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should be false if Content-Type is javascript" do
|
|
32
|
+
headers = {"Content-Type" => "text/javascript"}
|
|
33
|
+
response = stub(:body => "<html><head></head><body></body></html>")
|
|
34
|
+
middleware.should_not be_html_request(headers, response)
|
|
35
|
+
end
|
|
7
36
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
37
|
+
it "should be false if response body doesn't contain html tag" do
|
|
38
|
+
headers = {"Content-Type" => "text/html"}
|
|
39
|
+
response = stub(:body => "<div>Partial</div>")
|
|
40
|
+
middleware.should_not be_html_request(headers, response)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
11
43
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
44
|
+
context "empty?" do
|
|
45
|
+
it "should be false if response is a string and not empty" do
|
|
46
|
+
response = stub(:body => "<html><head></head><body></body></html>")
|
|
47
|
+
middleware.should_not be_empty(response)
|
|
15
48
|
end
|
|
16
49
|
|
|
17
|
-
it "should
|
|
18
|
-
|
|
19
|
-
middleware.
|
|
50
|
+
it "should be tru if response is not found" do
|
|
51
|
+
response = ["Not Found"]
|
|
52
|
+
middleware.should be_empty(response)
|
|
20
53
|
end
|
|
21
54
|
|
|
22
|
-
it "should
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
status, headers, response = middleware.call([])
|
|
26
|
-
response.should eq expected_response
|
|
55
|
+
it "should be true if response body is empty" do
|
|
56
|
+
response = stub(:body => "")
|
|
57
|
+
middleware.should be_empty(response)
|
|
27
58
|
end
|
|
28
59
|
end
|
|
29
60
|
|
|
30
|
-
context "
|
|
31
|
-
|
|
32
|
-
|
|
61
|
+
context "#call" do
|
|
62
|
+
context "when Bullet is enabled" do
|
|
63
|
+
it "should invoke Bullet.start_request and Bullet.end_request" do
|
|
64
|
+
Bullet.should_receive(:start_request)
|
|
65
|
+
Bullet.should_receive(:end_request)
|
|
66
|
+
middleware.call([])
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "should return original response body" do
|
|
70
|
+
expected_response = Support::ResponseDouble.new "Actual body"
|
|
71
|
+
app.response = expected_response
|
|
72
|
+
status, headers, response = middleware.call([])
|
|
73
|
+
response.should == expected_response
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "should change response body if notification is active" do
|
|
77
|
+
Bullet.should_receive(:notification?).and_return(true)
|
|
78
|
+
Bullet.should_receive(:gather_inline_notifications).and_return("<bullet></bullet>")
|
|
79
|
+
Bullet.should_receive(:perform_out_of_channel_notifications)
|
|
80
|
+
status, headers, response = middleware.call([200, {"Content-Type" => "text/html"}])
|
|
81
|
+
headers["Content-Length"].should == "56"
|
|
82
|
+
response.should == ["<html><head></head><body></body></html><bullet></bullet>"]
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
context "when Bullet is disabled" do
|
|
87
|
+
before(:each) { Bullet.stub(:enable?, false) }
|
|
33
88
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
89
|
+
it "should not call Bullet.start_request" do
|
|
90
|
+
Bullet.should_not_receive(:start_request)
|
|
91
|
+
middleware.call([])
|
|
92
|
+
end
|
|
37
93
|
end
|
|
38
94
|
end
|
|
39
95
|
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Bullet
|
|
4
|
+
module Registry
|
|
5
|
+
describe Association do
|
|
6
|
+
subject { Association.new.tap { |association| association.add(["key1", "key2"], "value") } }
|
|
7
|
+
|
|
8
|
+
context "#merge" do
|
|
9
|
+
it "should merge key/value" do
|
|
10
|
+
subject.merge("key0", "value0")
|
|
11
|
+
subject["key0"].should be_include("value0")
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
context "#similarly_associated" do
|
|
16
|
+
it "should return similarly associated keys" do
|
|
17
|
+
subject.similarly_associated("key1", Set.new(["value"])).should == ["key1", "key2"]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should return empty if key does not exist" do
|
|
21
|
+
subject.similarly_associated("key3", Set.new(["value"])).should be_empty
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Bullet
|
|
4
|
+
module Registry
|
|
5
|
+
describe Base do
|
|
6
|
+
subject { Base.new.tap { |base| base.add("key", "value") } }
|
|
7
|
+
|
|
8
|
+
context "#[]" do
|
|
9
|
+
it "should get value by key" do
|
|
10
|
+
subject["key"].should == Set.new(["value"])
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context "#delete" do
|
|
15
|
+
it "should delete key" do
|
|
16
|
+
subject.delete("key")
|
|
17
|
+
subject["key"].should be_nil
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
context "#add" do
|
|
22
|
+
it "should add value with string" do
|
|
23
|
+
subject.add("key", "new_value")
|
|
24
|
+
subject["key"].should == Set.new(["value", "new_value"])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should add value with array" do
|
|
28
|
+
subject.add("key", ["value1", "value2"])
|
|
29
|
+
subject["key"].should == Set.new(["value", "value1", "value2"])
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
context "#include?" do
|
|
34
|
+
it "should include key/value" do
|
|
35
|
+
subject.include?("key", "value").should be_true
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "should not include wrong key/value" do
|
|
39
|
+
subject.include?("key", "val").should be_false
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
module Bullet
|
|
5
|
+
module Registry
|
|
6
|
+
describe Object do
|
|
7
|
+
let(:post) { Post.first }
|
|
8
|
+
let(:another_post) { Post.last }
|
|
9
|
+
subject { Object.new.tap { |object| object.add(post.ar_key) } }
|
|
10
|
+
|
|
11
|
+
context "#include?" do
|
|
12
|
+
it "should include the object" do
|
|
13
|
+
subject.should be_include(post.ar_key)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
context "#add" do
|
|
18
|
+
it "should add an object" do
|
|
19
|
+
subject.add(another_post.ar_key)
|
|
20
|
+
subject.should be_include(another_post.ar_key)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
# This test is just used for http://github.com/flyerhzm/bullet/issues/#issue/14
|
|
4
|
+
describe Bullet::Detector::Association do
|
|
5
|
+
before(:each) do
|
|
6
|
+
Bullet.clear
|
|
7
|
+
Bullet.start_request
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
after(:each) do
|
|
11
|
+
Bullet.end_request
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe "for chris" do
|
|
15
|
+
it "should detect unpreload association from deal to hotel" do
|
|
16
|
+
Deal.all.each do |deal|
|
|
17
|
+
deal.hotel.location.name
|
|
18
|
+
end
|
|
19
|
+
Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Deal, :hotel)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "should detect unpreload association from hotel to location" do
|
|
23
|
+
Deal.includes(:hotel).each do |deal|
|
|
24
|
+
deal.hotel.location.name
|
|
25
|
+
end
|
|
26
|
+
Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Hotel, :location)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "should not detect unpreload association" do
|
|
30
|
+
Deal.includes({:hotel => :location}).each do |deal|
|
|
31
|
+
deal.hotel.location.name
|
|
32
|
+
end
|
|
33
|
+
Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
|
|
34
|
+
Bullet::Detector::Association.should_not be_has_unused_preload_associations
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
# This test is just used for http://github.com/flyerhzm/bullet/issues#issue/20
|
|
4
|
+
describe Bullet::Detector::Association do
|
|
5
|
+
before(:each) do
|
|
6
|
+
Bullet.clear
|
|
7
|
+
Bullet.start_request
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
after(:each) do
|
|
11
|
+
Bullet.end_request
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe "for peschkaj" do
|
|
15
|
+
it "should not detect unused preload associations" do
|
|
16
|
+
category = Category.includes({:submissions => :user}).order("id DESC").find_by_name('first')
|
|
17
|
+
category.submissions.map do |submission|
|
|
18
|
+
submission.name
|
|
19
|
+
submission.user.name
|
|
20
|
+
end
|
|
21
|
+
Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
|
|
22
|
+
Bullet::Detector::Association.should_not be_unused_preload_associations_for(Category, :submissions)
|
|
23
|
+
Bullet::Detector::Association.should_not be_unused_preload_associations_for(Submission, :user)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|