bullet 4.13.1 → 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 +29 -1
  3. data/CHANGELOG.md +53 -2
  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 +17 -0
  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 +214 -0
  27. data/lib/bullet/dependency.rb +12 -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 +2 -2
  32. data/lib/bullet/ext/object.rb +4 -2
  33. data/lib/bullet/mongoid5x.rb +56 -0
  34. data/lib/bullet/notification/base.rb +7 -11
  35. data/lib/bullet/notification/n_plus_one_query.rb +6 -4
  36. data/lib/bullet/rack.rb +20 -13
  37. data/lib/bullet/version.rb +1 -1
  38. data/lib/bullet.rb +24 -11
  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 +4 -29
  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 +2 -0
  59. data/update.sh +15 -0
  60. metadata +14 -7
@@ -18,6 +18,10 @@ module Bullet
18
18
  raise NoMethodError.new("no method body defined")
19
19
  end
20
20
 
21
+ def call_stack_messages
22
+ ""
23
+ end
24
+
21
25
  def whoami
22
26
  @user ||= ENV['USER'].presence || (`whoami`.chomp rescue "")
23
27
  if @user.present?
@@ -28,15 +32,7 @@ module Bullet
28
32
  end
29
33
 
30
34
  def body_with_caller
31
- body
32
- end
33
-
34
- def standard_notice
35
- @standard_notifice ||= title + "\n" + body
36
- end
37
-
38
- def full_notice
39
- [whoami.presence, url, title, body_with_caller].compact.join("\n")
35
+ "#{body}\n#{call_stack_messages}\n"
40
36
  end
41
37
 
42
38
  def notify_inline
@@ -48,7 +44,7 @@ module Bullet
48
44
  end
49
45
 
50
46
  def short_notice
51
- [whoami.presence, url, title, body].compact.join("\n")
47
+ [whoami.presence, url, title, body].compact.join(" ")
52
48
  end
53
49
 
54
50
  def notification_data
@@ -74,7 +70,7 @@ module Bullet
74
70
  end
75
71
 
76
72
  def associations_str
77
- ":include => #{@associations.map{ |a| a.to_s.to_sym unless a.is_a? Hash }.inspect}"
73
+ ":includes => #{@associations.map{ |a| a.to_s.to_sym unless a.is_a? Hash }.inspect}"
78
74
  end
79
75
  end
80
76
  end
@@ -7,10 +7,6 @@ module Bullet
7
7
  @callers = callers
8
8
  end
9
9
 
10
- def body_with_caller
11
- "#{body}\n#{call_stack_messages}"
12
- end
13
-
14
10
  def body
15
11
  "#{klazz_associations_str}\n Add to your finder: #{associations_str}"
16
12
  end
@@ -19,6 +15,12 @@ module Bullet
19
15
  "N+1 Query #{@path ? "in #{@path}" : 'detected'}"
20
16
  end
21
17
 
18
+ def notification_data
19
+ super.merge(
20
+ :backtrace => @callers
21
+ )
22
+ end
23
+
22
24
  protected
23
25
  def call_stack_messages
24
26
  (['N+1 Query method call stack'] + @callers).join( "\n " )
data/lib/bullet/rack.rb CHANGED
@@ -10,17 +10,16 @@ module Bullet
10
10
  return @app.call(env) unless Bullet.enable?
11
11
  Bullet.start_request
12
12
  status, headers, response = @app.call(env)
13
- return [status, headers, response] if file?(headers) || sse?(response) || empty?(response)
14
13
 
15
14
  response_body = nil
16
15
  if Bullet.notification?
17
- if status == 200 && !response_body(response).frozen? && html_request?(headers, response)
18
- response_body = response_body(response) << Bullet.gather_inline_notifications
19
- add_footer_note(response_body) if Bullet.add_footer
16
+ if !file?(headers) && !sse?(headers) && !empty?(response) &&
17
+ status == 200 && !response_body(response).frozen? && html_request?(headers, response)
18
+ response_body = response_body(response)
19
+ append_to_html_body(response_body, footer_note) if Bullet.add_footer
20
+ append_to_html_body(response_body, Bullet.gather_inline_notifications)
20
21
  headers['Content-Length'] = response_body.bytesize.to_s
21
22
  end
22
- end
23
- if Bullet.enable? && Bullet.notification?
24
23
  Bullet.perform_out_of_channel_notifications(env)
25
24
  end
26
25
  [status, headers, response_body ? [response_body] : response]
@@ -42,16 +41,25 @@ module Bullet
42
41
  end
43
42
  end
44
43
 
45
- def add_footer_note(response_body)
46
- response_body << "<div #{footer_div_style}>" + Bullet.footer_info.uniq.join("<br>") + "</div>"
44
+ def append_to_html_body(response_body, content)
45
+ if response_body.include?('</body>')
46
+ position = response_body.rindex('</body>')
47
+ response_body.insert(position, content)
48
+ else
49
+ response_body << content
50
+ end
51
+ end
52
+
53
+ def footer_note
54
+ "<div #{footer_div_attributes}>" + Bullet.footer_info.uniq.join("<br>") + "</div>"
47
55
  end
48
56
 
49
57
  def file?(headers)
50
58
  headers["Content-Transfer-Encoding"] == "binary"
51
59
  end
52
60
 
53
- def sse?(response)
54
- response.respond_to?(:stream) && response.stream.is_a?(ActionController::Live::Buffer)
61
+ def sse?(headers)
62
+ headers["Content-Type"] == "text/event-stream"
55
63
  end
56
64
 
57
65
  def html_request?(headers, response)
@@ -67,9 +75,9 @@ module Bullet
67
75
  end
68
76
 
69
77
  private
70
- def footer_div_style
78
+ def footer_div_attributes
71
79
  <<EOF
72
- style="position: fixed; bottom: 0pt; left: 0pt; cursor: pointer; border-style: solid; border-color: rgb(153, 153, 153);
80
+ data-is-bullet-footer ondblclick="this.parentNode.removeChild(this);" style="position: fixed; bottom: 0pt; left: 0pt; cursor: pointer; border-style: solid; border-color: rgb(153, 153, 153);
73
81
  -moz-border-top-colors: none; -moz-border-right-colors: none; -moz-border-bottom-colors: none;
74
82
  -moz-border-left-colors: none; -moz-border-image: none; border-width: 2pt 2pt 0px 0px;
75
83
  padding: 5px; border-radius: 0pt 10pt 0pt 0px; background: none repeat scroll 0% 0% rgba(200, 200, 200, 0.8);
@@ -78,4 +86,3 @@ EOF
78
86
  end
79
87
  end
80
88
  end
81
-
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Bullet
3
- VERSION = "4.13.1"
3
+ VERSION = "4.14.10"
4
4
  end
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
@@ -153,14 +163,14 @@ module Bullet
153
163
 
154
164
  def perform_out_of_channel_notifications(env = {})
155
165
  for_each_active_notifier_with_notification do |notification|
156
- notification.url = [env['HTTP_HOST'], env['REQUEST_URI']].compact.join
166
+ notification.url = env['REQUEST_URI']
157
167
  notification.notify_out_of_channel
158
168
  end
159
169
  end
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
@@ -56,33 +56,8 @@ module Bullet
56
56
  context "#body_with_caller" do
57
57
  it "should return body" do
58
58
  allow(subject).to receive(:body).and_return("body")
59
- expect(subject.body_with_caller).to eq("body")
60
- end
61
- end
62
-
63
- context "#standard_notice" do
64
- it "should return title + body" do
65
- allow(subject).to receive(:title).and_return("title")
66
- allow(subject).to receive(:body).and_return("body")
67
- expect(subject.standard_notice).to eq("title\nbody")
68
- end
69
- end
70
-
71
- context "#full_notice" do
72
- it "should return whoami + url + title + body_with_caller" do
73
- allow(subject).to receive(:whoami).and_return("whoami")
74
- allow(subject).to receive(:url).and_return("url")
75
- allow(subject).to receive(:title).and_return("title")
76
- allow(subject).to receive(:body_with_caller).and_return("body_with_caller")
77
- expect(subject.full_notice).to eq("whoami\nurl\ntitle\nbody_with_caller")
78
- end
79
-
80
- it "should return url + title + body_with_caller" do
81
- allow(subject).to receive(:whoami).and_return("")
82
- allow(subject).to receive(:url).and_return("url")
83
- allow(subject).to receive(:title).and_return("title")
84
- allow(subject).to receive(:body_with_caller).and_return("body_with_caller")
85
- expect(subject.full_notice).to eq("url\ntitle\nbody_with_caller")
59
+ allow(subject).to receive(:call_stack_messages).and_return("call_stack_messages")
60
+ expect(subject.body_with_caller).to eq("body\ncall_stack_messages\n")
86
61
  end
87
62
  end
88
63
 
@@ -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") }
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", " Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2" ]) }
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)