haveapi 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +15 -0
  3. data/CHANGELOG +15 -0
  4. data/README.md +66 -47
  5. data/doc/create-client.md +14 -5
  6. data/doc/json-schema.erb +16 -2
  7. data/doc/protocol.md +25 -3
  8. data/doc/protocol.plantuml +14 -8
  9. data/haveapi.gemspec +4 -2
  10. data/lib/haveapi.rb +5 -3
  11. data/lib/haveapi/action.rb +34 -6
  12. data/lib/haveapi/action_state.rb +92 -0
  13. data/lib/haveapi/authentication/basic/provider.rb +7 -0
  14. data/lib/haveapi/authentication/token/provider.rb +5 -0
  15. data/lib/haveapi/client_example.rb +83 -0
  16. data/lib/haveapi/client_examples/curl.rb +86 -0
  17. data/lib/haveapi/client_examples/fs_client.rb +116 -0
  18. data/lib/haveapi/client_examples/http.rb +91 -0
  19. data/lib/haveapi/client_examples/js_client.rb +149 -0
  20. data/lib/haveapi/client_examples/php_client.rb +122 -0
  21. data/lib/haveapi/client_examples/ruby_cli.rb +117 -0
  22. data/lib/haveapi/client_examples/ruby_client.rb +106 -0
  23. data/lib/haveapi/context.rb +3 -2
  24. data/lib/haveapi/example.rb +29 -2
  25. data/lib/haveapi/extensions/action_exceptions.rb +2 -2
  26. data/lib/haveapi/extensions/base.rb +1 -1
  27. data/lib/haveapi/extensions/exception_mailer.rb +339 -0
  28. data/lib/haveapi/hooks.rb +1 -1
  29. data/lib/haveapi/parameters/typed.rb +5 -3
  30. data/lib/haveapi/public/css/highlight.css +99 -0
  31. data/lib/haveapi/public/doc/protocol.png +0 -0
  32. data/lib/haveapi/public/js/highlight.pack.js +2 -0
  33. data/lib/haveapi/public/js/highlighter.js +9 -0
  34. data/lib/haveapi/public/js/main.js +32 -0
  35. data/lib/haveapi/public/js/nojs-tabs.js +196 -0
  36. data/lib/haveapi/resources/action_state.rb +196 -0
  37. data/lib/haveapi/server.rb +96 -27
  38. data/lib/haveapi/version.rb +2 -2
  39. data/lib/haveapi/views/main_layout.erb +14 -0
  40. data/lib/haveapi/views/version_page.erb +187 -13
  41. data/lib/haveapi/views/version_sidebar.erb +37 -3
  42. metadata +49 -5
@@ -183,7 +183,7 @@ module HaveAPI
183
183
  end
184
184
 
185
185
  def self.stop(ret)
186
- throw(ret)
186
+ throw(:stop, ret)
187
187
  end
188
188
  end
189
189
 
@@ -1,3 +1,5 @@
1
+ require 'date'
2
+
1
3
  module HaveAPI::Parameters
2
4
  class Typed
3
5
  ATTRIBUTES = %i(label desc type db_name default fill clean)
@@ -67,7 +69,7 @@ module HaveAPI::Parameters
67
69
 
68
70
  def clean(raw)
69
71
  return instance_exec(raw, &@clean) if @clean
70
-
72
+
71
73
  val = if raw.nil?
72
74
  @default
73
75
 
@@ -76,7 +78,7 @@ module HaveAPI::Parameters
76
78
 
77
79
  elsif @type == Integer
78
80
  raw.to_i
79
-
81
+
80
82
  elsif @type == Float
81
83
  raw.to_f
82
84
 
@@ -85,7 +87,7 @@ module HaveAPI::Parameters
85
87
 
86
88
  elsif @type == ::Datetime
87
89
  begin
88
- Time.iso8601(raw)
90
+ DateTime.iso8601(raw).to_time
89
91
 
90
92
  rescue ArgumentError
91
93
  raise HaveAPI::ValidationError.new("not in ISO 8601 format '#{raw}'")
@@ -0,0 +1,99 @@
1
+ /*
2
+
3
+ Original highlight.js style (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
4
+
5
+ */
6
+
7
+ .hljs {
8
+ display: block;
9
+ overflow-x: auto;
10
+ padding: 0.5em;
11
+ background: #F0F0F0;
12
+ }
13
+
14
+
15
+ /* Base color: saturation 0; */
16
+
17
+ .hljs,
18
+ .hljs-subst {
19
+ color: #444;
20
+ }
21
+
22
+ .hljs-comment {
23
+ color: #888888;
24
+ }
25
+
26
+ .hljs-keyword,
27
+ .hljs-attribute,
28
+ .hljs-selector-tag,
29
+ .hljs-meta-keyword,
30
+ .hljs-doctag,
31
+ .hljs-name {
32
+ font-weight: bold;
33
+ }
34
+
35
+
36
+ /* User color: hue: 0 */
37
+
38
+ .hljs-type,
39
+ .hljs-string,
40
+ .hljs-number,
41
+ .hljs-selector-id,
42
+ .hljs-selector-class,
43
+ .hljs-quote,
44
+ .hljs-template-tag,
45
+ .hljs-deletion {
46
+ color: #880000;
47
+ }
48
+
49
+ .hljs-title,
50
+ .hljs-section {
51
+ color: #880000;
52
+ font-weight: bold;
53
+ }
54
+
55
+ .hljs-regexp,
56
+ .hljs-symbol,
57
+ .hljs-variable,
58
+ .hljs-template-variable,
59
+ .hljs-link,
60
+ .hljs-selector-attr,
61
+ .hljs-selector-pseudo {
62
+ color: #BC6060;
63
+ }
64
+
65
+
66
+ /* Language color: hue: 90; */
67
+
68
+ .hljs-literal {
69
+ color: #78A960;
70
+ }
71
+
72
+ .hljs-built_in,
73
+ .hljs-bullet,
74
+ .hljs-code,
75
+ .hljs-addition {
76
+ color: #397300;
77
+ }
78
+
79
+
80
+ /* Meta color: hue: 200 */
81
+
82
+ .hljs-meta {
83
+ color: #1f7199;
84
+ }
85
+
86
+ .hljs-meta-string {
87
+ color: #4d99bf;
88
+ }
89
+
90
+
91
+ /* Misc effects */
92
+
93
+ .hljs-emphasis {
94
+ font-style: italic;
95
+ }
96
+
97
+ .hljs-strong {
98
+ font-weight: bold;
99
+ }
@@ -0,0 +1,2 @@
1
+ /*! highlight.js v9.8.0 | BSD3 License | git.io/hljslicense */
2
+ !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(e){function n(e){return e.replace(/[&<>]/gm,function(e){return I[e]})}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function a(e){return k.test(e)}function i(e){var n,t,r,i,o=e.className+" ";if(o+=e.parentNode?e.parentNode.className:"",t=B.exec(o))return R(t[1])?t[1]:"no-highlight";for(o=o.split(/\s+/),n=0,r=o.length;r>n;n++)if(i=o[n],a(i)||R(i))return i}function o(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset<r[0].offset?e:r:"start"===r[0].event?e:r:e.length?e:r}function o(e){function r(e){return" "+e.nodeName+'="'+n(e.value)+'"'}l+="<"+t(e)+w.map.call(e.attributes,r).join("")+">"}function u(e){l+="</"+t(e)+">"}function c(e){("start"===e.event?o:u)(e.node)}for(var s=0,l="",f=[];e.length||r.length;){var g=i();if(l+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g===e&&g.length&&g[0].offset===s);f.reverse().forEach(o)}else"start"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return l+n(a.substr(s))}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");u[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?c("keyword",a.k):E(a.k).forEach(function(e){c(e,a.k[e])}),a.k=u}a.lR=t(a.l||/\w+/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),null==a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push("self"===e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var l=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=l.length?t(l.join("|"),!0):{exec:function(){return null}}}}r(e)}function l(e,t,a,i){function o(e,n){var t,a;for(t=0,a=n.c.length;a>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function g(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function h(e,n,t,r){var a=r?"":y.classPrefix,i='<span class="'+a,o=t?"":C;return i+=e+'">',i+n+o}function p(){var e,t,r,a;if(!E.k)return n(B);for(a="",t=0,E.lR.lastIndex=0,r=E.lR.exec(B);r;)a+=n(B.substr(t,r.index-t)),e=g(E,r),e?(M+=e[1],a+=h(e[0],n(r[0]))):a+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(B);return a+n(B.substr(t))}function d(){var e="string"==typeof E.sL;if(e&&!x[E.sL])return n(B);var t=e?l(E.sL,B,!0,L[E.sL]):f(B,E.sL.length?E.sL:void 0);return E.r>0&&(M+=t.r),e&&(L[E.sL]=t.top),h(t.language,t.value,!1,!0)}function b(){k+=null!=E.sL?d():p(),B=""}function v(e){k+=e.cN?h(e.cN,"",!0):"",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(B+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?B+=n:(t.eB&&(B+=n),b(),t.rB||t.eB||(B=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var a=E;a.skip?B+=n:(a.rE||a.eE||(B+=n),b(),a.eE&&(B=n));do E.cN&&(k+=C),E.skip||(M+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,""),a.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme "'+n+'" for mode "'+(E.cN||"<unnamed>")+'"');return B+=n,n.length||1}var N=R(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var w,E=i||N,L={},k="";for(w=E;w!==N;w=w.parent)w.cN&&(k=h(w.cN,"",!0)+k);var B="",M=0;try{for(var I,j,O=0;;){if(E.t.lastIndex=O,I=E.t.exec(t),!I)break;j=m(t.substr(O,I.index-O),I[0]),O=I.index+j}for(m(t.substr(O)),w=E;w.parent;w=w.parent)w.cN&&(k+=C);return{r:M,value:k,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf("Illegal"))return{r:0,value:n(t)};throw T}}function f(e,t){t=t||y.languages||E(x);var r={r:0,value:n(e)},a=r;return t.filter(R).forEach(function(n){var t=l(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}),a.language&&(r.second_best=a),r}function g(e){return y.tabReplace||y.useBR?e.replace(M,function(e,n){return y.useBR&&"\n"===e?"<br>":y.tabReplace?n.replace(/\t/g,y.tabReplace):void 0}):e}function h(e,n,t){var r=n?L[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function p(e){var n,t,r,o,s,p=i(e);a(p)||(y.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n.innerHTML=e.innerHTML.replace(/\n/g,"").replace(/<br[ \/]*>/g,"\n")):n=e,s=n.textContent,r=p?l(p,s,!0):f(s),t=u(n),t.length&&(o=document.createElementNS("http://www.w3.org/1999/xhtml","div"),o.innerHTML=r.value,r.value=c(t,u(o),s)),r.value=g(r.value),e.innerHTML=r.value,e.className=h(e.className,p,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 d(e){y=o(y,e)}function b(){if(!b.called){b.called=!0;var e=document.querySelectorAll("pre code");w.forEach.call(e,p)}}function v(){addEventListener("DOMContentLoaded",b,!1),addEventListener("load",b,!1)}function m(n,t){var r=x[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function N(){return E(x)}function R(e){return e=(e||"").toLowerCase(),x[e]||x[L[e]]}var w=[],E=Object.keys,x={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,C="</span>",y={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},I={"&":"&amp;","<":"&lt;",">":"&gt;"};return e.highlight=l,e.highlightAuto=f,e.fixMarkup=g,e.highlightBlock=p,e.configure=d,e.initHighlighting=b,e.initHighlightingOnLoad=v,e.registerLanguage=m,e.listLanguages=N,e.getLanguage=R,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={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|like)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.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},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e});hljs.registerLanguage("bash",function(e){var t={cN:"variable",v:[{b:/\$[\w\d#@][\w\d_]*/},{b:/\$\{(.*?)}/}]},s={cN:"string",b:/"/,e:/"/,c:[e.BE,t,{cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]}]},a={cN:"string",b:/'/,e:/'/};return{aliases:["sh","zsh"],l:/-?[a-z\._]+/,k:{keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},c:[{cN:"meta",b:/^#![^\n]+sh\s*$/,r:10},{cN:"function",b:/\w[\w\d_]*\s*\(\s*\)\s*\{/,rB:!0,c:[e.inherit(e.TM,{b:/\w[\w\d_]*/})],r:0},e.HCM,s,a,t]}});hljs.registerLanguage("http",function(e){var t="HTTP/[0-9\\.]+";return{aliases:["https"],i:"\\S",c:[{b:"^"+t,e:"$",c:[{cN:"number",b:"\\b\\d{3}\\b"}]},{b:"^[A-Z]+ (.*?) "+t+"$",rB:!0,e:"$",c:[{cN:"string",b:" ",e:" ",eB:!0,eE:!0},{b:t},{cN:"keyword",b:"[A-Z]+"}]},{cN:"attribute",b:"^\\w",e:": ",eE:!0,i:"\\n|\\s|=",starts:{e:"$",r:0}},{b:"\\n\\n",starts:{sL:[],eW:!0}}]}});hljs.registerLanguage("xml",function(s){var e="[A-Za-z0-9\\._:-]+",t={eW:!0,i:/</,r:0,c:[{cN:"attr",b:e,r:0},{b:/=\s*/,r:0,c:[{cN:"string",endsParent:!0,v:[{b:/"/,e:/"/},{b:/'/,e:/'/},{b:/[^\s"'=<>`]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist"],cI:!0,c:[{cN:"meta",b:"<!DOCTYPE",e:">",r:10,c:[{b:"\\[",e:"\\]"}]},s.C("<!--","-->",{r:10}),{b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{b:/<\?(php)?/,e:/\?>/,sL:"php",c:[{b:"/\\*",e:"\\*/",skip:!0}]},{cN:"tag",b:"<style(?=\\s|>|$)",e:">",k:{name:"style"},c:[t],starts:{e:"</style>",rE:!0,sL:["css","xml"]}},{cN:"tag",b:"<script(?=\\s|>|$)",e:">",k:{name:"script"},c:[t],starts:{e:"</script>",rE:!0,sL:["actionscript","javascript","handlebars","xml"]}},{cN:"meta",v:[{b:/<\?xml/,e:/\?>/,r:10},{b:/<\?\w+/,e:/\?>/}]},{cN:"tag",b:"</?",e:"/?>",c:[{cN:"name",b:/[^\/><\s]+/,r:0},t]}]}});hljs.registerLanguage("php",function(e){var c={b:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},i={cN:"meta",b:/<\?(php)?|\?>/},t={cN:"string",c:[e.BE,i],v:[{b:'b"',e:'"'},{b:"b'",e:"'"},e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},a={v:[e.BNM,e.CNM]};return{aliases:["php3","php4","php5","php6"],cI:!0,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception default die require __FUNCTION__ enddeclare final try switch continue endfor endif declare unset true false trait goto instanceof insteadof __DIR__ __NAMESPACE__ yield finally",c:[e.HCM,e.C("//","$",{c:[i]}),e.C("/\\*","\\*/",{c:[{cN:"doctag",b:"@[A-Za-z]+"}]}),e.C("__halt_compiler.+?;",!1,{eW:!0,k:"__halt_compiler",l:e.UIR}),{cN:"string",b:/<<<['"]?\w+['"]?$/,e:/^\w+;?$/,c:[e.BE,{cN:"subst",v:[{b:/\$\w+/},{b:/\{\$/,e:/\}/}]}]},i,{cN:"keyword",b:/\$this\b/},c,{b:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{cN:"function",bK:"function",e:/[;{]/,eE:!0,i:"\\$|\\[|%",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)",c:["self",c,e.CBCM,t,a]}]},{cN:"class",bK:"class interface",e:"{",eE:!0,i:/[:\(\$"]/,c:[{bK:"extends implements"},e.UTM]},{bK:"namespace",e:";",i:/[\.']/,c:[e.UTM]},{bK:"use",e:";",c:[e.UTM]},{b:"=>"},t,a]}});hljs.registerLanguage("json",function(e){var i={literal:"true false null"},n=[e.QSM,e.CNM],r={e:",",eW:!0,eE:!0,c:n,k:i},t={b:"{",e:"}",c:[{cN:"attr",b:/"/,e:/"/,c:[e.BE],i:"\\n"},e.inherit(r,{b:/:/})],i:"\\S"},c={b:"\\[",e:"\\]",c:[e.inherit(r)],i:"\\S"};return n.splice(n.length,0,t,c),{c:n,k:i,i:"\\S"}});hljs.registerLanguage("javascript",function(e){var r="[A-Za-z$_][0-9A-Za-z$_]*",t={keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await static import from as",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},a={cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},n={cN:"subst",b:"\\$\\{",e:"\\}",k:t,c:[]},c={cN:"string",b:"`",e:"`",c:[e.BE,n]};n.c=[e.ASM,e.QSM,c,a,e.RM];var s=n.c.concat([e.CBCM,e.CLCM]);return{aliases:["js","jsx"],k:t,c:[{cN:"meta",r:10,b:/^\s*['"]use (strict|asm)['"]/},{cN:"meta",b:/^#!/,e:/$/},e.ASM,e.QSM,c,e.CLCM,e.CBCM,a,{b:/[{,]\s*/,r:0,c:[{b:r+"\\s*:",rB:!0,r:0,c:[{cN:"attr",b:r,r:0}]}]},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{cN:"function",b:"(\\(.*?\\)|"+r+")\\s*=>",rB:!0,e:"\\s*=>",c:[{cN:"params",v:[{b:r},{b:/\(\s*\)/},{b:/\(/,e:/\)/,eB:!0,eE:!0,k:t,c:s}]}]},{b:/</,e:/(\/\w+|\w+\/)>/,sL:"xml",c:[{b:/<\w+\s*\/>/,skip:!0},{b:/<\w+/,e:/(\/\w+|\w+\/)>/,skip:!0,c:[{b:/<\w+\s*\/>/,skip:!0},"self"]}]}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:r}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:s}],i:/\[|%/},{b:/\$[(.]/},e.METHOD_GUARD,{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]},{bK:"constructor",e:/\{/,eE:!0}],i:/#(?!!)/}});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+")\\s*",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,i.c=d;var l="[>?]>",o="[\\w#]+\\(\\w+\\):\\d+:\\d+>",w="(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>",u=[{b:/^\s*=>/,starts:{e:"$",c:d}},{cN:"meta",b:"^("+l+"|"+o+"|"+w+")",starts:{e:"$",c:d}}];return{aliases:["rb","gemspec","podspec","thor","irb"],k:r,i:/\/\*/,c:s.concat(u).concat(d)}});
@@ -0,0 +1,9 @@
1
+ importScripts('highlight.pack.js');
2
+
3
+ onmessage = function (e) {
4
+ if (e.data.language)
5
+ postMessage(hljs.highlight(e.data.language, e.data.code).value);
6
+
7
+ else
8
+ postMessage(hljs.highlightAuto(e.data.code).value);
9
+ }
@@ -0,0 +1,32 @@
1
+ (function () {
2
+
3
+ $(document).ready(function () {
4
+ if (window.Worker) {
5
+ var worker = new Worker('/js/highlighter.js');
6
+ var codes = $('pre code').toArray();
7
+ var currentElement;
8
+
9
+ var highlightNext = function () {
10
+ if (!codes.length)
11
+ return;
12
+
13
+ currentElement = codes.shift();
14
+ worker.postMessage({
15
+ language: currentElement.classList.item(0),
16
+ code: currentElement.textContent
17
+ });
18
+ };
19
+
20
+ worker.onmessage = function (e) {
21
+ currentElement.innerHTML = e.data;
22
+ highlightNext();
23
+ };
24
+
25
+ highlightNext();
26
+
27
+ } else {
28
+ hljs.initHighlighting();
29
+ }
30
+ });
31
+
32
+ })();
@@ -0,0 +1,196 @@
1
+ (function (root) {
2
+ var nojsTabs = function (opts) {
3
+ this.opts = opts;
4
+
5
+ var ul = this.createElement(document.createElement('UL'));
6
+ var tabNum = opts.tabs.children.length;
7
+
8
+ for (var i = 0; i < tabNum; i++) {
9
+ var tab = opts.tabs.children[i];
10
+
11
+ ul.appendChild(this.createTab(tab));
12
+ }
13
+
14
+ opts.tabBar.appendChild(ul);
15
+
16
+ this.init();
17
+
18
+ var that = this;
19
+ var prevOnPopState = window.onpopstate;
20
+
21
+ window.onpopstate = function(e) {
22
+ if (!e.state || !e.state.tabBar || that.opts.tabBar.id !== e.state.tabBar) {
23
+ if (prevOnPopState)
24
+ prevOnPopState(e);
25
+
26
+ return;
27
+ }
28
+
29
+ that.switchTab(document.getElementById(e.state.tab), false);
30
+ };
31
+ };
32
+
33
+ nojsTabs.Version = '0.2.0';
34
+
35
+ nojsTabs.prototype.createElement = function (el) {
36
+ if (this.opts.createElement !== undefined) {
37
+ var ret = this.opts.createElement(el);
38
+
39
+ return ret === undefined ? el : ret;
40
+ }
41
+
42
+ return el;
43
+ }
44
+
45
+ nojsTabs.prototype.createTab = function (tab) {
46
+ var li = this.createElement(document.createElement('LI'));
47
+ li.id = 'tab-anchor-' + tab.id;
48
+
49
+ var a = this.createElement(document.createElement('A'));
50
+ var a_text = document.createTextNode(this.getTitle(tab));
51
+
52
+ a.href = '#' + tab.id;
53
+
54
+ var that = this;
55
+
56
+ a.addEventListener('click', function (e) {
57
+ that.switchTab(tab);
58
+
59
+ if (history.pushState)
60
+ e.preventDefault();
61
+ });
62
+
63
+ a.appendChild(a_text);
64
+ li.appendChild(a);
65
+
66
+ tab.tabAnchor = li;
67
+ tab.tabAnchor.activate = function () {
68
+ that.activate([this]);
69
+ };
70
+
71
+ tab.tabAnchor.deactivate = function () {
72
+ that.deactivate([this]);
73
+ };
74
+
75
+ return li;
76
+ };
77
+
78
+ nojsTabs.prototype.getTitle = function (tab) {
79
+ var s = this.opts.titleSelector;
80
+
81
+ if (typeof s === 'string' || s instanceof String) {
82
+ var el = tab.querySelector(s);
83
+ var ret = el.innerHTML;
84
+
85
+ if (this.opts.removeHeading === undefined || this.opts.removeHeading)
86
+ el.parentElement.removeChild(el);
87
+
88
+ return ret;
89
+
90
+ } else
91
+ return s(tab);
92
+ };
93
+
94
+ nojsTabs.prototype.init = function () {
95
+ var that = this;
96
+ var targetTab = this.opts.tabs.children[0];
97
+
98
+ // Find initial tab
99
+ if (location.hash) {
100
+ this.eachTab(function (tab) {
101
+ if (location.hash == '#'+tab.id) {
102
+ targetTab = tab;
103
+ return true;
104
+ }
105
+ });
106
+
107
+ } else {
108
+ this.eachTab(function (tab) {
109
+ if (tab.classList.contains(that.opts.activeClass)) {
110
+ targetTab = tab;
111
+ return true;
112
+ }
113
+ });
114
+ }
115
+
116
+ // Show/hide tabs
117
+ this.eachTab(function (tab, i) {
118
+ var a = that.opts.tabBar.getElementsByTagName('li')[i];
119
+
120
+ if (targetTab.id == tab.id)
121
+ that.activate([tab, a]);
122
+ else
123
+ that.deactivate([tab, a]);
124
+ });
125
+ };
126
+
127
+ nojsTabs.prototype.eachTab = function (fn) {
128
+ var tabs = this.opts.tabs.children;
129
+ var tabNum = tabs.length;
130
+
131
+ for (var i = 0; i < tabNum; i++) {
132
+ if (fn(tabs[i], i))
133
+ break;
134
+ }
135
+ };
136
+
137
+ nojsTabs.prototype.switchTab = function (targetTab, pushState) {
138
+ var that = this;
139
+ var activeTab = this.opts.tabs.querySelector('.'+this.opts.activeClass);
140
+
141
+ if (activeTab.id == targetTab.id)
142
+ return;
143
+
144
+ // beforeChange
145
+ if (this.opts.beforeChange !== undefined)
146
+ this.opts.beforeChange(activeTab, targetTab);
147
+
148
+ // transition
149
+ if (this.opts.transition !== undefined)
150
+ this.opts.transition(activeTab, targetTab, function () {
151
+ that.transition(activeTab, targetTab);
152
+ });
153
+
154
+ else
155
+ this.transition(activeTab, targetTab);
156
+
157
+ if ((pushState === undefined || pushState) && history.pushState) {
158
+ history.pushState({
159
+ tabBar: this.opts.tabBar.id,
160
+ tab: targetTab.id
161
+ }, null, '#'+targetTab.id);
162
+ }
163
+ };
164
+
165
+ nojsTabs.prototype.transition = function(activeTab, targetTab) {
166
+ this.deactivate([activeTab, activeTab.tabAnchor]);
167
+ this.activate([targetTab, targetTab.tabAnchor]);
168
+
169
+ // afterChange
170
+ if (this.opts.beforeChange !== undefined)
171
+ this.opts.afterChange(targetTab, activeTab);
172
+ };
173
+
174
+ nojsTabs.prototype.activate = function (elems) {
175
+ var len = elems.length;
176
+
177
+ for (var i = 0; i < len; i++) {
178
+ elems[i].classList.remove(this.opts.hiddenClass);
179
+ elems[i].classList.add(this.opts.activeClass);
180
+ }
181
+ };
182
+
183
+ nojsTabs.prototype.deactivate = function (elems) {
184
+ var len = elems.length;
185
+
186
+ for (var i = 0; i < len; i++) {
187
+ elems[i].classList.remove(this.opts.activeClass);
188
+ elems[i].classList.add(this.opts.hiddenClass);
189
+ }
190
+ };
191
+
192
+ root.nojsTabs = function (opts) {
193
+ return new nojsTabs(opts);
194
+ };
195
+
196
+ })(window);
@@ -0,0 +1,196 @@
1
+ module HaveAPI::Resources
2
+ class ActionState < HaveAPI::Resource
3
+ desc 'Browse states of blocking actions'
4
+ auth false
5
+ version :all
6
+
7
+ params(:all) do
8
+ id :id
9
+ string :label, label: 'Label'
10
+ bool :finished, label: 'Finished'
11
+ bool :status, label: 'Status',
12
+ desc: 'Determines whether the action is proceeding or failing'
13
+ integer :current, label: 'Current progress'
14
+ integer :total, label: 'Total',
15
+ desc: 'The action is finished when current equals to total'
16
+ string :unit, label: 'Unit', desc: 'Unit of current and total'
17
+ bool :can_cancel, label: 'Can cancel',
18
+ desc: 'When true, execution of this action can be cancelled'
19
+ datetime :created_at, label: 'Created at'
20
+ datetime :updated_at, label: 'Updated at',
21
+ desc: 'When was the progress last updated'
22
+ end
23
+
24
+ module Mixin
25
+ def state_to_hash(state)
26
+ hash = {
27
+ id: state.id,
28
+ label: state.label,
29
+ status: state.status,
30
+ created_at: state.created_at,
31
+ updated_at: state.updated_at,
32
+ can_cancel: state.can_cancel?,
33
+ }
34
+
35
+ if state.finished?
36
+ hash[:finished] = true
37
+
38
+ else
39
+ hash[:finished] = false
40
+ end
41
+
42
+ progress = state.progress
43
+ hash[:current] = progress[:current] || 0
44
+ hash[:total] = progress[:total] || 0
45
+ hash[:unit] = progress[:unit]
46
+
47
+ hash
48
+ end
49
+ end
50
+
51
+ class Index < HaveAPI::Actions::Default::Index
52
+ include Mixin
53
+
54
+ desc 'List states of pending actions'
55
+
56
+ input(:hash) do
57
+ string :order, choices: %w(newest oldest), default: 'newest', fill: true
58
+ end
59
+
60
+ output(:hash_list) do
61
+ use :all
62
+ end
63
+
64
+ authorize { allow }
65
+
66
+ def exec
67
+ ret = []
68
+ actions = @context.server.action_state.list_pending(
69
+ current_user,
70
+ input[:offset],
71
+ input[:limit],
72
+ input[:order].to_sym
73
+ )
74
+
75
+ actions.each do |state|
76
+ ret << state_to_hash(state)
77
+ end
78
+
79
+ ret
80
+ end
81
+ end
82
+
83
+ class Poll < HaveAPI::Action
84
+ include Mixin
85
+
86
+ desc 'Returns when the action is completed or timeout occurs'
87
+ http_method :get
88
+ route ':%{resource}_id/poll'
89
+
90
+ input(:hash) do
91
+ float :timeout, label: 'Timeout', desc: 'in seconds', default: 15, fill: true
92
+ float :update_in, label: 'Progress',
93
+ desc: 'number of seconds after which the state is returned if the progress '+
94
+ 'has changed'
95
+ bool :status, desc: 'status to check with if update_in is set'
96
+ integer :current, desc: 'progress to check with if update_in is set'
97
+ integer :total, desc: 'progress to check with if update_in is set'
98
+ end
99
+
100
+ output(:hash) do
101
+ use :all
102
+ end
103
+
104
+ authorize { allow }
105
+
106
+ def exec
107
+ t = Time.now
108
+
109
+ loop do
110
+ state = @context.server.action_state.new(
111
+ current_user,
112
+ id: params[:action_state_id]
113
+ )
114
+
115
+ error('action state not found') unless state.valid?
116
+
117
+ if state.finished? || (Time.now - t) >= input[:timeout]
118
+ return state_to_hash(state)
119
+
120
+ elsif input[:update_in]
121
+ new_state = state_to_hash(state)
122
+
123
+ %i(status current total).each do |v|
124
+ return new_state if input[v] != new_state[v]
125
+ end
126
+ end
127
+
128
+ return state_to_hash(state.poll(input)) if state.respond_to?(:poll)
129
+
130
+ sleep(1)
131
+ end
132
+ end
133
+ end
134
+
135
+ class Show < HaveAPI::Actions::Default::Show
136
+ include Mixin
137
+
138
+ desc 'Show state of a pending action'
139
+
140
+ output(:hash) do
141
+ use :all
142
+ end
143
+
144
+ authorize { allow }
145
+
146
+ def exec
147
+ state = @context.server.action_state.new(
148
+ current_user,
149
+ id: params[:action_state_id]
150
+ )
151
+
152
+ return state_to_hash(state) if state.valid?
153
+
154
+ error('action state not found')
155
+ end
156
+ end
157
+
158
+ class Cancel < HaveAPI::Action
159
+ http_method :post
160
+ route ':%{resource}_id/cancel'
161
+ blocking true
162
+
163
+ output(:hash) {}
164
+
165
+ authorize { allow }
166
+
167
+ def exec
168
+ state = @context.server.action_state.new(
169
+ current_user,
170
+ id: params[:action_state_id]
171
+ )
172
+
173
+ error('action state not found') unless state.valid?
174
+
175
+ ret = state.cancel
176
+
177
+ if ret.is_a?(::Numeric)
178
+ @state_id = ret
179
+
180
+ elsif ret
181
+ ok
182
+
183
+ else
184
+ error('cancellation failed')
185
+ end
186
+
187
+ rescue RuntimeError, NotImplementedError => e
188
+ error(e.message)
189
+ end
190
+
191
+ def state_id
192
+ @state_id
193
+ end
194
+ end
195
+ end
196
+ end