fiveruns_tuneup 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/History.rdoc +3 -0
  2. data/Manifest +57 -0
  3. data/README.rdoc +44 -0
  4. data/Rakefile +15 -0
  5. data/assets/images/arrows.gif +0 -0
  6. data/assets/images/edit.png +0 -0
  7. data/assets/images/fade.png +0 -0
  8. data/assets/images/fade_down.png +0 -0
  9. data/assets/images/head.gif +0 -0
  10. data/assets/images/logo.gif +0 -0
  11. data/assets/images/logo_clear.png +0 -0
  12. data/assets/images/magnify.png +0 -0
  13. data/assets/images/pip.gif +0 -0
  14. data/assets/images/pointer.gif +0 -0
  15. data/assets/images/schema.png +0 -0
  16. data/assets/images/signin.gif +0 -0
  17. data/assets/images/spinner.gif +0 -0
  18. data/assets/images/warning.gif +0 -0
  19. data/assets/javascripts/prototype.js +2515 -0
  20. data/assets/javascripts/tuneup.js +30 -0
  21. data/assets/stylesheets/tuneup.css +204 -0
  22. data/bin/fiveruns_tuneup +26 -0
  23. data/fiveruns_tuneup.gemspec +49 -0
  24. data/init.rb +2 -0
  25. data/install.rb +18 -0
  26. data/lib/bumpspark_helper.rb +52 -0
  27. data/lib/fiveruns/tuneup.rb +103 -0
  28. data/lib/fiveruns/tuneup/asset_tags.rb +39 -0
  29. data/lib/fiveruns/tuneup/custom_methods.rb +8 -0
  30. data/lib/fiveruns/tuneup/environment.rb +29 -0
  31. data/lib/fiveruns/tuneup/instrumentation/action_controller/base.rb +59 -0
  32. data/lib/fiveruns/tuneup/instrumentation/action_view/base.rb +77 -0
  33. data/lib/fiveruns/tuneup/instrumentation/active_record/base.rb +126 -0
  34. data/lib/fiveruns/tuneup/instrumentation/cgi/session.rb +30 -0
  35. data/lib/fiveruns/tuneup/instrumentation/utilities.rb +172 -0
  36. data/lib/fiveruns/tuneup/multipart.rb +75 -0
  37. data/lib/fiveruns/tuneup/runs.rb +86 -0
  38. data/lib/fiveruns/tuneup/schema.rb +43 -0
  39. data/lib/fiveruns/tuneup/step.rb +219 -0
  40. data/lib/fiveruns/tuneup/urls.rb +23 -0
  41. data/lib/fiveruns/tuneup/version.rb +80 -0
  42. data/lib/fiveruns_tuneup.rb +1 -0
  43. data/lib/tuneup_config.rb +29 -0
  44. data/lib/tuneup_controller.rb +140 -0
  45. data/lib/tuneup_helper.rb +185 -0
  46. data/rails/init.rb +20 -0
  47. data/tasks/assets.rake +32 -0
  48. data/test/test_helper.rb +3 -0
  49. data/test/tuneup_test.rb +0 -0
  50. data/uninstall.rb +6 -0
  51. data/views/tuneup/_data.html.erb +15 -0
  52. data/views/tuneup/_flash.html.erb +6 -0
  53. data/views/tuneup/_link.html.erb +1 -0
  54. data/views/tuneup/_schema.html.erb +17 -0
  55. data/views/tuneup/_sql.html.erb +23 -0
  56. data/views/tuneup/_step.html.erb +15 -0
  57. data/views/tuneup/panel/_registered.html.erb +4 -0
  58. data/views/tuneup/panel/_unregistered.html.erb +14 -0
  59. metadata +146 -0
@@ -0,0 +1,39 @@
1
+ module Fiveruns
2
+ module Tuneup
3
+
4
+ module AssetTags
5
+
6
+ def add_asset_tags_to(response)
7
+ return unless show_for?(response)
8
+ before, after = response.body.split(/<\/head>/i, 2)
9
+ if after
10
+ insertion = %(
11
+ <!-- START FIVERUNS TUNEUP ASSETS -->
12
+ <link rel='stylesheet' type='text/css' href='/stylesheets/tuneup/tuneup.css'/>
13
+ #{insert_prototype unless response.body.include?('prototype.js')}
14
+ <script type='text/javascript'>
15
+ var TuneUp = {};
16
+ TuneUp.frontend_url = "#{Fiveruns::Tuneup.frontend_url}";
17
+ </script>
18
+ <script type='text/javascript' src='/javascripts/tuneup/tuneup.js'></script>
19
+ <!-- END FIVERUNS TUNEUP ASSETS -->
20
+ )
21
+ response.headers["Content-Length"] += insertion.size
22
+ response.body.replace(before << insertion << '</head>' << after)
23
+ end
24
+ end
25
+
26
+ def show_for?(response)
27
+ return false unless response.body
28
+ return false unless response.headers['Status'] && response.headers['Status'].include?('200')
29
+ true
30
+ end
31
+
32
+ def insert_prototype
33
+ "<script type='text/javascript' src='/javascripts/tuneup/prototype.js'></script>"
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,8 @@
1
+ module Fiveruns::Tuneup::CustomMethods
2
+
3
+ def tuneup(*args)
4
+ options = args.last.is_a?(Hash) ? args.pop : {}
5
+ Fiveruns::Tuneup.add_custom_methods(self, *args)
6
+ end
7
+
8
+ end
@@ -0,0 +1,29 @@
1
+ module Fiveruns
2
+ module Tuneup
3
+ module Environment
4
+
5
+ def environment
6
+ {
7
+ 'application_name' => application_name,
8
+ 'rails_env' => rails_env,
9
+ 'rails_version' => rails_version
10
+ }
11
+ end
12
+
13
+ def rails_env
14
+ RAILS_ENV || 'development'
15
+ end
16
+
17
+ def rails_version
18
+ ::Rails::VERSION::STRING rescue 'unknown Rails version'
19
+ end
20
+
21
+ def application_name
22
+ app_name = RAILS_ROOT.split('/').last
23
+ return app_name unless app_name == 'current'
24
+ File.join(RAILS_ROOT, '..').split('/').last
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,59 @@
1
+ module Fiveruns
2
+ module Tuneup
3
+ module Instrumentation
4
+ module ActionController
5
+ module Base
6
+ def self.included(base)
7
+ Fiveruns::Tuneup.instrument base, ClassMethods, InstanceMethods
8
+ end
9
+ module ClassMethods
10
+ def cache_page_with_fiveruns_tuneup(*args, &block)
11
+ Fiveruns::Tuneup.step "Cache page", :controller do
12
+ cache_page_without_fiveruns_tuneup(*args, &block)
13
+ end
14
+ end
15
+ def expire_page_with_fiveruns_tuneup(*args, &block)
16
+ Fiveruns::Tuneup.step "Expire cached page", :controller do
17
+ expire_page_without_fiveruns_tuneup(*args, &block)
18
+ end
19
+ end
20
+ end
21
+ module InstanceMethods
22
+ def perform_action_with_fiveruns_tuneup(*args, &block)
23
+ Fiveruns::Tuneup.run(self, request) do
24
+ action = (request.parameters['action'] || 'index').to_s
25
+ if Fiveruns::Tuneup.recording?
26
+ Fiveruns::Tuneup.instrument_filters(self)
27
+ Fiveruns::Tuneup.instrument_action_methods(self)
28
+ Fiveruns::Tuneup.instrument_custom_methods
29
+ end
30
+ result = Fiveruns::Tuneup.step "Perform #{action.capitalize} action in #{self.class.name}", :controller, false do
31
+ perform_action_without_fiveruns_tuneup(*args, &block)
32
+ end
33
+ end
34
+ end
35
+ def process_with_fiveruns_tuneup(request, response, *args, &block)
36
+ result = process_without_fiveruns_tuneup(request, response, *args, &block)
37
+ if !request.xhr? && response.content_type == 'text/html'
38
+ Fiveruns::Tuneup.add_asset_tags_to(response)
39
+ end
40
+ result
41
+ end
42
+
43
+ def write_fragment_with_fiveruns_tuneup(*args, &block)
44
+ Fiveruns::Tuneup.step "Cache fragment", :controller do
45
+ write_fragment_without_fiveruns_tuneup(*args, &block)
46
+ end
47
+ end
48
+ def expire_fragment_with_fiveruns_tuneup(*args, &block)
49
+ Fiveruns::Tuneup.step "Expire fragment cache", :controller do
50
+ expire_fragment_without_fiveruns_tuneup(*args, &block)
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,77 @@
1
+ module Fiveruns
2
+ module Tuneup
3
+ module Instrumentation
4
+ module ActionView
5
+ module Base
6
+
7
+ BASIC_TEMPLATE_PATH = File.join(RAILS_ROOT, 'app', 'views')
8
+
9
+ def self.included(base)
10
+ Fiveruns::Tuneup.instrument base, InstanceMethods
11
+ end
12
+
13
+ def self.normalize_path(path)
14
+ return path unless path
15
+ if path[0, BASIC_TEMPLATE_PATH.size] == BASIC_TEMPLATE_PATH
16
+ path[(BASIC_TEMPLATE_PATH.size + 1)..-1]
17
+ else
18
+ if (components = path.split(File::SEPARATOR)).size > 2
19
+ components[-2, 2].join(File::SEPARATOR)
20
+ else
21
+ components.join(File::SEPARATOR)
22
+ end
23
+ end
24
+ end
25
+
26
+ module InstanceMethods
27
+ def render_file_with_fiveruns_tuneup(path, *args, &block)
28
+ name = Fiveruns::Tuneup::Instrumentation::ActionView::Base.normalize_path(path)
29
+ Fiveruns::Tuneup.step "Render file #{name}", :view do
30
+ render_file_without_fiveruns_tuneup(path, *args, &block)
31
+ end
32
+ end
33
+ def update_page_with_fiveruns_tuneup(*args, &block)
34
+ path = block.to_s.split('/').last.split(':').first rescue ''
35
+ name = Fiveruns::Tuneup::Instrumentation::ActionView::Base.normalize_path(path)
36
+ Fiveruns::Tuneup.step "Render page update #{name}", :view do
37
+ update_page_without_fiveruns_tuneup(*args, &block)
38
+ end
39
+ end
40
+ def render_with_fiveruns_tuneup(*args, &block)
41
+ record = true
42
+ options = args.first || {}
43
+ path = case options
44
+ when String
45
+ "Render #{options}"
46
+ when :update
47
+ name = block.to_s.split('/').last.split(':').first rescue ''
48
+ "Render page update #{name}"
49
+ when Hash
50
+ if options[:file]
51
+ "Render #{options[:file]}"
52
+ elsif options[:partial]
53
+ # Don't record this as it causes duplicate records
54
+ record = false
55
+ elsif options[:inline]
56
+ "Render inline"
57
+ elsif options[:text]
58
+ "Render text"
59
+ end
60
+ end
61
+ path ||= 'Render'
62
+
63
+ if record
64
+ Fiveruns::Tuneup.step path, :view do
65
+ render_without_fiveruns_tuneup(*args, &block)
66
+ end
67
+ else
68
+ render_without_fiveruns_tuneup(*args, &block)
69
+ end
70
+ end
71
+
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,126 @@
1
+ module Fiveruns
2
+ module Tuneup
3
+ module Instrumentation
4
+ module ActiveRecord
5
+ module Base
6
+
7
+ def self.record(model, name, raw_sql=nil, &operation)
8
+ sql = nil
9
+ Fiveruns::Tuneup.exclude do
10
+ model.silence do
11
+ sql = Fiveruns::Tuneup::Step::SQL.new(raw_sql, model.connection) if raw_sql
12
+ Fiveruns::Tuneup.add_schema_for(model.table_name, model.connection)
13
+ end
14
+ end
15
+ Fiveruns::Tuneup.step(name, :model, true, sql, model.table_name, &operation)
16
+ end
17
+
18
+ def self.included(base)
19
+ Fiveruns::Tuneup.instrument base, InstanceMethods, ClassMethods
20
+ end
21
+
22
+ module ClassMethods
23
+
24
+ #
25
+ # FINDS
26
+ #
27
+
28
+ def find_with_fiveruns_tuneup(*args, &block)
29
+ Fiveruns::Tuneup::Instrumentation::ActiveRecord::Base.record self, "Find #{self.name}" do
30
+ find_without_fiveruns_tuneup(*args, &block)
31
+ end
32
+ end
33
+ def find_by_sql_with_fiveruns_tuneup(conditions, &block)
34
+ Fiveruns::Tuneup::Instrumentation::ActiveRecord::Base.record self, "Find #{self.name} by SQL", sanitize_sql(conditions) do
35
+ find_by_sql_without_fiveruns_tuneup(conditions, &block)
36
+ end
37
+ end
38
+
39
+ #
40
+ # CREATE
41
+ #
42
+
43
+ def create_with_fiveruns_tuneup(*args, &block)
44
+ Fiveruns::Tuneup::Instrumentation::ActiveRecord::Base.record self, "Create #{self.name}" do
45
+ create_without_fiveruns_tuneup(*args, &block)
46
+ end
47
+ end
48
+
49
+ #
50
+ # UPDATES
51
+ #
52
+
53
+ def update_with_fiveruns_tuneup(*args, &block)
54
+ Fiveruns::Tuneup::Instrumentation::ActiveRecord::Base.record self, "Update #{self.name}" do
55
+ update_without_fiveruns_tuneup(*args, &block)
56
+ end
57
+ end
58
+ def update_all_with_fiveruns_tuneup(*args, &block)
59
+ Fiveruns::Tuneup::Instrumentation::ActiveRecord::Base.record self, "Update #{self.name} (All)" do
60
+ update_all_without_fiveruns_tuneup(*args, &block)
61
+ end
62
+ end
63
+
64
+ #
65
+ # DELETES
66
+ #
67
+
68
+ def destroy_with_fiveruns_tuneup(*args, &block)
69
+ Fiveruns::Tuneup::Instrumentation::ActiveRecord::Base.record self, "Destroy #{self.name}" do
70
+ destroy_without_fiveruns_tuneup(*args, &block)
71
+ end
72
+ end
73
+ def destroy_all_with_fiveruns_tuneup(*args, &block)
74
+ Fiveruns::Tuneup::Instrumentation::ActiveRecord::Base.record self, "Destroy #{self.name} (All)" do
75
+ destroy_all_without_fiveruns_tuneup(*args, &block)
76
+ end
77
+ end
78
+ def delete_with_fiveruns_tuneup(*args, &block)
79
+ Fiveruns::Tuneup::Instrumentation::ActiveRecord::Base.record self, "Delete #{self.name}" do
80
+ delete_without_fiveruns_tuneup(*args, &block)
81
+ end
82
+ end
83
+ def delete_all_with_fiveruns_tuneup(*args, &block)
84
+ Fiveruns::Tuneup::Instrumentation::ActiveRecord::Base.record self, "Delete #{self.name} (All)" do
85
+ delete_all_without_fiveruns_tuneup(*args, &block)
86
+ end
87
+ end
88
+ end
89
+ module InstanceMethods
90
+
91
+ #
92
+ # UPDATES
93
+ #
94
+
95
+ def update_with_fiveruns_tuneup(*args, &block)
96
+ Fiveruns::Tuneup::Instrumentation::ActiveRecord::Base.record self.class, "Update #{self.class.name}" do
97
+ update_without_fiveruns_tuneup(*args, &block)
98
+ end
99
+ end
100
+ def save_with_fiveruns_tuneup(*args, &block)
101
+ Fiveruns::Tuneup::Instrumentation::ActiveRecord::Base.record self.class, "Save #{self.class.name}" do
102
+ save_without_fiveruns_tuneup(*args, &block)
103
+ end
104
+ end
105
+ def save_with_fiveruns_tuneup!(*args, &block)
106
+ Fiveruns::Tuneup::Instrumentation::ActiveRecord::Base.record self.class, "Save #{self.class.name}" do
107
+ save_without_fiveruns_tuneup!(*args, &block)
108
+ end
109
+ end
110
+
111
+ #
112
+ # DELETES
113
+ #
114
+
115
+ def destroy_with_fiveruns_tuneup(*args, &block)
116
+ Fiveruns::Tuneup::Instrumentation::ActiveRecord::Base.record self.class, "Destroy #{self.class.name}" do
117
+ destroy_without_fiveruns_tuneup(*args, &block)
118
+ end
119
+ end
120
+
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,30 @@
1
+ module Fiveruns
2
+ module Tuneup
3
+ module Instrumentation
4
+ module CGI
5
+ module Session
6
+ def self.included(base)
7
+ Fiveruns::Tuneup.instrument base, InstanceMethods
8
+ end
9
+ module InstanceMethods
10
+ def initialize_with_fiveruns_tuneup(*args, &block)
11
+ Fiveruns::Tuneup.step "Create session", :model do
12
+ initialize_without_fiveruns_tuneup(*args, &block)
13
+ end
14
+ end
15
+ def close_with_fiveruns_tuneup(*args, &block)
16
+ Fiveruns::Tuneup.step "Close session", :model do
17
+ close_without_fiveruns_tuneup(*args, &block)
18
+ end
19
+ end
20
+ def delete_with_fiveruns_tuneup(*args, &block)
21
+ Fiveruns::Tuneup.step "Delete session", :model do
22
+ delete_without_fiveruns_tuneup(*args, &block)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,172 @@
1
+ module Fiveruns
2
+ module Tuneup
3
+ module Instrumentation
4
+ module Utilities
5
+
6
+ def stack
7
+ @stack ||= []
8
+ end
9
+
10
+ def exclusion_stack
11
+ @exclusion_stack ||= [0]
12
+ end
13
+
14
+ def custom_methods
15
+ @custom_methods ||= {}
16
+ end
17
+
18
+ def add_custom_methods(target, *methods)
19
+ custom_methods[target] = [] unless custom_methods.key?(target)
20
+ custom_methods[target].push(*methods)
21
+ end
22
+
23
+ def stopwatch
24
+ start = Time.now.to_f
25
+ yield
26
+ (Time.now.to_f - start) * 1000
27
+ end
28
+
29
+ def exclude
30
+ result = nil
31
+ exclusion_stack[-1] += stopwatch { result = yield }
32
+ result
33
+ end
34
+
35
+ def step(name, layer=nil, link=true, sql=nil, table_name=nil, &block)
36
+ if recording?
37
+ result = nil
38
+ caller_line = caller.detect { |l| l.include?(RAILS_ROOT) && l !~ /tuneup|vendor\/rails/ } if link
39
+ file, line = caller_line ? caller_line.split(':')[0, 2] : [nil, nil]
40
+ line = line.to_i if line
41
+ returning ::Fiveruns::Tuneup::Step.new(name, layer, file, line, sql, &block) do |step|
42
+ step.table_name = table_name
43
+ stack.last << step
44
+ stack << step
45
+ handle_exclusions_in step do
46
+ step.time = stopwatch { result = yield(sql) }
47
+ end
48
+ stack.pop
49
+ end
50
+ result
51
+ else
52
+ yield(sql)
53
+ end
54
+ end
55
+
56
+ # Handle removal of excluded time from total for this step, and
57
+ # bubble the value up for removal from the parent step
58
+ def handle_exclusions_in(step)
59
+ exclusion_stack << 0
60
+ yield # Must set +step.time+
61
+ time_to_exclude = exclusion_stack.pop
62
+ step.time -= time_to_exclude
63
+ exclusion_stack[-1] += time_to_exclude unless exclusion_stack.blank?
64
+ end
65
+
66
+ def instrument(target, *mods)
67
+ mods.each do |mod|
68
+ # Change target for 'ClassMethods' module
69
+ real_target = mod.name.demodulize == 'ClassMethods' ? (class << target; self; end) : target
70
+ real_target.__send__(:include, mod)
71
+ # Find all the instrumentation hooks and chain them in
72
+ mod.instance_methods.each do |meth|
73
+ name = meth.to_s.sub('_with_fiveruns_tuneup', '')
74
+ real_target.alias_method_chain(name, :fiveruns_tuneup) rescue nil
75
+ end
76
+ end
77
+ end
78
+
79
+ def instrument_action_methods(controller)
80
+ klass = controller.class
81
+ actions_for(klass).each do |meth|
82
+ format = alias_format_for(meth)
83
+ next if controller.respond_to?(format % :with, true)
84
+ wrap(klass, format, meth, "Invoke #{meth} action", :controller)
85
+ end
86
+ end
87
+
88
+ def instrument_filters(controller)
89
+ klass = controller.class
90
+ filters_for(klass).each do |filter|
91
+ format = alias_format_for(filter.filter)
92
+ next if controller.respond_to?(format % :with, true)
93
+ wrap(klass, format, filter.filter, "#{filter.type.to_s.titleize} filter #{filter.filter}", :controller)
94
+ end
95
+ end
96
+
97
+ def instrument_custom_methods
98
+ custom_methods.each do |meth_target, meths|
99
+ lineage = meth_target.ancestors
100
+ layer = if lineage.include?(ActionController::Base)
101
+ :controller
102
+ elsif lineage.include?(ActiveRecord::Base)
103
+ :model
104
+ elsif lineage.include?(ActionView::Base)
105
+ :view
106
+ else
107
+ :other
108
+ end
109
+ meths.each do |meth|
110
+ format = alias_format_for(meth)
111
+ wrap(meth_target, format, meth, "Method #{meth}", layer)
112
+ end
113
+ end
114
+ end
115
+
116
+ #######
117
+ private
118
+ #######
119
+
120
+ def wrap(klass, format, meth, name, layer)
121
+ text = <<-EOC
122
+ def #{format % :with}(*args, &block)
123
+ Fiveruns::Tuneup.step "#{name}", :#{layer} do
124
+ #{format % :without}(*args, &block)
125
+ end
126
+ end
127
+ alias_method_chain :#{meth}, :fiveruns_tuneup
128
+ EOC
129
+ begin
130
+ klass.class_eval text
131
+ rescue SyntaxError => e
132
+ # XXX: Catch-all for reports of oddly-named methods affecting dynamically generated code
133
+ log :warn, %[Bad syntax wrapping #{klass}##{meth}, "#{name}"]
134
+ end
135
+ end
136
+
137
+ def alias_format_for(name)
138
+ name.to_s =~ /^(.*?)(\?|!|=)$/ ? "#{$1}_%s_fiveruns_tuneup#{$2}" : "#{name}_%s_fiveruns_tuneup"
139
+ end
140
+
141
+ def actions_for(klass)
142
+ klass.action_methods.reject { |meth| meth.to_s.include?('fiveruns') }
143
+ end
144
+
145
+ def filters_for(klass)
146
+ klass.filter_chain.select { |f| f.filter.is_a?(Symbol) }
147
+ end
148
+
149
+ def install_instrumentation
150
+ instrumentation_path = File.dirname(__FILE__)
151
+ Dir[File.join(instrumentation_path, '/*/**/*.rb')].each do |filename|
152
+ constant_path = filename[(instrumentation_path.size + 1)..-4]
153
+ constant_name = path_to_constant_name(constant_path)
154
+ if (constant = constant_name.constantize rescue nil)
155
+ instrumentation = "Fiveruns::Tuneup::Instrumentation::#{constant_name}".constantize
156
+ constant.__send__(:include, instrumentation)
157
+ else
158
+ log :debug, "#{constant_name} not found; skipping instrumentation."
159
+ end
160
+ end
161
+ end
162
+
163
+ def path_to_constant_name(path)
164
+ parts = path.split(File::SEPARATOR)
165
+ parts.map(&:camelize).join('::').sub('Cgi', 'CGI')
166
+ end
167
+
168
+ end
169
+ end
170
+ end
171
+ end
172
+