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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +7 -11
  4. data/CHANGELOG.md +62 -3
  5. data/Gemfile.mongoid +0 -2
  6. data/Gemfile.mongoid-4.0 +1 -5
  7. data/{Gemfile.mongoid-2.8 → Gemfile.mongoid-5.0} +2 -6
  8. data/{Gemfile.mongoid-2.5 → Gemfile.mongoid-6.0} +2 -6
  9. data/Gemfile.rails-4.0 +2 -5
  10. data/Gemfile.rails-4.1 +2 -5
  11. data/{Gemfile.rails-3.0 → Gemfile.rails-4.2} +3 -6
  12. data/{Gemfile.rails-3.1 → Gemfile.rails-5.0} +2 -6
  13. data/Hacking.md +1 -0
  14. data/README.md +39 -7
  15. data/bullet.gemspec +1 -1
  16. data/lib/bullet/active_record4.rb +109 -33
  17. data/lib/bullet/active_record41.rb +99 -37
  18. data/lib/bullet/active_record42.rb +251 -0
  19. data/lib/bullet/active_record5.rb +243 -0
  20. data/lib/bullet/dependency.rb +34 -28
  21. data/lib/bullet/detector/association.rb +16 -16
  22. data/lib/bullet/detector/counter_cache.rb +13 -13
  23. data/lib/bullet/detector/n_plus_one_query.rb +30 -35
  24. data/lib/bullet/detector/unused_eager_loading.rb +12 -11
  25. data/lib/bullet/ext/object.rb +4 -2
  26. data/lib/bullet/{mongoid3x.rb → mongoid5x.rb} +3 -3
  27. data/lib/bullet/{mongoid2x.rb → mongoid6x.rb} +12 -12
  28. data/lib/bullet/notification/base.rb +9 -13
  29. data/lib/bullet/notification/n_plus_one_query.rb +8 -6
  30. data/lib/bullet/notification/unused_eager_loading.rb +18 -1
  31. data/lib/bullet/rack.rb +28 -16
  32. data/lib/bullet/stack_trace_filter.rb +34 -0
  33. data/lib/bullet/version.rb +1 -1
  34. data/lib/bullet.rb +48 -14
  35. data/spec/bullet/detector/counter_cache_spec.rb +8 -8
  36. data/spec/bullet/detector/n_plus_one_query_spec.rb +46 -31
  37. data/spec/bullet/detector/unused_eager_loading_spec.rb +17 -3
  38. data/spec/bullet/ext/object_spec.rb +6 -0
  39. data/spec/bullet/notification/base_spec.rb +4 -29
  40. data/spec/bullet/notification/n_plus_one_query_spec.rb +4 -4
  41. data/spec/bullet/notification/unused_eager_loading_spec.rb +3 -3
  42. data/spec/bullet/rack_spec.rb +38 -7
  43. data/spec/bullet_spec.rb +93 -0
  44. data/spec/integration/active_record4/association_spec.rb +112 -4
  45. data/spec/integration/{active_record3 → active_record5}/association_spec.rb +121 -15
  46. data/spec/integration/counter_cache_spec.rb +24 -1
  47. data/spec/models/category.rb +4 -1
  48. data/spec/models/comment.rb +2 -0
  49. data/spec/models/post.rb +7 -2
  50. data/spec/models/reply.rb +3 -0
  51. data/spec/models/submission.rb +1 -1
  52. data/spec/spec_helper.rb +3 -5
  53. data/spec/support/mongo_seed.rb +13 -0
  54. data/spec/support/sqlite_seed.rb +15 -6
  55. data/test.sh +4 -10
  56. data/update.sh +7 -0
  57. metadata +21 -23
  58. data/Gemfile.mongoid-2.4 +0 -19
  59. data/Gemfile.mongoid-2.6 +0 -19
  60. data/Gemfile.mongoid-2.7 +0 -19
  61. data/Gemfile.mongoid-3.0 +0 -19
  62. data/Gemfile.mongoid-3.1 +0 -19
  63. data/Gemfile.rails-3.2 +0 -19
  64. data/lib/bullet/active_record3.rb +0 -154
  65. 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