mini-mini-profiler 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+