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
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
|
@@ -5,11 +5,12 @@ source "http://rubygems.org"
|
|
|
5
5
|
gemspec
|
|
6
6
|
|
|
7
7
|
gem 'rails'
|
|
8
|
-
gem 'sqlite3
|
|
8
|
+
gem 'sqlite3'
|
|
9
9
|
gem 'mysql'
|
|
10
10
|
gem 'activerecord-import'
|
|
11
11
|
|
|
12
12
|
gem "rspec"
|
|
13
|
-
gem "
|
|
13
|
+
gem "guard"
|
|
14
|
+
gem "guard-rspec"
|
|
14
15
|
|
|
15
16
|
gem "perftools.rb"
|
data/Gemfile.lock
CHANGED
|
@@ -1,95 +1,110 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
bullet (2.
|
|
4
|
+
bullet (2.3.0)
|
|
5
5
|
uniform_notifier (~> 1.0.0)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
8
8
|
remote: http://rubygems.org/
|
|
9
9
|
specs:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
rack (~> 1.
|
|
21
|
-
rack-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
10
|
+
actionmailer (3.2.2)
|
|
11
|
+
actionpack (= 3.2.2)
|
|
12
|
+
mail (~> 2.4.0)
|
|
13
|
+
actionpack (3.2.2)
|
|
14
|
+
activemodel (= 3.2.2)
|
|
15
|
+
activesupport (= 3.2.2)
|
|
16
|
+
builder (~> 3.0.0)
|
|
17
|
+
erubis (~> 2.7.0)
|
|
18
|
+
journey (~> 1.0.1)
|
|
19
|
+
rack (~> 1.4.0)
|
|
20
|
+
rack-cache (~> 1.1)
|
|
21
|
+
rack-test (~> 0.6.1)
|
|
22
|
+
sprockets (~> 2.1.2)
|
|
23
|
+
activemodel (3.2.2)
|
|
24
|
+
activesupport (= 3.2.2)
|
|
25
|
+
builder (~> 3.0.0)
|
|
26
|
+
activerecord (3.2.2)
|
|
27
|
+
activemodel (= 3.2.2)
|
|
28
|
+
activesupport (= 3.2.2)
|
|
29
|
+
arel (~> 3.0.2)
|
|
30
|
+
tzinfo (~> 0.3.29)
|
|
31
|
+
activerecord-import (0.2.9)
|
|
32
|
+
activerecord (~> 3.0)
|
|
33
|
+
activerecord (~> 3.0)
|
|
34
|
+
activeresource (3.2.2)
|
|
35
|
+
activemodel (= 3.2.2)
|
|
36
|
+
activesupport (= 3.2.2)
|
|
37
|
+
activesupport (3.2.2)
|
|
38
|
+
i18n (~> 0.6)
|
|
39
|
+
multi_json (~> 1.0)
|
|
40
|
+
arel (3.0.2)
|
|
41
|
+
builder (3.0.0)
|
|
41
42
|
diff-lcs (1.1.3)
|
|
42
|
-
erubis (2.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
erubis (2.7.0)
|
|
44
|
+
ffi (1.0.11)
|
|
45
|
+
guard (1.0.1)
|
|
46
|
+
ffi (>= 0.5.0)
|
|
47
|
+
thor (~> 0.14.6)
|
|
48
|
+
guard-rspec (0.6.0)
|
|
49
|
+
guard (>= 0.10.0)
|
|
50
|
+
hike (1.2.1)
|
|
51
|
+
i18n (0.6.0)
|
|
52
|
+
journey (1.0.3)
|
|
53
|
+
json (1.6.5)
|
|
54
|
+
mail (2.4.4)
|
|
48
55
|
i18n (>= 0.4.0)
|
|
49
56
|
mime-types (~> 1.16)
|
|
50
57
|
treetop (~> 1.4.8)
|
|
51
|
-
mime-types (1.
|
|
58
|
+
mime-types (1.18)
|
|
59
|
+
multi_json (1.1.0)
|
|
52
60
|
mysql (2.8.1)
|
|
53
|
-
perftools.rb (0.
|
|
61
|
+
perftools.rb (2.0.0)
|
|
54
62
|
polyglot (0.3.3)
|
|
55
|
-
rack (1.
|
|
56
|
-
rack-
|
|
57
|
-
rack (>=
|
|
58
|
-
rack-
|
|
63
|
+
rack (1.4.1)
|
|
64
|
+
rack-cache (1.2)
|
|
65
|
+
rack (>= 0.4)
|
|
66
|
+
rack-ssl (1.3.2)
|
|
67
|
+
rack
|
|
68
|
+
rack-test (0.6.1)
|
|
59
69
|
rack (>= 1.0)
|
|
60
|
-
rails (3.
|
|
61
|
-
actionmailer (= 3.
|
|
62
|
-
actionpack (= 3.
|
|
63
|
-
activerecord (= 3.
|
|
64
|
-
activeresource (= 3.
|
|
65
|
-
activesupport (= 3.
|
|
70
|
+
rails (3.2.2)
|
|
71
|
+
actionmailer (= 3.2.2)
|
|
72
|
+
actionpack (= 3.2.2)
|
|
73
|
+
activerecord (= 3.2.2)
|
|
74
|
+
activeresource (= 3.2.2)
|
|
75
|
+
activesupport (= 3.2.2)
|
|
66
76
|
bundler (~> 1.0)
|
|
67
|
-
railties (= 3.
|
|
68
|
-
railties (3.
|
|
69
|
-
actionpack (= 3.
|
|
70
|
-
activesupport (= 3.
|
|
77
|
+
railties (= 3.2.2)
|
|
78
|
+
railties (3.2.2)
|
|
79
|
+
actionpack (= 3.2.2)
|
|
80
|
+
activesupport (= 3.2.2)
|
|
81
|
+
rack-ssl (~> 1.3.2)
|
|
71
82
|
rake (>= 0.8.7)
|
|
72
83
|
rdoc (~> 3.4)
|
|
73
|
-
thor (~> 0.14.
|
|
84
|
+
thor (~> 0.14.6)
|
|
74
85
|
rake (0.9.2.2)
|
|
75
|
-
rdoc (3.
|
|
86
|
+
rdoc (3.12)
|
|
76
87
|
json (~> 1.4)
|
|
77
|
-
rspec (2.
|
|
78
|
-
rspec-core (~> 2.
|
|
79
|
-
rspec-expectations (~> 2.
|
|
80
|
-
rspec-mocks (~> 2.
|
|
81
|
-
rspec-core (2.
|
|
82
|
-
rspec-expectations (2.
|
|
83
|
-
diff-lcs (~> 1.1.
|
|
84
|
-
rspec-mocks (2.
|
|
85
|
-
|
|
88
|
+
rspec (2.9.0)
|
|
89
|
+
rspec-core (~> 2.9.0)
|
|
90
|
+
rspec-expectations (~> 2.9.0)
|
|
91
|
+
rspec-mocks (~> 2.9.0)
|
|
92
|
+
rspec-core (2.9.0)
|
|
93
|
+
rspec-expectations (2.9.0)
|
|
94
|
+
diff-lcs (~> 1.1.3)
|
|
95
|
+
rspec-mocks (2.9.0)
|
|
96
|
+
sprockets (2.1.2)
|
|
97
|
+
hike (~> 1.2)
|
|
98
|
+
rack (~> 1.0)
|
|
99
|
+
tilt (~> 1.1, != 1.3.0)
|
|
100
|
+
sqlite3 (1.3.5)
|
|
86
101
|
thor (0.14.6)
|
|
102
|
+
tilt (1.3.3)
|
|
87
103
|
treetop (1.4.10)
|
|
88
104
|
polyglot
|
|
89
105
|
polyglot (>= 0.3.1)
|
|
90
|
-
tzinfo (0.3.
|
|
106
|
+
tzinfo (0.3.32)
|
|
91
107
|
uniform_notifier (1.0.1)
|
|
92
|
-
watchr (0.7)
|
|
93
108
|
|
|
94
109
|
PLATFORMS
|
|
95
110
|
ruby
|
|
@@ -97,9 +112,10 @@ PLATFORMS
|
|
|
97
112
|
DEPENDENCIES
|
|
98
113
|
activerecord-import
|
|
99
114
|
bullet!
|
|
115
|
+
guard
|
|
116
|
+
guard-rspec
|
|
100
117
|
mysql
|
|
101
118
|
perftools.rb
|
|
102
119
|
rails
|
|
103
120
|
rspec
|
|
104
|
-
sqlite3
|
|
105
|
-
watchr
|
|
121
|
+
sqlite3
|
data/Guardfile
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# A sample Guardfile
|
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
|
3
|
+
|
|
4
|
+
guard 'rspec', :version => 2, :all_after_pass => false, :all_on_start => false, :cli => "--color --format nested --fail-fast" do
|
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
|
7
|
+
watch('spec/spec_helper.rb') { "spec" }
|
|
8
|
+
end
|
data/README.textile
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
h1. Bullet
|
|
2
2
|
|
|
3
|
-
!https://secure.travis-ci.org/flyerhzm/
|
|
3
|
+
!https://secure.travis-ci.org/flyerhzm/bullet.png!:http://travis-ci.org/flyerhzm/bullet
|
|
4
4
|
|
|
5
5
|
The Bullet plugin/gem is designed to help you increase your application's performance by reducing the number of queries it makes. It will watch your queries while you develop your application and notify you when you should add eager loading (N+1 queries), when you're using eager loading that isn't necessary and when you should use counter cache.
|
|
6
6
|
|
|
7
7
|
Best practice is to use Bullet in development mode or custom mode (staging, profile, etc.). The last thing you want is your clients getting alerts about how lazy you are.
|
|
8
8
|
|
|
9
|
-
The Bullet plugin/gem now supports rails 2.1, 2.2, 2.3, 3.0 and 3.
|
|
9
|
+
The Bullet plugin/gem now supports rails 2.1, 2.2, 2.3, 3.0, 3.1 and 3.2.
|
|
10
10
|
|
|
11
11
|
****************************************************************************
|
|
12
12
|
|
|
@@ -2,8 +2,7 @@ module Bullet
|
|
|
2
2
|
class ActionController
|
|
3
3
|
def self.enable
|
|
4
4
|
require 'action_controller'
|
|
5
|
-
|
|
6
|
-
when /^2.3/
|
|
5
|
+
if Rails.version =~ /\A2.3/
|
|
7
6
|
::ActionController::Dispatcher.middleware.use Bullet::Rack
|
|
8
7
|
::ActionController::Dispatcher.class_eval do
|
|
9
8
|
class <<self
|
|
@@ -14,7 +13,7 @@ module Bullet
|
|
|
14
13
|
end
|
|
15
14
|
end
|
|
16
15
|
end
|
|
17
|
-
|
|
16
|
+
elsif Rails.version =~ /\A2.[12]/
|
|
18
17
|
::ActionController::Dispatcher.class_eval do
|
|
19
18
|
alias_method :origin_reload_application, :reload_application
|
|
20
19
|
def reload_application
|
|
@@ -30,7 +29,8 @@ module Bullet
|
|
|
30
29
|
response = origin_process(request, response, method = :perform_action, *arguments)
|
|
31
30
|
|
|
32
31
|
if Bullet.notification?
|
|
33
|
-
if response.headers["type"]
|
|
32
|
+
if response.headers["type"] && response.headers["type"].include?('text/html') &&
|
|
33
|
+
response.body.include?("<html>") && response.body.include?("</html>")
|
|
34
34
|
response.body <<= Bullet.gather_inline_notifications
|
|
35
35
|
response.headers["Content-Length"] = response.body.length.to_s
|
|
36
36
|
end
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
module Bullet
|
|
2
|
-
LOAD_TARGET_RGX = /load_target/
|
|
3
2
|
module ActiveRecord
|
|
4
3
|
def self.enable
|
|
5
4
|
require 'active_record'
|
|
@@ -11,7 +10,7 @@ module Bullet
|
|
|
11
10
|
def find_every(options)
|
|
12
11
|
records = origin_find_every(options)
|
|
13
12
|
|
|
14
|
-
if records
|
|
13
|
+
if records
|
|
15
14
|
if records.size > 1
|
|
16
15
|
Bullet::Detector::Association.add_possible_objects(records)
|
|
17
16
|
Bullet::Detector::Counter.add_possible_objects(records)
|
|
@@ -41,7 +40,7 @@ module Bullet
|
|
|
41
40
|
end
|
|
42
41
|
end
|
|
43
42
|
|
|
44
|
-
::ActiveRecord::Associations::ClassMethods.class_eval do
|
|
43
|
+
::ActiveRecord::Associations::ClassMethods.class_eval do
|
|
45
44
|
# add include in named_scope
|
|
46
45
|
alias_method :origin_find_with_associations, :find_with_associations
|
|
47
46
|
def find_with_associations(options)
|
|
@@ -75,19 +74,19 @@ module Bullet
|
|
|
75
74
|
origin_load_target
|
|
76
75
|
end
|
|
77
76
|
end
|
|
78
|
-
|
|
77
|
+
|
|
79
78
|
::ActiveRecord::Associations::AssociationProxy.class_eval do
|
|
80
79
|
# call has_one and belong_to association
|
|
81
80
|
alias_method :origin_load_target, :load_target
|
|
82
81
|
def load_target
|
|
83
82
|
# avoid stack level too deep
|
|
84
83
|
result = origin_load_target
|
|
85
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless caller.
|
|
84
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless caller.any? {|c| c.include?("load_target") }
|
|
86
85
|
Bullet::Detector::Association.add_possible_objects(result)
|
|
87
86
|
result
|
|
88
87
|
end
|
|
89
88
|
end
|
|
90
|
-
|
|
89
|
+
|
|
91
90
|
::ActiveRecord::Associations::HasManyAssociation.class_eval do
|
|
92
91
|
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
|
93
92
|
def has_cached_counter?
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
module Bullet
|
|
2
|
-
LOAD_TARGET_RGX = /load_target/
|
|
3
2
|
module ActiveRecord
|
|
4
3
|
def self.enable
|
|
5
4
|
require 'active_record'
|
|
@@ -69,14 +68,14 @@ module Bullet
|
|
|
69
68
|
origin_load_target
|
|
70
69
|
end
|
|
71
70
|
end
|
|
72
|
-
|
|
71
|
+
|
|
73
72
|
::ActiveRecord::Associations::AssociationProxy.class_eval do
|
|
74
73
|
# call has_one and belong_to association
|
|
75
74
|
alias_method :origin_load_target, :load_target
|
|
76
75
|
def load_target
|
|
77
76
|
# avoid stack level too deep
|
|
78
77
|
result = origin_load_target
|
|
79
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless caller.
|
|
78
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless caller.any? { |c| c.include?("load_target") }
|
|
80
79
|
Bullet::Detector::Association.add_possible_objects(result)
|
|
81
80
|
result
|
|
82
81
|
end
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
module Bullet
|
|
2
|
-
LOAD_TARGET_RGX = /load_target/
|
|
3
2
|
module ActiveRecord
|
|
4
3
|
def self.enable
|
|
5
4
|
require 'active_record'
|
|
@@ -69,14 +68,13 @@ module Bullet
|
|
|
69
68
|
origin_load_target
|
|
70
69
|
end
|
|
71
70
|
end
|
|
72
|
-
|
|
73
|
-
::ActiveRecord::Associations::
|
|
74
|
-
# call has_one and
|
|
75
|
-
alias_method :
|
|
76
|
-
def
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless caller.find {|c| c =~ LOAD_TARGET_RGX }
|
|
71
|
+
|
|
72
|
+
::ActiveRecord::Associations::SingularAssociation.class_eval do
|
|
73
|
+
# call has_one and belongs_to associations
|
|
74
|
+
alias_method :origin_reader, :reader
|
|
75
|
+
def reader(force_reload = false)
|
|
76
|
+
result = origin_reader(force_reload)
|
|
77
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
80
78
|
Bullet::Detector::Association.add_possible_objects(result)
|
|
81
79
|
result
|
|
82
80
|
end
|
|
@@ -85,9 +83,9 @@ module Bullet
|
|
|
85
83
|
::ActiveRecord::Associations::HasManyAssociation.class_eval do
|
|
86
84
|
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
|
87
85
|
|
|
88
|
-
def has_cached_counter?
|
|
89
|
-
result = origin_has_cached_counter?
|
|
90
|
-
Bullet::Detector::Counter.add_counter_cache(
|
|
86
|
+
def has_cached_counter?(reflection = reflection)
|
|
87
|
+
result = origin_has_cached_counter?(reflection)
|
|
88
|
+
Bullet::Detector::Counter.add_counter_cache(owner, reflection.name) unless result
|
|
91
89
|
result
|
|
92
90
|
end
|
|
93
91
|
end
|
|
@@ -13,7 +13,6 @@ module Bullet
|
|
|
13
13
|
# derive from Bullet::Detector::Association - changing the variable
|
|
14
14
|
# in one subclass will make the change visible to all subclasses!
|
|
15
15
|
@@object_associations = nil
|
|
16
|
-
@@callers = nil
|
|
17
16
|
@@possible_objects = nil
|
|
18
17
|
@@impossible_objects = nil
|
|
19
18
|
@@call_object_associations = nil
|
|
@@ -21,83 +20,62 @@ module Bullet
|
|
|
21
20
|
end
|
|
22
21
|
|
|
23
22
|
def add_object_associations(object, associations)
|
|
24
|
-
object_associations.add(
|
|
23
|
+
object_associations.add(object.ar_key, associations)
|
|
25
24
|
end
|
|
26
25
|
|
|
27
26
|
def add_call_object_associations(object, associations)
|
|
28
|
-
|
|
27
|
+
if object.id
|
|
28
|
+
call_object_associations.add(object.ar_key, associations)
|
|
29
|
+
end
|
|
29
30
|
end
|
|
30
31
|
|
|
31
|
-
def add_possible_objects(
|
|
32
|
-
|
|
32
|
+
def add_possible_objects(object_or_objects)
|
|
33
|
+
if object_or_objects.is_a? Array
|
|
34
|
+
object_or_objects.each { |object| possible_objects.add object.ar_key }
|
|
35
|
+
else
|
|
36
|
+
possible_objects.add object_or_objects.ar_key
|
|
37
|
+
end
|
|
33
38
|
end
|
|
34
39
|
|
|
35
40
|
def add_impossible_object(object)
|
|
36
|
-
impossible_objects.add object
|
|
41
|
+
impossible_objects.add object.ar_key
|
|
37
42
|
end
|
|
38
43
|
|
|
39
44
|
def add_eager_loadings(objects, associations)
|
|
40
|
-
|
|
45
|
+
object_ar_keys = Array(objects).map(&:ar_key)
|
|
41
46
|
|
|
42
47
|
to_add = nil
|
|
43
48
|
to_merge, to_delete = [], []
|
|
44
49
|
eager_loadings.each do |k, v|
|
|
45
|
-
key_objects_overlap = k &
|
|
50
|
+
key_objects_overlap = k & object_ar_keys
|
|
46
51
|
|
|
47
52
|
next if key_objects_overlap.empty?
|
|
48
53
|
|
|
49
54
|
if key_objects_overlap == k
|
|
50
55
|
to_add = [k, associations]
|
|
51
56
|
break
|
|
52
|
-
|
|
53
57
|
else
|
|
54
58
|
to_merge << [key_objects_overlap, ( eager_loadings[k].dup << associations )]
|
|
55
59
|
|
|
56
|
-
keys_without_objects = k -
|
|
60
|
+
keys_without_objects = k - object_ar_keys
|
|
57
61
|
to_merge << [keys_without_objects, eager_loadings[k]]
|
|
58
62
|
to_delete << k
|
|
59
|
-
|
|
63
|
+
object_ar_keys = object_ar_keys - k
|
|
60
64
|
end
|
|
61
65
|
end
|
|
62
|
-
if to_add
|
|
63
|
-
eager_loadings.add *to_add
|
|
64
|
-
end
|
|
65
|
-
to_merge.each do |k,val|
|
|
66
|
-
eager_loadings.merge k, val
|
|
67
|
-
end
|
|
68
|
-
to_delete.each do |k|
|
|
69
|
-
eager_loadings.delete k
|
|
70
|
-
end
|
|
71
66
|
|
|
72
|
-
eager_loadings.add
|
|
67
|
+
eager_loadings.add *to_add if to_add
|
|
68
|
+
to_merge.each { |k,val| eager_loadings.merge k, val }
|
|
69
|
+
to_delete.each { |k| eager_loadings.delete k }
|
|
70
|
+
|
|
71
|
+
eager_loadings.add object_ar_keys, associations unless object_ar_keys.empty?
|
|
73
72
|
end
|
|
74
73
|
|
|
75
74
|
private
|
|
76
|
-
|
|
77
|
-
possible_objects.contains? object
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def impossible?(object)
|
|
81
|
-
impossible_objects.contains? object
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# check if object => associations already exists in object_associations.
|
|
85
|
-
def association?(object, associations)
|
|
86
|
-
value = object_associations[object]
|
|
87
|
-
if value
|
|
88
|
-
value.each do |v|
|
|
89
|
-
result = v.is_a?(Hash) ? v.has_key?(associations) : v == associations
|
|
90
|
-
return true if result
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
return false
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# object_associations keep the object relationships
|
|
75
|
+
# object_associations keep the object relationships
|
|
98
76
|
# that the object has many associations.
|
|
99
|
-
# e.g. {
|
|
100
|
-
# the object_associations keep all associations that may be or may no be
|
|
77
|
+
# e.g. { "Post:1" => [:comments] }
|
|
78
|
+
# the object_associations keep all associations that may be or may no be
|
|
101
79
|
# unpreload associations or unused preload associations.
|
|
102
80
|
def object_associations
|
|
103
81
|
@@object_associations ||= Bullet::Registry::Base.new
|
|
@@ -105,7 +83,7 @@ module Bullet
|
|
|
105
83
|
|
|
106
84
|
# call_object_assciations keep the object relationships
|
|
107
85
|
# that object.associations is called.
|
|
108
|
-
# e.g. {
|
|
86
|
+
# e.g. { "Post:1" => [:comments] }
|
|
109
87
|
# they are used to detect unused preload associations.
|
|
110
88
|
def call_object_associations
|
|
111
89
|
@@call_object_associations ||= Bullet::Registry::Base.new
|
|
@@ -113,14 +91,14 @@ module Bullet
|
|
|
113
91
|
|
|
114
92
|
# possible_objects keep the class to object relationships
|
|
115
93
|
# that the objects may cause N+1 query.
|
|
116
|
-
# e.g. { Post => [
|
|
94
|
+
# e.g. { Post => ["Post:1", "Post:2"] }
|
|
117
95
|
def possible_objects
|
|
118
96
|
@@possible_objects ||= Bullet::Registry::Object.new
|
|
119
97
|
end
|
|
120
98
|
|
|
121
99
|
# impossible_objects keep the class to objects relationships
|
|
122
100
|
# that the objects may not cause N+1 query.
|
|
123
|
-
# e.g. { Post => [
|
|
101
|
+
# e.g. { Post => ["Post:1", "Post:2"] }
|
|
124
102
|
# Notice: impossible_objects are not accurate,
|
|
125
103
|
# if find collection returns only one object, then the object is impossible object,
|
|
126
104
|
# impossible_objects are used to avoid treating 1+1 query to N+1 query.
|
|
@@ -130,14 +108,10 @@ module Bullet
|
|
|
130
108
|
|
|
131
109
|
# eager_loadings keep the object relationships
|
|
132
110
|
# that the associations are preloaded by find :include.
|
|
133
|
-
# e.g. { [
|
|
111
|
+
# e.g. { ["Post:1", "Post:2"] => [:comments, :user] }
|
|
134
112
|
def eager_loadings
|
|
135
113
|
@@eager_loadings ||= Bullet::Registry::Association.new
|
|
136
114
|
end
|
|
137
|
-
|
|
138
|
-
def callers
|
|
139
|
-
@@callers ||= []
|
|
140
|
-
end
|
|
141
115
|
end
|
|
142
116
|
end
|
|
143
117
|
end
|
|
@@ -1,42 +1,47 @@
|
|
|
1
1
|
module Bullet
|
|
2
2
|
module Detector
|
|
3
3
|
class Counter < Base
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
class <<self
|
|
5
|
+
def clear
|
|
6
|
+
@@possible_objects = nil
|
|
7
|
+
@@impossible_objects = nil
|
|
8
|
+
end
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
def add_counter_cache(object, associations)
|
|
11
|
+
if conditions_met?(object.ar_key, associations)
|
|
12
|
+
create_notification object.class.to_s, associations
|
|
13
|
+
end
|
|
12
14
|
end
|
|
13
|
-
end
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
def add_possible_objects(object_or_objects)
|
|
17
|
+
if object_or_objects.is_a? Array
|
|
18
|
+
object_or_objects.each { |object| possible_objects.add object.ar_key }
|
|
19
|
+
else
|
|
20
|
+
possible_objects.add object_or_objects.ar_key
|
|
21
|
+
end
|
|
22
|
+
end
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
private
|
|
24
|
-
def self.create_notification( klazz, associations )
|
|
25
|
-
notice = Bullet::Notification::CounterCache.new klazz, associations
|
|
26
|
-
Bullet.notification_collector.add notice
|
|
27
|
-
end
|
|
24
|
+
def add_impossible_object(object)
|
|
25
|
+
impossible_objects.add object.ar_key
|
|
26
|
+
end
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
private
|
|
29
|
+
def create_notification(klazz, associations)
|
|
30
|
+
notice = Bullet::Notification::CounterCache.new klazz, associations
|
|
31
|
+
Bullet.notification_collector.add notice
|
|
32
|
+
end
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
def possible_objects
|
|
35
|
+
@@possible_objects ||= Bullet::Registry::Object.new
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def impossible_objects
|
|
39
|
+
@@impossible_objects ||= Bullet::Registry::Object.new
|
|
40
|
+
end
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
def conditions_met?(object_ar_key, associations)
|
|
43
|
+
possible_objects.include?(object_ar_key) && !impossible_objects.include?(object_ar_key)
|
|
44
|
+
end
|
|
40
45
|
end
|
|
41
46
|
end
|
|
42
47
|
end
|