api_docs_engine 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/app/assets/javascripts/docs.js.coffee +42 -0
  3. data/app/assets/javascripts/highlight.js +1 -0
  4. data/app/assets/stylesheets/docs.css.scss +127 -0
  5. data/app/assets/stylesheets/highlight/styles/github.css +129 -0
  6. data/app/controllers/concerns/docs/manage_resource.rb +98 -0
  7. data/app/controllers/docs/application_controller.rb +24 -0
  8. data/app/controllers/docs/markdown_controller.rb +8 -0
  9. data/app/controllers/docs/missing_docs_controller.rb +7 -0
  10. data/app/controllers/docs/page_categories_controller.rb +27 -0
  11. data/app/controllers/docs/pages_controller.rb +56 -0
  12. data/app/helpers/docs/application_helper.rb +47 -0
  13. data/app/models/docs/missing_collection.rb +11 -0
  14. data/app/models/docs/page.rb +64 -0
  15. data/app/models/docs/page_category.rb +30 -0
  16. data/app/models/docs/routes_collection.rb +71 -0
  17. data/app/views/docs/application/edit.html.slim +2 -0
  18. data/app/views/docs/application/new.html.slim +2 -0
  19. data/app/views/docs/layouts/docs.html.slim +24 -0
  20. data/app/views/docs/markdown/create.json.erb +3 -0
  21. data/app/views/docs/missing_docs/index.html.slim +9 -0
  22. data/app/views/docs/page_categories/_form.html.slim +19 -0
  23. data/app/views/docs/pages/_form.html.slim +35 -0
  24. data/app/views/docs/pages/_page.html.slim +24 -0
  25. data/app/views/docs/pages/index.html.slim +34 -0
  26. data/config/initializers/formtastic.rb +1 -0
  27. data/config/routes.rb +5 -0
  28. data/lib/api_docs_engine/docs.rb +50 -0
  29. data/lib/api_docs_engine.rb +2 -0
  30. data/lib/bootstrap/active_link_to.rb +135 -0
  31. data/lib/bootstrap/markdown_renderer.rb +10 -0
  32. data/lib/docs/core_ext.rb +39 -0
  33. data/lib/docs/engine.rb +9 -0
  34. data/lib/generators/active_record/api_docs_engine_generator.rb +16 -0
  35. data/lib/generators/active_record/templates/migration.rb +27 -0
  36. data/lib/generators/api_docs_engine/api_docs_engine_generator.rb +12 -0
  37. data/lib/generators/api_docs_engine/install_generator.rb +16 -0
  38. data/lib/generators/api_docs_engine/orm_helpers.rb +10 -0
  39. data/lib/generators/templates/api_docs_engine.rb +11 -0
  40. metadata +284 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 704b4383489a584b6492682992016927e54e649e
4
+ data.tar.gz: 3d70c586a9bcf1976df23f9edb083e84a429c98b
5
+ SHA512:
6
+ metadata.gz: 85efb2b2da337b4b4b01760dae68c6d5843b9aa8de3e1d3c85e42b58ccf587e626e6ae82f5d895f3ec1afc12465f34596b00e62ade08da3e22a2b52292c4be09
7
+ data.tar.gz: 74ff7960fed4ecc13031a4cbe3b4d78a0c666be8bc74ed358c8ed02da029ab1dfe40e42a4cff08f3eba51e777fe576e441e35f1820369137893f696e24991bb9
@@ -0,0 +1,42 @@
1
+ #= require jquery
2
+ #= require jquery_ujs
3
+ #= require bootstrap
4
+ #= require bootstrap/affix
5
+ #= require turbolinks
6
+ #= require highlight
7
+
8
+ unless $.fn.doOnce
9
+ $.fn.doOnce = (fn) -> fn.call(@) if @length > 0
10
+
11
+ $.fn.add_edit_link = (classnames) ->
12
+ @toggleClass 'col-sm-10', on
13
+ @after -> """<a href="#{@dataset.editPath}" class="col-sm-2 #{classnames}"><i class="fa fa-edit"></i></a>"""
14
+
15
+ $.fn.highlight = (sel) ->
16
+ @find(sel).each (i, block) -> hljs.highlightBlock(block)
17
+
18
+ process_request = (source, callback) ->
19
+ $.post '/docs/markdown.json', source: source, callback, 'json'
20
+
21
+ $.fn.markdownize = (target) ->
22
+ @on 'change', -> process_request @value, (json) ->
23
+ ($ target).doOnce ->
24
+ @html json.result
25
+ @highlight 'pre code, pre samp'
26
+
27
+ jQuery ->
28
+ page_loaded = ->
29
+ ($ document).highlight('pre code, pre samp')
30
+
31
+ ($ "#left-sidebar [data-edit-path]").each ->
32
+ ($ @).add_edit_link "sidebar-edit-link text-muted"
33
+
34
+ ($ '.preview-markdown').each ->
35
+ ($ @dataset.source).markdownize(this).change()
36
+
37
+ ($ '.navbar-spyscroll').doOnce ->
38
+ ($ 'body').scrollspy target: '.navbar-spyscroll'
39
+ @affix()
40
+
41
+ ($ document).on 'page:load', page_loaded
42
+ do page_loaded
@@ -0,0 +1 @@
1
+ var hljs=new function(){function j(v){return v.replace(/&/gm,"&amp;").replace(/</gm,"&lt;").replace(/>/gm,"&gt;")}function t(v){return v.nodeName.toLowerCase()}function h(w,x){var v=w&&w.exec(x);return v&&v.index==0}function r(w){var v=(w.className+" "+(w.parentNode?w.parentNode.className:"")).split(/\s+/);v=v.map(function(x){return x.replace(/^lang(uage)?-/,"")});return v.filter(function(x){return i(x)||/no(-?)highlight/.test(x)})[0]}function o(x,y){var v={};for(var w in x){v[w]=x[w]}if(y){for(var w in y){v[w]=y[w]}}return v}function u(x){var v=[];(function w(y,z){for(var A=y.firstChild;A;A=A.nextSibling){if(A.nodeType==3){z+=A.nodeValue.length}else{if(A.nodeType==1){v.push({event:"start",offset:z,node:A});z=w(A,z);if(!t(A).match(/br|hr|img|input/)){v.push({event:"stop",offset:z,node:A})}}}}return z})(x,0);return v}function q(w,y,C){var x=0;var F="";var z=[];function B(){if(!w.length||!y.length){return w.length?w:y}if(w[0].offset!=y[0].offset){return(w[0].offset<y[0].offset)?w:y}return y[0].event=="start"?w:y}function A(H){function G(I){return" "+I.nodeName+'="'+j(I.value)+'"'}F+="<"+t(H)+Array.prototype.map.call(H.attributes,G).join("")+">"}function E(G){F+="</"+t(G)+">"}function v(G){(G.event=="start"?A:E)(G.node)}while(w.length||y.length){var D=B();F+=j(C.substr(x,D[0].offset-x));x=D[0].offset;if(D==w){z.reverse().forEach(E);do{v(D.splice(0,1)[0]);D=B()}while(D==w&&D.length&&D[0].offset==x);z.reverse().forEach(A)}else{if(D[0].event=="start"){z.push(D[0].node)}else{z.pop()}v(D.splice(0,1)[0])}}return F+j(C.substr(x))}function m(y){function v(z){return(z&&z.source)||z}function w(A,z){return RegExp(v(A),"m"+(y.cI?"i":"")+(z?"g":""))}function x(D,C){if(D.compiled){return}D.compiled=true;D.k=D.k||D.bK;if(D.k){var z={};var E=function(G,F){if(y.cI){F=F.toLowerCase()}F.split(" ").forEach(function(H){var I=H.split("|");z[I[0]]=[G,I[1]?Number(I[1]):1]})};if(typeof D.k=="string"){E("keyword",D.k)}else{Object.keys(D.k).forEach(function(F){E(F,D.k[F])})}D.k=z}D.lR=w(D.l||/\b[A-Za-z0-9_]+\b/,true);if(C){if(D.bK){D.b="\\b("+D.bK.split(" ").join("|")+")\\b"}if(!D.b){D.b=/\B|\b/}D.bR=w(D.b);if(!D.e&&!D.eW){D.e=/\B|\b/}if(D.e){D.eR=w(D.e)}D.tE=v(D.e)||"";if(D.eW&&C.tE){D.tE+=(D.e?"|":"")+C.tE}}if(D.i){D.iR=w(D.i)}if(D.r===undefined){D.r=1}if(!D.c){D.c=[]}var B=[];D.c.forEach(function(F){if(F.v){F.v.forEach(function(G){B.push(o(F,G))})}else{B.push(F=="self"?D:F)}});D.c=B;D.c.forEach(function(F){x(F,D)});if(D.starts){x(D.starts,C)}var A=D.c.map(function(F){return F.bK?"\\.?("+F.b+")\\.?":F.b}).concat([D.tE,D.i]).map(v).filter(Boolean);D.t=A.length?w(A.join("|"),true):{exec:function(F){return null}}}x(y)}function c(T,L,J,R){function v(V,W){for(var U=0;U<W.c.length;U++){if(h(W.c[U].bR,V)){return W.c[U]}}}function z(V,U){if(h(V.eR,U)){return V}if(V.eW){return z(V.parent,U)}}function A(U,V){return !J&&h(V.iR,U)}function E(W,U){var V=M.cI?U[0].toLowerCase():U[0];return W.k.hasOwnProperty(V)&&W.k[V]}function w(aa,Y,X,W){var U=W?"":b.classPrefix,V='<span class="'+U,Z=X?"":"</span>";V+=aa+'">';return V+Y+Z}function N(){if(!I.k){return j(C)}var U="";var X=0;I.lR.lastIndex=0;var V=I.lR.exec(C);while(V){U+=j(C.substr(X,V.index-X));var W=E(I,V);if(W){H+=W[1];U+=w(W[0],j(V[0]))}else{U+=j(V[0])}X=I.lR.lastIndex;V=I.lR.exec(C)}return U+j(C.substr(X))}function F(){if(I.sL&&!f[I.sL]){return j(C)}var U=I.sL?c(I.sL,C,true,S):e(C);if(I.r>0){H+=U.r}if(I.subLanguageMode=="continuous"){S=U.top}return w(U.language,U.value,false,true)}function Q(){return I.sL!==undefined?F():N()}function P(W,V){var U=W.cN?w(W.cN,"",true):"";if(W.rB){D+=U;C=""}else{if(W.eB){D+=j(V)+U;C=""}else{D+=U;C=V}}I=Object.create(W,{parent:{value:I}})}function G(U,Y){C+=U;if(Y===undefined){D+=Q();return 0}var W=v(Y,I);if(W){D+=Q();P(W,Y);return W.rB?0:Y.length}var X=z(I,Y);if(X){var V=I;if(!(V.rE||V.eE)){C+=Y}D+=Q();do{if(I.cN){D+="</span>"}H+=I.r;I=I.parent}while(I!=X.parent);if(V.eE){D+=j(Y)}C="";if(X.starts){P(X.starts,"")}return V.rE?0:Y.length}if(A(Y,I)){throw new Error('Illegal lexeme "'+Y+'" for mode "'+(I.cN||"<unnamed>")+'"')}C+=Y;return Y.length||1}var M=i(T);if(!M){throw new Error('Unknown language: "'+T+'"')}m(M);var I=R||M;var S;var D="";for(var K=I;K!=M;K=K.parent){if(K.cN){D=w(K.cN,"",true)+D}}var C="";var H=0;try{var B,y,x=0;while(true){I.t.lastIndex=x;B=I.t.exec(L);if(!B){break}y=G(L.substr(x,B.index-x),B[0]);x=B.index+y}G(L.substr(x));for(var K=I;K.parent;K=K.parent){if(K.cN){D+="</span>"}}return{r:H,value:D,language:T,top:I}}catch(O){if(O.message.indexOf("Illegal")!=-1){return{r:0,value:j(L)}}else{throw O}}}function e(y,x){x=x||b.languages||Object.keys(f);var v={r:0,value:j(y)};var w=v;x.forEach(function(z){if(!i(z)){return}var A=c(z,y,false);A.language=z;if(A.r>w.r){w=A}if(A.r>v.r){w=v;v=A}});if(w.language){v.second_best=w}return v}function g(v){if(b.tabReplace){v=v.replace(/^((<[^>]+>|\t)+)/gm,function(w,z,y,x){return z.replace(/\t/g,b.tabReplace)})}if(b.useBR){v=v.replace(/\n/g,"<br>")}return v}function p(A){var B=r(A);if(/no(-?)highlight/.test(B)){return}var y;if(b.useBR){y=document.createElementNS("http://www.w3.org/1999/xhtml","div");y.innerHTML=A.innerHTML.replace(/\n/g,"").replace(/<br[ \/]*>/g,"\n")}else{y=A}var z=y.textContent;var v=B?c(B,z,true):e(z);var x=u(y);if(x.length){var w=document.createElementNS("http://www.w3.org/1999/xhtml","div");w.innerHTML=v.value;v.value=q(x,u(w),z)}v.value=g(v.value);A.innerHTML=v.value;A.className+=" hljs "+(!B&&v.language||"");A.result={language:v.language,re:v.r};if(v.second_best){A.second_best={language:v.second_best.language,re:v.second_best.r}}}var b={classPrefix:"hljs-",tabReplace:null,useBR:false,languages:undefined};function s(v){b=o(b,v)}function l(){if(l.called){return}l.called=true;var v=document.querySelectorAll("pre code");Array.prototype.forEach.call(v,p)}function a(){addEventListener("DOMContentLoaded",l,false);addEventListener("load",l,false)}var f={};var n={};function d(v,x){var w=f[v]=x(this);if(w.aliases){w.aliases.forEach(function(y){n[y]=v})}}function k(){return Object.keys(f)}function i(v){return f[v]||f[n[v]]}this.highlight=c;this.highlightAuto=e;this.fixMarkup=g;this.highlightBlock=p;this.configure=s;this.initHighlighting=l;this.initHighlightingOnLoad=a;this.registerLanguage=d;this.listLanguages=k;this.getLanguage=i;this.inherit=o;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE]};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE]};this.PWM={b:/\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such)\b/};this.CLCM={cN:"comment",b:"//",e:"$",c:[this.PWM]};this.CBCM={cN:"comment",b:"/\\*",e:"\\*/",c:[this.PWM]};this.HCM={cN:"comment",b:"#",e:"$",c:[this.PWM]};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.CSSNM={cN:"number",b:this.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};this.RM={cN:"regexp",b:/\//,e:/\/[gim]*/,i:/\n/,c:[this.BE,{b:/\[/,e:/\]/,r:0,c:[this.BE]}]};this.TM={cN:"title",b:this.IR,r:0};this.UTM={cN:"title",b:this.UIR,r:0}}();hljs.registerLanguage("json",function(a){var e={literal:"true false null"};var d=[a.QSM,a.CNM];var c={cN:"value",e:",",eW:true,eE:true,c:d,k:e};var b={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:true,eE:true,c:[a.BE],i:"\\n",starts:c}],i:"\\S"};var f={b:"\\[",e:"\\]",c:[a.inherit(c,{cN:null})],i:"\\S"};d.splice(d.length,0,b,f);return{c:d,k:e,i:"\\S"}});
@@ -0,0 +1,127 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ */
13
+
14
+ @import "bootstrap";
15
+ @import "formtastic-bootstrap";
16
+ @import "font-awesome";
17
+ @import "highlight/styles/github";
18
+
19
+ .sidebar { display: none; }
20
+
21
+ @media (min-width: 768px) {
22
+ .sidebar {
23
+ position: fixed;
24
+ top: 0;
25
+ bottom: 0;
26
+ left: 0;
27
+ z-index: 1000;
28
+ display: block;
29
+ padding: 20px;
30
+ overflow-x: hidden;
31
+ overflow-y: auto;
32
+ background-color: #f5f5f5;
33
+ border-right: 1px solid $gray-lighter;
34
+ }
35
+ }
36
+
37
+ // Sidebar navigation
38
+ .nav-sidebar {
39
+ margin-right: -21px;
40
+ margin-bottom: 20px;
41
+ margin-left: -20px;
42
+ > li > a {
43
+ padding-right: 20px;
44
+ padding-left: 20px;
45
+ }
46
+ > li.active > a,
47
+ > li.active:hover > a {
48
+ color: $component-active-color;
49
+ background-color: $component-active-bg;
50
+ }
51
+ > li > a.sidebar-edit-link > i {
52
+ visibility: hidden;
53
+ }
54
+ > li:hover > a.sidebar-edit-link > i {
55
+ visibility: visible;
56
+ }
57
+ }
58
+
59
+ // Main content
60
+ .main { padding: 20px; }
61
+
62
+ @media (min-width: 768px) {
63
+ .main {
64
+ padding-right: 40px;
65
+ padding-left: 40px;
66
+ }
67
+ }
68
+
69
+ .main .page-header { margin-top: 0; }
70
+
71
+ .table-data {
72
+ td.col-actions ul.list-inline {
73
+ margin-bottom: 0;
74
+ }
75
+ .table-condensed {
76
+ font-size: 0.80em;
77
+ }
78
+ }
79
+
80
+ textarea.form-control {
81
+ font: 13px $font-family-monospace;
82
+ resize: vertical;
83
+
84
+ }
85
+
86
+ pre samp {
87
+ white-space: pre-wrap;
88
+ font-size: inherit;
89
+ color: inherit;
90
+ }
91
+
92
+ ul.nav.nav-right-spy {
93
+ font-size: 13px;
94
+ margin-bottom: 20px;
95
+
96
+ > li > a {
97
+ padding-top: 5px;
98
+ padding-bottom: 5px;
99
+ color: $gray-light;
100
+ }
101
+ > li.active > a,
102
+ > li > a:hover {
103
+ border-left: 2px solid #A13D26;
104
+ margin-left: -2px;
105
+ color: #A13D26;
106
+ background: transparent;
107
+ }
108
+ }
109
+
110
+ .panel-navbar {
111
+ min-height: 40px;
112
+ margin-bottom: 0px;
113
+ .navbar-nav > li > a {
114
+ padding-top: 10px;
115
+ padding-bottom: 10px;
116
+ }
117
+ }
118
+
119
+ .actions {
120
+ margin-top: 100px;
121
+ .action {
122
+ margin-top: 50px;
123
+ margin-bottom: 50px;
124
+ border-top: 1px solid $gray-lighter;
125
+ > h3 { margin-top: 10px; }
126
+ }
127
+ }
@@ -0,0 +1,129 @@
1
+ /*
2
+
3
+ github.com style (c) Vasily Polovnyov <vast@whiteants.net>
4
+
5
+ */
6
+ pre {
7
+ background-color: #f8f8f8;
8
+ }
9
+
10
+ .hljs {
11
+ display: block;
12
+ overflow-x: auto;
13
+ padding: 0.5em;
14
+ color: #333;
15
+ -webkit-text-size-adjust: none;
16
+ }
17
+
18
+ .hljs-comment,
19
+ .hljs-template_comment,
20
+ .diff .hljs-header,
21
+ .hljs-javadoc {
22
+ color: #998;
23
+ font-style: italic;
24
+ }
25
+
26
+ .hljs-keyword,
27
+ .css .rule .hljs-keyword,
28
+ .hljs-winutils,
29
+ .javascript .hljs-title,
30
+ .nginx .hljs-title,
31
+ .hljs-subst,
32
+ .hljs-request,
33
+ .hljs-status {
34
+ color: #333;
35
+ font-weight: bold;
36
+ }
37
+
38
+ .hljs-number,
39
+ .hljs-hexcolor,
40
+ .ruby .hljs-constant {
41
+ color: #008080;
42
+ }
43
+
44
+ .hljs-string,
45
+ .hljs-tag .hljs-value,
46
+ .hljs-phpdoc,
47
+ .hljs-dartdoc,
48
+ .tex .hljs-formula {
49
+ color: #d14;
50
+ }
51
+
52
+ .hljs-title,
53
+ .hljs-id,
54
+ .scss .hljs-preprocessor {
55
+ color: #900;
56
+ font-weight: bold;
57
+ }
58
+
59
+ .javascript .hljs-title,
60
+ .hljs-list .hljs-keyword,
61
+ .hljs-subst {
62
+ font-weight: normal;
63
+ }
64
+
65
+ .hljs-class .hljs-title,
66
+ .hljs-type,
67
+ .vhdl .hljs-literal,
68
+ .tex .hljs-command {
69
+ color: #458;
70
+ font-weight: bold;
71
+ }
72
+
73
+ .hljs-tag,
74
+ .hljs-tag .hljs-title,
75
+ .hljs-rules .hljs-property,
76
+ .django .hljs-tag .hljs-keyword {
77
+ color: #000080;
78
+ font-weight: normal;
79
+ }
80
+
81
+ .hljs-attribute,
82
+ .hljs-variable,
83
+ .lisp .hljs-body {
84
+ color: #008080;
85
+ }
86
+
87
+ .hljs-regexp {
88
+ color: #009926;
89
+ }
90
+
91
+ .hljs-symbol,
92
+ .ruby .hljs-symbol .hljs-string,
93
+ .lisp .hljs-keyword,
94
+ .clojure .hljs-keyword,
95
+ .scheme .hljs-keyword,
96
+ .tex .hljs-special,
97
+ .hljs-prompt {
98
+ color: #990073;
99
+ }
100
+
101
+ .hljs-built_in {
102
+ color: #0086b3;
103
+ }
104
+
105
+ .hljs-preprocessor,
106
+ .hljs-pragma,
107
+ .hljs-pi,
108
+ .hljs-doctype,
109
+ .hljs-shebang,
110
+ .hljs-cdata {
111
+ color: #999;
112
+ font-weight: bold;
113
+ }
114
+
115
+ .hljs-deletion {
116
+ background: #fdd;
117
+ }
118
+
119
+ .hljs-addition {
120
+ background: #dfd;
121
+ }
122
+
123
+ .diff .hljs-change {
124
+ background: #0086b3;
125
+ }
126
+
127
+ .hljs-chunk {
128
+ color: #aaa;
129
+ }
@@ -0,0 +1,98 @@
1
+ require "active_support/concern"
2
+
3
+ module Docs
4
+ module ManageResource
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ def collection
9
+ instance_variable_get collection_var_name
10
+ end
11
+
12
+ def resource
13
+ instance_variable_get resource_var_name
14
+ end
15
+
16
+ alias_method :single_resource, :resource
17
+ helper_method :collection, :resource, :single_resource
18
+ end
19
+
20
+ def show
21
+ end
22
+
23
+ def index
24
+ set_collection resource_class.all
25
+ end
26
+
27
+ def new
28
+ set_resource resource_class.new(resource_params)
29
+ end
30
+
31
+ def edit
32
+ resource.attributes = resource_params
33
+ end
34
+
35
+ def create(&block)
36
+ set_resource resource_class.new(permitted_params)
37
+ create!(&block)
38
+ end
39
+
40
+ def update(&block)
41
+ update!(&block)
42
+ end
43
+
44
+ def destroy(&block)
45
+ destroy!(&block)
46
+ end
47
+
48
+ private
49
+ def create!(&block)
50
+ if resource.save
51
+ yield_or_redirect_to_root! &block
52
+ else
53
+ render "new"
54
+ end
55
+ end
56
+
57
+ def update!(&block)
58
+ if resource.update_attributes(permitted_params)
59
+ yield_or_redirect_to_root! &block
60
+ else
61
+ render "edit"
62
+ end
63
+ end
64
+
65
+ def destroy!(&block)
66
+ resource.destroy
67
+ yield_or_redirect_to_root! &block
68
+ end
69
+
70
+ def yield_or_redirect_to_root!
71
+ if block_given?
72
+ yield
73
+ else
74
+ redirect_to root_path
75
+ end
76
+ end
77
+
78
+ def set_resource(new_resource)
79
+ instance_variable_set resource_var_name, new_resource
80
+ end
81
+
82
+ def set_collection(new_collection)
83
+ instance_variable_set collection_var_name, new_resource
84
+ end
85
+
86
+ def resource_var_name
87
+ @resource_var_name ||= "@#{controller_name.singularize}"
88
+ end
89
+
90
+ def collection_var_name
91
+ @collection_var_name ||= "@#{controller_name.pluralize}"
92
+ end
93
+
94
+ def resource_class
95
+ controller_name.pluralize.classify.constantize
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,24 @@
1
+ module Docs
2
+ class ApplicationController < ActionController::Base
3
+ if Docs.auth.is_a? Proc
4
+ Docs.auth.call(self)
5
+ end
6
+
7
+ protect_from_forgery
8
+ layout 'docs/layouts/docs'
9
+ before_filter :set_locale
10
+
11
+ private
12
+ def set_locale
13
+ I18n.locale = I18n.default_locale
14
+ end
15
+
16
+ def load_page
17
+ @page = Page.find params[:id]
18
+ end
19
+
20
+ def load_page_category
21
+ @page_category = PageCategory.friendly.find(params[:page_category_id].presence || params[:id])
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,8 @@
1
+ module Docs
2
+ class MarkdownController < ::Docs::ApplicationController
3
+ respond_to :json
4
+
5
+ def create
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ module Docs
2
+ class MissingDocsController < ::Docs::ApplicationController
3
+ def index
4
+ @missing_docs = MissingCollection.new.routes
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,27 @@
1
+ module Docs
2
+ class PageCategoriesController < ::Docs::ApplicationController
3
+ before_action :load_page_category, except: %i[index new create]
4
+ include ManageResource
5
+
6
+ def create
7
+ super { redirect_to [resource, :pages] }
8
+ end
9
+
10
+ def update
11
+ super { redirect_to [resource, :pages] }
12
+ end
13
+
14
+ private
15
+ def permitted_params
16
+ params.require(:page_category).permit(:slug, :title, :body)
17
+ end
18
+
19
+ def resource_params
20
+ params.fetch :page_category, {}
21
+ end
22
+
23
+ def resource_class
24
+ PageCategory
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,56 @@
1
+ module Docs
2
+ class PagesController < ::Docs::ApplicationController
3
+ before_action :load_page_category, only: %i[index]
4
+ before_action :load_page, only: %i[show edit update destroy]
5
+ include ManageResource
6
+
7
+ def index
8
+ @pages = @page_category.pages.order("via ASC, path ASC").sort_by &:weight
9
+ end
10
+
11
+ def new
12
+ @page = build_resource
13
+ @page.set_body_with_default_table
14
+ end
15
+
16
+ def edit
17
+ super
18
+ resource.set_body_with_default_table
19
+ end
20
+
21
+ def create
22
+ @page = build_resource
23
+ create! { redirect_to page_category_pages_path(@page.page_category) }
24
+ end
25
+
26
+ def update
27
+ super { redirect_to page_category_pages_path(@page.page_category) }
28
+ end
29
+
30
+ def destroy
31
+ super { redirect_to page_category_pages_path(@page.page_category) }
32
+ end
33
+
34
+ private
35
+ def build_resource
36
+ if params[:page_category_id].present?
37
+ load_page_category
38
+ @page_category.pages.build resource_params
39
+ else
40
+ Page.new resource_params
41
+ end
42
+ end
43
+
44
+ def permitted_params
45
+ params.require(:page).permit(:path, :via, :title, :body, :example, :reqs, :page_category_slug, :weight)
46
+ end
47
+
48
+ def resource_params
49
+ params.fetch :page, {}
50
+ end
51
+
52
+ def resource_class
53
+ Page
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,47 @@
1
+ require "bootstrap/active_link_to"
2
+ require "bootstrap/markdown_renderer"
3
+
4
+ module Docs
5
+ module ApplicationHelper
6
+ include Bootstrap::ActiveLinkTo
7
+ include FontAwesome::Rails::IconHelper
8
+
9
+ def li_active_to(*args, &block)
10
+ options = args.extract_options!
11
+ if block_given?
12
+ buffer = capture(&block)
13
+ if options.has_key?(:dropdown)
14
+ options[:dropdown_html] = buffer
15
+ else
16
+ args.unshift(buffer)
17
+ end
18
+ end
19
+ options[:wrap_tag] = :li
20
+ active_link_to *args, options
21
+ end
22
+
23
+ def li_link_to(*args, &block)
24
+ options = args.last.is_a?(Hash) ? args.pop : {}
25
+ url = args.pop
26
+ title = args.pop unless block_given?
27
+
28
+ link_options = options.delete(:link).presence || {}
29
+ li_options = { class: (%w(active) if current_page?(url)) }.smart_merge!(options.delete(:li).presence || {})
30
+
31
+ link = block_given? ? link_to(url, link_options, &block) : link_to(title, url, link_options)
32
+
33
+ content_tag :li, raw(link), li_options
34
+ end
35
+
36
+ def markdown(text)
37
+ renderer = Bootstrap::MarkdownRenderer.new(hard_wrap: true, filter_html: true)
38
+ options = {
39
+ autolink: true,
40
+ fenced_code_blocks: true,
41
+ superscript: true,
42
+ tables: true
43
+ }
44
+ Redcarpet::Markdown.new(renderer, options).render(text).html_safe
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,11 @@
1
+ module Docs
2
+ class MissingCollection < RoutesCollection
3
+ def initialize(*args)
4
+ super(*args)
5
+ existed_pages = Page.all.to_a
6
+ routes.select! do |r|
7
+ existed_pages.detect {|p| p.reqs == r.reqs && p.path == r.path }.nil?
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,64 @@
1
+ module Docs
2
+ class Page < ActiveRecord::Base
3
+ belongs_to :page_category, autosave: true
4
+ store_accessor :data, :notice, :reqs, :weight
5
+
6
+ default_value_for :via, "GET"
7
+
8
+ before_validation :upcase_via, if: :via?
9
+
10
+ validates :path, :via, :title, presence: true
11
+
12
+ def weight
13
+ super.to_i
14
+ end
15
+
16
+ def method_path
17
+ @method_path ||= [via, only_path].join(" ")
18
+ end
19
+
20
+ def page_category_slug
21
+ @page_category_slug ||= page_category.try(:slug)
22
+ end
23
+
24
+ def page_category_slug=(slug)
25
+ @page_category_slug = begin
26
+ slug = slug.to_s.downcase.underscore
27
+ self.page_category = PageCategory.find_or_initialize_by(slug: slug)
28
+ page_category.try(:slug)
29
+ end
30
+ end
31
+
32
+ def anchor
33
+ @anchor ||= method_path.downcase.gsub(/[^\w]/, "_")
34
+ end
35
+
36
+ def only_path
37
+ @only_path ||= path.split("?").first
38
+ end
39
+
40
+ def only_query
41
+ @only_query ||= splitted_path.length > 1 ? "?#{splitted_path.last}" : ""
42
+ end
43
+
44
+ def splitted_path
45
+ @splitted_path ||= path.split('?')
46
+ end
47
+
48
+ def set_body_with_default_table
49
+ return body if body.present?
50
+ self.body = <<-markdown.gsub(/^\s+\.|/, '')
51
+ .| Параметр | Описание |
52
+ .|----------|----------|
53
+ .| | |
54
+ .
55
+ .
56
+ markdown
57
+ end
58
+
59
+ private
60
+ def upcase_via
61
+ via.upcase!; true
62
+ end
63
+ end
64
+ end