miniprofiler 0.1.7.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.
- data/.gitignore +4 -0
- data/CHANGELOG +32 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +64 -0
- data/README.md +110 -0
- data/Rakefile +40 -0
- data/autotest/discover.rb +2 -0
- data/lib/mini_profiler/body_add_proxy.rb +45 -0
- data/lib/mini_profiler/client_timer_struct.rb +76 -0
- data/lib/mini_profiler/config.rb +52 -0
- data/lib/mini_profiler/context.rb +10 -0
- data/lib/mini_profiler/page_timer_struct.rb +53 -0
- data/lib/mini_profiler/profiler.rb +405 -0
- data/lib/mini_profiler/profiling_methods.rb +73 -0
- data/lib/mini_profiler/request_timer_struct.rb +96 -0
- data/lib/mini_profiler/sql_timer_struct.rb +48 -0
- data/lib/mini_profiler/storage/abstract_store.rb +27 -0
- data/lib/mini_profiler/storage/file_store.rb +108 -0
- data/lib/mini_profiler/storage/memory_store.rb +68 -0
- data/lib/mini_profiler/storage/redis_store.rb +44 -0
- data/lib/mini_profiler/timer_struct.rb +31 -0
- data/lib/mini_profiler_rails/railtie.rb +84 -0
- data/lib/patches/sql_patches.rb +185 -0
- data/lib/rack-mini-profiler.rb +6 -0
- data/rack-mini-profiler.gemspec +23 -0
- data/spec/components/body_add_proxy_spec.rb +90 -0
- data/spec/components/client_timer_struct_spec.rb +165 -0
- data/spec/components/file_store_spec.rb +47 -0
- data/spec/components/memory_store_spec.rb +40 -0
- data/spec/components/page_timer_struct_spec.rb +33 -0
- data/spec/components/profiler_spec.rb +126 -0
- data/spec/components/redis_store_spec.rb +44 -0
- data/spec/components/request_timer_struct_spec.rb +148 -0
- data/spec/components/sql_timer_struct_spec.rb +63 -0
- data/spec/components/timer_struct_spec.rb +47 -0
- data/spec/fixtures/simple_client_request.yml +17 -0
- data/spec/fixtures/weird_client_request.yml +13 -0
- data/spec/integration/mini_profiler_spec.rb +142 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/expand_each_to_matcher.rb +8 -0
- data/test_old/config.ru +41 -0
- metadata +155 -0
@@ -0,0 +1,108 @@
|
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
class FileStore < AbstractStore
|
4
|
+
|
5
|
+
class FileCache
|
6
|
+
def initialize(path, prefix)
|
7
|
+
@path = path
|
8
|
+
@prefix = prefix
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](key)
|
12
|
+
begin
|
13
|
+
data = ::File.open(path(key),"rb") {|f| f.read}
|
14
|
+
return Marshal.load data
|
15
|
+
rescue => e
|
16
|
+
return nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def []=(key,val)
|
21
|
+
::File.open(path(key), "wb+") {|f| f.write Marshal.dump(val)}
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def path(key)
|
26
|
+
@path + "/" + @prefix + "_" + key
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
EXPIRE_TIMER_CACHE = 3600 * 24
|
31
|
+
|
32
|
+
def initialize(args)
|
33
|
+
@path = args[:path]
|
34
|
+
raise ArgumentError.new :path unless @path
|
35
|
+
@timer_struct_cache = FileCache.new(@path, "mp_timers")
|
36
|
+
@timer_struct_lock = Mutex.new
|
37
|
+
@user_view_cache = FileCache.new(@path, "mp_views")
|
38
|
+
@user_view_lock = Mutex.new
|
39
|
+
|
40
|
+
me = self
|
41
|
+
Thread.new do
|
42
|
+
while true do
|
43
|
+
me.cleanup_cache if MiniProfiler.instance
|
44
|
+
sleep(3600)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def save(page_struct)
|
50
|
+
@timer_struct_lock.synchronize {
|
51
|
+
@timer_struct_cache[page_struct['Id']] = page_struct
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def load(id)
|
56
|
+
@timer_struct_lock.synchronize {
|
57
|
+
@timer_struct_cache[id]
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_unviewed(user, id)
|
62
|
+
@user_view_lock.synchronize {
|
63
|
+
current = @user_view_cache[user]
|
64
|
+
current = [] unless Array === current
|
65
|
+
current << id
|
66
|
+
@user_view_cache[user] = current.uniq
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def set_viewed(user, id)
|
71
|
+
@user_view_lock.synchronize {
|
72
|
+
@user_view_cache[user] ||= []
|
73
|
+
current = @user_view_cache[user]
|
74
|
+
current = [] unless Array === current
|
75
|
+
current.delete(id)
|
76
|
+
@user_view_cache[user] = current.uniq
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def get_unviewed_ids(user)
|
81
|
+
@user_view_lock.synchronize {
|
82
|
+
@user_view_cache[user]
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
|
90
|
+
def cleanup_cache
|
91
|
+
files = Dir.entries(@path)
|
92
|
+
@timer_struct_lock.synchronize {
|
93
|
+
files.each do |f|
|
94
|
+
f = @path + '/' + f
|
95
|
+
File.delete f if f =~ /^mp_timers/ and (Time.now - File.mtime(f)) > EXPIRE_TIMER_CACHE
|
96
|
+
end
|
97
|
+
}
|
98
|
+
@user_view_lock.synchronize {
|
99
|
+
files.each do |f|
|
100
|
+
f = @path + '/' + f
|
101
|
+
File.delete f if f =~ /^mp_views/ and (Time.now - File.mtime(f)) > EXPIRE_TIMER_CACHE
|
102
|
+
end
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
class MemoryStore < AbstractStore
|
4
|
+
|
5
|
+
EXPIRE_TIMER_CACHE = 3600 * 24
|
6
|
+
|
7
|
+
def initialize(args)
|
8
|
+
@timer_struct_lock = Mutex.new
|
9
|
+
@timer_struct_cache = {}
|
10
|
+
@user_view_lock = Mutex.new
|
11
|
+
@user_view_cache = {}
|
12
|
+
|
13
|
+
# TODO: fix it to use weak ref, trouble is may be broken in 1.9 so need to use the 'ref' gem
|
14
|
+
me = self
|
15
|
+
Thread.new do
|
16
|
+
while true do
|
17
|
+
me.cleanup_cache
|
18
|
+
sleep(3600)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def save(page_struct)
|
24
|
+
@timer_struct_lock.synchronize {
|
25
|
+
@timer_struct_cache[page_struct['Id']] = page_struct
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def load(id)
|
30
|
+
@timer_struct_lock.synchronize {
|
31
|
+
@timer_struct_cache[id]
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def set_unviewed(user, id)
|
36
|
+
@user_view_lock.synchronize {
|
37
|
+
@user_view_cache[user] ||= []
|
38
|
+
@user_view_cache[user] << id
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def set_viewed(user, id)
|
43
|
+
@user_view_lock.synchronize {
|
44
|
+
@user_view_cache[user] ||= []
|
45
|
+
@user_view_cache[user].delete(id)
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_unviewed_ids(user)
|
50
|
+
@user_view_lock.synchronize {
|
51
|
+
@user_view_cache[user]
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
|
59
|
+
def cleanup_cache
|
60
|
+
expire_older_than = ((Time.now.to_f - MiniProfiler::MemoryStore::EXPIRE_TIMER_CACHE) * 1000).to_i
|
61
|
+
@timer_struct_lock.synchronize {
|
62
|
+
@timer_struct_cache.delete_if { |k, v| v['Root']['StartMilliseconds'] < expire_older_than }
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
class RedisStore < AbstractStore
|
4
|
+
|
5
|
+
EXPIRE_SECONDS = 60 * 60 * 24
|
6
|
+
|
7
|
+
def initialize(args)
|
8
|
+
args ||= {}
|
9
|
+
@prefix = args[:prefix] || 'MPRedisStore'
|
10
|
+
end
|
11
|
+
|
12
|
+
def save(page_struct)
|
13
|
+
redis.setex "#{@prefix}#{page_struct['Id']}", EXPIRE_SECONDS, Marshal::dump(page_struct)
|
14
|
+
end
|
15
|
+
|
16
|
+
def load(id)
|
17
|
+
raw = redis.get "#{@prefix}#{id}"
|
18
|
+
if raw
|
19
|
+
Marshal::load raw
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def set_unviewed(user, id)
|
24
|
+
redis.sadd "#{@prefix}-#{user}-v", id
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_viewed(user, id)
|
28
|
+
redis.srem "#{@prefix}-#{user}-v", id
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_unviewed_ids(user)
|
32
|
+
redis.smembers "#{@prefix}-#{user}-v"
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def redis
|
38
|
+
require 'redis' unless defined? Redis
|
39
|
+
Redis.new
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
|
4
|
+
# A base class for timing structures
|
5
|
+
class TimerStruct
|
6
|
+
|
7
|
+
def initialize(attrs={})
|
8
|
+
@attributes = attrs
|
9
|
+
end
|
10
|
+
|
11
|
+
def attributes
|
12
|
+
@attributes ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](name)
|
16
|
+
attributes[name]
|
17
|
+
end
|
18
|
+
|
19
|
+
def []=(name, val)
|
20
|
+
attributes[name] = val
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_json(*a)
|
25
|
+
::JSON.generate(@attributes, a[0])
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module MiniProfilerRails
|
4
|
+
|
5
|
+
class Railtie < ::Rails::Railtie
|
6
|
+
|
7
|
+
initializer "rack_mini_profiler.configure_rails_initialization" do |app|
|
8
|
+
c = Rack::MiniProfiler.config
|
9
|
+
|
10
|
+
# By default, only show the MiniProfiler in development mode, in production allow profiling if post_authorize_cb is set
|
11
|
+
c.pre_authorize_cb = lambda { |env|
|
12
|
+
Rails.env.development? || Rails.env.production?
|
13
|
+
}
|
14
|
+
|
15
|
+
if Rails.env.development?
|
16
|
+
c.skip_paths ||= []
|
17
|
+
c.skip_paths << "/assets/"
|
18
|
+
c.skip_schema_queries = true
|
19
|
+
end
|
20
|
+
|
21
|
+
if Rails.env.production?
|
22
|
+
c.authorization_mode = :whitelist
|
23
|
+
end
|
24
|
+
|
25
|
+
# The file store is just so much less flaky
|
26
|
+
tmp = Rails.root.to_s + "/tmp/miniprofiler"
|
27
|
+
FileUtils.mkdir_p(tmp) unless File.exists?(tmp)
|
28
|
+
|
29
|
+
c.storage_options = {:path => tmp}
|
30
|
+
c.storage = Rack::MiniProfiler::FileStore
|
31
|
+
|
32
|
+
# Quiet the SQL stack traces
|
33
|
+
c.backtrace_remove = Rails.root.to_s + "/"
|
34
|
+
c.backtrace_filter = /^\/?(app|config|lib|test)/
|
35
|
+
c.skip_schema_queries = Rails.env != 'production'
|
36
|
+
|
37
|
+
# Install the Middleware
|
38
|
+
app.middleware.insert(0, Rack::MiniProfiler)
|
39
|
+
|
40
|
+
# Attach to various Rails methods
|
41
|
+
::Rack::MiniProfiler.profile_method(ActionController::Base, :process) {|action| "Executing action: #{action}"}
|
42
|
+
::Rack::MiniProfiler.profile_method(ActionView::Template, :render) {|x,y| "Rendering: #{@virtual_path}"}
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
# TODO: Implement something better here
|
47
|
+
# config.after_initialize do
|
48
|
+
#
|
49
|
+
# class ::ActionView::Helpers::AssetTagHelper::JavascriptIncludeTag
|
50
|
+
# alias_method :asset_tag_orig, :asset_tag
|
51
|
+
# def asset_tag(source,options)
|
52
|
+
# current = Rack::MiniProfiler.current
|
53
|
+
# return asset_tag_orig(source,options) unless current
|
54
|
+
# wrapped = ""
|
55
|
+
# unless current.mpt_init
|
56
|
+
# current.mpt_init = true
|
57
|
+
# wrapped << Rack::MiniProfiler::ClientTimerStruct.init_instrumentation
|
58
|
+
# end
|
59
|
+
# name = source.split('/')[-1]
|
60
|
+
# wrapped << Rack::MiniProfiler::ClientTimerStruct.instrument(name, asset_tag_orig(source,options)).html_safe
|
61
|
+
# wrapped
|
62
|
+
# end
|
63
|
+
# end
|
64
|
+
|
65
|
+
# class ::ActionView::Helpers::AssetTagHelper::StylesheetIncludeTag
|
66
|
+
# alias_method :asset_tag_orig, :asset_tag
|
67
|
+
# def asset_tag(source,options)
|
68
|
+
# current = Rack::MiniProfiler.current
|
69
|
+
# return asset_tag_orig(source,options) unless current
|
70
|
+
# wrapped = ""
|
71
|
+
# unless current.mpt_init
|
72
|
+
# current.mpt_init = true
|
73
|
+
# wrapped << Rack::MiniProfiler::ClientTimerStruct.init_instrumentation
|
74
|
+
# end
|
75
|
+
# name = source.split('/')[-1]
|
76
|
+
# wrapped << Rack::MiniProfiler::ClientTimerStruct.instrument(name, asset_tag_orig(source,options)).html_safe
|
77
|
+
# wrapped
|
78
|
+
# end
|
79
|
+
# end
|
80
|
+
|
81
|
+
# end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
class SqlPatches
|
2
|
+
|
3
|
+
def self.patched?
|
4
|
+
@patched
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.patched=(val)
|
8
|
+
@patched = val
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.class_exists?(name)
|
12
|
+
eval(name + ".class").to_s.eql?('Class')
|
13
|
+
rescue NameError
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.module_exists?(name)
|
18
|
+
eval(name + ".class").to_s.eql?('Module')
|
19
|
+
rescue NameError
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# The best kind of instrumentation is in the actual db provider, however we don't want to double instrument
|
25
|
+
if SqlPatches.class_exists? "Mysql2::Client"
|
26
|
+
|
27
|
+
class Mysql2::Result
|
28
|
+
alias_method :each_without_profiling, :each
|
29
|
+
def each(*args, &blk)
|
30
|
+
return each_without_profiling(*args, &blk) unless @miniprofiler_sql_id
|
31
|
+
|
32
|
+
start = Time.now
|
33
|
+
result = each_without_profiling(*args,&blk)
|
34
|
+
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
35
|
+
|
36
|
+
@miniprofiler_sql_id.report_reader_duration(elapsed_time)
|
37
|
+
result
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Mysql2::Client
|
42
|
+
alias_method :query_without_profiling, :query
|
43
|
+
def query(*args,&blk)
|
44
|
+
current = ::Rack::MiniProfiler.current
|
45
|
+
return query_without_profiling(*args,&blk) unless current
|
46
|
+
|
47
|
+
start = Time.now
|
48
|
+
result = query_without_profiling(*args,&blk)
|
49
|
+
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
50
|
+
result.instance_variable_set("@miniprofiler_sql_id", ::Rack::MiniProfiler.record_sql(args[0], elapsed_time))
|
51
|
+
|
52
|
+
result
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
SqlPatches.patched = true
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
# PG patches, keep in mind exec and async_exec have a exec{|r| } semantics that is yet to be implemented
|
62
|
+
if SqlPatches.class_exists? "PG::Result"
|
63
|
+
|
64
|
+
class PG::Result
|
65
|
+
alias_method :each_without_profiling, :each
|
66
|
+
alias_method :values_without_profiling, :values
|
67
|
+
|
68
|
+
def values(*args, &blk)
|
69
|
+
return values_without_profiling(*args, &blk) unless @miniprofiler_sql_id
|
70
|
+
|
71
|
+
start = Time.now
|
72
|
+
result = values_without_profiling(*args,&blk)
|
73
|
+
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
74
|
+
|
75
|
+
@miniprofiler_sql_id.report_reader_duration(elapsed_time)
|
76
|
+
result
|
77
|
+
end
|
78
|
+
|
79
|
+
def each(*args, &blk)
|
80
|
+
return each_without_profiling(*args, &blk) unless @miniprofiler_sql_id
|
81
|
+
|
82
|
+
start = Time.now
|
83
|
+
result = each_without_profiling(*args,&blk)
|
84
|
+
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
85
|
+
|
86
|
+
@miniprofiler_sql_id.report_reader_duration(elapsed_time)
|
87
|
+
result
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class PG::Connection
|
92
|
+
alias_method :exec_without_profiling, :exec
|
93
|
+
alias_method :async_exec_without_profiling, :async_exec
|
94
|
+
|
95
|
+
def exec(*args,&blk)
|
96
|
+
current = ::Rack::MiniProfiler.current
|
97
|
+
return exec_without_profiling(*args,&blk) unless current
|
98
|
+
|
99
|
+
start = Time.now
|
100
|
+
result = exec_without_profiling(*args,&blk)
|
101
|
+
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
102
|
+
result.instance_variable_set("@miniprofiler_sql_id", ::Rack::MiniProfiler.record_sql(args[0], elapsed_time))
|
103
|
+
|
104
|
+
result
|
105
|
+
end
|
106
|
+
|
107
|
+
def async_exec(*args,&blk)
|
108
|
+
current = ::Rack::MiniProfiler.current
|
109
|
+
return exec_without_profiling(*args,&blk) unless current
|
110
|
+
|
111
|
+
start = Time.now
|
112
|
+
result = exec_without_profiling(*args,&blk)
|
113
|
+
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
114
|
+
result.instance_variable_set("@miniprofiler_sql_id", ::Rack::MiniProfiler.record_sql(args[0], elapsed_time))
|
115
|
+
|
116
|
+
result
|
117
|
+
end
|
118
|
+
|
119
|
+
alias_method :query, :exec
|
120
|
+
end
|
121
|
+
|
122
|
+
SqlPatches.patched = true
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
# Fallback for sequel
|
128
|
+
if SqlPatches.class_exists?("Sequel::Database") && !SqlPatches.patched?
|
129
|
+
module Sequel
|
130
|
+
class Database
|
131
|
+
alias_method :log_duration_original, :log_duration
|
132
|
+
def log_duration(duration, message)
|
133
|
+
::Rack::MiniProfiler.record_sql(message, duration)
|
134
|
+
log_duration_original(duration, message)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
## based off https://github.com/newrelic/rpm/blob/master/lib/new_relic/agent/instrumentation/active_record.rb
|
142
|
+
## fallback for alls sorts of weird dbs
|
143
|
+
if SqlPatches.module_exists?('ActiveRecord')
|
144
|
+
module Rack
|
145
|
+
class MiniProfiler
|
146
|
+
module ActiveRecordInstrumentation
|
147
|
+
def self.included(instrumented_class)
|
148
|
+
instrumented_class.class_eval do
|
149
|
+
unless instrumented_class.method_defined?(:log_without_miniprofiler)
|
150
|
+
alias_method :log_without_miniprofiler, :log
|
151
|
+
alias_method :log, :log_with_miniprofiler
|
152
|
+
protected :log
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def log_with_miniprofiler(*args, &block)
|
158
|
+
current = ::Rack::MiniProfiler.current
|
159
|
+
return log_without_miniprofiler(*args, &block) unless current
|
160
|
+
|
161
|
+
sql, name, binds = args
|
162
|
+
t0 = Time.now
|
163
|
+
rval = log_without_miniprofiler(*args, &block)
|
164
|
+
|
165
|
+
# Don't log schema queries if the option is set
|
166
|
+
return rval if Rack::MiniProfiler.config.skip_schema_queries and name =~ /SCHEMA/
|
167
|
+
|
168
|
+
elapsed_time = ((Time.now - t0).to_f * 1000).round(1)
|
169
|
+
Rack::MiniProfiler.record_sql(sql, elapsed_time)
|
170
|
+
rval
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.insert_instrumentation
|
176
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.module_eval do
|
177
|
+
include ::Rack::MiniProfiler::ActiveRecordInstrumentation
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
if defined?(::Rails) && !SqlPatches.patched?
|
182
|
+
insert_instrumentation
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
require File.expand_path('mini_profiler/profiler', File.dirname(__FILE__) )
|
2
|
+
require File.expand_path('patches/sql_patches', File.dirname(__FILE__) )
|
3
|
+
|
4
|
+
if defined?(::Rails) && ::Rails::VERSION::MAJOR.to_i == 3
|
5
|
+
require File.expand_path('mini_profiler_rails/railtie', File.dirname(__FILE__) )
|
6
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "miniprofiler"
|
3
|
+
s.version = "0.1.7.1"
|
4
|
+
s.summary = "Profiles loading speed for rack applications."
|
5
|
+
s.authors = ["Aleks Totic","Sam Saffron", "Robin Ward"]
|
6
|
+
s.description = "Page loading speed displayed on every page. Optimize while you develop, performance is a feature."
|
7
|
+
s.email = "sam.saffron@gmail.com"
|
8
|
+
s.homepage = "http://miniprofiler.com"
|
9
|
+
s.files = `git ls-files`.split("\n")
|
10
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
11
|
+
s.extra_rdoc_files = [
|
12
|
+
"README.md",
|
13
|
+
"CHANGELOG"
|
14
|
+
]
|
15
|
+
s.add_runtime_dependency 'rack', '>= 1.1.3'
|
16
|
+
if RUBY_VERSION < "1.9"
|
17
|
+
s.add_runtime_dependency 'json', '>= 1.6'
|
18
|
+
end
|
19
|
+
|
20
|
+
s.add_development_dependency 'rake'
|
21
|
+
s.add_development_dependency 'rack-test'
|
22
|
+
s.add_development_dependency 'activerecord', '~> 3.0'
|
23
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mini_profiler/body_add_proxy'
|
3
|
+
|
4
|
+
describe Rack::MiniProfiler::BodyAddProxy do
|
5
|
+
|
6
|
+
context 'body as an array' do
|
7
|
+
|
8
|
+
before do
|
9
|
+
@proxy = Rack::MiniProfiler::BodyAddProxy.new(%w(a b c), 'd')
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'contains the appended value' do
|
13
|
+
@proxy.should expand_each_to %w(a b c d)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'has a correct value in to_str' do
|
17
|
+
@proxy.to_str.should == "abcd"
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'delegation' do
|
21
|
+
it 'delegates respond to <<' do
|
22
|
+
@proxy.respond_to?('<<').should be_true
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'delegates respond to first' do
|
26
|
+
@proxy.respond_to?(:first).should be_true
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'delegates method_missing' do
|
30
|
+
@proxy.first.should == 'a'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'body as a custom object' do
|
37
|
+
|
38
|
+
# A class and a super class to help us test appending to a custom object, such as
|
39
|
+
# Rails' ActionDispatch::Response
|
40
|
+
class Band
|
41
|
+
def style
|
42
|
+
'rock'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Beatles < Band
|
47
|
+
def each
|
48
|
+
yield 'john'
|
49
|
+
yield 'paul'
|
50
|
+
yield 'george'
|
51
|
+
end
|
52
|
+
|
53
|
+
def fake_method; nil; end
|
54
|
+
|
55
|
+
def method_missing(*args, &block)
|
56
|
+
'yoko'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
before do
|
61
|
+
@proxy = Rack::MiniProfiler::BodyAddProxy.new(Beatles.new, 'ringo')
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'contains the appended value' do
|
65
|
+
@proxy.should expand_each_to %w(john paul george ringo)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'has a correct value in to_str' do
|
69
|
+
@proxy.to_str.should == "johnpaulgeorgeringo"
|
70
|
+
end
|
71
|
+
|
72
|
+
describe 'delegation' do
|
73
|
+
it 'delegates respond to fake_method' do
|
74
|
+
@proxy.respond_to?(:fake_method).should be_true
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'delegates respond to a super class' do
|
78
|
+
@proxy.respond_to?(:style).should be_true
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'delegates method_missing' do
|
82
|
+
@proxy.doesnt_exist.should == 'yoko'
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
end
|