miniprofiler 0.1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|