Class: ProfiledProcessor

Inherits:
TestProcessor show all
Defined in:
lib/action_profiler/profiled_processor.rb

Overview

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.

Constant Summary collapse

PROFILERS =
['ZenProfiler', 'Prof', 'Profiler']

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from TestProcessor

#process

Constructor Details

#initialize(action, method, params, session, flash, only_html) ⇒ ProfiledProcessor

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.



157
158
159
160
# File 'lib/action_profiler/profiled_processor.rb', line 157

def initialize(action, method, params, session, flash, only_html)
  super action, method, params, session, flash
  @only_html = only_html
end

Class Method Details

.load_default_processorObject

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



128
129
130
131
132
133
134
135
136
# File 'lib/action_profiler/profiled_processor.rb', line 128

def self.load_default_processor
  PROFILERS.each do |profiler|
    begin
      return load_processor(profiler)
    rescue LoadError => e
    end
  end
  raise "couldn't load any profilers, how strange, sorry about that"
end

.load_processor(name) ⇒ Object

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



142
143
144
145
146
147
148
149
150
151
# File 'lib/action_profiler/profiled_processor.rb', line 142

def self.load_processor(name)
  debug "Loading #{name}Processor"
  # HACK I have no fucking clue how or why Rails' require mucks shit up,
  # nor can I reproduce it in a small testcase.
  require__ "action_profiler/#{name.downcase}_processor"
  return Object.path2class("#{name}Processor")
rescue LoadError => e
  debug "Failed to load #{name}Processor: #{e.message}"
  raise
end

.process_args(args = ARGV) ⇒ Object

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



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/action_profiler/profiled_processor.rb', line 22

def self.process_args(args = ARGV)
  app_path = Dir.pwd
  method = 'GET'
  only_html = false
  processor_klass = nil
  times = 1

  opts = OptionParser.new do |opts|
    opts.banner = "Usage: #{File.basename $0} [options] method [params [session [flash]]]"

    opts.separator ''
    opts.separator 'method: controller and action to run "GamesController#index"'
    opts.separator 'params, session, flash: Hash-style arguments ":id => 5"'
    opts.separator ''

    opts.on("-m", "--method=HTTP_METHOD",
            "HTTP request method for this action",
            "Default: #{method}") do |val|
      method = val
    end

    opts.on("-o", "--[no-]only-html",
            "Only output rendered page",
            "Default: #{only_html}") do |val|
      only_html = val
    end

    opts.on("-p", "--app-path=PATH",
            "Path to Rails application root",
            "Default: current directory") do |val|
      unless File.directory? val then
        raise OptionParser::InvalidArgument, "bad path: #{val}"
      end

      app_path = val
    end

    opts.on("-P", "--profiler=PROFILER",
            "Profiler to use",
            "Default: ZenProfiler, Prof then Profiler") do |val|
      begin
        processor_klass = load_processor val
      rescue LoadError
        raise OptionParser::InvalidArgument, "can't load #{val}_processor"
      end
    end

    opts.on("-t", "--times=TIMES", Integer,
            "Times to run the action under the profiler",
            "Default: #{times}") do |val|
      times = val
    end

    opts.separator ''
    opts.on("-h", "--help", "Display this help") { STDERR.puts opts; exit 1 }
    opts.on("-d", "--debug", "Enable debugging output") do |val|
      $AP_DEBUG = val
    end
    opts.separator ''

    opts.parse! args
  end

  processor_klass = load_default_processor if processor_klass.nil?

  begin
    Dir.chdir app_path
    require 'config/environment'
    require 'application' # HACK Rails can't find this by itself
  rescue LoadError => e
    debug "Application load error \"#{e.message}\""
    raise OptionParser::InvalidArgument, "could not load application, check your path"
  end

  raise OptionParser::ParseError, "action not specified" if args.empty?
  action = args.shift

  raise OptionParser::ParseError, "too many arguments" if args.length > 3

  begin
    params, session, flash = args.map { |arg| eval "{#{arg}}" }
  rescue Exception
    raise OptionParser::ParseError, "invalid param/session/flash argument"
  end

  params ||= {}
  session ||= {}
  flash ||= {}

  debug "Using #{processor_klass.inspect} processor"

  pp = processor_klass.new action, method, params, session, flash, only_html
  pp.profile times

rescue ArgumentError, OptionParser::ParseError => e
  STDERR.puts e.message
  debug "\t#{$!.backtrace.join("\n\t")}"
  STDERR.puts
  STDERR.puts opts.to_s
  exit 1
end

Instance Method Details

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

Raises:

  • (NotImplementedError)


203
204
205
# File 'lib/action_profiler/profiled_processor.rb', line 203

def print_profile(io)
  raise NotImplementedError
end

#profile(times = 1) ⇒ Object

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



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/action_profiler/profiled_processor.rb', line 166

def profile(times = 1)
  if @only_html then
    process
    puts @response.body
    return
  end

  STDERR.puts "Warmup..."
  3.times { process }

  begin
    STDERR.puts "Profiling..."
    start_profile
    times.times { process }
    stop_profile
  ensure
    print_profile
  end
end

#start_profileObject

Implemented by a subclass to start the profiler it uses.

Raises:

  • (NotImplementedError)


189
190
191
# File 'lib/action_profiler/profiled_processor.rb', line 189

def start_profile
  raise NotImplementedError
end

#stop_profileObject

Implemented by a subclass to stop the profiler it uses.

Raises:

  • (NotImplementedError)


196
197
198
# File 'lib/action_profiler/profiled_processor.rb', line 196

def stop_profile
  raise NotImplementedError
end