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,80 @@
1
+ # (The MIT License)
2
+ #
3
+ # Copyright (c) 2008 Jamis Buck <jamis@37signals.com>,
4
+ # with modifications by Bruce Williams <bruce@fiveruns.com>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # 'Software'), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ module Fiveruns
25
+
26
+ module Tuneup
27
+
28
+ # A class for describing the current version of a library. The version
29
+ # consists of three parts: the +major+ number, the +minor+ number, and the
30
+ # +tiny+ (or +patch+) number.
31
+ class Version
32
+
33
+ # A convenience method for instantiating a new Version instance with the
34
+ # given +major+, +minor+, and +tiny+ components.
35
+ def self.[](major, minor, tiny)
36
+ new(major, minor, tiny)
37
+ end
38
+
39
+ attr_reader :major, :minor, :tiny
40
+
41
+ # Create a new Version object with the given components.
42
+ def initialize(major, minor, tiny)
43
+ @major, @minor, @tiny = major, minor, tiny
44
+ end
45
+
46
+ # Compare this version to the given +version+ object.
47
+ def <=>(version)
48
+ to_i <=> version.to_i
49
+ end
50
+
51
+ # Converts this version object to a string, where each of the three
52
+ # version components are joined by the '.' character. E.g., 2.0.0.
53
+ def to_s
54
+ @to_s ||= [@major, @minor, @tiny].join(".")
55
+ end
56
+
57
+ # Converts this version to a canonical integer that may be compared
58
+ # against other version objects.
59
+ def to_i
60
+ @to_i ||= @major * 1_000_000 + @minor * 1_000 + @tiny
61
+ end
62
+
63
+ def to_a
64
+ [@major, @minor, @tiny]
65
+ end
66
+
67
+ MAJOR = 0
68
+ MINOR = 8
69
+ TINY = 2
70
+
71
+ # The current version as a Version instance
72
+ CURRENT = new(MAJOR, MINOR, TINY)
73
+ # The current version as a String
74
+ STRING = CURRENT.to_s
75
+
76
+ end
77
+
78
+ end
79
+
80
+ end
@@ -0,0 +1 @@
1
+ Dependencies.load_paths.unshift File.dirname(__FILE__)
@@ -0,0 +1,29 @@
1
+ require 'yaml'
2
+
3
+ class TuneupConfig
4
+
5
+ delegate :[], :[]=, :each, :update, :to => :data
6
+
7
+ def self.defaults
8
+ { 'api_key' => nil }
9
+ end
10
+
11
+ def save!
12
+ File.open(config_file, 'w') { |f| f.puts data.to_yaml }
13
+ end
14
+
15
+ def data
16
+ @data ||= begin
17
+ YAML.load(File.read(config_file)) rescue self.class.defaults
18
+ end
19
+ end
20
+
21
+ def config_file
22
+ File.join(RAILS_ROOT, 'config', 'fiveruns_tuneup.yml')
23
+ end
24
+
25
+ def state
26
+ data['api_key'] ? :registered : :unregistered
27
+ end
28
+
29
+ end
@@ -0,0 +1,140 @@
1
+ require 'net/https'
2
+ require 'ostruct'
3
+ require 'open-uri'
4
+
5
+ class TuneupController < ActionController::Base
6
+
7
+ self.logger = Fiveruns::Tuneup::LOGGER
8
+
9
+ def show
10
+ render :update do |page|
11
+ page << tuneup_reload_panel
12
+ end
13
+ end
14
+
15
+ def update
16
+ @config.update(params[:config])
17
+ @config.save!
18
+ redirect_to :action => 'show'
19
+ end
20
+
21
+ def signin
22
+ if api_key = retrieve_api_key
23
+ @config['api_key'] = api_key
24
+ @config.save!
25
+ end
26
+ render :update do |page|
27
+ if api_key
28
+ page << tuneup_reload_panel
29
+ else
30
+ page << tuneup_show_flash(:error,
31
+ :header => "TuneUp encountered an error",
32
+ :message => "Could not access your FiveRuns TuneUp account.")
33
+ end
34
+ end
35
+ end
36
+
37
+ def upload
38
+ token = upload_run
39
+ render :update do |page|
40
+ if token
41
+ link = link_to_function("here", tuneup_open_run(token))
42
+ page << tuneup_show_flash(:notice,
43
+ :header => 'Run Uploaded to TuneUp',
44
+ :message => "View your run #{link}.")
45
+ else
46
+ page << tuneup_show_flash(:error,
47
+ :header => "TuneUp encountered an error",
48
+ :message => "Could not upload run to your FiveRuns TuneUp account.")
49
+ end
50
+ end
51
+ end
52
+
53
+ def asset
54
+ filename = File.basename(params[:file])
55
+ if filename =~ /css$/
56
+ response.content_type = 'text/css'
57
+ end
58
+ send_file File.join(File.dirname(__FILE__) << "/../assets/#{filename}")
59
+ end
60
+
61
+ def on
62
+ collect true
63
+ end
64
+
65
+ def off
66
+ collect false
67
+ end
68
+
69
+ #######
70
+ private
71
+ #######
72
+
73
+ def collect(state)
74
+ Fiveruns::Tuneup.collecting = state
75
+ render(:update) { |p| p['tuneup-panel'].replace(render(:partial => 'tuneup/panel/registered')) }
76
+ end
77
+
78
+ def find_config
79
+ @config = TuneupConfig.new
80
+ end
81
+
82
+ #
83
+ # HTTP
84
+ #
85
+
86
+ def upload_run
87
+ safely do
88
+ http = Net::HTTP.new(upload_uri.host, upload_uri.port)
89
+ http.use_ssl = true if Fiveruns::Tuneup.collector_url =~ /^https/
90
+ resp = nil
91
+ # TODO: Support targeted upload
92
+ filename = Fiveruns::Tuneup.last_filename_for_run_uri(params[:uri])
93
+ Fiveruns::Tuneup.log :debug, "Uploading #{filename} for URI #{params[:uri]}"
94
+ File.open(filename, 'rb') do |file|
95
+ multipart = Fiveruns::Tuneup::Multipart.new(file, 'api_key' => @config['api_key'] )
96
+ # Fiveruns::Tuneup.log :debug, multipart.to_s
97
+ resp = http.post(upload_uri.request_uri, multipart.to_s, "Content-Type" => multipart.content_type)
98
+ end
99
+ case resp.code.to_i
100
+ when 201
101
+ return resp.body.strip rescue nil
102
+ else
103
+ Fiveruns::Tuneup.log :error, "Recieved bad response from service (#{resp.inspect})"
104
+ return false
105
+ end
106
+ end
107
+ end
108
+
109
+ def retrieve_api_key
110
+ safely do
111
+ http = Net::HTTP.new(api_key_uri.host, api_key_uri.port)
112
+ http.use_ssl = true if Fiveruns::Tuneup.collector_url =~ /^https/
113
+ data = "email=#{CGI.escape(params[:email])}&password=#{CGI.escape(params[:password])}"
114
+ resp = http.post(api_key_uri.path, data, "Content-Type" => "application/x-www-form-urlencoded")
115
+ case resp.code.to_i
116
+ when 200..299
117
+ resp.body.strip rescue nil
118
+ else
119
+ Fiveruns::Tuneup.log :error, "Recieved bad response from service (#{resp.inspect})"
120
+ false
121
+ end
122
+ end
123
+ end
124
+
125
+ def safely
126
+ yield
127
+ rescue Exception => e
128
+ Fiveruns::Tuneup.log :error, "Could not access service: #{e.message}"
129
+ false
130
+ end
131
+
132
+ def api_key_uri
133
+ @api_key_uri ||= URI.parse("#{Fiveruns::Tuneup.collector_url}/users")
134
+ end
135
+
136
+ def upload_uri
137
+ @upload_uri ||= URI.parse("#{Fiveruns::Tuneup.collector_url}/runs")
138
+ end
139
+
140
+ end
@@ -0,0 +1,185 @@
1
+ module TuneupHelper #:nodoc:
2
+
3
+ include BumpsparkHelper
4
+
5
+ def tuneup_signin_url
6
+ "#{Fiveruns::Tuneup.collector_url}/users"
7
+ end
8
+
9
+ def tuneup_signup_url
10
+ "#{Fiveruns::Tuneup.frontend_url}/signup"
11
+ end
12
+
13
+ def tuneup_collection_link
14
+ state = tuneup_collecting? ? :off : :on
15
+ link_to_remote "Turn #{state.to_s.titleize}", :url => "/tuneup/#{state}"
16
+ end
17
+
18
+ def tuneup_recording?
19
+ Fiveruns::Tuneup.recording?
20
+ end
21
+
22
+ def tuneup_css_class_for_step(step)
23
+ returning [] do |classes|
24
+ if step.children.any?
25
+ classes << 'with-children'
26
+ classes << 'tuneup-opened' if step.depth == 1 || open_step?(step)
27
+ end
28
+ end.join(' ')
29
+ end
30
+
31
+ def tuneup_collecting?
32
+ Fiveruns::Tuneup.collecting
33
+ end
34
+
35
+ def tuneup_data
36
+ most_recent_data = Fiveruns::Tuneup.stack.first
37
+ most_recent_data.blank? ? most_recent_data : most_recent_data['stack']
38
+ end
39
+
40
+ def tuneup_schemas
41
+ Fiveruns::Tuneup.stack.first['schemas']
42
+ end
43
+
44
+ def trend
45
+ numbers= if Fiveruns::Tuneup.trend.size > 50
46
+ Fiveruns::Tuneup.trend[-50..-1]
47
+ else
48
+ Fiveruns::Tuneup.trend
49
+ end
50
+ return unless numbers.size > 1
51
+ tag(:img,
52
+ :src => build_data_url("image/png",bumpspark(numbers)), :alt => '',
53
+ :title => "Trend over last #{pluralize(numbers.size, 'run')}")
54
+ end
55
+
56
+ def open_step?(step)
57
+ [/^Around filter/, /^Invoke/].any? { |pattern| step.name =~ pattern }
58
+ end
59
+
60
+ def tuneup_step_link(step)
61
+ name = tuneup_style_step_name(tuneup_truncate_step_name(step))
62
+ link = if step.children.any?
63
+ link_to_function(name, "$('#{dom_id(step, :children)}').toggle();$('#{dom_id(step)}').toggleClassName('tuneup-opened');")
64
+ else
65
+ name
66
+ end
67
+ link << additional_step_links(step)
68
+ end
69
+
70
+ def link_to_upload
71
+ link_to_remote "Upload this Run",
72
+ :url => "/tuneup/upload?uri=#{CGI.escape(params[:uri])}",
73
+ :loading => '$("tuneup-top").hide(); TuneUp.Spinner.start()',
74
+ :complete => ' TuneUp.Spinner.stop(); $("tuneup-top").show();',
75
+ :html => {:id => 'tuneup-save-link'}
76
+ end
77
+
78
+ def additional_step_links(step)
79
+ returning '' do |text|
80
+ text << sql_link(step) if step.sql
81
+ text << schema_link(step) if step.table_name
82
+ end
83
+ end
84
+
85
+ def schema_link(step)
86
+ link_to_schema(image_tag('/images/tuneup/schema.png', :alt => 'Schema'), step.table_name, :class => 'tuneup-schema tuneup-halo')
87
+ end
88
+
89
+ def sql_link(step)
90
+ link_to_function(image_tag('/images/tuneup/magnify.png', :alt => 'Query'), :class => 'tuneup-sql tuneup-halo', :title => 'View Query') { |p| p[dom_id(step, :sql)].toggle }
91
+ end
92
+
93
+ def link_to_schema(text, table, html_options={})
94
+ link_to_function(text, "TuneUp.switchSchema('#{table}')", html_options.merge(:title => "View Schema"))
95
+ end
96
+
97
+ def tuneup_truncate_step_name(step)
98
+ chars = 50 - (step.depth * 2)
99
+ tuneup_truncate(step.name, chars)
100
+ end
101
+
102
+ def tuneup_bar(step=tuneup_data, options={})
103
+ width = options.delete(:width) || 200
104
+ bars = Fiveruns::Tuneup::Step.layers.map do |layer|
105
+ percent = step.percentages_by_layer[layer]
106
+ if percent == 0
107
+ next
108
+ else
109
+ begin
110
+ size = (percent * width).to_i
111
+ rescue
112
+ raise "Can't find percent for #{layer.inspect} from #{step.percentages_by_layer.inspect}"\
113
+ end
114
+ end
115
+ size = 1 if size.zero?
116
+ content_tag(:li, ((size >= 10 && layer != :other) ? layer.to_s[0, 1].capitalize : ''),
117
+ :class => "tuneup-layer-#{layer}",
118
+ :style => "width:#{size}px",
119
+ :title => layer.to_s.titleize)
120
+ end
121
+ content_tag(:ul, bars.compact.join, options.merge(:class => 'tuneup-bar'))
122
+ end
123
+
124
+ def tuneup_style_step_name(name)
125
+ case name
126
+ when /^Perform (\S+) action in (\S+Controller)$/
127
+ "Perform <strong>#{h $1}</strong> action in <strong>#{h $2}</strong>"
128
+ when /^Invoke (\S+) action$/
129
+ "Invoke <strong>#{h $1}</strong> action"
130
+ when /^(Find|Create|Delete|Update) ([A-Z]\S*)(.*?)$/
131
+ "#{h $1} <strong>#{h $2}</strong>#{h $3}"
132
+ when /^(Render.*?)(\S+)$/
133
+ "#{h $1}<strong>#{h $2}</strong>"
134
+ when /^(\S+ filter )(.*?)$/
135
+ "#{h $1}<strong>#{h $2}</strong>"
136
+ when 'Other'
137
+ "(<i>Other</i>)"
138
+ else
139
+ h(name)
140
+ end
141
+ end
142
+
143
+ def tuneup_truncate(text, max=32)
144
+ if text.size > max
145
+ component = (max - 3) / 2
146
+ remainder = (max - 3) % 2
147
+ begin
148
+ text.sub(/^(.{#{component}}).*?(.{#{component + remainder}})$/s, '\1...\2')
149
+ rescue
150
+ text
151
+ end
152
+ else
153
+ text
154
+ end
155
+ end
156
+
157
+ def tuneup_open_run(token)
158
+ %[window.open("#{Fiveruns::Tuneup.frontend_url}/runs/#{token}", 'fiveruns_tuneup');]
159
+ end
160
+
161
+ def tuneup_reload_panel
162
+ update_page do |page|
163
+ page['tuneup-flash'].removeClassName('tuneup-show');
164
+ page['tuneup-content'].replace_html(render(:partial => "tuneup/panel/#{@config.state}"))
165
+ end
166
+ end
167
+
168
+ def tuneup_show_flash(type, locals)
169
+ types = [:error, :notice].reject { |t| t == type }
170
+ update_page do |page|
171
+ page['tuneup-flash'].replace_html(render(:partial => 'flash', :locals => locals.merge(:type => type)))
172
+ page['tuneup-flash'].addClassName('tuneup-show');
173
+ types.each do |other_type|
174
+ page['tuneup-flash'].removeClassName("tuneup-#{other_type}")
175
+ end
176
+ page['tuneup-flash'].addClassName("tuneup-#{type}");
177
+ end
178
+ end
179
+
180
+ def link_to_edit_step(step)
181
+ return nil unless step.file && step.line && RUBY_PLATFORM.include?('darwin')
182
+ link_to(image_tag('/images/tuneup/edit.png', :alt => 'Edit'), "txmt://open?url=file://#{CGI.escape step.file}&line=#{step.line}", :class => 'tuneup-edit tuneup-halo', :title => 'Open in TextMate')
183
+ end
184
+
185
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'dispatcher'
2
+ Dispatcher.to_prepare :tuneup_route do
3
+ ActionController::Routing::Routes.add_route '/tuneup', :controller => 'tuneup', :action => 'show'
4
+ ActionController::Routing::Routes.add_route '/tuneup/:action', :controller => 'tuneup'
5
+ 2.times do
6
+ route = ActionController::Routing::Routes.routes.pop
7
+ ActionController::Routing::Routes.routes.unshift(route)
8
+ end
9
+ end
10
+ Dispatcher.to_prepare :tuneup_controller_filters do
11
+ TuneupController.filter_chain.clear
12
+ TuneupController.before_filter :find_config, :except => :index
13
+ end
14
+ [ActionController::Base, ActiveRecord::Base, ActionView::Base].each do |target|
15
+ target.extend Fiveruns::Tuneup::CustomMethods
16
+ end
17
+
18
+ ActionController::Base.view_paths.push(File.dirname(__FILE__) << "/../views")
19
+ require File.dirname(__FILE__) << "/../install" # Check for assets
20
+ Fiveruns::Tuneup.start