rack-bug 0.2.1

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.
Files changed (77) hide show
  1. data/.gitignore +3 -0
  2. data/History.txt +0 -0
  3. data/MIT-LICENSE.txt +19 -0
  4. data/README.rdoc +29 -0
  5. data/Rakefile +36 -0
  6. data/VERSION +1 -0
  7. data/lib/rack/bug.rb +43 -0
  8. data/lib/rack/bug/options.rb +89 -0
  9. data/lib/rack/bug/panel.rb +50 -0
  10. data/lib/rack/bug/panel_app.rb +33 -0
  11. data/lib/rack/bug/panels/active_record_panel.rb +45 -0
  12. data/lib/rack/bug/panels/active_record_panel/activerecord_extensions.rb +18 -0
  13. data/lib/rack/bug/panels/cache_panel.rb +50 -0
  14. data/lib/rack/bug/panels/cache_panel/memcache_extension.rb +129 -0
  15. data/lib/rack/bug/panels/cache_panel/panel_app.rb +48 -0
  16. data/lib/rack/bug/panels/cache_panel/stats.rb +97 -0
  17. data/lib/rack/bug/panels/log_panel.rb +39 -0
  18. data/lib/rack/bug/panels/log_panel/rails_extension.rb +11 -0
  19. data/lib/rack/bug/panels/memory_panel.rb +27 -0
  20. data/lib/rack/bug/panels/rails_info_panel.rb +23 -0
  21. data/lib/rack/bug/panels/redis_panel.rb +44 -0
  22. data/lib/rack/bug/panels/redis_panel/redis_extension.rb +14 -0
  23. data/lib/rack/bug/panels/redis_panel/stats.rb +48 -0
  24. data/lib/rack/bug/panels/request_variables_panel.rb +25 -0
  25. data/lib/rack/bug/panels/sql_panel.rb +55 -0
  26. data/lib/rack/bug/panels/sql_panel/panel_app.rb +37 -0
  27. data/lib/rack/bug/panels/sql_panel/query.rb +73 -0
  28. data/lib/rack/bug/panels/sql_panel/sql_extension.rb +11 -0
  29. data/lib/rack/bug/panels/templates_panel.rb +44 -0
  30. data/lib/rack/bug/panels/templates_panel/actionview_extension.rb +12 -0
  31. data/lib/rack/bug/panels/templates_panel/rendering.rb +67 -0
  32. data/lib/rack/bug/panels/templates_panel/trace.rb +34 -0
  33. data/lib/rack/bug/panels/timer_panel.rb +40 -0
  34. data/lib/rack/bug/params_signature.rb +65 -0
  35. data/lib/rack/bug/public/__rack_bug__/bookmarklet.html +10 -0
  36. data/lib/rack/bug/public/__rack_bug__/bookmarklet.js +215 -0
  37. data/lib/rack/bug/public/__rack_bug__/bug.css +211 -0
  38. data/lib/rack/bug/public/__rack_bug__/bug.js +84 -0
  39. data/lib/rack/bug/public/__rack_bug__/jquery-1.3.2.js +4376 -0
  40. data/lib/rack/bug/public/__rack_bug__/jquery.tablesorter.min.js +1 -0
  41. data/lib/rack/bug/public/__rack_bug__/spinner.gif +0 -0
  42. data/lib/rack/bug/render.rb +66 -0
  43. data/lib/rack/bug/toolbar.rb +137 -0
  44. data/lib/rack/bug/views/error.html.erb +16 -0
  45. data/lib/rack/bug/views/panels/active_record.html.erb +17 -0
  46. data/lib/rack/bug/views/panels/cache.html.erb +93 -0
  47. data/lib/rack/bug/views/panels/execute_sql.html.erb +32 -0
  48. data/lib/rack/bug/views/panels/explain_sql.html.erb +32 -0
  49. data/lib/rack/bug/views/panels/log.html.erb +23 -0
  50. data/lib/rack/bug/views/panels/profile_sql.html.erb +32 -0
  51. data/lib/rack/bug/views/panels/rails_info.html.erb +19 -0
  52. data/lib/rack/bug/views/panels/redis.html.erb +32 -0
  53. data/lib/rack/bug/views/panels/request_variables.html.erb +107 -0
  54. data/lib/rack/bug/views/panels/sql.html.erb +43 -0
  55. data/lib/rack/bug/views/panels/templates.html.erb +7 -0
  56. data/lib/rack/bug/views/panels/timer.html.erb +19 -0
  57. data/lib/rack/bug/views/panels/view_cache.html.erb +19 -0
  58. data/lib/rack/bug/views/redirect.html.erb +16 -0
  59. data/lib/rack/bug/views/toolbar.html.erb +42 -0
  60. data/rack-bug.gemspec +126 -0
  61. data/spec/fixtures/config.ru +8 -0
  62. data/spec/fixtures/dummy_panel.rb +2 -0
  63. data/spec/fixtures/sample_app.rb +29 -0
  64. data/spec/rack/bug/panels/active_record_panel_spec.rb +30 -0
  65. data/spec/rack/bug/panels/cache_panel_spec.rb +159 -0
  66. data/spec/rack/bug/panels/log_panel_spec.rb +25 -0
  67. data/spec/rack/bug/panels/memory_panel_spec.rb +21 -0
  68. data/spec/rack/bug/panels/rails_info_panel_spec.rb +25 -0
  69. data/spec/rack/bug/panels/redis_panel_spec.rb +57 -0
  70. data/spec/rack/bug/panels/sql_panel_spec.rb +136 -0
  71. data/spec/rack/bug/panels/templates_panel_spec.rb +71 -0
  72. data/spec/rack/bug/panels/timer_panel_spec.rb +38 -0
  73. data/spec/rack/toolbar_spec.rb +100 -0
  74. data/spec/rcov.opts +1 -0
  75. data/spec/spec.opts +1 -0
  76. data/spec/spec_helper.rb +70 -0
  77. metadata +143 -0
@@ -0,0 +1,2 @@
1
+ class DummyPanel < Rack::Bug::Panel
2
+ end
@@ -0,0 +1,29 @@
1
+ require "sinatra/base"
2
+
3
+ class SampleApp < Sinatra::Default
4
+
5
+ get "/redirect" do
6
+ redirect "/"
7
+ end
8
+
9
+ get "/error" do
10
+ raise "Error!"
11
+ end
12
+
13
+ get "/" do
14
+ if params[:content_type]
15
+ headers["Content-Type"] = params[:content_type]
16
+ end
17
+
18
+ <<-HTML
19
+ <html>
20
+ <head>
21
+ </head>
22
+ <body>
23
+ <p>Hello</p>
24
+ </body>
25
+ </html>
26
+ HTML
27
+ end
28
+
29
+ end
@@ -0,0 +1,30 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
+
3
+ module Rack::Bug
4
+ describe ActiveRecordPanel do
5
+ before do
6
+ ActiveRecordPanel.reset
7
+ header "rack-bug.panel_classes", [ActiveRecordPanel]
8
+ end
9
+
10
+ describe "heading" do
11
+ it "displays the total number of instantiated AR objects" do
12
+ ActiveRecordPanel.record("User")
13
+ ActiveRecordPanel.record("Group")
14
+ response = get "/"
15
+ response.should have_heading("2 AR Objects")
16
+ end
17
+ end
18
+
19
+ describe "content" do
20
+ it "displays the count of instantiated objects for each class" do
21
+ ActiveRecordPanel.record("User")
22
+ ActiveRecordPanel.record("User")
23
+ ActiveRecordPanel.record("Group")
24
+ response = get "/"
25
+ response.should have_row("#active_record", "User", "2")
26
+ response.should have_row("#active_record", "Group", "1")
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,159 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
+
3
+ module Rack::Bug
4
+ describe CachePanel do
5
+ before do
6
+ CachePanel.reset
7
+ header "rack-bug.panel_classes", [CachePanel]
8
+ end
9
+
10
+ describe "heading" do
11
+ it "displays the total memcache time" do
12
+ response = get "/"
13
+ response.should have_heading("Cache: 0.00ms")
14
+ end
15
+ end
16
+
17
+ describe "content" do
18
+ describe "usage table" do
19
+ it "displays the total number of memcache calls" do
20
+ CachePanel.record(:get, "user:1") { }
21
+ response = get "/"
22
+
23
+ # This causes a bus error:
24
+ # response.should have_selector("th:content('Total Calls') + td", :content => "1")
25
+
26
+ response.should have_row("#cache_usage", "Total Calls", "1")
27
+ end
28
+
29
+ it "displays the total memcache time" do
30
+ response = get "/"
31
+ response.should have_row("#cache_usage", "Total Time", "0.00ms")
32
+ end
33
+
34
+ it "dispays the number of memcache hits" do
35
+ CachePanel.record(:get, "user:1") { }
36
+ response = get "/"
37
+ response.should have_row("#cache_usage", "Hits", "0")
38
+ end
39
+
40
+ it "displays the number of memcache misses" do
41
+ CachePanel.record(:get, "user:1") { }
42
+ response = get "/"
43
+ response.should have_row("#cache_usage", "Misses", "1")
44
+ end
45
+
46
+ it "displays the number of memcache gets" do
47
+ CachePanel.record(:get, "user:1") { }
48
+ response = get "/"
49
+ response.should have_row("#cache_usage", "gets", "1")
50
+ end
51
+
52
+ it "displays the number of memcache sets" do
53
+ CachePanel.record(:set, "user:1") { }
54
+ response = get "/"
55
+ response.should have_row("#cache_usage", "sets", "1")
56
+ end
57
+
58
+ it "displays the number of memcache deletes" do
59
+ CachePanel.record(:delete, "user:1") { }
60
+ response = get "/"
61
+ response.should have_row("#cache_usage", "deletes", "1")
62
+ end
63
+
64
+ it "displays the number of memcache get_multis" do
65
+ CachePanel.record(:get_multi, "user:1", "user:2") { }
66
+ response = get "/"
67
+ response.should have_row("#cache_usage", "get_multis", "1")
68
+ end
69
+ end
70
+
71
+ describe "breakdown" do
72
+ it "displays each memcache operation" do
73
+ CachePanel.record(:get, "user:1") { }
74
+ response = get "/"
75
+ response.should have_row("#cache_breakdown", "get")
76
+ end
77
+
78
+ it "displays the time for each memcache call" do
79
+ CachePanel.record(:get, "user:1") { }
80
+ response = get "/"
81
+ response.should have_row("#cache_breakdown", "user:1", TIME_MS_REGEXP)
82
+ end
83
+
84
+ it "displays the keys for each memcache call" do
85
+ CachePanel.record(:get, "user:1") { }
86
+ response = get "/"
87
+ response.should have_row("#cache_breakdown", "user:1", "get")
88
+ end
89
+ end
90
+ end
91
+
92
+ describe "expire_all" do
93
+ before do
94
+ header "rack-bug.secret_key", 'abc'
95
+ end
96
+
97
+ it "expires the cache keys" do
98
+ Rails.stub!(:cache => mock("cache"))
99
+ Rails.cache.should_receive(:delete).with("user:1")
100
+ Rails.cache.should_receive(:delete).with("user:2")
101
+ Rails.cache.should_receive(:delete).with("user:3")
102
+ Rails.cache.should_receive(:delete).with("user:4")
103
+
104
+ get "/__rack_bug__/delete_cache_list",
105
+ :keys_1 => "user:1", :keys_2 => "user:2", :keys_3 => "user:3", :keys_4 => "user:4",
106
+ :hash => "c367b76e0199c308862a3afd8fba32b8715e7976"
107
+ end
108
+
109
+ it "returns OK" do
110
+ Rails.stub!(:cache => mock("cache", :delete => nil))
111
+ response = get "/__rack_bug__/delete_cache_list",
112
+ :keys_1 => "user:1", :keys_2 => "user:2", :keys_3 => "user:3", :keys_4 => "user:4",
113
+ :hash => "c367b76e0199c308862a3afd8fba32b8715e7976"
114
+ response.should contain("OK")
115
+ end
116
+ end
117
+
118
+ describe "expire" do
119
+ before do
120
+ header "rack-bug.secret_key", 'abc'
121
+ end
122
+
123
+ it "expires the cache key" do
124
+ Rails.stub!(:cache => mock("cache"))
125
+ Rails.cache.should_receive(:delete).with("user:1")
126
+ get "/__rack_bug__/delete_cache", :key => "user:1",
127
+ :hash => "f87215442d312d8e42cf51e6b66fc3eb3d59ac74"
128
+ end
129
+
130
+ it "returns OK" do
131
+ Rails.stub!(:cache => mock("cache", :delete => nil))
132
+ response = get "/__rack_bug__/delete_cache", :key => "user:1",
133
+ :hash => "f87215442d312d8e42cf51e6b66fc3eb3d59ac74"
134
+ response.should contain("OK")
135
+ end
136
+ end
137
+
138
+ describe "view_cache" do
139
+ before do
140
+ header "rack-bug.secret_key", 'abc'
141
+ end
142
+
143
+ it "renders the cache key" do
144
+ Rails.stub!(:cache => mock("cache", :read => "cache body"))
145
+ response = get "/__rack_bug__/view_cache", :key => "user:1",
146
+ :hash => "f87215442d312d8e42cf51e6b66fc3eb3d59ac74"
147
+ response.should contain("cache body")
148
+ end
149
+
150
+ it "renders non-String cache values properly" do
151
+ Rails.stub!(:cache => mock("cache", :read => [1, 2]))
152
+ response = get "/__rack_bug__/view_cache", :key => "user:1",
153
+ :hash => "f87215442d312d8e42cf51e6b66fc3eb3d59ac74"
154
+ response.should contain("[1, 2]")
155
+ end
156
+ end
157
+
158
+ end
159
+ end
@@ -0,0 +1,25 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
+
3
+ module Rack::Bug
4
+ describe LogPanel do
5
+ before do
6
+ LogPanel.reset
7
+ header "rack-bug.panel_classes", [LogPanel]
8
+ end
9
+
10
+ describe "heading" do
11
+ it "displays 'Log'" do
12
+ response = get "/"
13
+ response.should have_heading("Log")
14
+ end
15
+ end
16
+
17
+ describe "content" do
18
+ it "displays recorded log lines" do
19
+ LogPanel.record("This is a logged message")
20
+ response = get "/"
21
+ response.should contain("This is a logged message")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
+
3
+ module Rack::Bug
4
+ describe MemoryPanel do
5
+ before do
6
+ header "rack-bug.panel_classes", [MemoryPanel]
7
+ end
8
+
9
+ describe "heading" do
10
+ it "displays the total memory" do
11
+ response = get "/"
12
+ response.should have_heading(/\d+ KB total/)
13
+ end
14
+
15
+ it "displays the memory change during the request" do
16
+ response = get "/"
17
+ response.should have_heading(/\d+ KB Δ/)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
+
3
+ module Rack::Bug
4
+ describe RailsInfoPanel do
5
+ before do
6
+ header "rack-bug.panel_classes", [RailsInfoPanel]
7
+ end
8
+
9
+ describe "heading" do
10
+ it "displays the Rails version" do
11
+ Rails.stub!(:version => "v2.3.0")
12
+ response = get "/"
13
+ response.should have_heading("Rails v2.3.0")
14
+ end
15
+ end
16
+
17
+ describe "content" do
18
+ it "displays the Rails::Info properties" do
19
+ Rails::Info.stub!(:properties => [["Name", "Value"]])
20
+ response = get "/"
21
+ response.should have_row("#rails_info", "Name", "Value")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,57 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
+ $LOADED_FEATURES << "redis.rb" #avoid dependency on redis
3
+
4
+ module Rack::Bug
5
+ describe RedisPanel do
6
+ before do
7
+ RedisPanel.reset
8
+ header "rack-bug.panel_classes", [RedisPanel]
9
+ end
10
+
11
+ describe "heading" do
12
+ it "displays the total redis time" do
13
+ response = get "/"
14
+ response.should have_heading("Redis: 0.00ms")
15
+ end
16
+ end
17
+
18
+ describe "content" do
19
+ describe "usage table" do
20
+ it "displays the total number of redis calls" do
21
+ RedisPanel.record(["get, user:1"]) { }
22
+ response = get "/"
23
+
24
+ # This causes a bus error:
25
+ # response.should have_selector("th:content('Total Calls') + td", :content => "1")
26
+
27
+ response.should have_row("#redis_usage", "Total Calls", "1")
28
+ end
29
+
30
+ it "displays the total redis time" do
31
+ response = get "/"
32
+ response.should have_row("#redis_usage", "Total Time", "0.00ms")
33
+ end
34
+ end
35
+
36
+ describe "breakdown" do
37
+ it "displays each redis operation" do
38
+ RedisPanel.record(["get, user:1"]) { }
39
+ response = get "/"
40
+ response.should have_row("#redis_breakdown", "get")
41
+ end
42
+
43
+ it "displays the time for redis call" do
44
+ RedisPanel.record(["get, user:1"]) { }
45
+ response = get "/"
46
+ response.should have_row("#redis_breakdown", "user:1", TIME_MS_REGEXP)
47
+ end
48
+
49
+ it "displays the arguments for each redis call" do
50
+ RedisPanel.record(["get, user:1"]) { }
51
+ response = get "/"
52
+ response.should have_row("#redis_breakdown", "user:1", "get")
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,136 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
+
3
+ module Rack::Bug
4
+ describe SQLPanel do
5
+ before do
6
+ SQLPanel.reset
7
+ header "rack-bug.panel_classes", [SQLPanel]
8
+ end
9
+
10
+ describe "heading" do
11
+ it "displays the total SQL query count" do
12
+ SQLPanel.record("SELECT NOW();") { }
13
+ response = get "/"
14
+ response.should have_heading("1 Queries")
15
+ end
16
+
17
+ it "displays the total SQL time" do
18
+ SQLPanel.record("SELECT NOW();") { }
19
+ response = get "/"
20
+ response.should have_heading(/Queries \(\d+\.\d{2}ms\)/)
21
+ end
22
+ end
23
+
24
+ describe "content" do
25
+ it "displays each executed SQL query" do
26
+ SQLPanel.record("SELECT NOW();") { }
27
+ response = get "/"
28
+ response.should have_row("#sql", "SELECT NOW();")
29
+ end
30
+
31
+ it "displays the time of each executed SQL query" do
32
+ SQLPanel.record("SELECT NOW();") { }
33
+ response = get "/"
34
+ response.should have_row("#sql", "SELECT NOW();", TIME_MS_REGEXP)
35
+ end
36
+ end
37
+
38
+ def stub_result(results = [[]])
39
+ columns = results.first
40
+ fields = columns.map { |c| stub("field", :name => c) }
41
+ rows = results[1..-1]
42
+
43
+ result = stub("result", :fetch_fields => fields)
44
+ result.stub!(:each).and_yield(*rows)
45
+ return result
46
+ end
47
+
48
+ def expect_query(sql, results)
49
+ conn = stub("connection")
50
+ ActiveRecord::Base.stub!(:connection => conn)
51
+ conn.should_receive(:execute).with(sql).and_return(stub_result(results))
52
+ end
53
+
54
+ describe "execute_sql" do
55
+ it "displays the query results" do
56
+ header "rack-bug.secret_key", "abc"
57
+ expect_query "SELECT username FROM users",
58
+ [["username"],
59
+ ["bryan"]]
60
+
61
+ response = get "/__rack_bug__/execute_sql", :query => "SELECT username FROM users",
62
+ :hash => "6f286f55b75716e5c91f16d77d09fa73b353ebc1"
63
+ response.should contain("SELECT username FROM users")
64
+ response.should be_ok
65
+ end
66
+
67
+ it "is forbidden when the hash is missing or wrong" do
68
+ header "rack-bug.secret_key", 'abc'
69
+
70
+ lambda {
71
+ get "/__rack_bug__/execute_sql", :query => "SELECT username FROM users",
72
+ :hash => "foobar"
73
+ }.should raise_error(SecurityError)
74
+ end
75
+
76
+ it "is not available when the rack-bug.secret_key is nil" do
77
+ header "rack-bug.secret_key", nil
78
+
79
+ lambda {
80
+ get "/__rack_bug__/execute_sql", :query => "SELECT username FROM users",
81
+ :hash => "6f286f55b75716e5c91f16d77d09fa73b353ebc1"
82
+ }.should raise_error(SecurityError)
83
+ end
84
+
85
+ it "is not available when the rack-bug.secret_key is an empty string" do
86
+ header "rack-bug.secret_key", ""
87
+
88
+ lambda {
89
+ get "/__rack_bug__/execute_sql", :query => "SELECT username FROM users",
90
+ :hash => "6f286f55b75716e5c91f16d77d09fa73b353ebc1"
91
+ }.should raise_error(SecurityError)
92
+ end
93
+ end
94
+
95
+ describe "explain_sql" do
96
+ it "displays the query explain plan" do
97
+ header "rack-bug.secret_key", "abc"
98
+ expect_query "EXPLAIN SELECT username FROM users",
99
+ [["table"],
100
+ ["users"]]
101
+
102
+ response = get "/__rack_bug__/explain_sql", :query => "SELECT username FROM users",
103
+ :hash => "6f286f55b75716e5c91f16d77d09fa73b353ebc1"
104
+ response.should contain("SELECT username FROM users")
105
+ response.should be_ok
106
+ end
107
+
108
+ it "is forbidden when the hash is missing or wrong" do
109
+ header "rack-bug.secret_key", 'abc'
110
+
111
+ lambda {
112
+ get "/__rack_bug__/explain_sql", :query => "SELECT username FROM users",
113
+ :hash => "foobar"
114
+ }.should raise_error(SecurityError)
115
+ end
116
+
117
+ it "is not available when the rack-bug.secret_key is nil" do
118
+ header "rack-bug.secret_key", nil
119
+
120
+ lambda {
121
+ get "/__rack_bug__/explain_sql", :query => "SELECT username FROM users",
122
+ :hash => "6f286f55b75716e5c91f16d77d09fa73b353ebc1"
123
+ }.should raise_error(SecurityError)
124
+ end
125
+
126
+ it "is not available when the rack-bug.secret_key is an empty string" do
127
+ header "rack-bug.secret_key", ""
128
+
129
+ lambda {
130
+ get "/__rack_bug__/explain_sql", :query => "SELECT username FROM users",
131
+ :hash => "6f286f55b75716e5c91f16d77d09fa73b353ebc1"
132
+ }.should raise_error(SecurityError)
133
+ end
134
+ end
135
+ end
136
+ end