From bfc9a3066b1c0c60e9ed4da4fe445055cc2a9c83 Mon Sep 17 00:00:00 2001 From: Yessiest Date: Sun, 10 Sep 2023 14:46:57 +0400 Subject: [PATCH] Added queries, multipart/form-data parser, and a couple examples on how to use them as of right now --- .yardoc/checksums | 22 +- .yardoc/object_types | Bin 954 -> 5797 bytes .yardoc/objects/root.dat | Bin 21677 -> 108369 bytes doc/Hyde.html | 67 +- doc/Hyde/CONNECTHandler.html | 221 ++++ doc/Hyde/DELETEHandler.html | 221 ++++ doc/Hyde/DSL.html | 128 +++ doc/Hyde/DSL/PathConstructors.html | 995 ++++++++++++++++ doc/Hyde/DSL/PathMethods.html | 704 ++++++++++++ doc/Hyde/DSL/ProbeMethods.html | 767 +++++++++++++ doc/Hyde/GETHandler.html | 366 ++++++ doc/Hyde/HEADHandler.html | 221 ++++ doc/Hyde/Handler.html | 609 ++++++++++ doc/Hyde/Node.html | 714 ++++++++++++ doc/Hyde/OPTIONSHandler.html | 221 ++++ doc/Hyde/PATCHHandler.html | 221 ++++ doc/Hyde/POSTHandler.html | 221 ++++ doc/Hyde/PUTHandler.html | 221 ++++ doc/Hyde/Path.html | 792 ++++++++++++- doc/Hyde/PathBinding.html | 128 ++- doc/Hyde/Pattern.html | 28 +- doc/Hyde/PatternMatching.html | 2 +- doc/Hyde/PatternMatching/Glob.html | 2 +- doc/Hyde/PatternMatching/ReMatch.html | 2 +- doc/Hyde/Probe.html | 471 ++++++++ doc/Hyde/ProbeBinding.html | 219 ++++ doc/Hyde/Request.html | 1505 +++++++++++++++++++++++++ doc/Hyde/Response.html | 1177 +++++++++++++++++++ doc/Hyde/ServeHandler.html | 476 ++++++++ doc/Hyde/Server.html | 441 ++++++++ doc/Hyde/ServerBinding.html | 162 +++ doc/Hyde/TRACEHandler.html | 221 ++++ doc/Hyde/Util.html | 487 ++++++++ doc/Hyde/Util/Lookup.html | 613 ++++++++++ doc/Hyde/Util/Query.html | 402 +++++++ doc/_index.html | 252 ++++- doc/class_list.html | 2 +- doc/file.README.html | 119 +- doc/index.html | 119 +- doc/method_list.html | 750 +++++++++++- doc/top-level-namespace.html | 2 +- examples/form/form.ru | 21 + examples/form/index.html | 27 + examples/form/lib | 1 + examples/query/config.ru | 19 + examples/query/lib | 1 + examples/query/readme.txt | 6 + lib/hyde/request.rb | 22 +- lib/hyde/util/header.rb | 41 + lib/hyde/util/html.rb | 5 +- lib/hyde/util/multipart.rb | 165 +++ lib/hyde/util/query.rb | 66 ++ lib/hyde/util/sorting.rb | 37 + test/Hyde_Util_Query.rb | 16 + 54 files changed, 14549 insertions(+), 169 deletions(-) create mode 100644 doc/Hyde/CONNECTHandler.html create mode 100644 doc/Hyde/DELETEHandler.html create mode 100644 doc/Hyde/DSL.html create mode 100644 doc/Hyde/DSL/PathConstructors.html create mode 100644 doc/Hyde/DSL/PathMethods.html create mode 100644 doc/Hyde/DSL/ProbeMethods.html create mode 100644 doc/Hyde/GETHandler.html create mode 100644 doc/Hyde/HEADHandler.html create mode 100644 doc/Hyde/Handler.html create mode 100644 doc/Hyde/Node.html create mode 100644 doc/Hyde/OPTIONSHandler.html create mode 100644 doc/Hyde/PATCHHandler.html create mode 100644 doc/Hyde/POSTHandler.html create mode 100644 doc/Hyde/PUTHandler.html create mode 100644 doc/Hyde/Probe.html create mode 100644 doc/Hyde/ProbeBinding.html create mode 100644 doc/Hyde/Request.html create mode 100644 doc/Hyde/Response.html create mode 100644 doc/Hyde/ServeHandler.html create mode 100644 doc/Hyde/Server.html create mode 100644 doc/Hyde/ServerBinding.html create mode 100644 doc/Hyde/TRACEHandler.html create mode 100644 doc/Hyde/Util.html create mode 100644 doc/Hyde/Util/Lookup.html create mode 100644 doc/Hyde/Util/Query.html create mode 100644 examples/form/form.ru create mode 100644 examples/form/index.html create mode 120000 examples/form/lib create mode 100644 examples/query/config.ru create mode 120000 examples/query/lib create mode 100644 examples/query/readme.txt create mode 100644 lib/hyde/util/header.rb create mode 100644 lib/hyde/util/multipart.rb create mode 100644 lib/hyde/util/query.rb create mode 100644 lib/hyde/util/sorting.rb create mode 100644 test/Hyde_Util_Query.rb diff --git a/.yardoc/checksums b/.yardoc/checksums index 1faf9f8..5ce3526 100644 --- a/.yardoc/checksums +++ b/.yardoc/checksums @@ -1,5 +1,21 @@ -lib/hyde.rb 6b22abff461426484b51291071193f02c2cf7850 -lib/hyde/pattern_matching.rb 84399fe8cc7f55b1da9a9348759a75ba1e6374e6 -lib/hyde/pattern_matching/glob.rb c8dab66857e6da3770f94ab26134ec27d262f240 +lib/hyde.rb 775368f05e89c7396fdf9a9c1f55e3537f8fba98 +lib/hyde/node.rb 93d92492390abb2b55649c6d027fa3a2cb8f2014 +lib/hyde/path.rb 280170523467824ac6ce310500fc88439808d59e +lib/hyde/probe.rb 994a5527bb30c713c1c70e56195ea6967ae39241 +lib/hyde/server.rb 3286219acc8606b432d12545e9ba3a57dd227473 +lib/hyde/request.rb 9baea24ae9975e3a26571878ed7e9da99889584f +lib/hyde/response.rb 0f0c107c7db883b308b81cbf5c0c4b7d5c90e7ba +lib/hyde/util/html.rb 3767a1632026ba555ae5517b59728b2e916b61de +lib/hyde/util/query.rb ffa6f9b6631277d7061f63dd36e4cb431a8169f5 +lib/hyde/util/lookup.rb 5b8e28a8471bb786f4e0a0d616885d238c806661 +lib/hyde/probe/binding.rb 1a83cfea5e7b620d075798c920dd4af3335871c7 +lib/hyde/probe/handler.rb 4d45e895a3bfee8e5234be3862c7e201772731b1 +lib/hyde/dsl/path_methods.rb 0369e370c594f0bd5c987c67791175eeaa8f9c21 +lib/hyde/pattern_matching.rb fe86f6529a2d22c128d9d3a74217862e9fa59c15 +lib/hyde/dsl/probe_methods.rb a9dd3ddbdf89875d79d5ca3e56fd3a261546dd25 +lib/hyde/probe/http_method.rb f5f6874998e4a581cd3673a06f3536c8a14e67c8 +lib/hyde/probe/serve_handler.rb 1690e7dd3f0abe9180a61d474bb89c0a765fc381 +lib/hyde/dsl/path_constructors.rb 97895412fc27eeb7f3fa379a85972c97a24d97bf +lib/hyde/pattern_matching/glob.rb 16595083bc8d6f7b98f4adda502a05bd411bc1ca lib/hyde/pattern_matching/util.rb 188dc7d5d9a9a6538a01943a83eb132c385dc092 lib/hyde/pattern_matching/rematch.rb 54a4f94791e68d85c38034d954e4c3174e01511b diff --git a/.yardoc/object_types b/.yardoc/object_types index 89279bce1d982c6265f447061ea4892f2140e5dd..e2ba358f37762c1c4dbd82cf95a8a8dca9749d85 100644 GIT binary patch literal 5797 zcma)AX>TJ%5CsVZcDaw`W`UUdi1<7!go0x>7TMUu4id6fqsdIi9%4@qbA%220e&{s zQ?J`yGZSmS*@|p>?&`d+N1m{({-h1#oExf`DN3#?D|AG4-1kmKiRlG{GCCfha z4GVptgyko`l-v$=R#rtFSGs6?2QSBFCCaMBz487Vto{4wY&M>pvj5kxb}~L4@yc7g zGMr5RI2%7ap7ZLvylP?(f}hhYaSUHp`MdI{yH0*Z*mC1BY zh|YSwe9T_vXNSZ7ZJ;ncoDYwCJ$wZqBtL@S;aXn=!F=-T=(IsJxErsdvRvGEggn(m z(T0$3J0B~NJuuhgl@&pVso@&0AoZMJVf;EfWeF>TV`u8GT9p;Ay~%24s?3RvG9aeL zZ?!6JSos}R=JgY;uj*U~n!rR9*b>l*Fi=vwc8@)8>Bh?znJvx4x<@bJW=ktx21-Xh zomlc7?>n6MG!i079~J=zpEg3^(?tlHYzQ#G^j3di{;=tQL+ogrsiqqlC2D<5&sseD z-*_K2Z|F5hv$RU1b@~qy3z!?3>6<_D7J!+T)h5cZj_ft-OWmjYr;0%&VAAO3g^pE; z4l4FE&e;!InG1zi(TkhaM2DB@TIG)ZC}={$)=B4p+jhO`$Pl41ABYHjqr6Z;hJe^e zmz5A&dK!-uD%}u#3a=p#=)PDdh00hWx6-*Ps#F0ef2*R-%;Cu+gx=W)M!1a=*U5PQ z3rCdIKxI_pwF>iX$W9LlX^upfg?b_q(kzEb3dY!=Ov~vYDujfLIF1FOu@tc3=Hb@V zaSBi&jiWWLZCn}XULCW~`Se!YVAOMXkie>4=mfp3=RY3|rr9N`u5I01=$>`IJl6%Pb6=uUvOQ&`(y<1> z;HHgmrJ}^q56QH$JSw7%;JTHR`8vX$El~QX*m6spZCy3iZBB6X?q>w(R$!wwk-ejp zKP!YOO2?X-wiy;kVslnmg;Fnyufprd7HXfXO}(yC@>6+wm@I@V0l7;l-^|oV>g*~k zT`zYII&vJU?Vx=bI7r~&GR?fJqAk59S>no}?K?`6kfY};x!l8dqSmTXLdDSyDyG?aLAbAUj1J2UcFYb5emy9cw zRbC!JTQgK0s4|Xnf~ne|CQ-MIFj1Eg5h+wfp^Gq&mgpS7+MCuo&G9oXe_w^@SgNpG zQGeBcbJBoaM~kK3bbT}r81tkF6Q%NS6_qH`9#6|c+iB4|aS$})<)Cz(3V`}STvAIR z&^Qbaks#n1v~yfgi+!-p!QwI7_s9lTCIW-+#beMw1O_32NXcZ!s7HU(0n6b4kx$Kq zh#|QXIc_10h#fHkwBkZc?Et+k#O^rvR!Ys*J_1@Yaan_1^ja3LV+zep4)(RyRxuMM zV{@C6Vml9_Z+7Q`h%K%hVtvJ12=G?k;pv$n=X{bKe2l| z-axi7Uj|r$R${Fh35;6%(dA_vrn*IWJg_W_zmHzHzyzLpmQK-#_q<+P5x`cJ7&;sBA*k;Q_8^=hDE zP|!j}l*ZRBDh|CbJe0C!TDlhjrc*~op=xFrPHnpgq?*B>^#oDKTIekms&4nDR4-5A zEU%x&nMpM}YnP{pjHdur#D|l9L}Hy~?q#_Xl&Xjnx?$pPP3NZixinK{`02Iy?ATeGh7M Pr`@W6?>0c-wgdS8Ed)eb delta 85 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
Defined in:
lib/hyde.rb,
- lib/hyde/pattern_matching.rb,
lib/hyde/pattern_matching/glob.rb,
lib/hyde/pattern_matching/util.rb,
lib/hyde/pattern_matching/rematch.rb
+ lib/hyde/node.rb,
lib/hyde/path.rb,
lib/hyde/probe.rb,
lib/hyde/server.rb,
lib/hyde/request.rb,
lib/hyde/response.rb,
lib/hyde/util/html.rb,
lib/hyde/util/query.rb,
lib/hyde/util/lookup.rb,
lib/hyde/probe/binding.rb,
lib/hyde/probe/handler.rb,
lib/hyde/dsl/path_methods.rb,
lib/hyde/pattern_matching.rb,
lib/hyde/dsl/probe_methods.rb,
lib/hyde/probe/http_method.rb,
lib/hyde/probe/serve_handler.rb,
lib/hyde/dsl/path_constructors.rb,
lib/hyde/pattern_matching/glob.rb,
lib/hyde/pattern_matching/util.rb,
lib/hyde/pattern_matching/rematch.rb
@@ -100,15 +100,74 @@

- Modules: PatternMatching + Modules: DSL, PatternMatching, Util - Classes: Path, PathBinding, Pattern + Classes: CONNECTHandler, DELETEHandler, GETHandler, HEADHandler, Handler, Node, OPTIONSHandler, PATCHHandler, POSTHandler, PUTHandler, Path, PathBinding, Pattern, Probe, ProbeBinding, Request, Response, ServeHandler, Server, ServerBinding, TRACEHandler

+ +

+ Constant Summary + collapse +

+ +
+ +
VERSION = +
+
+ +

Hyde version

+ + +
+
+
+ + +
+
+
'0.8 (beta/rewrite)'
+ +
VLINE = +
+
+ +

Hyde branding and version

+ + +
+
+
+ + +
+
+
"Hyde/#{Hyde::VERSION} (Ruby/#{RUBY_VERSION}/#{RUBY_RELEASE_DATE})\n"
+ + +
"Copyright 2023 Yessiest"
+ +
+ + @@ -120,7 +179,7 @@ diff --git a/doc/Hyde/CONNECTHandler.html b/doc/Hyde/CONNECTHandler.html new file mode 100644 index 0000000..9e86e2e --- /dev/null +++ b/doc/Hyde/CONNECTHandler.html @@ -0,0 +1,221 @@ + + + + + + + Class: Hyde::CONNECTHandler + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::CONNECTHandler + + + +

+
+ +
+
Inherits:
+
+ GETHandler + + + show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/probe/http_method.rb
+
+ +
+ +

Overview

+
+ +

Probe that executes callback on a CONNECT

+ + +
+
+
+ + +
+ +

+ Constant Summary + collapse +

+ +
+ +
METHOD = + +
+
"CONNECT"
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Handler

+

#request, #response

+ + + +

Attributes inherited from Probe

+

#properties

+ + + +

Attributes inherited from Node

+

#remap, #root

+ + + + + + + + + +

Method Summary

+ +

Methods inherited from GETHandler

+

#process

+ + + + + + + + + +

Methods inherited from Handler

+

#initialize, #process

+ + + + + + + + + +

Methods inherited from Probe

+

#initialize, #process

+ + + + + + + + + +

Methods inherited from Node

+

#go, #initialize, #process, #reject

+
+

Constructor Details

+ +

This class inherits a constructor from Hyde::Handler

+ +
+ + +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/DELETEHandler.html b/doc/Hyde/DELETEHandler.html new file mode 100644 index 0000000..98b97d2 --- /dev/null +++ b/doc/Hyde/DELETEHandler.html @@ -0,0 +1,221 @@ + + + + + + + Class: Hyde::DELETEHandler + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::DELETEHandler + + + +

+
+ +
+
Inherits:
+
+ GETHandler + + + show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/probe/http_method.rb
+
+ +
+ +

Overview

+
+ +

Probe that executes callback on a DELETE

+ + +
+
+
+ + +
+ +

+ Constant Summary + collapse +

+ +
+ +
METHOD = + +
+
"DELETE"
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Handler

+

#request, #response

+ + + +

Attributes inherited from Probe

+

#properties

+ + + +

Attributes inherited from Node

+

#remap, #root

+ + + + + + + + + +

Method Summary

+ +

Methods inherited from GETHandler

+

#process

+ + + + + + + + + +

Methods inherited from Handler

+

#initialize, #process

+ + + + + + + + + +

Methods inherited from Probe

+

#initialize, #process

+ + + + + + + + + +

Methods inherited from Node

+

#go, #initialize, #process, #reject

+
+

Constructor Details

+ +

This class inherits a constructor from Hyde::Handler

+ +
+ + +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/DSL.html b/doc/Hyde/DSL.html new file mode 100644 index 0000000..1279e94 --- /dev/null +++ b/doc/Hyde/DSL.html @@ -0,0 +1,128 @@ + + + + + + + Module: Hyde::DSL + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: Hyde::DSL + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/dsl/path_methods.rb,
+ lib/hyde/dsl/probe_methods.rb,
lib/hyde/dsl/path_constructors.rb
+
+
+ +
+ +

Overview

+
+ +

Shared DSL methods

+ + +
+
+
+ + +

Defined Under Namespace

+

+ + + Modules: PathConstructors, PathMethods, ProbeMethods + + + + +

+ + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/DSL/PathConstructors.html b/doc/Hyde/DSL/PathConstructors.html new file mode 100644 index 0000000..83472b8 --- /dev/null +++ b/doc/Hyde/DSL/PathConstructors.html @@ -0,0 +1,995 @@ + + + + + + + Module: Hyde::DSL::PathConstructors + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: Hyde::DSL::PathConstructors + + + +

+
+ + + + + + + + + +
+
Included in:
+
PathBinding
+
+ + + +
+
Defined in:
+
lib/hyde/dsl/path_constructors.rb
+
+ +
+ +

Overview

+
+ +

Path (and subclasses) DSL constructors

+ + +
+
+
+ + +
+ + + + + + + +

+ Instance Method Summary + collapse +

+ + + + + + +
+

Instance Method Details

+ + +
+

+ + #connect(path, &setup) ⇒ Object + + + + + +

+
+ +

Create a new CONNECTHandler object

+ + +
+
+
+ + +
+ + + + +
+
+
+
+55
+56
+57
+
+
# File 'lib/hyde/dsl/path_constructors.rb', line 55
+
+def connect(path, &setup)
+  register(Hyde::CONNECTHandler.new(path, parent: @origin, &setup))
+end
+
+
+ +
+

+ + #delete(path, &setup) ⇒ Object + + + + + +

+
+ +

Create a new Hyde::DELETEHandler object

+ + +
+
+
+ + +
+ + + + +
+
+
+
+50
+51
+52
+
+
# File 'lib/hyde/dsl/path_constructors.rb', line 50
+
+def delete(path, &setup)
+  register(Hyde::DELETEHandler.new(path, parent: @origin, &setup))
+end
+
+
+ +
+

+ + #get(path, &setup) ⇒ Object + + + + + +

+
+ +

Create a new GETHandler object

+ + +
+
+
+ + +
+ + + + +
+
+
+
+30
+31
+32
+
+
# File 'lib/hyde/dsl/path_constructors.rb', line 30
+
+def get(path, &setup)
+  register(Hyde::GETHandler.new(path, parent: @origin, &setup))
+end
+
+
+ +
+

+ + #head(path, &setup) ⇒ Object + + + + + +

+
+ +

Create a new HEADHandler object

+ + +
+
+
+ + +
+ + + + +
+
+
+
+45
+46
+47
+
+
# File 'lib/hyde/dsl/path_constructors.rb', line 45
+
+def head(path, &setup)
+  register(Hyde::HEADHandler.new(path, parent: @origin, &setup))
+end
+
+
+ +
+

+ + #options(path, &setup) ⇒ Object + + + + + +

+
+ +

Create a new OPTIONSHandler object

+ + +
+
+
+ + +
+ + + + +
+
+
+
+70
+71
+72
+
+
# File 'lib/hyde/dsl/path_constructors.rb', line 70
+
+def options(path, &setup)
+  register(Hyde::OPTIONSHandler.new(path, parent: @origin, &setup))
+end
+
+
+ +
+

+ + #patch(path, &setup) ⇒ Object + + + + + +

+
+ +

Create a new PATCHHandler object

+ + +
+
+
+ + +
+ + + + +
+
+
+
+65
+66
+67
+
+
# File 'lib/hyde/dsl/path_constructors.rb', line 65
+
+def patch(path, &setup)
+  register(Hyde::PATCHHandler.new(path, parent: @origin, &setup))
+end
+
+
+ +
+

+ + #path(path, &setup) ⇒ Object + + + + + +

+
+ +

Create a new Path object

+ + +
+
+
+ + +
+ + + + +
+
+
+
+18
+19
+20
+21
+22
+
+
# File 'lib/hyde/dsl/path_constructors.rb', line 18
+
+def path(path, &setup)
+  # i don't know WHAT is wrong with this thing. it just is wrong.
+  # @sg-ignore
+  register(Hyde::Path.new(path, parent: @origin, &setup))
+end
+
+
+ +
+

+ + #post(path, &setup) ⇒ Object + + + + + +

+
+ +

create a new POSTHandler object

+ + +
+
+
+ + +
+ + + + +
+
+
+
+35
+36
+37
+
+
# File 'lib/hyde/dsl/path_constructors.rb', line 35
+
+def post(path, &setup)
+  register(Hyde::POSTHandler.new(path, parent: @origin, &setup))
+end
+
+
+ +
+

+ + #probe(path, &_setup) ⇒ Object + + + + + +

+
+ +

Create a new Probe object

+ + +
+
+
+ + +
+ + + + +
+
+
+
+25
+26
+27
+
+
# File 'lib/hyde/dsl/path_constructors.rb', line 25
+
+def probe(path, &_setup)
+  register(Hyde::Probe.new(path, parent: @origin))
+end
+
+
+ +
+

+ + #put(path, &setup) ⇒ Object + + + + + +

+
+ +

Create a new PUTHandler object

+ + +
+
+
+ + +
+ + + + +
+
+
+
+40
+41
+42
+
+
# File 'lib/hyde/dsl/path_constructors.rb', line 40
+
+def put(path, &setup)
+  register(Hyde::PUTHandler.new(path, parent: @origin, &setup))
+end
+
+
+ +
+

+ + #register(obj) ⇒ Object + + + + + +

+
+ +

Append a Node child object to the list of children

+ + +
+
+
+ + +
+ + + + +
+
+
+
+9
+10
+11
+12
+13
+14
+15
+
+
# File 'lib/hyde/dsl/path_constructors.rb', line 9
+
+def register(obj)
+  unless obj.is_a? Hyde::Node
+    raise StandardError, "register accepts node children only"
+  end
+
+  @origin.children.append(obj)
+end
+
+
+ +
+

+ + #serve(path) ⇒ Object + + + + + +

+
+ +

Create a new GETHandler that serves static files

+ + +
+
+
+ + +
+ + + + +
+
+
+
+75
+76
+77
+
+
# File 'lib/hyde/dsl/path_constructors.rb', line 75
+
+def serve(path)
+  register(Hyde::ServeHandler.new(path, parent: @origin))
+end
+
+
+ +
+

+ + #trace(path, &setup) ⇒ Object + + + + + +

+
+ +

Create a new TRACEHandler object

+ + +
+
+
+ + +
+ + + + +
+
+
+
+60
+61
+62
+
+
# File 'lib/hyde/dsl/path_constructors.rb', line 60
+
+def trace(path, &setup)
+  register(Hyde::TRACEHandler.new(path, parent: @origin, &setup))
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/DSL/PathMethods.html b/doc/Hyde/DSL/PathMethods.html new file mode 100644 index 0000000..2aba23c --- /dev/null +++ b/doc/Hyde/DSL/PathMethods.html @@ -0,0 +1,704 @@ + + + + + + + Module: Hyde::DSL::PathMethods + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: Hyde::DSL::PathMethods + + + +

+
+ + + + + + + + + +
+
Included in:
+
PathBinding
+
+ + + +
+
Defined in:
+
lib/hyde/dsl/path_methods.rb
+
+ +
+ +

Overview

+
+ +

Common path methods

+ + +
+
+
+ + +
+ + + + + + + +

+ Instance Method Summary + collapse +

+ + + + + + +
+

Instance Method Details

+ + +
+

+ + #filter(&block) {|request| ... } ⇒ Object + + + + + +

+
+ +

Add a filter to the path. Blocks path access if a filter returns false.

+ + +
+
+
+

Parameters:

+
    + +
  • + + block + + + (#call) + + + +
  • + +
+ +

Yield Parameters:

+ + +
+ + + + +
+
+
+
+55
+56
+57
+58
+
+
# File 'lib/hyde/dsl/path_methods.rb', line 55
+
+def filter(&block)
+  @origin.filter(&block)
+  block
+end
+
+
+ +
+

+ + #index(index) ⇒ Object + + + + + +

+
+ +

Set path index

+ + +
+
+
+

Parameters:

+
    + +
  • + + index + + + (Array, String) + + + +
  • + +
+ + +
+ + + + +
+
+
+
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+
+
# File 'lib/hyde/dsl/path_methods.rb', line 10
+
+def index(index)
+  case index
+  when Array
+    @origin.properties['index'] = index
+  when String
+    @origin.properties['index'] = [index]
+  else
+    raise StandardError, "index should be an Array or a String"
+  end
+end
+
+
+ +
+

+ + #postprocess(&block) {|request, response| ... } ⇒ Object + + + + + +

+
+ +

Add a postprocessor to the path.

+ + +
+
+
+

Parameters:

+
    + +
  • + + block + + + (#call) + + + +
  • + +
+ +

Yield Parameters:

+ + +
+ + + + +
+
+
+
+46
+47
+48
+49
+
+
# File 'lib/hyde/dsl/path_methods.rb', line 46
+
+def postprocess(&block)
+  @origin.postprocess(&block)
+  block
+end
+
+
+ +
+

+ + #preprocess(&block) {|request| ... } ⇒ Object + + + + + +

+
+ +

Add a preprocessor to the path. Does not modify path execution.

+ + +
+
+
+

Parameters:

+
    + +
  • + + block + + + (#call) + + + +
  • + +
+ +

Yield Parameters:

+ + +
+ + + + +
+
+
+
+37
+38
+39
+40
+
+
# File 'lib/hyde/dsl/path_methods.rb', line 37
+
+def preprocess(&block)
+  @origin.preprocess(&block)
+  block
+end
+
+
+ +
+

+ + #remap(path) ⇒ Object + + + + + +

+
+ +

Set root path (without appending matched part).

+ + +
+
+
+

Parameters:

+
    + +
  • + + path + + + (String) + + + + — +
    +

    ath [String

    +
    + +
  • + +
+ + +
+ + + + +
+
+
+
+29
+30
+31
+
+
# File 'lib/hyde/dsl/path_methods.rb', line 29
+
+def remap(path)
+  @origin.remap = path
+end
+
+
+ +
+

+ + #root(path) ⇒ Object + + + + + +

+
+ +

Set root path (appends matched part of the path).

+ + +
+
+
+

Parameters:

+
    + +
  • + + path + + + (String) + + + + — +
    +

    ath [String

    +
    + +
  • + +
+ + +
+ + + + +
+
+
+
+23
+24
+25
+
+
# File 'lib/hyde/dsl/path_methods.rb', line 23
+
+def root(path)
+  @origin.root = path
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/DSL/ProbeMethods.html b/doc/Hyde/DSL/ProbeMethods.html new file mode 100644 index 0000000..7d90e6c --- /dev/null +++ b/doc/Hyde/DSL/ProbeMethods.html @@ -0,0 +1,767 @@ + + + + + + + Module: Hyde::DSL::ProbeMethods + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: Hyde::DSL::ProbeMethods + + + +

+
+ + + + + + + + + +
+
Included in:
+
ProbeBinding
+
+ + + +
+
Defined in:
+
lib/hyde/dsl/probe_methods.rb
+
+ +
+ +

Overview

+
+ +

Common methods for Probe objects

+ + +
+
+
+ + +
+ + + + + + + +

+ Instance Method Summary + collapse +

+ + + + + + +
+

Instance Method Details

+ + +
+

+ + #bounceObject + + + + + +

+
+ +

Bounce request to the next handler

+ + +
+
+
+ +

Raises:

+
    + +
  • + + + (UncaughtThrowError) + + + + — +
    +

    throws :break to get out of the callback

    +
    + +
  • + +
+ +
+ + + + +
+
+
+
+31
+32
+33
+
+
# File 'lib/hyde/dsl/probe_methods.rb', line 31
+
+def bounce
+  throw :break
+end
+
+
+ +
+

+ + #die(errorcode, backtrace: nil) ⇒ Object + + + + + +

+
+ +

Stop execution and generate a boilerplate response with the given code

+ + +
+
+
+

Parameters:

+
    + +
  • + + errorcode + + + (Integer) + + + +
  • + +
  • + + backtrace + + + (Array(String), nil) + + + (defaults to: nil) + + +
  • + +
+ +

Raises:

+
    + +
  • + + + (UncaughtThrowError) + + + + — +
    +

    throws :finish to return back to Server

    +
    + +
  • + +
+ +
+ + + + +
+
+
+
+19
+20
+21
+22
+23
+24
+25
+26
+27
+
+
# File 'lib/hyde/dsl/probe_methods.rb', line 19
+
+def die(errorcode, backtrace: nil)
+  throw :finish, [errorcode].append(
+    *(@origin.properties["handle.#{errorcode}"] or
+      @origin.properties["handle.default"]).call(
+        errorcode,
+        backtrace: backtrace
+      )
+  )
+end
+
+
+ +
+

+ + #header(key, value) ⇒ Object + + + + + +

+
+ +

Set response header (generate response if one doesn’t exist yet)

+ + +
+
+
+

Parameters:

+
    + +
  • + + key + + + (String) + + + + — +
    +

    header name

    +
    + +
  • + +
  • + + value + + + (String) + + + + — +
    +

    header value

    +
    + +
  • + +
+ + +
+ + + + +
+
+
+
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+
+
# File 'lib/hyde/dsl/probe_methods.rb', line 47
+
+def header(key, value)
+  return status(value) if key.downcase == "status"
+
+  if key.match(/(?:[(),\/:;<=>?@\[\]{}"]|[^ -~])/)
+    raise StandardError, "header key has invalid characters"
+  end
+
+  if value.match(/[^ -~]/)
+    raise StandardError, "value key has invalid characters"
+  end
+
+  @origin.response = (@origin.response or Hyde::Response.new)
+  key = key.downcase
+  @origin.response.add_header(key, value)
+end
+
+
+ +
+

+ + #remove_header(key, value = nil) ⇒ Object + + + + + +

+
+ +

Delete a header value from the headers hash If no value is provided, deletes all key entries

+ + +
+
+
+

Parameters:

+
    + +
  • + + key + + + (String) + + + + — +
    +

    header name

    +
    + +
  • + +
  • + + value + + + (String, nil) + + + (defaults to: nil) + + + — +
    +

    header value

    +
    + +
  • + +
+ + +
+ + + + +
+
+
+
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+
+
# File 'lib/hyde/dsl/probe_methods.rb', line 67
+
+def remove_header(key, value = nil)
+  return unless @origin.response
+
+  return if key.downcase == "status"
+
+  if key.match(/(?:[(),\/:;<=>?@\[\]{}"]|[^ -~])/)
+    raise StandardError, "header key has invalid characters"
+  end
+
+  if value&.match(/[^ -~]/)
+    raise StandardError, "value key has invalid characters"
+  end
+
+  @origin.response.delete_header(key, value)
+end
+
+
+ +
+

+ + #requestHyde::Request + + + + + +

+
+ +

Get the current request

+ + +
+
+
+ +

Returns:

+ + +
+ + + + +
+
+
+
+11
+12
+13
+
+
# File 'lib/hyde/dsl/probe_methods.rb', line 11
+
+def request
+  @origin.request
+end
+
+
+ +
+

+ + #status(status) ⇒ Object + + + + Also known as: + code + + + + +

+
+ +

Set response status (generate response if one doesn’t exist yet)

+ + +
+
+
+

Parameters:

+
    + +
  • + + status + + + (Integer) + + + + — +
    +

    http status code

    +
    + +
  • + +
+ + +
+ + + + +
+
+
+
+37
+38
+39
+40
+
+
# File 'lib/hyde/dsl/probe_methods.rb', line 37
+
+def status(status)
+  @response = (@response or Hyde::Response.new)
+  @response.status = status
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/GETHandler.html b/doc/Hyde/GETHandler.html new file mode 100644 index 0000000..96dca45 --- /dev/null +++ b/doc/Hyde/GETHandler.html @@ -0,0 +1,366 @@ + + + + + + + Class: Hyde::GETHandler + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::GETHandler + + + +

+
+ +
+
Inherits:
+
+ Handler + + + show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/probe/http_method.rb
+
+ +
+ +

Overview

+
+ +

Probe that executes callback on a GET

+ + +
+
+
+ + +
+ + +

+ Constant Summary + collapse +

+ +
+ +
METHOD = + +
+
"GET"
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Handler

+

#request, #response

+ + + +

Attributes inherited from Probe

+

#properties

+ + + +

Attributes inherited from Node

+

#remap, #root

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Handler

+

#initialize

+ + + + + + + + + +

Methods inherited from Probe

+

#initialize

+ + + + + + + + + +

Methods inherited from Node

+

#go, #initialize, #reject

+
+

Constructor Details

+ +

This class inherits a constructor from Hyde::Handler

+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #process(request) ⇒ Boolean + + + + + +

+
+ +

Method callback on successful request navigation. Runs block supplied with object initialization. Request’s #splat and #param are passed to block.

+ +

Callback’s returned should be one of viable responses:

+
  • +

    Response object

    +
  • +

    An array that matches Rack return form

    +
  • +

    An array that matches old (Rack 2.x) return form

    +
  • +

    A string (returned as HTML with code 200)

    +
  • +

    false (bounces the request to next handler)

    +
+ + +
+
+
+

Parameters:

+ + +

Returns:

+
    + +
  • + + + (Boolean) + + + + — +
    +

    true if further navigation is possible

    +
    + +
  • + +
+

Raises:

+
    + +
  • + + + (UncaughtThrowError) + + + + — +
    +

    may raise if die() is called.

    +
    + +
  • + +
+ +
+ + + + +
+
+
+
+26
+27
+28
+29
+30
+31
+32
+
+
# File 'lib/hyde/probe/http_method.rb', line 26
+
+def process(request)
+  unless request.request_method.casecmp(self.class::METHOD).zero?
+    return false
+  end
+
+  super(request)
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/HEADHandler.html b/doc/Hyde/HEADHandler.html new file mode 100644 index 0000000..23d6000 --- /dev/null +++ b/doc/Hyde/HEADHandler.html @@ -0,0 +1,221 @@ + + + + + + + Class: Hyde::HEADHandler + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::HEADHandler + + + +

+
+ +
+
Inherits:
+
+ GETHandler + + + show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/probe/http_method.rb
+
+ +
+ +

Overview

+
+ +

Probe that executes callback on a HEAD

+ + +
+
+
+ + +
+ +

+ Constant Summary + collapse +

+ +
+ +
METHOD = + +
+
"HEAD"
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Handler

+

#request, #response

+ + + +

Attributes inherited from Probe

+

#properties

+ + + +

Attributes inherited from Node

+

#remap, #root

+ + + + + + + + + +

Method Summary

+ +

Methods inherited from GETHandler

+

#process

+ + + + + + + + + +

Methods inherited from Handler

+

#initialize, #process

+ + + + + + + + + +

Methods inherited from Probe

+

#initialize, #process

+ + + + + + + + + +

Methods inherited from Node

+

#go, #initialize, #process, #reject

+
+

Constructor Details

+ +

This class inherits a constructor from Hyde::Handler

+ +
+ + +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/Handler.html b/doc/Hyde/Handler.html new file mode 100644 index 0000000..97826db --- /dev/null +++ b/doc/Hyde/Handler.html @@ -0,0 +1,609 @@ + + + + + + + Class: Hyde::Handler + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::Handler + + + +

+
+ +
+
Inherits:
+
+ Probe + +
    +
  • Object
  • + + + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/probe/handler.rb
+
+ +
+ +

Overview

+
+ +

Probe that executes a callback on request

+ + +
+
+
+ + +
+

Direct Known Subclasses

+

GETHandler

+
+ + + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #request ⇒ Object + + + + + + + + + readonly + + + + + + + + + +
    +

    Returns the value of attribute request.

    +
    + +
  • + + +
  • + + + #response ⇒ Object + + + + + + + + + + + + + + + + +
    +

    Returns the value of attribute response.

    +
    + +
  • + + +
+ + + + + +

Attributes inherited from Probe

+

#properties

+ + + +

Attributes inherited from Node

+

#remap, #root

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + + + + + + + +

Methods inherited from Node

+

#go, #reject

+
+

Constructor Details

+ +
+

+ + #initialize(path, parent:, &exec) ⇒ Handler + + + + + +

+
+ +

Returns a new instance of Handler.

+ + +
+
+
+

Parameters:

+
    + +
  • + + path + + + (Object) + + + +
  • + +
  • + + parent + + + (Hyde::Node) + + + +
  • + +
  • + + exec + + + (#call) + + + +
  • + +
+ + +
+ + + + +
+
+
+
+12
+13
+14
+15
+16
+17
+
+
# File 'lib/hyde/probe/handler.rb', line 12
+
+def initialize(path, parent:, &exec)
+  super(path, parent: parent)
+  @callback = exec
+  @binding = Hyde::ProbeBinding.new(self)
+  @response = nil
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #requestObject (readonly) + + + + + +

+
+ +

Returns the value of attribute request.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+20
+21
+22
+
+
# File 'lib/hyde/probe/handler.rb', line 20
+
+def request
+  @request
+end
+
+
+ + + +
+

+ + #responseObject + + + + + +

+
+ +

Returns the value of attribute response.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+19
+20
+21
+
+
# File 'lib/hyde/probe/handler.rb', line 19
+
+def response
+  @response
+end
+
+
+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #process(request) ⇒ Boolean + + + + + +

+
+ +

Method callback on successful request navigation. Runs block supplied with object initialization. Request’s #splat and #param are passed to block.

+ +

Callback’s returned should be one of viable responses:

+
  • +

    Response object

    +
  • +

    An array that matches Rack return form

    +
  • +

    An array that matches old (Rack 2.x) return form

    +
  • +

    A string (returned as HTML with code 200)

    +
  • +

    false (bounces the request to next handler)

    +
+ + +
+
+
+

Parameters:

+ + +

Returns:

+
    + +
  • + + + (Boolean) + + + + — +
    +

    true if further navigation is possible

    +
    + +
  • + +
+

Raises:

+
    + +
  • + + + (UncaughtThrowError) + + + + — +
    +

    may raise if die() is called.

    +
    + +
  • + +
+ +
+ + + + +
+
+
+
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+
+
# File 'lib/hyde/probe/handler.rb', line 36
+
+def process(request)
+  @response = nil
+  return reject(request) unless request.path.match?(/^\/?$/)
+
+  @request = request
+  response = catch(:break) do
+    @binding.instance_exec(*request.splat,
+                           **request.param,
+                           &@callback)
+  end
+  return false unless response
+
+  if @response and [String, File, IO].include? response.class
+    @response.body = response
+    throw :finish, @response
+  end
+  throw :finish, response
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/Node.html b/doc/Hyde/Node.html new file mode 100644 index 0000000..80a0c21 --- /dev/null +++ b/doc/Hyde/Node.html @@ -0,0 +1,714 @@ + + + + + + + Class: Hyde::Node + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::Node + Abstract + + +

+
+ +
+
Inherits:
+
+ Object + +
    +
  • Object
  • + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/node.rb
+
+ +
+ +

Overview

+
+
+ This class is abstract. +
+
+ +

Abstract class that reacts to request navigation. Does nothing by default, behaviour should be overriden through #reject and #process

+ + +
+
+
+ + +
+

Direct Known Subclasses

+

Path, Probe

+
+ + + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #remap ⇒ Object + + + + + + + + + + + + + + + + +
    +

    Returns the value of attribute remap.

    +
    + +
  • + + +
  • + + + #root ⇒ Object + + + + + + + + + + + + + + + + +
    +

    Returns the value of attribute root.

    +
    + +
  • + + +
+ + + + + +

+ Instance Method Summary + collapse +

+ + + + +
+

Constructor Details

+ +
+

+ + #initialize(path, parent:) ⇒ Node + + + + + +

+
+ +

Returns a new instance of Node.

+ + +
+
+
+

Parameters:

+
    + +
  • + + path + + + (Object) + + + +
  • + +
+ + +
+ + + + +
+
+
+
+12
+13
+14
+15
+16
+17
+
+
# File 'lib/hyde/node.rb', line 12
+
+def initialize(path, parent:)
+  @pattern = Pattern.new(path).freeze
+  @properties = Hyde::Util::Lookup.new(parent&.properties)
+  @root = nil
+  @remap = false
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #remapObject + + + + + +

+
+ +

Returns the value of attribute remap.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+69
+70
+71
+
+
# File 'lib/hyde/node.rb', line 69
+
+def remap
+  @remap
+end
+
+
+ + + +
+

+ + #rootObject + + + + + +

+
+ +

Returns the value of attribute root.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+69
+70
+71
+
+
# File 'lib/hyde/node.rb', line 69
+
+def root
+  @root
+end
+
+
+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #go(request) ⇒ Boolean + + + + + +

+
+ +

Try to navigate the path. Run method callback in response.

+ + +
+
+
+

Parameters:

+ + +

Returns:

+
    + +
  • + + + (Boolean) + + + +
  • + +
+ +
+ + + + +
+
+
+
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+
+
# File 'lib/hyde/node.rb', line 38
+
+def go(request)
+  # rejected at pattern
+  return reject(request) unless @pattern.match?(request.path)
+
+  request.push_state
+  path, splat, param = @pattern.match(request.path)
+  do_filepath(request, request.path.delete_suffix(path))
+  request.path = path
+  request.splat.append(*splat)
+  request.param.merge!(param)
+  value = process(request)
+  # rejected at callback - restore state
+  request.pop_state unless value
+  # finally, return process value
+  value
+end
+
+
+ +
+

+ + #process(_request) ⇒ Object + + + + + +

+
+ +

Method callback on successful request navigation

+ + +
+
+
+

Parameters:

+ + +

Returns:

+
    + +
  • + + + + + + + +
    +

    true

    +
    + +
  • + +
+ +
+ + + + +
+
+
+
+65
+66
+67
+
+
# File 'lib/hyde/node.rb', line 65
+
+def process(_request)
+  true
+end
+
+
+ +
+

+ + #reject(_request) ⇒ Object + + + + + +

+
+ +

Method callback on failed request navigation

+ + +
+
+
+

Parameters:

+ + +

Returns:

+
    + +
  • + + + + + + + +
    +

    false

    +
    + +
  • + +
+ +
+ + + + +
+
+
+
+58
+59
+60
+
+
# File 'lib/hyde/node.rb', line 58
+
+def reject(_request)
+  false
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/OPTIONSHandler.html b/doc/Hyde/OPTIONSHandler.html new file mode 100644 index 0000000..22312de --- /dev/null +++ b/doc/Hyde/OPTIONSHandler.html @@ -0,0 +1,221 @@ + + + + + + + Class: Hyde::OPTIONSHandler + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::OPTIONSHandler + + + +

+
+ +
+
Inherits:
+
+ GETHandler + + + show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/probe/http_method.rb
+
+ +
+ +

Overview

+
+ +

Probe that executes callback on a OPTIONS

+ + +
+
+
+ + +
+ +

+ Constant Summary + collapse +

+ +
+ +
METHOD = + +
+
"OPTIONS"
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Handler

+

#request, #response

+ + + +

Attributes inherited from Probe

+

#properties

+ + + +

Attributes inherited from Node

+

#remap, #root

+ + + + + + + + + +

Method Summary

+ +

Methods inherited from GETHandler

+

#process

+ + + + + + + + + +

Methods inherited from Handler

+

#initialize, #process

+ + + + + + + + + +

Methods inherited from Probe

+

#initialize, #process

+ + + + + + + + + +

Methods inherited from Node

+

#go, #initialize, #process, #reject

+
+

Constructor Details

+ +

This class inherits a constructor from Hyde::Handler

+ +
+ + +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/PATCHHandler.html b/doc/Hyde/PATCHHandler.html new file mode 100644 index 0000000..0b44079 --- /dev/null +++ b/doc/Hyde/PATCHHandler.html @@ -0,0 +1,221 @@ + + + + + + + Class: Hyde::PATCHHandler + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::PATCHHandler + + + +

+
+ +
+
Inherits:
+
+ GETHandler + + + show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/probe/http_method.rb
+
+ +
+ +

Overview

+
+ +

Probe that executes callback on a PATCH

+ + +
+
+
+ + +
+ +

+ Constant Summary + collapse +

+ +
+ +
METHOD = + +
+
"PATCH"
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Handler

+

#request, #response

+ + + +

Attributes inherited from Probe

+

#properties

+ + + +

Attributes inherited from Node

+

#remap, #root

+ + + + + + + + + +

Method Summary

+ +

Methods inherited from GETHandler

+

#process

+ + + + + + + + + +

Methods inherited from Handler

+

#initialize, #process

+ + + + + + + + + +

Methods inherited from Probe

+

#initialize, #process

+ + + + + + + + + +

Methods inherited from Node

+

#go, #initialize, #process, #reject

+
+

Constructor Details

+ +

This class inherits a constructor from Hyde::Handler

+ +
+ + +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/POSTHandler.html b/doc/Hyde/POSTHandler.html new file mode 100644 index 0000000..debc523 --- /dev/null +++ b/doc/Hyde/POSTHandler.html @@ -0,0 +1,221 @@ + + + + + + + Class: Hyde::POSTHandler + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::POSTHandler + + + +

+
+ +
+
Inherits:
+
+ GETHandler + + + show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/probe/http_method.rb
+
+ +
+ +

Overview

+
+ +

Probe that executes callback on a POST

+ + +
+
+
+ + +
+ +

+ Constant Summary + collapse +

+ +
+ +
METHOD = + +
+
"POST"
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Handler

+

#request, #response

+ + + +

Attributes inherited from Probe

+

#properties

+ + + +

Attributes inherited from Node

+

#remap, #root

+ + + + + + + + + +

Method Summary

+ +

Methods inherited from GETHandler

+

#process

+ + + + + + + + + +

Methods inherited from Handler

+

#initialize, #process

+ + + + + + + + + +

Methods inherited from Probe

+

#initialize, #process

+ + + + + + + + + +

Methods inherited from Node

+

#go, #initialize, #process, #reject

+
+

Constructor Details

+ +

This class inherits a constructor from Hyde::Handler

+ +
+ + +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/PUTHandler.html b/doc/Hyde/PUTHandler.html new file mode 100644 index 0000000..3989021 --- /dev/null +++ b/doc/Hyde/PUTHandler.html @@ -0,0 +1,221 @@ + + + + + + + Class: Hyde::PUTHandler + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::PUTHandler + + + +

+
+ +
+
Inherits:
+
+ GETHandler + + + show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/probe/http_method.rb
+
+ +
+ +

Overview

+
+ +

Probe that executes callback on a PUT

+ + +
+
+
+ + +
+ +

+ Constant Summary + collapse +

+ +
+ +
METHOD = + +
+
"PUT"
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Handler

+

#request, #response

+ + + +

Attributes inherited from Probe

+

#properties

+ + + +

Attributes inherited from Node

+

#remap, #root

+ + + + + + + + + +

Method Summary

+ +

Methods inherited from GETHandler

+

#process

+ + + + + + + + + +

Methods inherited from Handler

+

#initialize, #process

+ + + + + + + + + +

Methods inherited from Probe

+

#initialize, #process

+ + + + + + + + + +

Methods inherited from Node

+

#go, #initialize, #process, #reject

+
+

Constructor Details

+ +

This class inherits a constructor from Hyde::Handler

+ +
+ + +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/Path.html b/doc/Hyde/Path.html index 5877c85..b2340da 100644 --- a/doc/Hyde/Path.html +++ b/doc/Hyde/Path.html @@ -69,11 +69,13 @@
Inherits:
- Object + Node
  • Object
  • + +
@@ -94,24 +96,808 @@
Defined in:
-
lib/hyde.rb
+
lib/hyde/path.rb
+

Overview

+
+ +

Primary building block of request navigation.

+ + +
+
+
+ + +
+

Direct Known Subclasses

+

Server

+
+ + +

+ Constant Summary + collapse +

+ +
+ +
Binding = + +
+
Hyde::PathBinding
+ +
+ +

Instance Attribute Summary collapse

+
    + +
  • + + + #children ⇒ Object + + + + + + + + + readonly + + + + + + + + + +
    +

    Returns the value of attribute children.

    +
    + +
  • + + +
  • + + + #properties ⇒ Object + + + + + + + + + readonly + + + + + + + + + +
    +

    Returns the value of attribute properties.

    +
    + +
  • + + +
+ + +

Attributes inherited from Node

+

#remap, #root

+ +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Node

+

#go, #reject

+
+

Constructor Details

+ +
+

+ + #initialize(path, parent:, &setup) ⇒ Path + + + + + +

+
+ +

Returns a new instance of Path.

+ + +
+
+
+

Parameters:

+
    + +
  • + + path + + + (Object) + + + + — +
    +

    Object to generate Hyde::Pattern from

    +
    + +
  • + +
  • + + parent + + + (Hyde::Node) + + + + — +
    +

    Parent object to inherit properties to

    +
    + +
  • + +
  • + + setup + + + (#call) + + + + — +
    +

    Setup block

    +
    + +
  • + +
+ + +
+ + + + +
+
+
+
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+
+
# File 'lib/hyde/path.rb', line 27
+
+def initialize(path, parent:, &setup)
+  super(path, parent: parent)
+  # Child nodes array
+  @children = []
+  # Arrays of preprocessors, postprocessors and filters
+  @preprocessors = []
+  @postprocessors = []
+  @filters = []
+
+  binding = Binding.new(self)
+  binding.instance_exec(&setup)
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #childrenObject (readonly) + + + + + +

+
+ +

Returns the value of attribute children.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+86
+87
+88
+
+
# File 'lib/hyde/path.rb', line 86
+
+def children
+  @children
+end
+
+
+ + + +
+

+ + #propertiesObject (readonly) + + + + + +

+
+ +

Returns the value of attribute properties.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+86
+87
+88
+
+
# File 'lib/hyde/path.rb', line 86
+
+def properties
+  @properties
+end
+
+
+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #filter(&block) {|request| ... } ⇒ Object + + + + + +

+
+ +

Add a filter to the path. Blocks path access if a filter returns false.

+ + +
+
+
+

Parameters:

+
    + +
  • + + block + + + (#call) + + + +
  • + +
+ +

Yield Parameters:

+ + +
+ + + + +
+
+
+
+82
+83
+84
+
+
# File 'lib/hyde/path.rb', line 82
+
+def filter(&block)
+  @filters.append(block)
+end
+
+
+ +
+

+ + #postprocess(&block) {|request, response| ... } ⇒ Object + + + + + +

+
+ +

Add a postprocessor to the path.

+ + +
+
+
+

Parameters:

+
    + +
  • + + block + + + (#call) + + + +
  • + +
+ +

Yield Parameters:

+ + +
+ + + + +
+
+
+
+74
+75
+76
+
+
# File 'lib/hyde/path.rb', line 74
+
+def postprocess(&block)
+  @postprocessors.append(block)
+end
+
+
+ +
+

+ + #preprocess(&block) {|request| ... } ⇒ Object + + + + + +

+
+ +

Add a preprocessor to the path. Does not modify path execution.

+ + +
+
+
+

Parameters:

+
    + +
  • + + block + + + (#call) + + + +
  • + +
+ +

Yield Parameters:

+ + +
+ + + + +
+
+
+
+66
+67
+68
+
+
# File 'lib/hyde/path.rb', line 66
+
+def preprocess(&block)
+  @preprocessors.append(block)
+end
+
+
+ +
+

+ + #process(request) ⇒ Boolean + + + + + +

+
+ +

Method callback on successful request navigation. Finds the next appropriate path to go to.

+ + +
+
+
+ +

Returns:

+
    + +
  • + + + (Boolean) + + + + — +
    +

    true if further navigation will be done

    +
    + +
  • + +
+

Raises:

+
    + +
  • + + + (UncaughtThrowError) + + + + — +
    +

    by default throws :response if no matches found.

    +
    + +
  • + +
+ +
+ + + + +
+
+
+
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+
+
# File 'lib/hyde/path.rb', line 44
+
+def process(request)
+  return false unless run_filters(request)
+
+  run_preprocessors(request)
+  enqueue_postprocessors(request)
+  @children.each do |x|
+    if (value = x.go(request))
+      return value
+    end
+  end
+  value = index(request)
+  return value if value
+
+  _die(404)
+rescue StandardError => e
+  _die(500, backtrace: [e.to_s] + e.backtrace)
+end
+
+
+ +
+ diff --git a/doc/Hyde/PathBinding.html b/doc/Hyde/PathBinding.html index 79b7d40..3bb71a0 100644 --- a/doc/Hyde/PathBinding.html +++ b/doc/Hyde/PathBinding.html @@ -87,6 +87,11 @@ +
+
Includes:
+
DSL::PathConstructors, DSL::PathMethods
+
+ @@ -94,24 +99,143 @@
Defined in:
-
lib/hyde.rb
+
lib/hyde/path.rb
+

Overview

+
+ +

Protected interface that provides DSL context for setup block.

+ + +
+
+
+ + +
+

Direct Known Subclasses

+

ServerBinding

+
+ + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + +

Methods included from DSL::PathMethods

+

#filter, #index, #postprocess, #preprocess, #remap, #root

+ + + + + + + + + +

Methods included from DSL::PathConstructors

+

#connect, #delete, #get, #head, #options, #patch, #path, #post, #probe, #put, #register, #serve, #trace

+
+

Constructor Details

+ +
+

+ + #initialize(path) ⇒ PathBinding + + + + + +

+
+ +

Returns a new instance of PathBinding.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+15
+16
+17
+
+
# File 'lib/hyde/path.rb', line 15
+
+def initialize(path)
+  @origin = path
+end
+
+
+ +
diff --git a/doc/Hyde/Pattern.html b/doc/Hyde/Pattern.html index b3afed9..af2693e 100644 --- a/doc/Hyde/Pattern.html +++ b/doc/Hyde/Pattern.html @@ -275,13 +275,13 @@
 
 
+16
 17
 18
-19
-20
+19 -
# File 'lib/hyde/pattern_matching.rb', line 17
+      
# File 'lib/hyde/pattern_matching.rb', line 16
 
 def initialize(pattern)
   @pattern = patternify(pattern)
@@ -379,6 +379,7 @@
       
 
 
+35
 36
 37
 38
@@ -389,11 +390,10 @@
 43
 44
 45
-46
-47
+46
-
# File 'lib/hyde/pattern_matching.rb', line 36
+      
# File 'lib/hyde/pattern_matching.rb', line 35
 
 def match(input)
   if @pattern.is_a? String
@@ -471,20 +471,20 @@
       
 
 
+52
 53
 54
 55
 56
 57
-58
-59
+58
-
# File 'lib/hyde/pattern_matching.rb', line 53
+      
# File 'lib/hyde/pattern_matching.rb', line 52
 
 def match?(input)
   if @pattern.is_a? String
-    Hyde::PatternMatching.canonicalize(input).start_with? pattern
+    Hyde::PatternMatching.canonicalize(input).start_with? @pattern
   else
     @pattern.match?(input)
   end
@@ -533,12 +533,12 @@
       
 
 
+22
 23
-24
-25
+24
-
# File 'lib/hyde/pattern_matching.rb', line 23
+      
# File 'lib/hyde/pattern_matching.rb', line 22
 
 def static?
   @static
@@ -553,7 +553,7 @@
 
 
       
diff --git a/doc/Hyde/PatternMatching.html b/doc/Hyde/PatternMatching.html
index 68cef9f..b994167 100644
--- a/doc/Hyde/PatternMatching.html
+++ b/doc/Hyde/PatternMatching.html
@@ -232,7 +232,7 @@
 
 
       
diff --git a/doc/Hyde/PatternMatching/Glob.html b/doc/Hyde/PatternMatching/Glob.html
index 3136500..6b028a6 100644
--- a/doc/Hyde/PatternMatching/Glob.html
+++ b/doc/Hyde/PatternMatching/Glob.html
@@ -641,7 +641,7 @@
 
 
       
diff --git a/doc/Hyde/PatternMatching/ReMatch.html b/doc/Hyde/PatternMatching/ReMatch.html
index 541d4cd..843c1ce 100644
--- a/doc/Hyde/PatternMatching/ReMatch.html
+++ b/doc/Hyde/PatternMatching/ReMatch.html
@@ -559,7 +559,7 @@
 
 
       
diff --git a/doc/Hyde/Probe.html b/doc/Hyde/Probe.html
new file mode 100644
index 0000000..2282d75
--- /dev/null
+++ b/doc/Hyde/Probe.html
@@ -0,0 +1,471 @@
+
+
+  
+    
+
+
+  Class: Hyde::Probe
+  
+    — Documentation by YARD 0.9.34
+  
+
+
+  
+
+  
+
+
+
+
+  
+
+  
+
+
+  
+  
+    
+
+    
+ + +

Class: Hyde::Probe + + + +

+
+ +
+
Inherits:
+
+ Node + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/probe.rb
+
+ +
+ +

Overview

+
+ +

Test probe. Also base for all “reactive” nodes.

+ + +
+
+
+ + +
+

Direct Known Subclasses

+

Handler, ServeHandler

+
+ + + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #properties ⇒ Object + + + + + + + + + readonly + + + + + + + + + +
    +

    Returns the value of attribute properties.

    +
    + +
  • + + +
+ + + + + +

Attributes inherited from Node

+

#remap, #root

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Node

+

#go, #reject

+
+

Constructor Details

+ +
+

+ + #initialize(path, parent:) ⇒ Probe + + + + + +

+
+ +

Returns a new instance of Probe.

+ + +
+
+
+

Parameters:

+
    + +
  • + + path + + + (Object) + + + +
  • + +
  • + + parent + + + (Hyde::Node) + + + +
  • + +
+ + +
+ + + + +
+
+
+
+23
+24
+25
+
+
# File 'lib/hyde/probe.rb', line 23
+
+def initialize(path, parent:)
+  super(path, parent: parent)
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #propertiesObject (readonly) + + + + + +

+
+ +

Returns the value of attribute properties.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+27
+28
+29
+
+
# File 'lib/hyde/probe.rb', line 27
+
+def properties
+  @properties
+end
+
+
+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #process(request) ⇒ Boolean + + + + + +

+
+ +

Method callback on successful request navigation. Throws an error upon reaching the path. This behaviour should only be used internally.

+ + +
+
+
+

Parameters:

+ + +

Returns:

+
    + +
  • + + + (Boolean) + + + + — +
    +

    true if further navigation is possible

    +
    + +
  • + +
+

Raises:

+
    + +
  • + + + (StandardError) + + + +
  • + +
+ +
+ + + + +
+
+
+
+35
+36
+37
+38
+39
+40
+41
+42
+
+
# File 'lib/hyde/probe.rb', line 35
+
+def process(request)
+  return reject(request) unless request.path.match?(/^\/?$/)
+
+  raise StandardError, <<~STREND
+    probe reached #{request.splat.inspect}, #{request.param.inspect}
+    #{request.pretty_inspect}
+  STREND
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/ProbeBinding.html b/doc/Hyde/ProbeBinding.html new file mode 100644 index 0000000..84d9a19 --- /dev/null +++ b/doc/Hyde/ProbeBinding.html @@ -0,0 +1,219 @@ + + + + + + + Class: Hyde::ProbeBinding + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::ProbeBinding + + + +

+
+ +
+
Inherits:
+
+ Object + +
    +
  • Object
  • + + + +
+ show all + +
+
+ + + + + + +
+
Includes:
+
DSL::ProbeMethods
+
+ + + + + + +
+
Defined in:
+
lib/hyde/probe/binding.rb
+
+ +
+ + + + + + + + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods included from DSL::ProbeMethods

+

#bounce, #die, #header, #remove_header, #request, #status

+
+

Constructor Details

+ +
+

+ + #initialize(origin) ⇒ ProbeBinding + + + + + +

+
+ +

Returns a new instance of ProbeBinding.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+7
+8
+9
+
+
# File 'lib/hyde/probe/binding.rb', line 7
+
+def initialize(origin)
+  @origin = origin
+end
+
+
+ +
+ + +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/Request.html b/doc/Hyde/Request.html new file mode 100644 index 0000000..9efd6fe --- /dev/null +++ b/doc/Hyde/Request.html @@ -0,0 +1,1505 @@ + + + + + + + Class: Hyde::Request + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::Request + + + +

+
+ +
+
Inherits:
+
+ Object + +
    +
  • Object
  • + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/request.rb
+
+ +
+ +

Overview

+
+ +

Request wrapper for Rack protocol

+ + +
+
+
+ + +
+ + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #filepath ⇒ Object + + + + + + + + + + + + + + + + +
    +

    Returns the value of attribute filepath.

    +
    + +
  • + + +
  • + + + #headers ⇒ Object + + + + + + + + + readonly + + + + + + + + + +
    +

    Returns the value of attribute headers.

    +
    + +
  • + + +
  • + + + #param ⇒ Object + + + + + + + + + readonly + + + + + + + + + +
    +

    Returns the value of attribute param.

    +
    + +
  • + + +
  • + + + #path ⇒ Object + + + + + + + + + + + + + + + + +
    +

    Returns the value of attribute path.

    +
    + +
  • + + +
  • + + + #path_info ⇒ Object + + + + + + + + + readonly + + + + + + + + + +
    +

    Returns the value of attribute path_info.

    +
    + +
  • + + +
  • + + + #postprocessors ⇒ Object + + + + + + + + + readonly + + + + + + + + + +
    +

    Returns the value of attribute postprocessors.

    +
    + +
  • + + +
  • + + + #query ⇒ Object + + + + + + + + + readonly + + + + + + + + + +
    +

    Returns the value of attribute query.

    +
    + +
  • + + +
  • + + + #request_method ⇒ Object + + + + + + + + + readonly + + + + + + + + + +
    +

    Returns the value of attribute request_method.

    +
    + +
  • + + +
  • + + + #script_name ⇒ Object + + + + + + + + + readonly + + + + + + + + + +
    +

    Returns the value of attribute script_name.

    +
    + +
  • + + +
  • + + + #server_name ⇒ Object + + + + + + + + + readonly + + + + + + + + + +
    +

    Returns the value of attribute server_name.

    +
    + +
  • + + +
  • + + + #server_port ⇒ Object + + + + + + + + + readonly + + + + + + + + + +
    +

    Returns the value of attribute server_port.

    +
    + +
  • + + +
  • + + + #server_protocol ⇒ Object + + + + + + + + + readonly + + + + + + + + + +
    +

    Returns the value of attribute server_protocol.

    +
    + +
  • + + +
  • + + + #splat ⇒ Object + + + + + + + + + readonly + + + + + + + + + +
    +

    Returns the value of attribute splat.

    +
    + +
  • + + +
+ + + + + +

+ Instance Method Summary + collapse +

+ + + + +
+

Constructor Details

+ +
+

+ + #initialize(env) ⇒ Request + + + + + +

+
+ +

Returns a new instance of Request.

+ + +
+
+
+

Parameters:

+
    + +
  • + + env + + + (Array) + + + +
  • + +
+ + +
+ + + + +
+
+
+
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+
+
# File 'lib/hyde/request.rb', line 10
+
+def initialize(env)
+  # Should not be used under regular circumstances or depended upon.
+  @_original_env = env
+  # Rack environment variable bindings. Should be public and frozen.
+  init_request_params(env)
+  # Query parsing
+  @query = Query.new(@query_string)
+  # Pattern matching parameters. Public, readable, unfrozen.
+  @param = {}
+  @splat = []
+  # Traversal route. Public and writable.
+  @path = URI.decode_www_form_component(env["PATH_INFO"].dup)
+  # File serving path. Public and writable.
+  @filepath = "/"
+  # Encapsulates all rack variables. Should not be public.
+  @rack = init_rack_vars(env)
+  # Internal navigation states. Private.
+  @states = []
+  # Postprocessors for current request
+  @postprocessors = []
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #filepathObject + + + + + +

+
+ +

Returns the value of attribute filepath.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+59
+60
+61
+
+
# File 'lib/hyde/request.rb', line 59
+
+def filepath
+  @filepath
+end
+
+
+ + + +
+

+ + #headersObject (readonly) + + + + + +

+
+ +

Returns the value of attribute headers.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+56
+57
+58
+
+
# File 'lib/hyde/request.rb', line 56
+
+def headers
+  @headers
+end
+
+
+ + + +
+

+ + #paramObject (readonly) + + + + + +

+
+ +

Returns the value of attribute param.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+56
+57
+58
+
+
# File 'lib/hyde/request.rb', line 56
+
+def param
+  @param
+end
+
+
+ + + +
+

+ + #pathObject + + + + + +

+
+ +

Returns the value of attribute path.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+59
+60
+61
+
+
# File 'lib/hyde/request.rb', line 59
+
+def path
+  @path
+end
+
+
+ + + +
+

+ + #path_infoObject (readonly) + + + + + +

+
+ +

Returns the value of attribute path_info.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+56
+57
+58
+
+
# File 'lib/hyde/request.rb', line 56
+
+def path_info
+  @path_info
+end
+
+
+ + + +
+

+ + #postprocessorsObject (readonly) + + + + + +

+
+ +

Returns the value of attribute postprocessors.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+56
+57
+58
+
+
# File 'lib/hyde/request.rb', line 56
+
+def postprocessors
+  @postprocessors
+end
+
+
+ + + +
+

+ + #queryObject (readonly) + + + + + +

+
+ +

Returns the value of attribute query.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+56
+57
+58
+
+
# File 'lib/hyde/request.rb', line 56
+
+def query
+  @query
+end
+
+
+ + + +
+

+ + #request_methodObject (readonly) + + + + + +

+
+ +

Returns the value of attribute request_method.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+56
+57
+58
+
+
# File 'lib/hyde/request.rb', line 56
+
+def request_method
+  @request_method
+end
+
+
+ + + +
+

+ + #script_nameObject (readonly) + + + + + +

+
+ +

Returns the value of attribute script_name.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+56
+57
+58
+
+
# File 'lib/hyde/request.rb', line 56
+
+def script_name
+  @script_name
+end
+
+
+ + + +
+

+ + #server_nameObject (readonly) + + + + + +

+
+ +

Returns the value of attribute server_name.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+56
+57
+58
+
+
# File 'lib/hyde/request.rb', line 56
+
+def server_name
+  @server_name
+end
+
+
+ + + +
+

+ + #server_portObject (readonly) + + + + + +

+
+ +

Returns the value of attribute server_port.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+56
+57
+58
+
+
# File 'lib/hyde/request.rb', line 56
+
+def server_port
+  @server_port
+end
+
+
+ + + +
+

+ + #server_protocolObject (readonly) + + + + + +

+
+ +

Returns the value of attribute server_protocol.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+56
+57
+58
+
+
# File 'lib/hyde/request.rb', line 56
+
+def server_protocol
+  @server_protocol
+end
+
+
+ + + +
+

+ + #splatObject (readonly) + + + + + +

+
+ +

Returns the value of attribute splat.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+56
+57
+58
+
+
# File 'lib/hyde/request.rb', line 56
+
+def splat
+  @splat
+end
+
+
+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #bodynil, String + + + + + +

+
+ +

Returns request body (if POST data exists)

+ + +
+
+
+ +

Returns:

+
    + +
  • + + + (nil, String) + + + +
  • + +
+ +
+ + + + +
+
+
+
+42
+43
+44
+
+
# File 'lib/hyde/request.rb', line 42
+
+def body
+  @body ||= @rack.input&.gets
+end
+
+
+ +
+

+ + #pop_stateObject + + + + + +

+
+ +

Load last navigation state (path, splat, param) from state stack

+ + +
+
+
+ + +
+ + + + +
+
+
+
+52
+53
+54
+
+
# File 'lib/hyde/request.rb', line 52
+
+def pop_state
+  @path, @param, @splat, @filepath = @states.pop
+end
+
+
+ +
+

+ + #push_stateObject + + + + + +

+
+ +

Push current navigation state (path, splat, param) onto state stack

+ + +
+
+
+ + +
+ + + + +
+
+
+
+47
+48
+49
+
+
# File 'lib/hyde/request.rb', line 47
+
+def push_state
+  @states.push([@path, @param.dup, @splat.dup, @filepath.dup])
+end
+
+
+ +
+

+ + #run_postprocessors(response) ⇒ Object + + + + + +

+
+ +

Run postprocessors

+ + +
+
+
+

Parameters:

+ + + +
+ + + + +
+
+
+
+34
+35
+36
+37
+38
+
+
# File 'lib/hyde/request.rb', line 34
+
+def run_postprocessors(response)
+  @postprocessors.each do |postproc|
+    postproc.call(self, response)
+  end
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/Response.html b/doc/Hyde/Response.html new file mode 100644 index 0000000..e5fa4ba --- /dev/null +++ b/doc/Hyde/Response.html @@ -0,0 +1,1177 @@ + + + + + + + Class: Hyde::Response + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::Response + + + +

+
+ +
+
Inherits:
+
+ Object + +
    +
  • Object
  • + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/response.rb
+
+ +
+ +

Overview

+
+ +

Rack protocol response wrapper.

+ + +
+
+
+ + +
+ + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #body ⇒ Object + + + + + + + + + + + + + + + + +
    +

    Returns the value of attribute body.

    +
    + +
  • + + +
  • + + + #chunk_size ⇒ Object + + + + + + + + + + + + + + + + +
    +

    Returns the value of attribute chunk_size.

    +
    + +
  • + + +
  • + + + #headers ⇒ Object + + + + + + + + + + + + + + + + +
    +

    Returns the value of attribute headers.

    +
    + +
  • + + +
  • + + + #status ⇒ Object + + + + + + + + + + + + + + + + +
    +

    Returns the value of attribute status.

    +
    + +
  • + + +
+ + + + + +

+ Class Method Summary + collapse +

+ + + +

+ Instance Method Summary + collapse +

+ + + + +
+

Constructor Details

+ +
+

+ + #initialize(response = nil) ⇒ Response + + + + + +

+
+ +

Returns a new instance of Response.

+ + +
+
+
+

Parameters:

+
    + +
  • + + response + + + (Array(Integer, Hash, Array), nil) + + + (defaults to: nil) + + +
  • + +
+ + +
+ + + + +
+
+
+
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
+
# File 'lib/hyde/response.rb', line 11
+
+def initialize(response = nil)
+  if response
+    @status = response[0]
+    @headers = response[1]
+    @body = response[2]
+  else
+    @status = 404
+    @headers = {}
+    @body = []
+  end
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #bodyObject + + + + + +

+
+ +

Returns the value of attribute body.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+71
+72
+73
+
+
# File 'lib/hyde/response.rb', line 71
+
+def body
+  @body
+end
+
+
+ + + +
+

+ + #chunk_sizeObject + + + + + +

+
+ +

Returns the value of attribute chunk_size.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+8
+9
+10
+
+
# File 'lib/hyde/response.rb', line 8
+
+def chunk_size
+  @chunk_size
+end
+
+
+ + + +
+

+ + #headersObject + + + + + +

+
+ +

Returns the value of attribute headers.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+71
+72
+73
+
+
# File 'lib/hyde/response.rb', line 71
+
+def headers
+  @headers
+end
+
+
+ + + +
+

+ + #statusObject + + + + + +

+
+ +

Returns the value of attribute status.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+71
+72
+73
+
+
# File 'lib/hyde/response.rb', line 71
+
+def status
+  @status
+end
+
+
+ +
+ + +
+

Class Method Details

+ + +
+

+ + .chunk_body(text) ⇒ Array(String) + + + + + +

+
+ +

Turn body into array of chunks

+ + +
+
+
+

Parameters:

+
    + +
  • + + text + + + (String) + + + +
  • + +
+ +

Returns:

+
    + +
  • + + + (Array(String)) + + + +
  • + +
+ +
+ + + + +
+
+
+
+96
+97
+98
+
+
# File 'lib/hyde/response.rb', line 96
+
+def self.chunk_body(text)
+  text.chars.each_slice(@chunk_size).map(&:join)
+end
+
+
+ +
+

+ + .convert(obj) ⇒ Object + + + + + +

+
+ +

Ensure response correctness

+ + +
+
+
+

Parameters:

+ + +

Returns:

+
    + +
  • + + + + + + + +
    +

    Response

    +
    + +
  • + +
+ +
+ + + + +
+
+
+
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+
+
# File 'lib/hyde/response.rb', line 76
+
+def self.convert(obj)
+  case obj
+  when Response
+    obj.validate
+  when Array
+    Response.new(obj).validate
+  when String, File, IO
+    Response.new([200,
+                  {
+                    "content-type" => "text/html"
+                  },
+                  obj]).validate
+  else
+    Response.new([404, {}, []])
+  end
+end
+
+
+ +
+ +
+

Instance Method Details

+ + +
+

+ + #add_header(key, value) ⇒ Object + + + + + +

+
+ +

Add a header to the headers hash

+ + +
+
+
+

Parameters:

+
    + +
  • + + key + + + (String) + + + + — +
    +

    header name

    +
    + +
  • + +
  • + + value + + + (String) + + + + — +
    +

    header value

    +
    + +
  • + +
+ + +
+ + + + +
+
+
+
+49
+50
+51
+52
+53
+54
+55
+56
+57
+
+
# File 'lib/hyde/response.rb', line 49
+
+def add_header(key, value)
+  if @headers[key].is_a? String
+    @headers[key] = [@headers[key], value]
+  elsif @headers[key].is_a? Array
+    @headers[key].append(value)
+  else
+    @headers[key] = value
+  end
+end
+
+
+ +
+

+ + #delete_header(key, value = nil) ⇒ Object + + + + + +

+
+ +

Delete a header value from the headers hash If no value is provided, deletes all key entries

+ + +
+
+
+

Parameters:

+
    + +
  • + + key + + + (String) + + + + — +
    +

    header name

    +
    + +
  • + +
  • + + value + + + (String, nil) + + + (defaults to: nil) + + + — +
    +

    header value

    +
    + +
  • + +
+ + +
+ + + + +
+
+
+
+63
+64
+65
+66
+67
+68
+69
+
+
# File 'lib/hyde/response.rb', line 63
+
+def delete_header(key, value = nil)
+  if value and @response[key]
+    @response[key].delete(value)
+  else
+    @response.delete(key)
+  end
+end
+
+
+ +
+

+ + #finalizeArray(Integer,Hash,Array) + + + + + +

+
+ +

Return internal representation of Rack response

+ + +
+
+
+ +

Returns:

+
    + +
  • + + + (Array(Integer,Hash,Array)) + + + +
  • + +
+ +
+ + + + +
+
+
+
+25
+26
+27
+
+
# File 'lib/hyde/response.rb', line 25
+
+def finalize
+  [@status, @headers, @body]
+end
+
+
+ +
+

+ + #validateHyde::Response + + + + + +

+
+ +

Make internal representation conformant

+ + +
+
+
+ +

Returns:

+ + +
+ + + + +
+
+
+
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+
+
# File 'lib/hyde/response.rb', line 31
+
+def validate
+  if [204, 304].include?(@status) or (100..199).include?(@status)
+    @headers.delete "content-length"
+    @headers.delete "content-type"
+    @body = []
+  elsif @headers.empty?
+    @headers = {
+      "content-length" => content_size,
+      "content-type" => "text/html"
+    }
+  end
+  @body = self.class.chunk_body(@body) if @body.is_a? String
+  self
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/ServeHandler.html b/doc/Hyde/ServeHandler.html new file mode 100644 index 0000000..eb34b2e --- /dev/null +++ b/doc/Hyde/ServeHandler.html @@ -0,0 +1,476 @@ + + + + + + + Class: Hyde::ServeHandler + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::ServeHandler + + + +

+
+ +
+
Inherits:
+
+ Probe + +
    +
  • Object
  • + + + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/probe/serve_handler.rb
+
+ +
+ +

Overview

+
+ +

Probe that sends files from a location

+ + +
+
+
+ + +
+ + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #response ⇒ Object + + + + + + + + + + + + + + + + +
    +

    Returns the value of attribute response.

    +
    + +
  • + + +
+ + + + + +

Attributes inherited from Probe

+

#properties

+ + + +

Attributes inherited from Node

+

#remap, #root

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + + + + + + + +

Methods inherited from Node

+

#go, #reject

+
+

Constructor Details

+ +
+

+ + #initialize(path, parent:) ⇒ ServeHandler + + + + + +

+
+ +

Returns a new instance of ServeHandler.

+ + +
+
+
+

Parameters:

+
    + +
  • + + path + + + (Object) + + + +
  • + +
  • + + parent + + + (Hyde::Node) + + + +
  • + +
  • + + exec + + + (#call) + + + +
  • + +
+ + +
+ + + + +
+
+
+
+12
+13
+14
+
+
# File 'lib/hyde/probe/serve_handler.rb', line 12
+
+def initialize(path, parent:)
+  super(path, parent: parent)
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #responseObject + + + + + +

+
+ +

Returns the value of attribute response.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+16
+17
+18
+
+
# File 'lib/hyde/probe/serve_handler.rb', line 16
+
+def response
+  @response
+end
+
+
+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #process(request) ⇒ Boolean + + + + + +

+
+ +

Method callback on successful request navigation. Tries to serve files matched by handler

+ + +
+
+
+

Parameters:

+ + +

Returns:

+
    + +
  • + + + (Boolean) + + + + — +
    +

    true if file was found

    +
    + +
  • + +
+ +
+ + + + +
+
+
+
+22
+23
+24
+25
+26
+27
+28
+29
+
+
# File 'lib/hyde/probe/serve_handler.rb', line 22
+
+def process(request)
+  path = File.expand_path(request.filepath)
+  return unless path.start_with? @properties["path"]
+
+  File.open(path.delete_suffix("/"))
+rescue StandardError
+  false
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/Server.html b/doc/Hyde/Server.html new file mode 100644 index 0000000..0166536 --- /dev/null +++ b/doc/Hyde/Server.html @@ -0,0 +1,441 @@ + + + + + + + Class: Hyde::Server + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::Server + + + +

+
+ +
+
Inherits:
+
+ Path + +
    +
  • Object
  • + + + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/server.rb
+
+ +
+ +

Overview

+
+ +

A specialized path that can be used directly as a Rack application.

+ + +
+
+
+ + +
+ +

+ Constant Summary + collapse +

+ +
+ +
Binding = + +
+
ServerBinding
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Path

+

#children, #properties

+ + + +

Attributes inherited from Node

+

#remap, #root

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Path

+

#filter, #postprocess, #preprocess, #process

+ + + + + + + + + +

Methods inherited from Node

+

#go, #process, #reject

+
+

Constructor Details

+ +
+

+ + #initialize(parent: nil, &setup) ⇒ Server + + + + + +

+
+ +

Returns a new instance of Server.

+ + +
+
+
+

Parameters:

+
    + +
  • + + parent + + + (Hyde::Node, nil) + + + (defaults to: nil) + + + — +
    +

    Parent object to inherit properties to

    +
    + +
  • + +
  • + + setup + + + (#call) + + + + — +
    +

    Setup block

    +
    + +
  • + +
+ + +
+ + + + +
+
+
+
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+
+
# File 'lib/hyde/server.rb', line 18
+
+def initialize(parent: nil, &setup)
+  super("", parent: parent, &setup)
+  return if parent
+
+  {
+    "index" => [],
+    "handle.default" => proc do |code, backtrace: nil|
+      page = Hyde::Util.default_error_page(code, backtrace)
+      headers = {
+        "content-length": page.length,
+        "content-type": "text/html"
+      }
+      [headers, page]
+    end,
+    "path" => "/"
+  }.each { |k, v| @properties[k] = v unless @properties[k] }
+end
+
+
+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #call(env) ⇒ Array(Integer,Hash,Array) + + + + + +

+
+ +

Rack ingress point. This should not be called under any circumstances twice in the same application, although server nesting for the purpose of creating virtual hosts is allowed.

+ + +
+
+
+

Parameters:

+
    + +
  • + + env + + + (Hash) + + + +
  • + +
+ +

Returns:

+
    + +
  • + + + (Array(Integer,Hash,Array)) + + + +
  • + +
+ +
+ + + + +
+
+
+
+41
+42
+43
+44
+45
+46
+47
+48
+
+
# File 'lib/hyde/server.rb', line 41
+
+def call(env)
+  request = Hyde::Request.new(env)
+  response = catch(:finish) do
+    go(request)
+  end
+  request.run_postprocessors(response)
+  Response.convert(response).finalize
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/ServerBinding.html b/doc/Hyde/ServerBinding.html new file mode 100644 index 0000000..ce91faf --- /dev/null +++ b/doc/Hyde/ServerBinding.html @@ -0,0 +1,162 @@ + + + + + + + Class: Hyde::ServerBinding + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::ServerBinding + + + +

+
+ +
+
Inherits:
+
+ PathBinding + + + show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/server.rb
+
+ +
+ + + + + + + + + + + + + + + +

Method Summary

+ +

Methods inherited from PathBinding

+

#initialize

+ + + + + + + + + +

Methods included from DSL::PathMethods

+

#filter, #index, #postprocess, #preprocess, #remap, #root

+ + + + + + + + + +

Methods included from DSL::PathConstructors

+

#connect, #delete, #get, #head, #options, #patch, #path, #post, #probe, #put, #register, #serve, #trace

+
+

Constructor Details

+ +

This class inherits a constructor from Hyde::PathBinding

+ +
+ + +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/TRACEHandler.html b/doc/Hyde/TRACEHandler.html new file mode 100644 index 0000000..30a8e7e --- /dev/null +++ b/doc/Hyde/TRACEHandler.html @@ -0,0 +1,221 @@ + + + + + + + Class: Hyde::TRACEHandler + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::TRACEHandler + + + +

+
+ +
+
Inherits:
+
+ GETHandler + + + show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/probe/http_method.rb
+
+ +
+ +

Overview

+
+ +

Probe that executes callback on a TRACE

+ + +
+
+
+ + +
+ +

+ Constant Summary + collapse +

+ +
+ +
METHOD = + +
+
"TRACE"
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Handler

+

#request, #response

+ + + +

Attributes inherited from Probe

+

#properties

+ + + +

Attributes inherited from Node

+

#remap, #root

+ + + + + + + + + +

Method Summary

+ +

Methods inherited from GETHandler

+

#process

+ + + + + + + + + +

Methods inherited from Handler

+

#initialize, #process

+ + + + + + + + + +

Methods inherited from Probe

+

#initialize, #process

+ + + + + + + + + +

Methods inherited from Node

+

#go, #initialize, #process, #reject

+
+

Constructor Details

+ +

This class inherits a constructor from Hyde::Handler

+ +
+ + +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/Util.html b/doc/Hyde/Util.html new file mode 100644 index 0000000..c44f07c --- /dev/null +++ b/doc/Hyde/Util.html @@ -0,0 +1,487 @@ + + + + + + + Module: Hyde::Util + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: Hyde::Util + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/util/lookup.rb,
+ lib/hyde/util/html.rb,
lib/hyde/util/query.rb
+
+
+ +
+ +

Overview

+
+ +

Various things that exists for purely logical reasons

+ + +
+
+
+ + +

Defined Under Namespace

+

+ + + + + Classes: Lookup, Query + + +

+ + +

+ Constant Summary + collapse +

+ +
+ +
HTTP_STATUS = +
+
+ +

HTTP status codes and descriptions Taken from WEBrick https://github.com/ruby/webrick/blob/master/lib/webrick/httpstatus.rb

+ + +
+
+
+ + +
+
+
{
+  100 => 'Continue',
+  101 => 'Switching Protocols',
+  200 => 'OK',
+  201 => 'Created',
+  202 => 'Accepted',
+  203 => 'Non-Authoritative Information',
+  204 => 'No Content',
+  205 => 'Reset Content',
+  206 => 'Partial Content',
+  207 => 'Multi-Status',
+  300 => 'Multiple Choices',
+  301 => 'Moved Permanently',
+  302 => 'Found',
+  303 => 'See Other',
+  304 => 'Not Modified',
+  305 => 'Use Proxy',
+  307 => 'Temporary Redirect',
+  400 => 'Bad Request',
+  401 => 'Unauthorized',
+  402 => 'Payment Required',
+  403 => 'Forbidden',
+  404 => 'Not Found',
+  405 => 'Method Not Allowed',
+  406 => 'Not Acceptable',
+  407 => 'Proxy Authentication Required',
+  408 => 'Request Timeout',
+  409 => 'Conflict',
+  410 => 'Gone',
+  411 => 'Length Required',
+  412 => 'Precondition Failed',
+  413 => 'Request Entity Too Large',
+  414 => 'Request-URI Too Large',
+  415 => 'Unsupported Media Type',
+  416 => 'Request Range Not Satisfiable',
+  417 => 'Expectation Failed',
+  422 => 'Unprocessable Entity',
+  423 => 'Locked',
+  424 => 'Failed Dependency',
+  426 => 'Upgrade Required',
+  428 => 'Precondition Required',
+  429 => 'Too Many Requests',
+  431 => 'Request Header Fields Too Large',
+  451 => 'Unavailable For Legal Reasons',
+  500 => 'Internal Server Error',
+  501 => 'Not Implemented',
+  502 => 'Bad Gateway',
+  503 => 'Service Unavailable',
+  504 => 'Gateway Timeout',
+  505 => 'HTTP Version Not Supported',
+  507 => 'Insufficient Storage',
+  511 => 'Network Authentication Required'
+}.freeze
+ +
+ + + + + + + + + +

+ Class Method Summary + collapse +

+ + + + + + +
+

Class Method Details

+ + +
+

+ + .default_error_page(code, backtrace) ⇒ String + + + + + +

+
+ +

Default error page for Hyde

+ + +
+
+
+

Parameters:

+
    + +
  • + + code + + + (Integer) + + + + — +
    +

    HTTP Status code

    +
    + +
  • + +
  • + + backtrace + + + (Array(String), nil) + + + + — +
    +

    Message to show in backtrace window

    +
    + +
  • + +
+ +

Returns:

+
    + +
  • + + + (String) + + + +
  • + +
+ +
+ + + + +
+
+
+
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+
+
# File 'lib/hyde/util/html.rb', line 78
+
+def self.default_error_page(code, backtrace)
+  backtrace ||= []
+  errortext = HTTP_STATUS[code]
+  <<~HTMLEOF
+    <!DOCTYPE HTML>
+    <html>
+        <head>
+            <title>#{Util.escape_html(errortext)}</title>
+            <style> .header {padding: 0.5rem; overflow: auto;} .title { font-weight: bolder; font-size: 48px; margin: 10px 10px; text-shadow: 1px 1px 1px #202222, 2px 2px 2px #404444; float: left } body { margin: 0; } .text { font-size 1rem; } .small { color: #7D7D7D; font-size: 12px;} .code { font-family: monospace; font-size: 0.7rem; } </style>
+        </head>
+        <body>
+            <div class="header">
+                <p class="title">HYDE</p>
+                <p style="float: right"><a href="https://adastra7.net/git/yessiest/hyde">Source code</a></p>
+            </div>
+            <div style="padding: 0.5rem">
+                <p class="text">#{Util.escape_html(errortext)}</p>
+                <pre><code class="text code">
+    #{backtrace.map(&Util.method(:escape_html)).join('<br/>')}
+                </code></pre>
+                <hr/>
+                <p class="small">#{Util.escape_html(Hyde::VLINE)}</p>
+            </div>
+        </body>
+    </html>
+  HTMLEOF
+end
+
+
+ +
+

+ + .escape_html(str) ⇒ String + + + + + +

+
+ +

Return string with escaped HTML entities

+ + +
+
+
+

Parameters:

+
    + +
  • + + str + + + (String) + + + +
  • + +
+ +

Returns:

+
    + +
  • + + + (String) + + + +
  • + +
+ +
+ + + + +
+
+
+
+64
+65
+66
+67
+68
+69
+70
+
+
# File 'lib/hyde/util/html.rb', line 64
+
+def self.escape_html(str)
+  str.gsub("&", "&amp;")
+     .gsub("<", "&lt;")
+     .gsub(">", "&gt;")
+     .gsub("\"", "&quot;")
+     .gsub("'", "&#39;")
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/Util/Lookup.html b/doc/Hyde/Util/Lookup.html new file mode 100644 index 0000000..6af3b6f --- /dev/null +++ b/doc/Hyde/Util/Lookup.html @@ -0,0 +1,613 @@ + + + + + + + Class: Hyde::Util::Lookup + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::Util::Lookup + + + +

+
+ +
+
Inherits:
+
+ Object + +
    +
  • Object
  • + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/util/lookup.rb
+
+ +
+ +

Overview

+
+ +

Value container with recursive lookup

+ + +
+
+
+ + +
+ + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #parent ⇒ Object + + + + + + + + + + + + + + + + +
    +

    Returns the value of attribute parent.

    +
    + +
  • + + +
+ + + + + +

+ Class Method Summary + collapse +

+ + + +

+ Instance Method Summary + collapse +

+ + + + +
+

Constructor Details

+ +
+

+ + #initialize(parent = nil, hash = {}) ⇒ Lookup + + + + + +

+
+ +

Returns a new instance of Lookup.

+ + +
+
+
+

Parameters:

+
    + +
  • + + parent + + + (Lookup, nil) + + + (defaults to: nil) + + +
  • + +
+ + +
+ + + + +
+
+
+
+9
+10
+11
+12
+
+
# File 'lib/hyde/util/lookup.rb', line 9
+
+def initialize(parent = nil, hash = {})
+  @parent = (parent or Hash.new(nil))
+  @storage = hash
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #parentObject + + + + + +

+
+ +

Returns the value of attribute parent.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+34
+35
+36
+
+
# File 'lib/hyde/util/lookup.rb', line 34
+
+def parent
+  @parent
+end
+
+
+ +
+ + +
+

Class Method Details

+ + +
+

+ + .[](hash) ⇒ Object + + + + + +

+
+ +

Initialize a Lookup from Hash

+ + +
+
+
+

Parameters:

+
    + +
  • + + hash + + + (Hash) + + + +
  • + +
+ + +
+ + + + +
+
+
+
+16
+17
+18
+
+
# File 'lib/hyde/util/lookup.rb', line 16
+
+def self.[](hash)
+  Lookup.new(nil, Hash[hash])
+end
+
+
+ +
+ +
+

Instance Method Details

+ + +
+

+ + #[](key) ⇒ Object? + + + + + +

+
+ +

Get a value by key

+ + +
+
+
+

Parameters:

+
    + +
  • + + key + + + (#hash) + + + + — +
    +

    key for value

    +
    + +
  • + +
+ +

Returns:

+
    + +
  • + + + (Object, nil) + + + +
  • + +
+ +
+ + + + +
+
+
+
+23
+24
+25
+
+
# File 'lib/hyde/util/lookup.rb', line 23
+
+def [](key)
+  @storage[key] or @parent[key]
+end
+
+
+ +
+

+ + #[]=(key, value) ⇒ Object + + + + + +

+
+ +

Set a value by key

+ + +
+
+
+

Parameters:

+
    + +
  • + + key + + + (#hash) + + + + — +
    +

    key for value

    +
    + +
  • + +
  • + + value + + + (Object) + + + + — +
    +

    value value

    +
    + +
  • + +
+ + +
+ + + + +
+
+
+
+30
+31
+32
+
+
# File 'lib/hyde/util/lookup.rb', line 30
+
+def []=(key, value)
+  @storage[key] = value
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/doc/Hyde/Util/Query.html b/doc/Hyde/Util/Query.html new file mode 100644 index 0000000..120361b --- /dev/null +++ b/doc/Hyde/Util/Query.html @@ -0,0 +1,402 @@ + + + + + + + Class: Hyde::Util::Query + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Hyde::Util::Query + + + +

+
+ +
+
Inherits:
+
+ Object + +
    +
  • Object
  • + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/hyde/util/query.rb
+
+ +
+ +

Overview

+
+ +

Query string parser

+ + +
+
+
+ + +
+ + + + + + + +

+ Instance Method Summary + collapse +

+ +
    + +
  • + + + #initialize(query) ⇒ Query + + + + + + + constructor + + + + + + + + +
    +

    A new instance of Query.

    +
    + +
  • + + +
  • + + + #parse ⇒ Hash + + + + + + + + + + + + + +
    +

    Better(tm) query parser with Returns a hash with arrays Key semantics:.

    +
    + +
  • + + +
  • + + + #parse_shallow ⇒ Hash + + + + + + + + + + + + + +
    +

    Shallow query parser (does not do PHP-like array keys).

    +
    + +
  • + + +
+ + +
+

Constructor Details

+ +
+

+ + #initialize(query) ⇒ Query + + + + + +

+
+ +

Returns a new instance of Query.

+ + +
+
+
+

Parameters:

+
    + +
  • + + query + + + (String) + + + +
  • + +
+ + +
+ + + + +
+
+
+
+10
+11
+12
+
+
# File 'lib/hyde/util/query.rb', line 10
+
+def initialize(query)
+  @query = query
+end
+
+
+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #parseHash + + + + + +

+
+ +

Better(tm) query parser with Returns a hash with arrays Key semantics:

+
  • +

    ‘key=value` creates a key value pair

    +
  • +

    ‘key[]=value` appends `value` to an array named `key`

    +
  • +

    key=value` sets `value` at `index` of array named `key`

    +
+ + +
+
+
+ +

Returns:

+
    + +
  • + + + (Hash) + + + +
  • + +
+ +
+ + + + +
+
+
+
+30
+31
+32
+
+
# File 'lib/hyde/util/query.rb', line 30
+
+def parse
+  construct_deep_hash(URI.decode_www_form(@query, Encoding::UTF_8))
+end
+
+
+ +
+

+ + #parse_shallowHash + + + + + +

+
+ +

Shallow query parser (does not do PHP-like array keys)

+ + +
+
+
+ +

Returns:

+
    + +
  • + + + (Hash) + + + +
  • + +
+ +
+ + + + +
+
+
+
+16
+17
+18
+19
+20
+
+
# File 'lib/hyde/util/query.rb', line 16
+
+def parse_shallow
+  URI.decode_www_form(@query, Encoding::UTF_8)
+     .sort_by { |array| array[0] }
+     .to_h
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/doc/_index.html b/doc/_index.html index e298db3..fe474d2 100644 --- a/doc/_index.html +++ b/doc/_index.html @@ -76,10 +76,54 @@ + + + + + + + + + + + + + + +
    +
  • U
  • +
      + +
    • + Util + + (Hyde) + +
    • +
@@ -163,7 +413,7 @@ diff --git a/doc/class_list.html b/doc/class_list.html index f991863..5f73cb9 100644 --- a/doc/class_list.html +++ b/doc/class_list.html @@ -43,7 +43,7 @@ diff --git a/doc/file.README.html b/doc/file.README.html index 087fef9..0af2f42 100644 --- a/doc/file.README.html +++ b/doc/file.README.html @@ -67,108 +67,103 @@

A simple "Hello, World!" HTTP API using Hyde

-
require 'Hyde'
+
require 'hyde'
 
-server = Hyde::Server.new Port: 8000 do
-    get "/hello" do |ctx|
-        ctx.response.body = "Hello, World!"
-        ctx.response['Content-Type'] = "text/plain"
-    end
+app = Hyde::Server.new do
+  get "/hello" do
+    header "content-type", "text/plain"
+    "Hello world!"
+  end
 end
 
-server.start
+run app
 

A push/pull stack as an HTTP API

require 'hyde'
 
-Stack = []
+stack = []
 
-server = Hyde::Server.new Port: 8000 do
-    get "pull" do |ctx|
-        ctx.response.body = "#{Stack.pop}"
-        ctx.response["Content-Type"] = "text/plain"
-    end
-    post "push" do |ctx|
-        Stack.push ctx.request.body
-        ctx.response.body = "#{ctx.request.body}"
-        ctx.response["Content-Type"] = "text/plain"
-    end
+app = Hyde::Server.new do
+  get "/pop" do
+    header 'content-type', 'text/plain'
+    stack.pop.to_s
+  end
+  post "/push" do
+    header 'content-type', 'text/plain'
+    stack.push(request.body)
+    request.body
+  end
 end
 
-server.start
+run app
 

Several push/pull buckets

require 'hyde'
 
-Stack = {"bucket_1" => [], "bucket_2" => [], "bucket_3" => []}
+stack = { "1" => [], "2" => [], "3" => [] }
 
-server = Hyde::Server.new Port: 8000 do
-    path ["bucket_1","bucket_2","bucket_3"] do
-        get "pull" do |ctx|
-            bucket_name = (ctx.filepath.match /bucket_[^\/]*/)[0]
-            ctx.response.body = "#{Stack[bucket_name].pop}"
-            ctx.response["Content-Type"] = "text/plain"
-        end
-        post "push" do |ctx|
-            bucket_name = (ctx.filepath.match /bucket_[^\/]*/)[0]
-            Stack[bucket_name].push ctx.request.body
-            ctx.response.body = "#{ctx.request.body}"
-            ctx.response["Content-Type"] = "text/plain"
-        end
+app = Hyde::Server.new do
+  path "bucket_(1|2|3)" do
+    get "pop" do |bucket|
+      header "content-type", "text/plain"
+      stack[bucket].pop.to_s
     end
+    post "push" do |bucket|
+      header "content-type", "text/plain"
+      stack[bucket].push(request.body)
+      request.body
+    end
+  end
 end
 
-server.start
+run app
 

Static file serving (Note: index applies only to /var/www (to the path its defined in))

-
require 'hyde'
+
require 'hyde'
 
-server = Hyde::Server.new Port:8000 do
-    path "static" do
-        root "/var/www"
-        index ["index.html","index.htm"]
-        serve "*/*.html" safe_regexp: false
-        serve "*.html"
-    end
-end
+app = Hyde::Server.new do
+  root "/var/www"
+  index ["index.html","index.htm"]
+  serve "**/*.(html|htm)"
+end
 
-server.start
+run app
 

Logging on a particular path

require 'hyde'
 
-server = Hyde::Server.new Port:8000 do
-    path "unimportant" do
-        get "version" do |ctx|
-            ctx.response.body = '{"version": "the good one"}'
-            ctx.response['Content-Type'] = "application/json"
-        end
+app = Hyde::Server.new do
+  path "unimportant" do
+    get "version" do
+      header "content-type", "text/plain"
+      "1337 (the best one)"
     end
-    path "important" do
-        preprocess do |ctx|
-            # Implement logging logic here
-            puts "Client at #{ctx.request.remote_ip} wanted to access something /important!"
-        end
-        get "answer" do |ctx|
-            ctx.response.body = '{"answer":42}'
-            ctx.response['Content-Type'] = "application/json"
-        end
+  end
+  path "important" do
+    preprocess do |req|
+      # Implement logging logic here
+      puts "Client at #{req.headers['REMOTE_ADDR']} wanted to access something /important!"
     end
+    get "answer" do
+      header "content-type", "application/json"
+      '{"answer":42, "desc":"something important!"}'
+    end
+  end
 end
 
-server.start
+run app
 
-

And a lot more to come (hopefully)

+

And a lot more to be found in /examples in this repo.

Documentation

@@ -195,7 +190,7 @@ server.start diff --git a/doc/index.html b/doc/index.html index cb12e47..9ae2507 100644 --- a/doc/index.html +++ b/doc/index.html @@ -67,108 +67,103 @@

A simple "Hello, World!" HTTP API using Hyde

-
require 'Hyde'
+
require 'hyde'
 
-server = Hyde::Server.new Port: 8000 do
-    get "/hello" do |ctx|
-        ctx.response.body = "Hello, World!"
-        ctx.response['Content-Type'] = "text/plain"
-    end
+app = Hyde::Server.new do
+  get "/hello" do
+    header "content-type", "text/plain"
+    "Hello world!"
+  end
 end
 
-server.start
+run app
 

A push/pull stack as an HTTP API

require 'hyde'
 
-Stack = []
+stack = []
 
-server = Hyde::Server.new Port: 8000 do
-    get "pull" do |ctx|
-        ctx.response.body = "#{Stack.pop}"
-        ctx.response["Content-Type"] = "text/plain"
-    end
-    post "push" do |ctx|
-        Stack.push ctx.request.body
-        ctx.response.body = "#{ctx.request.body}"
-        ctx.response["Content-Type"] = "text/plain"
-    end
+app = Hyde::Server.new do
+  get "/pop" do
+    header 'content-type', 'text/plain'
+    stack.pop.to_s
+  end
+  post "/push" do
+    header 'content-type', 'text/plain'
+    stack.push(request.body)
+    request.body
+  end
 end
 
-server.start
+run app
 

Several push/pull buckets

require 'hyde'
 
-Stack = {"bucket_1" => [], "bucket_2" => [], "bucket_3" => []}
+stack = { "1" => [], "2" => [], "3" => [] }
 
-server = Hyde::Server.new Port: 8000 do
-    path ["bucket_1","bucket_2","bucket_3"] do
-        get "pull" do |ctx|
-            bucket_name = (ctx.filepath.match /bucket_[^\/]*/)[0]
-            ctx.response.body = "#{Stack[bucket_name].pop}"
-            ctx.response["Content-Type"] = "text/plain"
-        end
-        post "push" do |ctx|
-            bucket_name = (ctx.filepath.match /bucket_[^\/]*/)[0]
-            Stack[bucket_name].push ctx.request.body
-            ctx.response.body = "#{ctx.request.body}"
-            ctx.response["Content-Type"] = "text/plain"
-        end
+app = Hyde::Server.new do
+  path "bucket_(1|2|3)" do
+    get "pop" do |bucket|
+      header "content-type", "text/plain"
+      stack[bucket].pop.to_s
     end
+    post "push" do |bucket|
+      header "content-type", "text/plain"
+      stack[bucket].push(request.body)
+      request.body
+    end
+  end
 end
 
-server.start
+run app
 

Static file serving (Note: index applies only to /var/www (to the path its defined in))

-
require 'hyde'
+
require 'hyde'
 
-server = Hyde::Server.new Port:8000 do
-    path "static" do
-        root "/var/www"
-        index ["index.html","index.htm"]
-        serve "*/*.html" safe_regexp: false
-        serve "*.html"
-    end
-end
+app = Hyde::Server.new do
+  root "/var/www"
+  index ["index.html","index.htm"]
+  serve "**/*.(html|htm)"
+end
 
-server.start
+run app
 

Logging on a particular path

require 'hyde'
 
-server = Hyde::Server.new Port:8000 do
-    path "unimportant" do
-        get "version" do |ctx|
-            ctx.response.body = '{"version": "the good one"}'
-            ctx.response['Content-Type'] = "application/json"
-        end
+app = Hyde::Server.new do
+  path "unimportant" do
+    get "version" do
+      header "content-type", "text/plain"
+      "1337 (the best one)"
     end
-    path "important" do
-        preprocess do |ctx|
-            # Implement logging logic here
-            puts "Client at #{ctx.request.remote_ip} wanted to access something /important!"
-        end
-        get "answer" do |ctx|
-            ctx.response.body = '{"answer":42}'
-            ctx.response['Content-Type'] = "application/json"
-        end
+  end
+  path "important" do
+    preprocess do |req|
+      # Implement logging logic here
+      puts "Client at #{req.headers['REMOTE_ADDR']} wanted to access something /important!"
     end
+    get "answer" do
+      header "content-type", "application/json"
+      '{"answer":42, "desc":"something important!"}'
+    end
+  end
 end
 
-server.start
+run app
 
-

And a lot more to come (hopefully)

+

And a lot more to be found in /examples in this repo.

Documentation

@@ -195,7 +190,7 @@ server.start diff --git a/doc/method_list.html b/doc/method_list.html index 4ddfb6b..43b0bf2 100644 --- a/doc/method_list.html +++ b/doc/method_list.html @@ -44,6 +44,70 @@
    +
  • +
    + [] + Hyde::Util::Lookup +
    +
  • + + +
  • +
    + #[] + Hyde::Util::Lookup +
    +
  • + + +
  • +
    + #[]= + Hyde::Util::Lookup +
    +
  • + + +
  • +
    + #add_header + Hyde::Response +
    +
  • + + +
  • +
    + #body + Hyde::Request +
    +
  • + + +
  • +
    + #body + Hyde::Response +
    +
  • + + +
  • +
    + #bounce + Hyde::DSL::ProbeMethods +
    +
  • + + +
  • +
    + #call + Hyde::Server +
    +
  • + +
  • can_convert? @@ -68,6 +132,262 @@
  • +
  • +
    + #children + Hyde::Path +
    +
  • + + +
  • +
    + chunk_body + Hyde::Response +
    +
  • + + +
  • +
    + #chunk_size + Hyde::Response +
    +
  • + + +
  • +
    + #connect + Hyde::DSL::PathConstructors +
    +
  • + + +
  • +
    + convert + Hyde::Response +
    +
  • + + +
  • +
    + default_error_page + Hyde::Util +
    +
  • + + +
  • +
    + #delete + Hyde::DSL::PathConstructors +
    +
  • + + +
  • +
    + #delete_header + Hyde::Response +
    +
  • + + +
  • +
    + #die + Hyde::DSL::ProbeMethods +
    +
  • + + +
  • +
    + escape_html + Hyde::Util +
    +
  • + + +
  • +
    + #filepath + Hyde::Request +
    +
  • + + +
  • +
    + #filter + Hyde::Path +
    +
  • + + +
  • +
    + #filter + Hyde::DSL::PathMethods +
    +
  • + + +
  • +
    + #finalize + Hyde::Response +
    +
  • + + +
  • +
    + #get + Hyde::DSL::PathConstructors +
    +
  • + + +
  • +
    + #go + Hyde::Node +
    +
  • + + +
  • +
    + #head + Hyde::DSL::PathConstructors +
    +
  • + + +
  • +
    + #header + Hyde::DSL::ProbeMethods +
    +
  • + + +
  • +
    + #headers + Hyde::Request +
    +
  • + + +
  • +
    + #headers + Hyde::Response +
    +
  • + + +
  • +
    + #index + Hyde::DSL::PathMethods +
    +
  • + + +
  • +
    + #initialize + Hyde::Node +
    +
  • + + +
  • +
    + #initialize + Hyde::PathBinding +
    +
  • + + +
  • +
    + #initialize + Hyde::Path +
    +
  • + + +
  • +
    + #initialize + Hyde::Probe +
    +
  • + + +
  • +
    + #initialize + Hyde::Server +
    +
  • + + +
  • +
    + #initialize + Hyde::Request +
    +
  • + + +
  • +
    + #initialize + Hyde::Response +
    +
  • + + +
  • +
    + #initialize + Hyde::Util::Query +
    +
  • + + +
  • +
    + #initialize + Hyde::Util::Lookup +
    +
  • + + +
  • +
    + #initialize + Hyde::ProbeBinding +
    +
  • + + +
  • +
    + #initialize + Hyde::Handler +
    +
  • + +
  • #initialize @@ -77,6 +397,14 @@
  • +
    + #initialize + Hyde::ServeHandler +
    +
  • + + +
  • #initialize Hyde::PatternMatching::Glob @@ -84,7 +412,7 @@
  • -
  • +
  • #initialize Hyde::PatternMatching::ReMatch @@ -92,7 +420,7 @@
  • -
  • +
  • #match Hyde::Pattern @@ -100,7 +428,7 @@
  • -
  • +
  • #match Hyde::PatternMatching::Glob @@ -108,7 +436,7 @@
  • -
  • +
  • #match Hyde::PatternMatching::ReMatch @@ -116,7 +444,7 @@
  • -
  • +
  • #match? Hyde::Pattern @@ -124,7 +452,7 @@
  • -
  • +
  • #match? Hyde::PatternMatching::Glob @@ -132,7 +460,7 @@
  • -
  • +
  • #match? Hyde::PatternMatching::ReMatch @@ -140,6 +468,382 @@
  • +
  • +
    + #options + Hyde::DSL::PathConstructors +
    +
  • + + +
  • +
    + #param + Hyde::Request +
    +
  • + + +
  • +
    + #parent + Hyde::Util::Lookup +
    +
  • + + +
  • +
    + #parse + Hyde::Util::Query +
    +
  • + + +
  • +
    + #parse_shallow + Hyde::Util::Query +
    +
  • + + +
  • +
    + #patch + Hyde::DSL::PathConstructors +
    +
  • + + +
  • +
    + #path + Hyde::Request +
    +
  • + + +
  • +
    + #path + Hyde::DSL::PathConstructors +
    +
  • + + +
  • +
    + #path_info + Hyde::Request +
    +
  • + + +
  • +
    + #pop_state + Hyde::Request +
    +
  • + + +
  • +
    + #post + Hyde::DSL::PathConstructors +
    +
  • + + +
  • +
    + #postprocess + Hyde::Path +
    +
  • + + +
  • +
    + #postprocess + Hyde::DSL::PathMethods +
    +
  • + + +
  • +
    + #postprocessors + Hyde::Request +
    +
  • + + +
  • +
    + #preprocess + Hyde::Path +
    +
  • + + +
  • +
    + #preprocess + Hyde::DSL::PathMethods +
    +
  • + + +
  • +
    + #probe + Hyde::DSL::PathConstructors +
    +
  • + + +
  • +
    + #process + Hyde::Node +
    +
  • + + +
  • +
    + #process + Hyde::Path +
    +
  • + + +
  • +
    + #process + Hyde::Probe +
    +
  • + + +
  • +
    + #process + Hyde::Handler +
    +
  • + + +
  • +
    + #process + Hyde::GETHandler +
    +
  • + + +
  • +
    + #process + Hyde::ServeHandler +
    +
  • + + +
  • +
    + #properties + Hyde::Path +
    +
  • + + +
  • +
    + #properties + Hyde::Probe +
    +
  • + + +
  • +
    + #push_state + Hyde::Request +
    +
  • + + +
  • +
    + #put + Hyde::DSL::PathConstructors +
    +
  • + + +
  • +
    + #query + Hyde::Request +
    +
  • + + +
  • +
    + #register + Hyde::DSL::PathConstructors +
    +
  • + + +
  • +
    + #reject + Hyde::Node +
    +
  • + + +
  • +
    + #remap + Hyde::Node +
    +
  • + + +
  • +
    + #remap + Hyde::DSL::PathMethods +
    +
  • + + +
  • +
    + #remove_header + Hyde::DSL::ProbeMethods +
    +
  • + + +
  • +
    + #request + Hyde::Handler +
    +
  • + + +
  • +
    + #request + Hyde::DSL::ProbeMethods +
    +
  • + + +
  • +
    + #request_method + Hyde::Request +
    +
  • + + +
  • +
    + #response + Hyde::Handler +
    +
  • + + +
  • +
    + #response + Hyde::ServeHandler +
    +
  • + + +
  • +
    + #root + Hyde::Node +
    +
  • + + +
  • +
    + #root + Hyde::DSL::PathMethods +
    +
  • + + +
  • +
    + #run_postprocessors + Hyde::Request +
    +
  • + + +
  • +
    + #script_name + Hyde::Request +
    +
  • + + +
  • +
    + #serve + Hyde::DSL::PathConstructors +
    +
  • + + +
  • +
    + #server_name + Hyde::Request +
    +
  • + + +
  • +
    + #server_port + Hyde::Request +
    +
  • + + +
  • +
    + #server_protocol + Hyde::Request +
    +
  • + + +
  • +
    + #splat + Hyde::Request +
    +
  • + +
  • #static? @@ -148,6 +852,38 @@
  • +
  • +
    + #status + Hyde::Response +
    +
  • + + +
  • +
    + #status + Hyde::DSL::ProbeMethods +
    +
  • + + +
  • +
    + #trace + Hyde::DSL::PathConstructors +
    +
  • + + +
  • +
    + #validate + Hyde::Response +
    +
  • + +
diff --git a/doc/top-level-namespace.html b/doc/top-level-namespace.html index 96455f7..6ebc842 100644 --- a/doc/top-level-namespace.html +++ b/doc/top-level-namespace.html @@ -100,7 +100,7 @@ diff --git a/examples/form/form.ru b/examples/form/form.ru new file mode 100644 index 0000000..ccb7806 --- /dev/null +++ b/examples/form/form.ru @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib") +require 'hyde' +require 'hyde/util/multipart' +require 'hyde/util/header' + +app = Hyde::Server.new do + root ENV["PWD"] + index ["index.html"] + post "/" do + _, opts = Hyde::Util.parse_value(request.headers["content-type"]) + puts Hyde::Util::MultipartParser.new( + request.input, opts["boundary"] + ).to_h.pretty_inspect + bounce + end + serve "*.html" +end + +run app diff --git a/examples/form/index.html b/examples/form/index.html new file mode 100644 index 0000000..bb209c2 --- /dev/null +++ b/examples/form/index.html @@ -0,0 +1,27 @@ + + + + Form upload test + + + +

Form upload test

+
+

Please enter the following details:

+

+ + + + + + + +
+ + + + diff --git a/examples/form/lib b/examples/form/lib new file mode 120000 index 0000000..58677dd --- /dev/null +++ b/examples/form/lib @@ -0,0 +1 @@ +../../lib \ No newline at end of file diff --git a/examples/query/config.ru b/examples/query/config.ru new file mode 100644 index 0000000..588afb6 --- /dev/null +++ b/examples/query/config.ru @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib") +require_relative 'lib/hyde' + +app = Hyde::Server.new do + get "/hello" do + name = request.query["name"] + if name.is_a? String + "Hello #{name.downcase.capitalize}!" + elsif name.is_a? Array + "Hello #{name.map { |x| x.downcase.capitalize }.join(', ')}!" + else + "Hello user!" + end + end +end + +run app diff --git a/examples/query/lib b/examples/query/lib new file mode 120000 index 0000000..58677dd --- /dev/null +++ b/examples/query/lib @@ -0,0 +1 @@ +../../lib \ No newline at end of file diff --git a/examples/query/readme.txt b/examples/query/readme.txt new file mode 100644 index 0000000..1295126 --- /dev/null +++ b/examples/query/readme.txt @@ -0,0 +1,6 @@ +This example illustrates a basic API that responds with "Hello !" and receives arrays and strings as parameter. + +P.S. there is at least *one* vulnerability in this exmaple. +If you don't know which one you definitely want to read about XSS before it's too late +(https://owasp.org/www-community/attacks/xss/) + diff --git a/lib/hyde/request.rb b/lib/hyde/request.rb index 6681294..27e0b85 100644 --- a/lib/hyde/request.rb +++ b/lib/hyde/request.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'uri' +require_relative 'util/query' module Hyde # Request wrapper for Rack protocol @@ -11,6 +12,8 @@ module Hyde @_original_env = env # Rack environment variable bindings. Should be public and frozen. init_request_params(env) + # Query parsing + @query = Hyde::Util::Query.new(@query_string) # Pattern matching parameters. Public, readable, unfrozen. @param = {} @splat = [] @@ -37,7 +40,13 @@ module Hyde # Returns request body (if POST data exists) # @return [nil, String] def body - @body ||= @rack.input&.gets + @body ||= @rack.input&.read + end + + # Returns raw Rack input object + # @return [IO] (May not entirely be compatible with IO, see Rack/SPEC.rdoc) + def input + @rack.input end # Push current navigation state (path, splat, param) onto state stack @@ -52,7 +61,7 @@ module Hyde attr_reader :request_method, :script_name, :path_info, :server_name, :server_port, :server_protocol, :headers, :param, :splat, - :postprocessors + :postprocessors, :query attr_accessor :path, :filepath private @@ -63,6 +72,7 @@ module Hyde @request_method = env["REQUEST_METHOD"] @script_name = env["SCRIPT_NAME"] @path_info = env["PATH_INFO"] + @query_string = env["QUERY_STRING"] @server_name = env["SERVER_NAME"] @server_port = env["SERVER_PORT"] @server_protocol = env["SERVER_PROTOCOL"] @@ -111,10 +121,12 @@ module Hyde headers = env.filter_map do |name, value| [name.delete_prefix("HTTP_"), value] if name.start_with?("HTTP_") end.to_h - headers.merge!({ "CONTENT_TYPE" => env["CONTENT_TYPE"], - "CONTENT_LENGTH" => env["CONTENT_LENGTH"], + headers.merge!({ "CONTENT-TYPE" => env["CONTENT_TYPE"], + "CONTENT-LENGTH" => env["CONTENT_LENGTH"], "REMOTE_ADDR" => env["REMOTE_ADDR"] }) - headers.freeze + headers.transform_keys do |x| + x.downcase.gsub("_", "-") if x.is_a? String + end.freeze end end end diff --git a/lib/hyde/util/header.rb b/lib/hyde/util/header.rb new file mode 100644 index 0000000..54671c1 --- /dev/null +++ b/lib/hyde/util/header.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'uri' + +module Hyde + module Util + # Parse parametrized header values + # @param input [String] + # @param sep [String, Regexp] + # @return [Array(String, Hash)] + def self.parse_value(input, sep = ";") + parts = input.split(sep).map { |x| URI.decode_uri_component(x).strip } + base = parts.shift + opts = parts.map do |raw| + key, value = raw.match(/^([^=]*)(?:=(.*)|)\Z/).to_a[1..] + [key, ((value&.match?(/^".*"$/) ? value[1..-2] : value) or true)] + end.to_h + [base, opts] + end + + # Construct a parametrized header value + # @param input [String] + # @param opts [Hash] + # @return [String] + def self.make_value(input, opts, sep = ";") + unless input.match?(/^[\w!#$%&'*+-.^_`|~]*=?[^[:cntrl:]\\",;]+$/) + raise StandardError, "input contains invalid characters" + end + + output = input + opts.each do |key, value| + output += if value.is_a? String + "#{sep} #{key}=#{value}" + else + "#{sep} #{key}" + end + end + output + end + end +end diff --git a/lib/hyde/util/html.rb b/lib/hyde/util/html.rb index ed797f2..867815f 100644 --- a/lib/hyde/util/html.rb +++ b/lib/hyde/util/html.rb @@ -58,7 +58,10 @@ module Hyde 511 => 'Network Authentication Required' }.freeze - # Return string with escaped HTML entities + # Return string with escaped HTML entities. + # @note Do **not** use this to inject untrusted input into JavaScript code + # or CSS style of the page. + # This function is not adequate to prevent string interpolation. # @param str [String] # @return [String] def self.escape_html(str) diff --git a/lib/hyde/util/multipart.rb b/lib/hyde/util/multipart.rb new file mode 100644 index 0000000..8b589ad --- /dev/null +++ b/lib/hyde/util/multipart.rb @@ -0,0 +1,165 @@ +# frozen_string_literal: false + +require 'uri' +require 'stringio' +require 'tempfile' +require_relative 'header' + +module Hyde + module Util + # Valid element of form data with headers + # @!attribute headers [Hash] headers recevied from form data + # @!attribute name [String] name of the form part + # @!attribute data [String,nil] Data received in the field through form data + # @!attribute filename [String,nil] Original name of the sent file + # @!attribute filetype [String,nil] MIME-type of the file + # @!attribute tempfile [File,nil] Temporary file for storing sent file data. + FormPart = Struct.new(:data, :name, :filename, + :filetype, :tempfile, :headers) do + # Is this form part a file or plain data? + # @return [Boolean] + def file? + !tempfile.nil? + end + + # If FormPart is not a file, simplify to string. + # @return [FormPart, String] + def simplify + file? ? self : self.data + end + end + + # A very naive implementation of a Multipart form parser. + class MultipartParser + include Hyde::Util::ParserCommon + def initialize(io, boundary) + @input = io.is_a?(String) ? StringIO.new(io) : io + @boundary = boundary + @state = :idle + @data = [] + end + + # lord forgive me for what i'm about to do + # TODO: replace the god method with a state machine object + # rubocop:disable Metrics/* + + # Parse multipart formdata + # @return [Array] + def parse + return @data unless @data.empty? + + while (line = @input.gets) + case @state + when :idle # waiting for valid boundary + if line == "--#{@boundary}\r\n" + # transition to :headers on valid boundary + @state = :headers + @data.append(FormPart.new(*([nil] * 5), {})) + end + when :headers # after valid boundary - checking for headers + if line == "\r\n" + # prepare form field and transition to :data or :file + @state = file?(@data[-1].headers) ? :file : :data + if @state == :data + setup_data_meta(@data[-1]) + else + setup_file_meta(@data[-1]) + end + next + end + push_header(line, @data[-1].headers) + when :data, :file # after headers - processing form data + if @data[-1].headers.empty? + # transition to :idle on empty headers + @state = :idle + next + end + if ["--#{@boundary}\r\n", "--#{@boundary}--\r\n"].include? line + # finalize and transition to either :headers or :idle + if @state == :file + @data[-1].tempfile.truncate(@data[-1].tempfile.size - 2) + @data[-1].tempfile.close + else + @data[-1].data.delete_suffix! "\r\n" + end + @state = line == "--#{@boundary}\r\n" ? :headers : :idle + @data.append(FormPart.new(*([nil] * 5), {})) + next + end + if @state == :data + @data[-1].data ||= "" + @data[-1].data << line + else + @data[-1].tempfile << line + end + end + end + @state = :idle + @data.pop + @data.freeze + end + # rubocop:enable Metrics/* + + # Return a hash of current form. + # (equivalent to Query.parse but for multipart/form-data) + # @return [Hash] + def to_h + flatten(sort(gen_hash(parse))) + end + + private + + def gen_hash(array) + hash = {} + array.each do |formpart| + key = formpart.name.to_s + if key.match?(/.*\[\d*\]\Z/) + new_key, index = key.match(/(.*)\[(\d*)\]\Z/).to_a[1..] + hash[new_key] = [] unless hash[new_key] + hash[new_key].append([index, formpart.simplify]) + else + hash[key] = formpart.simplify + end + end + hash + end + + # Setup file metadata + # @part part [FormPart] + def setup_file_meta(part) + part.name = part.headers.dig("content-disposition", 1, "name") + part.filename = part.headers.dig("content-disposition", 1, "filename") + part.filetype = part.headers["content-type"] + part.tempfile = Tempfile.new + end + + # Setup plain metadata + # @part part [FormPart] + def setup_data_meta(part) + part.name = part.headers.dig("content-disposition", 1, "name") + end + + # Analyze headers to check if current data part is a file. + # @param headers_hash [Hash] + # @return [Boolean] + def file?(headers_hash) + if headers_hash.dig("content-disposition", 1, "filename") and + headers_hash['content-type'] + return true + end + + false + end + + # Parse a header and append it to headers_hash + # @param line [String] + # @param headers_hash [Hash] + def push_header(line, headers_hash) + return unless line.match(/^[\w!#$%&'*+-.^_`|~]+:.*\r\n$/) + + k, v = line.match(/^([\w!#$%&'*+-.^_`|~]+):(.*)\r\n$/).to_a[1..] + headers_hash[k.downcase] = Hyde::Util.parse_value(v) + end + end + end +end diff --git a/lib/hyde/util/query.rb b/lib/hyde/util/query.rb new file mode 100644 index 0000000..24858f0 --- /dev/null +++ b/lib/hyde/util/query.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require 'uri' +require_relative 'sorting' + +module Hyde + module Util + # Query string parser + class Query + include Hyde::Util::ParserCommon + # @param query [String] + def initialize(query) + @query = query + end + + # Shallow query parser (does not do PHP-like array keys) + # @return [Hash] + def parse_shallow + URI.decode_www_form(@query, Encoding::UTF_8) + .sort_by { |array| array[0] } + .to_h + end + + # Better(tm) query parser with + # Returns a hash with arrays + # Key semantics: + # + # - `key=value` creates a key value pair + # - `key[]=value` appends `value` to an array named `key` + # - `key[index]=value` sets `value` at `index` of array named `key` + # @return [Hash] + def parse + construct_deep_hash(URI.decode_www_form(@query, Encoding::UTF_8)) + end + + # Get key from query + # @param key [String] + # @return [String,Array] + def [](key) + (@cache ||= parse)[key] + end + + private + + # Construct a hash with array support + def construct_deep_hash(array) + flatten(sort(group(array))) + end + + # Assign values to keys in a new hash and group arrayable keys + def group(array) + hash = {} + array.each do |key, value| + if key.match?(/.*\[\d*\]\Z/) + new_key, index = key.match(/(.*)\[(\d*)\]\Z/).to_a[1..] + hash[new_key] = [] unless hash[new_key] + hash[new_key].append([index, value]) + else + hash[key] = value + end + end + hash + end + end + end +end diff --git a/lib/hyde/util/sorting.rb b/lib/hyde/util/sorting.rb new file mode 100644 index 0000000..a9a1af4 --- /dev/null +++ b/lib/hyde/util/sorting.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Hyde + module Util + # Internal library for generating form hashes + module ParserCommon + private + + # Sort key-value pair arrays + def sort(hash) + hash.filter { |_, v| v.is_a? Array }.each do |_, value| + value.sort_by! { |array| array[0].to_i } + end + hash + end + + # Flatten key-value pair arrays + def flatten(hash) + hash.transform_values do |value| + if value.is_a? Array + new_array = [] + value.each do |k, v| + if k.match?(/\d+/) and k.to_i < new_array.size + new_array[k.to_i] = v + else + new_array.append(v) + end + end + new_array + else + value + end + end.to_h + end + end + end +end diff --git a/test/Hyde_Util_Query.rb b/test/Hyde_Util_Query.rb new file mode 100644 index 0000000..eb9472c --- /dev/null +++ b/test/Hyde_Util_Query.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require_relative "../lib/hyde/util/query" +require "test/unit" + +class TestQuery < Test::Unit::TestCase + include Hyde::Util + def test_query + test1 = Query.new("key1=1&key2=2&key3[]=1&key3[]=2&key3[]=3") + assert_equal({"key1"=>"1","key2"=>"2","key3"=>["1","2","3"]},test1.parse) + test2 = Query.new("key1[1]=1&key1[500]=3&key1[50]=2") + assert_equal({"key1"=>["1","2","3"]},test2.parse) + test3 = Query.new("array[]=1&array[]=2&array[0]=0&array[500]=3") + assert_equal({"array"=>["0","2","3"]},test3.parse) + end +end