bullet 4.13.1 → 5.5.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +7 -11
- data/CHANGELOG.md +62 -3
- data/Gemfile.mongoid +0 -2
- data/Gemfile.mongoid-4.0 +1 -5
- data/{Gemfile.mongoid-2.8 → Gemfile.mongoid-5.0} +2 -6
- data/{Gemfile.mongoid-2.5 → Gemfile.mongoid-6.0} +2 -6
- data/Gemfile.rails-4.0 +2 -5
- data/Gemfile.rails-4.1 +2 -5
- data/{Gemfile.rails-3.0 → Gemfile.rails-4.2} +3 -6
- data/{Gemfile.rails-3.1 → Gemfile.rails-5.0} +2 -6
- data/Hacking.md +1 -0
- data/README.md +39 -7
- data/bullet.gemspec +1 -1
- data/lib/bullet/active_record4.rb +109 -33
- data/lib/bullet/active_record41.rb +99 -37
- data/lib/bullet/active_record42.rb +251 -0
- data/lib/bullet/active_record5.rb +243 -0
- data/lib/bullet/dependency.rb +34 -28
- data/lib/bullet/detector/association.rb +16 -16
- data/lib/bullet/detector/counter_cache.rb +13 -13
- data/lib/bullet/detector/n_plus_one_query.rb +30 -35
- data/lib/bullet/detector/unused_eager_loading.rb +12 -11
- data/lib/bullet/ext/object.rb +4 -2
- data/lib/bullet/{mongoid3x.rb → mongoid5x.rb} +3 -3
- data/lib/bullet/{mongoid2x.rb → mongoid6x.rb} +12 -12
- data/lib/bullet/notification/base.rb +9 -13
- data/lib/bullet/notification/n_plus_one_query.rb +8 -6
- data/lib/bullet/notification/unused_eager_loading.rb +18 -1
- data/lib/bullet/rack.rb +28 -16
- data/lib/bullet/stack_trace_filter.rb +34 -0
- data/lib/bullet/version.rb +1 -1
- data/lib/bullet.rb +48 -14
- data/spec/bullet/detector/counter_cache_spec.rb +8 -8
- data/spec/bullet/detector/n_plus_one_query_spec.rb +46 -31
- data/spec/bullet/detector/unused_eager_loading_spec.rb +17 -3
- data/spec/bullet/ext/object_spec.rb +6 -0
- data/spec/bullet/notification/base_spec.rb +4 -29
- data/spec/bullet/notification/n_plus_one_query_spec.rb +4 -4
- data/spec/bullet/notification/unused_eager_loading_spec.rb +3 -3
- data/spec/bullet/rack_spec.rb +38 -7
- data/spec/bullet_spec.rb +93 -0
- data/spec/integration/active_record4/association_spec.rb +112 -4
- data/spec/integration/{active_record3 → active_record5}/association_spec.rb +121 -15
- data/spec/integration/counter_cache_spec.rb +24 -1
- data/spec/models/category.rb +4 -1
- data/spec/models/comment.rb +2 -0
- data/spec/models/post.rb +7 -2
- data/spec/models/reply.rb +3 -0
- data/spec/models/submission.rb +1 -1
- data/spec/spec_helper.rb +3 -5
- data/spec/support/mongo_seed.rb +13 -0
- data/spec/support/sqlite_seed.rb +15 -6
- data/test.sh +4 -10
- data/update.sh +7 -0
- metadata +21 -23
- data/Gemfile.mongoid-2.4 +0 -19
- data/Gemfile.mongoid-2.6 +0 -19
- data/Gemfile.mongoid-2.7 +0 -19
- data/Gemfile.mongoid-3.0 +0 -19
- data/Gemfile.mongoid-3.1 +0 -19
- data/Gemfile.rails-3.2 +0 -19
- data/lib/bullet/active_record3.rb +0 -154
- data/lib/bullet/active_record3x.rb +0 -134
data/Gemfile.mongoid-2.7
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
source "https://rubygems.org"
|
|
2
|
-
|
|
3
|
-
gemspec
|
|
4
|
-
|
|
5
|
-
gem 'rails', '~> 3.2.16'
|
|
6
|
-
gem 'sqlite3', platforms: [:ruby]
|
|
7
|
-
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
|
|
8
|
-
gem 'mongoid', '~> 2.7.1'
|
|
9
|
-
|
|
10
|
-
gem "rspec"
|
|
11
|
-
gem "guard"
|
|
12
|
-
gem "guard-rspec"
|
|
13
|
-
|
|
14
|
-
gem 'coveralls', require: false
|
|
15
|
-
|
|
16
|
-
platforms :rbx do
|
|
17
|
-
gem 'rubysl', '~> 2.0'
|
|
18
|
-
gem 'rubinius-developer_tools'
|
|
19
|
-
end
|
data/Gemfile.mongoid-3.0
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
source "https://rubygems.org"
|
|
2
|
-
|
|
3
|
-
gemspec
|
|
4
|
-
|
|
5
|
-
gem 'rails', '~> 3.2.16'
|
|
6
|
-
gem 'sqlite3', platforms: [:ruby]
|
|
7
|
-
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
|
|
8
|
-
gem 'mongoid', '~> 3.0.23'
|
|
9
|
-
|
|
10
|
-
gem "rspec"
|
|
11
|
-
gem "guard"
|
|
12
|
-
gem "guard-rspec"
|
|
13
|
-
|
|
14
|
-
gem 'coveralls', require: false
|
|
15
|
-
|
|
16
|
-
platforms :rbx do
|
|
17
|
-
gem 'rubysl', '~> 2.0'
|
|
18
|
-
gem 'rubinius-developer_tools'
|
|
19
|
-
end
|
data/Gemfile.mongoid-3.1
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
source "https://rubygems.org"
|
|
2
|
-
|
|
3
|
-
gemspec
|
|
4
|
-
|
|
5
|
-
gem 'rails', '~> 3.2.16'
|
|
6
|
-
gem 'sqlite3', platforms: [:ruby]
|
|
7
|
-
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
|
|
8
|
-
gem 'mongoid', '~> 3.1.6'
|
|
9
|
-
|
|
10
|
-
gem "rspec"
|
|
11
|
-
gem "guard"
|
|
12
|
-
gem "guard-rspec"
|
|
13
|
-
|
|
14
|
-
gem 'coveralls', require: false
|
|
15
|
-
|
|
16
|
-
platforms :rbx do
|
|
17
|
-
gem 'rubysl', '~> 2.0'
|
|
18
|
-
gem 'rubinius-developer_tools'
|
|
19
|
-
end
|
data/Gemfile.rails-3.2
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
source "https://rubygems.org"
|
|
2
|
-
|
|
3
|
-
gemspec
|
|
4
|
-
|
|
5
|
-
gem 'rails', '~> 3.2.19'
|
|
6
|
-
gem 'sqlite3', platforms: [:ruby]
|
|
7
|
-
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
|
|
8
|
-
gem 'activerecord-import'
|
|
9
|
-
|
|
10
|
-
gem "rspec"
|
|
11
|
-
gem "guard"
|
|
12
|
-
gem "guard-rspec"
|
|
13
|
-
|
|
14
|
-
gem 'coveralls', require: false
|
|
15
|
-
|
|
16
|
-
platforms :rbx do
|
|
17
|
-
gem 'rubysl', '~> 2.0'
|
|
18
|
-
gem 'rubinius-developer_tools'
|
|
19
|
-
end
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
module Bullet
|
|
2
|
-
module ActiveRecord
|
|
3
|
-
def self.enable
|
|
4
|
-
require 'active_record'
|
|
5
|
-
::ActiveRecord::Relation.class_eval do
|
|
6
|
-
alias_method :origin_to_a, :to_a
|
|
7
|
-
# if select a collection of objects, then these objects have possible to cause N+1 query.
|
|
8
|
-
# if select only one object, then the only one object has impossible to cause N+1 query.
|
|
9
|
-
def to_a
|
|
10
|
-
records = origin_to_a
|
|
11
|
-
if records.size > 1
|
|
12
|
-
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
13
|
-
Bullet::Detector::CounterCache.add_possible_objects(records)
|
|
14
|
-
elsif records.size == 1
|
|
15
|
-
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
16
|
-
Bullet::Detector::CounterCache.add_impossible_object(records.first)
|
|
17
|
-
end
|
|
18
|
-
records
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
::ActiveRecord::AssociationPreload::ClassMethods.class_eval do
|
|
23
|
-
alias_method :origin_preload_associations, :preload_associations
|
|
24
|
-
# include query for one to many associations.
|
|
25
|
-
# keep this eager loadings.
|
|
26
|
-
def preload_associations(records, associations, preload_options={})
|
|
27
|
-
records = [records].flatten.compact.uniq
|
|
28
|
-
return if records.empty?
|
|
29
|
-
records.each do |record|
|
|
30
|
-
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
31
|
-
end
|
|
32
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
|
33
|
-
origin_preload_associations(records, associations, preload_options={})
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
::ActiveRecord::FinderMethods.class_eval do
|
|
38
|
-
# add includes in scope
|
|
39
|
-
alias_method :origin_find_with_associations, :find_with_associations
|
|
40
|
-
def find_with_associations
|
|
41
|
-
records = origin_find_with_associations
|
|
42
|
-
associations = (@eager_load_values + @includes_values).uniq
|
|
43
|
-
records.each do |record|
|
|
44
|
-
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
45
|
-
end
|
|
46
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
|
47
|
-
records
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
::ActiveRecord::Associations::ClassMethods::JoinDependency.class_eval do
|
|
52
|
-
alias_method :origin_instantiate, :instantiate
|
|
53
|
-
alias_method :origin_construct_association, :construct_association
|
|
54
|
-
|
|
55
|
-
def instantiate(rows)
|
|
56
|
-
@bullet_eager_loadings = {}
|
|
57
|
-
records = origin_instantiate(rows)
|
|
58
|
-
|
|
59
|
-
@bullet_eager_loadings.each do |klazz, eager_loadings_hash|
|
|
60
|
-
objects = eager_loadings_hash.keys
|
|
61
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
|
|
62
|
-
end
|
|
63
|
-
records
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# call join associations
|
|
67
|
-
def construct_association(record, join, row)
|
|
68
|
-
result = origin_construct_association(record, join, row)
|
|
69
|
-
|
|
70
|
-
associations = join.reflection.name
|
|
71
|
-
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
72
|
-
Bullet::Detector::NPlusOneQuery.call_association(record, associations)
|
|
73
|
-
@bullet_eager_loadings[record.class] ||= {}
|
|
74
|
-
@bullet_eager_loadings[record.class][record] ||= Set.new
|
|
75
|
-
@bullet_eager_loadings[record.class][record] << associations
|
|
76
|
-
|
|
77
|
-
result
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
::ActiveRecord::Associations::AssociationCollection.class_eval do
|
|
82
|
-
# call one to many associations
|
|
83
|
-
alias_method :origin_load_target, :load_target
|
|
84
|
-
def load_target
|
|
85
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
86
|
-
origin_load_target
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
alias_method :origin_first, :first
|
|
90
|
-
def first(*args)
|
|
91
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
92
|
-
origin_first(*args)
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
alias_method :origin_last, :last
|
|
96
|
-
def last(*args)
|
|
97
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
98
|
-
origin_last(*args)
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
alias_method :origin_empty?, :empty?
|
|
102
|
-
def empty?
|
|
103
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
104
|
-
origin_empty?
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
alias_method :origin_include?, :include?
|
|
108
|
-
def include?(object)
|
|
109
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
110
|
-
origin_include?(object)
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
::ActiveRecord::Associations::AssociationProxy.class_eval do
|
|
115
|
-
# call has_one and belong_to association
|
|
116
|
-
alias_method :origin_load_target, :load_target
|
|
117
|
-
def load_target
|
|
118
|
-
# avoid stack level too deep
|
|
119
|
-
result = origin_load_target
|
|
120
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless caller.any? { |c| c.include?("load_target") }
|
|
121
|
-
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
|
122
|
-
result
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
alias_method :origin_set_inverse_instance, :set_inverse_instance
|
|
126
|
-
def set_inverse_instance(record, instance)
|
|
127
|
-
if record && we_can_set_the_inverse_on_this?(record)
|
|
128
|
-
Bullet::Detector::NPlusOneQuery.add_inversed_object(record, @reflection.inverse_of.name)
|
|
129
|
-
end
|
|
130
|
-
origin_set_inverse_instance(record, instance)
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
::ActiveRecord::Associations::HasManyAssociation.class_eval do
|
|
135
|
-
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
|
136
|
-
|
|
137
|
-
def has_cached_counter?
|
|
138
|
-
result = origin_has_cached_counter?
|
|
139
|
-
Bullet::Detector::CounterCache.add_counter_cache(@owner, @reflection.name) unless result
|
|
140
|
-
result
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
::ActiveRecord::Associations::HasManyThroughAssociation.class_eval do
|
|
145
|
-
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
|
146
|
-
def has_cached_counter?
|
|
147
|
-
result = origin_has_cached_counter?
|
|
148
|
-
Bullet::Detector::CounterCache.add_counter_cache(@owner, @reflection.name) unless result
|
|
149
|
-
result
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
|
-
end
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
module Bullet
|
|
2
|
-
module ActiveRecord
|
|
3
|
-
def self.enable
|
|
4
|
-
require 'active_record'
|
|
5
|
-
::ActiveRecord::Relation.class_eval do
|
|
6
|
-
alias_method :origin_to_a, :to_a
|
|
7
|
-
# if select a collection of objects, then these objects have possible to cause N+1 query.
|
|
8
|
-
# if select only one object, then the only one object has impossible to cause N+1 query.
|
|
9
|
-
def to_a
|
|
10
|
-
records = origin_to_a
|
|
11
|
-
if records.size > 1
|
|
12
|
-
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
13
|
-
Bullet::Detector::CounterCache.add_possible_objects(records)
|
|
14
|
-
elsif records.size == 1
|
|
15
|
-
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
16
|
-
Bullet::Detector::CounterCache.add_impossible_object(records.first)
|
|
17
|
-
end
|
|
18
|
-
records
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
::ActiveRecord::Associations::Preloader.class_eval do
|
|
23
|
-
# include query for one to many associations.
|
|
24
|
-
# keep this eager loadings.
|
|
25
|
-
alias_method :origin_initialize, :initialize
|
|
26
|
-
def initialize(records, associations, preload_scope = nil)
|
|
27
|
-
origin_initialize(records, associations, preload_scope)
|
|
28
|
-
records = [records].flatten.compact.uniq
|
|
29
|
-
return if records.empty?
|
|
30
|
-
records.each do |record|
|
|
31
|
-
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
32
|
-
end
|
|
33
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
::ActiveRecord::FinderMethods.class_eval do
|
|
38
|
-
# add includes in scope
|
|
39
|
-
alias_method :origin_find_with_associations, :find_with_associations
|
|
40
|
-
def find_with_associations
|
|
41
|
-
records = origin_find_with_associations
|
|
42
|
-
associations = (eager_load_values + includes_values).uniq
|
|
43
|
-
records.each do |record|
|
|
44
|
-
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
45
|
-
end
|
|
46
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
|
47
|
-
records
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
::ActiveRecord::Associations::JoinDependency.class_eval do
|
|
52
|
-
alias_method :origin_instantiate, :instantiate
|
|
53
|
-
alias_method :origin_construct_association, :construct_association
|
|
54
|
-
|
|
55
|
-
def instantiate(rows)
|
|
56
|
-
@bullet_eager_loadings = {}
|
|
57
|
-
records = origin_instantiate(rows)
|
|
58
|
-
|
|
59
|
-
@bullet_eager_loadings.each do |klazz, eager_loadings_hash|
|
|
60
|
-
objects = eager_loadings_hash.keys
|
|
61
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
|
|
62
|
-
end
|
|
63
|
-
records
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# call join associations
|
|
67
|
-
def construct_association(record, join, row)
|
|
68
|
-
result = origin_construct_association(record, join, row)
|
|
69
|
-
|
|
70
|
-
associations = join.reflection.name
|
|
71
|
-
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
72
|
-
Bullet::Detector::NPlusOneQuery.call_association(record, associations)
|
|
73
|
-
@bullet_eager_loadings[record.class] ||= {}
|
|
74
|
-
@bullet_eager_loadings[record.class][record] ||= Set.new
|
|
75
|
-
@bullet_eager_loadings[record.class][record] << associations
|
|
76
|
-
|
|
77
|
-
result
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
::ActiveRecord::Associations::CollectionAssociation.class_eval do
|
|
82
|
-
# call one to many associations
|
|
83
|
-
alias_method :origin_load_target, :load_target
|
|
84
|
-
def load_target
|
|
85
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
86
|
-
origin_load_target
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
alias_method :origin_empty?, :empty?
|
|
90
|
-
def empty?
|
|
91
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
92
|
-
origin_empty?
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
alias_method :origin_include?, :include?
|
|
96
|
-
def include?(object)
|
|
97
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
98
|
-
origin_include?(object)
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
::ActiveRecord::Associations::SingularAssociation.class_eval do
|
|
103
|
-
# call has_one and belongs_to associations
|
|
104
|
-
alias_method :origin_reader, :reader
|
|
105
|
-
def reader(force_reload = false)
|
|
106
|
-
result = origin_reader(force_reload)
|
|
107
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
108
|
-
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
|
109
|
-
result
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
::ActiveRecord::Associations::Association.class_eval do
|
|
114
|
-
alias_method :origin_set_inverse_instance, :set_inverse_instance
|
|
115
|
-
def set_inverse_instance(record)
|
|
116
|
-
if record && invertible_for?(record)
|
|
117
|
-
Bullet::Detector::NPlusOneQuery.add_inversed_object(record, inverse_reflection_for(record).name)
|
|
118
|
-
end
|
|
119
|
-
origin_set_inverse_instance(record)
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
::ActiveRecord::Associations::HasManyAssociation.class_eval do
|
|
124
|
-
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
|
125
|
-
|
|
126
|
-
def has_cached_counter?(reflection = reflection())
|
|
127
|
-
result = origin_has_cached_counter?(reflection)
|
|
128
|
-
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name) unless result
|
|
129
|
-
result
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
end
|