bullet 4.13.1 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +7 -11
- data/CHANGELOG.md +62 -3
- data/Gemfile.mongoid +0 -2
- data/Gemfile.mongoid-4.0 +1 -5
- data/{Gemfile.mongoid-2.8 → Gemfile.mongoid-5.0} +2 -6
- data/{Gemfile.mongoid-2.5 → Gemfile.mongoid-6.0} +2 -6
- data/Gemfile.rails-4.0 +2 -5
- data/Gemfile.rails-4.1 +2 -5
- data/{Gemfile.rails-3.0 → Gemfile.rails-4.2} +3 -6
- data/{Gemfile.rails-3.1 → Gemfile.rails-5.0} +2 -6
- data/Hacking.md +1 -0
- data/README.md +39 -7
- data/bullet.gemspec +1 -1
- data/lib/bullet/active_record4.rb +109 -33
- data/lib/bullet/active_record41.rb +99 -37
- data/lib/bullet/active_record42.rb +251 -0
- data/lib/bullet/active_record5.rb +243 -0
- data/lib/bullet/dependency.rb +34 -28
- data/lib/bullet/detector/association.rb +16 -16
- data/lib/bullet/detector/counter_cache.rb +13 -13
- data/lib/bullet/detector/n_plus_one_query.rb +30 -35
- data/lib/bullet/detector/unused_eager_loading.rb +12 -11
- data/lib/bullet/ext/object.rb +4 -2
- data/lib/bullet/{mongoid3x.rb → mongoid5x.rb} +3 -3
- data/lib/bullet/{mongoid2x.rb → mongoid6x.rb} +12 -12
- data/lib/bullet/notification/base.rb +9 -13
- data/lib/bullet/notification/n_plus_one_query.rb +8 -6
- data/lib/bullet/notification/unused_eager_loading.rb +18 -1
- data/lib/bullet/rack.rb +28 -16
- data/lib/bullet/stack_trace_filter.rb +34 -0
- data/lib/bullet/version.rb +1 -1
- data/lib/bullet.rb +48 -14
- data/spec/bullet/detector/counter_cache_spec.rb +8 -8
- data/spec/bullet/detector/n_plus_one_query_spec.rb +46 -31
- data/spec/bullet/detector/unused_eager_loading_spec.rb +17 -3
- data/spec/bullet/ext/object_spec.rb +6 -0
- data/spec/bullet/notification/base_spec.rb +4 -29
- data/spec/bullet/notification/n_plus_one_query_spec.rb +4 -4
- data/spec/bullet/notification/unused_eager_loading_spec.rb +3 -3
- data/spec/bullet/rack_spec.rb +38 -7
- data/spec/bullet_spec.rb +93 -0
- data/spec/integration/active_record4/association_spec.rb +112 -4
- data/spec/integration/{active_record3 → active_record5}/association_spec.rb +121 -15
- data/spec/integration/counter_cache_spec.rb +24 -1
- data/spec/models/category.rb +4 -1
- data/spec/models/comment.rb +2 -0
- data/spec/models/post.rb +7 -2
- data/spec/models/reply.rb +3 -0
- data/spec/models/submission.rb +1 -1
- data/spec/spec_helper.rb +3 -5
- data/spec/support/mongo_seed.rb +13 -0
- data/spec/support/sqlite_seed.rb +15 -6
- data/test.sh +4 -10
- data/update.sh +7 -0
- metadata +21 -23
- data/Gemfile.mongoid-2.4 +0 -19
- data/Gemfile.mongoid-2.6 +0 -19
- data/Gemfile.mongoid-2.7 +0 -19
- data/Gemfile.mongoid-3.0 +0 -19
- data/Gemfile.mongoid-3.1 +0 -19
- data/Gemfile.rails-3.2 +0 -19
- data/lib/bullet/active_record3.rb +0 -154
- data/lib/bullet/active_record3x.rb +0 -134
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7f8cac0418ea4a1bb9f8abc6c12e84022f2f118d
|
|
4
|
+
data.tar.gz: ebdf99115037dec22a5480a63d3bed5f15956dda
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0904f996964a403a495af310bab8a0612e614eb72a71d08646948508c25d4bf36d2b939da65156a5fc81595477820e0f123aa2a88d618fe97e28e81a6b6f8ada
|
|
7
|
+
data.tar.gz: d491e12ccce38a26a5e3d27d3f6b84acafe34bb7b6aff56b78a8e22a73a5a3abf88443110587de0080e50b32ef70d8ca387eab5faac0b1bec16efed04bf3acc1
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
|
+
sudo: false
|
|
1
2
|
language: ruby
|
|
2
3
|
rvm:
|
|
3
|
-
- 2.
|
|
4
|
+
- 2.3.0
|
|
4
5
|
gemfile:
|
|
6
|
+
- Gemfile.rails-5.0
|
|
7
|
+
- Gemfile.rails-4.2
|
|
5
8
|
- Gemfile.rails-4.1
|
|
6
9
|
- Gemfile.rails-4.0
|
|
7
|
-
- Gemfile.
|
|
8
|
-
- Gemfile.
|
|
9
|
-
- Gemfile.
|
|
10
|
-
- Gemfile.mongoid-3.1
|
|
11
|
-
- Gemfile.mongoid-3.0
|
|
12
|
-
- Gemfile.mongoid-2.8
|
|
13
|
-
- Gemfile.mongoid-2.7
|
|
14
|
-
- Gemfile.mongoid-2.6
|
|
15
|
-
- Gemfile.mongoid-2.5
|
|
16
|
-
- Gemfile.mongoid-2.4
|
|
10
|
+
- Gemfile.mongoid-6.0
|
|
11
|
+
- Gemfile.mongoid-5.0
|
|
12
|
+
- Gemfile.mongoid-4.0
|
|
17
13
|
env:
|
|
18
14
|
- DB=sqlite
|
|
19
15
|
services:
|
data/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,67 @@
|
|
|
1
1
|
# Next Release
|
|
2
2
|
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
*
|
|
3
|
+
## 5.5.0 (12/30/2016)
|
|
4
|
+
|
|
5
|
+
* Display http request method #311
|
|
6
|
+
* Add close button to footer
|
|
7
|
+
* Raise an error if bullet does not support AR or Mongoid
|
|
8
|
+
* Avoid double backtrace
|
|
9
|
+
* Fix false alert on counter cache when associations are already loaded #288
|
|
10
|
+
* Fix "false alert" in rails 5 #239
|
|
11
|
+
* Do not support ActiveRecord 3.x and Mongoid 3.x / 4.x anymore
|
|
12
|
+
|
|
13
|
+
## 5.4.0 (10/09/2016)
|
|
14
|
+
|
|
15
|
+
* Support rails 5.1
|
|
16
|
+
* Extract stack trace filtering into module
|
|
17
|
+
|
|
18
|
+
## 5.3.0 (15/08/2016)
|
|
19
|
+
|
|
20
|
+
* Fix false alert on through association with join sql #301
|
|
21
|
+
* Fix association.target in `through_association` can be singular #302
|
|
22
|
+
* Support `find_by_sql` #303
|
|
23
|
+
* Fix env `REQUEST_URI`
|
|
24
|
+
|
|
25
|
+
## 5.2.0 (07/26/2016)
|
|
26
|
+
|
|
27
|
+
* Fix `has_cached_counter?` is not defined in HABTM #297
|
|
28
|
+
* Fix false alert if preloaded association has no records #260
|
|
29
|
+
* Support Rails 5.0.0
|
|
30
|
+
|
|
31
|
+
## 5.1.0 (05/21/2016)
|
|
32
|
+
|
|
33
|
+
* Fix false alert when `empty?` used with `counter_cache`
|
|
34
|
+
* Fix `alias_method_chain` deprecation for rails 5
|
|
35
|
+
* Add response handling for non-Rails Rack responses
|
|
36
|
+
* Fix false alert when querying immediately after creation
|
|
37
|
+
* Fix UnusedEagerLoading bug when multiple eager loading query include same objects
|
|
38
|
+
|
|
39
|
+
## 5.0.0 (01/06/2016)
|
|
40
|
+
|
|
41
|
+
* Support Rails 5.0.0.beta1
|
|
42
|
+
* Fix `has_many :through` infinite loop issue
|
|
43
|
+
* Support mongoid 5.0.0
|
|
44
|
+
* Do not report association queries immediately after object creation to
|
|
45
|
+
require a preload
|
|
46
|
+
* Detect `counter_cache` for `has_many :through` association
|
|
47
|
+
* Compatible with `composite_primary_keys` gem
|
|
48
|
+
* Fix AR 4.2 SingularAssociation#reader result can be nil
|
|
49
|
+
* `perform_out_of_channel_notifications` should always be triggered
|
|
50
|
+
* Fix false positive with `belongs_to` -> `belongs_to` for active\_record 4.2
|
|
51
|
+
* Activate active\_record hacks only when Bullet already start
|
|
52
|
+
* Don't execute query when running `to_sql`
|
|
53
|
+
* Send backtrace to `uniform_notifier`
|
|
54
|
+
* Fix sse response check
|
|
55
|
+
* Dynamically delegate available notifiers to UniformNotifier
|
|
56
|
+
* Hotfix nil object when `add_impossible_object`
|
|
57
|
+
* Fix `has_one` then `has_many` associations in rails 4.2
|
|
58
|
+
* Append js and dom to html body in proper position
|
|
59
|
+
|
|
60
|
+
## 4.14.0 (10/03/2014)
|
|
61
|
+
|
|
62
|
+
* Support rails 4.2
|
|
63
|
+
* Polish notification output
|
|
64
|
+
* Fix warning: `*' interpreted as argument prefix
|
|
6
65
|
|
|
7
66
|
## 4.13.0 (07/19/2014)
|
|
8
67
|
|
data/Gemfile.mongoid
CHANGED
data/Gemfile.mongoid-4.0
CHANGED
|
@@ -2,16 +2,12 @@ source "https://rubygems.org"
|
|
|
2
2
|
|
|
3
3
|
gemspec
|
|
4
4
|
|
|
5
|
-
gem 'rails', '~> 4.0.
|
|
5
|
+
gem 'rails', '~> 4.0.0'
|
|
6
6
|
gem 'sqlite3', platforms: [:ruby]
|
|
7
7
|
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
|
|
8
8
|
gem 'mongoid', '~> 4.0.0'
|
|
9
9
|
|
|
10
10
|
gem "rspec"
|
|
11
|
-
gem "guard"
|
|
12
|
-
gem "guard-rspec"
|
|
13
|
-
|
|
14
|
-
gem 'coveralls', require: false
|
|
15
11
|
|
|
16
12
|
platforms :rbx do
|
|
17
13
|
gem 'rubysl', '~> 2.0'
|
|
@@ -2,16 +2,12 @@ source "https://rubygems.org"
|
|
|
2
2
|
|
|
3
3
|
gemspec
|
|
4
4
|
|
|
5
|
-
gem 'rails', '~>
|
|
5
|
+
gem 'rails', '~> 4.0.0'
|
|
6
6
|
gem 'sqlite3', platforms: [:ruby]
|
|
7
7
|
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
|
|
8
|
-
gem 'mongoid', '~>
|
|
8
|
+
gem 'mongoid', '~> 5.1.0'
|
|
9
9
|
|
|
10
10
|
gem "rspec"
|
|
11
|
-
gem "guard"
|
|
12
|
-
gem "guard-rspec"
|
|
13
|
-
|
|
14
|
-
gem 'coveralls', require: false
|
|
15
11
|
|
|
16
12
|
platforms :rbx do
|
|
17
13
|
gem 'rubysl', '~> 2.0'
|
|
@@ -2,16 +2,12 @@ source "https://rubygems.org"
|
|
|
2
2
|
|
|
3
3
|
gemspec
|
|
4
4
|
|
|
5
|
-
gem 'rails', '~>
|
|
5
|
+
gem 'rails', '~> 5.0.0'
|
|
6
6
|
gem 'sqlite3', platforms: [:ruby]
|
|
7
7
|
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
|
|
8
|
-
gem 'mongoid', '~>
|
|
8
|
+
gem 'mongoid', '~> 6.0.0'
|
|
9
9
|
|
|
10
10
|
gem "rspec"
|
|
11
|
-
gem "guard"
|
|
12
|
-
gem "guard-rspec"
|
|
13
|
-
|
|
14
|
-
gem 'coveralls', require: false
|
|
15
11
|
|
|
16
12
|
platforms :rbx do
|
|
17
13
|
gem 'rubysl', '~> 2.0'
|
data/Gemfile.rails-4.0
CHANGED
|
@@ -2,16 +2,13 @@ source "https://rubygems.org"
|
|
|
2
2
|
|
|
3
3
|
gemspec
|
|
4
4
|
|
|
5
|
-
gem 'rails', '~> 4.0.
|
|
5
|
+
gem 'rails', '~> 4.0.0'
|
|
6
6
|
gem 'sqlite3', platforms: [:ruby]
|
|
7
7
|
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
|
|
8
8
|
gem 'activerecord-import'
|
|
9
|
+
gem 'tins', '~> 1.6.0', platforms: [:ruby_19]
|
|
9
10
|
|
|
10
11
|
gem "rspec"
|
|
11
|
-
gem "guard"
|
|
12
|
-
gem "guard-rspec"
|
|
13
|
-
|
|
14
|
-
gem 'coveralls', require: false
|
|
15
12
|
|
|
16
13
|
platforms :rbx do
|
|
17
14
|
gem 'rubysl', '~> 2.0'
|
data/Gemfile.rails-4.1
CHANGED
|
@@ -2,16 +2,13 @@ source "https://rubygems.org"
|
|
|
2
2
|
|
|
3
3
|
gemspec
|
|
4
4
|
|
|
5
|
-
gem 'rails', '~> 4.1.
|
|
5
|
+
gem 'rails', '~> 4.1.0'
|
|
6
6
|
gem 'sqlite3'
|
|
7
7
|
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
|
|
8
8
|
gem 'activerecord-import'
|
|
9
|
+
gem 'tins', '~> 1.6.0', platforms: [:ruby_19]
|
|
9
10
|
|
|
10
11
|
gem "rspec"
|
|
11
|
-
gem "guard"
|
|
12
|
-
gem "guard-rspec"
|
|
13
|
-
|
|
14
|
-
gem 'coveralls', require: false
|
|
15
12
|
|
|
16
13
|
platforms :rbx do
|
|
17
14
|
gem 'rubysl', '~> 2.0'
|
|
@@ -2,16 +2,13 @@ source "https://rubygems.org"
|
|
|
2
2
|
|
|
3
3
|
gemspec
|
|
4
4
|
|
|
5
|
-
gem 'rails', '~>
|
|
6
|
-
gem 'sqlite3'
|
|
5
|
+
gem 'rails', '~> 4.2.0'
|
|
6
|
+
gem 'sqlite3'
|
|
7
7
|
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
|
|
8
8
|
gem 'activerecord-import'
|
|
9
|
+
gem 'tins', '~> 1.6.0', platforms: [:ruby_19]
|
|
9
10
|
|
|
10
11
|
gem "rspec"
|
|
11
|
-
gem "guard"
|
|
12
|
-
gem "guard-rspec"
|
|
13
|
-
|
|
14
|
-
gem 'coveralls', require: false
|
|
15
12
|
|
|
16
13
|
platforms :rbx do
|
|
17
14
|
gem 'rubysl', '~> 2.0'
|
|
@@ -2,16 +2,12 @@ source "https://rubygems.org"
|
|
|
2
2
|
|
|
3
3
|
gemspec
|
|
4
4
|
|
|
5
|
-
gem 'rails', '
|
|
6
|
-
gem 'sqlite3'
|
|
5
|
+
gem 'rails', '5.0.0'
|
|
6
|
+
gem 'sqlite3'
|
|
7
7
|
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
|
|
8
8
|
gem 'activerecord-import'
|
|
9
9
|
|
|
10
10
|
gem "rspec"
|
|
11
|
-
gem "guard"
|
|
12
|
-
gem "guard-rspec"
|
|
13
|
-
|
|
14
|
-
gem 'coveralls', require: false
|
|
15
11
|
|
|
16
12
|
platforms :rbx do
|
|
17
13
|
gem 'rubysl', '~> 2.0'
|
data/Hacking.md
CHANGED
|
@@ -23,6 +23,7 @@ Notification instances contain the message that will be displayed, and will
|
|
|
23
23
|
use a Presenter class to display their message to the user.
|
|
24
24
|
|
|
25
25
|
So the flow of a request goes like this:
|
|
26
|
+
|
|
26
27
|
1. Bullet.start_request is called, which resets all the detectors and empties
|
|
27
28
|
the notification collector
|
|
28
29
|
2. The request is handled by Rails, and the installed ActiveRecord extensions
|
data/README.md
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
[](http://badge.fury.io/rb/bullet)
|
|
4
4
|
[](http://travis-ci.org/flyerhzm/bullet)
|
|
5
|
-
[](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
|
[](http://coderwall.com/flyerhzm)
|
|
8
7
|
|
|
@@ -10,10 +9,12 @@ The Bullet gem is designed to help you increase your application's performance b
|
|
|
10
9
|
|
|
11
10
|
Best practice is to use Bullet in development mode or custom mode (staging, profile, etc.). The last thing you want is your clients getting alerts about how lazy you are.
|
|
12
11
|
|
|
13
|
-
Bullet gem now supports **activerecord** >=
|
|
12
|
+
Bullet gem now supports **activerecord** >= 4.0 and **mongoid** >= 4.0.
|
|
14
13
|
|
|
15
14
|
If you use activerecord 2.x, please use bullet <= 4.5.0
|
|
16
15
|
|
|
16
|
+
If you use activerecord 3.x, plesae use bullet < 5.5.0
|
|
17
|
+
|
|
17
18
|
## External Introduction
|
|
18
19
|
|
|
19
20
|
* [http://railscasts.com/episodes/372-bullet](http://railscasts.com/episodes/372-bullet)
|
|
@@ -36,6 +37,9 @@ or add it into a Gemfile (Bundler):
|
|
|
36
37
|
gem "bullet", :group => "development"
|
|
37
38
|
```
|
|
38
39
|
|
|
40
|
+
**Note**: make sure `bullet` gem is added after activerecord (rails) and
|
|
41
|
+
mongoid.
|
|
42
|
+
|
|
39
43
|
## Configuration
|
|
40
44
|
|
|
41
45
|
Bullet won't do ANYTHING unless you tell it to explicitly. Append to
|
|
@@ -53,10 +57,14 @@ config.after_initialize do
|
|
|
53
57
|
:receiver => 'your_account@jabber.org',
|
|
54
58
|
:show_online_status => true }
|
|
55
59
|
Bullet.rails_logger = true
|
|
60
|
+
Bullet.honeybadger = true
|
|
56
61
|
Bullet.bugsnag = true
|
|
57
62
|
Bullet.airbrake = true
|
|
63
|
+
Bullet.rollbar = true
|
|
58
64
|
Bullet.add_footer = true
|
|
59
65
|
Bullet.stacktrace_includes = [ 'your_gem', 'your_middleware' ]
|
|
66
|
+
Bullet.stacktrace_excludes = [ 'their_gem', 'their_middleware' ]
|
|
67
|
+
Bullet.slack = { webhook_url: 'http://some.slack.url', channel: '#default', username: 'notifier' }
|
|
60
68
|
end
|
|
61
69
|
```
|
|
62
70
|
|
|
@@ -67,13 +75,18 @@ The code above will enable all seven of the Bullet notification systems:
|
|
|
67
75
|
* `Bullet.alert`: pop up a JavaScript alert in the browser
|
|
68
76
|
* `Bullet.bullet_logger`: log to the Bullet log file (Rails.root/log/bullet.log)
|
|
69
77
|
* `Bullet.rails_logger`: add warnings directly to the Rails log
|
|
78
|
+
* `Bullet.honeybadger`: add notifications to Honeybadger
|
|
79
|
+
* `Bullet.bugsnag`: add notifications to bugsnag
|
|
70
80
|
* `Bullet.airbrake`: add notifications to airbrake
|
|
81
|
+
* `Bullet.rollbar`: add notifications to rollbar
|
|
71
82
|
* `Bullet.console`: log warnings to your browser's console.log (Safari/Webkit browsers or Firefox w/Firebug installed)
|
|
72
83
|
* `Bullet.growl`: pop up Growl warnings if your system has Growl installed. Requires a little bit of configuration
|
|
73
84
|
* `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.
|
|
74
85
|
* `Bullet.raise`: raise errors, useful for making your specs fail unless they have optimized queries
|
|
75
|
-
* `Bullet.add_footer`: adds the details in the bottom left corner of the page
|
|
86
|
+
* `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.
|
|
76
87
|
* `Bullet.stacktrace_includes`: include paths with any of these substrings in the stack trace, even if they are not in your main app
|
|
88
|
+
* `Bullet.stacktrace_excludes`: ignore paths with any of these substrings in the stack trace, even if they are not in your main app.
|
|
89
|
+
* `Bullet.slack`: add notifications to slack
|
|
77
90
|
|
|
78
91
|
Bullet also allows you to disable any of its detectors.
|
|
79
92
|
|
|
@@ -102,6 +115,22 @@ Bullet.add_whitelist :type => :unused_eager_loading, :class_name => "Post", :ass
|
|
|
102
115
|
Bullet.add_whitelist :type => :counter_cache, :class_name => "Country", :association => :cities
|
|
103
116
|
```
|
|
104
117
|
|
|
118
|
+
If you want to skip bullet in some specific controller actions, you can
|
|
119
|
+
do like
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
class ApplicationController < ActionController::Base
|
|
123
|
+
around_action :skip_bullet
|
|
124
|
+
|
|
125
|
+
def skip_bullet
|
|
126
|
+
Bullet.enable = false
|
|
127
|
+
yield
|
|
128
|
+
ensure
|
|
129
|
+
Bullet.enable = true
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
```
|
|
133
|
+
|
|
105
134
|
## Log
|
|
106
135
|
|
|
107
136
|
The Bullet log `log/bullet.log` will look something like this:
|
|
@@ -153,8 +182,9 @@ The Bullet gem uses rack middleware to profile requests. If you want to use Bull
|
|
|
153
182
|
```ruby
|
|
154
183
|
Bullet.profile do
|
|
155
184
|
# do anything
|
|
185
|
+
|
|
186
|
+
warnings = Bullet.warnings
|
|
156
187
|
end
|
|
157
|
-
warnings = Bullet.warnings
|
|
158
188
|
```
|
|
159
189
|
|
|
160
190
|
### Work with sinatra
|
|
@@ -209,7 +239,9 @@ Bullet outputs some details info, to enable debug mode, set
|
|
|
209
239
|
|
|
210
240
|
## Demo
|
|
211
241
|
|
|
212
|
-
Bullet is designed to function as you browse through your application in development. To see it in action,
|
|
242
|
+
Bullet is designed to function as you browse through your application in development. To see it in action,
|
|
243
|
+
you can visit [https://github.com/flyerhzm/bullet_test](https://github.com/flyerhzm/bullet_test) or
|
|
244
|
+
follow these steps to create, detect, and fix example query problems.
|
|
213
245
|
|
|
214
246
|
1\. Create an example application
|
|
215
247
|
|
|
@@ -302,7 +334,7 @@ model: Post => associations: [comment]
|
|
|
302
334
|
|
|
303
335
|
which means there is a N+1 query from the Post object to its Comment association.
|
|
304
336
|
|
|
305
|
-
In the
|
|
337
|
+
In the meantime, there's a log appended into `log/bullet.log` file
|
|
306
338
|
|
|
307
339
|
```
|
|
308
340
|
2010-03-07 14:12:18[INFO] N+1 Query in /posts
|
|
@@ -429,4 +461,4 @@ Meanwhile, there's a line appended to `log/bullet.log`
|
|
|
429
461
|
Post => [:comments]
|
|
430
462
|
```
|
|
431
463
|
|
|
432
|
-
Copyright (c) 2009 -
|
|
464
|
+
Copyright (c) 2009 - 2016 Richard Huang (flyerhzm@gmail.com), released under the MIT license
|
data/bullet.gemspec
CHANGED
|
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
|
|
|
18
18
|
s.required_rubygems_version = ">= 1.3.6"
|
|
19
19
|
|
|
20
20
|
s.add_runtime_dependency "activesupport", ">= 3.0.0"
|
|
21
|
-
s.add_runtime_dependency "uniform_notifier", "
|
|
21
|
+
s.add_runtime_dependency "uniform_notifier", "~> 1.10.0"
|
|
22
22
|
|
|
23
23
|
s.files = `git ls-files`.split("\n")
|
|
24
24
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
@@ -2,35 +2,81 @@ 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
|
+
if result.size > 1
|
|
13
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
|
14
|
+
Bullet::Detector::CounterCache.add_possible_objects(result)
|
|
15
|
+
elsif result.size == 1
|
|
16
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
|
|
17
|
+
Bullet::Detector::CounterCache.add_impossible_object(result.first)
|
|
18
|
+
end
|
|
19
|
+
elsif result.is_a? ::ActiveRecord::Base
|
|
20
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
|
|
21
|
+
Bullet::Detector::CounterCache.add_impossible_object(result)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
result
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
5
29
|
::ActiveRecord::Relation.class_eval do
|
|
6
30
|
alias_method :origin_to_a, :to_a
|
|
7
31
|
# if select a collection of objects, then these objects have possible to cause N+1 query.
|
|
8
32
|
# if select only one object, then the only one object has impossible to cause N+1 query.
|
|
9
33
|
def to_a
|
|
10
34
|
records = origin_to_a
|
|
11
|
-
if
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
35
|
+
if Bullet.start?
|
|
36
|
+
if records.size > 1
|
|
37
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
38
|
+
Bullet::Detector::CounterCache.add_possible_objects(records)
|
|
39
|
+
elsif records.size == 1
|
|
40
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
41
|
+
Bullet::Detector::CounterCache.add_impossible_object(records.first)
|
|
42
|
+
end
|
|
17
43
|
end
|
|
18
44
|
records
|
|
19
45
|
end
|
|
20
46
|
end
|
|
21
47
|
|
|
48
|
+
::ActiveRecord::Persistence.class_eval do
|
|
49
|
+
def save_with_bullet(*args, &proc)
|
|
50
|
+
was_new_record = new_record?
|
|
51
|
+
save_without_bullet(*args, &proc).tap do |result|
|
|
52
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
alias_method_chain :save, :bullet
|
|
56
|
+
|
|
57
|
+
def save_with_bullet!(*args, &proc)
|
|
58
|
+
was_new_record = new_record?
|
|
59
|
+
save_without_bullet!(*args, &proc).tap do |result|
|
|
60
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
alias_method_chain :save!, :bullet
|
|
64
|
+
end
|
|
65
|
+
|
|
22
66
|
::ActiveRecord::Associations::Preloader.class_eval do
|
|
23
67
|
# include query for one to many associations.
|
|
24
68
|
# keep this eager loadings.
|
|
25
69
|
alias_method :origin_initialize, :initialize
|
|
26
70
|
def initialize(records, associations, preload_scope = nil)
|
|
27
71
|
origin_initialize(records, associations, preload_scope)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
72
|
+
if Bullet.start?
|
|
73
|
+
records = [records].flatten.compact.uniq
|
|
74
|
+
return if records.empty?
|
|
75
|
+
records.each do |record|
|
|
76
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
77
|
+
end
|
|
78
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
|
32
79
|
end
|
|
33
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
|
34
80
|
end
|
|
35
81
|
end
|
|
36
82
|
|
|
@@ -39,11 +85,13 @@ module Bullet
|
|
|
39
85
|
alias_method :origin_find_with_associations, :find_with_associations
|
|
40
86
|
def find_with_associations
|
|
41
87
|
records = origin_find_with_associations
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
88
|
+
if Bullet.start?
|
|
89
|
+
associations = (eager_load_values + includes_values).uniq
|
|
90
|
+
records.each do |record|
|
|
91
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
92
|
+
end
|
|
93
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
|
45
94
|
end
|
|
46
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
|
47
95
|
records
|
|
48
96
|
end
|
|
49
97
|
end
|
|
@@ -56,9 +104,11 @@ module Bullet
|
|
|
56
104
|
@bullet_eager_loadings = {}
|
|
57
105
|
records = origin_instantiate(rows)
|
|
58
106
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
107
|
+
if Bullet.start?
|
|
108
|
+
@bullet_eager_loadings.each do |klazz, eager_loadings_hash|
|
|
109
|
+
objects = eager_loadings_hash.keys
|
|
110
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
|
|
111
|
+
end
|
|
62
112
|
end
|
|
63
113
|
records
|
|
64
114
|
end
|
|
@@ -67,12 +117,14 @@ module Bullet
|
|
|
67
117
|
def construct_association(record, join, row)
|
|
68
118
|
result = origin_construct_association(record, join, row)
|
|
69
119
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
120
|
+
if Bullet.start?
|
|
121
|
+
associations = join.reflection.name
|
|
122
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
123
|
+
Bullet::Detector::NPlusOneQuery.call_association(record, associations)
|
|
124
|
+
@bullet_eager_loadings[record.class] ||= {}
|
|
125
|
+
@bullet_eager_loadings[record.class][record] ||= Set.new
|
|
126
|
+
@bullet_eager_loadings[record.class][record] << associations
|
|
127
|
+
end
|
|
76
128
|
|
|
77
129
|
result
|
|
78
130
|
end
|
|
@@ -82,20 +134,38 @@ module Bullet
|
|
|
82
134
|
# call one to many associations
|
|
83
135
|
alias_method :origin_load_target, :load_target
|
|
84
136
|
def load_target
|
|
85
|
-
Bullet
|
|
137
|
+
if Bullet.start?
|
|
138
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
139
|
+
end
|
|
86
140
|
origin_load_target
|
|
87
141
|
end
|
|
88
142
|
|
|
143
|
+
alias_method :origin_include?, :include?
|
|
144
|
+
def include?(object)
|
|
145
|
+
if Bullet.start?
|
|
146
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
147
|
+
end
|
|
148
|
+
origin_include?(object)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
::ActiveRecord::Associations::HasManyAssociation.class_eval do
|
|
89
153
|
alias_method :origin_empty?, :empty?
|
|
90
154
|
def empty?
|
|
91
|
-
Bullet
|
|
155
|
+
if Bullet.start? && !loaded? && !has_cached_counter?(@reflection)
|
|
156
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
157
|
+
end
|
|
92
158
|
origin_empty?
|
|
93
159
|
end
|
|
160
|
+
end
|
|
94
161
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
162
|
+
::ActiveRecord::Associations::HasAndBelongsToManyAssociation.class_eval do
|
|
163
|
+
alias_method :origin_empty?, :empty?
|
|
164
|
+
def empty?
|
|
165
|
+
if Bullet.start? && !loaded?
|
|
166
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
167
|
+
end
|
|
168
|
+
origin_empty?
|
|
99
169
|
end
|
|
100
170
|
end
|
|
101
171
|
|
|
@@ -104,8 +174,12 @@ module Bullet
|
|
|
104
174
|
alias_method :origin_reader, :reader
|
|
105
175
|
def reader(force_reload = false)
|
|
106
176
|
result = origin_reader(force_reload)
|
|
107
|
-
Bullet
|
|
108
|
-
|
|
177
|
+
if Bullet.start?
|
|
178
|
+
unless @inversed
|
|
179
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
180
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
109
183
|
result
|
|
110
184
|
end
|
|
111
185
|
end
|
|
@@ -115,7 +189,9 @@ module Bullet
|
|
|
115
189
|
|
|
116
190
|
def has_cached_counter?(reflection = reflection())
|
|
117
191
|
result = origin_has_cached_counter?(reflection)
|
|
118
|
-
Bullet
|
|
192
|
+
if Bullet.start? && !result
|
|
193
|
+
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
194
|
+
end
|
|
119
195
|
result
|
|
120
196
|
end
|
|
121
197
|
end
|