fiveruns_tuneup 0.8.2

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 (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
+