rack-insight 0.5.0

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 (126) hide show
  1. data/.gitignore +13 -0
  2. data/.rspec +1 -0
  3. data/.simplecov +4 -0
  4. data/.travis.yml +8 -0
  5. data/CHANGELOG +58 -0
  6. data/Gemfile +3 -0
  7. data/Gemfile.lock +82 -0
  8. data/LICENSE +24 -0
  9. data/README.md +189 -0
  10. data/Rakefile +27 -0
  11. data/TODO +7 -0
  12. data/lib/rack-insight.rb +1 -0
  13. data/lib/rack/insight.rb +19 -0
  14. data/lib/rack/insight/app.rb +198 -0
  15. data/lib/rack/insight/config.rb +30 -0
  16. data/lib/rack/insight/database.rb +193 -0
  17. data/lib/rack/insight/enable-button.rb +43 -0
  18. data/lib/rack/insight/filtered_backtrace.rb +45 -0
  19. data/lib/rack/insight/instrumentation.rb +9 -0
  20. data/lib/rack/insight/instrumentation/backstage.rb +10 -0
  21. data/lib/rack/insight/instrumentation/client.rb +20 -0
  22. data/lib/rack/insight/instrumentation/instrument.rb +109 -0
  23. data/lib/rack/insight/instrumentation/package-definition.rb +58 -0
  24. data/lib/rack/insight/instrumentation/probe-definition.rb +20 -0
  25. data/lib/rack/insight/instrumentation/probe.rb +196 -0
  26. data/lib/rack/insight/instrumentation/setup.rb +32 -0
  27. data/lib/rack/insight/logger.rb +53 -0
  28. data/lib/rack/insight/options.rb +116 -0
  29. data/lib/rack/insight/panel.rb +135 -0
  30. data/lib/rack/insight/panel_app.rb +31 -0
  31. data/lib/rack/insight/panels-content.rb +22 -0
  32. data/lib/rack/insight/panels-header.rb +18 -0
  33. data/lib/rack/insight/panels/active_record_panel.rb +46 -0
  34. data/lib/rack/insight/panels/active_resource_panel.rb +48 -0
  35. data/lib/rack/insight/panels/active_resource_panel/query.rb +27 -0
  36. data/lib/rack/insight/panels/cache_panel.rb +68 -0
  37. data/lib/rack/insight/panels/cache_panel/panel_app.rb +46 -0
  38. data/lib/rack/insight/panels/cache_panel/stats.rb +90 -0
  39. data/lib/rack/insight/panels/log_panel.rb +53 -0
  40. data/lib/rack/insight/panels/memory_panel.rb +36 -0
  41. data/lib/rack/insight/panels/mongo_panel.rb +41 -0
  42. data/lib/rack/insight/panels/mongo_panel/mongo_extension.rb +24 -0
  43. data/lib/rack/insight/panels/mongo_panel/stats.rb +46 -0
  44. data/lib/rack/insight/panels/rails_info_panel.rb +19 -0
  45. data/lib/rack/insight/panels/redis_panel.rb +42 -0
  46. data/lib/rack/insight/panels/redis_panel/redis_extension.rb +23 -0
  47. data/lib/rack/insight/panels/redis_panel/stats.rb +50 -0
  48. data/lib/rack/insight/panels/request_variables_panel.rb +70 -0
  49. data/lib/rack/insight/panels/speedtracer_panel.rb +89 -0
  50. data/lib/rack/insight/panels/speedtracer_panel/profiling.rb +29 -0
  51. data/lib/rack/insight/panels/speedtracer_panel/trace-app.rb +52 -0
  52. data/lib/rack/insight/panels/speedtracer_panel/tracer.rb +213 -0
  53. data/lib/rack/insight/panels/sphinx_panel.rb +41 -0
  54. data/lib/rack/insight/panels/sphinx_panel/stats.rb +94 -0
  55. data/lib/rack/insight/panels/sql_panel.rb +53 -0
  56. data/lib/rack/insight/panels/sql_panel/panel_app.rb +37 -0
  57. data/lib/rack/insight/panels/sql_panel/query.rb +94 -0
  58. data/lib/rack/insight/panels/templates_panel.rb +58 -0
  59. data/lib/rack/insight/panels/templates_panel/rendering.rb +81 -0
  60. data/lib/rack/insight/panels/timer_panel.rb +40 -0
  61. data/lib/rack/insight/params_signature.rb +61 -0
  62. data/lib/rack/insight/path-filter.rb +23 -0
  63. data/lib/rack/insight/public/__insight__/bookmarklet.html +10 -0
  64. data/lib/rack/insight/public/__insight__/bookmarklet.js +223 -0
  65. data/lib/rack/insight/public/__insight__/insight.css +235 -0
  66. data/lib/rack/insight/public/__insight__/insight.js +127 -0
  67. data/lib/rack/insight/public/__insight__/jquery-1.3.2.js +4376 -0
  68. data/lib/rack/insight/public/__insight__/jquery.tablesorter.min.js +1 -0
  69. data/lib/rack/insight/public/__insight__/spinner.gif +0 -0
  70. data/lib/rack/insight/rack_static_bug_avoider.rb +16 -0
  71. data/lib/rack/insight/redirect_interceptor.rb +25 -0
  72. data/lib/rack/insight/render.rb +72 -0
  73. data/lib/rack/insight/request-recorder.rb +22 -0
  74. data/lib/rack/insight/rspec_matchers.rb +33 -0
  75. data/lib/rack/insight/toolbar.rb +69 -0
  76. data/lib/rack/insight/version.rb +7 -0
  77. data/lib/rack/insight/views/enable-button.html.erb +21 -0
  78. data/lib/rack/insight/views/error.html.erb +17 -0
  79. data/lib/rack/insight/views/headers_fragment.html.erb +20 -0
  80. data/lib/rack/insight/views/panels/active_record.html.erb +17 -0
  81. data/lib/rack/insight/views/panels/active_resource.html.erb +47 -0
  82. data/lib/rack/insight/views/panels/cache.html.erb +93 -0
  83. data/lib/rack/insight/views/panels/execute_sql.html.erb +32 -0
  84. data/lib/rack/insight/views/panels/explain_sql.html.erb +32 -0
  85. data/lib/rack/insight/views/panels/log.html.erb +21 -0
  86. data/lib/rack/insight/views/panels/mongo.html.erb +32 -0
  87. data/lib/rack/insight/views/panels/profile_sql.html.erb +32 -0
  88. data/lib/rack/insight/views/panels/rails_info.html.erb +19 -0
  89. data/lib/rack/insight/views/panels/redis.html.erb +46 -0
  90. data/lib/rack/insight/views/panels/request_variables.html.erb +25 -0
  91. data/lib/rack/insight/views/panels/speedtracer/serverevent.html.erb +10 -0
  92. data/lib/rack/insight/views/panels/speedtracer/servertrace.html.erb +12 -0
  93. data/lib/rack/insight/views/panels/speedtracer/traces.html.erb +18 -0
  94. data/lib/rack/insight/views/panels/sphinx.html.erb +32 -0
  95. data/lib/rack/insight/views/panels/sql.html.erb +43 -0
  96. data/lib/rack/insight/views/panels/templates.html.erb +6 -0
  97. data/lib/rack/insight/views/panels/timer.html.erb +19 -0
  98. data/lib/rack/insight/views/panels/view_cache.html.erb +19 -0
  99. data/lib/rack/insight/views/redirect.html.erb +16 -0
  100. data/lib/rack/insight/views/request_fragment.html.erb +25 -0
  101. data/lib/rack/insight/views/toolbar.html.erb +29 -0
  102. data/rack-insight.gemspec +40 -0
  103. data/spec/custom_matchers.rb +0 -0
  104. data/spec/fixtures/config.ru +8 -0
  105. data/spec/fixtures/dummy_panel.rb +2 -0
  106. data/spec/fixtures/sample_app.rb +72 -0
  107. data/spec/fixtures/star_trek_panel.rb +1 -0
  108. data/spec/insight_spec.rb +163 -0
  109. data/spec/instrumentation_spec.rb +188 -0
  110. data/spec/rack/insight/config_spec.rb +20 -0
  111. data/spec/rack/insight/panels/active_record_panel_spec.rb +43 -0
  112. data/spec/rack/insight/panels/active_resource_panel_spec.rb +40 -0
  113. data/spec/rack/insight/panels/cache_panel_spec.rb +178 -0
  114. data/spec/rack/insight/panels/log_panel_spec.rb +44 -0
  115. data/spec/rack/insight/panels/memory_panel_spec.rb +21 -0
  116. data/spec/rack/insight/panels/mongo_panel_spec_pending.rb +52 -0
  117. data/spec/rack/insight/panels/rails_info_panel_spec.rb +29 -0
  118. data/spec/rack/insight/panels/redis_panel_spec.rb +67 -0
  119. data/spec/rack/insight/panels/speedtracer_panel_spec.rb +86 -0
  120. data/spec/rack/insight/panels/sql_panel_spec.rb +146 -0
  121. data/spec/rack/insight/panels/templates_panel_spec.rb +86 -0
  122. data/spec/rack/insight/panels/timer_panel_spec.rb +38 -0
  123. data/spec/rcov.opts +1 -0
  124. data/spec/spec.opts +1 -0
  125. data/spec/spec_helper.rb +111 -0
  126. metadata +380 -0
@@ -0,0 +1 @@
1
+ (function($){$.extend({tablesorter:new function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'.',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}var rows=table.tBodies[0].rows;if(table.tBodies[0].rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,cells[i]);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,node){var l=parsers.length;for(var i=1;i<l;i++){if(parsers[i].is($.trim(getElementText(table.config,node)),table,node)){return parsers[i];}}return parsers[0];}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=table.tBodies[0].rows[i],cols=[];cache.row.push($(c));for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c.cells[j]),table,c.cells[j]));}cols.push(i);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){if(!node)return"";var t="";if(config.textExtraction=="simple"){if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){t=node.childNodes[0].innerHTML;}else{t=node.innerHTML;}}else{if(typeof(config.textExtraction)=="function"){t=config.textExtraction(node);}else{t=$(node).text();}}return t;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){rows.push(r[n[i][checkCell]]);if(!table.config.appender){var o=r[n[i][checkCell]];var l=o.length;for(var j=0;j<l;j++){tableBody[0].appendChild(o[j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false,tableHeadersRows=[];for(var i=0;i<table.tHead.rows.length;i++){tableHeadersRows[i]=0;};$tableHeaders=$("thead th",table);$tableHeaders.each(function(index){this.count=0;this.column=index;this.order=formatSortingOrder(table.config.sortInitialOrder);if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(!this.sortDisabled){$(this).addClass(table.config.cssHeader);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){i=(v.toLowerCase()=="desc")?1:0;}else{i=(v==(0||1))?v:0;}return i;}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(getCachedSortType(table.config.parsers,c)=="text")?((order==0)?"sortText":"sortTextDesc"):((order==0)?"sortNumeric":"sortNumericDesc");var e="e"+i;dynamicExp+="var "+e+" = "+s+"(a["+c+"],b["+c+"]); ";dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function sortText(a,b){return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){$this.trigger("sortStart");var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){var $cell=$(this);var i=this.column;this.order=this.count++%2;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){var DECIMAL='\\'+config.decimal;var exp='/(^[+]?0('+DECIMAL+'0+)?$)|(^([-+]?[1-9][0-9]*)$)|(^([-+]?((0?|[1-9][0-9]*)'+DECIMAL+'(0*[1-9][0-9]*)))$)|(^[-+]?[1-9]+[0-9]*'+DECIMAL+'0+$)/';return RegExp(exp).test($.trim(s));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[£$€?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[^0-9.]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}$("tr:visible",table.tBodies[0]).filter(':even').removeClass(table.config.widgetZebra.css[1]).addClass(table.config.widgetZebra.css[0]).end().filter(':odd').removeClass(table.config.widgetZebra.css[0]).addClass(table.config.widgetZebra.css[1]);if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery);
@@ -0,0 +1,16 @@
1
+ module Rack::Insight
2
+ class RackStaticBugAvoider
3
+ def initialize(app, static_app)
4
+ @app = app
5
+ @static_app = static_app
6
+ end
7
+
8
+ def call(env)
9
+ if env["PATH_INFO"]
10
+ @static_app.call(env)
11
+ else
12
+ @app.call(env)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ module Rack::Insight
2
+ class RedirectInterceptor
3
+ include Render
4
+
5
+ def initialize(app)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ status, headers, body = @app.call(env)
11
+ @response = Rack::Response.new(body, status, headers)
12
+ if @response.redirect? && env["rack-insight.intercept_redirects"]
13
+ intercept_redirect
14
+ end
15
+ @response.to_a
16
+ end
17
+
18
+ def intercept_redirect
19
+ new_body = render_template("redirect", :redirect_to => @response.location)
20
+ new_headers = { "Content-Type" => "text/html", "Content-Length" => new_body.size.to_s }
21
+ @response = Rack::Response.new(new_body, 200, new_headers)
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,72 @@
1
+ require 'erb'
2
+
3
+ module Rack::Insight
4
+ module Render
5
+ include ERB::Util
6
+ include Logging
7
+
8
+ def signed_params(hash)
9
+ ParamsSignature.sign(request, hash)
10
+ end
11
+
12
+ module CompiledTemplates
13
+ end
14
+ include CompiledTemplates
15
+
16
+ def render_template(filename, local_assigns = {})
17
+ compile(filename, local_assigns)
18
+ render_symbol = method_name(filename, local_assigns)
19
+ send(render_symbol, local_assigns)
20
+ end
21
+
22
+ def compile(filename, local_assigns)
23
+ render_symbol = method_name(filename, local_assigns)
24
+
25
+ if !CompiledTemplates.instance_methods.include?(render_symbol.to_s)
26
+ compile!(filename, local_assigns)
27
+ end
28
+ end
29
+
30
+ def compile!(filename, local_assigns)
31
+ render_symbol = method_name(filename, local_assigns)
32
+ locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join
33
+
34
+ source = <<-end_src
35
+ def #{render_symbol}(local_assigns)
36
+ #{locals_code}
37
+ #{compiled_source(filename)}
38
+ end
39
+ end_src
40
+
41
+ begin
42
+ CompiledTemplates.module_eval(source, filename, 0)
43
+ rescue Object => ex
44
+ logger.debug do
45
+ "#{ex.class.name}: #{ex.message} in\n" +
46
+ source +
47
+ ex.backtrace.join("\n")
48
+ end
49
+ end
50
+ end
51
+
52
+ def compiled_source(filename)
53
+ ::ERB.new(::File.read(::File.dirname(__FILE__) + "/views/#{filename}.html.erb"), nil, "-").src
54
+ end
55
+
56
+ def method_name(filename, local_assigns)
57
+ if local_assigns && local_assigns.any?
58
+ method_name = method_name_without_locals(filename).dup
59
+ method_name << "_locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}"
60
+ else
61
+ method_name = method_name_without_locals(filename)
62
+ end
63
+ method_name.to_sym
64
+ end
65
+
66
+ def method_name_without_locals(filename)
67
+ filename.split("/").join("_").tr("-", "_")
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,22 @@
1
+ require 'rack/insight/database'
2
+
3
+ module Rack::Insight
4
+ class RequestRecorder
5
+ def initialize(app)
6
+ @app = app
7
+ @request_table = Database::RequestTable.new()
8
+ end
9
+
10
+ def call(env)
11
+ env["rack-insight.request-id"] =
12
+ @request_table.store(env["REQUEST_METHOD"],
13
+ env["PATH_INFO"])
14
+
15
+ results = @app.call(env)
16
+
17
+ @request_table.sweep
18
+
19
+ return results
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,33 @@
1
+ module Rack::Insight
2
+ module RspecMatchers
3
+
4
+ RSpec::Matchers.define :have_row do |container, key, value|
5
+ match do |response|
6
+ if value
7
+ response.should have_selector("#{container} tr", :content => key) do |row|
8
+ row.should contain(value)
9
+ end
10
+ else
11
+ response.should have_selector("#{container} tr", :content => key)
12
+ end
13
+ end
14
+
15
+ failure_message_for_should do |response|
16
+ "Expected: \n#{response.body}\nto have a row matching #{key}"
17
+ end
18
+ end
19
+
20
+ RSpec::Matchers.define :have_heading do |text|
21
+ match do |response|
22
+ response.should have_selector("#rack-insight_toolbar li") do |heading|
23
+ heading.should contain(text)
24
+ end
25
+ end
26
+
27
+ failure_message_for_should do |response|
28
+ "Expected: \n#{response.body}\nto have heading #{text}"
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,69 @@
1
+ module Rack::Insight
2
+ class Toolbar
3
+ include Render
4
+ include Logging
5
+
6
+ MIME_TYPES = ["text/html", "application/xhtml+xml"]
7
+
8
+ def initialize(app, insight)
9
+ @app = app
10
+ @insight = insight
11
+ @request_table = Database::RequestTable.new
12
+ end
13
+
14
+ def call(env)
15
+ @env = env
16
+ status, headers, body = @app.call(@env)
17
+
18
+ response = Rack::Response.new(body, status, headers)
19
+
20
+ if okay_to_modify?(env, response)
21
+ inject_toolbar(response)
22
+ end
23
+
24
+ return response.to_a
25
+ end
26
+
27
+ def okay_to_modify?(env, response)
28
+ req = Rack::Request.new(env)
29
+ content_type, charset = response.content_type.split(";")
30
+
31
+ response.ok? && MIME_TYPES.include?(content_type) && !req.xhr?
32
+ end
33
+
34
+ def inject_toolbar(response)
35
+ full_body = response.body.join
36
+ full_body.sub! /<\/body>/, render + "</body>"
37
+
38
+ response["Content-Length"] = full_body.size.to_s
39
+
40
+ # Ensure that browser doesn't cache
41
+ response["Etag"] = ""
42
+ response["Cache-Control"] = "no-cache"
43
+
44
+ response.body = [full_body]
45
+ end
46
+
47
+ def render
48
+ req_id = (@env['rack-insight.request-id'] || @request_table.last_request_id).to_i
49
+ requests = @request_table.to_a.map do |row|
50
+ { :id => row[0], :method => row[1], :path => row[2] }
51
+ end
52
+
53
+ logger.info{ "Injecting toolbar: active panels: #{@insight.panels.map{|pnl| pnl.class.name}.inspect}" }
54
+
55
+ headers_fragment = render_template("headers_fragment",
56
+ :panels => @insight.panels,
57
+ :request_id => req_id)
58
+
59
+ current_request_fragment = render_template("request_fragment",
60
+ :request_id => req_id,
61
+ :requests => requests,
62
+ :panels => @insight.panels)
63
+ render_template("toolbar",
64
+ :request_fragment => current_request_fragment,
65
+ :headers_fragment => headers_fragment,
66
+ :request_id => req_id)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,7 @@
1
+ module Rack
2
+ module Insight
3
+
4
+ VERSION = '0.5.0'
5
+
6
+ end
7
+ end
@@ -0,0 +1,21 @@
1
+ <script type="text/javascript" src='/__insight__/bookmarklet.js'></script>
2
+ <style>
3
+ #rack-insight-enabler {
4
+ position: absolute;
5
+ z-index: 100000;
6
+ overflow: hidden;
7
+ width: 4px;
8
+ height: 4px;
9
+ top: 0;
10
+ left: 0;
11
+ background: #326342;
12
+ color: #92EF3F;
13
+ }
14
+
15
+ #rack-insight-enabler:hover {
16
+ width: 50px;
17
+ height: 24px;
18
+ }
19
+ </style>
20
+
21
+ <a id='rack-insight-enabler' href="#" onclick="document.insightEnable()" style="">Rack::Insight</a>
@@ -0,0 +1,17 @@
1
+ <script type="text/javascript" charset="utf-8">
2
+ if (typeof jQuery == 'undefined') {
3
+ var jquery_url = '/__insight__/jquery-1.3.2.js';
4
+ document.write(unescape('%3Cscript src="' + jquery_url + '" type="text/javascript"%3E%3C/script%3E'));
5
+ }
6
+ </script>
7
+ <script type="text/javascript" src="/__insight__/insight.js"></script>
8
+ <style type="text/css" media="screen">
9
+ @import url(/__insight__/insight.css);
10
+ </style>
11
+
12
+ <div id="rack-insight" class="rack-insight_error">
13
+ <div id="rack-insight_toolbar">
14
+
15
+ <p>There was an error within Rack::Insight!</p>
16
+ </div>
17
+ </div>
@@ -0,0 +1,20 @@
1
+ <li id="rack-insight_disable_button">[X]</li>
2
+ <li id="rack-insight_debug_button">Rack::Insight (req# <span id="request_id"><%= request_id %></span>)</li>
3
+ <% panels.each do |panel| %>
4
+ <li>
5
+ <% begin %>
6
+ <% if panel.has_content? %>
7
+ <a href="#" class="<%= panel.name %>">
8
+ <%= panel.heading_for_request(request_id) %>
9
+ </a>
10
+ <% else %>
11
+ <a href="javascript:void(0);">
12
+ <%= panel.heading_for_request(request_id) %>
13
+ </a>
14
+ <% end %>
15
+ <% rescue Object => ex %>
16
+ Err: <%= panel.name rescue "xxx" %>
17
+ <!-- <%= ([ex.class.name, ex.message] + ex.backtrace[0..5]).join("\n") %> -->
18
+ <% end %>
19
+ </li>
20
+ <% end %>
@@ -0,0 +1,17 @@
1
+ <h3>ActiveRecord Objects</h3>
2
+ <table class="sortable">
3
+ <thead>
4
+ <tr>
5
+ <th>Count</th>
6
+ <th>Class</th>
7
+ </tr>
8
+ </thead>
9
+ <tbody>
10
+ <% records.each do |class_name, count| %>
11
+ <tr>
12
+ <td><%= count %></td>
13
+ <td><%= class_name %></td>
14
+ </tr>
15
+ <% end %>
16
+ </tbody>
17
+ </table>
@@ -0,0 +1,47 @@
1
+ <h3>ActiveResource HTTP Queries</h3>
2
+ <table class="sortable">
3
+ <thead>
4
+ <tr>
5
+ <th class="time">Time&nbsp;(ms)</th>
6
+ <th class="query">Query</th>
7
+ <th class="backtrace">Backtrace</th>
8
+ <th class="result">Result</th>
9
+ </tr>
10
+ </thead>
11
+ <tbody>
12
+ <% i = 1 %>
13
+ <% queries.each do |query| %>
14
+ <tr class="<%= i % 2 == 0 ? "even" : "odd" %>">
15
+ <td><%= query.human_time %></td>
16
+ <td class="syntax"><%= query.path %><%= query.args.inspect %></td>
17
+ <td>
18
+ <% if query.has_backtrace? %>
19
+ <a href="#" class="reveal_backtrace">Show Backtrace</a>
20
+ <% end %>
21
+ </td>
22
+ <td>
23
+ <a href="#" class="reveal_response">Show Response Body</a>
24
+ </td>
25
+ </tr>
26
+ <tr style="display:none">
27
+ <td></td>
28
+ <td colspan="3">
29
+ <ul>
30
+ <% query.filtered_backtrace.each do |line| %>
31
+ <li><%=h line %></li>
32
+ <% end %>
33
+ </ul>
34
+ </td>
35
+ </tr>
36
+ <tr id="response" style="display:none">
37
+ <td></td>
38
+ <td colspan="3">
39
+ <p><%= query.results %></p>
40
+ <!-- <%= query.inspect %> -->
41
+ <!-- <%= query.results.inspect %> -->
42
+ </td>
43
+ </tr>
44
+ <% i += 1 %>
45
+ <% end %>
46
+ </tbody>
47
+ </table>
@@ -0,0 +1,93 @@
1
+ <h3>Cache Usage</h3>
2
+ <table id="cache_usage">
3
+ <colgroup>
4
+ <col width="12%"/>
5
+ <col width="12%"/>
6
+ <col width="12%"/>
7
+ <col width="12%"/>
8
+ <col width="12%"/>
9
+ <col width="12%"/>
10
+ <col width="12%"/>
11
+ <col width="12%"/>
12
+ </colgroup>
13
+ <tr>
14
+ <th>Total Calls</th>
15
+ <td><%= stats.calls %></td>
16
+
17
+ <th>Total Time</th>
18
+ <td><%= stats.display_time %></td>
19
+
20
+ <th>Hits</th>
21
+ <td><%= stats.hits %></td>
22
+
23
+ <th>Misses</th>
24
+ <td><%= stats.misses %></td>
25
+ </tr>
26
+ <tr>
27
+ <th>gets</th>
28
+ <td><%= stats.gets %></td>
29
+
30
+ <th>sets</th>
31
+ <td><%= stats.sets %></td>
32
+
33
+ <th>deletes</th>
34
+ <td><%= stats.deletes %></td>
35
+
36
+ <th>get_multis</th>
37
+ <td><%= stats.get_multis %></td>
38
+ </tr>
39
+ </table>
40
+
41
+ <% if stats.queries.any? %>
42
+ <h3>Breakdown</h3>
43
+ <table id="cache_breakdown" class="sortable">
44
+ <thead>
45
+ <tr>
46
+ <th>Time&nbsp;(ms)</th>
47
+ <th>Type</th>
48
+ <th>Parameters</th>
49
+ <th>Function</th>
50
+ <th>
51
+ <a href="/__insight__/delete_cache_list?<%= signed_params(stats.queries_to_param) %>" class="rack-insight_delete_cache">
52
+ Delete All
53
+ </a>
54
+ </th>
55
+ </tr>
56
+ </thead>
57
+ <tbody>
58
+ <% i = 1 %>
59
+ <% stats.queries.each do |query| %>
60
+ <tr class="<%= i % 2 == 0 ? "even" : "odd" %>">
61
+ <td><%= query.display_time %></td>
62
+ <td><%= query.method %></td>
63
+ <td><%= query.display_keys %></td>
64
+ <td></td>
65
+ <td>
66
+ <a href="/__insight__/view_cache?<%= signed_params("key" => query.keys.first) %>" class="remote_call">View</a> |
67
+ <a href="/__insight__/delete_cache?<%= signed_params("key" => query.keys.first) %>" class="rack-insight_delete_cache">Delete</a>
68
+ </td>
69
+ </tr>
70
+ <% i += 1 %>
71
+ <% end %>
72
+ </tbody>
73
+ </table>
74
+ <% end %>
75
+
76
+ <script type="text/javascript" charset="utf-8">
77
+ jQuery(function () {
78
+ jQuery("#rack-insight .rack-insight_delete_cache").click(function (evt) {
79
+ jQuery.ajax({
80
+ url: this.href,
81
+ beforeSend: function() {
82
+ jQuery(evt.target).parent("td, th").addClass("rack-insight_spinner");
83
+ },
84
+ success: function () {
85
+ jQuery(evt.target).parent("td, th").removeClass("rack-insight_spinner");
86
+ jQuery(evt.target).replaceWith("Deleted");
87
+ }
88
+ });
89
+
90
+ return false;
91
+ });
92
+ });
93
+ </script>