speed_gun 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rubocop.yml +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +161 -0
  7. data/Rakefile +1 -0
  8. data/app/assets/javascripts/browser.js +65 -0
  9. data/app/assets/javascripts/profiler.js +45 -0
  10. data/app/views/speed_gun/_meter.html.slim +9 -0
  11. data/lib/speed_gun/app/public/browser.js +65 -0
  12. data/lib/speed_gun/app/public/jquery-1.10.2.min.js +6 -0
  13. data/lib/speed_gun/app/public/profile.js +5 -0
  14. data/lib/speed_gun/app/public/profiler.js +45 -0
  15. data/lib/speed_gun/app/public/style.css +170 -0
  16. data/lib/speed_gun/app/views/meter.html.slim +9 -0
  17. data/lib/speed_gun/app/views/profile.slim +97 -0
  18. data/lib/speed_gun/app.rb +58 -0
  19. data/lib/speed_gun/browser/navigation.rb +23 -0
  20. data/lib/speed_gun/browser/timing.rb +92 -0
  21. data/lib/speed_gun/browser.rb +22 -0
  22. data/lib/speed_gun/config.rb +59 -0
  23. data/lib/speed_gun/hook.rb +25 -0
  24. data/lib/speed_gun/middleware.rb +91 -0
  25. data/lib/speed_gun/profiler/action_controller.rb +12 -0
  26. data/lib/speed_gun/profiler/action_view.rb +12 -0
  27. data/lib/speed_gun/profiler/active_record.rb +16 -0
  28. data/lib/speed_gun/profiler/base.rb +139 -0
  29. data/lib/speed_gun/profiler/js.rb +17 -0
  30. data/lib/speed_gun/profiler/manual.rb +14 -0
  31. data/lib/speed_gun/profiler/rack.rb +7 -0
  32. data/lib/speed_gun/profiler.rb +124 -0
  33. data/lib/speed_gun/railtie.rb +33 -0
  34. data/lib/speed_gun/store/base.rb +9 -0
  35. data/lib/speed_gun/store/file.rb +62 -0
  36. data/lib/speed_gun/store/memcache.rb +27 -0
  37. data/lib/speed_gun/store/memory.rb +22 -0
  38. data/lib/speed_gun/store/redis.rb +28 -0
  39. data/lib/speed_gun/store.rb +6 -0
  40. data/lib/speed_gun/template.rb +15 -0
  41. data/lib/speed_gun/version.rb +3 -0
  42. data/lib/speed_gun.rb +52 -0
  43. data/speed_gun.gemspec +29 -0
  44. metadata +184 -0
@@ -0,0 +1,23 @@
1
+ require 'speed_gun/browser'
2
+
3
+ class SpeedGun::Browser::Navigation < Hash
4
+ NAVIGATION_TYPES = [
5
+ 'Navigate',
6
+ 'Reload',
7
+ 'Back/Forward'
8
+ ]
9
+
10
+ def initialize(hash)
11
+ hash.each_pair do |key, val|
12
+ self[key.to_s.to_sym] = val
13
+ end
14
+ end
15
+
16
+ def type
17
+ NAVIGATION_TYPES[self[:type].to_i] || 'Unknown'
18
+ end
19
+
20
+ def redirect_count
21
+ self[:redirect_count].to_i
22
+ end
23
+ end
@@ -0,0 +1,92 @@
1
+ require 'speed_gun/browser'
2
+
3
+ class SpeedGun::Browser::Timing < Hash
4
+ # rubocop:disable SymbolName
5
+ ATTRIBUTES = [
6
+ :navigationStart,
7
+ :unloadEventStart,
8
+ :unloadEventEnd,
9
+ :redirectStart,
10
+ :redirectEnd,
11
+ :fetchStart,
12
+ :domainLookupStart,
13
+ :domainLookupEnd,
14
+ :connectStart,
15
+ :connectEnd,
16
+ :secureConnectionStart,
17
+ :requestStart,
18
+ :responseStart,
19
+ :responseEnd,
20
+ :domLoading,
21
+ :domInteractive,
22
+ :domContentLoadedEventStart,
23
+ :domContentLoadedEventEnd,
24
+ :domComplete,
25
+ :loadEventStart,
26
+ :loadEventEnd,
27
+ ]
28
+ # rubocop:enable all
29
+
30
+ class Timing
31
+ def initialize(name, base, started_at, ended_at)
32
+ @name = name
33
+ @started_at = started_at - base
34
+ @elapsed_time = ended_at - started_at
35
+ end
36
+ attr_reader :name, :started_at, :elapsed_time
37
+ end
38
+
39
+ def initialize(hash)
40
+ hash.each_pair do |key, val|
41
+ self[key.to_s.to_sym] = val.to_i
42
+ end
43
+ end
44
+
45
+ ATTRIBUTES.each do |key|
46
+ define_method(key) { self[key] }
47
+ end
48
+
49
+ def load_time
50
+ loadEventEnd - navigationStart
51
+ end
52
+
53
+ # rubocop:disable MethodLength
54
+ def timings
55
+ @timings ||= [
56
+ (
57
+ if redirectStart > 0
58
+ Timing.new('Redirect', navigationStart, redirectStart, redirectEnd)
59
+ else
60
+ nil
61
+ end
62
+ ),
63
+ Timing.new('Fetch all', navigationStart, fetchStart, responseEnd),
64
+ Timing.new(
65
+ 'DNS Lookup', navigationStart, domainLookupStart, domainLookupEnd
66
+ ),
67
+ Timing.new(
68
+ 'TCP Connecting', navigationStart, connectStart, connectEnd
69
+ ),
70
+ (
71
+ if secureConnectionStart > 0
72
+ Timing.new('SSL', navigationStart, secureConnectionStart, connectEnd)
73
+ else
74
+ nil
75
+ end
76
+ ),
77
+ Timing.new('Request', navigationStart, requestStart, responseStart),
78
+ Timing.new('Response', navigationStart, responseStart, responseEnd),
79
+ Timing.new('Unload', navigationStart, unloadEventStart, unloadEventEnd),
80
+ Timing.new('DOM Load', navigationStart, domLoading, domInteractive),
81
+ Timing.new(
82
+ 'DOMContentLoaded Event',
83
+ navigationStart, domContentLoadedEventStart, domContentLoadedEventEnd
84
+ ),
85
+ Timing.new(
86
+ 'Sub resources loading', navigationStart, domInteractive, domComplete
87
+ ),
88
+ Timing.new('Load Event', navigationStart, loadEventStart, loadEventEnd),
89
+ ].reject { |timing| timing.nil? }
90
+ end
91
+ # rubocop:enable
92
+ end
@@ -0,0 +1,22 @@
1
+ require 'speed_gun'
2
+ require 'useragent'
3
+
4
+ class SpeedGun::Browser
5
+ def initialize(hash)
6
+ @user_agent = UserAgent.parse(hash['user_agent'] || '')
7
+ @navigation = Navigation.new(hash['navigation'] || {})
8
+ @timing = Timing.new(hash['timing'] || {})
9
+ end
10
+ attr_reader :user_agent, :navigation, :timing
11
+
12
+ def as_msgpack(*args)
13
+ {
14
+ user_agent: @user_agent.to_s,
15
+ navigation: @navigation,
16
+ timing: @timing,
17
+ }
18
+ end
19
+ end
20
+
21
+ require 'speed_gun/browser/navigation'
22
+ require 'speed_gun/browser/timing'
@@ -0,0 +1,59 @@
1
+ require 'speed_gun'
2
+
3
+ class SpeedGun::Config < Hash
4
+ def enable?
5
+ enable && enable_if.call
6
+ end
7
+
8
+ def enable
9
+ fetch(:enable, true)
10
+ end
11
+
12
+ def enable_if
13
+ self[:enable_if] ||= -> { true }
14
+ end
15
+
16
+ def prefix
17
+ self[:prefix] ||= '/speed_gun'
18
+ end
19
+
20
+ def prefix_regexp
21
+ self[:prefix_regexp] ||= /^#{Regexp.escape(prefix)}/x
22
+ end
23
+
24
+ def store
25
+ self[:store] ||= SpeedGun::Store::Memory.new
26
+ end
27
+
28
+ def auto_inject?
29
+ fetch(:auto_inject, true)
30
+ end
31
+
32
+ def backtrace_remove
33
+ self[:backtrace_remove] ||= ''
34
+ end
35
+
36
+ def backtrace_includes
37
+ self[:backtrace_includes] ||= []
38
+ end
39
+
40
+ def show_button?
41
+ fetch(:show_button, true)
42
+ end
43
+
44
+ def no_include_jquery?
45
+ fetch(:no_include_jquery, false)
46
+ end
47
+
48
+ def skip_paths
49
+ self[:skip_paths] ||= [/favicon/]
50
+ end
51
+
52
+ def force_profile?
53
+ fetch(:force_profile, true)
54
+ end
55
+
56
+ def authorize_proc
57
+ self[:authorize_proc] ||= ->(request) { true }
58
+ end
59
+ end
@@ -0,0 +1,25 @@
1
+ require 'speed_gun'
2
+
3
+ class SpeedGun::Hook
4
+ HOOKS = []
5
+
6
+ def self.inherited(klass)
7
+ HOOKS.push(klass) unless HOOKS.include?(klass)
8
+ end
9
+
10
+ def self.invoke_all(profiler)
11
+ HOOKS.each { |hook| hook.invoke(profiler) }
12
+ end
13
+
14
+ def self.invoke(profiler)
15
+ new(profiler).invoke
16
+ end
17
+
18
+ def initialize(profiler)
19
+ @profiler = profiler
20
+ end
21
+ attr_reader :profiler
22
+
23
+ def invoke
24
+ end
25
+ end
@@ -0,0 +1,91 @@
1
+ require 'speed_gun'
2
+ require 'speed_gun/app'
3
+ require 'speed_gun/profiler'
4
+ require 'speed_gun/profiler/rack'
5
+ require 'speed_gun/template'
6
+
7
+ class SpeedGun::Middleware
8
+ BODY_END_REGEXP = /<\/(?:body|html)>/
9
+
10
+ def initialize(app)
11
+ @app = app
12
+ end
13
+
14
+ def call(env)
15
+ return SpeedGun::App.call(env) if under_speed_gun?(env)
16
+
17
+ SpeedGun.current = SpeedGun::Profiler.new(env) if SpeedGun.enable?
18
+
19
+ call_with_speed_gun(env)
20
+ ensure
21
+ SpeedGun.current = nil
22
+ end
23
+
24
+ private
25
+
26
+ def call_with_speed_gun(env)
27
+ remove_conditional_get_headers(env)
28
+
29
+ status, headers, body = *SpeedGun.current.profile(:rack) { @app.call(env) }
30
+
31
+ if SpeedGun.active?
32
+ inject_header(headers)
33
+ SpeedGun.current.dump
34
+ end
35
+
36
+ if SpeedGun.config.auto_inject? && SpeedGun.active?
37
+ inject_body(status, headers, body)
38
+ else
39
+ return [status, headers, body]
40
+ end
41
+ end
42
+
43
+ def remove_conditional_get_headers(env)
44
+ return if !SpeedGun.config.force_profile? && SpeedGun.active?
45
+
46
+ env['HTTP_IF_MODIFIED_SINCE'] = ''
47
+ env['HTTP_IF_NONE_MATCH'] = ''
48
+ end
49
+
50
+ def inject_header(headers)
51
+ if SpeedGun.config.force_profile?
52
+ headers.delete('ETag')
53
+ headers.delete('Date')
54
+ headers['Cache-Control'] = 'must-revalidate, private, max-age=0'
55
+ end
56
+
57
+ headers['X-SPEEDGUN-ID'] = SpeedGun.current.id
58
+ end
59
+
60
+ def inject_body(status, headers, body)
61
+ unless headers['Content-Type'] =~ /text\/html/
62
+ return [status, headers, body]
63
+ end
64
+
65
+ response = Rack::Response.new([], status, headers)
66
+
67
+ body = [body] if body.kind_of?(String)
68
+ body.each { |fragment| response.write(inject_fragment(fragment)) }
69
+ body.close if body.respond_to?(:close)
70
+
71
+ response.finish
72
+ end
73
+
74
+ def inject_fragment(body)
75
+ return body unless body.match(BODY_END_REGEXP)
76
+
77
+ body.sub(BODY_END_REGEXP) do |matched|
78
+ SpeedGun::Template.render + matched
79
+ end
80
+ end
81
+
82
+ def under_speed_gun?(env)
83
+ if SpeedGun.config.prefix_regexp.match(env['PATH_INFO'])
84
+ env['PATH_INFO'] =
85
+ env['PATH_INFO'].sub(SpeedGun.config.prefix_regexp, '')
86
+ true
87
+ else
88
+ false
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,12 @@
1
+ require 'speed_gun/profiler/base'
2
+
3
+ class SpeedGun::Profiler::ActionController < SpeedGun::Profiler::Base
4
+ hook_method ::ActionController::Base, :process
5
+
6
+ attr_reader :action_name
7
+ alias_method :title, :action_name
8
+
9
+ def before_profile(controller, action)
10
+ @action_name = "#{controller.class.name}##{action}"
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'speed_gun/profiler/base'
2
+
3
+ class SpeedGun::Profiler::ActionView < SpeedGun::Profiler::Base
4
+ hook_method ::ActionView::Template, :render
5
+
6
+ attr_reader :template_path
7
+ alias_method :title, :template_path
8
+
9
+ def before_profile(action_view, *args)
10
+ @template_path = action_view.instance_variable_get(:@virtual_path)
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ require 'speed_gun/profiler/base'
2
+
3
+ class SpeedGun::Profiler::ActiveRecord < SpeedGun::Profiler::Base
4
+ def title
5
+ "#{@name}"
6
+ end
7
+
8
+ def html
9
+ %Q{<pre class="sql">#{@sql}</pre>}
10
+ end
11
+
12
+ def before_profile(adapter, sql, name = nil)
13
+ @sql = sql
14
+ @name = name
15
+ end
16
+ end
@@ -0,0 +1,139 @@
1
+ require 'speed_gun/profiler'
2
+ require 'securerandom'
3
+
4
+ class SpeedGun::Profiler::Base
5
+ def self.label
6
+ name.sub(/.*::/, '')
7
+ end
8
+
9
+ def self.profiler_type
10
+ name\
11
+ .sub(/.*::/, '')\
12
+ .gsub(/(.)([A-Z])/) { |m| "#{$1}_#{$2.downcase}" }\
13
+ .downcase\
14
+ .to_sym
15
+ end
16
+
17
+ def self.inherited(klass)
18
+ SpeedGun::Profiler::PROFILERS[klass.profiler_type] = klass
19
+ end
20
+
21
+ def self.hook_method(klass, method_name)
22
+ without_profiling = "#{method_name}_withtout_profile".intern
23
+ with_profiling = "#{method_name}_with_profile".intern
24
+ return unless klass.send(:method_defined?, method_name)
25
+ return if klass.send(:method_defined?, with_profiling)
26
+
27
+ profiler = profiler_type
28
+ klass.send(:alias_method, without_profiling, method_name)
29
+ klass.send(:define_method, with_profiling) do |*args, &block|
30
+ return send(without_profiling, *args, &block) unless SpeedGun.current
31
+
32
+ profile_args = [self] + args
33
+ SpeedGun.current.profile(profiler, *profile_args) do
34
+ send(without_profiling, *args, &block)
35
+ end
36
+ end
37
+ klass.send(:alias_method, method_name, with_profiling)
38
+ end
39
+
40
+ def self.load(data)
41
+ data.delete('label')
42
+ type = data.delete('type')
43
+ profiler = SpeedGun::Profiler::PROFILERS[type.to_sym]
44
+ profile = profiler.new
45
+
46
+ data.each_pair do |key, val|
47
+ profile.send(:instance_variable_set, :"@#{key}", val)
48
+ end
49
+
50
+ profile
51
+ end
52
+
53
+ def self.profile(profiler, *args, &block)
54
+ profile = new
55
+ profiler.profiles << profile
56
+ profile.profile(*args, &block)
57
+ end
58
+
59
+ def initialize
60
+ @id = SecureRandom.uuid
61
+ @parent_profile_id = nil
62
+ @elapsed_time = 0
63
+ @backtrace = []
64
+ end
65
+ attr_reader :id, :elapsed_time, :parent_profile_id, :backtrace
66
+
67
+ def title
68
+ warn 'Override this method'
69
+ end
70
+
71
+ def html
72
+ ''
73
+ end
74
+
75
+ def label
76
+ self.class.label
77
+ end
78
+
79
+ def type
80
+ self.class.profiler_type
81
+ end
82
+
83
+ def profile(*args, &block)
84
+ @backtrace = cleanup_caller(caller(3))
85
+ parent_profile = SpeedGun.current.now_profile
86
+ SpeedGun.current.now_profile = self
87
+ call_without_defined(:before_profile, *args, &block)
88
+ result = measure(&block)
89
+ call_without_defined(:after_profile, *args, &block)
90
+ return result
91
+ ensure
92
+ SpeedGun.current.now_profile = parent_profile
93
+ @parent_profile_id = parent_profile.id if parent_profile
94
+ end
95
+
96
+ def measure(&block)
97
+ now = Time.now
98
+ result = yield
99
+ @elapsed_time = Time.now - now
100
+
101
+ result
102
+ end
103
+
104
+ def as_msgpack(*args)
105
+ hash = {}
106
+ instance_variables.each do |key|
107
+ unless key.to_s =~ /^\@_/
108
+ hash[key.to_s.sub(/^\@/, '')] = instance_variable_get(key)
109
+ end
110
+ end
111
+ hash['type'] = type.to_s
112
+ hash['label'] = label
113
+ hash['title'] = title
114
+ hash
115
+ end
116
+
117
+ def to_msgpack(*args)
118
+ as_msgpack(*args).to_msgpack(*args)
119
+ end
120
+
121
+ private
122
+
123
+ def call_without_defined(method, *args)
124
+ send(method, *args) if respond_to?(method)
125
+ end
126
+
127
+ def cleanup_caller(caller)
128
+ backtraces = caller.map do |backtrace|
129
+ backtrace.sub(SpeedGun.config.backtrace_remove, '')
130
+ end
131
+ backtraces.reject! do |backtrace|
132
+ !SpeedGun.config.backtrace_includes.any? do |regexp|
133
+ backtrace =~ regexp
134
+ end
135
+ end
136
+
137
+ backtraces
138
+ end
139
+ end
@@ -0,0 +1,17 @@
1
+ require 'speed_gun/profiler/base'
2
+
3
+ class SpeedGun::Profiler::Js < SpeedGun::Profiler::Base
4
+ def self.profile(profiler, title, elapsed_time, backtrace)
5
+ profile = new
6
+ profiler.profiles << profile
7
+ profile.save(title, elapsed_time, backtrace)
8
+ end
9
+
10
+ attr_reader :title
11
+
12
+ def save(title, elapsed_time, backtrace)
13
+ @title = title.to_s
14
+ @elapsed_time = elapsed_time.to_i * 0.001
15
+ @backtrace = backtrace
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ require 'speed_gun/profiler/base'
2
+
3
+ class SpeedGun::Profiler::Manual < SpeedGun::Profiler::Base
4
+ def self.label
5
+ 'Manual'
6
+ end
7
+
8
+ attr_reader :html
9
+
10
+ def before_profile(title, html = '')
11
+ @title = title
12
+ @html = html
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ require 'speed_gun/profiler/base'
2
+
3
+ class SpeedGun::Profiler::Rack < SpeedGun::Profiler::Base
4
+ def title
5
+ 'Rack Total'
6
+ end
7
+ end
@@ -0,0 +1,124 @@
1
+ require 'speed_gun'
2
+ require 'speed_gun/store'
3
+ require 'speed_gun/browser'
4
+ require 'speed_gun/hook'
5
+ require 'securerandom'
6
+ require 'msgpack'
7
+ require 'multi_json'
8
+
9
+ class SpeedGun::Profiler
10
+ PROFILERS = {}
11
+
12
+ def self.load(id)
13
+ src = SpeedGun.store[id]
14
+
15
+ return nil unless src
16
+
17
+ data = MessagePack.unpack(src)
18
+
19
+ profiler = new({})
20
+ profiler.restore_by_hash(data)
21
+
22
+ profiler
23
+ end
24
+
25
+ def initialize(env)
26
+ @id = SecureRandom.uuid
27
+ @path = env['PATH_INFO']
28
+ @query = env['QUERY_STRING']
29
+ @env = env
30
+ @requested_at = Time.now
31
+ @profiles = []
32
+ @browser = nil
33
+ @active = true
34
+ @now_profile = nil
35
+ end
36
+ attr_reader :id, :path, :query, :env, :requested_at, :profiles, :browser
37
+ attr_accessor :now_profile
38
+
39
+ def profile(type, *args, &block)
40
+ profiler = PROFILERS[type]
41
+
42
+ if profiler
43
+ profiler.profile(self, *args, &block)
44
+ else
45
+ yield
46
+ end
47
+ end
48
+
49
+ def skip?
50
+ SpeedGun.config.skip_paths.any? { |prefix| prefix.match(@path) }
51
+ end
52
+
53
+ def active?
54
+ @active && !skip?
55
+ end
56
+
57
+ def activate!
58
+ @active = true
59
+ end
60
+
61
+ def deactivate!
62
+ @active = false
63
+ end
64
+
65
+ def dump
66
+ SpeedGun.store[id] = to_msgpack
67
+
68
+ SpeedGun::Hook.invoke_all(self)
69
+ end
70
+
71
+ def browser=(hash)
72
+ @browser = SpeedGun::Browser.new(hash)
73
+ end
74
+
75
+ def as_msgpack(*args)
76
+ {
77
+ id: @id,
78
+ path: @path,
79
+ query: @query,
80
+ env: msgpackable_env,
81
+ requested_at: @requested_at.to_i,
82
+ profiles: @profiles.map { |profile| profile.as_msgpack(*args) },
83
+ browser: @browser ? @browser.as_msgpack(*args) : nil,
84
+ }
85
+ end
86
+
87
+ def to_msgpack(*args)
88
+ as_msgpack(*args).to_msgpack(*args)
89
+ end
90
+
91
+ def to_json(*args)
92
+ MultiJson.dump(as_msgpack(*args))
93
+ end
94
+
95
+ def restore_by_hash(hash)
96
+ hash.each_pair do |key, val|
97
+ instance_variable_set(:"@#{key}", restore_attribute(key, val))
98
+ end
99
+ end
100
+
101
+ def restore_attribute(key, val)
102
+ case key
103
+ when 'requested_at'
104
+ Time.at(val)
105
+ when 'profiles'
106
+ val.map { |profile| SpeedGun::Profiler::Base.load(profile) }
107
+ when 'browser'
108
+ val ? SpeedGun::Browser.new(val) : val
109
+ else
110
+ val
111
+ end
112
+ end
113
+
114
+ private
115
+
116
+ def msgpackable_env
117
+ env = {}
118
+ @env.each_pair { |key, val| env[key] = val if key[0] =~ /[A-Z]/ }
119
+ env
120
+ end
121
+ end
122
+
123
+ require 'speed_gun/profiler/manual'
124
+ require 'speed_gun/profiler/js'
@@ -0,0 +1,33 @@
1
+ require 'speed_gun'
2
+ require 'speed_gun/store/file'
3
+
4
+ class SpeedGun::Railtie < ::Rails::Railtie
5
+ initializer 'speed_gun' do |app|
6
+ app.middleware.insert(0, SpeedGun::Middleware)
7
+
8
+ SpeedGun.config[:enable_if] = -> { Rails.env.development? }
9
+ SpeedGun.config[:backtrace_remove] = Rails.root.to_s + '/'
10
+ SpeedGun.config[:backtrace_includes] = [/^(app|config|lib|test|spec)/]
11
+ SpeedGun.config[:authorize_proc] = ->(request) { Rails.env.development? }
12
+ SpeedGun.config.skip_paths << /^#{Regexp.escape(app.config.assets.prefix)}/
13
+ SpeedGun.config[:store] =
14
+ SpeedGun::Store::File.new(path: Rails.root.join('tmp/speed_gun'))
15
+
16
+ ActiveSupport.on_load(:action_controller) do
17
+ require 'speed_gun/profiler/action_controller'
18
+ end
19
+
20
+ ActiveSupport.on_load(:action_view) do
21
+ require 'speed_gun/profiler/action_view'
22
+ end
23
+
24
+ ActiveSupport.on_load(:active_record) do
25
+ require 'speed_gun/profiler/active_record'
26
+
27
+ SpeedGun::Profiler::ActiveRecord.hook_method(
28
+ ActiveRecord::Base.connection.class,
29
+ :execute
30
+ )
31
+ end
32
+ end
33
+ end