bullet 6.1.4 → 7.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +82 -0
  3. data/CHANGELOG.md +40 -0
  4. data/Gemfile.rails-7.0 +10 -0
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +32 -26
  7. data/lib/bullet/active_record41.rb +1 -0
  8. data/lib/bullet/active_record42.rb +1 -0
  9. data/lib/bullet/active_record5.rb +10 -8
  10. data/lib/bullet/active_record52.rb +21 -25
  11. data/lib/bullet/active_record60.rb +20 -24
  12. data/lib/bullet/active_record61.rb +20 -24
  13. data/lib/bullet/active_record70.rb +284 -0
  14. data/lib/bullet/bullet_xhr.js +3 -2
  15. data/lib/bullet/dependency.rb +10 -0
  16. data/lib/bullet/detector/association.rb +8 -0
  17. data/lib/bullet/detector/base.rb +2 -1
  18. data/lib/bullet/detector/counter_cache.rb +2 -2
  19. data/lib/bullet/detector/n_plus_one_query.rb +24 -13
  20. data/lib/bullet/detector/unused_eager_loading.rb +3 -3
  21. data/lib/bullet/mongoid7x.rb +34 -19
  22. data/lib/bullet/notification.rb +2 -1
  23. data/lib/bullet/rack.rb +42 -7
  24. data/lib/bullet/registry/call_stack.rb +12 -0
  25. data/lib/bullet/registry.rb +1 -0
  26. data/lib/bullet/stack_trace_filter.rb +14 -10
  27. data/lib/bullet/version.rb +1 -1
  28. data/lib/bullet.rb +28 -24
  29. data/lib/generators/bullet/install_generator.rb +0 -1
  30. data/perf/benchmark.rb +4 -1
  31. data/spec/bullet/detector/n_plus_one_query_spec.rb +1 -33
  32. data/spec/bullet/detector/unused_eager_loading_spec.rb +11 -2
  33. data/spec/bullet/ext/object_spec.rb +1 -1
  34. data/spec/bullet/notification/base_spec.rb +4 -4
  35. data/spec/bullet/rack_spec.rb +50 -18
  36. data/spec/bullet/stack_trace_filter_spec.rb +26 -0
  37. data/spec/bullet_spec.rb +15 -15
  38. data/spec/integration/active_record/association_spec.rb +58 -10
  39. data/spec/integration/counter_cache_spec.rb +4 -4
  40. data/spec/integration/mongoid/association_spec.rb +1 -1
  41. data/spec/models/deal.rb +5 -0
  42. data/spec/models/folder.rb +2 -1
  43. data/spec/models/group.rb +2 -1
  44. data/spec/models/page.rb +2 -1
  45. data/spec/models/post.rb +2 -0
  46. data/spec/models/role.rb +7 -0
  47. data/spec/models/user.rb +1 -0
  48. data/spec/models/writer.rb +2 -1
  49. data/spec/spec_helper.rb +0 -2
  50. data/spec/support/mongo_seed.rb +1 -0
  51. data/spec/support/sqlite_seed.rb +30 -0
  52. data/test.sh +2 -0
  53. metadata +13 -4
  54. data/.travis.yml +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 74ea4e863bfe254dde17af73253ffe7041cd2b2dabee2dad05d1eccb6904f229
4
- data.tar.gz: 284dcd1a516922384bdaf1b3dd9e6a5e6776c213c3ecbc9ca41b223c5b92d36b
3
+ metadata.gz: f67f5d4add512b0606aa27cf26e9d2a628d6eb7dc58078d4c3eaeca4d3bfccf7
4
+ data.tar.gz: aa55cdd15e0f585a679595b98b858194c3d217acedf9ef0db70085cc74b7710c
5
5
  SHA512:
6
- metadata.gz: 26c43bfdac9582f059d067d4f56d4d85ed28416654ff5fd4686bf9977cbfbc8d92f887079954662e904fbe3d1b861d02482ac2fefac30a859fbdad8c3833e225
7
- data.tar.gz: 48decfa9d28b9936f5c1f39c669b6e8061fae649f9036c29d4b28138083a43db8739f3b73bb9c390e9f6baea62aa1f66f79e8ced69cf13e789b8f116c101f25f
6
+ metadata.gz: 919bb619038a60d8657bc09f2c34949934c933de190ae0d3ed2025e4599e6cbe284b132b2ed92ea6f94dfbc06566ba56aa522684107cab1aa3a1386a431a527e
7
+ data.tar.gz: 3682a26c40d5a6b6559f7d82324ad3236ba91db28a99f1416117a5147986e26cdc75c643a0a0a58891f781ba5511a44d90124f5f7a637ce43b2d3c3e31baf3a5
@@ -0,0 +1,82 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: CI
9
+
10
+ on:
11
+ push:
12
+ branches: [ main ]
13
+ pull_request:
14
+ branches: [ main ]
15
+
16
+ jobs:
17
+ test_rails_4:
18
+ runs-on: ubuntu-latest
19
+ strategy:
20
+ matrix:
21
+ gemfile: ['Gemfile.rails-4.0', 'Gemfile.rails-4.1', 'Gemfile.rails-4.2']
22
+ env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
23
+ BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}
24
+ steps:
25
+ - uses: actions/checkout@v2
26
+ - name: Set up Ruby
27
+ uses: ruby/setup-ruby@v1
28
+ with:
29
+ ruby-version: 2.3
30
+ bundler: 1
31
+ bundler-cache: true
32
+ - name: Run tests
33
+ run: bundle exec rake
34
+ test_rails_5:
35
+ runs-on: ubuntu-latest
36
+ strategy:
37
+ matrix:
38
+ gemfile: ['Gemfile.rails-5.0', 'Gemfile.rails-5.1', 'Gemfile.rails-5.2']
39
+ env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
40
+ BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}
41
+ steps:
42
+ - uses: actions/checkout@v2
43
+ - name: Set up Ruby
44
+ uses: ruby/setup-ruby@v1
45
+ with:
46
+ ruby-version: 2.5
47
+ bundler: 1
48
+ bundler-cache: true
49
+ - name: Run tests
50
+ run: bundle exec rake
51
+ test_rails_6:
52
+ runs-on: ubuntu-latest
53
+ strategy:
54
+ matrix:
55
+ gemfile: ['Gemfile.rails-6.0', 'Gemfile.rails-6.1']
56
+ env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
57
+ BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}
58
+ steps:
59
+ - uses: actions/checkout@v2
60
+ - name: Set up Ruby
61
+ uses: ruby/setup-ruby@v1
62
+ with:
63
+ ruby-version: 2.7
64
+ bundler-cache: true
65
+ - name: Run tests
66
+ run: bundle exec rake
67
+ test_rails_7:
68
+ runs-on: ubuntu-latest
69
+ strategy:
70
+ matrix:
71
+ gemfile: ['Gemfile.rails-7.0']
72
+ env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
73
+ BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}
74
+ steps:
75
+ - uses: actions/checkout@v2
76
+ - name: Set up Ruby
77
+ uses: ruby/setup-ruby@v1
78
+ with:
79
+ ruby-version: 3.1
80
+ bundler-cache: true
81
+ - name: Run tests
82
+ run: bundle exec rake
data/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  ## Next Release
2
2
 
3
+ ## 7.0.5 (01/01/2023)
4
+
5
+ * Fix n+1 false positives in AR 7.0
6
+ * Fix eager_load nested has_many :through false positives
7
+ * Respect Content-Security-Policy nonces
8
+ * Added CallStacks support for avoid eager loading
9
+ * Iterate fewer times over objects
10
+
11
+ ## 7.0.4 (11/28/2022)
12
+
13
+ * Fix `eager_load` `has_many :through` false positives
14
+ * mongoid7x: add dynamic methods
15
+
16
+ ## 7.0.3 (08/13/2022)
17
+
18
+ * Replace `Array()` with `Array.wrap()`
19
+
20
+ ## 7.0.2 (05/31/2022)
21
+
22
+ * Drop growl support
23
+ * Do not check html tag in Bullet::Rack anymore
24
+
25
+ ## 7.0.1 (01/15/2022)
26
+
27
+ * Get rid of *_whitelist methods
28
+ * Hack ActiveRecord::Associations::Preloader::Batch in rails 7
29
+
30
+ ## 7.0.0 (12/18/2021)
31
+
32
+ * Support rails 7
33
+ * Fix Mongoid 7 view iteration
34
+ * Move CI from Travis to Github Actions
35
+
36
+ ## 6.1.5 (08/16/2021)
37
+
38
+ * Rename whitelist to safelist
39
+ * Fix onload called twice
40
+ * Support Rack::Files::Iterator responses
41
+ * Ensure HABTM associations are not incorrectly labeled n+1
42
+
3
43
  ## 6.1.4 (02/26/2021)
4
44
 
5
45
  * Added an option to stop adding HTTP headers to API requests
data/Gemfile.rails-7.0 ADDED
@@ -0,0 +1,10 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'rails', '~> 7.0.0'
6
+ gem 'sqlite3'
7
+ gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
+ gem 'activerecord-import'
9
+
10
+ gem "rspec"
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 - 2010 Richard Huang (flyerhzm@gmail.com)
1
+ Copyright (c) 2009 - 2022 Richard Huang (flyerhzm@gmail.com)
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Bullet
2
2
 
3
+ ![Main workflow](https://github.com/flyerhzm/bullet/actions/workflows/main.yml/badge.svg)
3
4
  [![Gem Version](https://badge.fury.io/rb/bullet.svg)](http://badge.fury.io/rb/bullet)
4
- [![Build Status](https://secure.travis-ci.org/flyerhzm/bullet.svg)](http://travis-ci.org/flyerhzm/bullet)
5
5
  [![AwesomeCode Status for flyerhzm/bullet](https://awesomecode.io/projects/6755235b-e2c1-459e-bf92-b8b13d0c0472/status)](https://awesomecode.io/repos/flyerhzm/bullet)
6
6
  [![Coderwall Endorse](http://api.coderwall.com/flyerhzm/endorsecount.png)](http://coderwall.com/flyerhzm)
7
7
 
@@ -49,7 +49,7 @@ mongoid.
49
49
 
50
50
  ## Configuration
51
51
 
52
- Bullet won't do ANYTHING unless you tell it to explicitly. Append to
52
+ Bullet won't enable any notification systems unless you tell it to explicitly. Append to
53
53
  `config/environments/development.rb` initializer with the following code:
54
54
 
55
55
  ```ruby
@@ -59,7 +59,6 @@ config.after_initialize do
59
59
  Bullet.alert = true
60
60
  Bullet.bullet_logger = true
61
61
  Bullet.console = true
62
- Bullet.growl = true
63
62
  Bullet.xmpp = { :account => 'bullets_account@jabber.org',
64
63
  :password => 'bullets_password_for_jabber',
65
64
  :receiver => 'your_account@jabber.org',
@@ -67,6 +66,7 @@ config.after_initialize do
67
66
  Bullet.rails_logger = true
68
67
  Bullet.honeybadger = true
69
68
  Bullet.bugsnag = true
69
+ Bullet.appsignal = true
70
70
  Bullet.airbrake = true
71
71
  Bullet.rollbar = true
72
72
  Bullet.add_footer = true
@@ -84,12 +84,12 @@ The code above will enable all of the Bullet notification systems:
84
84
  * `Bullet.alert`: pop up a JavaScript alert in the browser
85
85
  * `Bullet.bullet_logger`: log to the Bullet log file (Rails.root/log/bullet.log)
86
86
  * `Bullet.console`: log warnings to your browser's console.log (Safari/Webkit browsers or Firefox w/Firebug installed)
87
- * `Bullet.growl`: pop up Growl warnings if your system has Growl installed. Requires a little bit of configuration
88
87
  * `Bullet.xmpp`: send XMPP/Jabber notifications to the receiver indicated. Note that the code will currently not handle the adding of contacts, so you will need to make both accounts indicated know each other manually before you will receive any notifications. If you restart the development server frequently, the 'coming online' sound for the Bullet account may start to annoy - in this case set :show_online_status to false; you will still get notifications, but the Bullet account won't announce it's online status anymore.
89
88
  * `Bullet.rails_logger`: add warnings directly to the Rails log
90
89
  * `Bullet.honeybadger`: add notifications to Honeybadger
91
90
  * `Bullet.bugsnag`: add notifications to bugsnag
92
91
  * `Bullet.airbrake`: add notifications to airbrake
92
+ * `Bullet.appsignal`: add notifications to AppSignal
93
93
  * `Bullet.rollbar`: add notifications to rollbar
94
94
  * `Bullet.sentry`: add notifications to sentry
95
95
  * `Bullet.add_footer`: adds the details in the bottom left corner of the page. Double click the footer or use close button to hide footer.
@@ -119,15 +119,15 @@ Bullet.unused_eager_loading_enable = false
119
119
  Bullet.counter_cache_enable = false
120
120
  ```
121
121
 
122
- ## Whitelist
122
+ ## Safe list
123
123
 
124
124
  Sometimes Bullet may notify you of query problems you don't care to fix, or
125
- which come from outside your code. You can whitelist these to ignore them:
125
+ which come from outside your code. You can add them to a safe list to ignore them:
126
126
 
127
127
  ```ruby
128
- Bullet.add_whitelist :type => :n_plus_one_query, :class_name => "Post", :association => :comments
129
- Bullet.add_whitelist :type => :unused_eager_loading, :class_name => "Post", :association => :comments
130
- Bullet.add_whitelist :type => :counter_cache, :class_name => "Country", :association => :cities
128
+ Bullet.add_safelist :type => :n_plus_one_query, :class_name => "Post", :association => :comments
129
+ Bullet.add_safelist :type => :unused_eager_loading, :class_name => "Post", :association => :comments
130
+ Bullet.add_safelist :type => :counter_cache, :class_name => "Country", :association => :cities
131
131
  ```
132
132
 
133
133
  If you want to skip bullet in some specific controller actions, you can
@@ -154,25 +154,26 @@ The Bullet log `log/bullet.log` will look something like this:
154
154
  * N+1 Query:
155
155
 
156
156
  ```
157
- 2009-08-25 20:40:17[INFO] N+1 Query: PATH_INFO: /posts; model: Post => associations: [comments]·
158
- Add to your finder: :include => [:comments]
159
- 2009-08-25 20:40:17[INFO] N+1 Query: method call stack:·
160
- /Users/richard/Downloads/test/app/views/posts/index.html.erb:11:in `_run_erb_app47views47posts47index46html46erb'
161
- /Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `each'
162
- /Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `_run_erb_app47views47posts47index46html46erb'
163
- /Users/richard/Downloads/test/app/controllers/posts_controller.rb:7:in `index'
157
+ 2009-08-25 20:40:17[INFO] USE eager loading detected:
158
+ Post => [:comments]·
159
+ Add to your query: .includes([:comments])
160
+ 2009-08-25 20:40:17[INFO] Call stack
161
+ /Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `each'
162
+ /Users/richard/Downloads/test/app/controllers/posts_controller.rb:7:in `index'
164
163
  ```
165
164
 
166
- The first two lines are notifications that N+1 queries have been encountered. The remaining lines are stack traces so you can find exactly where the queries were invoked in your code, and fix them.
165
+ The first log entry is a notification that N+1 queries have been encountered. The remaining entry is a stack trace so you can find exactly where the queries were invoked in your code, and fix them.
167
166
 
168
167
  * Unused eager loading:
169
168
 
170
169
  ```
171
- 2009-08-25 20:53:56[INFO] Unused eager loadings: PATH_INFO: /posts; model: Post => associations: [comments]·
172
- Remove from your finder: :include => [:comments]
170
+ 2009-08-25 20:53:56[INFO] AVOID eager loading detected
171
+ Post => [:comments]·
172
+ Remove from your query: .includes([:comments])
173
+ 2009-08-25 20:53:56[INFO] Call stack
173
174
  ```
174
175
 
175
- These two lines are notifications that unused eager loadings have been encountered.
176
+ These lines are notifications that unused eager loadings have been encountered.
176
177
 
177
178
  * Need counter cache:
178
179
 
@@ -181,10 +182,14 @@ These two lines are notifications that unused eager loadings have been encounter
181
182
  Post => [:comments]
182
183
  ```
183
184
 
184
- ## Growl, XMPP/Jabber and Airbrake Support
185
+ ## XMPP/Jabber and Airbrake Support
185
186
 
186
187
  see [https://github.com/flyerhzm/uniform_notifier](https://github.com/flyerhzm/uniform_notifier)
187
188
 
189
+ ## Growl Support
190
+
191
+ Growl support is dropped from uniform_notifier 1.16.0, if you still want it, please use uniform_notifier 1.15.0.
192
+
188
193
  ## Important
189
194
 
190
195
  If you find Bullet does not work for you, *please disable your browser's cache*.
@@ -217,7 +222,7 @@ end
217
222
 
218
223
  ### Work with sinatra
219
224
 
220
- Configure and use `Bullet::Rack`
225
+ Configure and use `Bullet::Rack`.
221
226
 
222
227
  ```ruby
223
228
  configure :development do
@@ -227,6 +232,8 @@ configure :development do
227
232
  end
228
233
  ```
229
234
 
235
+ If your application generates a Content-Security-Policy via a separate middleware, ensure that `Bullet::Rack` is loaded _before_ that middleware.
236
+
230
237
  ### Run in tests
231
238
 
232
239
  First you need to enable Bullet in test environment.
@@ -268,8 +275,7 @@ Bullet outputs some details info, to enable debug mode, set
268
275
  ## Demo
269
276
 
270
277
  Bullet is designed to function as you browse through your application in development. To see it in action,
271
- you can visit [https://github.com/flyerhzm/bullet_test](https://github.com/flyerhzm/bullet_test) or
272
- follow these steps to create, detect, and fix example query problems.
278
+ you can follow these steps to create, detect, and fix example query problems.
273
279
 
274
280
  1\. Create an example application
275
281
 
@@ -281,7 +287,7 @@ $ rails g scaffold comment name:string post_id:integer
281
287
  $ bundle exec rake db:migrate
282
288
  ```
283
289
 
284
- 2\. Change `app/model/post.rb` and `app/model/comment.rb`
290
+ 2\. Change `app/models/post.rb` and `app/models/comment.rb`
285
291
 
286
292
  ```ruby
287
293
  class Post < ActiveRecord::Base
@@ -480,4 +486,4 @@ Meanwhile, there's a line appended to `log/bullet.log`
480
486
  Post => [:comments]
481
487
  ```
482
488
 
483
- Copyright (c) 2009 - 2019 Richard Huang (flyerhzm@gmail.com), released under the MIT license
489
+ Copyright (c) 2009 - 2022 Richard Huang (flyerhzm@gmail.com), released under the MIT license
@@ -30,6 +30,7 @@ module Bullet
30
30
 
31
31
  ::ActiveRecord::Relation.class_eval do
32
32
  alias_method :origin_to_a, :to_a
33
+
33
34
  # if select a collection of objects, then these objects have possible to cause N+1 query.
34
35
  # if select only one object, then the only one object has impossible to cause N+1 query.
35
36
  def to_a
@@ -52,6 +52,7 @@ module Bullet
52
52
 
53
53
  ::ActiveRecord::Relation.class_eval do
54
54
  alias_method :origin_to_a, :to_a
55
+
55
56
  # if select a collection of objects, then these objects have possible to cause N+1 query.
56
57
  # if select only one object, then the only one object has impossible to cause N+1 query.
57
58
  def to_a
@@ -177,16 +177,18 @@ module Bullet
177
177
  if Bullet.start?
178
178
  if is_a? ::ActiveRecord::Associations::ThroughAssociation
179
179
  refl = reflection.through_reflection
180
- Bullet::Detector::NPlusOneQuery.call_association(owner, refl.name)
181
- association = owner.association refl.name
182
- Array(association.target).each do |through_record|
183
- Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
184
- end
180
+ association = owner.association(refl.name)
181
+ if association.loaded?
182
+ Bullet::Detector::NPlusOneQuery.call_association(owner, refl.name)
183
+ Array.wrap(association.target).each do |through_record|
184
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
185
+ end
185
186
 
186
- if refl.through_reflection?
187
- refl = refl.through_reflection while refl.through_reflection?
187
+ if refl.through_reflection?
188
+ refl = refl.through_reflection while refl.through_reflection?
188
189
 
189
- Bullet::Detector::NPlusOneQuery.call_association(owner, refl.name)
190
+ Bullet::Detector::NPlusOneQuery.call_association(owner, refl.name)
191
+ end
190
192
  end
191
193
  end
192
194
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
@@ -75,23 +75,6 @@ module Bullet
75
75
  end
76
76
  )
77
77
 
78
- ::ActiveRecord::FinderMethods.prepend(
79
- Module.new do
80
- # add includes in scope
81
- def find_with_associations
82
- return super { |r| yield r } if block_given?
83
-
84
- records = super
85
- if Bullet.start?
86
- associations = (eager_load_values + includes_values).uniq
87
- records.each { |record| Bullet::Detector::Association.add_object_associations(record, associations) }
88
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
89
- end
90
- records
91
- end
92
- end
93
- )
94
-
95
78
  ::ActiveRecord::Associations::JoinDependency.prepend(
96
79
  Module.new do
97
80
  def instantiate(result_set, &block)
@@ -149,6 +132,17 @@ module Bullet
149
132
  end
150
133
  )
151
134
 
135
+ ::ActiveRecord::Associations::Association.prepend(
136
+ Module.new do
137
+ def inversed_from(record)
138
+ if Bullet.start?
139
+ Bullet::Detector::NPlusOneQuery.add_inversed_object(owner, reflection.name)
140
+ end
141
+ super
142
+ end
143
+ end
144
+ )
145
+
152
146
  ::ActiveRecord::Associations::CollectionAssociation.prepend(
153
147
  Module.new do
154
148
  def load_target
@@ -156,14 +150,16 @@ module Bullet
156
150
 
157
151
  if Bullet.start?
158
152
  if is_a? ::ActiveRecord::Associations::ThroughAssociation
159
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
160
- association = owner.association reflection.through_reflection.name
161
- Array(association.target).each do |through_record|
162
- Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
163
- end
153
+ association = owner.association(reflection.through_reflection.name)
154
+ if association.loaded?
155
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
156
+ Array.wrap(association.target).each do |through_record|
157
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
158
+ end
164
159
 
165
- if reflection.through_reflection != through_reflection
166
- Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
160
+ if reflection.through_reflection != through_reflection
161
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
162
+ end
167
163
  end
168
164
  end
169
165
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
@@ -205,7 +201,7 @@ module Bullet
205
201
  if is_a? ::ActiveRecord::Associations::ThroughAssociation
206
202
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
207
203
  association = owner.association reflection.through_reflection.name
208
- Array(association.target).each do |through_record|
204
+ Array.wrap(association.target).each do |through_record|
209
205
  Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
210
206
  end
211
207
 
@@ -102,23 +102,6 @@ module Bullet
102
102
  end
103
103
  )
104
104
 
105
- ::ActiveRecord::FinderMethods.prepend(
106
- Module.new do
107
- # add includes in scope
108
- def find_with_associations
109
- return super { |r| yield r } if block_given?
110
-
111
- records = super
112
- if Bullet.start?
113
- associations = (eager_load_values + includes_values).uniq
114
- records.each { |record| Bullet::Detector::Association.add_object_associations(record, associations) }
115
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
116
- end
117
- records
118
- end
119
- end
120
- )
121
-
122
105
  ::ActiveRecord::Associations::JoinDependency.prepend(
123
106
  Module.new do
124
107
  def instantiate(result_set, &block)
@@ -176,6 +159,17 @@ module Bullet
176
159
  end
177
160
  )
178
161
 
162
+ ::ActiveRecord::Associations::Association.prepend(
163
+ Module.new do
164
+ def inversed_from(record)
165
+ if Bullet.start?
166
+ Bullet::Detector::NPlusOneQuery.add_inversed_object(owner, reflection.name)
167
+ end
168
+ super
169
+ end
170
+ end
171
+ )
172
+
179
173
  ::ActiveRecord::Associations::CollectionAssociation.prepend(
180
174
  Module.new do
181
175
  def load_target
@@ -183,14 +177,16 @@ module Bullet
183
177
 
184
178
  if Bullet.start?
185
179
  if is_a? ::ActiveRecord::Associations::ThroughAssociation
186
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
187
180
  association = owner.association(reflection.through_reflection.name)
188
- Array(association.target).each do |through_record|
189
- Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
190
- end
181
+ if association.loaded?
182
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
183
+ Array.wrap(association.target).each do |through_record|
184
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
185
+ end
191
186
 
192
- if reflection.through_reflection != through_reflection
193
- Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
187
+ if reflection.through_reflection != through_reflection
188
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
189
+ end
194
190
  end
195
191
  end
196
192
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
@@ -232,7 +228,7 @@ module Bullet
232
228
  if is_a? ::ActiveRecord::Associations::ThroughAssociation
233
229
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
234
230
  association = owner.association(reflection.through_reflection.name)
235
- Array(association.target).each do |through_record|
231
+ Array.wrap(association.target).each do |through_record|
236
232
  Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
237
233
  end
238
234
 
@@ -102,23 +102,6 @@ module Bullet
102
102
  end
103
103
  )
104
104
 
105
- ::ActiveRecord::FinderMethods.prepend(
106
- Module.new do
107
- # add includes in scope
108
- def find_with_associations
109
- return super { |r| yield r } if block_given?
110
-
111
- records = super
112
- if Bullet.start?
113
- associations = (eager_load_values + includes_values).uniq
114
- records.each { |record| Bullet::Detector::Association.add_object_associations(record, associations) }
115
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
116
- end
117
- records
118
- end
119
- end
120
- )
121
-
122
105
  ::ActiveRecord::Associations::JoinDependency.prepend(
123
106
  Module.new do
124
107
  def instantiate(result_set, strict_loading_value, &block)
@@ -176,6 +159,17 @@ module Bullet
176
159
  end
177
160
  )
178
161
 
162
+ ::ActiveRecord::Associations::Association.prepend(
163
+ Module.new do
164
+ def inversed_from(record)
165
+ if Bullet.start?
166
+ Bullet::Detector::NPlusOneQuery.add_inversed_object(owner, reflection.name)
167
+ end
168
+ super
169
+ end
170
+ end
171
+ )
172
+
179
173
  ::ActiveRecord::Associations::CollectionAssociation.prepend(
180
174
  Module.new do
181
175
  def load_target
@@ -183,14 +177,16 @@ module Bullet
183
177
 
184
178
  if Bullet.start?
185
179
  if is_a? ::ActiveRecord::Associations::ThroughAssociation
186
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
187
180
  association = owner.association(reflection.through_reflection.name)
188
- Array(association.target).each do |through_record|
189
- Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
190
- end
181
+ if association.loaded?
182
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
183
+ Array.wrap(association.target).each do |through_record|
184
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
185
+ end
191
186
 
192
- if reflection.through_reflection != through_reflection
193
- Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
187
+ if reflection.through_reflection != through_reflection
188
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
189
+ end
194
190
  end
195
191
  end
196
192
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
@@ -232,7 +228,7 @@ module Bullet
232
228
  if is_a? ::ActiveRecord::Associations::ThroughAssociation
233
229
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
234
230
  association = owner.association(reflection.through_reflection.name)
235
- Array(association.target).each do |through_record|
231
+ Array.wrap(association.target).each do |through_record|
236
232
  Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
237
233
  end
238
234