rack-mini-profiler 0.1.1 → 0.1.6

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.

Potentially problematic release.


This version of rack-mini-profiler might be problematic. Click here for more details.

@@ -0,0 +1,26 @@
1
+ 28-June-2012 - Sam
2
+
3
+ * Started change log
4
+ * Corrected profiler so it properly captures POST requests (was supressing non 200s)
5
+ * Amended Rack.MiniProfiler.config[:user_provider] to use ip addres for identity
6
+ * Fixed bug where unviewed missing ids never got cleared
7
+ * Supress all '/assets/' in the rails tie (makes debugging easier)
8
+ * record_sql was mega buggy
9
+
10
+ 9-July-2012 - Sam
11
+
12
+ * Cleaned up mechanism for profiling in production, all you need to do now
13
+ is call Rack::MiniProfiler.authorize_request to get profiling working in
14
+ production
15
+ * Added option to display full backtraces pp=full-backtrace
16
+ * Cleaned up railties, got rid of the post authorize callback
17
+ * Version 0.1.3
18
+
19
+ 12-July-2012 - Sam
20
+
21
+ * Fixed incorrect profiling steps (was not indenting or measuring start time right
22
+ * Implemented native PG and MySql2 interceptors, this gives way more accurate times
23
+ * Refactored context so its a proper class and not a hash
24
+ * Added some more client probing built in to rails
25
+ * More tests
26
+
data/README.md CHANGED
@@ -1,27 +1,31 @@
1
1
  # rack-mini-profiler
2
2
 
3
- Middleware that displays speed badge for every html page.
3
+ Middleware that displays speed badge for every html page. Designed to work both in production and in development.
4
4
 
5
- ## What does it do
6
-
7
- MiniProfiler keeps you aware of your site's performance as you are developing it.
8
- It does this by....
9
-
10
- `env['profiler.mini']` is the profiler
11
-
12
- ## Using mini-profiler in your app
5
+ ## Using rack-mini-profiler in your app
13
6
 
14
7
  Install/add to Gemfile
15
8
 
16
9
  ```ruby
17
10
  gem 'rack-mini-profiler'
18
11
  ```
12
+ Using Rails:
19
13
 
20
- Add it to your middleware stack:
14
+ All you have to do is include the Gem and you're good to go in development.
21
15
 
22
- Using Rails:
16
+ rack-mini-profiler is designed with production profiling in mind. To enable that just run `Rack::MiniProfiler.authorize_request` once you know a request is allowed to profile.
17
+
18
+ For example:
19
+
20
+ ```ruby
21
+ # A hook in your ApplicationController
22
+ def authorize
23
+ if current_user.is_admin?
24
+ Rack::MiniProfiler.authorize_request
25
+ end
26
+ end
27
+ ````
23
28
 
24
- All you have to do is include the Gem and you're good to go.
25
29
 
26
30
  Using Builder:
27
31
 
@@ -58,15 +62,18 @@ You can set configuration options using the configuration accessor on Rack::Mini
58
62
 
59
63
  ```
60
64
  # Have Mini Profiler show up on the right
61
- Rack::MiniProfiler.configuration[:position] = 'right'
65
+ Rack::MiniProfiler.config.position = 'right'
62
66
  ```
63
67
 
64
68
  In a Rails app, this can be done conveniently in an initializer such as config/initializers/mini_profiler.rb.
65
69
 
66
70
  ## Available Options
67
71
 
68
- * authorize_cb - A lambda callback you can set to determine whether or not mini_profiler should be visible on a given request. Default in a Rails environment is only on in development mode. If in a Rack app, the default is always on.
72
+ * pre_authorize_cb - A lambda callback you can set to determine whether or not mini_profiler should be visible on a given request. Default in a Rails environment is only on in development mode. If in a Rack app, the default is always on.
69
73
  * position - Can either be 'right' or 'left'. Default is 'left'.
70
- * skip_schema_queries - Whether or not you want to log the queries about the schema of your tables. Default is 'true'
74
+ * skip_schema_queries - Whether or not you want to log the queries about the schema of your tables. Default is 'false', 'true' in rails development.
75
+
76
+ ## Special query strings
71
77
 
78
+ If you include the query string `pp=help` at the end of your request you will see the various option you have. You can use these options to extend or contract the amount of diagnostics rack-mini-profiler gathers.
72
79
 
@@ -475,6 +475,18 @@ var MiniProfiler = (function ($) {
475
475
  })();
476
476
  }
477
477
 
478
+ // also fetch results after ExtJS requests, in case it is being used
479
+ if (typeof (Ext) != 'undefined' && typeof (Ext.Ajax) != 'undefined' && typeof (Ext.Ajax.on) != 'undefined') {
480
+ // Ext.Ajax is a singleton, so we just have to attach to its 'requestcomplete' event
481
+ Ext.Ajax.on('requestcomplete', function(e, xhr, settings) {
482
+ var stringIds = xhr.getResponseHeader('X-MiniProfiler-Ids');
483
+ if (stringIds) {
484
+ var ids = typeof JSON != 'undefined' ? JSON.parse(stringIds) : eval(stringIds);
485
+ fetchResults(ids);
486
+ }
487
+ });
488
+ }
489
+
478
490
  // some elements want to be hidden on certain doc events
479
491
  bindDocumentEvents();
480
492
  };
@@ -6,6 +6,19 @@ module Rack
6
6
  # This class holds the client timings
7
7
  class ClientTimerStruct < TimerStruct
8
8
 
9
+ def self.init_instrumentation
10
+ "<script type=\"text/javascript\">mPt=function(){var t=[];return{t:t,probe:function(n){t.push({d:new Date(),n:n})}}}()</script>"
11
+ end
12
+
13
+ def self.instrument(name,orig)
14
+ probe = "<script>mPt.probe('#{name}')</script>"
15
+ wrapped = probe
16
+ wrapped << orig
17
+ wrapped << probe
18
+ wrapped
19
+ end
20
+
21
+
9
22
  def initialize(env={})
10
23
  super
11
24
  end
@@ -21,6 +34,26 @@ module Rack
21
34
  baseTime = clientTimes['navigationStart'].to_i if clientTimes
22
35
  return unless clientTimes && baseTime
23
36
 
37
+ probes = form['clientProbes']
38
+ translated = {}
39
+ if probes && probes != "null"
40
+ probes.each do |id, val|
41
+ name = val["n"]
42
+ translated[name] ||= {}
43
+ if translated[name][:start]
44
+ translated[name][:finish] = val["d"]
45
+ else
46
+ translated[name][:start] = val["d"]
47
+ end
48
+ end
49
+ end
50
+
51
+ translated.each do |name, data|
52
+ h = {"Name" => name, "Start" => data[:start].to_i - baseTime}
53
+ h["Duration"] = data[:finish].to_i - data[:start].to_i if data[:finish]
54
+ timings.push(h)
55
+ end
56
+
24
57
  clientTimes.keys.find_all{|k| k =~ /Start$/ }.each do |k|
25
58
  start = clientTimes[k].to_i - baseTime
26
59
  finish = clientTimes[k.sub(/Start$/, "End")].to_i - baseTime
@@ -0,0 +1,51 @@
1
+ module Rack
2
+ class MiniProfiler
3
+ class Config
4
+
5
+ def self.attr_accessor(*vars)
6
+ @attributes ||= []
7
+ @attributes.concat vars
8
+ super(*vars)
9
+ end
10
+
11
+ def self.attributes
12
+ @attributes
13
+ end
14
+
15
+ attr_accessor :auto_inject, :base_url_path, :pre_authorize_cb, :position,
16
+ :backtrace_remove, :backtrace_filter, :skip_schema_queries,
17
+ :storage, :user_provider, :storage_instance, :storage_options, :skip_paths, :authorization_mode
18
+
19
+ def self.default
20
+ new.instance_eval {
21
+ @auto_inject = true # automatically inject on every html page
22
+ @base_url_path = "/mini-profiler-resources/"
23
+
24
+ # called prior to rack chain, to ensure we are allowed to profile
25
+ @pre_authorize_cb = lambda {|env| true}
26
+
27
+ # called after rack chain, to ensure we are REALLY allowed to profile
28
+ @position = 'left' # Where it is displayed
29
+ @skip_schema_queries = false
30
+ @storage = MiniProfiler::MemoryStore
31
+ @user_provider = Proc.new{|env| Rack::Request.new(env).ip}
32
+ @authorization_mode = :allow_all
33
+ self
34
+ }
35
+ end
36
+
37
+ def merge!(config)
38
+ return unless config
39
+ if Hash === config
40
+ config.each{|k,v| instance_variable_set "@#{k}",v}
41
+ else
42
+ self.class.attributes.each{ |k|
43
+ v = config.send k
44
+ instance_variable_set "@#{k}", v if v
45
+ }
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,10 @@
1
+ class Rack::MiniProfiler::Context
2
+ attr_accessor :inject_js,:current_timer,:page_struct,:skip_backtrace,:full_backtrace,:discard, :mpt_init
3
+
4
+ def initialize(opts = {})
5
+ opts.each do |k,v|
6
+ self.instance_variable_set('@' + k, v)
7
+ end
8
+ end
9
+
10
+ end
@@ -35,15 +35,19 @@ module Rack
35
35
  def duration_ms
36
36
  @attributes['Root']['DurationMilliseconds']
37
37
  end
38
+
39
+ def root
40
+ @attributes['Root']
41
+ end
38
42
 
39
43
  def to_json(*a)
40
44
  attribs = @attributes.merge(
41
45
  "Started" => '/Date(%d)/' % @attributes['Started'],
42
46
  "DurationMilliseconds" => @attributes['Root']['DurationMilliseconds']
43
47
  )
44
- ::JSON.generate(attribs, a[0])
48
+ ::JSON.generate(attribs, :max_nesting => 100)
45
49
  end
46
50
  end
47
51
 
48
52
  end
49
- end
53
+ end
@@ -11,67 +11,102 @@ require 'mini_profiler/storage/abstract_store'
11
11
  require 'mini_profiler/storage/memory_store'
12
12
  require 'mini_profiler/storage/redis_store'
13
13
  require 'mini_profiler/storage/file_store'
14
+ require 'mini_profiler/config'
15
+ require 'mini_profiler/profiling_methods'
16
+ require 'mini_profiler/context'
14
17
 
15
18
  module Rack
16
19
 
17
20
  class MiniProfiler
18
21
 
19
- VERSION = 'rZlycOOTnzxZvxTmFuOEV0dSmu4P5m5bLrCtwJHVXPA='.freeze
20
- @@instance = nil
22
+ VERSION = 'rZlycOOTnzxZvxTmFuOEV0dSmu4P5m5bLrCtwJHVXPA=A'.freeze
21
23
 
22
- def self.instance
23
- @@instance
24
- end
24
+ class << self
25
+
26
+ include Rack::MiniProfiler::ProfilingMethods
25
27
 
26
- def self.generate_id
27
- rand(36**20).to_s(36)
28
- end
28
+ def generate_id
29
+ rand(36**20).to_s(36)
30
+ end
29
31
 
30
- # Defaults for MiniProfiler's configuration
31
- def self.configuration_defaults
32
- {
33
- :auto_inject => true, # automatically inject on every html page
34
- :base_url_path => "/mini-profiler-resources/",
35
- :authorize_cb => lambda {|env| true}, # callback returns true if this request is authorized to profile
36
- :position => 'left', # Where it is displayed
37
- :backtrace_remove => nil,
38
- :backtrace_filter => nil,
39
- :skip_schema_queries => true,
40
- :storage => MiniProfiler::MemoryStore,
41
- :user_provider => Proc.new{|env| Rack::Request.new(env).ip }
42
- }
43
- end
32
+ def reset_config
33
+ @config = Config.default
34
+ end
44
35
 
45
- def self.reset_configuration
46
- @configuration = configuration_defaults
47
- end
36
+ # So we can change the configuration if we want
37
+ def config
38
+ @config ||= Config.default
39
+ end
48
40
 
49
- # So we can change the configuration if we want
50
- def self.configuration
51
- @configuration ||= configuration_defaults.dup
52
- end
41
+ def share_template
42
+ return @share_template unless @share_template.nil?
43
+ @share_template = ::File.read(::File.expand_path("../html/share.html", ::File.dirname(__FILE__)))
44
+ end
45
+
46
+ def current
47
+ Thread.current[:mini_profiler_private]
48
+ end
49
+
50
+ def current=(c)
51
+ # we use TLS cause we need access to this from sql blocks and code blocks that have no access to env
52
+ Thread.current[:mini_profiler_private]= c
53
+ end
54
+
55
+ # discard existing results, don't track this request
56
+ def discard_results
57
+ self.current.discard = true if current
58
+ end
59
+
60
+ # user has the mini profiler cookie, only used when config.authorization_mode == :whitelist
61
+ def has_profiling_cookie?(env)
62
+ env['HTTP_COOKIE'] && env['HTTP_COOKIE'].include?("__profilin=stylin")
63
+ end
64
+
65
+ # remove the mini profiler cookie, only used when config.authorization_mode == :whitelist
66
+ def remove_profiling_cookie(headers)
67
+ Rack::Utils.delete_cookie_header!(headers, '__profilin')
68
+ end
53
69
 
54
- def self.share_template
55
- return @share_template unless @share_template.nil?
56
- @share_template = ::File.read(::File.expand_path("../html/share.html", ::File.dirname(__FILE__)))
70
+ def set_profiling_cookie(headers)
71
+ Rack::Utils.set_cookie_header!(headers, '__profilin', 'stylin')
72
+ end
73
+
74
+ def create_current(env={}, options={})
75
+ # profiling the request
76
+ self.current = Context.new
77
+ self.current.inject_js = config.auto_inject && (!env['HTTP_X_REQUESTED_WITH'].eql? 'XMLHttpRequest')
78
+ self.current.page_struct = PageTimerStruct.new(env)
79
+ self.current.current_timer = current.page_struct['Root']
80
+ end
81
+
82
+ def authorize_request
83
+ Thread.current[:mp_authorized] = true
84
+ end
85
+
86
+ def deauthorize_request
87
+ Thread.current[:mp_authorized] = nil
88
+ end
89
+
90
+ def request_authorized?
91
+ Thread.current[:mp_authorized]
92
+ end
57
93
  end
58
94
 
59
95
  #
60
96
  # options:
61
97
  # :auto_inject - should script be automatically injected on every html page (not xhr)
62
- def initialize(app, opts={})
63
- @@instance = self
64
- MiniProfiler.configuration.merge!(opts)
65
- @options = MiniProfiler.configuration
98
+ def initialize(app, config = nil)
99
+ MiniProfiler.config.merge!(config)
100
+ @config = MiniProfiler.config
66
101
  @app = app
67
- @options[:base_url_path] << "/" unless @options[:base_url_path].end_with? "/"
68
- unless @options[:storage_instance]
69
- @storage = @options[:storage_instance] = @options[:storage].new(@options[:storage_options])
102
+ @config.base_url_path << "/" unless @config.base_url_path.end_with? "/"
103
+ unless @config.storage_instance
104
+ @storage = @config.storage_instance = @config.storage.new(@config.storage_options)
70
105
  end
71
106
  end
72
107
 
73
108
  def user(env)
74
- options[:user_provider].call(env)
109
+ @config.user_provider.call(env)
75
110
  end
76
111
 
77
112
  def serve_results(env)
@@ -97,7 +132,7 @@ module Rack
97
132
 
98
133
  # Otherwise give the HTML back
99
134
  html = MiniProfiler.share_template.dup
100
- html.gsub!(/\{path\}/, @options[:base_url_path])
135
+ html.gsub!(/\{path\}/, @config.base_url_path)
101
136
  html.gsub!(/\{version\}/, MiniProfiler::VERSION)
102
137
  html.gsub!(/\{json\}/, result_json)
103
138
  html.gsub!(/\{includes\}/, get_profile_script(env))
@@ -110,7 +145,7 @@ module Rack
110
145
  end
111
146
 
112
147
  def serve_html(env)
113
- file_name = env['PATH_INFO'][(@options[:base_url_path].length)..1000]
148
+ file_name = env['PATH_INFO'][(@config.base_url_path.length)..1000]
114
149
  return serve_results(env) if file_name.eql?('results')
115
150
  full_path = ::File.expand_path("../html/#{file_name}", ::File.dirname(__FILE__))
116
151
  return [404, {}, ["Not found"]] unless ::File.exists? full_path
@@ -120,15 +155,7 @@ module Rack
120
155
  f.serving env
121
156
  end
122
157
 
123
- def self.current
124
- Thread.current['profiler.mini.private']
125
- end
126
-
127
- def self.current=(c)
128
- # we use TLS cause we need access to this from sql blocks and code blocks that have no access to env
129
- Thread.current['profiler.mini.private'] = c
130
- end
131
-
158
+
132
159
  def current
133
160
  MiniProfiler.current
134
161
  end
@@ -137,54 +164,73 @@ module Rack
137
164
  MiniProfiler.current=c
138
165
  end
139
166
 
140
- def options
141
- @options
142
- end
143
167
 
144
- def self.create_current(env={}, options={})
145
- # profiling the request
146
- self.current = {}
147
- self.current['inject_js'] = options[:auto_inject] && (!env['HTTP_X_REQUESTED_WITH'].eql? 'XMLHttpRequest')
148
- self.current['page_struct'] = PageTimerStruct.new(env)
149
- self.current['current_timer'] = current['page_struct']['Root']
168
+ def config
169
+ @config
150
170
  end
151
171
 
172
+
152
173
  def call(env)
153
- status = headers = body = nil
174
+ status = headers = body = nil
175
+ path = env['PATH_INFO']
154
176
 
155
- # only profile if authorized
156
- return @app.call(env) unless @options[:authorize_cb].call(env)
177
+ skip_it = (@config.pre_authorize_cb && !@config.pre_authorize_cb.call(env)) ||
178
+ (@config.skip_paths && @config.skip_paths.any?{ |p| path[0,p.length] == p}) ||
179
+ env["QUERY_STRING"] =~ /pp=skip/
180
+
181
+ has_profiling_cookie = MiniProfiler.has_profiling_cookie?(env)
182
+
183
+ if skip_it || (@config.authorization_mode == :whitelist && !has_profiling_cookie)
184
+ status,headers,body = @app.call(env)
185
+ if !skip_it && @config.authorization_mode == :whitelist && !has_profiling_cookie && MiniProfiler.request_authorized?
186
+ MiniProfiler.set_profiling_cookie(headers)
187
+ end
188
+ return [status,headers,body]
189
+ end
157
190
 
158
- # handle all /mini-profiler requests here
159
- return serve_html(env) if env['PATH_INFO'].start_with? @options[:base_url_path]
191
+ # handle all /mini-profiler requests here
192
+ return serve_html(env) if env['PATH_INFO'].start_with? @config.base_url_path
160
193
 
161
- MiniProfiler.create_current(env, @options)
162
- if env["QUERY_STRING"] =~ /pp=skip-backtrace/
163
- current['skip-backtrace'] = true
194
+ MiniProfiler.create_current(env, @config)
195
+ MiniProfiler.deauthorize_request if @config.authorization_mode == :whitelist
196
+ if env["QUERY_STRING"] =~ /pp=no-backtrace/
197
+ current.skip_backtrace = true
198
+ elsif env["QUERY_STRING"] =~ /pp=full-backtrace/
199
+ current.full_backtrace = true
164
200
  end
165
201
 
166
- start = Time.now
167
-
168
202
  done_sampling = false
169
203
  quit_sampler = false
170
204
  backtraces = nil
205
+ missing_stacktrace = false
171
206
  if env["QUERY_STRING"] =~ /pp=sample/
172
207
  backtraces = []
173
208
  t = Thread.current
174
209
  Thread.new {
175
- i = 10000 # for sanity never grab more than 10k samples
176
- unless done_sampling || i < 0
177
- i -= 1
178
- backtraces << t.backtrace
179
- sleep 0.001
210
+ begin
211
+ require 'stacktrace' rescue nil
212
+ if !t.respond_to? :stacktrace
213
+ missing_stacktrace = true
214
+ quit_sampler = true
215
+ return
216
+ end
217
+ i = 10000 # for sanity never grab more than 10k samples
218
+ while i > 0
219
+ break if done_sampling
220
+ i -= 1
221
+ backtraces << t.stacktrace
222
+ sleep 0.001
223
+ end
224
+ ensure
225
+ quit_sampler = true
180
226
  end
181
- quit_sampler = true
182
227
  }
183
228
  end
184
229
 
185
230
  status, headers, body = nil
231
+ start = Time.now
186
232
  begin
187
- status,headers, body = @app.call(env)
233
+ status,headers,body = @app.call(env)
188
234
  ensure
189
235
  if backtraces
190
236
  done_sampling = true
@@ -192,9 +238,35 @@ module Rack
192
238
  end
193
239
  end
194
240
 
195
- page_struct = current['page_struct']
241
+ skip_it = current.discard
242
+ if (config.authorization_mode == :whitelist && !MiniProfiler.request_authorized?)
243
+ MiniProfiler.remove_profiling_cookie(headers)
244
+ skip_it = true
245
+ end
246
+
247
+ return [status,headers,body] if skip_it
248
+
249
+ # we must do this here, otherwise current[:discard] is not being properly treated
250
+ if env["QUERY_STRING"] =~ /pp=env/
251
+ body.close if body.respond_to? :close
252
+ return dump_env env
253
+ end
254
+
255
+ if env["QUERY_STRING"] =~ /pp=help/
256
+ body.close if body.respond_to? :close
257
+ return help
258
+ end
259
+
260
+ page_struct = current.page_struct
196
261
  page_struct['Root'].record_time((Time.now - start) * 1000)
197
262
 
263
+ if backtraces
264
+ body.close if body.respond_to? :close
265
+ return help(:stacktrace) if missing_stacktrace
266
+ return analyze(backtraces, page_struct)
267
+ end
268
+
269
+
198
270
  # no matter what it is, it should be unviewed, otherwise we will miss POST
199
271
  @storage.set_unviewed(user(env), page_struct['Id'])
200
272
  @storage.save(page_struct)
@@ -208,7 +280,7 @@ module Rack
208
280
  end
209
281
 
210
282
  # inject script
211
- if current['inject_js'] \
283
+ if current.inject_js \
212
284
  && headers.has_key?('Content-Type') \
213
285
  && !headers['Content-Type'].match(/text\/html/).nil? then
214
286
  body = MiniProfiler::BodyAddProxy.new(body, self.get_profile_script(env))
@@ -226,8 +298,68 @@ module Rack
226
298
  current = nil
227
299
  end
228
300
 
301
+ def dump_env(env)
302
+ headers = {'Content-Type' => 'text/plain'}
303
+ body = ""
304
+ env.each do |k,v|
305
+ body << "#{k}: #{v}\n"
306
+ end
307
+ [200, headers, [body]]
308
+ end
309
+
310
+ def help(category = nil)
311
+ headers = {'Content-Type' => 'text/plain'}
312
+ body = "Append the following to your query string:
313
+
314
+ pp=help : display this screen
315
+ pp=env : display the rack environment
316
+ pp=skip : skip mini profiler for this request
317
+ pp=no-backtrace : don't collect stack traces from all the SQL executed
318
+ pp=full-backtrace : enable full backtrace for SQL executed
319
+ pp=sample : sample stack traces and return a report isolating heavy usage (requires the stacktrace gem)
320
+ "
321
+ if (category == :stacktrace)
322
+ body = "pp=stacktrace requires the stacktrace gem - add gem 'stacktrace' to your Gemfile"
323
+ end
324
+
325
+ [200, headers, [body]]
326
+ end
327
+
328
+ def analyze(traces, page_struct)
329
+ headers = {'Content-Type' => 'text/plain'}
330
+ body = "Collected: #{traces.count} stack traces. Duration(ms): #{page_struct.duration_ms}"
331
+
332
+ seen = {}
333
+ fulldump = ""
334
+ traces.each do |trace|
335
+ fulldump << "\n\n"
336
+ distinct = {}
337
+ trace.each do |frame|
338
+ name = "#{frame.klass} #{frame.method}"
339
+ unless distinct[name]
340
+ distinct[name] = true
341
+ seen[name] ||= 0
342
+ seen[name] += 1
343
+ end
344
+ fulldump << name << "\n"
345
+ end
346
+ end
347
+
348
+ body << "\n\nStack Trace Analysis\n"
349
+ seen.to_a.sort{|x,y| y[1] <=> x[1]}.each do |name, count|
350
+ if count > traces.count / 10
351
+ body << "#{name} x #{count}\n"
352
+ end
353
+ end
354
+
355
+ body << "\n\n\nRaw traces \n"
356
+ body << fulldump
357
+
358
+ [200, headers, [body]]
359
+ end
360
+
229
361
  def ids_json(env)
230
- ids = [current['page_struct']["Id"]] + (@storage.get_unviewed_ids(user(env)) || [])
362
+ ids = [current.page_struct["Id"]] + (@storage.get_unviewed_ids(user(env)) || [])
231
363
  ::JSON.generate(ids.uniq)
232
364
  end
233
365
 
@@ -239,14 +371,14 @@ module Rack
239
371
  # * you do not want script to be automatically appended for the current page. You can also call cancel_auto_inject
240
372
  def get_profile_script(env)
241
373
  ids = ids_json(env)
242
- path = @options[:base_url_path]
374
+ path = @config.base_url_path
243
375
  version = MiniProfiler::VERSION
244
- position = @options[:position]
376
+ position = @config.position
245
377
  showTrivial = false
246
378
  showChildren = false
247
379
  maxTracesToShow = 10
248
380
  showControls = false
249
- currentId = current['page_struct']["Id"]
381
+ currentId = current.page_struct["Id"]
250
382
  authorized = true
251
383
  useExistingjQuery = false
252
384
  # TODO : cache this snippet
@@ -258,52 +390,13 @@ module Rack
258
390
  end
259
391
  # replace the '{{' and '}}''
260
392
  script.gsub!(/\{\{/, '{').gsub!(/\}\}/, '}')
261
- current['inject_js'] = false
393
+ current.inject_js = false
262
394
  script
263
395
  end
264
396
 
265
397
  # cancels automatic injection of profile script for the current page
266
398
  def cancel_auto_inject(env)
267
- current['inject_js'] = false
268
- end
269
-
270
- # perform a profiling step on given block
271
- def self.step(name)
272
- if current
273
- old_timer = current['current_timer']
274
- new_step = RequestTimerStruct.new(name, current['page_struct'])
275
- current['current_timer'] = new_step
276
- new_step['Name'] = name
277
- start = Time.now
278
- result = yield if block_given?
279
- new_step.record_time((Time.now - start)*1000)
280
- old_timer.add_child(new_step)
281
- current['current_timer'] = old_timer
282
- result
283
- else
284
- yield if block_given?
285
- end
286
- end
287
-
288
- def self.profile_method(klass, method, &blk)
289
- default_name = klass.to_s + " " + method.to_s
290
- with_profiling = (method.to_s + "_with_mini_profiler").intern
291
- without_profiling = (method.to_s + "_without_mini_profiler").intern
292
-
293
- klass.send :alias_method, without_profiling, method
294
- klass.send :define_method, with_profiling do |*args, &orig|
295
- name = default_name
296
- name = blk.bind(self).call(*args) if blk
297
- ::Rack::MiniProfiler.step name do
298
- self.send without_profiling, *args, &orig
299
- end
300
- end
301
- klass.send :alias_method, method, with_profiling
302
- end
303
-
304
- def record_sql(query, elapsed_ms)
305
- c = current
306
- c['current_timer'].add_sql(query, elapsed_ms, c['page_struct'], c['skip-backtrace']) if (c && c['current_timer'])
399
+ current.inject_js = false
307
400
  end
308
401
 
309
402
  end
@@ -0,0 +1,73 @@
1
+ module Rack
2
+ class MiniProfiler
3
+ module ProfilingMethods
4
+
5
+ def record_sql(query, elapsed_ms)
6
+ c = current
7
+ return unless c
8
+ c.current_timer.add_sql(query, elapsed_ms, c.page_struct, c.skip_backtrace, c.full_backtrace) if (c && c.current_timer)
9
+ end
10
+
11
+ # perform a profiling step on given block
12
+ def step(name)
13
+ if current
14
+ parent_timer = current.current_timer
15
+ result = nil
16
+ current.current_timer = current_timer = current.current_timer.add_child(name)
17
+ begin
18
+ result = yield if block_given?
19
+ ensure
20
+ current_timer.record_time
21
+ current.current_timer = parent_timer
22
+ end
23
+ result
24
+ else
25
+ yield if block_given?
26
+ end
27
+ end
28
+
29
+ def unprofile_method(klass, method)
30
+ with_profiling = (method.to_s + "_with_mini_profiler").intern
31
+ without_profiling = (method.to_s + "_without_mini_profiler").intern
32
+
33
+ if klass.send :method_defined?, with_profiling
34
+ klass.send :alias_method, method, without_profiling
35
+ klass.send :remove_method, with_profiling
36
+ klass.send :remove_method, without_profiling
37
+ end
38
+ end
39
+
40
+ def profile_method(klass, method, &blk)
41
+ default_name = klass.to_s + " " + method.to_s
42
+ with_profiling = (method.to_s + "_with_mini_profiler").intern
43
+ without_profiling = (method.to_s + "_without_mini_profiler").intern
44
+
45
+ if klass.send :method_defined?, with_profiling
46
+ return # dont double profile
47
+ end
48
+
49
+ klass.send :alias_method, without_profiling, method
50
+ klass.send :define_method, with_profiling do |*args, &orig|
51
+ return self.send without_profiling, *args, &orig unless Rack::MiniProfiler.current
52
+
53
+ name = default_name
54
+ name = blk.bind(self).call(*args) if blk
55
+
56
+ parent_timer = Rack::MiniProfiler.current.current_timer
57
+ page_struct = Rack::MiniProfiler.current.page_struct
58
+ result = nil
59
+
60
+ Rack::MiniProfiler.current.current_timer = current_timer = parent_timer.add_child(name)
61
+ begin
62
+ result = self.send without_profiling, *args, &orig
63
+ ensure
64
+ current_timer.record_time
65
+ Rack::MiniProfiler.current.current_timer = parent_timer
66
+ end
67
+ result
68
+ end
69
+ klass.send :alias_method, method, with_profiling
70
+ end
71
+ end
72
+ end
73
+ end
@@ -6,14 +6,14 @@ module Rack
6
6
  class RequestTimerStruct < TimerStruct
7
7
 
8
8
  def self.createRoot(name, page)
9
- rt = RequestTimerStruct.new(name, page)
9
+ rt = RequestTimerStruct.new(name, page, nil)
10
10
  rt["IsRoot"]= true
11
11
  rt
12
12
  end
13
13
 
14
- attr_reader :children_duration
14
+ attr_accessor :children_duration
15
15
 
16
- def initialize(name, page)
16
+ def initialize(name, page, parent)
17
17
  super("Id" => MiniProfiler.generate_id,
18
18
  "Name" => name,
19
19
  "DurationMilliseconds" => 0,
@@ -30,34 +30,65 @@ module Rack
30
30
  "SqlTimingsDurationMilliseconds"=> 0,
31
31
  "IsTrivial"=> false,
32
32
  "IsRoot"=> false,
33
- "Depth"=> 0,
33
+ "Depth"=> parent ? parent.depth + 1 : 0,
34
34
  "ExecutedReaders"=> 0,
35
35
  "ExecutedScalars"=> 0,
36
36
  "ExecutedNonQueries"=> 0)
37
37
  @children_duration = 0
38
+ @start = Time.now
39
+ @parent = parent
40
+ @page = page
38
41
  end
39
42
 
40
- def add_child(request_timer)
43
+ def duration_ms
44
+ self['DurationMilliseconds']
45
+ end
46
+
47
+ def start_ms
48
+ self['StartMilliseconds']
49
+ end
50
+
51
+ def start
52
+ @start
53
+ end
54
+
55
+ def depth
56
+ self['Depth']
57
+ end
58
+
59
+ def children
60
+ self['Children']
61
+ end
62
+
63
+ def add_child(name)
64
+ request_timer = RequestTimerStruct.new(name, @page, self)
41
65
  self['Children'].push(request_timer)
42
66
  self['HasChildren'] = true
43
67
  request_timer['ParentTimingId'] = self['Id']
44
68
  request_timer['Depth'] = self['Depth'] + 1
45
- @children_duration += request_timer['DurationMilliseconds']
69
+ request_timer
46
70
  end
47
71
 
48
- def add_sql(query, elapsed_ms, page, skip_backtrace = false)
49
- timer = SqlTimerStruct.new(query, elapsed_ms, page, skip_backtrace)
72
+ def add_sql(query, elapsed_ms, page, skip_backtrace = false, full_backtrace = false)
73
+ timer = SqlTimerStruct.new(query, elapsed_ms, page, self , skip_backtrace, full_backtrace)
50
74
  timer['ParentTimingId'] = self['Id']
51
75
  self['SqlTimings'].push(timer)
52
76
  self['HasSqlTimings'] = true
53
77
  self['SqlTimingsDurationMilliseconds'] += elapsed_ms
54
78
  page['DurationMillisecondsInSql'] += elapsed_ms
79
+ timer
55
80
  end
56
81
 
57
- def record_time(milliseconds)
82
+ def record_time(milliseconds = nil)
83
+ milliseconds ||= (Time.now - @start) * 1000
58
84
  self['DurationMilliseconds'] = milliseconds
59
85
  self['IsTrivial'] = true if milliseconds < self["TrivialDurationThresholdMilliseconds"]
60
86
  self['DurationWithoutChildrenMilliseconds'] = milliseconds - @children_duration
87
+
88
+ if @parent
89
+ @parent.children_duration += milliseconds
90
+ end
91
+
61
92
  end
62
93
  end
63
94
  end
@@ -5,7 +5,7 @@ module Rack
5
5
 
6
6
  # Timing system for a SQL query
7
7
  class SqlTimerStruct < TimerStruct
8
- def initialize(query, duration_ms, page, skip_backtrace = false)
8
+ def initialize(query, duration_ms, page, parent, skip_backtrace = false, full_backtrace = false)
9
9
 
10
10
  stack_trace = nil
11
11
  unless skip_backtrace
@@ -13,24 +13,35 @@ module Rack
13
13
  stack_trace = ""
14
14
  # Clean up the stack trace if there are options to do so
15
15
  Kernel.caller.each do |ln|
16
- ln.gsub!(Rack::MiniProfiler.configuration[:backtrace_remove], '') if Rack::MiniProfiler.configuration[:backtrace_remove]
17
- if Rack::MiniProfiler.configuration[:backtrace_filter].nil? or ln =~ Rack::MiniProfiler.configuration[:backtrace_filter]
16
+ ln.gsub!(Rack::MiniProfiler.config.backtrace_remove, '') if Rack::MiniProfiler.config.backtrace_remove and !full_backtrace
17
+ if full_backtrace or Rack::MiniProfiler.config.backtrace_filter.nil? or ln =~ Rack::MiniProfiler.config.backtrace_filter
18
18
  stack_trace << ln << "\n"
19
19
  end
20
20
  end
21
21
  end
22
22
 
23
+ @parent = parent
24
+ @page = page
25
+
23
26
  super("ExecuteType" => 3, # TODO
24
27
  "FormattedCommandString" => query,
25
28
  "StackTraceSnippet" => stack_trace,
26
- "StartMilliseconds" => (Time.now.to_f * 1000).to_i - page['Started'],
29
+ "StartMilliseconds" => ((Time.now.to_f * 1000).to_i - page['Started']) - duration_ms,
27
30
  "DurationMilliseconds" => duration_ms,
28
- "FirstFetchDurationMilliseconds" => 0,
31
+ "FirstFetchDurationMilliseconds" => duration_ms,
29
32
  "Parameters" => nil,
30
33
  "ParentTimingId" => nil,
31
34
  "IsDuplicate" => false)
32
35
  end
33
36
 
37
+ def report_reader_duration(elapsed_ms)
38
+ return if @reported
39
+ @reported = true
40
+ self["DurationMilliseconds"] += elapsed_ms
41
+ @parent["SqlTimingsDurationMilliseconds"] += elapsed_ms
42
+ @page["DurationMillisecondsInSql"] += elapsed_ms
43
+ end
44
+
34
45
  end
35
46
 
36
47
  end
@@ -1,33 +1,82 @@
1
1
  module MiniProfilerRails
2
+
2
3
  class Railtie < ::Rails::Railtie
3
4
 
4
5
  initializer "rack_mini_profiler.configure_rails_initialization" do |app|
6
+ c = Rack::MiniProfiler.config
5
7
 
6
- # By default, only show the MiniProfiler in development mode
7
- Rack::MiniProfiler.configuration[:authorize_cb] = lambda { |env|
8
- Rails.env.development? && !(env['PATH_INFO'] =~ /^\/assets\//)
8
+ # By default, only show the MiniProfiler in development mode, in production allow profiling if post_authorize_cb is set
9
+ c.pre_authorize_cb = lambda { |env|
10
+ Rails.env.development? || Rails.env.production?
9
11
  }
10
12
 
13
+ if Rails.env.development?
14
+ c.skip_paths ||= []
15
+ c.skip_paths << "/assets/"
16
+ c.skip_schema_queries = true
17
+ end
18
+
19
+ if Rails.env.production?
20
+ c.authorization_mode = :whitelist
21
+ end
22
+
11
23
  # The file store is just so much less flaky
12
24
  tmp = Rails.root.to_s + "/tmp/miniprofiler"
13
25
  Dir::mkdir(tmp) unless File.exists?(tmp)
14
-
15
- Rack::MiniProfiler.configuration[:storage_options] = {:path => tmp}
16
- Rack::MiniProfiler.configuration[:storage] = Rack::MiniProfiler::FileStore
26
+
27
+ c.storage_options = {:path => tmp}
28
+ c.storage = Rack::MiniProfiler::FileStore
17
29
 
18
30
  # Quiet the SQL stack traces
19
- Rack::MiniProfiler.configuration[:backtrace_remove] = Rails.root.to_s + "/"
20
- Rack::MiniProfiler.configuration[:backtrace_filter] = /^\/?(app|config|lib|test)/
31
+ c.backtrace_remove = Rails.root.to_s + "/"
32
+ c.backtrace_filter = /^\/?(app|config|lib|test)/
33
+ c.skip_schema_queries = Rails.env != 'production'
21
34
 
22
35
  # Install the Middleware
23
- app.middleware.insert_before 'Rack::Lock', 'Rack::MiniProfiler'
36
+ app.middleware.insert(0, Rack::MiniProfiler)
24
37
 
25
38
  # Attach to various Rails methods
26
39
  ::Rack::MiniProfiler.profile_method(ActionController::Base, :process) {|action| "Executing action: #{action}"}
27
40
  ::Rack::MiniProfiler.profile_method(ActionView::Template, :render) {|x,y| "Rendering: #{@virtual_path}"}
28
41
 
29
-
30
42
  end
31
43
 
44
+ # TODO: Implement something better here
45
+ # config.after_initialize do
46
+ #
47
+ # class ::ActionView::Helpers::AssetTagHelper::JavascriptIncludeTag
48
+ # alias_method :asset_tag_orig, :asset_tag
49
+ # def asset_tag(source,options)
50
+ # current = Rack::MiniProfiler.current
51
+ # return asset_tag_orig(source,options) unless current
52
+ # wrapped = ""
53
+ # unless current.mpt_init
54
+ # current.mpt_init = true
55
+ # wrapped << Rack::MiniProfiler::ClientTimerStruct.init_instrumentation
56
+ # end
57
+ # name = source.split('/')[-1]
58
+ # wrapped << Rack::MiniProfiler::ClientTimerStruct.instrument(name, asset_tag_orig(source,options)).html_safe
59
+ # wrapped
60
+ # end
61
+ # end
62
+
63
+ # class ::ActionView::Helpers::AssetTagHelper::StylesheetIncludeTag
64
+ # alias_method :asset_tag_orig, :asset_tag
65
+ # def asset_tag(source,options)
66
+ # current = Rack::MiniProfiler.current
67
+ # return asset_tag_orig(source,options) unless current
68
+ # wrapped = ""
69
+ # unless current.mpt_init
70
+ # current.mpt_init = true
71
+ # wrapped << Rack::MiniProfiler::ClientTimerStruct.init_instrumentation
72
+ # end
73
+ # name = source.split('/')[-1]
74
+ # wrapped << Rack::MiniProfiler::ClientTimerStruct.instrument(name, asset_tag_orig(source,options)).html_safe
75
+ # wrapped
76
+ # end
77
+ # end
78
+
79
+ # end
80
+
32
81
  end
33
82
  end
@@ -1,4 +1,13 @@
1
- module SqlPatches
1
+ class SqlPatches
2
+
3
+ def self.patched?
4
+ @patched
5
+ end
6
+
7
+ def self.patched=(val)
8
+ @patched = val
9
+ end
10
+
2
11
  def self.class_exists?(name)
3
12
  eval(name + ".class").to_s.eql?('Class')
4
13
  rescue NameError
@@ -6,12 +15,116 @@ module SqlPatches
6
15
  end
7
16
  end
8
17
 
9
- if SqlPatches.class_exists? "Sequel::Database" then
18
+ # The best kind of instrumentation is in the actual db provider, however we don't want to double instrument
19
+ if SqlPatches.class_exists? "Mysql2::Client"
20
+
21
+ class Mysql2::Result
22
+ alias_method :each_without_profiling, :each
23
+ def each(*args, &blk)
24
+ return each_without_profiling(*args, &blk) unless @miniprofiler_sql_id
25
+
26
+ start = Time.now
27
+ result = each_without_profiling(*args,&blk)
28
+ elapsed_time = ((Time.now - start).to_f * 1000).round(1)
29
+
30
+ @miniprofiler_sql_id.report_reader_duration(elapsed_time)
31
+ result
32
+ end
33
+ end
34
+
35
+ class Mysql2::Client
36
+ alias_method :query_without_profiling, :query
37
+ def query(*args,&blk)
38
+ current = ::Rack::MiniProfiler.current
39
+ return query_without_profiling(*args,&blk) unless current
40
+
41
+ start = Time.now
42
+ result = query_without_profiling(*args,&blk)
43
+ elapsed_time = ((Time.now - start).to_f * 1000).round(1)
44
+ result.instance_variable_set("@miniprofiler_sql_id", ::Rack::MiniProfiler.record_sql(args[0], elapsed_time))
45
+
46
+ result
47
+
48
+ end
49
+ end
50
+
51
+ SqlPatches.patched = true
52
+ end
53
+
54
+
55
+ # PG patches, keep in mind exec and async_exec have a exec{|r| } semantics that is yet to be implemented
56
+ if SqlPatches.class_exists? "PG::Result"
57
+
58
+ class PG::Result
59
+ alias_method :each_without_profiling, :each
60
+ alias_method :values_without_profiling, :values
61
+
62
+ def values(*args, &blk)
63
+ return values_without_profiling(*args, &blk) unless @miniprofiler_sql_id
64
+
65
+ start = Time.now
66
+ result = values_without_profiling(*args,&blk)
67
+ elapsed_time = ((Time.now - start).to_f * 1000).round(1)
68
+
69
+ @miniprofiler_sql_id.report_reader_duration(elapsed_time)
70
+ result
71
+ end
72
+
73
+ def each(*args, &blk)
74
+ return each_without_profiling(*args, &blk) unless @miniprofiler_sql_id
75
+
76
+ start = Time.now
77
+ result = each_without_profiling(*args,&blk)
78
+ elapsed_time = ((Time.now - start).to_f * 1000).round(1)
79
+
80
+ @miniprofiler_sql_id.report_reader_duration(elapsed_time)
81
+ result
82
+ end
83
+ end
84
+
85
+ class PG::Connection
86
+ alias_method :exec_without_profiling, :exec
87
+ alias_method :async_exec_without_profiling, :async_exec
88
+
89
+ def exec(*args,&blk)
90
+ current = ::Rack::MiniProfiler.current
91
+ return exec_without_profiling(*args,&blk) unless current
92
+
93
+ start = Time.now
94
+ result = exec_without_profiling(*args,&blk)
95
+ elapsed_time = ((Time.now - start).to_f * 1000).round(1)
96
+ result.instance_variable_set("@miniprofiler_sql_id", ::Rack::MiniProfiler.record_sql(args[0], elapsed_time))
97
+
98
+ result
99
+ end
100
+
101
+ def async_exec(*args,&blk)
102
+ current = ::Rack::MiniProfiler.current
103
+ return exec_without_profiling(*args,&blk) unless current
104
+
105
+ start = Time.now
106
+ result = exec_without_profiling(*args,&blk)
107
+ elapsed_time = ((Time.now - start).to_f * 1000).round(1)
108
+ result.instance_variable_set("@miniprofiler_sql_id", ::Rack::MiniProfiler.record_sql(args[0], elapsed_time))
109
+
110
+ result
111
+ end
112
+
113
+ alias_method :query, :exec
114
+ end
115
+
116
+ SqlPatches.patched = true
117
+ end
118
+
119
+
120
+
121
+ # Fallback for sequel
122
+ if SqlPatches.class_exists?("Sequel::Database") && !SqlPatches.patched?
10
123
  module Sequel
11
124
  class Database
12
125
  alias_method :log_duration_original, :log_duration
13
126
  def log_duration(duration, message)
14
- ::Rack::MiniProfiler.instance.record_sql(message, duration) if ::Rack::MiniProfiler.instance
127
+ ::Rack::MiniProfiler.record_sql(message, duration)
15
128
  log_duration_original(duration, message)
16
129
  end
17
130
  end
@@ -20,6 +133,7 @@ end
20
133
 
21
134
 
22
135
  ## based off https://github.com/newrelic/rpm/blob/master/lib/new_relic/agent/instrumentation/active_record.rb
136
+ ## fallback for alls sorts of weird dbs
23
137
  module Rack
24
138
  class MiniProfiler
25
139
  module ActiveRecordInstrumentation
@@ -34,19 +148,18 @@ module Rack
34
148
  end
35
149
 
36
150
  def log_with_miniprofiler(*args, &block)
151
+ current = ::Rack::MiniProfiler.current
152
+ return log_without_miniprofiler(*args, &block) unless current
153
+
37
154
  sql, name, binds = args
38
155
  t0 = Time.now
39
156
  rval = log_without_miniprofiler(*args, &block)
40
-
41
- # Get our MP Instance
42
- instance = ::Rack::MiniProfiler.instance
43
- return rval unless instance
44
-
157
+
45
158
  # Don't log schema queries if the option is set
46
- return rval if instance.options[:skip_schema_queries] and name =~ /SCHEMA/
159
+ return rval if Rack::MiniProfiler.config.skip_schema_queries and name =~ /SCHEMA/
47
160
 
48
161
  elapsed_time = ((Time.now - t0).to_f * 1000).round(1)
49
- instance.record_sql(sql, elapsed_time)
162
+ Rack::MiniProfiler.record_sql(sql, elapsed_time)
50
163
  rval
51
164
  end
52
165
  end
@@ -58,15 +171,8 @@ module Rack
58
171
  end
59
172
  end
60
173
 
61
- if defined?(::Rails)
62
- if ::Rails::VERSION::MAJOR.to_i == 3
63
- # in theory this is the right thing to do for rails 3 ... but it seems to work anyway
64
- #Rails.configuration.after_initialize do
65
- insert_instrumentation
66
- #end
67
- else
68
- insert_instrumentation
69
- end
174
+ if defined?(::Rails) && !SqlPatches.patched?
175
+ insert_instrumentation
70
176
  end
71
177
  end
72
178
 
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "rack-mini-profiler"
3
- s.version = "0.1.1"
3
+ s.version = "0.1.6"
4
4
  s.summary = "Profiles loading speed for rack applications."
5
5
  s.authors = ["Aleks Totic","Sam Saffron", "Robin Ward"]
6
6
  s.date = "2012-04-02"
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
12
12
  ].concat( Dir.glob('lib/**/*').reject {|f| File.directory?(f) || f =~ /~$/ } )
13
13
  s.extra_rdoc_files = [
14
14
  "README.md",
15
- "CHANGELOG.md"
15
+ "CHANGELOG"
16
16
  ]
17
17
  s.add_runtime_dependency 'rack', '>= 1.1.3'
18
18
  if RUBY_VERSION < "1.9"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-mini-profiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -84,14 +84,17 @@ executables: []
84
84
  extensions: []
85
85
  extra_rdoc_files:
86
86
  - README.md
87
- - CHANGELOG.md
87
+ - CHANGELOG
88
88
  files:
89
89
  - rack-mini-profiler.gemspec
90
90
  - lib/mini_profiler/sql_timer_struct.rb
91
91
  - lib/mini_profiler/request_timer_struct.rb
92
92
  - lib/mini_profiler/timer_struct.rb
93
93
  - lib/mini_profiler/page_timer_struct.rb
94
+ - lib/mini_profiler/context.rb
95
+ - lib/mini_profiler/config.rb
94
96
  - lib/mini_profiler/body_add_proxy.rb
97
+ - lib/mini_profiler/profiling_methods.rb
95
98
  - lib/mini_profiler/client_timer_struct.rb
96
99
  - lib/mini_profiler/profiler.rb
97
100
  - lib/mini_profiler/storage/memory_store.rb
@@ -114,7 +117,7 @@ files:
114
117
  - lib/mini_profiler_rails/railtie.rb
115
118
  - lib/patches/sql_patches.rb
116
119
  - README.md
117
- - CHANGELOG.md
120
+ - CHANGELOG
118
121
  homepage: http://miniprofiler.com
119
122
  licenses: []
120
123
  post_install_message:
@@ -129,7 +132,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
129
132
  version: '0'
130
133
  segments:
131
134
  - 0
132
- hash: -779964803
135
+ hash: -974819475
133
136
  required_rubygems_version: !ruby/object:Gem::Requirement
134
137
  none: false
135
138
  requirements:
@@ -138,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
141
  version: '0'
139
142
  segments:
140
143
  - 0
141
- hash: -779964803
144
+ hash: -974819475
142
145
  requirements: []
143
146
  rubyforge_project:
144
147
  rubygems_version: 1.8.24
@@ -1,7 +0,0 @@
1
- 28-June-2012 - Sam
2
-
3
- * Started change log
4
- * Corrected profiler so it properly captures POST requests (was supressing non 200s)
5
- * Amended Rack.MiniProfiler.config[:user_provider] to use ip addres for identity
6
- * Fixed bug where unviewed missing ids never got cleared
7
- * Supress all '/assets/' in the rails tie (makes debugging easier)