lecter 0.1.6 → 0.2.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.
- checksums.yaml +4 -4
- data/.all-contributorsrc +26 -0
- data/.circleci/config.yml +86 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +16 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +25 -0
- data/CHANGELOG.md +20 -0
- data/CONTRIBUTING.md +35 -0
- data/Gemfile +3 -2
- data/Gemfile.lock +37 -69
- data/LICENSE.txt +1 -1
- data/README.md +162 -54
- data/Rakefile +5 -3
- data/app/controllers/lecter/diagnosis_controller.rb +28 -49
- data/app/views/layouts/lecter.html.erb +258 -0
- data/app/views/lecter/diagnosis/new.html.erb +61 -0
- data/app/views/lecter/diagnosis/show.html.erb +54 -0
- data/bin/console +1 -0
- data/bin/rails +2 -0
- data/config/locales/en.yml +4 -3
- data/config/locales/ru.yml +4 -3
- data/config/routes.rb +5 -1
- data/lecter.gemspec +9 -5
- data/lib/lecter/engine.rb +2 -0
- data/lib/lecter/formatter_headers.rb +26 -0
- data/lib/lecter/formatter_payload.rb +35 -0
- data/lib/lecter/html_generator.rb +66 -0
- data/lib/lecter/html_row.rb +42 -0
- data/lib/lecter/rack.rb +30 -36
- data/lib/lecter/railtie.rb +9 -0
- data/lib/lecter/requester.rb +63 -0
- data/lib/lecter/trace_point.rb +30 -0
- data/lib/lecter/version.rb +3 -1
- data/lib/lecter.rb +15 -11
- metadata +45 -19
- data/.travis.yml +0 -7
- data/app/assets/javascripts/lecter.js +0 -2
- data/app/assets/stylesheets/lecter.css +0 -261
- data/app/views/layouts/lecter.slim +0 -14
- data/app/views/lecter/diagnosis/new.erb +0 -20
- data/app/views/lecter/diagnosis/show.slim +0 -52
@@ -0,0 +1,54 @@
|
|
1
|
+
<%= content_for :title, t('.title') %>
|
2
|
+
|
3
|
+
<div class="container">
|
4
|
+
<h2><%= t('.executed_code') %></h2>
|
5
|
+
</div>
|
6
|
+
|
7
|
+
<div class="row py-3">
|
8
|
+
<div class="col-3 order-2" style="width: 280px;">
|
9
|
+
<div class="sticky-top">
|
10
|
+
<ul>
|
11
|
+
<% @file_listings.each_with_index do |file_listing, index| %>
|
12
|
+
<li>
|
13
|
+
<a href="#anchor<%= index %>" class="nav-link link-dark">
|
14
|
+
<%= raw(file_listing.file_path.split('/').last) %>
|
15
|
+
</a>
|
16
|
+
</li>
|
17
|
+
<% end %>
|
18
|
+
</ul>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<div class="col">
|
23
|
+
<div class="accordion" id="accordionExample">
|
24
|
+
<% @file_listings.each_with_index do |file_listing, index| %>
|
25
|
+
<div class="accordion-item">
|
26
|
+
<h2 class="accordion-header" id="headingOne">
|
27
|
+
<button class="accordion-button" type="button" data-bs-toggle="collapse"
|
28
|
+
data-bs-target="#collapse<%=index%>" aria-expanded="true" aria-controls="collapse<%=index%>"
|
29
|
+
id="anchor<%=index%>"
|
30
|
+
>
|
31
|
+
<%= file_listing.file_path.split('/').last %>
|
32
|
+
</button>
|
33
|
+
</h2>
|
34
|
+
<div id="collapse<%=index%>" class="accordion-collapse collapse show" aria-labelledby="heading<%=index%>>" data-bs-parent="#accordionExample">
|
35
|
+
<div class="accordion-body">
|
36
|
+
<pre>
|
37
|
+
<code><% file_listing.html_rows.each do |html_row| %><%= raw(html_row) %><% end %></code>
|
38
|
+
</pre>
|
39
|
+
</div>
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
<% end %>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
|
47
|
+
<script>
|
48
|
+
/*! highlight.js v9.15.8 | BSD3 License | git.io/hljslicense */
|
49
|
+
!function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(a){var f=[],u=Object.keys,N={},c={},n=/^(no-?highlight|plain|text)$/i,s=/\blang(?:uage)?-([\w-]+)\b/i,t=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,r={case_insensitive:"cI",lexemes:"l",contains:"c",keywords:"k",subLanguage:"sL",className:"cN",begin:"b",beginKeywords:"bK",end:"e",endsWithParent:"eW",illegal:"i",excludeBegin:"eB",excludeEnd:"eE",returnBegin:"rB",returnEnd:"rE",relevance:"r",variants:"v",IDENT_RE:"IR",UNDERSCORE_IDENT_RE:"UIR",NUMBER_RE:"NR",C_NUMBER_RE:"CNR",BINARY_NUMBER_RE:"BNR",RE_STARTERS_RE:"RSR",BACKSLASH_ESCAPE:"BE",APOS_STRING_MODE:"ASM",QUOTE_STRING_MODE:"QSM",PHRASAL_WORDS_MODE:"PWM",C_LINE_COMMENT_MODE:"CLCM",C_BLOCK_COMMENT_MODE:"CBCM",HASH_COMMENT_MODE:"HCM",NUMBER_MODE:"NM",C_NUMBER_MODE:"CNM",BINARY_NUMBER_MODE:"BNM",CSS_NUMBER_MODE:"CSSNM",REGEXP_MODE:"RM",TITLE_MODE:"TM",UNDERSCORE_TITLE_MODE:"UTM",COMMENT:"C",beginRe:"bR",endRe:"eR",illegalRe:"iR",lexemesRe:"lR",terminators:"t",terminator_end:"tE"},b="</span>",h={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};function _(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function E(e){return e.nodeName.toLowerCase()}function v(e,n){var t=e&&e.exec(n);return t&&0===t.index}function l(e){return n.test(e)}function g(e){var n,t={},r=Array.prototype.slice.call(arguments,1);for(n in e)t[n]=e[n];return r.forEach(function(e){for(n in e)t[n]=e[n]}),t}function R(e){var a=[];return function e(n,t){for(var r=n.firstChild;r;r=r.nextSibling)3===r.nodeType?t+=r.nodeValue.length:1===r.nodeType&&(a.push({event:"start",offset:t,node:r}),t=e(r,t),E(r).match(/br|hr|img|input/)||a.push({event:"stop",offset:t,node:r}));return t}(e,0),a}function i(e){if(r&&!e.langApiRestored){for(var n in e.langApiRestored=!0,r)e[n]&&(e[r[n]]=e[n]);(e.c||[]).concat(e.v||[]).forEach(i)}}function m(o){function s(e){return e&&e.source||e}function c(e,n){return new RegExp(s(e),"m"+(o.cI?"i":"")+(n?"g":""))}!function n(t,e){if(!t.compiled){if(t.compiled=!0,t.k=t.k||t.bK,t.k){function r(t,e){o.cI&&(e=e.toLowerCase()),e.split(" ").forEach(function(e){var n=e.split("|");a[n[0]]=[t,n[1]?Number(n[1]):1]})}var a={};"string"==typeof t.k?r("keyword",t.k):u(t.k).forEach(function(e){r(e,t.k[e])}),t.k=a}t.lR=c(t.l||/\w+/,!0),e&&(t.bK&&(t.b="\\b("+t.bK.split(" ").join("|")+")\\b"),t.b||(t.b=/\B|\b/),t.bR=c(t.b),t.endSameAsBegin&&(t.e=t.b),t.e||t.eW||(t.e=/\B|\b/),t.e&&(t.eR=c(t.e)),t.tE=s(t.e)||"",t.eW&&e.tE&&(t.tE+=(t.e?"|":"")+e.tE)),t.i&&(t.iR=c(t.i)),null==t.r&&(t.r=1),t.c||(t.c=[]),t.c=Array.prototype.concat.apply([],t.c.map(function(e){return function(n){return n.v&&!n.cached_variants&&(n.cached_variants=n.v.map(function(e){return g(n,{v:null},e)})),n.cached_variants||n.eW&&[g(n)]||[n]}("self"===e?t:e)})),t.c.forEach(function(e){n(e,t)}),t.starts&&n(t.starts,e);var i=t.c.map(function(e){return e.bK?"\\.?(?:"+e.b+")\\.?":e.b}).concat([t.tE,t.i]).map(s).filter(Boolean);t.t=i.length?c(function(e,n){for(var t=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,a="",i=0;i<e.length;i++){var o=r,c=s(e[i]);for(0<i&&(a+=n);0<c.length;){var u=t.exec(c);if(null==u){a+=c;break}a+=c.substring(0,u.index),c=c.substring(u.index+u[0].length),"\\"==u[0][0]&&u[1]?a+="\\"+String(Number(u[1])+o):(a+=u[0],"("==u[0]&&r++)}}return a}(i,"|"),!0):{exec:function(){return null}}}}(o)}function C(e,n,i,t){function c(e,n,t,r){var a='<span class="'+(r?"":h.classPrefix);return e?(a+=e+'">')+n+(t?"":b):n}function o(){E+=null!=l.sL?function(){var e="string"==typeof l.sL;if(e&&!N[l.sL])return _(g);var n=e?C(l.sL,g,!0,f[l.sL]):O(g,l.sL.length?l.sL:void 0);return 0<l.r&&(R+=n.r),e&&(f[l.sL]=n.top),c(n.language,n.value,!1,!0)}():function(){var e,n,t,r,a,i,o;if(!l.k)return _(g);for(r="",n=0,l.lR.lastIndex=0,t=l.lR.exec(g);t;)r+=_(g.substring(n,t.index)),a=l,i=t,void 0,o=s.cI?i[0].toLowerCase():i[0],(e=a.k.hasOwnProperty(o)&&a.k[o])?(R+=e[1],r+=c(e[0],_(t[0]))):r+=_(t[0]),n=l.lR.lastIndex,t=l.lR.exec(g);return r+_(g.substr(n))}(),g=""}function u(e){E+=e.cN?c(e.cN,"",!0):"",l=Object.create(e,{parent:{value:l}})}function r(e,n){if(g+=e,null==n)return o(),0;var t=function(e,n){var t,r,a;for(t=0,r=n.c.length;t<r;t++)if(v(n.c[t].bR,e))return n.c[t].endSameAsBegin&&(n.c[t].eR=(a=n.c[t].bR.exec(e)[0],new RegExp(a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&"),"m"))),n.c[t]}(n,l);if(t)return t.skip?g+=n:(t.eB&&(g+=n),o(),t.rB||t.eB||(g=n)),u(t),t.rB?0:n.length;var r=function e(n,t){if(v(n.eR,t)){for(;n.endsParent&&n.parent;)n=n.parent;return n}if(n.eW)return e(n.parent,t)}(l,n);if(r){var a=l;for(a.skip?g+=n:(a.rE||a.eE||(g+=n),o(),a.eE&&(g=n));l.cN&&(E+=b),l.skip||l.sL||(R+=l.r),(l=l.parent)!==r.parent;);return r.starts&&(r.endSameAsBegin&&(r.starts.eR=r.eR),u(r.starts)),a.rE?0:n.length}if(function(e,n){return!i&&v(n.iR,e)}(n,l))throw new Error('Illegal lexeme "'+n+'" for mode "'+(l.cN||"<unnamed>")+'"');return g+=n,n.length||1}var s=B(e);if(!s)throw new Error('Unknown language: "'+e+'"');m(s);var a,l=t||s,f={},E="";for(a=l;a!==s;a=a.parent)a.cN&&(E=c(a.cN,"",!0)+E);var g="",R=0;try{for(var d,p,M=0;l.t.lastIndex=M,d=l.t.exec(n);)p=r(n.substring(M,d.index),d[0]),M=d.index+p;for(r(n.substr(M)),a=l;a.parent;a=a.parent)a.cN&&(E+=b);return{r:R,value:E,language:e,top:l}}catch(e){if(e.message&&-1!==e.message.indexOf("Illegal"))return{r:0,value:_(n)};throw e}}function O(t,e){e=e||h.languages||u(N);var r={r:0,value:_(t)},a=r;return e.filter(B).filter(M).forEach(function(e){var n=C(e,t,!1);n.language=e,n.r>a.r&&(a=n),n.r>r.r&&(a=r,r=n)}),a.language&&(r.second_best=a),r}function d(e){return h.tabReplace||h.useBR?e.replace(t,function(e,n){return h.useBR&&"\n"===e?"<br>":h.tabReplace?n.replace(/\t/g,h.tabReplace):""}):e}function o(e){var n,t,r,a,i,o=function(e){var n,t,r,a,i=e.className+" ";if(i+=e.parentNode?e.parentNode.className:"",t=s.exec(i))return B(t[1])?t[1]:"no-highlight";for(n=0,r=(i=i.split(/\s+/)).length;n<r;n++)if(l(a=i[n])||B(a))return a}(e);l(o)||(h.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div")).innerHTML=e.innerHTML.replace(/\n/g,"").replace(/<br[ \/]*>/g,"\n"):n=e,i=n.textContent,r=o?C(o,i,!0):O(i),(t=R(n)).length&&((a=document.createElementNS("http://www.w3.org/1999/xhtml","div")).innerHTML=r.value,r.value=function(e,n,t){var r=0,a="",i=[];function o(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset<n[0].offset?e:n:"start"===n[0].event?e:n:e.length?e:n}function c(e){a+="<"+E(e)+f.map.call(e.attributes,function(e){return" "+e.nodeName+'="'+_(e.value).replace('"',""")+'"'}).join("")+">"}function u(e){a+="</"+E(e)+">"}function s(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var l=o();if(a+=_(t.substring(r,l[0].offset)),r=l[0].offset,l===e){for(i.reverse().forEach(u);s(l.splice(0,1)[0]),(l=o())===e&&l.length&&l[0].offset===r;);i.reverse().forEach(c)}else"start"===l[0].event?i.push(l[0].node):i.pop(),s(l.splice(0,1)[0])}return a+_(t.substr(r))}(t,R(a),i)),r.value=d(r.value),e.innerHTML=r.value,e.className=function(e,n,t){var r=n?c[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}(e.className,o,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function p(){if(!p.called){p.called=!0;var e=document.querySelectorAll("pre code");f.forEach.call(e,o)}}function B(e){return e=(e||"").toLowerCase(),N[e]||N[c[e]]}function M(e){var n=B(e);return n&&!n.disableAutodetect}return a.highlight=C,a.highlightAuto=O,a.fixMarkup=d,a.highlightBlock=o,a.configure=function(e){h=g(h,e)},a.initHighlighting=p,a.initHighlightingOnLoad=function(){addEventListener("DOMContentLoaded",p,!1),addEventListener("load",p,!1)},a.registerLanguage=function(n,e){var t=N[n]=e(a);i(t),t.aliases&&t.aliases.forEach(function(e){c[e]=n})},a.listLanguages=function(){return u(N)},a.getLanguage=B,a.autoDetection=M,a.inherit=g,a.IR=a.IDENT_RE="[a-zA-Z]\\w*",a.UIR=a.UNDERSCORE_IDENT_RE="[a-zA-Z_]\\w*",a.NR=a.NUMBER_RE="\\b\\d+(\\.\\d+)?",a.CNR=a.C_NUMBER_RE="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",a.BNR=a.BINARY_NUMBER_RE="\\b(0b[01]+)",a.RSR=a.RE_STARTERS_RE="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",a.BE=a.BACKSLASH_ESCAPE={b:"\\\\[\\s\\S]",r:0},a.ASM=a.APOS_STRING_MODE={cN:"string",b:"'",e:"'",i:"\\n",c:[a.BE]},a.QSM=a.QUOTE_STRING_MODE={cN:"string",b:'"',e:'"',i:"\\n",c:[a.BE]},a.PWM=a.PHRASAL_WORDS_MODE={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},a.C=a.COMMENT=function(e,n,t){var r=a.inherit({cN:"comment",b:e,e:n,c:[]},t||{});return r.c.push(a.PWM),r.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),r},a.CLCM=a.C_LINE_COMMENT_MODE=a.C("//","$"),a.CBCM=a.C_BLOCK_COMMENT_MODE=a.C("/\\*","\\*/"),a.HCM=a.HASH_COMMENT_MODE=a.C("#","$"),a.NM=a.NUMBER_MODE={cN:"number",b:a.NR,r:0},a.CNM=a.C_NUMBER_MODE={cN:"number",b:a.CNR,r:0},a.BNM=a.BINARY_NUMBER_MODE={cN:"number",b:a.BNR,r:0},a.CSSNM=a.CSS_NUMBER_MODE={cN:"number",b:a.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},a.RM=a.REGEXP_MODE={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[a.BE,{b:/\[/,e:/\]/,r:0,c:[a.BE]}]},a.TM=a.TITLE_MODE={cN:"title",b:a.IR,r:0},a.UTM=a.UNDERSCORE_TITLE_MODE={cN:"title",b:a.UIR,r:0},a.METHOD_GUARD={b:"\\.\\s*"+a.UIR,r:0},a});hljs.registerLanguage("ruby",function(e){var b="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",r={keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",literal:"true false nil"},c={cN:"doctag",b:"@[A-Za-z]+"},a={b:"#<",e:">"},s=[e.C("#","$",{c:[c]}),e.C("^\\=begin","^\\=end",{c:[c],r:10}),e.C("^__END__","\\n$")],n={cN:"subst",b:"#\\{",e:"}",k:r},t={cN:"string",c:[e.BE,n],v:[{b:/'/,e:/'/},{b:/"/,e:/"/},{b:/`/,e:/`/},{b:"%[qQwWx]?\\(",e:"\\)"},{b:"%[qQwWx]?\\[",e:"\\]"},{b:"%[qQwWx]?{",e:"}"},{b:"%[qQwWx]?<",e:">"},{b:"%[qQwWx]?/",e:"/"},{b:"%[qQwWx]?%",e:"%"},{b:"%[qQwWx]?-",e:"-"},{b:"%[qQwWx]?\\|",e:"\\|"},{b:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{b:/<<(-?)\w+$/,e:/^\s*\w+$/}]},i={cN:"params",b:"\\(",e:"\\)",endsParent:!0,k:r},d=[t,a,{cN:"class",bK:"class module",e:"$|;",i:/=/,c:[e.inherit(e.TM,{b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{b:"<\\s*",c:[{b:"("+e.IR+"::)?"+e.IR}]}].concat(s)},{cN:"function",bK:"def",e:"$|;",c:[e.inherit(e.TM,{b:b}),i].concat(s)},{b:e.IR+"::"},{cN:"symbol",b:e.UIR+"(\\!|\\?)?:",r:0},{cN:"symbol",b:":(?!\\s)",c:[t,{b:b}],r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{cN:"params",b:/\|/,e:/\|/,k:r},{b:"("+e.RSR+"|unless)\\s*",k:"unless",c:[a,{cN:"regexp",c:[e.BE,n],i:/\n/,v:[{b:"/",e:"/[a-z]*"},{b:"%r{",e:"}[a-z]*"},{b:"%r\\(",e:"\\)[a-z]*"},{b:"%r!",e:"![a-z]*"},{b:"%r\\[",e:"\\][a-z]*"}]}].concat(s),r:0}].concat(s);n.c=d;var l=[{b:/^\s*=>/,starts:{e:"$",c:i.c=d}},{cN:"meta",b:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+>|(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>)",starts:{e:"$",c:d}}];return{aliases:["rb","gemspec","podspec","thor","irb"],k:r,i:/\/\*/,c:s.concat(l).concat(d)}});
|
50
|
+
|
51
|
+
$(document).ready(function(){
|
52
|
+
hljs.initHighlighting();
|
53
|
+
})
|
54
|
+
</script>
|
data/bin/console
CHANGED
data/bin/rails
CHANGED
data/config/locales/en.yml
CHANGED
@@ -6,10 +6,11 @@ en:
|
|
6
6
|
diagnosis:
|
7
7
|
show:
|
8
8
|
title: 'Diagnosis results'
|
9
|
-
|
9
|
+
executed_code: 'Executed code:'
|
10
10
|
new:
|
11
11
|
title: 'New diagnosis'
|
12
12
|
endpoint: 'Endpoint'
|
13
|
-
|
13
|
+
body_raw: 'Body (raw)'
|
14
14
|
method: 'HTTP method'
|
15
|
-
|
15
|
+
send: 'Send request'
|
16
|
+
headers: 'Headers'
|
data/config/locales/ru.yml
CHANGED
@@ -6,10 +6,11 @@ ru:
|
|
6
6
|
diagnosis:
|
7
7
|
show:
|
8
8
|
title: 'Диагноз'
|
9
|
-
|
9
|
+
executed_code: 'Используемый код:'
|
10
10
|
new:
|
11
11
|
title: 'Новый диагноз'
|
12
12
|
endpoint: 'Абсолютный путь'
|
13
|
-
|
13
|
+
body_raw: 'Тело запроса'
|
14
14
|
method: 'HTTP метод'
|
15
|
-
|
15
|
+
send: 'Послать запрос'
|
16
|
+
headers: 'Заголовки'
|
data/config/routes.rb
CHANGED
data/lecter.gemspec
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
lib = File.expand_path('lib', __dir__)
|
2
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
5
|
require 'lecter/version'
|
@@ -8,8 +10,9 @@ Gem::Specification.new do |spec|
|
|
8
10
|
spec.authors = ['Neodelf']
|
9
11
|
spec.email = ['neodelf@gmail.com']
|
10
12
|
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
13
|
+
spec.summary = 'Show executable code by request.'
|
14
|
+
spec.description = 'The main purpose of that gem is helping developers to understand which '\
|
15
|
+
'code executes by request.<br>'
|
13
16
|
spec.homepage = 'https://github.com/neodelf/lecter'
|
14
17
|
spec.license = 'MIT'
|
15
18
|
|
@@ -21,7 +24,7 @@ Gem::Specification.new do |spec|
|
|
21
24
|
|
22
25
|
# Specify which files should be added to the gem when it is released.
|
23
26
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
24
|
-
spec.files
|
27
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
25
28
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
29
|
end
|
27
30
|
spec.bindir = 'exe'
|
@@ -29,9 +32,10 @@ Gem::Specification.new do |spec|
|
|
29
32
|
spec.require_paths = ['lib']
|
30
33
|
|
31
34
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
32
|
-
spec.add_development_dependency 'rake', '~>
|
35
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
33
36
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
37
|
+
spec.add_development_dependency 'rubocop'
|
34
38
|
|
35
|
-
spec.add_runtime_dependency 'slim-rails'
|
36
39
|
spec.add_runtime_dependency 'rest-client'
|
40
|
+
spec.add_runtime_dependency 'simplecov', '0.17.1'
|
37
41
|
end
|
data/lib/lecter/engine.rb
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lecter
|
4
|
+
class FormatterHeaders
|
5
|
+
WRONG_HEADERS_MSG = 'Wrong headers'
|
6
|
+
attr_reader :result, :error_message
|
7
|
+
|
8
|
+
def initialize(headers)
|
9
|
+
@dirty_headers = headers
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
@result = dirty_headers
|
14
|
+
.split(',')
|
15
|
+
.map { |header_with_value| header_with_value.split('=') }
|
16
|
+
.to_h
|
17
|
+
rescue StandardError
|
18
|
+
@error_message = WRONG_HEADERS_MSG
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_accessor :dirty_headers
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Lecter
|
6
|
+
class FormatterPayload
|
7
|
+
WRONG_PARAMETERS_MSG = 'Wrong parameters'
|
8
|
+
attr_reader :result, :error_message
|
9
|
+
|
10
|
+
def initialize(payload)
|
11
|
+
@dirty_payload = payload
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
@result = json_parse(dirty_payload).merge(lecter_enabled_parameter)
|
16
|
+
rescue JSON::ParserError
|
17
|
+
@error_message = WRONG_PARAMETERS_MSG
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_accessor :dirty_payload
|
24
|
+
|
25
|
+
def json_parse(string)
|
26
|
+
string = '{' + string + '}' unless string.match(/\A{.*}\z/)
|
27
|
+
string.gsub!('=>', ':')&.gsub!(/(“|”)/, '"')
|
28
|
+
JSON.parse(string)
|
29
|
+
end
|
30
|
+
|
31
|
+
def lecter_enabled_parameter
|
32
|
+
{ lecter_enabled: true }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lecter
|
4
|
+
class HtmlGenerator
|
5
|
+
COUNT_LINES_AROUND_RUNNING_ROW = 5
|
6
|
+
ELLIPSIS = '...'
|
7
|
+
NEW_LINE = "\n"
|
8
|
+
|
9
|
+
def initialize(data)
|
10
|
+
@data = data
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
@data.each.map do |item|
|
15
|
+
@file_path = item.keys.first
|
16
|
+
@executable_row_numbers = item.values.flatten
|
17
|
+
previous_row_is_empty = false
|
18
|
+
|
19
|
+
html_rows = file_context.each_with_index.map do |file_row, file_row_index|
|
20
|
+
@file_row_index = file_row_index
|
21
|
+
row_executable = executable_row_numbers.include?(file_row_index + 1)
|
22
|
+
|
23
|
+
if row_executable || file_row_in_showing_range?(file_row_index)
|
24
|
+
previous_row_is_empty = false
|
25
|
+
Lecter::HtmlRow.new(
|
26
|
+
file_row,
|
27
|
+
file_row_index + 1,
|
28
|
+
row_executable,
|
29
|
+
executable_row_numbers
|
30
|
+
).create
|
31
|
+
elsif !previous_row_is_empty
|
32
|
+
previous_row_is_empty = true
|
33
|
+
ELLIPSIS + NEW_LINE
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
FileListing.new(file_path, html_rows)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_accessor :executable_row_numbers, :file_row_index, :file_path
|
44
|
+
|
45
|
+
def file_row_in_showing_range?(_index)
|
46
|
+
executable_row_numbers.reduce(false) do |memo, row_number|
|
47
|
+
memo ||
|
48
|
+
(row_number - COUNT_LINES_AROUND_RUNNING_ROW - 1..
|
49
|
+
row_number + COUNT_LINES_AROUND_RUNNING_ROW - 1).include?(file_row_index)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def file_context
|
54
|
+
File.open(file_path, 'r').read.split(NEW_LINE)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class FileListing
|
59
|
+
attr_reader :file_path, :html_rows
|
60
|
+
|
61
|
+
def initialize(file_path, html_rows)
|
62
|
+
@file_path = file_path
|
63
|
+
@html_rows = html_rows
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lecter
|
4
|
+
class HtmlRow
|
5
|
+
ARROW = '-> '
|
6
|
+
BACKGROUND_INCLUDED_ROW = '#4a4a4a'
|
7
|
+
NEW_LINE_SYMBOL = "\n"
|
8
|
+
|
9
|
+
def initialize(row, row_number, row_executable, order_of_executed_lines)
|
10
|
+
@row = row
|
11
|
+
@row_number = row_number
|
12
|
+
@row_executable = row_executable
|
13
|
+
@order_of_executed_lines = order_of_executed_lines
|
14
|
+
end
|
15
|
+
|
16
|
+
def create
|
17
|
+
"<div style='#{style}'><code>#{html_row}</code></div>"
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :row, :row_number, :row_executable, :order_of_executed_lines
|
23
|
+
|
24
|
+
def html_row
|
25
|
+
[row_number, row, row_calling_order_number].join(' ') + NEW_LINE_SYMBOL
|
26
|
+
end
|
27
|
+
|
28
|
+
def row_calling_order_number
|
29
|
+
return unless row_executable
|
30
|
+
|
31
|
+
ARROW + order_of_executed_lines
|
32
|
+
.each_with_index
|
33
|
+
.select { |_, index| order_of_executed_lines[index] == row_number }
|
34
|
+
.map { |_, index| index + 1 }
|
35
|
+
.join(', ')
|
36
|
+
end
|
37
|
+
|
38
|
+
def style
|
39
|
+
row_executable ? "background-color: #{BACKGROUND_INCLUDED_ROW};" : nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/lecter/rack.rb
CHANGED
@@ -1,45 +1,39 @@
|
|
1
|
-
|
2
|
-
def initialize(app)
|
3
|
-
@app = app
|
4
|
-
end
|
1
|
+
# frozen_string_literal: true
|
5
2
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
if tp.path &&
|
13
|
-
!tp.path.include?('/app/views') &&
|
14
|
-
!tp.path.include?('/app/helpers') &&
|
15
|
-
tp.path.include?(Rails.root.to_s) &&
|
16
|
-
tp.method_id != :method_added &&
|
17
|
-
tp.defined_class != Module &&
|
18
|
-
tp.defined_class != Class &&
|
19
|
-
tp.defined_class != String &&
|
20
|
-
tp.defined_class != Kernel &&
|
21
|
-
tp.defined_class != NilClass
|
3
|
+
module Lecter
|
4
|
+
class Rack
|
5
|
+
def initialize(app)
|
6
|
+
@app = app
|
7
|
+
@tp = Lecter::TracePoint.new.build
|
8
|
+
end
|
22
9
|
|
23
|
-
|
24
|
-
|
10
|
+
def call(env)
|
11
|
+
if ::Rack::Request.new(env).params['lecter_enabled']
|
12
|
+
thread = Thread.current
|
13
|
+
thread[:items] = ''
|
14
|
+
tp.enable
|
15
|
+
ActionController::Base.allow_forgery_protection = false
|
25
16
|
end
|
26
|
-
tp.enable
|
27
|
-
ActionController::Base.allow_forgery_protection = false
|
28
|
-
end
|
29
17
|
|
30
|
-
|
18
|
+
status, headers, response = @app.call(env)
|
31
19
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
20
|
+
if tp.enabled?
|
21
|
+
response = [status.to_s + thread[:items]]
|
22
|
+
status = 200
|
23
|
+
headers = {}
|
24
|
+
end
|
37
25
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
26
|
+
[status, headers, response]
|
27
|
+
ensure
|
28
|
+
if tp.enabled?
|
29
|
+
tp.disable
|
30
|
+
ActionController::Base.allow_forgery_protection = true
|
31
|
+
Thread.current[:items] = nil
|
32
|
+
end
|
43
33
|
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
attr_reader :tp
|
44
38
|
end
|
45
39
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lecter
|
4
|
+
class Requester
|
5
|
+
WRONG_URL_MSG = 'Wrong url'
|
6
|
+
|
7
|
+
attr_reader :lines, :error_message
|
8
|
+
|
9
|
+
def initialize(params)
|
10
|
+
@method = params[:method]
|
11
|
+
@url = params[:url]
|
12
|
+
@payload = params[:payload]
|
13
|
+
@lines = []
|
14
|
+
@headers = params[:headers]
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
return false unless response
|
19
|
+
|
20
|
+
prepare_lines
|
21
|
+
rescue URI::InvalidURIError
|
22
|
+
@error_message = WRONG_URL_MSG
|
23
|
+
false
|
24
|
+
rescue RestClient::ExceptionWithResponse => e
|
25
|
+
@error_message = e.message
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_accessor :method, :url, :payload, :headers
|
32
|
+
|
33
|
+
def prepare_lines
|
34
|
+
items.each do |item|
|
35
|
+
file, line_number = item.split(' ')
|
36
|
+
line_number = line_number.to_i
|
37
|
+
|
38
|
+
if line_belongs_to_last?(file)
|
39
|
+
lines.last[file] = lines.last[file] << line_number
|
40
|
+
else
|
41
|
+
lines << { file.to_s => [line_number] }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def response
|
47
|
+
@response ||= RestClient::Request.execute(
|
48
|
+
method: method,
|
49
|
+
url: url,
|
50
|
+
payload: payload,
|
51
|
+
headers: headers
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
def items
|
56
|
+
@items ||= response.body[3..-1].split(';')
|
57
|
+
end
|
58
|
+
|
59
|
+
def line_belongs_to_last?(file)
|
60
|
+
lines.last.is_a?(Hash) && lines.last.keys.first.to_s == file
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lecter
|
4
|
+
class TracePoint
|
5
|
+
def build
|
6
|
+
tp = ::TracePoint.new(:line, :class, :call, :c_call, :return) do |trace_point|
|
7
|
+
if trace_point.path&.exclude?('/app/views') &&
|
8
|
+
trace_point.path&.exclude?('/app/helpers') &&
|
9
|
+
trace_point.path&.include?(Rails.root.to_s) &&
|
10
|
+
trace_point.method_id != :method_added &&
|
11
|
+
trace_point.defined_class != Module &&
|
12
|
+
trace_point.defined_class != Class &&
|
13
|
+
trace_point.defined_class != String &&
|
14
|
+
trace_point.defined_class != Kernel &&
|
15
|
+
trace_point.defined_class != NilClass
|
16
|
+
|
17
|
+
Thread.current[:items] += [
|
18
|
+
trace_point.path,
|
19
|
+
trace_point.lineno,
|
20
|
+
trace_point.defined_class,
|
21
|
+
trace_point.method_id,
|
22
|
+
trace_point.event
|
23
|
+
].join(' ') + ';'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
tp
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/lecter/version.rb
CHANGED
data/lib/lecter.rb
CHANGED
@@ -1,15 +1,19 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'lecter/engine' if defined?(Rails::Engine)
|
4
|
+
require 'lecter/formatter_payload'
|
5
|
+
require 'lecter/html_generator'
|
6
|
+
require 'lecter/html_row'
|
3
7
|
require 'lecter/rack'
|
8
|
+
require 'lecter/railtie' if defined?(Rails::Railtie)
|
9
|
+
require 'lecter/requester'
|
10
|
+
require 'lecter/version'
|
11
|
+
require 'lecter/trace_point'
|
12
|
+
require 'lecter/formatter_headers'
|
4
13
|
|
5
|
-
|
6
|
-
# autoload :Rack, 'lecter/rack'
|
14
|
+
require 'rest-client'
|
7
15
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
app.middleware.use Lecter::Rack
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
16
|
+
module Lecter
|
17
|
+
AVAILABLE_METHODS = %w[GET POST PUT PATCH DELETE].freeze
|
18
|
+
DEFAULT_METHOD = 'GET'
|
15
19
|
end
|