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.
Files changed (81) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +4 -1
  3. data/Gemfile +3 -2
  4. data/Gemfile.lock +85 -69
  5. data/Guardfile +8 -0
  6. data/README.textile +2 -2
  7. data/lib/bullet/action_controller2.rb +4 -4
  8. data/lib/bullet/active_record2.rb +5 -6
  9. data/lib/bullet/active_record3.rb +2 -3
  10. data/lib/bullet/active_record31.rb +10 -12
  11. data/lib/bullet/detector/association.rb +27 -53
  12. data/lib/bullet/detector/counter.rb +34 -29
  13. data/lib/bullet/detector/n_plus_one_query.rb +47 -28
  14. data/lib/bullet/detector/unused_eager_association.rb +31 -30
  15. data/lib/bullet/notification/base.rb +14 -12
  16. data/lib/bullet/notification/n_plus_one_query.rb +6 -10
  17. data/lib/bullet/notification/unused_eager_loading.rb +1 -2
  18. data/lib/bullet/notification_collector.rb +1 -2
  19. data/lib/bullet/rack.rb +6 -3
  20. data/lib/bullet/registry/association.rb +4 -6
  21. data/lib/bullet/registry/base.rb +10 -7
  22. data/lib/bullet/registry/object.rb +6 -6
  23. data/lib/bullet/version.rb +1 -1
  24. data/lib/bullet.rb +13 -8
  25. data/perf/benchmark.rb +30 -12
  26. data/spec/bullet/detector/association_spec.rb +90 -0
  27. data/spec/bullet/detector/base_spec.rb +14 -0
  28. data/spec/bullet/detector/counter_spec.rb +65 -0
  29. data/spec/bullet/detector/n_plus_one_query_spec.rb +94 -0
  30. data/spec/bullet/detector/unused_eager_association_spec.rb +62 -0
  31. data/spec/bullet/notification/base_spec.rb +67 -0
  32. data/spec/bullet/notification/counter_cache_spec.rb +12 -0
  33. data/spec/bullet/notification/n_plus_one_query_spec.rb +13 -0
  34. data/spec/bullet/notification/unused_eager_loading_spec.rb +12 -0
  35. data/spec/bullet/notification_collector_spec.rb +32 -0
  36. data/spec/bullet/rack_spec.rb +80 -24
  37. data/spec/bullet/registry/association_spec.rb +26 -0
  38. data/spec/bullet/registry/base_spec.rb +44 -0
  39. data/spec/bullet/registry/object_spec.rb +25 -0
  40. data/spec/integration/association_for_chris_spec.rb +37 -0
  41. data/spec/integration/association_for_peschkaj_spec.rb +26 -0
  42. data/spec/{bullet → integration}/association_spec.rb +1 -359
  43. data/spec/integration/counter_spec.rb +37 -0
  44. data/spec/models/address.rb +3 -0
  45. data/spec/models/author.rb +3 -0
  46. data/spec/models/base_user.rb +5 -0
  47. data/spec/models/category.rb +7 -0
  48. data/spec/models/city.rb +3 -0
  49. data/spec/models/client.rb +4 -0
  50. data/spec/models/comment.rb +4 -0
  51. data/spec/models/company.rb +3 -0
  52. data/spec/models/contact.rb +3 -0
  53. data/spec/models/country.rb +3 -0
  54. data/spec/models/deal.rb +4 -0
  55. data/spec/models/document.rb +5 -0
  56. data/spec/models/email.rb +3 -0
  57. data/spec/models/entry.rb +3 -0
  58. data/spec/models/firm.rb +4 -0
  59. data/spec/models/folder.rb +2 -0
  60. data/spec/models/hotel.rb +4 -0
  61. data/spec/models/location.rb +3 -0
  62. data/spec/models/newspaper.rb +3 -0
  63. data/spec/models/page.rb +2 -0
  64. data/spec/models/person.rb +3 -0
  65. data/spec/models/pet.rb +3 -0
  66. data/spec/models/post.rb +11 -0
  67. data/spec/models/relationship.rb +4 -0
  68. data/spec/models/student.rb +3 -0
  69. data/spec/models/submission.rb +4 -0
  70. data/spec/models/teacher.rb +3 -0
  71. data/spec/models/user.rb +4 -0
  72. data/spec/models/writer.rb +2 -0
  73. data/spec/spec_helper.rb +16 -106
  74. data/spec/support/bullet_ext.rb +55 -0
  75. data/spec/support/rack_double.rb +55 -0
  76. data/spec/support/seed.rb +256 -0
  77. metadata +104 -14
  78. data/.watchr +0 -24
  79. data/spec/bullet/association_for_chris_spec.rb +0 -96
  80. data/spec/bullet/association_for_peschkaj_spec.rb +0 -86
  81. 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
@@ -1,39 +1,95 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
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
- describe Bullet::Rack do
5
- let(:middleware) { Bullet::Rack.new app }
6
- let(:app) { AppDouble.new }
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
- describe "#call" do
9
- context "when Bullet is enabled" do
10
- before(:each) { Bullet.enable = true }
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
- it "should invoke Bullet.start_request" do
13
- Bullet.should_receive(:start_request)
14
- middleware.call([])
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 invoke Bullet.end_request" do
18
- Bullet.should_receive(:end_request)
19
- middleware.call([])
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 return original response body" do
23
- expected_response = ResponseDouble.new "Actual body"
24
- app.response = expected_response
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 "when Bullet is disabled" do
31
- before(:each) { Bullet.enable = false }
32
- after(:each) { Bullet.enable = true }
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
- it "should not call Bullet.start_request" do
35
- Bullet.should_not_receive(:start_request)
36
- middleware.call([])
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