bullet 6.1.0 → 7.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +101 -2
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +48 -30
  5. data/lib/bullet/active_job.rb +5 -1
  6. data/lib/bullet/active_record4.rb +21 -6
  7. data/lib/bullet/active_record41.rb +22 -6
  8. data/lib/bullet/active_record42.rb +33 -12
  9. data/lib/bullet/active_record5.rb +50 -20
  10. data/lib/bullet/active_record52.rb +71 -36
  11. data/lib/bullet/active_record60.rb +70 -35
  12. data/lib/bullet/active_record61.rb +302 -0
  13. data/lib/bullet/active_record70.rb +318 -0
  14. data/lib/bullet/active_record71.rb +318 -0
  15. data/lib/bullet/bullet_xhr.js +18 -17
  16. data/lib/bullet/dependency.rb +28 -0
  17. data/lib/bullet/detector/association.rb +8 -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 +21 -13
  21. data/lib/bullet/detector/unused_eager_loading.rb +6 -3
  22. data/lib/bullet/ext/object.rb +14 -3
  23. data/lib/bullet/mongoid4x.rb +1 -1
  24. data/lib/bullet/mongoid5x.rb +1 -1
  25. data/lib/bullet/mongoid6x.rb +1 -1
  26. data/lib/bullet/mongoid7x.rb +32 -17
  27. data/lib/bullet/mongoid8x.rb +59 -0
  28. data/lib/bullet/notification/base.rb +22 -10
  29. data/lib/bullet/notification/counter_cache.rb +1 -1
  30. data/lib/bullet/notification.rb +2 -1
  31. data/lib/bullet/rack.rb +67 -24
  32. data/lib/bullet/registry/association.rb +2 -1
  33. data/lib/bullet/registry/call_stack.rb +12 -0
  34. data/lib/bullet/registry.rb +1 -0
  35. data/lib/bullet/stack_trace_filter.rb +16 -15
  36. data/lib/bullet/version.rb +1 -1
  37. data/lib/bullet.rb +45 -30
  38. data/lib/generators/bullet/install_generator.rb +22 -25
  39. metadata +13 -148
  40. data/.gitignore +0 -15
  41. data/.rspec +0 -2
  42. data/.travis.yml +0 -33
  43. data/Gemfile +0 -24
  44. data/Gemfile.mongoid +0 -12
  45. data/Gemfile.mongoid-4.0 +0 -15
  46. data/Gemfile.mongoid-5.0 +0 -15
  47. data/Gemfile.mongoid-6.0 +0 -15
  48. data/Gemfile.mongoid-7.0 +0 -15
  49. data/Gemfile.rails-4.0 +0 -16
  50. data/Gemfile.rails-4.1 +0 -16
  51. data/Gemfile.rails-4.2 +0 -16
  52. data/Gemfile.rails-5.0 +0 -15
  53. data/Gemfile.rails-5.1 +0 -15
  54. data/Gemfile.rails-5.2 +0 -15
  55. data/Gemfile.rails-6.0 +0 -15
  56. data/Guardfile +0 -8
  57. data/Hacking.md +0 -75
  58. data/Rakefile +0 -51
  59. data/bullet.gemspec +0 -33
  60. data/perf/benchmark.rb +0 -115
  61. data/rails/init.rb +0 -3
  62. data/spec/bullet/detector/association_spec.rb +0 -28
  63. data/spec/bullet/detector/base_spec.rb +0 -10
  64. data/spec/bullet/detector/counter_cache_spec.rb +0 -58
  65. data/spec/bullet/detector/n_plus_one_query_spec.rb +0 -182
  66. data/spec/bullet/detector/unused_eager_loading_spec.rb +0 -121
  67. data/spec/bullet/ext/object_spec.rb +0 -44
  68. data/spec/bullet/ext/string_spec.rb +0 -15
  69. data/spec/bullet/notification/base_spec.rb +0 -94
  70. data/spec/bullet/notification/counter_cache_spec.rb +0 -14
  71. data/spec/bullet/notification/n_plus_one_query_spec.rb +0 -31
  72. data/spec/bullet/notification/unused_eager_loading_spec.rb +0 -18
  73. data/spec/bullet/notification_collector_spec.rb +0 -34
  74. data/spec/bullet/rack_spec.rb +0 -153
  75. data/spec/bullet/registry/association_spec.rb +0 -28
  76. data/spec/bullet/registry/base_spec.rb +0 -46
  77. data/spec/bullet/registry/object_spec.rb +0 -26
  78. data/spec/bullet_spec.rb +0 -136
  79. data/spec/integration/active_record/association_spec.rb +0 -739
  80. data/spec/integration/counter_cache_spec.rb +0 -68
  81. data/spec/integration/mongoid/association_spec.rb +0 -246
  82. data/spec/models/address.rb +0 -5
  83. data/spec/models/author.rb +0 -5
  84. data/spec/models/base_user.rb +0 -7
  85. data/spec/models/category.rb +0 -12
  86. data/spec/models/city.rb +0 -5
  87. data/spec/models/client.rb +0 -8
  88. data/spec/models/comment.rb +0 -8
  89. data/spec/models/company.rb +0 -5
  90. data/spec/models/country.rb +0 -5
  91. data/spec/models/document.rb +0 -7
  92. data/spec/models/entry.rb +0 -5
  93. data/spec/models/firm.rb +0 -7
  94. data/spec/models/folder.rb +0 -3
  95. data/spec/models/group.rb +0 -3
  96. data/spec/models/mongoid/address.rb +0 -9
  97. data/spec/models/mongoid/category.rb +0 -10
  98. data/spec/models/mongoid/comment.rb +0 -9
  99. data/spec/models/mongoid/company.rb +0 -9
  100. data/spec/models/mongoid/entry.rb +0 -9
  101. data/spec/models/mongoid/post.rb +0 -14
  102. data/spec/models/mongoid/user.rb +0 -7
  103. data/spec/models/newspaper.rb +0 -5
  104. data/spec/models/page.rb +0 -3
  105. data/spec/models/person.rb +0 -5
  106. data/spec/models/pet.rb +0 -5
  107. data/spec/models/post.rb +0 -32
  108. data/spec/models/relationship.rb +0 -6
  109. data/spec/models/reply.rb +0 -5
  110. data/spec/models/student.rb +0 -5
  111. data/spec/models/submission.rb +0 -6
  112. data/spec/models/teacher.rb +0 -5
  113. data/spec/models/user.rb +0 -6
  114. data/spec/models/writer.rb +0 -3
  115. data/spec/spec_helper.rb +0 -99
  116. data/spec/support/bullet_ext.rb +0 -56
  117. data/spec/support/mongo_seed.rb +0 -58
  118. data/spec/support/rack_double.rb +0 -49
  119. data/spec/support/sqlite_seed.rb +0 -246
  120. data/test.sh +0 -13
  121. data/update.sh +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd668fdfd675ea8437eee0242974d7cb1cfacc0479be4cc5f3f58ee7d05a6d63
4
- data.tar.gz: 3d841fb8471fe18b66135fa087ee3d1874f412c114d09ba05ba150e2daaf9775
3
+ metadata.gz: 752fed6e96f9ea5c1baefca2e9a80ea3aefa13c279d24688f3341d18c8334afc
4
+ data.tar.gz: 78edabbca2c71e9c6d2c793bd3786d5b826f4530664aa18e7cd42cb059a85373
5
5
  SHA512:
6
- metadata.gz: 714c614f2cf44f332f4f9996638c10dca565c92a2279316509256fefba2ca824fc9c71d2a3c3e4ca28edfc45849f76b61e521158e0051c365f5b8a81f41e9d8d
7
- data.tar.gz: a3b1a7e58b6e5554a641aa4b8edf16dbb4983b2f7a072c5b7275ee04a7251e4d095fed37f82d2945ef759603c8aeaf8a394d9e7fcf9f70a723e6235ab5f3e646
6
+ metadata.gz: 5cad943f3641de44f17da3fe2062ed83525d0bbb1d4321208f55b4d05d6a43840f25fe0728fa4e10f42701dd8ef4eaa2322ca9ab8c8d6a2e7d7067c70d0d3186
7
+ data.tar.gz: a58309dcbfaadbb9f67352c0a1730be8397c2522ef403093fcf58a8fdcc596b45e961d39b239e40412d46d98a50033ed02398efc70df4314631bd16e220a7c88
data/CHANGELOG.md CHANGED
@@ -1,5 +1,104 @@
1
1
  ## Next Release
2
2
 
3
+ ## 7.1.6 (01/16/2024)
4
+
5
+ * Allow apps to not include the user in a notification
6
+
7
+ ## 7.1.5 (01/05/2024)
8
+
9
+ * Fix mongoid8
10
+
11
+ ## 7.1.4 (11/17/2023)
12
+
13
+ * Call association also on through reflection
14
+
15
+ ## 7.1.3 (11/05/2023)
16
+
17
+ * Call NPlusOneQuery's call_association when calling count on collection association
18
+
19
+ ## 7.1.2 (10/13/2023)
20
+
21
+ * Handle Rails 7.1 composite primary keys
22
+
23
+ ## 7.1.1 (10/07/2023)
24
+
25
+ * Add support for `Content-Security-Policy-Report-Only` nonces
26
+ * Fix count method signature
27
+
28
+ ## 7.1.0 (10/06/2023)
29
+
30
+ * Support rails 7.1
31
+ * Alias `Bullet.enable?` to `enabled?`, and `Bullet.enable=` to `enabled=`
32
+ * Added `always_append_html_body` option, so the html snippet is always included even if there are no notifications
33
+ * Added detection of n+1 count queries from `count` method
34
+ * Changed the counter cache notification title to recommend using `size`
35
+
36
+ ## 7.0.7 (03/01/2023)
37
+
38
+ * Check `Rails.application.config.content_security_policy` before insert `Bullet::Rack`
39
+
40
+ ## 7.0.6 (03/01/2023)
41
+
42
+ * Better way to check if `ActionDispatch::ContentSecurityPolicy::Middleware` exists
43
+
44
+ ## 7.0.5 (01/01/2023)
45
+
46
+ * Fix n+1 false positives in AR 7.0
47
+ * Fix eager_load nested has_many :through false positives
48
+ * Respect Content-Security-Policy nonces
49
+ * Added CallStacks support for avoid eager loading
50
+ * Iterate fewer times over objects
51
+
52
+ ## 7.0.4 (11/28/2022)
53
+
54
+ * Fix `eager_load` `has_many :through` false positives
55
+ * mongoid7x: add dynamic methods
56
+
57
+ ## 7.0.3 (08/13/2022)
58
+
59
+ * Replace `Array()` with `Array.wrap()`
60
+
61
+ ## 7.0.2 (05/31/2022)
62
+
63
+ * Drop growl support
64
+ * Do not check html tag in Bullet::Rack anymore
65
+
66
+ ## 7.0.1 (01/15/2022)
67
+
68
+ * Get rid of *_whitelist methods
69
+ * Hack ActiveRecord::Associations::Preloader::Batch in rails 7
70
+
71
+ ## 7.0.0 (12/18/2021)
72
+
73
+ * Support rails 7
74
+ * Fix Mongoid 7 view iteration
75
+ * Move CI from Travis to Github Actions
76
+
77
+ ## 6.1.5 (08/16/2021)
78
+
79
+ * Rename whitelist to safelist
80
+ * Fix onload called twice
81
+ * Support Rack::Files::Iterator responses
82
+ * Ensure HABTM associations are not incorrectly labeled n+1
83
+
84
+ ## 6.1.4 (02/26/2021)
85
+
86
+ * Added an option to stop adding HTTP headers to API requests
87
+
88
+ ## 6.1.3 (01/21/2021)
89
+
90
+ * Consider ThroughAssociation at SingularAssociation like CollectionAssociation
91
+ * Add xhr_script only when add_footer is enabled
92
+
93
+ ## 6.1.2 (12/12/2020)
94
+
95
+ * Revert "Make whitelist thread safe"
96
+
97
+ ## 6.1.1 (12/12/2020)
98
+
99
+ * Add support Rails 6.1
100
+ * Make whitelist thread safe
101
+
3
102
  ## 6.1.0 (12/28/2019)
4
103
 
5
104
  * Add skip_html_injection flag
@@ -31,7 +130,7 @@
31
130
 
32
131
  * Fix through reflection for rails 5.x
33
132
  * Fix false positive in after_save/after_create callbacks
34
- * Don't triger a preload error on "manual" preloads
133
+ * Don't trigger a preload error on "manual" preloads
35
134
  * Avoid Bullet from making extra queries in mongoid6
36
135
  * Support option for #first and #last on mongoid6.x
37
136
  * Fix duplicate logs in mongoid 4.x and 5.x version
@@ -173,7 +272,7 @@
173
272
 
174
273
  ## 4.5.0 (03/24/2013)
175
274
 
176
- * Add api way to access captured associatioin
275
+ * Add api way to access captured association
177
276
  * Allow disable n_plus_one_query, unused_eager_loading and counter_cache respectively
178
277
  * Add whitelist
179
278
 
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
 
@@ -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,22 +84,25 @@ 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
93
101
  item is a line number, a Range of line numbers, or a (bare) method name, to exclude only particular lines in a file.
94
102
  * `Bullet.slack`: add notifications to slack
95
103
  * `Bullet.raise`: raise errors, useful for making your specs fail unless they have optimized queries
104
+ * `Bullet.always_append_html_body`: always append the html body even if no notifications are present. Note: `console` or `add_footer` must also be true. Useful for Single Page Applications where the initial page load might not have any notifications present.
105
+ * `Bullet.skip_user_in_notification`: exclude the OS user (`whoami`) from notifications.
96
106
 
97
107
 
98
108
  Bullet also allows you to disable any of its detectors.
@@ -111,15 +121,17 @@ Bullet.unused_eager_loading_enable = false
111
121
  Bullet.counter_cache_enable = false
112
122
  ```
113
123
 
114
- ## Whitelist
124
+ Note: When calling `Bullet.enable`, all other detectors are reset to their defaults (`true`) and need reconfiguring.
125
+
126
+ ## Safe list
115
127
 
116
128
  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:
129
+ which come from outside your code. You can add them to a safe list to ignore them:
118
130
 
119
131
  ```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
132
+ Bullet.add_safelist :type => :n_plus_one_query, :class_name => "Post", :association => :comments
133
+ Bullet.add_safelist :type => :unused_eager_loading, :class_name => "Post", :association => :comments
134
+ Bullet.add_safelist :type => :counter_cache, :class_name => "Country", :association => :cities
123
135
  ```
124
136
 
125
137
  If you want to skip bullet in some specific controller actions, you can
@@ -146,25 +158,26 @@ The Bullet log `log/bullet.log` will look something like this:
146
158
  * N+1 Query:
147
159
 
148
160
  ```
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'
161
+ 2009-08-25 20:40:17[INFO] USE eager loading detected:
162
+ Post => [:comments]·
163
+ Add to your query: .includes([:comments])
164
+ 2009-08-25 20:40:17[INFO] Call stack
165
+ /Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `each'
166
+ /Users/richard/Downloads/test/app/controllers/posts_controller.rb:7:in `index'
156
167
  ```
157
168
 
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.
169
+ 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
170
 
160
171
  * Unused eager loading:
161
172
 
162
173
  ```
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]
174
+ 2009-08-25 20:53:56[INFO] AVOID eager loading detected
175
+ Post => [:comments]·
176
+ Remove from your query: .includes([:comments])
177
+ 2009-08-25 20:53:56[INFO] Call stack
165
178
  ```
166
179
 
167
- These two lines are notifications that unused eager loadings have been encountered.
180
+ These lines are notifications that unused eager loadings have been encountered.
168
181
 
169
182
  * Need counter cache:
170
183
 
@@ -173,10 +186,14 @@ These two lines are notifications that unused eager loadings have been encounter
173
186
  Post => [:comments]
174
187
  ```
175
188
 
176
- ## Growl, XMPP/Jabber and Airbrake Support
189
+ ## XMPP/Jabber and Airbrake Support
177
190
 
178
191
  see [https://github.com/flyerhzm/uniform_notifier](https://github.com/flyerhzm/uniform_notifier)
179
192
 
193
+ ## Growl Support
194
+
195
+ Growl support is dropped from uniform_notifier 1.16.0, if you still want it, please use uniform_notifier 1.15.0.
196
+
180
197
  ## Important
181
198
 
182
199
  If you find Bullet does not work for you, *please disable your browser's cache*.
@@ -209,7 +226,7 @@ end
209
226
 
210
227
  ### Work with sinatra
211
228
 
212
- Configure and use `Bullet::Rack`
229
+ Configure and use `Bullet::Rack`.
213
230
 
214
231
  ```ruby
215
232
  configure :development do
@@ -219,6 +236,8 @@ configure :development do
219
236
  end
220
237
  ```
221
238
 
239
+ If your application generates a Content-Security-Policy via a separate middleware, ensure that `Bullet::Rack` is loaded _before_ that middleware.
240
+
222
241
  ### Run in tests
223
242
 
224
243
  First you need to enable Bullet in test environment.
@@ -260,8 +279,7 @@ Bullet outputs some details info, to enable debug mode, set
260
279
  ## Demo
261
280
 
262
281
  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.
282
+ you can follow these steps to create, detect, and fix example query problems.
265
283
 
266
284
  1\. Create an example application
267
285
 
@@ -270,17 +288,17 @@ $ rails new test_bullet
270
288
  $ cd test_bullet
271
289
  $ rails g scaffold post name:string
272
290
  $ rails g scaffold comment name:string post_id:integer
273
- $ bundle exec rake db:migrate
291
+ $ bundle exec rails db:migrate
274
292
  ```
275
293
 
276
- 2\. Change `app/model/post.rb` and `app/model/comment.rb`
294
+ 2\. Change `app/models/post.rb` and `app/models/comment.rb`
277
295
 
278
296
  ```ruby
279
- class Post < ActiveRecord::Base
297
+ class Post < ApplicationRecord
280
298
  has_many :comments
281
299
  end
282
300
 
283
- class Comment < ActiveRecord::Base
301
+ class Comment < ApplicationRecord
284
302
  belongs_to :post
285
303
  end
286
304
  ```
@@ -472,4 +490,4 @@ Meanwhile, there's a line appended to `log/bullet.log`
472
490
  Post => [:comments]
473
491
  ```
474
492
 
475
- Copyright (c) 2009 - 2019 Richard Huang (flyerhzm@gmail.com), released under the MIT license
493
+ 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
@@ -107,12 +107,17 @@ module Bullet
107
107
  result = origin_construct_association(record, join, row)
108
108
 
109
109
  if Bullet.start?
110
- associations = join.reflection.name
111
- Bullet::Detector::Association.add_object_associations(record, associations)
112
- Bullet::Detector::NPlusOneQuery.call_association(record, associations)
113
- @bullet_eager_loadings[record.class] ||= {}
114
- @bullet_eager_loadings[record.class][record] ||= Set.new
115
- @bullet_eager_loadings[record.class][record] << associations
110
+ associations = [join.reflection.name]
111
+ if join.reflection.nested?
112
+ associations << join.reflection.through_reflection.name
113
+ end
114
+ associations.each do |association|
115
+ Bullet::Detector::Association.add_object_associations(record, association)
116
+ Bullet::Detector::NPlusOneQuery.call_association(record, association)
117
+ @bullet_eager_loadings[record.class] ||= {}
118
+ @bullet_eager_loadings[record.class][record] ||= Set.new
119
+ @bullet_eager_loadings[record.class][record] << association
120
+ end
116
121
  end
117
122
 
118
123
  result
@@ -176,6 +181,16 @@ module Bullet
176
181
  result
177
182
  end
178
183
  end
184
+
185
+ ::ActiveRecord::Associations::CollectionProxy.class_eval do
186
+ def count(column_name = nil, options = {})
187
+ if Bullet.start?
188
+ Bullet::Detector::CounterCache.add_counter_cache(proxy_association.owner, proxy_association.reflection.name)
189
+ Bullet::Detector::NPlusOneQuery.call_association(proxy_association.owner, proxy_association.reflection.name)
190
+ end
191
+ super(column_name, options)
192
+ end
193
+ end
179
194
  end
180
195
  end
181
196
  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
@@ -109,12 +110,17 @@ module Bullet
109
110
  result = origin_construct_model(record, node, row, model_cache, id, aliases)
110
111
 
111
112
  if Bullet.start?
112
- associations = node.reflection.name
113
- Bullet::Detector::Association.add_object_associations(record, associations)
114
- Bullet::Detector::NPlusOneQuery.call_association(record, associations)
115
- @bullet_eager_loadings[record.class] ||= {}
116
- @bullet_eager_loadings[record.class][record] ||= Set.new
117
- @bullet_eager_loadings[record.class][record] << associations
113
+ associations = [node.reflection.name]
114
+ if node.reflection.nested?
115
+ associations << node.reflection.through_reflection.name
116
+ end
117
+ associations.each do |association|
118
+ Bullet::Detector::Association.add_object_associations(record, association)
119
+ Bullet::Detector::NPlusOneQuery.call_association(record, association)
120
+ @bullet_eager_loadings[record.class] ||= {}
121
+ @bullet_eager_loadings[record.class][record] ||= Set.new
122
+ @bullet_eager_loadings[record.class][record] << association
123
+ end
118
124
  end
119
125
 
120
126
  result
@@ -167,6 +173,16 @@ module Bullet
167
173
  origin_count_records
168
174
  end
169
175
  end
176
+
177
+ ::ActiveRecord::Associations::CollectionProxy.class_eval do
178
+ def count(column_name = nil, options = {})
179
+ if Bullet.start?
180
+ Bullet::Detector::CounterCache.add_counter_cache(proxy_association.owner, proxy_association.reflection.name)
181
+ Bullet::Detector::NPlusOneQuery.call_association(proxy_association.owner, proxy_association.reflection.name)
182
+ end
183
+ super(column_name, options)
184
+ end
185
+ end
170
186
  end
171
187
  end
172
188
  end
@@ -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
@@ -128,12 +129,17 @@ module Bullet
128
129
  id = row[key]
129
130
  next unless id.nil?
130
131
 
131
- associations = node.reflection.name
132
- Bullet::Detector::Association.add_object_associations(ar_parent, associations)
133
- Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
134
- @bullet_eager_loadings[ar_parent.class] ||= {}
135
- @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
136
- @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
132
+ associations = [node.reflection.name]
133
+ if node.reflection.nested?
134
+ associations << node.reflection.through_reflection.name
135
+ end
136
+ associations.each do |association|
137
+ Bullet::Detector::Association.add_object_associations(ar_parent, association)
138
+ Bullet::Detector::NPlusOneQuery.call_association(ar_parent, association)
139
+ @bullet_eager_loadings[ar_parent.class] ||= {}
140
+ @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
141
+ @bullet_eager_loadings[ar_parent.class][ar_parent] << association
142
+ end
137
143
  end
138
144
  end
139
145
  end
@@ -146,12 +152,17 @@ module Bullet
146
152
  result = origin_construct_model(record, node, row, model_cache, id, aliases)
147
153
 
148
154
  if Bullet.start?
149
- associations = node.reflection.name
150
- Bullet::Detector::Association.add_object_associations(record, associations)
151
- Bullet::Detector::NPlusOneQuery.call_association(record, associations)
152
- @bullet_eager_loadings[record.class] ||= {}
153
- @bullet_eager_loadings[record.class][record] ||= Set.new
154
- @bullet_eager_loadings[record.class][record] << associations
155
+ associations = [node.reflection.name]
156
+ if node.reflection.nested?
157
+ associations << node.reflection.through_reflection.name
158
+ end
159
+ associations.each do |association|
160
+ Bullet::Detector::Association.add_object_associations(record, association)
161
+ Bullet::Detector::NPlusOneQuery.call_association(record, association)
162
+ @bullet_eager_loadings[record.class] ||= {}
163
+ @bullet_eager_loadings[record.class][record] ||= Set.new
164
+ @bullet_eager_loadings[record.class][record] << association
165
+ end
155
166
  end
156
167
 
157
168
  result
@@ -232,6 +243,16 @@ module Bullet
232
243
  origin_count_records
233
244
  end
234
245
  end
246
+
247
+ ::ActiveRecord::Associations::CollectionProxy.class_eval do
248
+ def count(column_name = nil, options = {})
249
+ if Bullet.start?
250
+ Bullet::Detector::CounterCache.add_counter_cache(proxy_association.owner, proxy_association.reflection.name)
251
+ Bullet::Detector::NPlusOneQuery.call_association(proxy_association.owner, proxy_association.reflection.name)
252
+ end
253
+ super(column_name, options)
254
+ end
255
+ end
235
256
  end
236
257
  end
237
258
  end
@@ -138,12 +138,17 @@ module Bullet
138
138
  id = row[key]
139
139
  next unless id.nil?
140
140
 
141
- associations = node.reflection.name
142
- Bullet::Detector::Association.add_object_associations(ar_parent, associations)
143
- Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
144
- @bullet_eager_loadings[ar_parent.class] ||= {}
145
- @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
146
- @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
141
+ associations = [node.reflection.name]
142
+ if node.reflection.through_reflection?
143
+ associations << node.reflection.through_reflection.name
144
+ end
145
+ associations.each do |association|
146
+ Bullet::Detector::Association.add_object_associations(ar_parent, association)
147
+ Bullet::Detector::NPlusOneQuery.call_association(ar_parent, association)
148
+ @bullet_eager_loadings[ar_parent.class] ||= {}
149
+ @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
150
+ @bullet_eager_loadings[ar_parent.class][ar_parent] << association
151
+ end
147
152
  end
148
153
  end
149
154
  end
@@ -156,12 +161,17 @@ module Bullet
156
161
  result = super
157
162
 
158
163
  if Bullet.start?
159
- associations = node.reflection.name
160
- Bullet::Detector::Association.add_object_associations(record, associations)
161
- Bullet::Detector::NPlusOneQuery.call_association(record, associations)
162
- @bullet_eager_loadings[record.class] ||= {}
163
- @bullet_eager_loadings[record.class][record] ||= Set.new
164
- @bullet_eager_loadings[record.class][record] << associations
164
+ associations = [node.reflection.name]
165
+ if node.reflection.through_reflection?
166
+ associations << node.reflection.through_reflection.name
167
+ end
168
+ associations.each do |association|
169
+ Bullet::Detector::Association.add_object_associations(record, association)
170
+ Bullet::Detector::NPlusOneQuery.call_association(record, association)
171
+ @bullet_eager_loadings[record.class] ||= {}
172
+ @bullet_eager_loadings[record.class][record] ||= Set.new
173
+ @bullet_eager_loadings[record.class][record] << association
174
+ end
165
175
  end
166
176
 
167
177
  result
@@ -177,16 +187,18 @@ module Bullet
177
187
  if Bullet.start?
178
188
  if is_a? ::ActiveRecord::Associations::ThroughAssociation
179
189
  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
190
+ association = owner.association(refl.name)
191
+ if association.loaded?
192
+ Bullet::Detector::NPlusOneQuery.call_association(owner, refl.name)
193
+ Array.wrap(association.target).each do |through_record|
194
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
195
+ end
185
196
 
186
- if refl.through_reflection?
187
- refl = refl.through_reflection while refl.through_reflection?
197
+ if refl.through_reflection?
198
+ refl = refl.through_reflection while refl.through_reflection?
188
199
 
189
- Bullet::Detector::NPlusOneQuery.call_association(owner, refl.name)
200
+ Bullet::Detector::NPlusOneQuery.call_association(owner, refl.name)
201
+ end
190
202
  end
191
203
  end
192
204
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
@@ -258,6 +270,24 @@ module Bullet
258
270
  end
259
271
  end
260
272
  )
273
+
274
+ ::ActiveRecord::Associations::CollectionProxy.prepend(
275
+ Module.new do
276
+ def count
277
+ if Bullet.start? && !proxy_association.is_a?(::ActiveRecord::Associations::ThroughAssociation)
278
+ Bullet::Detector::CounterCache.add_counter_cache(
279
+ proxy_association.owner,
280
+ proxy_association.reflection.name
281
+ )
282
+ Bullet::Detector::NPlusOneQuery.call_association(
283
+ proxy_association.owner,
284
+ proxy_association.reflection.name
285
+ )
286
+ end
287
+ super
288
+ end
289
+ end
290
+ )
261
291
  end
262
292
  end
263
293
  end