api_docs_engine 0.1.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 (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