flyerhzm-bullet 1.4.0 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +17 -0
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/bullet.gemspec +5 -2
- data/lib/bullet.rb +2 -0
- data/lib/bullet/active_record.rb +9 -0
- data/lib/bullet/association.rb +174 -117
- data/lib/bullet/counter.rb +29 -0
- data/spec/bullet_association_spec.rb +2 -0
- data/spec/bullet_counter_spec.rb +124 -0
- data/spec/spec_helper.rb +1 -0
- metadata +5 -2
data/README.textile
CHANGED
@@ -9,6 +9,7 @@ Best practice is to use Bullet in development mode or custom mode (staging, prof
|
|
9
9
|
h2. Thanks
|
10
10
|
|
11
11
|
flipsasser added Growl, console.log and Rails.log support, very awesome. And he improved README.
|
12
|
+
rainux add group style console.log.
|
12
13
|
|
13
14
|
****************************************************************************
|
14
15
|
|
@@ -97,6 +98,22 @@ To get Growl support up-and-running for Bullet, follow the steps below:
|
|
97
98
|
|
98
99
|
****************************************************************************
|
99
100
|
|
101
|
+
h2. Advance
|
102
|
+
|
103
|
+
The bullet plugin use rack middleware for http request. If you want to bullet for without http server, such as job server. You can do like this:
|
104
|
+
|
105
|
+
<pre><code>
|
106
|
+
Bullet::Association.start_request if Bullet.enable?
|
107
|
+
# run job
|
108
|
+
if Bullet.enable?
|
109
|
+
Bullet::Association.growl_notification
|
110
|
+
Bullet::Association.log_notificatioin('JobServer: ')
|
111
|
+
Bullet::Association.end_request
|
112
|
+
end
|
113
|
+
</code></pre>
|
114
|
+
|
115
|
+
****************************************************************************
|
116
|
+
|
100
117
|
h2. Step by step example
|
101
118
|
|
102
119
|
Bullet is designed to function as you browse through your application in development. It will alert you whenever it encounters N+1 queries or unused eager loading.
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.4.
|
1
|
+
1.4.1
|
data/bullet.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{bullet}
|
8
|
-
s.version = "1.4.
|
8
|
+
s.version = "1.4.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Richard Huang"]
|
12
|
-
s.date = %q{2009-09-
|
12
|
+
s.date = %q{2009-09-09}
|
13
13
|
s.description = %q{The Bullet plugin is designed to help you increase your application's performance by reducing the number of queries it makes. It will watch your queries while you develop your application and notify you when you should add eager loading (N+1 queries) or when you're using eager loading that isn't necessary.}
|
14
14
|
s.email = %q{flyerhzm@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -24,10 +24,12 @@ Gem::Specification.new do |s|
|
|
24
24
|
"lib/bullet.rb",
|
25
25
|
"lib/bullet/active_record.rb",
|
26
26
|
"lib/bullet/association.rb",
|
27
|
+
"lib/bullet/counter.rb",
|
27
28
|
"lib/bullet/logger.rb",
|
28
29
|
"lib/bulletware.rb",
|
29
30
|
"rails/init.rb",
|
30
31
|
"spec/bullet_association_spec.rb",
|
32
|
+
"spec/bullet_counter_spec.rb",
|
31
33
|
"spec/spec.opts",
|
32
34
|
"spec/spec_helper.rb",
|
33
35
|
"tasks/bullet_tasks.rake"
|
@@ -39,6 +41,7 @@ Gem::Specification.new do |s|
|
|
39
41
|
s.summary = %q{A plugin to kill N+1 queries and unused eager loading}
|
40
42
|
s.test_files = [
|
41
43
|
"spec/bullet_association_spec.rb",
|
44
|
+
"spec/bullet_counter_spec.rb",
|
42
45
|
"spec/spec_helper.rb"
|
43
46
|
]
|
44
47
|
|
data/lib/bullet.rb
CHANGED
data/lib/bullet/active_record.rb
CHANGED
@@ -62,6 +62,15 @@ module Bullet
|
|
62
62
|
origin_load_target
|
63
63
|
end
|
64
64
|
end
|
65
|
+
|
66
|
+
::ActiveRecord::Associations::HasManyAssociation.class_eval do
|
67
|
+
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
68
|
+
def has_cached_counter?
|
69
|
+
result = origin_has_cached_counter?
|
70
|
+
Bullet::Counter.add_counter_cache(@owner, @reflection.name) unless result
|
71
|
+
result
|
72
|
+
end
|
73
|
+
end
|
65
74
|
end
|
66
75
|
end
|
67
76
|
end
|
data/lib/bullet/association.rb
CHANGED
@@ -11,33 +11,13 @@ module Bullet
|
|
11
11
|
@@growl_password = nil
|
12
12
|
@@rails_logger = nil
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
def end_request
|
19
|
-
# puts "end request"
|
20
|
-
@@object_associations = nil
|
21
|
-
@@unpreload_associations = nil
|
22
|
-
@@unused_preload_associations = nil
|
23
|
-
@@callers = nil
|
24
|
-
@@possible_objects = nil
|
25
|
-
@@impossible_objects = nil
|
26
|
-
@@call_object_associations = nil
|
27
|
-
@@eager_loadings = nil
|
28
|
-
end
|
29
|
-
|
14
|
+
###################################################
|
15
|
+
# Configurations
|
16
|
+
###################################################
|
30
17
|
def alert=(alert)
|
31
18
|
@@alert = alert
|
32
19
|
end
|
33
20
|
|
34
|
-
def bullet_logger=(bullet_logger)
|
35
|
-
if @@bullet_logger = bullet_logger
|
36
|
-
@@logger_file = File.open(Bullet::BulletLogger::LOG_FILE, 'a+')
|
37
|
-
@@logger = Bullet::BulletLogger.new(@@logger_file)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
21
|
def console=(console)
|
42
22
|
@@console = console
|
43
23
|
end
|
@@ -59,32 +39,44 @@ module Bullet
|
|
59
39
|
@@growl_password = growl_password
|
60
40
|
end
|
61
41
|
|
42
|
+
def bullet_logger=(bullet_logger)
|
43
|
+
if @@bullet_logger = bullet_logger
|
44
|
+
@@logger_file = File.open(Bullet::BulletLogger::LOG_FILE, 'a+')
|
45
|
+
@@logger = Bullet::BulletLogger.new(@@logger_file)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
62
49
|
def rails_logger=(rails_logger)
|
63
50
|
@@rails_logger = rails_logger
|
64
51
|
end
|
65
52
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
add_unused_preload_associations(object.class, diff_object_association) unless diff_object_association.empty?
|
72
|
-
end
|
53
|
+
#####################################################
|
54
|
+
# login control interface
|
55
|
+
#####################################################
|
56
|
+
def start_request
|
57
|
+
# puts "start request"
|
73
58
|
end
|
74
|
-
|
59
|
+
|
60
|
+
def end_request
|
61
|
+
# puts "end request"
|
62
|
+
@@object_associations = nil
|
63
|
+
@@unpreload_associations = nil
|
64
|
+
@@unused_preload_associations = nil
|
65
|
+
@@callers = nil
|
66
|
+
@@possible_objects = nil
|
67
|
+
@@impossible_objects = nil
|
68
|
+
@@call_object_associations = nil
|
69
|
+
@@eager_loadings = nil
|
70
|
+
end
|
71
|
+
|
75
72
|
def has_bad_assocations?
|
76
73
|
check_unused_preload_associations
|
77
74
|
has_unpreload_associations? or has_unused_preload_associations?
|
78
75
|
end
|
79
76
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
def has_unpreload_associations?
|
85
|
-
!unpreload_associations.empty?
|
86
|
-
end
|
87
|
-
|
77
|
+
######################################################
|
78
|
+
# Notifications
|
79
|
+
######################################################
|
88
80
|
def javascript_notification
|
89
81
|
str = ''
|
90
82
|
if @@alert || @@console
|
@@ -94,11 +86,30 @@ module Bullet
|
|
94
86
|
str << wrap_js_association("alert(#{response.join("\n").inspect});")
|
95
87
|
end
|
96
88
|
if @@console
|
97
|
-
|
89
|
+
title = []
|
90
|
+
title << unused_preload_messages.first.first unless unused_preload_messages.empty?
|
91
|
+
title << unpreload_messages.first.first unless unpreload_messages.empty?
|
92
|
+
code = <<-CODE
|
93
|
+
if (typeof(console) !== 'undefined') {
|
94
|
+
|
95
|
+
if (console.groupCollapsed && console.groupEnd && console.log) {
|
96
|
+
|
97
|
+
console.groupCollapsed(#{title.join(', ').inspect});
|
98
|
+
console.log(#{response.join("\n").inspect});
|
99
|
+
console.log(#{call_stack_messages.join("\n").inspect});
|
100
|
+
console.groupEnd();
|
101
|
+
|
102
|
+
} else if (console.log) {
|
103
|
+
|
104
|
+
console.log(#{response.join("\n").inspect});
|
105
|
+
}
|
106
|
+
}
|
107
|
+
CODE
|
108
|
+
str << wrap_js_association(code)
|
98
109
|
end
|
99
110
|
str
|
100
111
|
end
|
101
|
-
|
112
|
+
|
102
113
|
def growl_notification
|
103
114
|
if @@growl
|
104
115
|
response = notification_response
|
@@ -113,77 +124,21 @@ module Bullet
|
|
113
124
|
def log_notificatioin(path)
|
114
125
|
if (@@bullet_logger || @@rails_logger) && (!unpreload_associations.empty? || !unused_preload_associations.empty?)
|
115
126
|
Rails.logger.warn '' if @@rails_logger
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
127
|
+
unused_preload_messages(path).each do |message|
|
128
|
+
@@logger.info(message.join("\n")) if @@bullet_logger
|
129
|
+
Rails.logger.warn(message.join("\n")) if @@rails_logger
|
130
|
+
end
|
131
|
+
unpreload_messages(path).each do |message|
|
132
|
+
@@logger.info(message.join("\n")) if @@bullet_logger
|
133
|
+
Rails.logger.warn(message.join("\n")) if @@rails_logger
|
120
134
|
end
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
Rails.logger.warn(log) if @@rails_logger
|
125
|
-
end
|
126
|
-
callers.each do |c|
|
127
|
-
log = ["N+1 Query method call stack", c.map{|line| " #{line}"}].flatten.join("\n")
|
128
|
-
@@logger.info(log) if @@bullet_logger
|
129
|
-
Rails.logger.warn(log) if @@rails_logger
|
135
|
+
call_stack_messages.each do |message|
|
136
|
+
@@logger.info(message.join("\n")) if @@bullet_logger
|
137
|
+
Rails.logger.warn(message.join("\n")) if @@rails_logger
|
130
138
|
end
|
131
139
|
@@logger_file.flush if @@bullet_logger
|
132
140
|
end
|
133
141
|
end
|
134
|
-
|
135
|
-
def klazz_associations_str(klazz, associations)
|
136
|
-
" #{klazz} => [#{associations.map(&:inspect).join(', ')}]"
|
137
|
-
end
|
138
|
-
|
139
|
-
def associations_str(associations)
|
140
|
-
":include => #{associations.map{|a| a.to_sym unless a.is_a? Hash}.inspect}"
|
141
|
-
end
|
142
|
-
|
143
|
-
def notification_response
|
144
|
-
response = []
|
145
|
-
if has_unused_preload_associations?
|
146
|
-
response.push("Unused eager loadings detected:\n")
|
147
|
-
response.push(*@@unused_preload_associations.to_a.collect{|klazz, associations| klazz_associations_str(klazz, associations)}.join("\n"))
|
148
|
-
end
|
149
|
-
if has_unpreload_associations?
|
150
|
-
response.push("#{"\n" unless response.empty?}N+1 queries detected:\n")
|
151
|
-
response.push(*@@unpreload_associations.to_a.collect{|klazz, associations| " #{klazz} => [#{associations.map(&:inspect).join(', ')}]"}.join("\n"))
|
152
|
-
end
|
153
|
-
response
|
154
|
-
end
|
155
|
-
|
156
|
-
def wrap_js_association(message)
|
157
|
-
str = ''
|
158
|
-
str << "<script type=\"text/javascript\">/*<![CDATA[*/"
|
159
|
-
str << message
|
160
|
-
str << "/*]]>*/</script>\n"
|
161
|
-
end
|
162
|
-
|
163
|
-
def has_klazz_association(klazz)
|
164
|
-
!klazz_associations[klazz].nil? and klazz_associations.keys.include?(klazz)
|
165
|
-
end
|
166
|
-
|
167
|
-
def define_association(klazz, associations)
|
168
|
-
# puts "define association, #{klazz} => #{associations.inspect}"
|
169
|
-
add_klazz_associations(klazz, associations)
|
170
|
-
end
|
171
|
-
|
172
|
-
def call_association(object, associations)
|
173
|
-
# puts "call association, #{object} => #{associations.inspect}"
|
174
|
-
add_call_object_associations(object, associations)
|
175
|
-
if unpreload_associations?(object, associations)
|
176
|
-
add_unpreload_associations(object.class, associations)
|
177
|
-
caller_in_project
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
def unpreload_associations?(object, associations)
|
182
|
-
klazz = object.class
|
183
|
-
(!possible_objects[klazz].nil? and possible_objects[klazz].include?(object)) and
|
184
|
-
(impossible_objects[klazz].nil? or !impossible_objects[klazz].include?(object)) and
|
185
|
-
(object_associations[object].nil? or !object_associations[object].include?(associations))
|
186
|
-
end
|
187
142
|
|
188
143
|
def add_unpreload_associations(klazz, associations)
|
189
144
|
# puts "add unpreload associations, #{klazz} => #{associations.inspect}"
|
@@ -191,7 +146,7 @@ module Bullet
|
|
191
146
|
unpreload_associations[klazz] << associations
|
192
147
|
unique(unpreload_associations[klazz])
|
193
148
|
end
|
194
|
-
|
149
|
+
|
195
150
|
def add_unused_preload_associations(klazz, associations)
|
196
151
|
# puts "add unused preload associations, #{klazz} => #{associations.inspect}"
|
197
152
|
unused_preload_associations[klazz] ||= []
|
@@ -206,6 +161,9 @@ module Bullet
|
|
206
161
|
unique(object_associations[object])
|
207
162
|
end
|
208
163
|
|
164
|
+
###################################################
|
165
|
+
# interface for active record
|
166
|
+
###################################################
|
209
167
|
def add_call_object_associations(object, associations)
|
210
168
|
# puts "add call object associations, #{object} => #{associations.inspect}"
|
211
169
|
call_object_associations[object] ||= []
|
@@ -228,7 +186,7 @@ module Bullet
|
|
228
186
|
impossible_objects[klazz] << object
|
229
187
|
impossible_objects[klazz].uniq!
|
230
188
|
end
|
231
|
-
|
189
|
+
|
232
190
|
def add_klazz_associations(klazz, associations)
|
233
191
|
# puts "define associations, #{klazz} => #{associations.inspect}"
|
234
192
|
klazz_associations[klazz] ||= []
|
@@ -243,28 +201,127 @@ module Bullet
|
|
243
201
|
eager_loadings[objects] << associations
|
244
202
|
unique(eager_loadings[objects])
|
245
203
|
end
|
204
|
+
|
205
|
+
def define_association(klazz, associations)
|
206
|
+
# puts "define association, #{klazz} => #{associations.inspect}"
|
207
|
+
add_klazz_associations(klazz, associations)
|
208
|
+
end
|
209
|
+
|
210
|
+
def call_association(object, associations)
|
211
|
+
# puts "call association, #{object} => #{associations.inspect}"
|
212
|
+
add_call_object_associations(object, associations)
|
213
|
+
if unpreload_associations?(object, associations)
|
214
|
+
add_unpreload_associations(object.class, associations)
|
215
|
+
caller_in_project
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
############################################
|
220
|
+
# for rspec
|
221
|
+
############################################
|
222
|
+
def check_unused_preload_associations
|
223
|
+
object_associations.each do |object, association|
|
224
|
+
related_objects = eager_loadings.select {|key, value| key.include?(object) and value == association}.collect(&:first).flatten
|
225
|
+
call_object_association = related_objects.collect { |related_object| call_object_associations[related_object] }.compact.flatten.uniq
|
226
|
+
diff_object_association = (association - call_object_association).reject {|a| a.is_a? Hash}
|
227
|
+
add_unused_preload_associations(object.class, diff_object_association) unless diff_object_association.empty?
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def has_unused_preload_associations?
|
232
|
+
!unused_preload_associations.empty?
|
233
|
+
end
|
234
|
+
|
235
|
+
def has_unpreload_associations?
|
236
|
+
!unpreload_associations.empty?
|
237
|
+
end
|
238
|
+
|
239
|
+
|
240
|
+
private
|
241
|
+
def unpreload_associations?(object, associations)
|
242
|
+
klazz = object.class
|
243
|
+
(!possible_objects[klazz].nil? and possible_objects[klazz].include?(object)) and
|
244
|
+
(impossible_objects[klazz].nil? or !impossible_objects[klazz].include?(object)) and
|
245
|
+
(object_associations[object].nil? or !object_associations[object].include?(associations))
|
246
|
+
end
|
247
|
+
|
248
|
+
def notification_response
|
249
|
+
response = []
|
250
|
+
if has_unused_preload_associations?
|
251
|
+
response << unused_preload_messages.join("\n")
|
252
|
+
end
|
253
|
+
if has_unpreload_associations?
|
254
|
+
response << unpreload_messages.join("\n")
|
255
|
+
end
|
256
|
+
response
|
257
|
+
end
|
258
|
+
|
259
|
+
def unused_preload_messages(path = nil)
|
260
|
+
messages = []
|
261
|
+
unused_preload_associations.each do |klazz, associations|
|
262
|
+
messages << [
|
263
|
+
"Unused Eager Loading #{path ? "in #{path}" : 'detected'}",
|
264
|
+
klazz_associations_str(klazz, associations),
|
265
|
+
" Remove from your finder: #{associations_str(associations)}"
|
266
|
+
]
|
267
|
+
end
|
268
|
+
messages
|
269
|
+
end
|
270
|
+
|
271
|
+
def unpreload_messages(path = nil)
|
272
|
+
messages = []
|
273
|
+
unpreload_associations.each do |klazz, associations|
|
274
|
+
messages << [
|
275
|
+
"N+1 Query #{path ? "in #{path}" : 'detected'}",
|
276
|
+
klazz_associations_str(klazz, associations),
|
277
|
+
" Add to your finder: #{associations_str(associations)}"
|
278
|
+
]
|
279
|
+
end
|
280
|
+
messages
|
281
|
+
end
|
282
|
+
|
283
|
+
def klazz_associations_str(klazz, associations)
|
284
|
+
" #{klazz} => [#{associations.map(&:inspect).join(', ')}]"
|
285
|
+
end
|
286
|
+
|
287
|
+
def associations_str(associations)
|
288
|
+
":include => #{associations.map{|a| a.to_sym unless a.is_a? Hash}.inspect}"
|
289
|
+
end
|
290
|
+
|
291
|
+
def wrap_js_association(message)
|
292
|
+
str = ''
|
293
|
+
str << "<script type=\"text/javascript\">/*<![CDATA[*/"
|
294
|
+
str << message
|
295
|
+
str << "/*]]>*/</script>\n"
|
296
|
+
end
|
297
|
+
|
298
|
+
def call_stack_messages
|
299
|
+
callers.inject([]) do |messages, c|
|
300
|
+
messages << ['N+1 Query method call stack', c.collect {|line| " #{line}"}].flatten
|
301
|
+
end
|
302
|
+
end
|
246
303
|
|
247
304
|
def unique(array)
|
248
305
|
array.flatten!
|
249
306
|
array.uniq!
|
250
307
|
end
|
251
|
-
|
308
|
+
|
252
309
|
def unpreload_associations
|
253
310
|
@@unpreload_associations ||= {}
|
254
311
|
end
|
255
|
-
|
312
|
+
|
256
313
|
def unused_preload_associations
|
257
314
|
@@unused_preload_associations ||= {}
|
258
315
|
end
|
259
|
-
|
316
|
+
|
260
317
|
def object_associations
|
261
318
|
@@object_associations ||= {}
|
262
319
|
end
|
263
|
-
|
320
|
+
|
264
321
|
def call_object_associations
|
265
322
|
@@call_object_associations ||= {}
|
266
323
|
end
|
267
|
-
|
324
|
+
|
268
325
|
def possible_objects
|
269
326
|
@@possible_objects ||= {}
|
270
327
|
end
|
@@ -272,7 +329,7 @@ module Bullet
|
|
272
329
|
def impossible_objects
|
273
330
|
@@impossible_objects ||= {}
|
274
331
|
end
|
275
|
-
|
332
|
+
|
276
333
|
def klazz_associations
|
277
334
|
@@klazz_associations ||= {}
|
278
335
|
end
|
@@ -280,13 +337,13 @@ module Bullet
|
|
280
337
|
def eager_loadings
|
281
338
|
@@eager_loadings ||= {}
|
282
339
|
end
|
283
|
-
|
340
|
+
|
284
341
|
VENDOR_ROOT = File.join(RAILS_ROOT, 'vendor')
|
285
342
|
def caller_in_project
|
286
343
|
callers << caller.select {|c| c =~ /#{RAILS_ROOT}/}.reject {|c| c =~ /#{VENDOR_ROOT}/}
|
287
344
|
callers.uniq!
|
288
345
|
end
|
289
|
-
|
346
|
+
|
290
347
|
def callers
|
291
348
|
@@callers ||= []
|
292
349
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Bullet
|
2
|
+
class Counter
|
3
|
+
class <<self
|
4
|
+
def start_request
|
5
|
+
|
6
|
+
end
|
7
|
+
|
8
|
+
def end_request
|
9
|
+
@@klazz_associations = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def need_counter_caches?
|
13
|
+
!klazz_associations.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_counter_cache(object, associations)
|
17
|
+
klazz = object.class
|
18
|
+
klazz_associations[klazz] ||= []
|
19
|
+
klazz_associations[klazz] << associations
|
20
|
+
klazz_associations[klazz].flatten!
|
21
|
+
klazz_associations[klazz].uniq!
|
22
|
+
end
|
23
|
+
|
24
|
+
def klazz_associations
|
25
|
+
@@klazz_associations ||= {}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :dbfile => ':memory:')
|
4
|
+
|
5
|
+
describe Bullet::Counter do
|
6
|
+
def setup_db
|
7
|
+
ActiveRecord::Schema.define(:version => 1) do
|
8
|
+
create_table :countries do |t|
|
9
|
+
t.string :name
|
10
|
+
end
|
11
|
+
|
12
|
+
create_table :cities do |t|
|
13
|
+
t.string :name
|
14
|
+
t.integer :country_id
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def teardown_db
|
20
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
21
|
+
ActiveRecord::Base.connection.drop_table(table)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Country < ActiveRecord::Base
|
26
|
+
has_many :cities
|
27
|
+
end
|
28
|
+
|
29
|
+
class City < ActiveRecord::Base
|
30
|
+
belongs_to :country
|
31
|
+
end
|
32
|
+
|
33
|
+
before(:all) do
|
34
|
+
setup_db
|
35
|
+
|
36
|
+
country1 = Country.create(:name => 'first')
|
37
|
+
country2 = Country.create(:name => 'second')
|
38
|
+
|
39
|
+
country1.cities.create(:name => 'first')
|
40
|
+
country1.cities.create(:name => 'second')
|
41
|
+
country2.cities.create(:name => 'third')
|
42
|
+
country2.cities.create(:name => 'fourth')
|
43
|
+
end
|
44
|
+
|
45
|
+
after(:all) do
|
46
|
+
teardown_db
|
47
|
+
end
|
48
|
+
|
49
|
+
before(:each) do
|
50
|
+
Bullet::Counter.start_request
|
51
|
+
end
|
52
|
+
|
53
|
+
after(:each) do
|
54
|
+
Bullet::Counter.end_request
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should need counter cache with count" do
|
58
|
+
Country.all.each do |country|
|
59
|
+
country.cities.size
|
60
|
+
end
|
61
|
+
Bullet::Counter.should be_need_counter_caches
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe Bullet::Counter do
|
66
|
+
def setup_db
|
67
|
+
ActiveRecord::Schema.define(:version => 1) do
|
68
|
+
create_table :people do |t|
|
69
|
+
t.string :name
|
70
|
+
t.integer :pets_count
|
71
|
+
end
|
72
|
+
|
73
|
+
create_table :pets do |t|
|
74
|
+
t.string :name
|
75
|
+
t.integer :person_id
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def teardown_db
|
81
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
82
|
+
ActiveRecord::Base.connection.drop_table(table)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class Person < ActiveRecord::Base
|
87
|
+
has_many :pets
|
88
|
+
end
|
89
|
+
|
90
|
+
class Pet < ActiveRecord::Base
|
91
|
+
belongs_to :person, :counter_cache => true
|
92
|
+
end
|
93
|
+
|
94
|
+
before(:all) do
|
95
|
+
setup_db
|
96
|
+
|
97
|
+
person1 = Person.create(:name => 'first')
|
98
|
+
person2 = Person.create(:name => 'second')
|
99
|
+
|
100
|
+
person1.pets.create(:name => 'first')
|
101
|
+
person1.pets.create(:name => 'second')
|
102
|
+
person2.pets.create(:name => 'third')
|
103
|
+
person2.pets.create(:name => 'fourth')
|
104
|
+
end
|
105
|
+
|
106
|
+
after(:all) do
|
107
|
+
teardown_db
|
108
|
+
end
|
109
|
+
|
110
|
+
before(:each) do
|
111
|
+
Bullet::Counter.start_request
|
112
|
+
end
|
113
|
+
|
114
|
+
after(:each) do
|
115
|
+
Bullet::Counter.end_request
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should need counter cache with count" do
|
119
|
+
Person.all.each do |person|
|
120
|
+
person.pets.size
|
121
|
+
end
|
122
|
+
Bullet::Counter.should_not be_need_counter_caches
|
123
|
+
end
|
124
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -7,6 +7,7 @@ RAILS_ROOT = File.expand_path(__FILE__).split('/')[0..-3].join('/') unless defin
|
|
7
7
|
require File.expand_path(File.join(File.dirname(__FILE__), '../lib/bullet/logger'))
|
8
8
|
require File.expand_path(File.join(File.dirname(__FILE__), '../lib/bullet/active_record'))
|
9
9
|
require File.expand_path(File.join(File.dirname(__FILE__), '../lib/bullet/association'))
|
10
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../lib/bullet/counter'))
|
10
11
|
require File.expand_path(File.join(File.dirname(__FILE__), '../lib/bullet'))
|
11
12
|
require File.expand_path(File.join(File.dirname(__FILE__), '../lib/bulletware'))
|
12
13
|
Bullet.enable = true
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flyerhzm-bullet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Huang
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-09-
|
12
|
+
date: 2009-09-09 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -30,10 +30,12 @@ files:
|
|
30
30
|
- lib/bullet.rb
|
31
31
|
- lib/bullet/active_record.rb
|
32
32
|
- lib/bullet/association.rb
|
33
|
+
- lib/bullet/counter.rb
|
33
34
|
- lib/bullet/logger.rb
|
34
35
|
- lib/bulletware.rb
|
35
36
|
- rails/init.rb
|
36
37
|
- spec/bullet_association_spec.rb
|
38
|
+
- spec/bullet_counter_spec.rb
|
37
39
|
- spec/spec.opts
|
38
40
|
- spec/spec_helper.rb
|
39
41
|
- tasks/bullet_tasks.rake
|
@@ -65,4 +67,5 @@ specification_version: 3
|
|
65
67
|
summary: A plugin to kill N+1 queries and unused eager loading
|
66
68
|
test_files:
|
67
69
|
- spec/bullet_association_spec.rb
|
70
|
+
- spec/bullet_counter_spec.rb
|
68
71
|
- spec/spec_helper.rb
|