A Rails action processor that profiles an entire action.

ProfiledProcessor is an abstract class. A subclasses must implement start_profile, stop_profile and print_profile.

Methods
Constants
PROFILERS = ['ZenProfiler', 'Prof', 'Profiler']
Public Class methods
load_default_processor()

Attempts to load the default profilers in order. Returns the first successfully found profiler class.

     # File lib/action_profiler/profiled_processor.rb, line 128
128:   def self.load_default_processor
129:     PROFILERS.each do |profiler|
130:       begin
131:         return load_processor(profiler)
132:       rescue LoadError => e
133:       end
134:     end
135:     raise "couldn't load any profilers, how strange, sorry about that"
136:   end
load_processor(name)

Attempts to load a processor starting with name. Returns the loaded class if successful.

     # File lib/action_profiler/profiled_processor.rb, line 142
142:   def self.load_processor(name)
143:     debug "Loading #{name}Processor"
144:     # HACK I have no fucking clue how or why Rails' require mucks shit up,
145:     # nor can I reproduce it in a small testcase.
146:     require__ "action_profiler/#{name.downcase}_processor"
147:     return Object.path2class("#{name}Processor")
148:   rescue LoadError => e
149:     debug "Failed to load #{name}Processor: #{e.message}"
150:     raise
151:   end
new(action, method, params, session, flash, only_html)

If only_html is true then only the rendered page will be displayed and no profiling will be performed. See TestProcessor#new for the rest.

     # File lib/action_profiler/profiled_processor.rb, line 157
157:   def initialize(action, method, params, session, flash, only_html)
158:     super action, method, params, session, flash
159:     @only_html = only_html
160:   end
process_args(args = ARGV)

Processes args then runs a profile based on the arguments given.

     # File lib/action_profiler/profiled_processor.rb, line 22
 22:   def self.process_args(args = ARGV)
 23:     app_path = Dir.pwd
 24:     method = 'GET'
 25:     only_html = false
 26:     processor_klass = nil
 27:     times = 1
 28: 
 29:     opts = OptionParser.new do |opts|
 30:       opts.banner = "Usage: #{File.basename $0} [options] method [params [session [flash]]]"
 31: 
 32:       opts.separator ''
 33:       opts.separator 'method: controller and action to run "GamesController#index"'
 34:       opts.separator 'params, session, flash: Hash-style arguments ":id => 5"'
 35:       opts.separator ''
 36: 
 37:       opts.on("-m", "--method=HTTP_METHOD",
 38:               "HTTP request method for this action",
 39:               "Default: #{method}") do |val|
 40:         method = val
 41:       end
 42: 
 43:       opts.on("-o", "--[no-]only-html",
 44:               "Only output rendered page",
 45:               "Default: #{only_html}") do |val|
 46:         only_html = val
 47:       end
 48: 
 49:       opts.on("-p", "--app-path=PATH",
 50:               "Path to Rails application root",
 51:               "Default: current directory") do |val|
 52:         unless File.directory? val then
 53:           raise OptionParser::InvalidArgument, "bad path: #{val}"
 54:         end
 55: 
 56:         app_path = val
 57:       end
 58: 
 59:       opts.on("-P", "--profiler=PROFILER",
 60:               "Profiler to use",
 61:               "Default: ZenProfiler, Prof then Profiler") do |val|
 62:         begin
 63:           processor_klass = load_processor val
 64:         rescue LoadError
 65:           raise OptionParser::InvalidArgument, "can't load #{val}_processor"
 66:         end
 67:       end
 68: 
 69:       opts.on("-t", "--times=TIMES", Integer,
 70:               "Times to run the action under the profiler",
 71:               "Default: #{times}") do |val|
 72:         times = val
 73:       end
 74: 
 75:       opts.separator ''
 76:       opts.on("-h", "--help", "Display this help") { STDERR.puts opts; exit 1 }
 77:       opts.on("-d", "--debug", "Enable debugging output") do |val|
 78:         $AP_DEBUG = val
 79:       end
 80:       opts.separator ''
 81: 
 82:       opts.parse! args
 83:     end
 84: 
 85:     processor_klass = load_default_processor if processor_klass.nil?
 86: 
 87:     begin
 88:       Dir.chdir app_path
 89:       require 'config/environment'
 90:       require 'application' # HACK Rails can't find this by itself
 91:     rescue LoadError => e
 92:       debug "Application load error \"#{e.message}\""
 93:       raise OptionParser::InvalidArgument, "could not load application, check your path"
 94:     end
 95: 
 96:     raise OptionParser::ParseError, "action not specified" if args.empty?
 97:     action = args.shift
 98: 
 99:     raise OptionParser::ParseError, "too many arguments" if args.length > 3
100: 
101:     begin
102:       params, session, flash = args.map { |arg| eval "{#{arg}}" }
103:     rescue Exception
104:       raise OptionParser::ParseError, "invalid param/session/flash argument"
105:     end
106: 
107:     params ||= {}
108:     session ||= {}
109:     flash ||= {}
110: 
111:     debug "Using #{processor_klass.inspect} processor"
112: 
113:     pp = processor_klass.new action, method, params, session, flash, only_html
114:     pp.profile times
115: 
116:   rescue ArgumentError, OptionParser::ParseError => e
117:     STDERR.puts e.message
118:     debug "\t#{$!.backtrace.join("\n\t")}"
119:     STDERR.puts
120:     STDERR.puts opts.to_s
121:     exit 1
122:   end
Public Instance methods
print_profile(io)

Implemented by a subclass to print out the profile data to io.

     # File lib/action_profiler/profiled_processor.rb, line 203
203:   def print_profile(io)
204:     raise NotImplementedError
205:   end
profile(times = 1)

Profiles the action, running it under the profiler times times after three warmup actions.

     # File lib/action_profiler/profiled_processor.rb, line 166
166:   def profile(times = 1)
167:     if @only_html then
168:       process
169:       puts @response.body
170:       return
171:     end
172: 
173:     STDERR.puts "Warmup..."
174:     3.times { process }
175: 
176:     begin
177:       STDERR.puts "Profiling..."
178:       start_profile
179:       times.times { process }
180:       stop_profile
181:     ensure
182:       print_profile
183:     end
184:   end
start_profile()

Implemented by a subclass to start the profiler it uses.

     # File lib/action_profiler/profiled_processor.rb, line 189
189:   def start_profile
190:     raise NotImplementedError
191:   end
stop_profile()

Implemented by a subclass to stop the profiler it uses.

     # File lib/action_profiler/profiled_processor.rb, line 196
196:   def stop_profile
197:     raise NotImplementedError
198:   end