thaold-bullet 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 - 2010 Richard Huang (flyerhzm@gmail.com)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,396 @@
1
+ h1. Bullet
2
+
3
+ The Bullet plugin/gem 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), when you're using eager loading that isn't necessary and when you should use counter cache.
4
+
5
+ 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.
6
+
7
+ The Bullet plugin/gem now supports rails 2.1, 2.2, 2.3 and 3.0.
8
+
9
+ ****************************************************************************
10
+
11
+ h2. Install
12
+
13
+ You can install it as a gem:
14
+ <pre><code>
15
+ gem install bullet
16
+ </code></pre>
17
+
18
+ or add it into a Gemfile (Bundler):
19
+ <pre><code>
20
+ gem "bullet", :group => "development"
21
+ </code></code>
22
+
23
+ ****************************************************************************
24
+
25
+ h2. Configuration
26
+
27
+ Bullet won't do ANYTHING unless you tell it to explicitly. Append to <code>config/environments/development.rb</code> initializer with the following code:
28
+ <pre><code>
29
+ config.after_initialize do
30
+ Bullet.enable = true
31
+ Bullet.alert = true
32
+ Bullet.bullet_logger = true
33
+ Bullet.console = true
34
+ Bullet.growl = true
35
+ Bullet.xmpp = { :account => 'bullets_account@jabber.org',
36
+ :password => 'bullets_password_for_jabber',
37
+ :receiver => 'your_account@jabber.org',
38
+ :show_online_status => true }
39
+ Bullet.rails_logger = true
40
+ Bullet.disable_browser_cache = true
41
+ end
42
+ </code></pre>
43
+
44
+ The notifier of bullet is a wrap of "uniform_notifier":https://github.com/flyerhzm/uniform_notifier
45
+
46
+ The code above will enable all six of the Bullet notification systems:
47
+ * <code>Bullet.enable</code>: enable Bullet plugin/gem, otherwise do nothing
48
+ * <code>Bullet.alert</code>: pop up a JavaScript alert in the browser
49
+ * <code>Bullet.bullet_logger</code>: log to the Bullet log file (Rails.root/log/bullet.log)
50
+ * <code>Bullet.rails_logger</code>: add warnings directly to the Rails log
51
+ * <code>Bullet.console</code>: log warnings to your browser's console.log (Safari/Webkit browsers or Firefox w/Firebug installed)
52
+ * <code>Bullet.growl</code>: pop up Growl warnings if your system has Growl installed. Requires a little bit of configuration
53
+ * <code>Bullet.xmpp</code>: 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.
54
+ * <code>Bullet.disable_browser_cache</code>: disable browser cache which usually causes unexpected problems
55
+
56
+ ****************************************************************************
57
+
58
+ h2. Log
59
+
60
+ The Bullet log <code>log/bullet.log</code> will look something like this:
61
+
62
+ * N+1 Query:
63
+ <pre><code>
64
+ 2009-08-25 20:40:17[INFO] N+1 Query: PATH_INFO: /posts; model: Post => associations: [comments]·
65
+ Add to your finder: :include => [:comments]
66
+ 2009-08-25 20:40:17[INFO] N+1 Query: method call stack:·
67
+ /Users/richard/Downloads/test/app/views/posts/index.html.erb:11:in `_run_erb_app47views47posts47index46html46erb'
68
+ /Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `each'
69
+ /Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `_run_erb_app47views47posts47index46html46erb'
70
+ /Users/richard/Downloads/test/app/controllers/posts_controller.rb:7:in `index'
71
+ </code></pre>
72
+
73
+ 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.
74
+
75
+ * Unused eager loading:
76
+ <pre><code>
77
+ 2009-08-25 20:53:56[INFO] Unused eager loadings: PATH_INFO: /posts; model: Post => associations: [comments]·
78
+ Remove from your finder: :include => [:comments]
79
+ </code></pre>
80
+
81
+ These two lines are notifications that unused eager loadings have been encountered.
82
+
83
+ * Need counter cache:
84
+ <pre><code>
85
+ 2009-09-11 09:46:50[INFO] Need Counter Cache
86
+ Post => [:comments]
87
+ </code></pre>
88
+
89
+ ****************************************************************************
90
+
91
+ h2. Growl Support
92
+
93
+ To get Growl support up-and-running for Bullet, follow the steps below:
94
+ * Install the ruby-growl gem: <code>gem install ruby-growl</code>
95
+ * Open the Growl preference pane in Systems Preferences
96
+ * Click the "Network" tab
97
+ * Make sure both "Listen for incoming notifications" and "Allow remote application registration" are checked. *Note*: If you set a password, you will need to set <code>Bullet.growl_password = { :password => 'growl password' }</code> in the config file.
98
+ * Restart Growl ("General" tab -> Stop Growl -> Start Growl)
99
+ * Boot up your application. Bullet will automatically send a Growl notification when Growl is turned on. If you do not see it when your application loads, make sure it is enabled in your initializer and double-check the steps above.
100
+
101
+ ****************************************************************************
102
+
103
+ h2. Ruby 1.9 issue
104
+
105
+ ruby-growl gem has an issue about md5 in ruby 1.9, if you use growl and ruby 1.9, check this gist http://gist.github.com/300184
106
+
107
+ ****************************************************************************
108
+
109
+ h2. XMPP/Jabber Support
110
+
111
+ To get XMPP support up-and-running for Bullet, follow the steps below:
112
+ * Install the xmpp4r gem: <code>sudo gem install xmpp4r</code>
113
+ * Make both the bullet and the receipient account add each other as contacts.
114
+ This will require you to manually log into both accounts, add each other
115
+ as contact and confirm each others contact request.
116
+ * Boot up your application. Bullet will automatically send an XMPP notification when XMPP is turned on.
117
+
118
+ ****************************************************************************
119
+
120
+ h2. Important
121
+
122
+ If you find bullet does not work for you, *please disable your browser's cache*.
123
+
124
+ ****************************************************************************
125
+
126
+ h2. Advance
127
+
128
+ The bullet plugin/gem use rack middleware for http request. If you want to bullet for without http server, such as job server. You can do like this:
129
+
130
+ <pre><code>
131
+ Bullet.start_request if Bullet.enable?
132
+ # run job
133
+ if Bullet.enable?
134
+ Bullet.growl_notification
135
+ Bullet.log_notification('JobServer: ')
136
+ Bullet.end_request
137
+ end
138
+ </code></pre>
139
+
140
+ Or you want to use it in test mode
141
+
142
+ <pre><code>
143
+ before(:each)
144
+ Bullet.start_request if Bullet.enable?
145
+ end
146
+
147
+ after(:each)
148
+ if Bullet.enable?
149
+ Bullet.perform_out_of_channel_notifications
150
+ Bullet.end_request
151
+ end
152
+ end
153
+ </code></pre>
154
+
155
+ Don't forget enabling bullet in test environment.
156
+
157
+ ****************************************************************************
158
+
159
+ h2. Links
160
+
161
+ * "http://weblog.rubyonrails.org/2009/10/22/community-highlights":http://weblog.rubyonrails.org/2009/10/22/community-highlights
162
+ * "http://ruby5.envylabs.com/episodes/9-episode-8-september-8-2009":http://ruby5.envylabs.com/episodes/9-episode-8-september-8-2009
163
+ * "http://railslab.newrelic.com/2009/10/23/episode-19-on-the-edge-part-1":http://railslab.newrelic.com/2009/10/23/episode-19-on-the-edge-part-1
164
+
165
+ ****************************************************************************
166
+
167
+ h2. Contributors
168
+
169
+ sriedel did a lot of awesome refactors, added xmpp notification support, and added Hacking.textile about how to extend to bullet plugin.
170
+ flipsasser added Growl, console.log and Rails.log support, very awesome. And he also improved README.
171
+ rainux added group style console.log.
172
+ 2collegebums added some great specs to generate red bar.
173
+
174
+ ****************************************************************************
175
+
176
+ h2. Step by step example
177
+
178
+ 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.
179
+
180
+ 1. setup test environment
181
+
182
+ <pre><code>
183
+ $ rails test
184
+ $ cd test
185
+ $ script/rails g scaffold post name:string
186
+ $ script/rails g scaffold comment name:string post_id:integer
187
+ $ rake db:migrate
188
+ </code></pre>
189
+
190
+ 2. change <code>app/model/post.rb</code> and <code>app/model/comment.rb</code>
191
+
192
+ <pre><code>
193
+ class Post < ActiveRecord::Base
194
+ has_many :comments
195
+ end
196
+
197
+ class Comment < ActiveRecord::Base
198
+ belongs_to :post
199
+ end
200
+ </code></pre>
201
+
202
+ 3. go to <code>script/rails c</code> and execute
203
+
204
+ <pre><code>
205
+ post1 = Post.create(:name => 'first')
206
+ post2 = Post.create(:name => 'second')
207
+ post1.comments.create(:name => 'first')
208
+ post1.comments.create(:name => 'second')
209
+ post2.comments.create(:name => 'third')
210
+ post2.comments.create(:name => 'fourth')
211
+ </code></pre>
212
+
213
+ 4. change the <code>app/views/posts/index.html.erb</code> to produce a N+1 query
214
+
215
+ <pre><code>
216
+ <% @posts.each do |post| %>
217
+ <tr>
218
+ <td><%= post.name %></td>
219
+ <td><%= post.comments.collect(&:name) %></td>
220
+ <td><%= link_to 'Show', post %></td>
221
+ <td><%= link_to 'Edit', edit_post_path(post) %></td>
222
+ <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %></td>
223
+ </tr>
224
+ <% end %>
225
+ </code></pre>
226
+
227
+ 5. add bullet gem to <code>Gemfile</code>
228
+
229
+ <pre><code>
230
+ gem "bullet", "2.0.0.beta.2"
231
+ </code></pre>
232
+
233
+ And run
234
+
235
+ <pre><code>bundle install</code></pre>
236
+
237
+ 6. enable the bullet plugin in development, add a line to <code>config/environments/development.rb</code>
238
+
239
+ <pre><code>
240
+ config.after_initialize do
241
+ Bullet.enable = true
242
+ Bullet.alert = true
243
+ Bullet.bullet_logger = true
244
+ Bullet.console = true
245
+ # Bullet.growl = true
246
+ Bullet.rails_logger = true
247
+ Bullet.disable_browser_cache = true
248
+ end
249
+ </code></pre>
250
+
251
+ 7. start server
252
+
253
+ <pre><code>
254
+ $ script/rails s
255
+ </code></pre>
256
+
257
+ 8. input http://localhost:3000/posts in browser, then you will see a popup alert box says
258
+
259
+ <pre><code>
260
+ The request has unused preload associations as follows:
261
+ None
262
+ The request has N+1 queries as follows:
263
+ model: Post => associations: [comment]
264
+ </code></pre>
265
+
266
+ which means there is a N+1 query from post object to comments associations.
267
+
268
+ In the meanwhile, there's a log appended into <code>log/bullet.log</code> file
269
+
270
+ <pre><code>
271
+ 2010-03-07 14:12:18[INFO] N+1 Query in /posts
272
+ Post => [:comments]
273
+ Add to your finder: :include => [:comments]
274
+ 2010-03-07 14:12:18[INFO] N+1 Query method call stack
275
+ /home/flyerhzm/NetBeansProjects/test_bullet2/app/views/posts/index.html.erb:14:in `_render_template__600522146_80203160_0'
276
+ /home/flyerhzm/NetBeansProjects/test_bullet2/app/views/posts/index.html.erb:11:in `each'
277
+ /home/flyerhzm/NetBeansProjects/test_bullet2/app/views/posts/index.html.erb:11:in `_render_template__600522146_80203160_0'
278
+ /home/flyerhzm/NetBeansProjects/test_bullet2/app/controllers/posts_controller.rb:7:in `index'
279
+ </code></pre>
280
+
281
+ The generated SQLs are
282
+
283
+ <pre><code>
284
+ Post Load (1.0ms) SELECT * FROM "posts"
285
+ Comment Load (0.4ms) SELECT * FROM "comments" WHERE ("comments".post_id = 1)
286
+ Comment Load (0.3ms) SELECT * FROM "comments" WHERE ("comments".post_id = 2)
287
+ </code></pre>
288
+
289
+
290
+ 9. fix the N+1 query, change <code>app/controllers/posts_controller.rb</code> file
291
+
292
+ <pre><code>
293
+ def index
294
+ @posts = Post.includes(:comments)
295
+
296
+ respond_to do |format|
297
+ format.html # index.html.erb
298
+ format.xml { render :xml => @posts }
299
+ end
300
+ end
301
+ </code></pre>
302
+
303
+ 10. refresh http://localhost:3000/posts page, no alert box and no log appended.
304
+
305
+ The generated SQLs are
306
+
307
+ <pre><code>
308
+ Post Load (0.5ms) SELECT * FROM "posts"
309
+ Comment Load (0.5ms) SELECT "comments".* FROM "comments" WHERE ("comments".post_id IN (1,2))
310
+ </code></pre>
311
+
312
+ a N+1 query fixed. Cool!
313
+
314
+ 11. now simulate unused eager loading. Change <code>app/controllers/posts_controller.rb</code> and <code>app/views/posts/index.html.erb</code>
315
+
316
+ <pre><code>
317
+ def index
318
+ @posts = Post.includes(:comments)
319
+
320
+ respond_to do |format|
321
+ format.html # index.html.erb
322
+ format.xml { render :xml => @posts }
323
+ end
324
+ end
325
+ </code></pre>
326
+
327
+ <pre><code>
328
+ <% @posts.each do |post| %>
329
+ <tr>
330
+ <td><%= post.name %></td>
331
+ <td><%= link_to 'Show', post %></td>
332
+ <td><%= link_to 'Edit', edit_post_path(post) %></td>
333
+ <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %></td>
334
+ </tr>
335
+ <% end %>
336
+ </code></pre>
337
+
338
+ 12. refresh http://localhost:3000/posts page, then you will see a popup alert box says
339
+
340
+ <pre><code>
341
+ The request has unused preload associations as follows:
342
+ model: Post => associations: [comment]
343
+ The request has N+1 queries as follows:
344
+ None
345
+ </code></pre>
346
+
347
+ In the meanwhile, there's a log appended into <code>log/bullet.log</code> file
348
+
349
+ <pre><code>
350
+ 2009-08-25 21:13:22[INFO] Unused preload associations: PATH_INFO: /posts; model: Post => associations: [comments]·
351
+ Remove from your finder: :include => [:comments]
352
+ </code></pre>
353
+
354
+ 13. simulate counter_cache. Change <code>app/controllers/posts_controller.rb</code> and <code>app/views/posts/index.html.erb</code>
355
+
356
+ <pre><code>
357
+ def index
358
+ @posts = Post.all
359
+
360
+ respond_to do |format|
361
+ format.html # index.html.erb
362
+ format.xml { render :xml => @posts }
363
+ end
364
+ end
365
+ </code></pre>
366
+
367
+ <pre><code>
368
+ <% @posts.each do |post| %>
369
+ <tr>
370
+ <td><%= post.name %></td>
371
+ <td><%= post.comments.size %></td>
372
+ <td><%= link_to 'Show', post %></td>
373
+ <td><%= link_to 'Edit', edit_post_path(post) %></td>
374
+ <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %></td>
375
+ </tr>
376
+ <% end %>
377
+ </code></pre>
378
+
379
+ 14. refresh http://localhost:3000/posts page, then you will see a popup alert box says
380
+
381
+ <pre><code>
382
+ Need counter cache
383
+ Post => [:comments]
384
+ </code></pre>
385
+
386
+ In the meanwhile, there's a log appended into <code>log/bullet.log</code> file.
387
+
388
+ <pre><code>
389
+ 2009-09-11 10:07:10[INFO] Need Counter Cache
390
+ Post => [:comments]
391
+ </code></pre>
392
+
393
+ ****************************************************************************
394
+
395
+
396
+ Copyright (c) 2009 - 2010 Richard Huang (flyerhzm@gmail.com), released under the MIT license
@@ -0,0 +1,403 @@
1
+ h1. Bullet
2
+
3
+ The Bullet plugin/gem 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), when you're using eager loading that isn't necessary and when you should use counter cache.
4
+
5
+ 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.
6
+
7
+ The Bullet plugin/gem now supports rails 2.1, 2.2 and 2.3, tested in rails 2.1.2, 2.2.2 and 2.3.2.
8
+
9
+ ****************************************************************************
10
+
11
+ h2. Change
12
+
13
+ There is a large refactor from gem 1.4 to 1.5, so if you upgrade to 1.5 gem, please read the Configuration section again and rewrite your configuration.
14
+
15
+ ****************************************************************************
16
+
17
+ h2. Contributors
18
+
19
+ sriedel did a lot of awesome refactors, added xmpp notification support, and added Hacking.textile about how to extend to bullet plugin.
20
+ flipsasser added Growl, console.log and Rails.log support, very awesome. And he also improved README.
21
+ rainux added group style console.log.
22
+ 2collegebums added some great specs to generate red bar.
23
+
24
+ ****************************************************************************
25
+
26
+ h2. Install
27
+
28
+ You can install it as a gem:
29
+ <pre><code>
30
+ sudo gem install bullet
31
+ </code></pre>
32
+
33
+ ****************************************************************************
34
+
35
+ h2. Configuration
36
+
37
+ Bullet won't do ANYTHING unless you tell it to explicitly. Append to <code>config/environments/development.rb</code> initializer with the following code:
38
+ <pre><code>
39
+ config.after_initialize do
40
+ Bullet.enable = true
41
+ Bullet.alert = true
42
+ Bullet.bullet_logger = true
43
+ Bullet.console = true
44
+ Bullet.growl = true
45
+ Bullet.rails_logger = true
46
+ Bullet.disable_browser_cache = true
47
+ Bullet.xmpp = { :account => 'bullets_account@jabber.org',
48
+ :password => 'bullets_password_for_jabber',
49
+ :receiver => 'your_account@jabber.org',
50
+ :show_online_status => true }
51
+ end
52
+ </code></pre>
53
+
54
+ The notifier of bullet is a wrap of "uniform_notifier":https://github.com/flyerhzm/uniform_notifier
55
+
56
+ The code above will enable all five of the Bullet notification systems:
57
+ * <code>Bullet.enable</code>: enable Bullet plugin/gem, otherwise do nothing
58
+ * <code>Bullet.alert</code>: pop up a JavaScript alert in the browser
59
+ * <code>Bullet.bullet_logger</code>: log to the Bullet log file (RAILS_ROOT/log/bullet.log)
60
+ * <code>Bullet.rails_logger</code>: add warnings directly to the Rails log
61
+ * <code>Bullet.console</code>: log warnings to your browser's console.log (Safari/Webkit browsers or Firefox w/Firebug installed)
62
+ * <code>Bullet.growl</code>: pop up Growl warnings if your system has Growl installed. Requires a little bit of configuration
63
+ * <code>Bullet.disable_browser_cache</code>: disable browser cache which usually causes unexpected problems
64
+
65
+ ****************************************************************************
66
+
67
+ h2. Log
68
+
69
+ The Bullet log <code>log/bullet.log</code> will look something like this:
70
+
71
+ * N+1 Query:
72
+ <pre><code>
73
+ 2009-08-25 20:40:17[INFO] N+1 Query: PATH_INFO: /posts; model: Post => associations: [comments]·
74
+ Add to your finder: :include => [:comments]
75
+ 2009-08-25 20:40:17[INFO] N+1 Query: method call stack:·
76
+ /Users/richard/Downloads/test/app/views/posts/index.html.erb:11:in `_run_erb_app47views47posts47index46html46erb'
77
+ /Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `each'
78
+ /Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `_run_erb_app47views47posts47index46html46erb'
79
+ /Users/richard/Downloads/test/app/controllers/posts_controller.rb:7:in `index'
80
+ </code></pre>
81
+
82
+ 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.
83
+
84
+ * Unused eager loading:
85
+ <pre><code>
86
+ 2009-08-25 20:53:56[INFO] Unused eager loadings: PATH_INFO: /posts; model: Post => associations: [comments]·
87
+ Remove from your finder: :include => [:comments]
88
+ </code></pre>
89
+
90
+ These two lines are notifications that unused eager loadings have been encountered.
91
+
92
+ * Need counter cache:
93
+ <pre><code>
94
+ 2009-09-11 09:46:50[INFO] Need Counter Cache
95
+ Post => [:comments]
96
+ </code></pre>
97
+
98
+ ****************************************************************************
99
+
100
+ h2. Growl Support
101
+
102
+ To get Growl support up-and-running for Bullet, follow the steps below:
103
+ * Install the ruby-growl gem: <code>gem install ruby-growl</code>
104
+ * Open the Growl preference pane in Systems Preferences
105
+ * Click the "Network" tab
106
+ * Make sure both "Listen for incoming notifications" and "Allow remote application registration" are checked. *Note*: If you set a password, you will need to set <code>Bullet.growl_password = { :password => 'growl password' }</code> in the config file.
107
+ * Restart Growl ("General" tab -> Stop Growl -> Start Growl)
108
+ * Boot up your application. Bullet will automatically send a Growl notification when Growl is turned on. If you do not see it when your application loads, make sure it is enabled in your initializer and double-check the steps above.
109
+
110
+ ****************************************************************************
111
+
112
+ h2. Ruby 1.9 issue
113
+
114
+ ruby-growl gem has an issue about md5 in ruby 1.9, if you use growl and ruby 1.9, check this gist http://gist.github.com/300184
115
+
116
+ ****************************************************************************
117
+
118
+ h2. XMPP/Jabber Support
119
+
120
+ To get XMPP support up-and-running for Bullet, follow the steps below:
121
+ * Install the xmpp4r gem: <code>sudo gem install xmpp4r</code>
122
+ * Make both the bullet and the receipient account add each other as contacts.
123
+ This will require you to manually log into both accounts, add each other
124
+ as contact and confirm each others contact request.
125
+ * Boot up your application. Bullet will automatically send an XMPP notification when XMPP is turned on.
126
+
127
+ ****************************************************************************
128
+
129
+ h2. Important
130
+
131
+ If you encounter the following errors in development environment:
132
+
133
+ <pre><code>
134
+ You might have expected an instance of Array.
135
+ The error occurred while evaluating nil.include?
136
+ /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/attribute_methods.rb:142:in `create_time_zone_conversion_attribute?'
137
+ /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/attribute_methods.rb:75:in `define_attribute_methods'
138
+ /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/attribute_methods.rb:71:in `each'
139
+ /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/attribute_methods.rb:71:in `define_attribute_methods'
140
+ /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/attribute_methods.rb:242:in `method_missing'
141
+ </code></pre>
142
+
143
+ Or any strange behavior of bullet plugin/gem, *please disable your browser's cache*.
144
+
145
+ ****************************************************************************
146
+
147
+ h2. Advance
148
+
149
+ The bullet plugin/gem use rack middleware for http request. If you want to bullet for without http server, such as job server. You can do like this:
150
+
151
+ <pre><code>
152
+ Bullet.start_request if Bullet.enable?
153
+ # run job
154
+ if Bullet.enable?
155
+ Bullet.growl_notification
156
+ Bullet.log_notification('JobServer: ')
157
+ Bullet.end_request
158
+ end
159
+ </code></pre>
160
+
161
+ Or you want to use it in test mode
162
+
163
+ <pre><code>
164
+ before(:each)
165
+ Bullet.start_request if Bullet.enable?
166
+ end
167
+
168
+ after(:each)
169
+ if Bullet.enable?
170
+ Bullet.perform_out_of_channel_notifications
171
+ Bullet.end_request
172
+ end
173
+ end
174
+ </code></pre>
175
+
176
+ Don't forget enabling bullet in test environment.
177
+
178
+ ****************************************************************************
179
+
180
+ h2. Links
181
+
182
+ * "http://weblog.rubyonrails.org/2009/10/22/community-highlights":http://weblog.rubyonrails.org/2009/10/22/community-highlights
183
+ * "http://ruby5.envylabs.com/episodes/9-episode-8-september-8-2009":http://ruby5.envylabs.com/episodes/9-episode-8-september-8-2009
184
+ * "http://railslab.newrelic.com/2009/10/23/episode-19-on-the-edge-part-1":http://railslab.newrelic.com/2009/10/23/episode-19-on-the-edge-part-1
185
+
186
+ ****************************************************************************
187
+
188
+ h2. Step by step example
189
+
190
+ 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.
191
+
192
+ 1. setup test environment
193
+
194
+ <pre><code>
195
+ $ rails test
196
+ $ cd test
197
+ $ script/generate scaffold post name:string
198
+ $ script/generate scaffold comment name:string post_id:integer
199
+ $ rake db:migrate
200
+ </code></pre>
201
+
202
+ 2. change <code>app/model/post.rb</code> and <code>app/model/comment.rb</code>
203
+
204
+ <pre><code>
205
+ class Post < ActiveRecord::Base
206
+ has_many :comments
207
+ end
208
+
209
+ class Comment < ActiveRecord::Base
210
+ belongs_to :post
211
+ end
212
+ </code></pre>
213
+
214
+ 3. go to script/console and execute
215
+
216
+ <pre><code>
217
+ post1 = Post.create(:name => 'first')
218
+ post2 = Post.create(:name => 'second')
219
+ post1.comments.create(:name => 'first')
220
+ post1.comments.create(:name => 'second')
221
+ post2.comments.create(:name => 'third')
222
+ post2.comments.create(:name => 'fourth')
223
+ </code></pre>
224
+
225
+ 4. change the <code>app/views/posts/index.html.erb</code> to produce a N+1 query
226
+
227
+ <pre><code>
228
+ <% @posts.each do |post| %>
229
+ <tr>
230
+ <td><%=h post.name %></td>
231
+ <td><%= post.comments.collect(&:name) %></td>
232
+ <td><%= link_to 'Show', post %></td>
233
+ <td><%= link_to 'Edit', edit_post_path(post) %></td>
234
+ <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %></td>
235
+ </tr>
236
+ <% end %>
237
+ </code></pre>
238
+
239
+ 5. add bullet plugin
240
+
241
+ <pre><code>
242
+ $ script/plugin install git://github.com/flyerhzm/bullet.git
243
+ </code></pre>
244
+
245
+ 6. enable the bullet plugin in development, add a line to <code>config/environments/development.rb</code>
246
+
247
+ <pre><code>
248
+ config.after_initialize do
249
+ Bullet.enable = true
250
+ Bullet.alert = true
251
+ Bullet.bullet_logger = true
252
+ Bullet.console = true
253
+ # Bullet.growl = true
254
+ Bullet.rails_logger = true
255
+ Bullet.disable_browser_cache = true
256
+ end
257
+ </code></pre>
258
+
259
+ 7. start server
260
+
261
+ <pre><code>
262
+ $ script/server
263
+ </code></pre>
264
+
265
+ 8. input http://localhost:3000/posts in browser, then you will see a popup alert box says
266
+
267
+ <pre><code>
268
+ The request has unused preload associations as follows:
269
+ None
270
+ The request has N+1 queries as follows:
271
+ model: Post => associations: [comment]
272
+ </code></pre>
273
+
274
+ which means there is a N+1 query from post object to comments associations.
275
+
276
+ In the meanwhile, there's a log appended into <code>log/bullet.log</code> file
277
+
278
+ <pre><code>
279
+ 2009-08-20 09:12:19[INFO] N+1 Query: PATH_INFO: /posts; model: Post => assocations: [comments]
280
+ Add your finder: :include => [:comments]
281
+ 2009-08-20 09:12:19[INFO] N+1 Query: method call stack:
282
+ /Users/richard/Downloads/test/app/views/posts/index.html.erb:11:in `_run_erb_app47views47posts47index46html46erb'
283
+ /Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `each'
284
+ /Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `_run_erb_app47views47posts47index46html46erb'
285
+ /Users/richard/Downloads/test/app/controllers/posts_controller.rb:7:in `index'
286
+ </code></pre>
287
+
288
+ The generated SQLs are
289
+
290
+ <pre><code>
291
+ Post Load (1.0ms) SELECT * FROM "posts"
292
+ Comment Load (0.4ms) SELECT * FROM "comments" WHERE ("comments".post_id = 1)
293
+ Comment Load (0.3ms) SELECT * FROM "comments" WHERE ("comments".post_id = 2)
294
+ </code></pre>
295
+
296
+
297
+ 9. fix the N+1 query, change <code>app/controllers/posts_controller.rb</code> file
298
+
299
+ <pre><code>
300
+ def index
301
+ @posts = Post.find(:all, :include => :comments)
302
+
303
+ respond_to do |format|
304
+ format.html # index.html.erb
305
+ format.xml { render :xml => @posts }
306
+ end
307
+ end
308
+ </code></pre>
309
+
310
+ 10. refresh http://localhost:3000/posts page, no alert box and no log appended.
311
+
312
+ The generated SQLs are
313
+
314
+ <pre><code>
315
+ Post Load (0.5ms) SELECT * FROM "posts"
316
+ Comment Load (0.5ms) SELECT "comments".* FROM "comments" WHERE ("comments".post_id IN (1,2))
317
+ </code></pre>
318
+
319
+ a N+1 query fixed. Cool!
320
+
321
+ 11. now simulate unused eager loading. Change <code>app/controllers/posts_controller.rb</code> and <code>app/views/posts/index.html.erb</code>
322
+
323
+ <pre><code>
324
+ def index
325
+ @posts = Post.find(:all, :include => :comments)
326
+
327
+ respond_to do |format|
328
+ format.html # index.html.erb
329
+ format.xml { render :xml => @posts }
330
+ end
331
+ end
332
+ </code></pre>
333
+
334
+ <pre><code>
335
+ <% @posts.each do |post| %>
336
+ <tr>
337
+ <td><%=h post.name %></td>
338
+ <td><%= link_to 'Show', post %></td>
339
+ <td><%= link_to 'Edit', edit_post_path(post) %></td>
340
+ <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %></td>
341
+ </tr>
342
+ <% end %>
343
+ </code></pre>
344
+
345
+ 12. refresh http://localhost:3000/posts page, then you will see a popup alert box says
346
+
347
+ <pre><code>
348
+ The request has unused preload associations as follows:
349
+ model: Post => associations: [comment]
350
+ The request has N+1 queries as follows:
351
+ None
352
+ </code></pre>
353
+
354
+ In the meanwhile, there's a log appended into <code>log/bullet.log</code> file
355
+
356
+ <pre><code>
357
+ 2009-08-25 21:13:22[INFO] Unused preload associations: PATH_INFO: /posts; model: Post => associations: [comments]·
358
+ Remove from your finder: :include => [:comments]
359
+ </code></pre>
360
+
361
+ 13. simulate counter_cache. Change <code>app/controllers/posts_controller.rb</code> and <code>app/views/posts/index.html.erb</code>
362
+
363
+ <pre><code>
364
+ def index
365
+ @posts = Post.find(:all)
366
+
367
+ respond_to do |format|
368
+ format.html # index.html.erb
369
+ format.xml { render :xml => @posts }
370
+ end
371
+ end
372
+ </code></pre>
373
+
374
+ <pre><code>
375
+ <% @posts.each do |post| %>
376
+ <tr>
377
+ <td><%=h post.name %></td>
378
+ <td><%=h post.comments.size %></td>
379
+ <td><%= link_to 'Show', post %></td>
380
+ <td><%= link_to 'Edit', edit_post_path(post) %></td>
381
+ <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %></td>
382
+ </tr>
383
+ <% end %>
384
+ </code></pre>
385
+
386
+ 14. refresh http://localhost:3000/posts page, then you will see a popup alert box says
387
+
388
+ <pre><code>
389
+ Need counter cache
390
+ Post => [:comments]
391
+ </code></pre>
392
+
393
+ In the meanwhile, there's a log appended into <code>log/bullet.log</code> file.
394
+
395
+ <pre><code>
396
+ 2009-09-11 10:07:10[INFO] Need Counter Cache
397
+ Post => [:comments]
398
+ </code></pre>
399
+
400
+ ****************************************************************************
401
+
402
+
403
+ Copyright (c) 2009 - 2010 Richard Huang (flyerhzm@gmail.com), released under the MIT license