bullet 5.5.0 → 5.6.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/.travis.yml +1 -5
- data/CHANGELOG.md +9 -0
- data/Gemfile +6 -1
- data/Gemfile.rails-5.0 +1 -1
- data/Gemfile.rails-5.1 +15 -0
- data/Hacking.md +1 -1
- data/README.md +11 -19
- data/lib/bullet/active_record5.rb +44 -62
- data/lib/bullet/detector/association.rb +2 -2
- data/lib/bullet/detector/n_plus_one_query.rb +6 -5
- data/lib/bullet/ext/object.rb +1 -1
- data/lib/bullet/ext/string.rb +1 -1
- data/lib/bullet/notification/base.rb +1 -1
- data/lib/bullet/rack.rb +1 -1
- data/lib/bullet/stack_trace_filter.rb +2 -1
- data/lib/bullet/version.rb +1 -1
- data/lib/generators/bullet/install_generator.rb +46 -0
- data/spec/bullet/detector/n_plus_one_query_spec.rb +11 -11
- data/spec/integration/{active_record4 → active_record}/association_spec.rb +1 -1
- data/spec/support/mongo_seed.rb +2 -10
- metadata +7 -7
- data/spec/integration/active_record5/association_spec.rb +0 -768
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 652f59b5a91224c16cace3931e368d0da02fa449
|
|
4
|
+
data.tar.gz: 9ef371969315cd308b194a9d230d1f19892a57e4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ed473ab951e1f168de703cf0615f203abc93e37ef896330d6f91c7b61d320cd0906e691e41245e13a11aa9e3aa3d059ea62b00b256fa96af0540c8ab35ebc2fb
|
|
7
|
+
data.tar.gz: f6c4456dfb56957b441fce1b56094baacc911186b59b48b43f22e41ad3bbed88c52aa16b4bc7247b7ca37a3c7d026bb45f5342ccc24b37bc47836cd63fb41a1c
|
data/.travis.yml
CHANGED
|
@@ -3,14 +3,10 @@ language: ruby
|
|
|
3
3
|
rvm:
|
|
4
4
|
- 2.3.0
|
|
5
5
|
gemfile:
|
|
6
|
+
- Gemfile.rails-5.1
|
|
6
7
|
- Gemfile.rails-5.0
|
|
7
8
|
- Gemfile.rails-4.2
|
|
8
9
|
- Gemfile.rails-4.1
|
|
9
10
|
- Gemfile.rails-4.0
|
|
10
|
-
- Gemfile.mongoid-6.0
|
|
11
|
-
- Gemfile.mongoid-5.0
|
|
12
|
-
- Gemfile.mongoid-4.0
|
|
13
11
|
env:
|
|
14
12
|
- DB=sqlite
|
|
15
|
-
services:
|
|
16
|
-
- mongodb
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Next Release
|
|
2
2
|
|
|
3
|
+
## 5.6.0 (07/16/2017)
|
|
4
|
+
|
|
5
|
+
* Migrate alias_method to Module#prepend
|
|
6
|
+
* Add install generator
|
|
7
|
+
* Stack trace filter
|
|
8
|
+
* Fix rails 5.1 compatibility
|
|
9
|
+
* Fix inverse_of for rails 5
|
|
10
|
+
* Fix detect file attachment for rack #319
|
|
11
|
+
|
|
3
12
|
## 5.5.0 (12/30/2016)
|
|
4
13
|
|
|
5
14
|
* Display http request method #311
|
data/Gemfile
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
source 'https://rubygems.org'
|
|
2
2
|
|
|
3
|
+
git_source(:github) do |repo_name|
|
|
4
|
+
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?('/')
|
|
5
|
+
"https://github.com/#{repo_name}.git"
|
|
6
|
+
end
|
|
7
|
+
|
|
3
8
|
gemspec
|
|
4
9
|
|
|
5
|
-
gem 'rails', github: 'rails
|
|
10
|
+
gem 'rails', github: 'rails'
|
|
6
11
|
gem 'sqlite3', platforms: [:ruby]
|
|
7
12
|
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
|
|
8
13
|
gem 'activerecord-import'
|
data/Gemfile.rails-5.0
CHANGED
data/Gemfile.rails-5.1
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
source "https://rubygems.org"
|
|
2
|
+
|
|
3
|
+
gemspec
|
|
4
|
+
|
|
5
|
+
gem 'rails', '~> 5.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/Hacking.md
CHANGED
|
@@ -28,7 +28,7 @@ So the flow of a request goes like this:
|
|
|
28
28
|
the notification collector
|
|
29
29
|
2. The request is handled by Rails, and the installed ActiveRecord extensions
|
|
30
30
|
trigger Detector callbacks
|
|
31
|
-
3. Detectors once called, will determine whether something noteworthy
|
|
31
|
+
3. Detectors once called, will determine whether something noteworthy happened.
|
|
32
32
|
If yes, then a Notification is created and stored in the notification collector.
|
|
33
33
|
4. Rails finishes handling the request
|
|
34
34
|
5. For each notification in the collector, Bullet will iterate over each
|
data/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Bullet gem now supports **activerecord** >= 4.0 and **mongoid** >= 4.0.
|
|
|
13
13
|
|
|
14
14
|
If you use activerecord 2.x, please use bullet <= 4.5.0
|
|
15
15
|
|
|
16
|
-
If you use activerecord 3.x,
|
|
16
|
+
If you use activerecord 3.x, please use bullet < 5.5.0
|
|
17
17
|
|
|
18
18
|
## External Introduction
|
|
19
19
|
|
|
@@ -34,7 +34,7 @@ or add it into a Gemfile (Bundler):
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
```ruby
|
|
37
|
-
gem
|
|
37
|
+
gem 'bullet', group: 'development'
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
**Note**: make sure `bullet` gem is added after activerecord (rails) and
|
|
@@ -70,23 +70,24 @@ end
|
|
|
70
70
|
|
|
71
71
|
The notifier of Bullet is a wrap of [uniform_notifier](https://github.com/flyerhzm/uniform_notifier)
|
|
72
72
|
|
|
73
|
-
The code above will enable all
|
|
73
|
+
The code above will enable all of the Bullet notification systems:
|
|
74
74
|
* `Bullet.enable`: enable Bullet gem, otherwise do nothing
|
|
75
75
|
* `Bullet.alert`: pop up a JavaScript alert in the browser
|
|
76
76
|
* `Bullet.bullet_logger`: log to the Bullet log file (Rails.root/log/bullet.log)
|
|
77
|
+
* `Bullet.console`: log warnings to your browser's console.log (Safari/Webkit browsers or Firefox w/Firebug installed)
|
|
78
|
+
* `Bullet.growl`: pop up Growl warnings if your system has Growl installed. Requires a little bit of configuration
|
|
79
|
+
* `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.
|
|
77
80
|
* `Bullet.rails_logger`: add warnings directly to the Rails log
|
|
78
81
|
* `Bullet.honeybadger`: add notifications to Honeybadger
|
|
79
82
|
* `Bullet.bugsnag`: add notifications to bugsnag
|
|
80
83
|
* `Bullet.airbrake`: add notifications to airbrake
|
|
81
84
|
* `Bullet.rollbar`: add notifications to rollbar
|
|
82
|
-
* `Bullet.console`: log warnings to your browser's console.log (Safari/Webkit browsers or Firefox w/Firebug installed)
|
|
83
|
-
* `Bullet.growl`: pop up Growl warnings if your system has Growl installed. Requires a little bit of configuration
|
|
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.
|
|
85
|
-
* `Bullet.raise`: raise errors, useful for making your specs fail unless they have optimized queries
|
|
86
85
|
* `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.
|
|
87
86
|
* `Bullet.stacktrace_includes`: include paths with any of these substrings in the stack trace, even if they are not in your main app
|
|
88
87
|
* `Bullet.stacktrace_excludes`: ignore paths with any of these substrings in the stack trace, even if they are not in your main app.
|
|
89
88
|
* `Bullet.slack`: add notifications to slack
|
|
89
|
+
* `Bullet.raise`: raise errors, useful for making your specs fail unless they have optimized queries
|
|
90
|
+
|
|
90
91
|
|
|
91
92
|
Bullet also allows you to disable any of its detectors.
|
|
92
93
|
|
|
@@ -302,19 +303,10 @@ And run
|
|
|
302
303
|
bundle install
|
|
303
304
|
```
|
|
304
305
|
|
|
305
|
-
6\. enable the Bullet gem
|
|
306
|
-
`config/environments/development.rb`
|
|
306
|
+
6\. enable the Bullet gem with generate command
|
|
307
307
|
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
Bullet.enable = true
|
|
311
|
-
Bullet.alert = true
|
|
312
|
-
Bullet.bullet_logger = true
|
|
313
|
-
Bullet.console = true
|
|
314
|
-
# Bullet.growl = true
|
|
315
|
-
Bullet.rails_logger = true
|
|
316
|
-
Bullet.add_footer = true
|
|
317
|
-
end
|
|
308
|
+
```
|
|
309
|
+
bundle exec rails g bullet:install
|
|
318
310
|
```
|
|
319
311
|
|
|
320
312
|
7\. Start the server
|
|
@@ -18,38 +18,34 @@ module Bullet
|
|
|
18
18
|
module ActiveRecord
|
|
19
19
|
def self.enable
|
|
20
20
|
require 'active_record'
|
|
21
|
-
::ActiveRecord::Base.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
result
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
|
|
33
|
-
Bullet::Detector::CounterCache.add_impossible_object(result.first)
|
|
34
|
-
end
|
|
35
|
-
elsif result.is_a? ::ActiveRecord::Base
|
|
36
|
-
Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
|
|
37
|
-
Bullet::Detector::CounterCache.add_impossible_object(result)
|
|
21
|
+
::ActiveRecord::Base.extend(Module.new {
|
|
22
|
+
def find_by_sql(sql, binds = [], preparable: nil, &block)
|
|
23
|
+
result = super
|
|
24
|
+
if Bullet.start?
|
|
25
|
+
if result.is_a? Array
|
|
26
|
+
if result.size > 1
|
|
27
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
|
28
|
+
Bullet::Detector::CounterCache.add_possible_objects(result)
|
|
29
|
+
elsif result.size == 1
|
|
30
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
|
|
31
|
+
Bullet::Detector::CounterCache.add_impossible_object(result.first)
|
|
38
32
|
end
|
|
33
|
+
elsif result.is_a? ::ActiveRecord::Base
|
|
34
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
|
|
35
|
+
Bullet::Detector::CounterCache.add_impossible_object(result)
|
|
39
36
|
end
|
|
40
|
-
result
|
|
41
37
|
end
|
|
38
|
+
result
|
|
42
39
|
end
|
|
43
|
-
|
|
40
|
+
})
|
|
44
41
|
|
|
45
42
|
::ActiveRecord::Base.prepend(SaveWithBulletSupport)
|
|
46
43
|
|
|
47
|
-
::ActiveRecord::Relation.
|
|
48
|
-
alias_method :origin_records, :records
|
|
44
|
+
::ActiveRecord::Relation.prepend(Module.new {
|
|
49
45
|
# if select a collection of objects, then these objects have possible to cause N+1 query.
|
|
50
46
|
# if select only one object, then the only one object has impossible to cause N+1 query.
|
|
51
47
|
def records
|
|
52
|
-
result =
|
|
48
|
+
result = super
|
|
53
49
|
if Bullet.start?
|
|
54
50
|
if result.first.class.name !~ /^HABTM_/
|
|
55
51
|
if result.size > 1
|
|
@@ -63,11 +59,9 @@ module Bullet
|
|
|
63
59
|
end
|
|
64
60
|
result
|
|
65
61
|
end
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
::ActiveRecord::Associations::Preloader.class_eval do
|
|
69
|
-
alias_method :origin_preloaders_for_one, :preloaders_for_one
|
|
62
|
+
})
|
|
70
63
|
|
|
64
|
+
::ActiveRecord::Associations::Preloader.prepend(Module.new {
|
|
71
65
|
def preloaders_for_one(association, records, scope)
|
|
72
66
|
if Bullet.start?
|
|
73
67
|
records.compact!
|
|
@@ -78,16 +72,15 @@ module Bullet
|
|
|
78
72
|
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
|
79
73
|
end
|
|
80
74
|
end
|
|
81
|
-
|
|
75
|
+
super
|
|
82
76
|
end
|
|
83
|
-
|
|
77
|
+
})
|
|
84
78
|
|
|
85
|
-
::ActiveRecord::FinderMethods.
|
|
79
|
+
::ActiveRecord::FinderMethods.prepend(Module.new {
|
|
86
80
|
# add includes in scope
|
|
87
|
-
alias_method :origin_find_with_associations, :find_with_associations
|
|
88
81
|
def find_with_associations
|
|
89
|
-
return
|
|
90
|
-
records =
|
|
82
|
+
return super { |r| yield r } if block_given?
|
|
83
|
+
records = super
|
|
91
84
|
if Bullet.start?
|
|
92
85
|
associations = (eager_load_values + includes_values).uniq
|
|
93
86
|
records.each do |record|
|
|
@@ -97,16 +90,12 @@ module Bullet
|
|
|
97
90
|
end
|
|
98
91
|
records
|
|
99
92
|
end
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
::ActiveRecord::Associations::JoinDependency.class_eval do
|
|
103
|
-
alias_method :origin_instantiate, :instantiate
|
|
104
|
-
alias_method :origin_construct, :construct
|
|
105
|
-
alias_method :origin_construct_model, :construct_model
|
|
93
|
+
})
|
|
106
94
|
|
|
95
|
+
::ActiveRecord::Associations::JoinDependency.prepend(Module.new {
|
|
107
96
|
def instantiate(result_set, aliases)
|
|
108
97
|
@bullet_eager_loadings = {}
|
|
109
|
-
records =
|
|
98
|
+
records = super
|
|
110
99
|
|
|
111
100
|
if Bullet.start?
|
|
112
101
|
@bullet_eager_loadings.each do |klazz, eager_loadings_hash|
|
|
@@ -135,12 +124,12 @@ module Bullet
|
|
|
135
124
|
end
|
|
136
125
|
end
|
|
137
126
|
|
|
138
|
-
|
|
127
|
+
super
|
|
139
128
|
end
|
|
140
129
|
|
|
141
130
|
# call join associations
|
|
142
131
|
def construct_model(record, node, row, model_cache, id, aliases)
|
|
143
|
-
result =
|
|
132
|
+
result = super
|
|
144
133
|
|
|
145
134
|
if Bullet.start?
|
|
146
135
|
associations = node.reflection.name
|
|
@@ -153,13 +142,11 @@ module Bullet
|
|
|
153
142
|
|
|
154
143
|
result
|
|
155
144
|
end
|
|
156
|
-
|
|
145
|
+
})
|
|
157
146
|
|
|
158
|
-
::ActiveRecord::Associations::CollectionAssociation.
|
|
159
|
-
# call one to many associations
|
|
160
|
-
alias_method :origin_load_target, :load_target
|
|
147
|
+
::ActiveRecord::Associations::CollectionAssociation.prepend(Module.new {
|
|
161
148
|
def load_target
|
|
162
|
-
records =
|
|
149
|
+
records = super
|
|
163
150
|
|
|
164
151
|
if Bullet.start?
|
|
165
152
|
if self.is_a? ::ActiveRecord::Associations::ThroughAssociation
|
|
@@ -183,28 +170,25 @@ module Bullet
|
|
|
183
170
|
records
|
|
184
171
|
end
|
|
185
172
|
|
|
186
|
-
alias_method :origin_empty?, :empty?
|
|
187
173
|
def empty?
|
|
188
174
|
if Bullet.start? && !reflection.has_cached_counter?
|
|
189
175
|
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
|
|
190
176
|
end
|
|
191
|
-
|
|
177
|
+
super
|
|
192
178
|
end
|
|
193
179
|
|
|
194
|
-
alias_method :origin_include?, :include?
|
|
195
180
|
def include?(object)
|
|
196
181
|
if Bullet.start?
|
|
197
182
|
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
|
|
198
183
|
end
|
|
199
|
-
|
|
184
|
+
super
|
|
200
185
|
end
|
|
201
|
-
|
|
186
|
+
})
|
|
202
187
|
|
|
203
|
-
::ActiveRecord::Associations::SingularAssociation.
|
|
188
|
+
::ActiveRecord::Associations::SingularAssociation.prepend(Module.new {
|
|
204
189
|
# call has_one and belongs_to associations
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
result = origin_reader(force_reload)
|
|
190
|
+
def target
|
|
191
|
+
result = super()
|
|
208
192
|
if Bullet.start?
|
|
209
193
|
if owner.class.name !~ /^HABTM_/ && !@inversed
|
|
210
194
|
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
|
|
@@ -217,27 +201,25 @@ module Bullet
|
|
|
217
201
|
end
|
|
218
202
|
result
|
|
219
203
|
end
|
|
220
|
-
|
|
204
|
+
})
|
|
221
205
|
|
|
222
|
-
::ActiveRecord::Associations::HasManyAssociation.
|
|
223
|
-
alias_method :origin_many_empty?, :empty?
|
|
206
|
+
::ActiveRecord::Associations::HasManyAssociation.prepend(Module.new {
|
|
224
207
|
def empty?
|
|
225
|
-
result =
|
|
208
|
+
result = super
|
|
226
209
|
if Bullet.start? && !reflection.has_cached_counter?
|
|
227
210
|
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
|
|
228
211
|
end
|
|
229
212
|
result
|
|
230
213
|
end
|
|
231
214
|
|
|
232
|
-
alias_method :origin_count_records, :count_records
|
|
233
215
|
def count_records
|
|
234
216
|
result = reflection.has_cached_counter?
|
|
235
217
|
if Bullet.start? && !result && !self.is_a?(::ActiveRecord::Associations::ThroughAssociation)
|
|
236
218
|
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
237
219
|
end
|
|
238
|
-
|
|
220
|
+
super
|
|
239
221
|
end
|
|
240
|
-
|
|
222
|
+
})
|
|
241
223
|
end
|
|
242
224
|
end
|
|
243
225
|
end
|
|
@@ -7,7 +7,7 @@ module Bullet
|
|
|
7
7
|
return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
|
|
8
8
|
return unless object.primary_key_value
|
|
9
9
|
|
|
10
|
-
Bullet.debug("Detector::Association#add_object_associations", "object: #{object.bullet_key}, associations: #{associations}")
|
|
10
|
+
Bullet.debug("Detector::Association#add_object_associations".freeze, "object: #{object.bullet_key}, associations: #{associations}")
|
|
11
11
|
object_associations.add(object.bullet_key, associations)
|
|
12
12
|
end
|
|
13
13
|
|
|
@@ -16,7 +16,7 @@ module Bullet
|
|
|
16
16
|
return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
|
|
17
17
|
return unless object.primary_key_value
|
|
18
18
|
|
|
19
|
-
Bullet.debug("Detector::Association#add_call_object_associations", "object: #{object.bullet_key}, associations: #{associations}")
|
|
19
|
+
Bullet.debug("Detector::Association#add_call_object_associations".freeze, "object: #{object.bullet_key}, associations: #{associations}")
|
|
20
20
|
call_object_associations.add(object.bullet_key, associations)
|
|
21
21
|
end
|
|
22
22
|
|
|
@@ -11,11 +11,12 @@ module Bullet
|
|
|
11
11
|
# if it is, keeps this unpreload associations and caller.
|
|
12
12
|
def call_association(object, associations)
|
|
13
13
|
return unless Bullet.start?
|
|
14
|
+
return unless Bullet.n_plus_one_query_enable?
|
|
14
15
|
return unless object.primary_key_value
|
|
15
16
|
return if inversed_objects.include?(object.bullet_key, associations)
|
|
16
17
|
add_call_object_associations(object, associations)
|
|
17
18
|
|
|
18
|
-
Bullet.debug("Detector::NPlusOneQuery#call_association", "object: #{object.bullet_key}, associations: #{associations}")
|
|
19
|
+
Bullet.debug("Detector::NPlusOneQuery#call_association".freeze, "object: #{object.bullet_key}, associations: #{associations}")
|
|
19
20
|
if !excluded_stacktrace_path? && conditions_met?(object, associations)
|
|
20
21
|
Bullet.debug("detect n + 1 query", "object: #{object.bullet_key}, associations: #{associations}")
|
|
21
22
|
create_notification caller_in_project, object.class.to_s, associations
|
|
@@ -28,7 +29,7 @@ module Bullet
|
|
|
28
29
|
objects = Array(object_or_objects)
|
|
29
30
|
return if objects.map(&:primary_key_value).compact.empty?
|
|
30
31
|
|
|
31
|
-
Bullet.debug("Detector::NPlusOneQuery#add_possible_objects", "objects: #{objects.map(&:bullet_key).join(', ')}")
|
|
32
|
+
Bullet.debug("Detector::NPlusOneQuery#add_possible_objects".freeze, "objects: #{objects.map(&:bullet_key).join(', '.freeze)}")
|
|
32
33
|
objects.each { |object| possible_objects.add object.bullet_key }
|
|
33
34
|
end
|
|
34
35
|
|
|
@@ -37,7 +38,7 @@ module Bullet
|
|
|
37
38
|
return unless Bullet.n_plus_one_query_enable?
|
|
38
39
|
return unless object.primary_key_value
|
|
39
40
|
|
|
40
|
-
Bullet.debug("Detector::NPlusOneQuery#add_impossible_object", "object: #{object.bullet_key}")
|
|
41
|
+
Bullet.debug("Detector::NPlusOneQuery#add_impossible_object".freeze, "object: #{object.bullet_key}")
|
|
41
42
|
impossible_objects.add object.bullet_key
|
|
42
43
|
end
|
|
43
44
|
|
|
@@ -46,7 +47,7 @@ module Bullet
|
|
|
46
47
|
return unless Bullet.n_plus_one_query_enable?
|
|
47
48
|
return unless object.primary_key_value
|
|
48
49
|
|
|
49
|
-
Bullet.debug("Detector::NPlusOneQuery#add_inversed_object", "object: #{object.bullet_key}, association: #{association}")
|
|
50
|
+
Bullet.debug("Detector::NPlusOneQuery#add_inversed_object".freeze, "object: #{object.bullet_key}, association: #{association}")
|
|
50
51
|
inversed_objects.add object.bullet_key, association
|
|
51
52
|
end
|
|
52
53
|
|
|
@@ -68,7 +69,7 @@ module Bullet
|
|
|
68
69
|
value = object_associations[object.bullet_key]
|
|
69
70
|
if value
|
|
70
71
|
value.each do |v|
|
|
71
|
-
# associations == v
|
|
72
|
+
# associations == v comparison order is important here because
|
|
72
73
|
# v variable might be a squeel node where :== method is redefined,
|
|
73
74
|
# so it does not compare values at all and return unexpected results
|
|
74
75
|
result = v.is_a?(Hash) ? v.key?(associations) : associations == v
|
data/lib/bullet/ext/object.rb
CHANGED
|
@@ -5,7 +5,7 @@ class Object
|
|
|
5
5
|
|
|
6
6
|
def primary_key_value
|
|
7
7
|
if self.class.respond_to?(:primary_keys) && self.class.primary_keys
|
|
8
|
-
self.class.primary_keys.map { |primary_key| self.send primary_key }.join(',')
|
|
8
|
+
self.class.primary_keys.map { |primary_key| self.send primary_key }.join(','.freeze)
|
|
9
9
|
elsif self.class.respond_to?(:primary_key) && self.class.primary_key
|
|
10
10
|
self.send self.class.primary_key
|
|
11
11
|
else
|
data/lib/bullet/ext/string.rb
CHANGED
data/lib/bullet/rack.rb
CHANGED
|
@@ -5,7 +5,8 @@ module Bullet
|
|
|
5
5
|
def caller_in_project
|
|
6
6
|
app_root = rails? ? Rails.root.to_s : Dir.pwd
|
|
7
7
|
vendor_root = app_root + VENDOR_PATH
|
|
8
|
-
|
|
8
|
+
caller_locations.select do |location|
|
|
9
|
+
caller_path = location.absolute_path
|
|
9
10
|
caller_path.include?(app_root) && !caller_path.include?(vendor_root) ||
|
|
10
11
|
Bullet.stacktrace_includes.any? do |include_pattern|
|
|
11
12
|
case include_pattern
|
data/lib/bullet/version.rb
CHANGED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Bullet
|
|
2
|
+
module Generators
|
|
3
|
+
class InstallGenerator < ::Rails::Generators::Base
|
|
4
|
+
desc <<-DESC
|
|
5
|
+
Description:
|
|
6
|
+
Enable bullet in development/test for your application.
|
|
7
|
+
DESC
|
|
8
|
+
|
|
9
|
+
def enable_in_development
|
|
10
|
+
environment(nil, env: "development") do
|
|
11
|
+
<<-"FILE".strip
|
|
12
|
+
|
|
13
|
+
config.after_initialize do
|
|
14
|
+
Bullet.enable = true
|
|
15
|
+
Bullet.alert = true
|
|
16
|
+
Bullet.bullet_logger = true
|
|
17
|
+
Bullet.console = true
|
|
18
|
+
# Bullet.growl = true
|
|
19
|
+
Bullet.rails_logger = true
|
|
20
|
+
Bullet.add_footer = true
|
|
21
|
+
end
|
|
22
|
+
FILE
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
say "Enabled bullet in config/environments/development.rb"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def enable_in_test
|
|
29
|
+
if yes?("Would you like to enable bullet in test environment? (y/n)")
|
|
30
|
+
environment(nil, env: "test") do
|
|
31
|
+
<<-"FILE".strip
|
|
32
|
+
|
|
33
|
+
config.after_initialize do
|
|
34
|
+
Bullet.enable = true
|
|
35
|
+
Bullet.bullet_logger = true
|
|
36
|
+
Bullet.raise = true # raise an error if n+1 query occurs
|
|
37
|
+
end
|
|
38
|
+
FILE
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
say "Enabled bullet in config/environments/test.rb"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -91,11 +91,11 @@ module Bullet
|
|
|
91
91
|
after { Bullet.stacktrace_excludes = nil }
|
|
92
92
|
|
|
93
93
|
it "should not create notification when stacktrace contains paths that are in the exclude list" do
|
|
94
|
-
in_project = File.join(Dir.pwd, 'abc', 'abc.rb')
|
|
95
|
-
included_path = '/ghi/ghi.rb'
|
|
96
|
-
excluded_path = '/def/def.rb'
|
|
94
|
+
in_project = OpenStruct.new(:absolute_path => File.join(Dir.pwd, 'abc', 'abc.rb'))
|
|
95
|
+
included_path = OpenStruct.new(:absolute_path => '/ghi/ghi.rb')
|
|
96
|
+
excluded_path = OpenStruct.new(:absolute_path => '/def/def.rb')
|
|
97
97
|
|
|
98
|
-
expect(NPlusOneQuery).to receive(:
|
|
98
|
+
expect(NPlusOneQuery).to receive(:caller_locations).and_return([in_project, included_path, excluded_path])
|
|
99
99
|
expect(NPlusOneQuery).to_not receive(:create_notification)
|
|
100
100
|
NPlusOneQuery.call_association(@post, :association)
|
|
101
101
|
end
|
|
@@ -104,10 +104,10 @@ module Bullet
|
|
|
104
104
|
|
|
105
105
|
context ".caller_in_project" do
|
|
106
106
|
it "should include only paths that are in the project" do
|
|
107
|
-
in_project = File.join(Dir.pwd, 'abc', 'abc.rb')
|
|
108
|
-
not_in_project = '/def/def.rb'
|
|
107
|
+
in_project = OpenStruct.new(:absolute_path => File.join(Dir.pwd, 'abc', 'abc.rb'))
|
|
108
|
+
not_in_project = OpenStruct.new(:absolute_path => '/def/def.rb')
|
|
109
109
|
|
|
110
|
-
expect(NPlusOneQuery).to receive(:
|
|
110
|
+
expect(NPlusOneQuery).to receive(:caller_locations).and_return([in_project, not_in_project])
|
|
111
111
|
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
|
|
112
112
|
expect(NPlusOneQuery).to receive(:create_notification).with([in_project], "Post", :association)
|
|
113
113
|
NPlusOneQuery.call_association(@post, :association)
|
|
@@ -118,11 +118,11 @@ module Bullet
|
|
|
118
118
|
after { Bullet.stacktrace_includes = nil }
|
|
119
119
|
|
|
120
120
|
it "should include paths that are in the stacktrace_include list" do
|
|
121
|
-
in_project = File.join(Dir.pwd, 'abc', 'abc.rb')
|
|
122
|
-
included_gems = ['/def/def.rb', 'xyz/xyz.rb']
|
|
123
|
-
excluded_gem = '/ghi/ghi.rb'
|
|
121
|
+
in_project = OpenStruct.new(:absolute_path => File.join(Dir.pwd, 'abc', 'abc.rb'))
|
|
122
|
+
included_gems = [OpenStruct.new(:absolute_path => '/def/def.rb'), OpenStruct.new(:absolute_path => 'xyz/xyz.rb')]
|
|
123
|
+
excluded_gem = OpenStruct.new(:absolute_path => '/ghi/ghi.rb')
|
|
124
124
|
|
|
125
|
-
expect(NPlusOneQuery).to receive(:
|
|
125
|
+
expect(NPlusOneQuery).to receive(:caller_locations).and_return([in_project, *included_gems, excluded_gem])
|
|
126
126
|
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
|
|
127
127
|
expect(NPlusOneQuery).to receive(:create_notification).with([in_project, *included_gems], "Post", :association)
|
|
128
128
|
NPlusOneQuery.call_association(@post, :association)
|
data/spec/support/mongo_seed.rb
CHANGED
|
@@ -33,15 +33,7 @@ module Support
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def setup_db
|
|
36
|
-
if Mongoid::VERSION =~ /\
|
|
37
|
-
Mongoid.configure do |config|
|
|
38
|
-
config.master = Mongo::Connection.new.db("bullet")
|
|
39
|
-
end
|
|
40
|
-
elsif Mongoid::VERSION =~ /\A3/
|
|
41
|
-
Mongoid.configure do |config|
|
|
42
|
-
config.connect_to("bullet")
|
|
43
|
-
end
|
|
44
|
-
elsif Mongoid::VERSION =~ /\A4/
|
|
36
|
+
if Mongoid::VERSION =~ /\A4/
|
|
45
37
|
Mongoid.configure do |config|
|
|
46
38
|
config.load_configuration(
|
|
47
39
|
sessions: {
|
|
@@ -52,7 +44,7 @@ module Support
|
|
|
52
44
|
}
|
|
53
45
|
)
|
|
54
46
|
end
|
|
55
|
-
|
|
47
|
+
else
|
|
56
48
|
Mongoid.configure do |config|
|
|
57
49
|
config.load_configuration(
|
|
58
50
|
clients: {
|