bullet 6.1.0 → 7.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +82 -0
  3. data/CHANGELOG.md +45 -0
  4. data/Gemfile.rails-6.0 +1 -1
  5. data/Gemfile.rails-6.1 +15 -0
  6. data/Gemfile.rails-7.0 +10 -0
  7. data/README.md +37 -25
  8. data/lib/bullet/active_job.rb +5 -1
  9. data/lib/bullet/active_record41.rb +1 -0
  10. data/lib/bullet/active_record42.rb +1 -0
  11. data/lib/bullet/active_record5.rb +1 -1
  12. data/lib/bullet/active_record52.rb +23 -18
  13. data/lib/bullet/active_record60.rb +23 -18
  14. data/lib/bullet/active_record61.rb +272 -0
  15. data/lib/bullet/active_record70.rb +275 -0
  16. data/lib/bullet/bullet_xhr.js +18 -17
  17. data/lib/bullet/dependency.rb +16 -0
  18. data/lib/bullet/detector/base.rb +2 -1
  19. data/lib/bullet/detector/counter_cache.rb +2 -2
  20. data/lib/bullet/detector/n_plus_one_query.rb +5 -5
  21. data/lib/bullet/detector/unused_eager_loading.rb +2 -2
  22. data/lib/bullet/mongoid4x.rb +1 -1
  23. data/lib/bullet/mongoid5x.rb +1 -1
  24. data/lib/bullet/mongoid6x.rb +1 -1
  25. data/lib/bullet/mongoid7x.rb +24 -7
  26. data/lib/bullet/notification.rb +2 -1
  27. data/lib/bullet/rack.rb +28 -19
  28. data/lib/bullet/stack_trace_filter.rb +7 -9
  29. data/lib/bullet/version.rb +1 -1
  30. data/lib/bullet.rb +29 -28
  31. data/lib/generators/bullet/install_generator.rb +22 -25
  32. data/perf/benchmark.rb +4 -1
  33. data/spec/bullet/detector/counter_cache_spec.rb +1 -1
  34. data/spec/bullet/detector/n_plus_one_query_spec.rb +1 -1
  35. data/spec/bullet/detector/unused_eager_loading_spec.rb +10 -10
  36. data/spec/bullet/ext/object_spec.rb +1 -1
  37. data/spec/bullet/notification/base_spec.rb +4 -4
  38. data/spec/bullet/notification/n_plus_one_query_spec.rb +1 -3
  39. data/spec/bullet/rack_spec.rb +135 -9
  40. data/spec/bullet_spec.rb +15 -15
  41. data/spec/integration/active_record/association_spec.rb +64 -10
  42. data/spec/integration/counter_cache_spec.rb +4 -4
  43. data/spec/integration/mongoid/association_spec.rb +4 -4
  44. data/spec/models/attachment.rb +5 -0
  45. data/spec/models/deal.rb +5 -0
  46. data/spec/models/folder.rb +2 -1
  47. data/spec/models/group.rb +2 -1
  48. data/spec/models/page.rb +2 -1
  49. data/spec/models/post.rb +2 -0
  50. data/spec/models/role.rb +7 -0
  51. data/spec/models/submission.rb +1 -0
  52. data/spec/models/user.rb +2 -0
  53. data/spec/models/writer.rb +2 -1
  54. data/spec/spec_helper.rb +0 -2
  55. data/spec/support/mongo_seed.rb +1 -0
  56. data/spec/support/sqlite_seed.rb +38 -0
  57. data/test.sh +2 -0
  58. metadata +17 -7
  59. data/.travis.yml +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd668fdfd675ea8437eee0242974d7cb1cfacc0479be4cc5f3f58ee7d05a6d63
4
- data.tar.gz: 3d841fb8471fe18b66135fa087ee3d1874f412c114d09ba05ba150e2daaf9775
3
+ metadata.gz: ad1f81292cc89778a41c6dd178bbfb6342fe1b69c4e57c3fdad0d8003d97b525
4
+ data.tar.gz: 5c1c8113f6e03ecdb449bce5e12273b969f29c323a5bb802c13c0539ea439a69
5
5
  SHA512:
6
- metadata.gz: 714c614f2cf44f332f4f9996638c10dca565c92a2279316509256fefba2ca824fc9c71d2a3c3e4ca28edfc45849f76b61e521158e0051c365f5b8a81f41e9d8d
7
- data.tar.gz: a3b1a7e58b6e5554a641aa4b8edf16dbb4983b2f7a072c5b7275ee04a7251e4d095fed37f82d2945ef759603c8aeaf8a394d9e7fcf9f70a723e6235ab5f3e646
6
+ metadata.gz: 1f0d4d73910f96ab8c6ae5b6b1bc58de547c2c32a7c689c2984778465dae29d2c2cae81c23699b3848d2eaef30223db66bcca6ccb421b9078d5616f497421d45
7
+ data.tar.gz: 3b9001966918da22bfe7b2960526ac0db471a75e79e7939b099bebe3dde1fb27ea69b1afbdec0fcc98a6cb4260b845b5551a1a07cec036207c885be4e14ef7b9
@@ -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: [ master ]
13
+ pull_request:
14
+ branches: [ master ]
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,50 @@
1
1
  ## Next Release
2
2
 
3
+ ## 7.0.3 (08/13/2022)
4
+
5
+ * Replace `Array()` with `Array.wrap()`
6
+
7
+ ## 7.0.2 (05/31/2022)
8
+
9
+ * Drop growl support
10
+ * Do not check html tag in Bullet::Rack anymore
11
+
12
+ ## 7.0.1 (01/15/2022)
13
+
14
+ * Get rid of *_whitelist methods
15
+ * Hack ActiveRecord::Associations::Preloader::Batch in rails 7
16
+
17
+ ## 7.0.0 (12/18/2021)
18
+
19
+ * Support rails 7
20
+ * Fix Mongoid 7 view iteration
21
+ * Move CI from Travis to Github Actions
22
+
23
+ ## 6.1.5 (08/16/2021)
24
+
25
+ * Rename whitelist to safelist
26
+ * Fix onload called twice
27
+ * Support Rack::Files::Iterator responses
28
+ * Ensure HABTM associations are not incorrectly labeled n+1
29
+
30
+ ## 6.1.4 (02/26/2021)
31
+
32
+ * Added an option to stop adding HTTP headers to API requests
33
+
34
+ ## 6.1.3 (01/21/2021)
35
+
36
+ * Consider ThroughAssociation at SingularAssociation like CollectionAssociation
37
+ * Add xhr_script only when add_footer is enabled
38
+
39
+ ## 6.1.2 (12/12/2020)
40
+
41
+ * Revert "Make whitelist thread safe"
42
+
43
+ ## 6.1.1 (12/12/2020)
44
+
45
+ * Add support Rails 6.1
46
+ * Make whitelist thread safe
47
+
3
48
  ## 6.1.0 (12/28/2019)
4
49
 
5
50
  * Add skip_html_injection flag
data/Gemfile.rails-6.0 CHANGED
@@ -2,7 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rails', '6.0.0'
5
+ gem 'rails', '~> 6.0.0'
6
6
  gem 'sqlite3'
7
7
  gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
8
  gem 'activerecord-import'
data/Gemfile.rails-6.1 ADDED
@@ -0,0 +1,15 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'rails', '~> 6.1.0'
6
+ gem 'sqlite3'
7
+ gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
+ gem 'activerecord-import'
9
+
10
+ gem "rspec"
11
+
12
+ platforms :rbx do
13
+ gem 'rubysl', '~> 2.0'
14
+ gem 'rubinius-developer_tools'
15
+ end
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/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
 
@@ -37,12 +37,19 @@ or add it into a Gemfile (Bundler):
37
37
  gem 'bullet', group: 'development'
38
38
  ```
39
39
 
40
+ enable the Bullet gem with generate command
41
+
42
+ ```ruby
43
+ bundle exec rails g bullet:install
44
+ ```
45
+ The generate command will auto generate the default configuration and may ask to include in the test environment as well. See below for custom configuration.
46
+
40
47
  **Note**: make sure `bullet` gem is added after activerecord (rails) and
41
48
  mongoid.
42
49
 
43
50
  ## Configuration
44
51
 
45
- 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
46
53
  `config/environments/development.rb` initializer with the following code:
47
54
 
48
55
  ```ruby
@@ -52,7 +59,6 @@ config.after_initialize do
52
59
  Bullet.alert = true
53
60
  Bullet.bullet_logger = true
54
61
  Bullet.console = true
55
- Bullet.growl = true
56
62
  Bullet.xmpp = { :account => 'bullets_account@jabber.org',
57
63
  :password => 'bullets_password_for_jabber',
58
64
  :receiver => 'your_account@jabber.org',
@@ -60,6 +66,7 @@ config.after_initialize do
60
66
  Bullet.rails_logger = true
61
67
  Bullet.honeybadger = true
62
68
  Bullet.bugsnag = true
69
+ Bullet.appsignal = true
63
70
  Bullet.airbrake = true
64
71
  Bullet.rollbar = true
65
72
  Bullet.add_footer = true
@@ -77,16 +84,17 @@ The code above will enable all of the Bullet notification systems:
77
84
  * `Bullet.alert`: pop up a JavaScript alert in the browser
78
85
  * `Bullet.bullet_logger`: log to the Bullet log file (Rails.root/log/bullet.log)
79
86
  * `Bullet.console`: log warnings to your browser's console.log (Safari/Webkit browsers or Firefox w/Firebug installed)
80
- * `Bullet.growl`: pop up Growl warnings if your system has Growl installed. Requires a little bit of configuration
81
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.
82
88
  * `Bullet.rails_logger`: add warnings directly to the Rails log
83
89
  * `Bullet.honeybadger`: add notifications to Honeybadger
84
90
  * `Bullet.bugsnag`: add notifications to bugsnag
85
91
  * `Bullet.airbrake`: add notifications to airbrake
92
+ * `Bullet.appsignal`: add notifications to AppSignal
86
93
  * `Bullet.rollbar`: add notifications to rollbar
87
94
  * `Bullet.sentry`: add notifications to sentry
88
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.
89
- * `Bullet.skip_html_injection`: prevents Bullet from injecting XHR into the returned HTML. This must be false for receiving alerts or console logging.
96
+ * `Bullet.skip_html_injection`: prevents Bullet from injecting code into the returned HTML. This must be false for receiving alerts, showing the footer or console logging.
97
+ * `Bullet.skip_http_headers`: don't add headers to API requests, and remove the javascript that relies on them. Note that this prevents bullet from logging warnings to the browser console or updating the footer.
90
98
  * `Bullet.stacktrace_includes`: include paths with any of these substrings in the stack trace, even if they are not in your main app
91
99
  * `Bullet.stacktrace_excludes`: ignore paths with any of these substrings in the stack trace, even if they are not in your main app.
92
100
  Each item can be a string (match substring), a regex, or an array where the first item is a path to match, and the second
@@ -111,15 +119,15 @@ Bullet.unused_eager_loading_enable = false
111
119
  Bullet.counter_cache_enable = false
112
120
  ```
113
121
 
114
- ## Whitelist
122
+ ## Safe list
115
123
 
116
124
  Sometimes Bullet may notify you of query problems you don't care to fix, or
117
- 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:
118
126
 
119
127
  ```ruby
120
- Bullet.add_whitelist :type => :n_plus_one_query, :class_name => "Post", :association => :comments
121
- Bullet.add_whitelist :type => :unused_eager_loading, :class_name => "Post", :association => :comments
122
- 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
123
131
  ```
124
132
 
125
133
  If you want to skip bullet in some specific controller actions, you can
@@ -146,25 +154,26 @@ The Bullet log `log/bullet.log` will look something like this:
146
154
  * N+1 Query:
147
155
 
148
156
  ```
149
- 2009-08-25 20:40:17[INFO] N+1 Query: PATH_INFO: /posts; model: Post => associations: [comments]·
150
- Add to your finder: :include => [:comments]
151
- 2009-08-25 20:40:17[INFO] N+1 Query: method call stack:·
152
- /Users/richard/Downloads/test/app/views/posts/index.html.erb:11:in `_run_erb_app47views47posts47index46html46erb'
153
- /Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `each'
154
- /Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `_run_erb_app47views47posts47index46html46erb'
155
- /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'
156
163
  ```
157
164
 
158
- 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.
159
166
 
160
167
  * Unused eager loading:
161
168
 
162
169
  ```
163
- 2009-08-25 20:53:56[INFO] Unused eager loadings: PATH_INFO: /posts; model: Post => associations: [comments]·
164
- 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
165
174
  ```
166
175
 
167
- These two lines are notifications that unused eager loadings have been encountered.
176
+ These lines are notifications that unused eager loadings have been encountered.
168
177
 
169
178
  * Need counter cache:
170
179
 
@@ -173,10 +182,14 @@ These two lines are notifications that unused eager loadings have been encounter
173
182
  Post => [:comments]
174
183
  ```
175
184
 
176
- ## Growl, XMPP/Jabber and Airbrake Support
185
+ ## XMPP/Jabber and Airbrake Support
177
186
 
178
187
  see [https://github.com/flyerhzm/uniform_notifier](https://github.com/flyerhzm/uniform_notifier)
179
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
+
180
193
  ## Important
181
194
 
182
195
  If you find Bullet does not work for you, *please disable your browser's cache*.
@@ -260,8 +273,7 @@ Bullet outputs some details info, to enable debug mode, set
260
273
  ## Demo
261
274
 
262
275
  Bullet is designed to function as you browse through your application in development. To see it in action,
263
- you can visit [https://github.com/flyerhzm/bullet_test](https://github.com/flyerhzm/bullet_test) or
264
- follow these steps to create, detect, and fix example query problems.
276
+ you can follow these steps to create, detect, and fix example query problems.
265
277
 
266
278
  1\. Create an example application
267
279
 
@@ -472,4 +484,4 @@ Meanwhile, there's a line appended to `log/bullet.log`
472
484
  Post => [:comments]
473
485
  ```
474
486
 
475
- Copyright (c) 2009 - 2019 Richard Huang (flyerhzm@gmail.com), released under the MIT license
487
+ Copyright (c) 2009 - 2022 Richard Huang (flyerhzm@gmail.com), released under the MIT license
@@ -3,7 +3,11 @@
3
3
  module Bullet
4
4
  module ActiveJob
5
5
  def self.included(base)
6
- base.class_eval { around_perform { |_job, block| Bullet.profile { block.call } } }
6
+ base.class_eval do
7
+ around_perform do |_job, block|
8
+ Bullet.profile { block.call }
9
+ end
10
+ end
7
11
  end
8
12
  end
9
13
  end
@@ -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
@@ -179,7 +179,7 @@ module Bullet
179
179
  refl = reflection.through_reflection
180
180
  Bullet::Detector::NPlusOneQuery.call_association(owner, refl.name)
181
181
  association = owner.association refl.name
182
- Array(association.target).each do |through_record|
182
+ Array.wrap(association.target).each do |through_record|
183
183
  Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
184
184
  end
185
185
 
@@ -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
@@ -158,7 +152,7 @@ module Bullet
158
152
  if is_a? ::ActiveRecord::Associations::ThroughAssociation
159
153
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
160
154
  association = owner.association reflection.through_reflection.name
161
- Array(association.target).each do |through_record|
155
+ Array.wrap(association.target).each do |through_record|
162
156
  Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
163
157
  end
164
158
 
@@ -202,6 +196,17 @@ module Bullet
202
196
 
203
197
  if Bullet.start?
204
198
  if owner.class.name !~ /^HABTM_/ && !@inversed
199
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
200
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
201
+ association = owner.association reflection.through_reflection.name
202
+ Array.wrap(association.target).each do |through_record|
203
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
204
+ end
205
+
206
+ if reflection.through_reflection != through_reflection
207
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
208
+ end
209
+ end
205
210
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
206
211
 
207
212
  if Bullet::Detector::NPlusOneQuery.impossible?(owner)
@@ -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
@@ -185,7 +179,7 @@ module Bullet
185
179
  if is_a? ::ActiveRecord::Associations::ThroughAssociation
186
180
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
187
181
  association = owner.association(reflection.through_reflection.name)
188
- Array(association.target).each do |through_record|
182
+ Array.wrap(association.target).each do |through_record|
189
183
  Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
190
184
  end
191
185
 
@@ -229,6 +223,17 @@ module Bullet
229
223
 
230
224
  if Bullet.start?
231
225
  if owner.class.name !~ /^HABTM_/ && !@inversed
226
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
227
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
228
+ association = owner.association(reflection.through_reflection.name)
229
+ Array.wrap(association.target).each do |through_record|
230
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
231
+ end
232
+
233
+ if reflection.through_reflection != through_reflection
234
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
235
+ end
236
+ end
232
237
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
233
238
 
234
239
  if Bullet::Detector::NPlusOneQuery.impossible?(owner)