bullet 4.0.0 → 4.13.1
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 +7 -0
- data/.gitignore +4 -0
- data/.rspec +1 -1
- data/.travis.yml +17 -6
- data/CHANGELOG.md +86 -0
- data/Gemfile +13 -12
- data/Gemfile.mongoid +14 -0
- data/Gemfile.mongoid-2.4 +19 -0
- data/Gemfile.mongoid-2.5 +19 -0
- data/Gemfile.mongoid-2.6 +19 -0
- data/Gemfile.mongoid-2.7 +19 -0
- data/Gemfile.mongoid-2.8 +19 -0
- data/Gemfile.mongoid-3.0 +19 -0
- data/Gemfile.mongoid-3.1 +19 -0
- data/Gemfile.mongoid-4.0 +19 -0
- data/Gemfile.rails-3.0 +19 -0
- data/Gemfile.rails-3.1 +19 -0
- data/Gemfile.rails-3.2 +19 -0
- data/Gemfile.rails-4.0 +19 -0
- data/Gemfile.rails-4.1 +19 -0
- data/{Hacking.textile → Hacking.md} +10 -5
- data/README.md +432 -0
- data/Rakefile +13 -6
- data/bullet.gemspec +6 -3
- data/lib/bullet/active_record3.rb +49 -11
- data/lib/bullet/active_record3x.rb +134 -0
- data/lib/bullet/{active_record31.rb → active_record4.rb} +45 -15
- data/lib/bullet/active_record41.rb +128 -0
- data/lib/bullet/dependency.rb +81 -0
- data/lib/bullet/detector/association.rb +24 -68
- data/lib/bullet/detector/base.rb +0 -6
- data/lib/bullet/detector/counter_cache.rb +59 -0
- data/lib/bullet/detector/n_plus_one_query.rb +56 -15
- data/lib/bullet/detector/unused_eager_loading.rb +84 -0
- data/lib/bullet/detector.rb +2 -2
- data/lib/bullet/ext/object.rb +10 -2
- data/lib/bullet/{mongoid.rb → mongoid2x.rb} +18 -18
- data/lib/bullet/mongoid3x.rb +56 -0
- data/lib/bullet/mongoid4x.rb +56 -0
- data/lib/bullet/notification/base.rb +22 -4
- data/lib/bullet/notification/n_plus_one_query.rb +1 -1
- data/lib/bullet/notification.rb +2 -0
- data/lib/bullet/rack.rb +51 -17
- data/lib/bullet/registry/base.rb +1 -1
- data/lib/bullet/registry/object.rb +4 -4
- data/lib/bullet/version.rb +1 -2
- data/lib/bullet.rb +119 -39
- data/perf/benchmark.rb +1 -4
- data/spec/bullet/detector/association_spec.rb +2 -70
- data/spec/bullet/detector/base_spec.rb +0 -6
- data/spec/bullet/detector/counter_cache_spec.rb +56 -0
- data/spec/bullet/detector/n_plus_one_query_spec.rb +78 -34
- data/spec/bullet/detector/unused_eager_loading_spec.rb +88 -0
- data/spec/bullet/ext/object_spec.rb +18 -7
- data/spec/bullet/ext/string_spec.rb +2 -2
- data/spec/bullet/notification/base_spec.rb +68 -15
- data/spec/bullet/notification/counter_cache_spec.rb +2 -2
- data/spec/bullet/notification/n_plus_one_query_spec.rb +4 -3
- data/spec/bullet/notification/unused_eager_loading_spec.rb +2 -2
- data/spec/bullet/notification_collector_spec.rb +4 -4
- data/spec/bullet/rack_spec.rb +40 -39
- data/spec/bullet/registry/association_spec.rb +3 -3
- data/spec/bullet/registry/base_spec.rb +6 -6
- data/spec/bullet/registry/object_spec.rb +4 -5
- data/spec/bullet_spec.rb +41 -0
- data/spec/integration/active_record3/association_spec.rb +662 -0
- data/spec/integration/active_record4/association_spec.rb +660 -0
- data/spec/integration/counter_cache_spec.rb +63 -0
- data/spec/integration/mongoid/association_spec.rb +190 -208
- data/spec/models/category.rb +1 -1
- data/spec/models/client.rb +1 -1
- data/spec/models/comment.rb +2 -2
- data/spec/models/document.rb +2 -2
- data/spec/models/firm.rb +1 -1
- data/spec/models/mongoid/address.rb +2 -0
- data/spec/models/mongoid/category.rb +2 -0
- data/spec/models/mongoid/comment.rb +2 -0
- data/spec/models/mongoid/company.rb +2 -0
- data/spec/models/mongoid/entry.rb +2 -0
- data/spec/models/mongoid/post.rb +4 -0
- data/spec/models/mongoid/user.rb +5 -0
- data/spec/models/newspaper.rb +1 -1
- data/spec/models/pet.rb +1 -1
- data/spec/models/post.rb +6 -13
- data/spec/spec_helper.rb +60 -25
- data/spec/support/bullet_ext.rb +1 -1
- data/spec/support/mongo_seed.rb +26 -2
- data/spec/support/sqlite_seed.rb +0 -6
- data/test.sh +15 -0
- metadata +77 -54
- data/.rvmrc +0 -2
- data/.rvmrc.example +0 -2
- data/Gemfile.lock +0 -132
- data/Gemfile.rails-2.3.14 +0 -16
- data/Gemfile.rails-2.3.14.lock +0 -65
- data/Gemfile.rails-3.0.12 +0 -17
- data/Gemfile.rails-3.0.12.lock +0 -116
- data/Gemfile.rails-3.1.4 +0 -18
- data/Gemfile.rails-3.1.4.lock +0 -134
- data/README.textile +0 -395
- data/README_for_rails2.textile +0 -400
- data/lib/bullet/action_controller2.rb +0 -49
- data/lib/bullet/active_record2.rb +0 -121
- data/lib/bullet/detector/counter.rb +0 -48
- data/lib/bullet/detector/unused_eager_association.rb +0 -42
- data/spec/bullet/detector/counter_spec.rb +0 -64
- data/spec/bullet/detector/unused_eager_association_spec.rb +0 -62
- data/spec/integration/association_spec.rb +0 -593
- data/spec/integration/counter_spec.rb +0 -39
- data/spec/integration/rails2/association_spec.rb +0 -593
- data/spec/integration/rails2/counter_spec.rb +0 -39
- data/test.result +0 -2293
data/lib/bullet.rb
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
|
+
require "active_support/core_ext/module/delegation"
|
|
1
2
|
require 'set'
|
|
2
3
|
require 'uniform_notifier'
|
|
3
4
|
require 'bullet/ext/object'
|
|
4
5
|
require 'bullet/ext/string'
|
|
6
|
+
require 'bullet/dependency'
|
|
5
7
|
|
|
6
8
|
module Bullet
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
else
|
|
12
|
-
autoload :ActiveRecord, 'bullet/active_record2'
|
|
13
|
-
autoload :ActionController, 'bullet/action_controller2'
|
|
14
|
-
end
|
|
15
|
-
autoload :Mongoid, 'bullet/mongoid'
|
|
9
|
+
extend Dependency
|
|
10
|
+
|
|
11
|
+
autoload :ActiveRecord, "bullet/#{active_record_version}" if active_record?
|
|
12
|
+
autoload :Mongoid, "bullet/#{mongoid_version}" if mongoid?
|
|
16
13
|
autoload :Rack, 'bullet/rack'
|
|
17
|
-
autoload :BulletLogger, 'bullet/logger'
|
|
18
14
|
autoload :Notification, 'bullet/notification'
|
|
19
15
|
autoload :Detector, 'bullet/detector'
|
|
20
16
|
autoload :Registry, 'bullet/registry'
|
|
@@ -28,66 +24,122 @@ module Bullet
|
|
|
28
24
|
end
|
|
29
25
|
end
|
|
30
26
|
|
|
31
|
-
class <<self
|
|
32
|
-
|
|
33
|
-
attr_reader :notification_collector
|
|
27
|
+
class << self
|
|
28
|
+
attr_writer :enable, :n_plus_one_query_enable, :unused_eager_loading_enable, :counter_cache_enable, :stacktrace_includes
|
|
29
|
+
attr_reader :notification_collector, :whitelist
|
|
30
|
+
attr_accessor :add_footer, :orm_pathches_applied
|
|
34
31
|
|
|
35
|
-
delegate :alert=, :console=, :growl=, :rails_logger=, :xmpp=, :to => UniformNotifier
|
|
32
|
+
delegate :alert=, :console=, :growl=, :rails_logger=, :xmpp=, :airbrake=, :bugsnag=, :to => UniformNotifier
|
|
33
|
+
|
|
34
|
+
def raise=(should_raise)
|
|
35
|
+
UniformNotifier.raise=(should_raise ? Notification::UnoptimizedQueryError : false)
|
|
36
|
+
end
|
|
36
37
|
|
|
37
38
|
DETECTORS = [ Bullet::Detector::NPlusOneQuery,
|
|
38
|
-
Bullet::Detector::
|
|
39
|
-
Bullet::Detector::
|
|
39
|
+
Bullet::Detector::UnusedEagerLoading,
|
|
40
|
+
Bullet::Detector::CounterCache ]
|
|
40
41
|
|
|
41
42
|
def enable=(enable)
|
|
42
|
-
@enable = enable
|
|
43
|
+
@enable = @n_plus_one_query_enable = @unused_eager_loading_enable = @counter_cache_enable = enable
|
|
43
44
|
if enable?
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
begin
|
|
50
|
-
require 'active_record'
|
|
51
|
-
Bullet::ActiveRecord.enable
|
|
52
|
-
if Rails.version =~ /\A2./
|
|
53
|
-
Bullet::ActionController.enable
|
|
54
|
-
end
|
|
55
|
-
rescue LoadError
|
|
45
|
+
reset_whitelist
|
|
46
|
+
unless orm_pathches_applied
|
|
47
|
+
self.orm_pathches_applied = true
|
|
48
|
+
Bullet::Mongoid.enable if mongoid?
|
|
49
|
+
Bullet::ActiveRecord.enable if active_record?
|
|
56
50
|
end
|
|
57
51
|
end
|
|
58
52
|
end
|
|
59
53
|
|
|
60
54
|
def enable?
|
|
61
|
-
|
|
55
|
+
!!@enable
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def n_plus_one_query_enable?
|
|
59
|
+
self.enable? && !!@n_plus_one_query_enable
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def unused_eager_loading_enable?
|
|
63
|
+
self.enable? && !!@unused_eager_loading_enable
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def counter_cache_enable?
|
|
67
|
+
self.enable? && !!@counter_cache_enable
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def stacktrace_includes
|
|
71
|
+
@stacktrace_includes || []
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def add_whitelist(options)
|
|
75
|
+
@whitelist[options[:type]][options[:class_name].classify] ||= []
|
|
76
|
+
@whitelist[options[:type]][options[:class_name].classify] << options[:association].to_sym
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def get_whitelist_associations(type, class_name)
|
|
80
|
+
Array(@whitelist[type][class_name])
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def reset_whitelist
|
|
84
|
+
@whitelist = {:n_plus_one_query => {}, :unused_eager_loading => {}, :counter_cache => {}}
|
|
62
85
|
end
|
|
63
86
|
|
|
64
87
|
def bullet_logger=(active)
|
|
65
88
|
if active
|
|
66
|
-
|
|
67
|
-
|
|
89
|
+
require 'fileutils'
|
|
90
|
+
root_path = "#{rails? ? Rails.root.to_s : Dir.pwd}"
|
|
91
|
+
FileUtils.mkdir_p(root_path + '/log')
|
|
92
|
+
bullet_log_file = File.open("#{root_path}/log/bullet.log", 'a+')
|
|
93
|
+
bullet_log_file.sync = true
|
|
68
94
|
UniformNotifier.customized_logger = bullet_log_file
|
|
69
95
|
end
|
|
70
96
|
end
|
|
71
97
|
|
|
98
|
+
def debug(title, message)
|
|
99
|
+
puts "[Bullet][#{title}] #{message}" if ENV['BULLET_DEBUG'] == 'true'
|
|
100
|
+
end
|
|
101
|
+
|
|
72
102
|
def start_request
|
|
73
|
-
|
|
74
|
-
|
|
103
|
+
Thread.current[:bullet_start] = true
|
|
104
|
+
Thread.current[:bullet_notification_collector] = Bullet::NotificationCollector.new
|
|
105
|
+
|
|
106
|
+
Thread.current[:bullet_object_associations] = Bullet::Registry::Base.new
|
|
107
|
+
Thread.current[:bullet_call_object_associations] = Bullet::Registry::Base.new
|
|
108
|
+
Thread.current[:bullet_possible_objects] = Bullet::Registry::Object.new
|
|
109
|
+
Thread.current[:bullet_impossible_objects] = Bullet::Registry::Object.new
|
|
110
|
+
Thread.current[:bullet_inversed_objects] = Bullet::Registry::Base.new
|
|
111
|
+
Thread.current[:bullet_eager_loadings] = Bullet::Registry::Association.new
|
|
112
|
+
|
|
113
|
+
Thread.current[:bullet_counter_possible_objects] ||= Bullet::Registry::Object.new
|
|
114
|
+
Thread.current[:bullet_counter_impossible_objects] ||= Bullet::Registry::Object.new
|
|
75
115
|
end
|
|
76
116
|
|
|
77
117
|
def end_request
|
|
78
|
-
|
|
118
|
+
Thread.current[:bullet_start] = nil
|
|
119
|
+
Thread.current[:bullet_notification_collector] = nil
|
|
120
|
+
|
|
121
|
+
Thread.current[:bullet_object_associations] = nil
|
|
122
|
+
Thread.current[:bullet_call_object_associations] = nil
|
|
123
|
+
Thread.current[:bullet_possible_objects] = nil
|
|
124
|
+
Thread.current[:bullet_impossible_objects] = nil
|
|
125
|
+
Thread.current[:bullet_inversed_objects] = nil
|
|
126
|
+
Thread.current[:bullet_eager_loadings] = nil
|
|
127
|
+
|
|
128
|
+
Thread.current[:bullet_counter_possible_objects] = nil
|
|
129
|
+
Thread.current[:bullet_counter_impossible_objects] = nil
|
|
79
130
|
end
|
|
80
131
|
|
|
81
|
-
def
|
|
82
|
-
|
|
132
|
+
def start?
|
|
133
|
+
Thread.current[:bullet_start]
|
|
83
134
|
end
|
|
84
135
|
|
|
85
136
|
def notification_collector
|
|
86
|
-
|
|
137
|
+
Thread.current[:bullet_notification_collector]
|
|
87
138
|
end
|
|
88
139
|
|
|
89
140
|
def notification?
|
|
90
|
-
|
|
141
|
+
return unless start?
|
|
142
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
|
91
143
|
notification_collector.notifications_present?
|
|
92
144
|
end
|
|
93
145
|
|
|
@@ -106,6 +158,34 @@ module Bullet
|
|
|
106
158
|
end
|
|
107
159
|
end
|
|
108
160
|
|
|
161
|
+
def footer_info
|
|
162
|
+
info = []
|
|
163
|
+
for_each_active_notifier_with_notification do |notification|
|
|
164
|
+
info << notification.short_notice
|
|
165
|
+
end
|
|
166
|
+
info
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def warnings
|
|
170
|
+
notification_collector.collection.inject({}) do |warnings, notification|
|
|
171
|
+
warning_type = notification.class.to_s.split(':').last.tableize
|
|
172
|
+
warnings[warning_type] ||= []
|
|
173
|
+
warnings[warning_type] << notification
|
|
174
|
+
warnings
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def profile
|
|
179
|
+
Bullet.start_request if Bullet.enable?
|
|
180
|
+
|
|
181
|
+
yield
|
|
182
|
+
|
|
183
|
+
if Bullet.enable? && Bullet.notification?
|
|
184
|
+
Bullet.perform_out_of_channel_notifications
|
|
185
|
+
end
|
|
186
|
+
Bullet.end_request if Bullet.enable?
|
|
187
|
+
end
|
|
188
|
+
|
|
109
189
|
private
|
|
110
190
|
def for_each_active_notifier_with_notification
|
|
111
191
|
UniformNotifier.active_notifiers.each do |notifier|
|
data/perf/benchmark.rb
CHANGED
|
@@ -27,7 +27,7 @@ class User < ActiveRecord::Base
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
# create database bullet_benchmark;
|
|
30
|
-
ActiveRecord::Base.establish_connection(:adapter => '
|
|
30
|
+
ActiveRecord::Base.establish_connection(:adapter => 'mysql2', :database => 'bullet_benchmark', :server => '/tmp/mysql.socket', :username => 'root')
|
|
31
31
|
|
|
32
32
|
ActiveRecord::Base.connection.tables.each do |table|
|
|
33
33
|
ActiveRecord::Base.connection.drop_table(table)
|
|
@@ -79,8 +79,6 @@ puts "Start benchmarking..."
|
|
|
79
79
|
|
|
80
80
|
Bullet.enable = true
|
|
81
81
|
|
|
82
|
-
PerfTools::CpuProfiler.start(ARGV[0]|| "benchmark_profile") if defined? PerfTools
|
|
83
|
-
|
|
84
82
|
Benchmark.bm(70) do |bm|
|
|
85
83
|
bm.report("Querying & Iterating #{posts_size} Posts with #{comments_size} Comments and #{users_size} Users") do
|
|
86
84
|
10.times do
|
|
@@ -98,7 +96,6 @@ Benchmark.bm(70) do |bm|
|
|
|
98
96
|
end
|
|
99
97
|
end
|
|
100
98
|
|
|
101
|
-
PerfTools::CpuProfiler.stop if defined? PerfTools
|
|
102
99
|
puts "End benchmarking..."
|
|
103
100
|
|
|
104
101
|
|
|
@@ -7,86 +7,18 @@ module Bullet
|
|
|
7
7
|
@post1 = Post.first
|
|
8
8
|
@post2 = Post.last
|
|
9
9
|
end
|
|
10
|
-
before(:each) { Association.clear }
|
|
11
|
-
|
|
12
|
-
context ".start_request" do
|
|
13
|
-
it "should set @@checked to false" do
|
|
14
|
-
Association.start_request
|
|
15
|
-
Association.class_variable_get(:@@checked).should be_false
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
context ".clear" do
|
|
20
|
-
it "should clear all class variables" do
|
|
21
|
-
Association.clear
|
|
22
|
-
Association.class_variable_get(:@@object_associations).should be_nil
|
|
23
|
-
Association.class_variable_get(:@@possible_objects).should be_nil
|
|
24
|
-
Association.class_variable_get(:@@impossible_objects).should be_nil
|
|
25
|
-
Association.class_variable_get(:@@call_object_associations).should be_nil
|
|
26
|
-
Association.class_variable_get(:@@eager_loadings).should be_nil
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
10
|
|
|
30
11
|
context ".add_object_association" do
|
|
31
12
|
it "should add object, associations pair" do
|
|
32
13
|
Association.add_object_associations(@post1, :associations)
|
|
33
|
-
Association.send(:object_associations).
|
|
14
|
+
expect(Association.send(:object_associations)).to be_include(@post1.bullet_key, :associations)
|
|
34
15
|
end
|
|
35
16
|
end
|
|
36
17
|
|
|
37
18
|
context ".add_call_object_associations" do
|
|
38
19
|
it "should add call object, associations pair" do
|
|
39
20
|
Association.add_call_object_associations(@post1, :associations)
|
|
40
|
-
Association.send(:call_object_associations).
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
context ".add_possible_objects" do
|
|
45
|
-
it "should add possible objects" do
|
|
46
|
-
Association.add_possible_objects([@post1, @post2])
|
|
47
|
-
Association.send(:possible_objects).should be_include(@post1.bullet_ar_key)
|
|
48
|
-
Association.send(:possible_objects).should be_include(@post2.bullet_ar_key)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
it "should not raise error if object is nil" do
|
|
52
|
-
lambda { Association.add_possible_objects(nil) }.should_not raise_error
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
context ".add_impossible_object" do
|
|
57
|
-
it "should add impossible object" do
|
|
58
|
-
Association.add_impossible_object(@post1)
|
|
59
|
-
Association.send(:impossible_objects).should be_include(@post1.bullet_ar_key)
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
context ".add_eager_loadings" do
|
|
64
|
-
it "should add objects, associations pair when eager_loadings are empty" do
|
|
65
|
-
Association.add_eager_loadings([@post1, @post2], :associations)
|
|
66
|
-
Association.send(:eager_loadings).should be_include([@post1.bullet_ar_key, @post2.bullet_ar_key], :associations)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
it "should add objects, associations pair for existing eager_loadings" do
|
|
70
|
-
Association.add_eager_loadings([@post1, @post2], :association1)
|
|
71
|
-
Association.add_eager_loadings([@post1, @post2], :association2)
|
|
72
|
-
Association.send(:eager_loadings).should be_include([@post1.bullet_ar_key, @post2.bullet_ar_key], :association1)
|
|
73
|
-
Association.send(:eager_loadings).should be_include([@post1.bullet_ar_key, @post2.bullet_ar_key], :association2)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
it "should merge objects, associations pair for existing eager_loadings" do
|
|
77
|
-
Association.add_eager_loadings([@post1], :association1)
|
|
78
|
-
Association.add_eager_loadings([@post1, @post2], :association2)
|
|
79
|
-
Association.send(:eager_loadings).should be_include([@post1.bullet_ar_key], :association1)
|
|
80
|
-
Association.send(:eager_loadings).should be_include([@post1.bullet_ar_key], :association2)
|
|
81
|
-
Association.send(:eager_loadings).should be_include([@post1.bullet_ar_key, @post2.bullet_ar_key], :association2)
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
it "should delete objects, associations pair for existing eager_loadings" do
|
|
85
|
-
Association.add_eager_loadings([@post1, @post2], :association1)
|
|
86
|
-
Association.add_eager_loadings([@post1], :association2)
|
|
87
|
-
Association.send(:eager_loadings).should be_include([@post1.bullet_ar_key], :association1)
|
|
88
|
-
Association.send(:eager_loadings).should be_include([@post1.bullet_ar_key], :association2)
|
|
89
|
-
Association.send(:eager_loadings).should be_include([@post2.bullet_ar_key], :association1)
|
|
21
|
+
expect(Association.send(:call_object_associations)).to be_include(@post1.bullet_key, :associations)
|
|
90
22
|
end
|
|
91
23
|
end
|
|
92
24
|
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Bullet
|
|
4
|
+
module Detector
|
|
5
|
+
describe CounterCache do
|
|
6
|
+
before :all do
|
|
7
|
+
@post1 = Post.first
|
|
8
|
+
@post2 = Post.last
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
context ".add_counter_cache" do
|
|
12
|
+
it "should create notification if conditions met" do
|
|
13
|
+
expect(CounterCache).to receive(:conditions_met?).with(@post1.bullet_key, [:comments]).and_return(true)
|
|
14
|
+
expect(CounterCache).to receive(:create_notification).with("Post", [:comments])
|
|
15
|
+
CounterCache.add_counter_cache(@post1, [:comments])
|
|
16
|
+
end
|
|
17
|
+
|
|
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)
|
|
20
|
+
expect(CounterCache).to receive(:create_notification).never
|
|
21
|
+
CounterCache.add_counter_cache(@post1, [:comments])
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context ".add_possible_objects" do
|
|
26
|
+
it "should add possible objects" do
|
|
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)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should add impossible object" do
|
|
33
|
+
CounterCache.add_impossible_object(@post1)
|
|
34
|
+
expect(CounterCache.send(:impossible_objects)).to be_include(@post1.bullet_key)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
context ".conditions_met?" do
|
|
39
|
+
it "should be true when object is possible, not impossible" do
|
|
40
|
+
CounterCache.add_possible_objects(@post1)
|
|
41
|
+
expect(CounterCache.send(:conditions_met?, @post1.bullet_key, :associations)).to eq true
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "should be false when object is not possible" do
|
|
45
|
+
expect(CounterCache.send(:conditions_met?, @post1.bullet_key, :associations)).to eq false
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should be true when object is possible, and impossible" do
|
|
49
|
+
CounterCache.add_possible_objects(@post1)
|
|
50
|
+
CounterCache.add_impossible_object(@post1)
|
|
51
|
+
expect(CounterCache.send(:conditions_met?, @post1.bullet_key, :associations)).to eq false
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -3,17 +3,14 @@ require 'spec_helper'
|
|
|
3
3
|
module Bullet
|
|
4
4
|
module Detector
|
|
5
5
|
describe NPlusOneQuery do
|
|
6
|
-
before(:all)
|
|
7
|
-
|
|
6
|
+
before(:all) do
|
|
7
|
+
@post = Post.first
|
|
8
|
+
@post2 = Post.last
|
|
9
|
+
end
|
|
8
10
|
|
|
9
11
|
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
12
|
it "should add call_object_associations" do
|
|
16
|
-
NPlusOneQuery.
|
|
13
|
+
expect(NPlusOneQuery).to receive(:add_call_object_associations).with(@post, :associations)
|
|
17
14
|
NPlusOneQuery.call_association(@post, :associations)
|
|
18
15
|
end
|
|
19
16
|
end
|
|
@@ -21,74 +18,121 @@ module Bullet
|
|
|
21
18
|
context ".possible?" do
|
|
22
19
|
it "should be true if possible_objects contain" do
|
|
23
20
|
NPlusOneQuery.add_possible_objects(@post)
|
|
24
|
-
NPlusOneQuery.send(:possible?, @post.
|
|
21
|
+
expect(NPlusOneQuery.send(:possible?, @post.bullet_key)).to eq true
|
|
25
22
|
end
|
|
26
23
|
end
|
|
27
24
|
|
|
28
25
|
context ".impossible?" do
|
|
29
26
|
it "should be true if impossible_objects contain" do
|
|
30
27
|
NPlusOneQuery.add_impossible_object(@post)
|
|
31
|
-
NPlusOneQuery.send(:impossible?, @post.
|
|
28
|
+
expect(NPlusOneQuery.send(:impossible?, @post.bullet_key)).to eq true
|
|
32
29
|
end
|
|
33
30
|
end
|
|
34
31
|
|
|
35
32
|
context ".association?" do
|
|
36
33
|
it "should be true if object, associations pair is already existed" do
|
|
37
34
|
NPlusOneQuery.add_object_associations(@post, :association)
|
|
38
|
-
NPlusOneQuery.send(:association?, @post.
|
|
35
|
+
expect(NPlusOneQuery.send(:association?, @post.bullet_key, :association)).to eq true
|
|
39
36
|
end
|
|
40
37
|
|
|
41
38
|
it "should be false if object, association pair is not existed" do
|
|
42
39
|
NPlusOneQuery.add_object_associations(@post, :association1)
|
|
43
|
-
NPlusOneQuery.send(:association?, @post.
|
|
40
|
+
expect(NPlusOneQuery.send(:association?, @post.bullet_key, :associatio2)).to eq false
|
|
44
41
|
end
|
|
45
42
|
end
|
|
46
43
|
|
|
47
44
|
context ".conditions_met?" do
|
|
48
45
|
it "should be true if object is possible, not impossible and object, associations pair is not already existed" do
|
|
49
|
-
NPlusOneQuery.
|
|
50
|
-
NPlusOneQuery.
|
|
51
|
-
NPlusOneQuery.
|
|
52
|
-
NPlusOneQuery.send(:conditions_met?, @post.
|
|
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
|
|
53
50
|
end
|
|
54
51
|
|
|
55
52
|
it "should be false if object is not possible, not impossible and object, associations pair is not already existed" do
|
|
56
|
-
NPlusOneQuery.
|
|
57
|
-
NPlusOneQuery.
|
|
58
|
-
NPlusOneQuery.
|
|
59
|
-
NPlusOneQuery.send(:conditions_met?, @post.
|
|
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
|
|
60
57
|
end
|
|
61
58
|
|
|
62
59
|
it "should be false if object is possible, but impossible and object, associations pair is not already existed" do
|
|
63
|
-
NPlusOneQuery.
|
|
64
|
-
NPlusOneQuery.
|
|
65
|
-
NPlusOneQuery.
|
|
66
|
-
NPlusOneQuery.send(:conditions_met?, @post.
|
|
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
|
|
67
64
|
end
|
|
68
65
|
|
|
69
66
|
it "should be false if object is possible, not impossible and object, associations pair is already existed" do
|
|
70
|
-
NPlusOneQuery.
|
|
71
|
-
NPlusOneQuery.
|
|
72
|
-
NPlusOneQuery.
|
|
73
|
-
NPlusOneQuery.send(:conditions_met?, @post.
|
|
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
|
|
74
71
|
end
|
|
75
72
|
end
|
|
76
73
|
|
|
77
74
|
context ".call_association" do
|
|
78
75
|
it "should create notification if conditions met" do
|
|
79
|
-
NPlusOneQuery.
|
|
80
|
-
NPlusOneQuery.
|
|
81
|
-
NPlusOneQuery.
|
|
76
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post.bullet_key, :association).and_return(true)
|
|
77
|
+
expect(NPlusOneQuery).to receive(:caller_in_project).and_return(["caller"])
|
|
78
|
+
expect(NPlusOneQuery).to receive(:create_notification).with(["caller"], "Post", :association)
|
|
82
79
|
NPlusOneQuery.call_association(@post, :association)
|
|
83
80
|
end
|
|
84
81
|
|
|
85
82
|
it "should not create notification if conditions not met" do
|
|
86
|
-
NPlusOneQuery.
|
|
87
|
-
NPlusOneQuery.
|
|
88
|
-
NPlusOneQuery.
|
|
83
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post.bullet_key, :association).and_return(false)
|
|
84
|
+
expect(NPlusOneQuery).not_to receive(:caller_in_project!)
|
|
85
|
+
expect(NPlusOneQuery).not_to receive(:create_notification).with("Post", :association)
|
|
89
86
|
NPlusOneQuery.call_association(@post, :association)
|
|
90
87
|
end
|
|
91
88
|
end
|
|
89
|
+
|
|
90
|
+
context ".caller_in_project" do
|
|
91
|
+
it "should include only paths that are in the project" do
|
|
92
|
+
in_project = File.join(Dir.pwd, 'abc', 'abc.rb')
|
|
93
|
+
not_in_project = '/def/def.rb'
|
|
94
|
+
|
|
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)
|
|
97
|
+
expect(NPlusOneQuery).to receive(:create_notification).with([in_project], "Post", :association)
|
|
98
|
+
NPlusOneQuery.call_association(@post, :association)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
context "stacktrace_includes" do
|
|
102
|
+
before { Bullet.stacktrace_includes = [ 'def' ] }
|
|
103
|
+
after { Bullet.stacktrace_includes = nil }
|
|
104
|
+
|
|
105
|
+
it "should include paths that are in the stacktrace_include list" do
|
|
106
|
+
in_project = File.join(Dir.pwd, 'abc', 'abc.rb')
|
|
107
|
+
included_gem = '/def/def.rb'
|
|
108
|
+
excluded_gem = '/ghi/ghi.rb'
|
|
109
|
+
|
|
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)
|
|
112
|
+
expect(NPlusOneQuery).to receive(:create_notification).with([in_project, included_gem], "Post", :association)
|
|
113
|
+
NPlusOneQuery.call_association(@post, :association)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
context ".add_possible_objects" do
|
|
119
|
+
it "should add possible objects" do
|
|
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)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "should not raise error if object is nil" do
|
|
126
|
+
expect { NPlusOneQuery.add_possible_objects(nil) }.not_to raise_error
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
context ".add_impossible_object" do
|
|
131
|
+
it "should add impossible object" do
|
|
132
|
+
NPlusOneQuery.add_impossible_object(@post)
|
|
133
|
+
expect(NPlusOneQuery.send(:impossible_objects)).to be_include(@post.bullet_key)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
92
136
|
end
|
|
93
137
|
end
|
|
94
138
|
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Bullet
|
|
4
|
+
module Detector
|
|
5
|
+
describe UnusedEagerLoading do
|
|
6
|
+
before(:all) do
|
|
7
|
+
@post = Post.first
|
|
8
|
+
@post2 = Post.last
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
context ".call_associations" do
|
|
12
|
+
it "should get empty array if eager_loadings" do
|
|
13
|
+
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to be_empty
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "should get call associations if object and association are both in eager_loadings and call_object_associations" do
|
|
17
|
+
UnusedEagerLoading.add_eager_loadings([@post], :association)
|
|
18
|
+
UnusedEagerLoading.add_call_object_associations(@post, :association)
|
|
19
|
+
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to eq([:association])
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "should not get call associations if not exist in call_object_associations" do
|
|
23
|
+
UnusedEagerLoading.add_eager_loadings([@post], :association)
|
|
24
|
+
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to be_empty
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
context ".diff_object_associations" do
|
|
29
|
+
it "should return associations not exist in call_association" do
|
|
30
|
+
expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to eq([:association])
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "should return empty if associations exist in call_association" do
|
|
34
|
+
UnusedEagerLoading.add_eager_loadings([@post], :association)
|
|
35
|
+
UnusedEagerLoading.add_call_object_associations(@post, :association)
|
|
36
|
+
expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to be_empty
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
context ".check_unused_preload_associations" do
|
|
41
|
+
it "should create notification if object_association_diff is not empty" do
|
|
42
|
+
UnusedEagerLoading.add_object_associations(@post, :association)
|
|
43
|
+
expect(UnusedEagerLoading).to receive(:create_notification).with("Post", [:association])
|
|
44
|
+
UnusedEagerLoading.check_unused_preload_associations
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should not create notification if object_association_diff is empty" do
|
|
48
|
+
UnusedEagerLoading.add_object_associations(@post, :association)
|
|
49
|
+
UnusedEagerLoading.add_eager_loadings([@post], :association)
|
|
50
|
+
UnusedEagerLoading.add_call_object_associations(@post, :association)
|
|
51
|
+
expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to be_empty
|
|
52
|
+
expect(UnusedEagerLoading).not_to receive(:create_notification).with("Post", [:association])
|
|
53
|
+
UnusedEagerLoading.check_unused_preload_associations
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
context ".add_eager_loadings" do
|
|
58
|
+
it "should add objects, associations pair when eager_loadings are empty" do
|
|
59
|
+
UnusedEagerLoading.add_eager_loadings([@post, @post2], :associations)
|
|
60
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key, @post2.bullet_key], :associations)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "should add objects, associations pair for existing eager_loadings" do
|
|
64
|
+
UnusedEagerLoading.add_eager_loadings([@post, @post2], :association1)
|
|
65
|
+
UnusedEagerLoading.add_eager_loadings([@post, @post2], :association2)
|
|
66
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key, @post2.bullet_key], :association1)
|
|
67
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key, @post2.bullet_key], :association2)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "should merge objects, associations pair for existing eager_loadings" do
|
|
71
|
+
UnusedEagerLoading.add_eager_loadings([@post], :association1)
|
|
72
|
+
UnusedEagerLoading.add_eager_loadings([@post, @post2], :association2)
|
|
73
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association1)
|
|
74
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association2)
|
|
75
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key, @post2.bullet_key], :association2)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "should delete objects, associations pair for existing eager_loadings" do
|
|
79
|
+
UnusedEagerLoading.add_eager_loadings([@post, @post2], :association1)
|
|
80
|
+
UnusedEagerLoading.add_eager_loadings([@post], :association2)
|
|
81
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association1)
|
|
82
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association2)
|
|
83
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post2.bullet_key], :association1)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|