mini-mini-profiler 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/Ruby/CHANGELOG +135 -0
  2. data/Ruby/README.md +161 -0
  3. data/Ruby/lib/html/flamegraph.html +325 -0
  4. data/Ruby/lib/html/includes.css +451 -0
  5. data/Ruby/lib/html/includes.js +945 -0
  6. data/Ruby/lib/html/includes.less +471 -0
  7. data/Ruby/lib/html/includes.tmpl +108 -0
  8. data/Ruby/lib/html/jquery.1.7.1.js +4 -0
  9. data/Ruby/lib/html/jquery.tmpl.js +486 -0
  10. data/Ruby/lib/html/list.css +9 -0
  11. data/Ruby/lib/html/list.js +38 -0
  12. data/Ruby/lib/html/list.tmpl +34 -0
  13. data/Ruby/lib/html/profile_handler.js +1 -0
  14. data/Ruby/lib/html/share.html +11 -0
  15. data/Ruby/lib/mini_profiler/client_settings.rb +65 -0
  16. data/Ruby/lib/mini_profiler/client_timer_struct.rb +78 -0
  17. data/Ruby/lib/mini_profiler/config.rb +57 -0
  18. data/Ruby/lib/mini_profiler/context.rb +11 -0
  19. data/Ruby/lib/mini_profiler/custom_timer_struct.rb +22 -0
  20. data/Ruby/lib/mini_profiler/flame_graph.rb +54 -0
  21. data/Ruby/lib/mini_profiler/gc_profiler.rb +107 -0
  22. data/Ruby/lib/mini_profiler/page_timer_struct.rb +58 -0
  23. data/Ruby/lib/mini_profiler/profiler.rb +544 -0
  24. data/Ruby/lib/mini_profiler/profiling_methods.rb +133 -0
  25. data/Ruby/lib/mini_profiler/request_timer_struct.rb +115 -0
  26. data/Ruby/lib/mini_profiler/sql_timer_struct.rb +58 -0
  27. data/Ruby/lib/mini_profiler/storage/abstract_store.rb +31 -0
  28. data/Ruby/lib/mini_profiler/storage/file_store.rb +111 -0
  29. data/Ruby/lib/mini_profiler/storage/memcache_store.rb +53 -0
  30. data/Ruby/lib/mini_profiler/storage/memory_store.rb +65 -0
  31. data/Ruby/lib/mini_profiler/storage/redis_store.rb +54 -0
  32. data/Ruby/lib/mini_profiler/timer_struct.rb +33 -0
  33. data/Ruby/lib/mini_profiler/version.rb +5 -0
  34. data/Ruby/lib/mini_profiler_rails/railtie.rb +107 -0
  35. data/Ruby/lib/patches/net_patches.rb +14 -0
  36. data/Ruby/lib/patches/sql_patches.rb +272 -0
  37. data/Ruby/lib/rack-mini-profiler.rb +7 -0
  38. data/mini-mini-profiler.gemspec +26 -0
  39. metadata +154 -0
@@ -0,0 +1,58 @@
1
+ require 'mini_profiler/timer_struct'
2
+
3
+ module Rack
4
+ class MiniProfiler
5
+
6
+ # PageTimerStruct
7
+ # Root: RequestTimer
8
+ # :has_many RequestTimer children
9
+ # :has_many SqlTimer children
10
+ # :has_many CustomTimer children
11
+ class PageTimerStruct < TimerStruct
12
+ def initialize(env)
13
+ super("Id" => MiniProfiler.generate_id,
14
+ "Name" => env['PATH_INFO'],
15
+ "Started" => (Time.now.to_f * 1000).to_i,
16
+ "MachineName" => env['SERVER_NAME'],
17
+ "Level" => 0,
18
+ "User" => "unknown user",
19
+ "HasUserViewed" => false,
20
+ "ClientTimings" => nil,
21
+ "DurationMilliseconds" => 0,
22
+ "HasTrivialTimings" => true,
23
+ "HasAllTrivialTimigs" => false,
24
+ "TrivialDurationThresholdMilliseconds" => 2,
25
+ "Head" => nil,
26
+ "DurationMillisecondsInSql" => 0,
27
+ "HasSqlTimings" => true,
28
+ "HasDuplicateSqlTimings" => false,
29
+ "ExecutedReaders" => 0,
30
+ "ExecutedScalars" => 0,
31
+ "ExecutedNonQueries" => 0,
32
+ "CustomTimingNames" => [],
33
+ "CustomTimingStats" => {}
34
+ )
35
+ name = "#{env['REQUEST_METHOD']} http://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{env['SCRIPT_NAME']}#{env['PATH_INFO']}"
36
+ self['Root'] = RequestTimerStruct.createRoot(name, self)
37
+ end
38
+
39
+ def duration_ms
40
+ @attributes['Root']['DurationMilliseconds']
41
+ end
42
+
43
+ def root
44
+ @attributes['Root']
45
+ end
46
+
47
+ def to_json(*a)
48
+ attribs = @attributes.merge(
49
+ "Started" => '/Date(%d)/' % @attributes['Started'],
50
+ "DurationMilliseconds" => @attributes['Root']['DurationMilliseconds'],
51
+ "CustomTimingNames" => @attributes['CustomTimingStats'].keys.sort
52
+ )
53
+ ::JSON.generate(attribs, :max_nesting => 100)
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,544 @@
1
+ require 'json'
2
+ require 'timeout'
3
+ require 'thread'
4
+
5
+ require 'mini_profiler/version'
6
+ require 'mini_profiler/page_timer_struct'
7
+ require 'mini_profiler/sql_timer_struct'
8
+ require 'mini_profiler/custom_timer_struct'
9
+ require 'mini_profiler/client_timer_struct'
10
+ require 'mini_profiler/request_timer_struct'
11
+ require 'mini_profiler/storage/abstract_store'
12
+ require 'mini_profiler/storage/memcache_store'
13
+ require 'mini_profiler/storage/memory_store'
14
+ require 'mini_profiler/storage/redis_store'
15
+ require 'mini_profiler/storage/file_store'
16
+ require 'mini_profiler/config'
17
+ require 'mini_profiler/profiling_methods'
18
+ require 'mini_profiler/context'
19
+ require 'mini_profiler/client_settings'
20
+ require 'mini_profiler/gc_profiler'
21
+ require 'mini_profiler/flame_graph'
22
+
23
+ module Rack
24
+
25
+ class MiniProfiler
26
+
27
+ class << self
28
+
29
+ include Rack::MiniProfiler::ProfilingMethods
30
+
31
+ def generate_id
32
+ rand(36**20).to_s(36)
33
+ end
34
+
35
+ def reset_config
36
+ @config = Config.default
37
+ end
38
+
39
+ # So we can change the configuration if we want
40
+ def config
41
+ @config ||= Config.default
42
+ end
43
+
44
+ def share_template
45
+ return @share_template unless @share_template.nil?
46
+ @share_template = ::File.read(::File.expand_path("../html/share.html", ::File.dirname(__FILE__)))
47
+ end
48
+
49
+ def current
50
+ Thread.current[:mini_profiler_private]
51
+ end
52
+
53
+ def current=(c)
54
+ # we use TLS cause we need access to this from sql blocks and code blocks that have no access to env
55
+ Thread.current[:mini_profiler_private]= c
56
+ end
57
+
58
+ # discard existing results, don't track this request
59
+ def discard_results
60
+ self.current.discard = true if current
61
+ end
62
+
63
+ def create_current(env={}, options={})
64
+ # profiling the request
65
+ self.current = Context.new
66
+ self.current.inject_js = config.auto_inject && (!env['HTTP_X_REQUESTED_WITH'].eql? 'XMLHttpRequest')
67
+ self.current.page_struct = PageTimerStruct.new(env)
68
+ self.current.current_timer = current.page_struct['Root']
69
+ end
70
+
71
+ def authorize_request
72
+ Thread.current[:mp_authorized] = true
73
+ end
74
+
75
+ def deauthorize_request
76
+ Thread.current[:mp_authorized] = nil
77
+ end
78
+
79
+ def request_authorized?
80
+ Thread.current[:mp_authorized]
81
+ end
82
+
83
+ end
84
+
85
+ #
86
+ # options:
87
+ # :auto_inject - should script be automatically injected on every html page (not xhr)
88
+ def initialize(app, config = nil)
89
+ MiniProfiler.config.merge!(config)
90
+ @config = MiniProfiler.config
91
+ @app = app
92
+ @config.base_url_path << "/" unless @config.base_url_path.end_with? "/"
93
+ unless @config.storage_instance
94
+ @config.storage_instance = @config.storage.new(@config.storage_options)
95
+ end
96
+ @storage = @config.storage_instance
97
+ end
98
+
99
+ def user(env)
100
+ @config.user_provider.call(env)
101
+ end
102
+
103
+ def serve_results(env)
104
+ request = Rack::Request.new(env)
105
+ id = request['id']
106
+ page_struct = @storage.load(id)
107
+ unless page_struct
108
+ @storage.set_viewed(user(env), id)
109
+ return [404, {}, ["Request not found: #{request['id']} - user #{user(env)}"]]
110
+ end
111
+ unless page_struct['HasUserViewed']
112
+ page_struct['ClientTimings'] = ClientTimerStruct.init_from_form_data(env, page_struct)
113
+ page_struct['HasUserViewed'] = true
114
+ @storage.save(page_struct)
115
+ @storage.set_viewed(user(env), id)
116
+ end
117
+
118
+ result_json = page_struct.to_json
119
+ # If we're an XMLHttpRequest, serve up the contents as JSON
120
+ if request.xhr?
121
+ [200, { 'Content-Type' => 'application/json'}, [result_json]]
122
+ else
123
+
124
+ # Otherwise give the HTML back
125
+ html = MiniProfiler.share_template.dup
126
+ html.gsub!(/\{path\}/, "#{env['SCRIPT_NAME']}#{@config.base_url_path}")
127
+ html.gsub!(/\{version\}/, MiniProfiler::VERSION)
128
+ html.gsub!(/\{json\}/, result_json)
129
+ html.gsub!(/\{includes\}/, get_profile_script(env))
130
+ html.gsub!(/\{name\}/, page_struct['Name'])
131
+ html.gsub!(/\{duration\}/, "%.1f" % page_struct.duration_ms)
132
+
133
+ [200, {'Content-Type' => 'text/html'}, [html]]
134
+ end
135
+
136
+ end
137
+
138
+ def serve_html(env)
139
+ file_name = env['PATH_INFO'][(@config.base_url_path.length)..1000]
140
+ return serve_results(env) if file_name.eql?('results')
141
+ full_path = ::File.expand_path("../html/#{file_name}", ::File.dirname(__FILE__))
142
+ return [404, {}, ["Not found"]] unless ::File.exists? full_path
143
+ f = Rack::File.new nil
144
+ f.path = full_path
145
+
146
+ begin
147
+ f.cache_control = "max-age:86400"
148
+ f.serving env
149
+ rescue
150
+ # old versions of rack have a different api
151
+ status, headers, body = f.serving
152
+ headers.merge! 'Cache-Control' => "max-age:86400"
153
+ [status, headers, body]
154
+ end
155
+
156
+ end
157
+
158
+
159
+ def current
160
+ MiniProfiler.current
161
+ end
162
+
163
+ def current=(c)
164
+ MiniProfiler.current=c
165
+ end
166
+
167
+
168
+ def config
169
+ @config
170
+ end
171
+
172
+
173
+ def call(env)
174
+
175
+ client_settings = ClientSettings.new(env)
176
+
177
+ status = headers = body = nil
178
+ query_string = env['QUERY_STRING']
179
+ path = env['PATH_INFO']
180
+
181
+ skip_it = (@config.pre_authorize_cb && !@config.pre_authorize_cb.call(env)) ||
182
+ (@config.skip_paths && @config.skip_paths.any?{ |p| path[0,p.length] == p}) ||
183
+ query_string =~ /pp=skip/
184
+
185
+ has_profiling_cookie = client_settings.has_cookie?
186
+
187
+ if skip_it || (@config.authorization_mode == :whitelist && !has_profiling_cookie)
188
+ status,headers,body = @app.call(env)
189
+ if !skip_it && @config.authorization_mode == :whitelist && !has_profiling_cookie && MiniProfiler.request_authorized?
190
+ client_settings.write!(headers)
191
+ end
192
+ return [status,headers,body]
193
+ end
194
+
195
+ # handle all /mini-profiler requests here
196
+ return serve_html(env) if path.start_with? @config.base_url_path
197
+
198
+ has_disable_cookie = client_settings.disable_profiling?
199
+ # manual session disable / enable
200
+ if query_string =~ /pp=disable/ || has_disable_cookie
201
+ skip_it = true
202
+ end
203
+
204
+ if query_string =~ /pp=enable/
205
+ skip_it = false
206
+ end
207
+
208
+ if skip_it
209
+ status,headers,body = @app.call(env)
210
+ client_settings.disable_profiling = true
211
+ client_settings.write!(headers)
212
+ return [status,headers,body]
213
+ else
214
+ client_settings.disable_profiling = false
215
+ end
216
+
217
+ if query_string =~ /pp=profile-gc/
218
+ if query_string =~ /pp=profile-gc-time/
219
+ return Rack::MiniProfiler::GCProfiler.new.profile_gc_time(@app, env)
220
+ else
221
+ return Rack::MiniProfiler::GCProfiler.new.profile_gc(@app, env)
222
+ end
223
+ end
224
+
225
+ MiniProfiler.create_current(env, @config)
226
+ MiniProfiler.deauthorize_request if @config.authorization_mode == :whitelist
227
+ if query_string =~ /pp=normal-backtrace/
228
+ client_settings.backtrace_level = ClientSettings::BACKTRACE_DEFAULT
229
+ elsif query_string =~ /pp=no-backtrace/
230
+ current.skip_backtrace = true
231
+ client_settings.backtrace_level = ClientSettings::BACKTRACE_NONE
232
+ elsif query_string =~ /pp=full-backtrace/ || client_settings.backtrace_full?
233
+ current.full_backtrace = true
234
+ client_settings.backtrace_level = ClientSettings::BACKTRACE_FULL
235
+ elsif client_settings.backtrace_none?
236
+ current.skip_backtrace = true
237
+ end
238
+
239
+ done_sampling = false
240
+ quit_sampler = false
241
+ backtraces = nil
242
+
243
+ if query_string =~ /pp=sample/ || query_string =~ /pp=flamegraph/
244
+ current.measure = false
245
+ skip_frames = 0
246
+ backtraces = []
247
+ t = Thread.current
248
+
249
+ Thread.new {
250
+ # new in Ruby 2.0
251
+ has_backtrace_locations = t.respond_to?(:backtrace_locations)
252
+ begin
253
+ i = 10000 # for sanity never grab more than 10k samples
254
+ while i > 0
255
+ break if done_sampling
256
+ i -= 1
257
+ backtraces << (has_backtrace_locations ? t.backtrace_locations : t.backtrace)
258
+
259
+ # On my machine using Ruby 2.0 this give me excellent fidelity of stack trace per 1.2ms
260
+ # with this fidelity analysis becomes very powerful
261
+ sleep 0.0005
262
+ end
263
+ ensure
264
+ quit_sampler = true
265
+ end
266
+ }
267
+ end
268
+
269
+ status, headers, body = nil
270
+ start = Time.now
271
+ begin
272
+
273
+ # Strip all the caching headers so we don't get 304s back
274
+ # This solves a very annoying bug where rack mini profiler never shows up
275
+ env['HTTP_IF_MODIFIED_SINCE'] = nil
276
+ env['HTTP_IF_NONE_MATCH'] = nil
277
+
278
+ status,headers,body = @app.call(env)
279
+ client_settings.write!(headers)
280
+ ensure
281
+ if backtraces
282
+ done_sampling = true
283
+ sleep 0.001 until quit_sampler
284
+ end
285
+ end
286
+
287
+ skip_it = current.discard
288
+
289
+ if (config.authorization_mode == :whitelist && !MiniProfiler.request_authorized?)
290
+ # this is non-obvious, don't kill the profiling cookie on errors or short requests
291
+ # this ensures that stuff that never reaches the rails stack does not kill profiling
292
+ if status == 200 && ((Time.now - start) > 0.1)
293
+ client_settings.discard_cookie!(headers)
294
+ end
295
+ skip_it = true
296
+ end
297
+
298
+ return [status,headers,body] if skip_it
299
+
300
+ # we must do this here, otherwise current[:discard] is not being properly treated
301
+ if query_string =~ /pp=env/
302
+ body.close if body.respond_to? :close
303
+ return dump_env env
304
+ end
305
+
306
+ if query_string =~ /pp=help/
307
+ body.close if body.respond_to? :close
308
+ return help(client_settings)
309
+ end
310
+
311
+ page_struct = current.page_struct
312
+ page_struct['User'] = user(env)
313
+ page_struct['Root'].record_time((Time.now - start) * 1000)
314
+
315
+ if backtraces
316
+ body.close if body.respond_to? :close
317
+ if query_string =~ /pp=sample/
318
+ return analyze(backtraces, page_struct)
319
+ else
320
+ return flame_graph(backtraces, page_struct)
321
+ end
322
+ end
323
+
324
+
325
+ # no matter what it is, it should be unviewed, otherwise we will miss POST
326
+ @storage.set_unviewed(page_struct['User'], page_struct['Id'])
327
+ @storage.save(page_struct)
328
+
329
+ # inject headers, script
330
+ if status == 200
331
+
332
+ client_settings.write!(headers)
333
+
334
+ # mini profiler is meddling with stuff, we can not cache cause we will get incorrect data
335
+ # Rack::ETag has already inserted some nonesense in the chain
336
+ headers.delete('ETag')
337
+ headers.delete('Date')
338
+ headers['Cache-Control'] = 'must-revalidate, private, max-age=0'
339
+
340
+ # inject header
341
+ if headers.is_a? Hash
342
+ headers['X-MiniProfiler-Ids'] = ids_json(env)
343
+ end
344
+
345
+ # inject script
346
+ if current.inject_js \
347
+ && headers.has_key?('Content-Type') \
348
+ && !headers['Content-Type'].match(/text\/html/).nil? then
349
+
350
+ response = Rack::Response.new([], status, headers)
351
+ script = self.get_profile_script(env)
352
+
353
+ if String === body
354
+ response.write inject(body,script)
355
+ else
356
+ body.each { |fragment| response.write inject(fragment, script) }
357
+ end
358
+ body.close if body.respond_to? :close
359
+ return response.finish
360
+ end
361
+ end
362
+
363
+ client_settings.write!(headers)
364
+ [status, headers, body]
365
+ ensure
366
+ # Make sure this always happens
367
+ current = nil
368
+ end
369
+
370
+ def inject(fragment, script)
371
+ if fragment.match(/<\/body>/i)
372
+ # explicit </body>
373
+
374
+ regex = /<\/body>/i
375
+ close_tag = '</body>'
376
+ elsif fragment.match(/<\/html>/i)
377
+ # implicit </body>
378
+
379
+ regex = /<\/html>/i
380
+ close_tag = '</html>'
381
+ else
382
+ # implicit </body> and </html>. Just append the script.
383
+
384
+ return fragment + script
385
+ end
386
+
387
+ matches = fragment.scan(regex).length
388
+ index = 1
389
+ fragment.gsub(regex) do
390
+ # though malformed there is an edge case where /body exists earlier in the html, work around
391
+ if index < matches
392
+ index += 1
393
+ close_tag
394
+ else
395
+
396
+ # if for whatever crazy reason we dont get a utf string,
397
+ # just force the encoding, no utf in the mp scripts anyway
398
+ if script.respond_to?(:encoding) && script.respond_to?(:force_encoding)
399
+ (script + close_tag).force_encoding(fragment.encoding)
400
+ else
401
+ script + close_tag
402
+ end
403
+ end
404
+ end
405
+ end
406
+
407
+ def dump_env(env)
408
+ headers = {'Content-Type' => 'text/plain'}
409
+ body = "Rack Environment\n---------------\n"
410
+ env.each do |k,v|
411
+ body << "#{k}: #{v}\n"
412
+ end
413
+
414
+ body << "\n\nEnvironment\n---------------\n"
415
+ ENV.each do |k,v|
416
+ body << "#{k}: #{v}\n"
417
+ end
418
+
419
+ body << "\n\nInternals\n---------------\n"
420
+ body << "Storage Provider #{config.storage_instance}\n"
421
+ body << "User #{user(env)}\n"
422
+ body << config.storage_instance.diagnostics(user(env)) rescue "no diagnostics implemented for storage"
423
+
424
+ [200, headers, [body]]
425
+ end
426
+
427
+ def help(client_settings)
428
+ headers = {'Content-Type' => 'text/plain'}
429
+ body = "Append the following to your query string:
430
+
431
+ pp=help : display this screen
432
+ pp=env : display the rack environment
433
+ pp=skip : skip mini profiler for this request
434
+ pp=no-backtrace #{"(*) " if client_settings.backtrace_none?}: don't collect stack traces from all the SQL executed (sticky, use pp=normal-backtrace to enable)
435
+ pp=normal-backtrace #{"(*) " if client_settings.backtrace_default?}: collect stack traces from all the SQL executed and filter normally
436
+ pp=full-backtrace #{"(*) " if client_settings.backtrace_full?}: enable full backtraces for SQL executed (use pp=normal-backtrace to disable)
437
+ pp=sample : sample stack traces and return a report isolating heavy usage (works best on Ruby 2.0)
438
+ pp=disable : disable profiling for this session
439
+ pp=enable : enable profiling for this session (if previously disabled)
440
+ pp=profile-gc: perform gc profiling on this request, analyzes ObjectSpace generated by request (ruby 1.9.3 only)
441
+ pp=profile-gc-time: perform built-in gc profiling on this request (ruby 1.9.3 only)
442
+ pp=flamegraph: works best on Ruby 2.0, a graph representing sampled activity.
443
+ "
444
+
445
+ client_settings.write!(headers)
446
+ [200, headers, [body]]
447
+ end
448
+
449
+ def flame_graph(traces, page_struct)
450
+ graph = FlameGraph.new(traces)
451
+ data = graph.graph_data
452
+
453
+ headers = {'Content-Type' => 'text/html'}
454
+
455
+ body = IO.read(::File.expand_path('../html/flamegraph.html', ::File.dirname(__FILE__)))
456
+ body.gsub!("/*DATA*/", ::JSON.generate(data));
457
+
458
+ [200, headers, [body]]
459
+ end
460
+
461
+ def analyze(traces, page_struct)
462
+ headers = {'Content-Type' => 'text/plain'}
463
+ body = "Collected: #{traces.count} stack traces. Duration(ms): #{page_struct.duration_ms}"
464
+
465
+ seen = {}
466
+ fulldump = ""
467
+ traces.each do |trace|
468
+ fulldump << "\n\n"
469
+ distinct = {}
470
+ trace.each do |frame|
471
+ frame = frame.to_s unless String === frame
472
+ unless distinct[frame]
473
+ distinct[frame] = true
474
+ seen[frame] ||= 0
475
+ seen[frame] += 1
476
+ end
477
+ fulldump << frame << "\n"
478
+ end
479
+ end
480
+
481
+ body << "\n\nStack Trace Analysis\n"
482
+ seen.to_a.sort{|x,y| y[1] <=> x[1]}.each do |name, count|
483
+ if count > traces.count / 10
484
+ body << "#{name} x #{count}\n"
485
+ end
486
+ end
487
+
488
+ body << "\n\n\nRaw traces \n"
489
+ body << fulldump
490
+
491
+ [200, headers, [body]]
492
+ end
493
+
494
+ def ids_json(env)
495
+ # cap at 10 ids, otherwise there is a chance you can blow the header
496
+ ids = [current.page_struct["Id"]] + (@storage.get_unviewed_ids(user(env)) || [])[0..8]
497
+ ::JSON.generate(ids.uniq)
498
+ end
499
+
500
+ def ids_comma_separated(env)
501
+ # cap at 10 ids, otherwise there is a chance you can blow the header
502
+ ids = [current.page_struct["Id"]] + (@storage.get_unviewed_ids(user(env)) || [])[0..8]
503
+ ids.join(",")
504
+ end
505
+
506
+ # get_profile_script returns script to be injected inside current html page
507
+ # By default, profile_script is appended to the end of all html requests automatically.
508
+ # Calling get_profile_script cancels automatic append for the current page
509
+ # Use it when:
510
+ # * you have disabled auto append behaviour throught :auto_inject => false flag
511
+ # * you do not want script to be automatically appended for the current page. You can also call cancel_auto_inject
512
+ def get_profile_script(env)
513
+ ids = ids_comma_separated(env)
514
+ path = "#{env['SCRIPT_NAME']}#{@config.base_url_path}"
515
+ version = MiniProfiler::VERSION
516
+ position = @config.position
517
+ showTrivial = false
518
+ showChildren = false
519
+ maxTracesToShow = 10
520
+ showControls = false
521
+ currentId = current.page_struct["Id"]
522
+ authorized = true
523
+ toggleShortcut = @config.toggle_shortcut
524
+ startHidden = @config.start_hidden
525
+ # TODO : cache this snippet
526
+ script = IO.read(::File.expand_path('../html/profile_handler.js', ::File.dirname(__FILE__)))
527
+ # replace the variables
528
+ [:ids, :path, :version, :position, :showTrivial, :showChildren, :maxTracesToShow, :showControls, :currentId, :authorized, :toggleShortcut, :startHidden].each do |v|
529
+ regex = Regexp.new("\\{#{v.to_s}\\}")
530
+ script.gsub!(regex, eval(v.to_s).to_s)
531
+ end
532
+ current.inject_js = false
533
+ script
534
+ end
535
+
536
+ # cancels automatic injection of profile script for the current page
537
+ def cancel_auto_inject(env)
538
+ current.inject_js = false
539
+ end
540
+
541
+ end
542
+
543
+ end
544
+