speed_gun 0.0.1

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