bullet 5.1.0 → 5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f726582290756f07599b35204f8e903fd20350b3
4
- data.tar.gz: 2e4a9e95087d246cb69e887eef07868c2d9e0a63
3
+ metadata.gz: ed73722e74144833068cf4572e3624687a9db64b
4
+ data.tar.gz: e882a8f5c7e4bfbe1d37254e1255ed79fc3f3948
5
5
  SHA512:
6
- metadata.gz: 66c4b18f8c522a2cfa310bd8b818b177a12507c99255860881d225bac39722f35bfea65d7ea14287496f7fcf4d52d618e77ba712ebb28b2dad7a7a08909f56a8
7
- data.tar.gz: f68a5397786c67ae3aefe57ce3030133f4c0f54a253add872dc54ba83ab00c33439325b6f0ee3500a0534bddca951e95b5929a8dfc0c74d8bf5c818b204992ad
6
+ metadata.gz: a62b1a339dccad8b79e97971c8d7fc73f977eb9d0b1c19758f59259e85531116fb57a9ef51c90e013d6051a27616ec861380d0940f658102155a163f52eb1873
7
+ data.tar.gz: de637d2bb853c67a5e7f2b269f698b18b59a481f5988025407d094e0442df03de805bc3890945e49accba76d3ae3c7a62c4185a8fde8d584550eac251a281836
data/.travis.yml CHANGED
@@ -5,7 +5,7 @@ rvm:
5
5
  - 2.0
6
6
  - 2.1
7
7
  - 2.2
8
- - 2.2.3
8
+ - 2.3.0
9
9
  gemfile:
10
10
  - Gemfile.rails-5.0
11
11
  - Gemfile.rails-4.2
@@ -63,14 +63,14 @@ matrix:
63
63
  gemfile: Gemfile.rails-3.0
64
64
  - rvm: 2.1
65
65
  gemfile: Gemfile.rails-5.0
66
- - rvm: 2.2
67
- gemfile: Gemfile.rails-5.0
68
66
  - rvm: 2.2
69
67
  gemfile: Gemfile.rails-3.0
70
68
  - rvm: 2.2
71
69
  gemfile: Gemfile.rails-3.1
72
70
  - rvm: 2.2
73
71
  gemfile: Gemfile.rails-3.2
72
+ - rvm: 2.2
73
+ gemfile: Gemfile.rails-5.0
74
74
  - rvm: 2.2
75
75
  gemfile: Gemfile.mongoid-3.1
76
76
  - rvm: 2.2
@@ -85,23 +85,23 @@ matrix:
85
85
  gemfile: Gemfile.mongoid-2.5
86
86
  - rvm: 2.2
87
87
  gemfile: Gemfile.mongoid-2.4
88
- - rvm: 2.2.3
88
+ - rvm: 2.3.0
89
89
  gemfile: Gemfile.rails-3.0
90
- - rvm: 2.2.3
90
+ - rvm: 2.3.0
91
91
  gemfile: Gemfile.rails-3.1
92
- - rvm: 2.2.3
92
+ - rvm: 2.3.0
93
93
  gemfile: Gemfile.rails-3.2
94
- - rvm: 2.2.3
94
+ - rvm: 2.3.0
95
95
  gemfile: Gemfile.mongoid-3.1
96
- - rvm: 2.2.3
96
+ - rvm: 2.3.0
97
97
  gemfile: Gemfile.mongoid-3.0
98
- - rvm: 2.2.3
98
+ - rvm: 2.3.0
99
99
  gemfile: Gemfile.mongoid-2.8
100
- - rvm: 2.2.3
100
+ - rvm: 2.3.0
101
101
  gemfile: Gemfile.mongoid-2.7
102
- - rvm: 2.2.3
102
+ - rvm: 2.3.0
103
103
  gemfile: Gemfile.mongoid-2.6
104
- - rvm: 2.2.3
104
+ - rvm: 2.3.0
105
105
  gemfile: Gemfile.mongoid-2.5
106
- - rvm: 2.2.3
106
+ - rvm: 2.3.0
107
107
  gemfile: Gemfile.mongoid-2.4
data/CHANGELOG.md CHANGED
@@ -1,6 +1,19 @@
1
1
  # Next Release
2
2
 
3
- ## 5.1.0 (05/21/2015)
3
+ ## 5.3.0 (15/08/2016)
4
+
5
+ * Fix false alert on through association with join sql #301
6
+ * Fix association.target in through_association can be singular #302
7
+ * Support find_by_sql #303
8
+ * Fix env REQUEST_URI
9
+
10
+ ## 5.2.0 (07/26/2016)
11
+
12
+ * Fix `has_cached_counter?` is not defined in HABTM #297
13
+ * Fix false alert if preloaded association has no records #260
14
+ * Support Rails 5.0.0
15
+
16
+ ## 5.1.0 (05/21/2016)
4
17
 
5
18
  * Fix false alert when `empty?` used with `counter_cache`
6
19
  * Fix `alias_method_chain` deprecation for rails 5
@@ -8,7 +21,7 @@
8
21
  * Fix false alert when querying immediately after creation
9
22
  * Fix UnusedEagerLoading bug when multiple eager loading query include same objects
10
23
 
11
- ## 5.0.0 (01/06/2015)
24
+ ## 5.0.0 (01/06/2016)
12
25
 
13
26
  * Support Rails 5.0.0.beta1
14
27
  * Fix `has_many :through` infinite loop issue
data/Gemfile.mongoid-2.4 CHANGED
@@ -9,8 +9,6 @@ gem 'mongoid', '~> 2.4.0'
9
9
 
10
10
  gem "rspec"
11
11
 
12
- gem 'coveralls', require: false
13
-
14
12
  platforms :rbx do
15
13
  gem 'rubysl', '~> 2.0'
16
14
  gem 'rubinius-developer_tools'
data/Gemfile.mongoid-2.5 CHANGED
@@ -9,8 +9,6 @@ gem 'mongoid', '~> 2.5.0'
9
9
 
10
10
  gem "rspec"
11
11
 
12
- gem 'coveralls', require: false
13
-
14
12
  platforms :rbx do
15
13
  gem 'rubysl', '~> 2.0'
16
14
  gem 'rubinius-developer_tools'
data/Gemfile.mongoid-2.6 CHANGED
@@ -9,8 +9,6 @@ gem 'mongoid', '~> 2.6.0'
9
9
 
10
10
  gem "rspec"
11
11
 
12
- gem 'coveralls', require: false
13
-
14
12
  platforms :rbx do
15
13
  gem 'rubysl', '~> 2.0'
16
14
  gem 'rubinius-developer_tools'
data/Gemfile.mongoid-2.7 CHANGED
@@ -9,8 +9,6 @@ gem 'mongoid', '~> 2.7.0'
9
9
 
10
10
  gem "rspec"
11
11
 
12
- gem 'coveralls', require: false
13
-
14
12
  platforms :rbx do
15
13
  gem 'rubysl', '~> 2.0'
16
14
  gem 'rubinius-developer_tools'
data/Gemfile.mongoid-2.8 CHANGED
@@ -9,8 +9,6 @@ gem 'mongoid', '~> 2.8.0'
9
9
 
10
10
  gem "rspec"
11
11
 
12
- gem 'coveralls', require: false
13
-
14
12
  platforms :rbx do
15
13
  gem 'rubysl', '~> 2.0'
16
14
  gem 'rubinius-developer_tools'
data/Gemfile.mongoid-3.0 CHANGED
@@ -9,8 +9,6 @@ gem 'mongoid', '~> 3.0.0'
9
9
 
10
10
  gem "rspec"
11
11
 
12
- gem 'coveralls', require: false
13
-
14
12
  platforms :rbx do
15
13
  gem 'rubysl', '~> 2.0'
16
14
  gem 'rubinius-developer_tools'
data/Gemfile.mongoid-3.1 CHANGED
@@ -9,8 +9,6 @@ gem 'mongoid', '~> 3.1.0'
9
9
 
10
10
  gem "rspec"
11
11
 
12
- gem 'coveralls', require: false
13
-
14
12
  platforms :rbx do
15
13
  gem 'rubysl', '~> 2.0'
16
14
  gem 'rubinius-developer_tools'
data/Gemfile.mongoid-4.0 CHANGED
@@ -9,8 +9,6 @@ gem 'mongoid', '~> 4.0.0'
9
9
 
10
10
  gem "rspec"
11
11
 
12
- gem 'coveralls', require: false
13
-
14
12
  platforms :rbx do
15
13
  gem 'rubysl', '~> 2.0'
16
14
  gem 'rubinius-developer_tools'
data/Gemfile.mongoid-5.0 CHANGED
@@ -9,8 +9,6 @@ gem 'mongoid', '~> 5.0.0'
9
9
 
10
10
  gem "rspec"
11
11
 
12
- gem 'coveralls', require: false
13
-
14
12
  platforms :rbx do
15
13
  gem 'rubysl', '~> 2.0'
16
14
  gem 'rubinius-developer_tools'
data/Gemfile.rails-3.0 CHANGED
@@ -10,8 +10,6 @@ gem 'tins', '~> 1.6.0', platforms: [:ruby_19]
10
10
 
11
11
  gem "rspec"
12
12
 
13
- gem 'coveralls', require: false
14
-
15
13
  platforms :rbx do
16
14
  gem 'rubysl', '~> 2.0'
17
15
  gem 'rubinius-developer_tools'
data/Gemfile.rails-3.1 CHANGED
@@ -10,8 +10,6 @@ gem 'tins', '~> 1.6.0', platforms: [:ruby_19]
10
10
 
11
11
  gem "rspec"
12
12
 
13
- gem 'coveralls', require: false
14
-
15
13
  platforms :rbx do
16
14
  gem 'rubysl', '~> 2.0'
17
15
  gem 'rubinius-developer_tools'
data/Gemfile.rails-3.2 CHANGED
@@ -10,8 +10,6 @@ gem 'tins', '~> 1.6.0', platforms: [:ruby_19]
10
10
 
11
11
  gem "rspec"
12
12
 
13
- gem 'coveralls', require: false
14
-
15
13
  platforms :rbx do
16
14
  gem 'rubysl', '~> 2.0'
17
15
  gem 'rubinius-developer_tools'
data/Gemfile.rails-4.0 CHANGED
@@ -10,8 +10,6 @@ gem 'tins', '~> 1.6.0', platforms: [:ruby_19]
10
10
 
11
11
  gem "rspec"
12
12
 
13
- gem 'coveralls', require: false
14
-
15
13
  platforms :rbx do
16
14
  gem 'rubysl', '~> 2.0'
17
15
  gem 'rubinius-developer_tools'
data/Gemfile.rails-4.1 CHANGED
@@ -10,8 +10,6 @@ gem 'tins', '~> 1.6.0', platforms: [:ruby_19]
10
10
 
11
11
  gem "rspec"
12
12
 
13
- gem 'coveralls', require: false
14
-
15
13
  platforms :rbx do
16
14
  gem 'rubysl', '~> 2.0'
17
15
  gem 'rubinius-developer_tools'
data/Gemfile.rails-4.2 CHANGED
@@ -10,8 +10,6 @@ gem 'tins', '~> 1.6.0', platforms: [:ruby_19]
10
10
 
11
11
  gem "rspec"
12
12
 
13
- gem 'coveralls', require: false
14
-
15
13
  platforms :rbx do
16
14
  gem 'rubysl', '~> 2.0'
17
15
  gem 'rubinius-developer_tools'
data/Gemfile.rails-5.0 CHANGED
@@ -2,15 +2,13 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rails', '5.0.0.beta1'
5
+ gem 'rails', '5.0.0'
6
6
  gem 'sqlite3'
7
7
  gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
8
  gem 'activerecord-import'
9
9
 
10
10
  gem "rspec"
11
11
 
12
- gem 'coveralls', require: false
13
-
14
12
  platforms :rbx do
15
13
  gem 'rubysl', '~> 2.0'
16
14
  gem 'rubinius-developer_tools'
data/README.md CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/bullet.png)](http://badge.fury.io/rb/bullet)
4
4
  [![Build Status](https://secure.travis-ci.org/flyerhzm/bullet.png)](http://travis-ci.org/flyerhzm/bullet)
5
- [![Coverage Status](https://coveralls.io/repos/flyerhzm/bullet/badge.png?branch=master)](https://coveralls.io/r/flyerhzm/bullet)
6
5
  <a href="https://codeclimate.com/github/flyerhzm/bullet"><img src="https://codeclimate.com/github/flyerhzm/bullet.png" /></a>
7
6
  [![Coderwall Endorse](http://api.coderwall.com/flyerhzm/endorsecount.png)](http://coderwall.com/flyerhzm)
8
7
 
@@ -63,7 +62,7 @@ config.after_initialize do
63
62
  Bullet.add_footer = true
64
63
  Bullet.stacktrace_includes = [ 'your_gem', 'your_middleware' ]
65
64
  Bullet.stacktrace_excludes = [ 'their_gem', 'their_middleware' ]
66
- Bullet.slack = { webhook_url: 'http://some.slack.url', foo: 'bar' }
65
+ Bullet.slack = { webhook_url: 'http://some.slack.url', channel: '#default', username: 'notifier' }
67
66
  end
68
67
  ```
69
68
 
@@ -238,7 +237,9 @@ Bullet outputs some details info, to enable debug mode, set
238
237
 
239
238
  ## Demo
240
239
 
241
- Bullet is designed to function as you browse through your application in development. To see it in action, follow these steps to create, detect, and fix example query problems.
240
+ Bullet is designed to function as you browse through your application in development. To see it in action,
241
+ you can visit [https://github.com/flyerhzm/bullet_test](https://github.com/flyerhzm/bullet_test) or
242
+ follow these steps to create, detect, and fix example query problems.
242
243
 
243
244
  1\. Create an example application
244
245
 
@@ -458,4 +459,4 @@ Meanwhile, there's a line appended to `log/bullet.log`
458
459
  Post => [:comments]
459
460
  ```
460
461
 
461
- Copyright (c) 2009 - 2015 Richard Huang (flyerhzm@gmail.com), released under the MIT license
462
+ Copyright (c) 2009 - 2016 Richard Huang (flyerhzm@gmail.com), released under the MIT license
@@ -4,6 +4,25 @@ module Bullet
4
4
 
5
5
  def self.enable
6
6
  require 'active_record'
7
+ ::ActiveRecord::Base.class_eval do
8
+ class <<self
9
+ alias_method :origin_find_by_sql, :find_by_sql
10
+ def find_by_sql(sql)
11
+ result = origin_find_by_sql(sql)
12
+ if Bullet.start?
13
+ if result.is_a? Array
14
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
15
+ Bullet::Detector::CounterCache.add_possible_objects(result)
16
+ elsif result.is_a? ::ActiveRecord::Base
17
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
18
+ Bullet::Detector::CounterCache.add_impossible_object(result)
19
+ end
20
+ end
21
+ result
22
+ end
23
+ end
24
+ end
25
+
7
26
  ::ActiveRecord::Relation.class_eval do
8
27
  alias_method :origin_to_a, :to_a
9
28
  # if select a collection of objects, then these objects have possible to cause N+1 query.
@@ -134,6 +153,16 @@ module Bullet
134
153
  origin_last(*args)
135
154
  end
136
155
 
156
+ alias_method :origin_include?, :include?
157
+ def include?(object)
158
+ if Bullet.start?
159
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
160
+ end
161
+ origin_include?(object)
162
+ end
163
+ end
164
+
165
+ ::ActiveRecord::Associations::HasManyAssociation.class_eval do
137
166
  alias_method :origin_empty?, :empty?
138
167
  def empty?
139
168
  if Bullet.start? && !has_cached_counter?
@@ -141,13 +170,15 @@ module Bullet
141
170
  end
142
171
  origin_empty?
143
172
  end
173
+ end
144
174
 
145
- alias_method :origin_include?, :include?
146
- def include?(object)
175
+ ::ActiveRecord::Associations::HasAndBelongsToManyAssociation.class_eval do
176
+ alias_method :origin_empty?, :empty?
177
+ def empty?
147
178
  if Bullet.start?
148
179
  Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
149
180
  end
150
- origin_include?(object)
181
+ origin_empty?
151
182
  end
152
183
  end
153
184
 
@@ -2,6 +2,25 @@ module Bullet
2
2
  module ActiveRecord
3
3
  def self.enable
4
4
  require 'active_record'
5
+ ::ActiveRecord::Base.class_eval do
6
+ class <<self
7
+ alias_method :origin_find_by_sql, :find_by_sql
8
+ def find_by_sql(sql, binds = [])
9
+ result = origin_find_by_sql(sql, binds)
10
+ if Bullet.start?
11
+ if result.is_a? Array
12
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
13
+ Bullet::Detector::CounterCache.add_possible_objects(result)
14
+ elsif result.is_a? ::ActiveRecord::Base
15
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
16
+ Bullet::Detector::CounterCache.add_impossible_object(result)
17
+ end
18
+ end
19
+ result
20
+ end
21
+ end
22
+ end
23
+
5
24
  ::ActiveRecord::Relation.class_eval do
6
25
  alias_method :origin_to_a, :to_a
7
26
  # if select a collection of objects, then these objects have possible to cause N+1 query.
@@ -116,6 +135,16 @@ module Bullet
116
135
  origin_load_target
117
136
  end
118
137
 
138
+ alias_method :origin_include?, :include?
139
+ def include?(object)
140
+ if Bullet.start?
141
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
142
+ end
143
+ origin_include?(object)
144
+ end
145
+ end
146
+
147
+ ::ActiveRecord::Associations::HasManyAssociation.class_eval do
119
148
  alias_method :origin_empty?, :empty?
120
149
  def empty?
121
150
  if Bullet.start? && !has_cached_counter?(@reflection)
@@ -123,13 +152,15 @@ module Bullet
123
152
  end
124
153
  origin_empty?
125
154
  end
155
+ end
126
156
 
127
- alias_method :origin_include?, :include?
128
- def include?(object)
157
+ ::ActiveRecord::Associations::HasAndBelongsToManyAssociation.class_eval do
158
+ alias_method :origin_empty?, :empty?
159
+ def empty?
129
160
  if Bullet.start?
130
161
  Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
131
162
  end
132
- origin_include?(object)
163
+ origin_empty?
133
164
  end
134
165
  end
135
166
 
@@ -2,6 +2,25 @@ module Bullet
2
2
  module ActiveRecord
3
3
  def self.enable
4
4
  require 'active_record'
5
+ ::ActiveRecord::Base.class_eval do
6
+ class <<self
7
+ alias_method :origin_find_by_sql, :find_by_sql
8
+ def find_by_sql(sql, binds = [])
9
+ result = origin_find_by_sql(sql, binds)
10
+ if Bullet.start?
11
+ if result.is_a? Array
12
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
13
+ Bullet::Detector::CounterCache.add_possible_objects(result)
14
+ elsif result.is_a? ::ActiveRecord::Base
15
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
16
+ Bullet::Detector::CounterCache.add_impossible_object(result)
17
+ end
18
+ end
19
+ result
20
+ end
21
+ end
22
+ end
23
+
5
24
  ::ActiveRecord::Relation.class_eval do
6
25
  alias_method :origin_to_a, :to_a
7
26
  # if select a collection of objects, then these objects have possible to cause N+1 query.
@@ -116,6 +135,16 @@ module Bullet
116
135
  origin_load_target
117
136
  end
118
137
 
138
+ alias_method :origin_include?, :include?
139
+ def include?(object)
140
+ if Bullet.start?
141
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
142
+ end
143
+ origin_include?(object)
144
+ end
145
+ end
146
+
147
+ ::ActiveRecord::Associations::HasManyAssociation.class_eval do
119
148
  alias_method :origin_empty?, :empty?
120
149
  def empty?
121
150
  if Bullet.start? && !has_cached_counter?(@reflection)
@@ -123,13 +152,15 @@ module Bullet
123
152
  end
124
153
  origin_empty?
125
154
  end
155
+ end
126
156
 
127
- alias_method :origin_include?, :include?
128
- def include?(object)
157
+ ::ActiveRecord::Associations::HasAndBelongsToManyAssociation.class_eval do
158
+ alias_method :origin_empty?, :empty?
159
+ def empty?
129
160
  if Bullet.start?
130
161
  Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
131
162
  end
132
- origin_include?(object)
163
+ origin_empty?
133
164
  end
134
165
  end
135
166
 
@@ -2,6 +2,25 @@ module Bullet
2
2
  module ActiveRecord
3
3
  def self.enable
4
4
  require 'active_record'
5
+ ::ActiveRecord::Base.class_eval do
6
+ class <<self
7
+ alias_method :origin_find_by_sql, :find_by_sql
8
+ def find_by_sql(sql, binds = [])
9
+ result = origin_find_by_sql(sql, binds)
10
+ if Bullet.start?
11
+ if result.is_a? Array
12
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
13
+ Bullet::Detector::CounterCache.add_possible_objects(result)
14
+ elsif result.is_a? ::ActiveRecord::Base
15
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
16
+ Bullet::Detector::CounterCache.add_impossible_object(result)
17
+ end
18
+ end
19
+ result
20
+ end
21
+ end
22
+ end
23
+
5
24
  ::ActiveRecord::Relation.class_eval do
6
25
  alias_method :origin_to_a, :to_a
7
26
  # if select a collection of objects, then these objects have possible to cause N+1 query.
@@ -18,6 +18,21 @@ module Bullet
18
18
  end
19
19
  result
20
20
  end
21
+
22
+ alias_method :origin_find_by_sql, :find_by_sql
23
+ def find_by_sql(sql, binds = [])
24
+ result = origin_find_by_sql(sql, binds)
25
+ if Bullet.start?
26
+ if result.is_a? Array
27
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
28
+ Bullet::Detector::CounterCache.add_possible_objects(result)
29
+ elsif result.is_a? ::ActiveRecord::Base
30
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
31
+ Bullet::Detector::CounterCache.add_impossible_object(result)
32
+ end
33
+ end
34
+ result
35
+ end
21
36
  end
22
37
  end
23
38
 
@@ -96,6 +111,7 @@ module Bullet
96
111
 
97
112
  ::ActiveRecord::Associations::JoinDependency.class_eval do
98
113
  alias_method :origin_instantiate, :instantiate
114
+ alias_method :origin_construct, :construct
99
115
  alias_method :origin_construct_model, :construct_model
100
116
 
101
117
  def instantiate(result_set, aliases)
@@ -111,6 +127,27 @@ module Bullet
111
127
  records
112
128
  end
113
129
 
130
+ def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
131
+ if Bullet.start?
132
+ unless ar_parent.nil?
133
+ parent.children.each do |node|
134
+ key = aliases.column_alias(node, node.primary_key)
135
+ id = row[key]
136
+ if id.nil?
137
+ associations = node.reflection.name
138
+ Bullet::Detector::Association.add_object_associations(ar_parent, associations)
139
+ Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
140
+ @bullet_eager_loadings[ar_parent.class] ||= {}
141
+ @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
142
+ @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ origin_construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
149
+ end
150
+
114
151
  # call join associations
115
152
  def construct_model(record, node, row, model_cache, id, aliases)
116
153
  result = origin_construct_model(record, node, row, model_cache, id, aliases)
@@ -20,9 +20,9 @@ module Bullet
20
20
  require 'active_record'
21
21
  ::ActiveRecord::Base.class_eval do
22
22
  class <<self
23
- alias_method :origin_find, :find
24
- def find(*args)
25
- result = origin_find(*args)
23
+ alias_method :origin_find_by_sql, :find_by_sql
24
+ def find_by_sql(sql, binds = [], preparable: nil)
25
+ result = origin_find_by_sql(sql, binds, preparable: nil)
26
26
  if Bullet.start?
27
27
  if result.is_a? Array
28
28
  Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
@@ -40,38 +40,30 @@ module Bullet
40
40
  ::ActiveRecord::Base.prepend(SaveWithBulletSupport)
41
41
 
42
42
  ::ActiveRecord::Relation.class_eval do
43
- alias_method :origin_to_a, :to_a
43
+ alias_method :origin_records, :records
44
44
  # if select a collection of objects, then these objects have possible to cause N+1 query.
45
45
  # if select only one object, then the only one object has impossible to cause N+1 query.
46
- def to_a
47
- records = origin_to_a
46
+ def records
47
+ result = origin_records
48
48
  if Bullet.start?
49
- if records.first.class.name !~ /^HABTM_/
50
- if records.size > 1
51
- Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
52
- Bullet::Detector::CounterCache.add_possible_objects(records)
53
- elsif records.size == 1
54
- Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
55
- Bullet::Detector::CounterCache.add_impossible_object(records.first)
49
+ if result.first.class.name !~ /^HABTM_/
50
+ if result.size > 1
51
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
52
+ Bullet::Detector::CounterCache.add_possible_objects(result)
53
+ elsif result.size == 1
54
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
55
+ Bullet::Detector::CounterCache.add_impossible_object(result.first)
56
56
  end
57
57
  end
58
58
  end
59
- records
60
- end
61
- end
62
-
63
- ::ActiveRecord::Associations::Association.class_eval do
64
- alias_method :origin_initialize, :initialize
65
- def initialize(owner, reflection)
66
- origin_initialize(owner, reflection)
67
- reflection.set_owner owner
59
+ result
68
60
  end
69
61
  end
70
62
 
71
63
  ::ActiveRecord::Associations::Preloader.class_eval do
72
- alias_method :origin_preloaders_on, :preloaders_on
64
+ alias_method :origin_preloaders_for_one, :preloaders_for_one
73
65
 
74
- def preloaders_on(association, records, scope)
66
+ def preloaders_for_one(association, records, scope)
75
67
  if Bullet.start?
76
68
  records.compact!
77
69
  if records.first.class.name !~ /^HABTM_/
@@ -81,7 +73,7 @@ module Bullet
81
73
  Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
82
74
  end
83
75
  end
84
- origin_preloaders_on(association, records, scope)
76
+ origin_preloaders_for_one(association, records, scope)
85
77
  end
86
78
  end
87
79
 
@@ -104,6 +96,7 @@ module Bullet
104
96
 
105
97
  ::ActiveRecord::Associations::JoinDependency.class_eval do
106
98
  alias_method :origin_instantiate, :instantiate
99
+ alias_method :origin_construct, :construct
107
100
  alias_method :origin_construct_model, :construct_model
108
101
 
109
102
  def instantiate(result_set, aliases)
@@ -119,6 +112,27 @@ module Bullet
119
112
  records
120
113
  end
121
114
 
115
+ def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
116
+ if Bullet.start?
117
+ unless ar_parent.nil?
118
+ parent.children.each do |node|
119
+ key = aliases.column_alias(node, node.primary_key)
120
+ id = row[key]
121
+ if id.nil?
122
+ associations = node.reflection.name
123
+ Bullet::Detector::Association.add_object_associations(ar_parent, associations)
124
+ Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
125
+ @bullet_eager_loadings[ar_parent.class] ||= {}
126
+ @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
127
+ @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ origin_construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
134
+ end
135
+
122
136
  # call join associations
123
137
  def construct_model(record, node, row, model_cache, id, aliases)
124
138
  result = origin_construct_model(record, node, row, model_cache, id, aliases)
@@ -143,7 +157,14 @@ module Bullet
143
157
  records = origin_load_target
144
158
 
145
159
  if Bullet.start?
146
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
160
+ if self.is_a? ::ActiveRecord::Associations::ThroughAssociation
161
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
162
+ association = self.owner.association self.through_reflection.name
163
+ Array(association.target).each do |through_record|
164
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
165
+ end
166
+ end
167
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
147
168
  if records.first.class.name !~ /^HABTM_/
148
169
  if records.size > 1
149
170
  Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
@@ -159,8 +180,8 @@ module Bullet
159
180
 
160
181
  alias_method :origin_empty?, :empty?
161
182
  def empty?
162
- if Bullet.start? && !@reflection.has_cached_counter?
163
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
183
+ if Bullet.start? && !reflection.has_cached_counter?
184
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
164
185
  end
165
186
  origin_empty?
166
187
  end
@@ -168,7 +189,7 @@ module Bullet
168
189
  alias_method :origin_include?, :include?
169
190
  def include?(object)
170
191
  if Bullet.start?
171
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
192
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
172
193
  end
173
194
  origin_include?(object)
174
195
  end
@@ -180,9 +201,9 @@ module Bullet
180
201
  def reader(force_reload = false)
181
202
  result = origin_reader(force_reload)
182
203
  if Bullet.start?
183
- if @owner.class.name !~ /^HABTM_/ && !@inversed
184
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
185
- if Bullet::Detector::NPlusOneQuery.impossible?(@owner)
204
+ if owner.class.name !~ /^HABTM_/ && !@inversed
205
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
206
+ if Bullet::Detector::NPlusOneQuery.impossible?(owner)
186
207
  Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
187
208
  else
188
209
  Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
@@ -196,28 +217,20 @@ module Bullet
196
217
  ::ActiveRecord::Associations::HasManyAssociation.class_eval do
197
218
  alias_method :origin_many_empty?, :empty?
198
219
  def empty?
199
- Thread.current[:bullet_collection_empty] = true
200
220
  result = origin_many_empty?
201
- Thread.current[:bullet_collection_empty] = nil
202
- if Bullet.start? && !@reflection.has_cached_counter?
203
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
221
+ if Bullet.start? && !reflection.has_cached_counter?
222
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
204
223
  end
205
224
  result
206
225
  end
207
- end
208
-
209
- ::ActiveRecord::Reflection::AbstractReflection.class_eval do
210
- def set_owner(owner)
211
- @owner = owner
212
- end
213
226
 
214
- alias_method :origin_has_cached_counter?, :has_cached_counter?
215
- def has_cached_counter?
216
- result = origin_has_cached_counter?
217
- if Bullet.start? && !result && !Thread.current[:bullet_collection_empty]
218
- Bullet::Detector::CounterCache.add_counter_cache(@owner, @name)
227
+ alias_method :origin_count_records, :count_records
228
+ def count_records
229
+ result = reflection.has_cached_counter?
230
+ if Bullet.start? && !result && !self.is_a?(::ActiveRecord::Associations::ThroughAssociation)
231
+ Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
219
232
  end
220
- result
233
+ origin_count_records
221
234
  end
222
235
  end
223
236
  end
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Bullet
3
- VERSION = "5.1.0"
3
+ VERSION = "5.3.0"
4
4
  end
data/lib/bullet.rb CHANGED
@@ -166,8 +166,9 @@ module Bullet
166
166
  end
167
167
 
168
168
  def perform_out_of_channel_notifications(env = {})
169
+ request_uri = env['REQUEST_URI'] || build_request_uri(env)
169
170
  for_each_active_notifier_with_notification do |notification|
170
- notification.url = env['REQUEST_URI']
171
+ notification.url = request_uri
171
172
  notification.notify_out_of_channel
172
173
  end
173
174
  end
@@ -190,17 +191,22 @@ module Bullet
190
191
  end
191
192
 
192
193
  def profile
194
+ return_value = nil
193
195
  if Bullet.enable?
194
196
  begin
195
197
  Bullet.start_request
196
198
 
197
- yield
199
+ return_value = yield
198
200
 
199
201
  Bullet.perform_out_of_channel_notifications if Bullet.notification?
200
202
  ensure
201
203
  Bullet.end_request
202
204
  end
205
+ else
206
+ return_value = yield
203
207
  end
208
+
209
+ return_value
204
210
  end
205
211
 
206
212
  private
@@ -212,5 +218,13 @@ module Bullet
212
218
  end
213
219
  end
214
220
  end
221
+
222
+ def build_request_uri(env)
223
+ if env['QUERY_STRING'].present?
224
+ "#{env['PATH_INFO']}?#{env['QUERY_STRING']}"
225
+ else
226
+ env['PATH_INFO']
227
+ end
228
+ end
215
229
  end
216
230
  end
@@ -60,7 +60,7 @@ module Bullet
60
60
  it "should return original response body" do
61
61
  expected_response = Support::ResponseDouble.new "Actual body"
62
62
  app.response = expected_response
63
- _, _, response = middleware.call([])
63
+ _, _, response = middleware.call({})
64
64
  expect(response).to eq(expected_response)
65
65
  end
66
66
 
@@ -68,7 +68,7 @@ module Bullet
68
68
  expect(Bullet).to receive(:notification?).and_return(true)
69
69
  expect(Bullet).to receive(:gather_inline_notifications).and_return("<bullet></bullet>")
70
70
  expect(Bullet).to receive(:perform_out_of_channel_notifications)
71
- status, headers, response = middleware.call([200, {"Content-Type" => "text/html"}])
71
+ status, headers, response = middleware.call({"Content-Type" => "text/html"})
72
72
  expect(headers["Content-Length"]).to eq("56")
73
73
  expect(response).to eq(["<html><head></head><body><bullet></bullet></body></html>"])
74
74
  end
@@ -79,7 +79,7 @@ module Bullet
79
79
  app.response = response
80
80
  expect(Bullet).to receive(:notification?).and_return(true)
81
81
  expect(Bullet).to receive(:gather_inline_notifications).and_return("<bullet></bullet>")
82
- status, headers, response = middleware.call([200, {"Content-Type" => "text/html"}])
82
+ status, headers, response = middleware.call({"Content-Type" => "text/html"})
83
83
  expect(headers["Content-Length"]).to eq("58")
84
84
  end
85
85
  end
@@ -89,7 +89,7 @@ module Bullet
89
89
 
90
90
  it "should not call Bullet.start_request" do
91
91
  expect(Bullet).not_to receive(:start_request)
92
- middleware.call([])
92
+ middleware.call({})
93
93
  end
94
94
  end
95
95
  end
data/spec/bullet_spec.rb CHANGED
@@ -94,4 +94,47 @@ describe Bullet, focused: true do
94
94
  end
95
95
  end
96
96
  end
97
+
98
+ describe '#perform_out_of_channel_notifications' do
99
+ let(:notification) { double }
100
+
101
+ before do
102
+ allow(Bullet).to receive(:for_each_active_notifier_with_notification).and_yield(notification)
103
+ allow(notification).to receive(:notify_out_of_channel)
104
+ end
105
+
106
+ context 'when called with no args' do
107
+ it 'should notification.url is nil' do
108
+ expect(notification).to receive(:url=).with(nil)
109
+ Bullet.perform_out_of_channel_notifications
110
+ end
111
+ end
112
+
113
+ context 'when called with Rack environment hash' do
114
+ let(:env) {
115
+ {
116
+ 'PATH_INFO' => '/path',
117
+ 'QUERY_STRING' => 'foo=bar',
118
+ }
119
+ }
120
+
121
+ context "when env['REQUEST_URI'] is nil" do
122
+ before { env['REQUEST_URI'] = nil }
123
+
124
+ it 'should notification.url is built' do
125
+ expect(notification).to receive(:url=).with('/path?foo=bar')
126
+ Bullet.perform_out_of_channel_notifications(env)
127
+ end
128
+ end
129
+
130
+ context "when env['REQUEST_URI'] is present" do
131
+ before { env['REQUEST_URI'] = 'http://example.com/path' }
132
+
133
+ it "should notification.url is env['REQUEST_URI']" do
134
+ expect(notification).to receive(:url=).with(env['REQUEST_URI'])
135
+ Bullet.perform_out_of_channel_notifications(env)
136
+ end
137
+ end
138
+ end
139
+ end
97
140
  end
@@ -13,6 +13,16 @@ if !mongoid? && active_record3?
13
13
  expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
14
14
  end
15
15
 
16
+ it "should detect non preload post => comments for find_by_sql" do
17
+ Post.find_by_sql("SELECT * FROM posts").each do |post|
18
+ post.comments.map(&:name)
19
+ end
20
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
21
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
22
+
23
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
24
+ end
25
+
16
26
  it "should detect preload with post => comments" do
17
27
  Post.includes(:comments).each do |post|
18
28
  post.comments.map(&:name)
@@ -214,7 +224,7 @@ if !mongoid? && active_record3?
214
224
  context "post => comment" do
215
225
  it "should detect unused preload with post => comments" do
216
226
  Post.includes(:comments).each do |post|
217
- post.comments.first.name
227
+ post.comments.first.name if post.comments.first
218
228
  end
219
229
  Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
220
230
  expect(Bullet::Detector::Association).not_to be_unused_preload_associations_for(Post, :comments)
@@ -453,6 +463,16 @@ if !mongoid? && active_record3?
453
463
 
454
464
  expect(Bullet::Detector::Association).to be_completely_preloading_associations
455
465
  end
466
+
467
+ it "should detect non preload student => teachers with empty?" do
468
+ Student.all.each do |student|
469
+ student.teachers.empty?
470
+ end
471
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
472
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
473
+
474
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Student, :teachers)
475
+ end
456
476
  end
457
477
  end
458
478
 
@@ -13,6 +13,16 @@ if !mongoid? && active_record4?
13
13
  expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
14
14
  end
15
15
 
16
+ it "should detect non preload post => comments for find_by_sql" do
17
+ Post.find_by_sql("SELECT * FROM posts").each do |post|
18
+ post.comments.map(&:name)
19
+ end
20
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
21
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
22
+
23
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
24
+ end
25
+
16
26
  it "should detect preload with post => comments" do
17
27
  Post.includes(:comments).each do |post|
18
28
  post.comments.map(&:name)
@@ -214,7 +224,7 @@ if !mongoid? && active_record4?
214
224
  context "post => comment" do
215
225
  it "should detect unused preload with post => comments" do
216
226
  Post.includes(:comments).each do |post|
217
- post.comments.first.name
227
+ post.comments.first.name if post.comments.first
218
228
  end
219
229
  Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
220
230
  expect(Bullet::Detector::Association).not_to be_unused_preload_associations_for(Post, :comments)
@@ -465,6 +475,16 @@ if !mongoid? && active_record4?
465
475
 
466
476
  expect(Bullet::Detector::Association).to be_completely_preloading_associations
467
477
  end
478
+
479
+ it "should detect non preload student => teachers with empty?" do
480
+ Student.all.each do |student|
481
+ student.teachers.empty?
482
+ end
483
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
484
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
485
+
486
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Student, :teachers)
487
+ end
468
488
  end
469
489
  end
470
490
 
@@ -13,6 +13,16 @@ if !mongoid? && active_record5?
13
13
  expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
14
14
  end
15
15
 
16
+ it "should detect non preload post => comments for find_by_sql" do
17
+ Post.find_by_sql("SELECT * FROM posts").each do |post|
18
+ post.comments.map(&:name)
19
+ end
20
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
21
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
22
+
23
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
24
+ end
25
+
16
26
  it "should detect preload with post => comments" do
17
27
  Post.includes(:comments).each do |post|
18
28
  post.comments.map(&:name)
@@ -214,7 +224,7 @@ if !mongoid? && active_record5?
214
224
  context "post => comment" do
215
225
  it "should detect unused preload with post => comments" do
216
226
  Post.includes(:comments).each do |post|
217
- post.comments.first.name
227
+ post.comments.first.name if post.comments.first
218
228
  end
219
229
  Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
220
230
  expect(Bullet::Detector::Association).not_to be_unused_preload_associations_for(Post, :comments)
@@ -465,6 +475,16 @@ if !mongoid? && active_record5?
465
475
 
466
476
  expect(Bullet::Detector::Association).to be_completely_preloading_associations
467
477
  end
478
+
479
+ it "should detect non preload student => teachers with empty?" do
480
+ Student.all.each do |student|
481
+ student.teachers.empty?
482
+ end
483
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
484
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
485
+
486
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Student, :teachers)
487
+ end
468
488
  end
469
489
  end
470
490
 
data/spec/spec_helper.rb CHANGED
@@ -30,9 +30,6 @@ $LOAD_PATH.unshift(MODELS)
30
30
  SUPPORT = File.join(File.dirname(__FILE__), "support")
31
31
  Dir[ File.join(SUPPORT, "*.rb") ].reject { |filename| filename =~ /_seed.rb$/ }.sort.each { |file| require file }
32
32
 
33
- require 'coveralls'
34
- Coveralls.wear!
35
-
36
33
  RSpec.configure do |config|
37
34
  config.extend Bullet::Dependency
38
35
 
@@ -16,6 +16,7 @@ module Support
16
16
  post1 = category1.posts.create(:name => 'first', :writer => writer1)
17
17
  post1a = category1.posts.create(:name => 'like first', :writer => writer2, active: false)
18
18
  post2 = category2.posts.create(:name => 'second', :writer => writer2)
19
+ post3 = category2.posts.create(:name => 'third', :writer => writer2)
19
20
 
20
21
  comment1 = post1.comments.create(:name => 'first', :author => writer1)
21
22
  comment2 = post1.comments.create(:name => 'first2', :author => writer1)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bullet
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0
4
+ version: 5.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-22 00:00:00.000000000 Z
11
+ date: 2016-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport