mirror of https://github.com/adamdruppe/arsd.git
new stuff im working on
This commit is contained in:
parent
3fa87f08c4
commit
57c995f4bf
114
cgi.d
114
cgi.d
|
|
@ -3770,11 +3770,15 @@ class ListeningConnectionManager {
|
||||||
}
|
}
|
||||||
semaphore.notify();
|
semaphore.notify();
|
||||||
|
|
||||||
|
bool hasAnyRunning;
|
||||||
foreach(thread; threads) {
|
foreach(thread; threads) {
|
||||||
if(!thread.isRunning) {
|
if(!thread.isRunning) {
|
||||||
thread.join();
|
thread.join();
|
||||||
|
} else hasAnyRunning = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if(!hasAnyRunning)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: i typically stop this with ctrl+c which never
|
// FIXME: i typically stop this with ctrl+c which never
|
||||||
|
|
@ -3876,7 +3880,7 @@ class ConnectionThread : Thread {
|
||||||
}
|
}
|
||||||
try
|
try
|
||||||
dg(socket);
|
dg(socket);
|
||||||
catch(Exception e)
|
catch(Throwable e)
|
||||||
socket.close();
|
socket.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5950,6 +5954,8 @@ private string beautify(string name, char space = ' ', bool allLowerCase = false
|
||||||
}
|
}
|
||||||
|
|
||||||
lastWasCap = true;
|
lastWasCap = true;
|
||||||
|
} else {
|
||||||
|
lastWasCap = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(shouldSpace) {
|
if(shouldSpace) {
|
||||||
|
|
@ -6260,9 +6266,11 @@ auto callFromCgi(alias method, T)(T dg, Cgi cgi) {
|
||||||
|
|
||||||
---
|
---
|
||||||
class MyPresenter : WebPresenter!(MyPresenter) {
|
class MyPresenter : WebPresenter!(MyPresenter) {
|
||||||
|
@Override
|
||||||
void presentSuccessfulReturnAsHtml(T : CustomType)(Cgi cgi, T ret) {
|
void presentSuccessfulReturnAsHtml(T : CustomType)(Cgi cgi, T ret) {
|
||||||
// present the CustomType
|
// present the CustomType
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
void presentSuccessfulReturnAsHtml(T)(Cgi cgi, T ret) {
|
void presentSuccessfulReturnAsHtml(T)(Cgi cgi, T ret) {
|
||||||
// handle everything else via the super class, which will call
|
// handle everything else via the super class, which will call
|
||||||
// back to your class when appropriate
|
// back to your class when appropriate
|
||||||
|
|
@ -6273,6 +6281,11 @@ auto callFromCgi(alias method, T)(T dg, Cgi cgi) {
|
||||||
|
|
||||||
+/
|
+/
|
||||||
class WebPresenter(CRTP) {
|
class WebPresenter(CRTP) {
|
||||||
|
|
||||||
|
/// A UDA version of the built-in `override`, to be used for static template polymorphism
|
||||||
|
/// If you override a plain method, use `override`. If a template, use `@Override`.
|
||||||
|
enum Override;
|
||||||
|
|
||||||
string script() {
|
string script() {
|
||||||
return `
|
return `
|
||||||
`;
|
`;
|
||||||
|
|
@ -6290,7 +6303,8 @@ class WebPresenter(CRTP) {
|
||||||
}
|
}
|
||||||
|
|
||||||
string genericFormStyling() {
|
string genericFormStyling() {
|
||||||
return `
|
return
|
||||||
|
q"css
|
||||||
table.automatic-data-display {
|
table.automatic-data-display {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
border: solid 1px var(--mild-border);
|
border: solid 1px var(--mild-border);
|
||||||
|
|
@ -6332,28 +6346,29 @@ class WebPresenter(CRTP) {
|
||||||
.add-array-button {
|
.add-array-button {
|
||||||
|
|
||||||
}
|
}
|
||||||
`;
|
css";
|
||||||
}
|
}
|
||||||
|
|
||||||
string genericSiteStyling() {
|
string genericSiteStyling() {
|
||||||
return `
|
return
|
||||||
|
q"css
|
||||||
* { box-sizing: border-box; }
|
* { box-sizing: border-box; }
|
||||||
html, body { margin: 0px; }
|
html, body { margin: 0px; }
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
}
|
}
|
||||||
#header {
|
header {
|
||||||
background: var(--accent-color);
|
background: var(--accent-color);
|
||||||
height: 64px;
|
height: 64px;
|
||||||
}
|
}
|
||||||
#footer {
|
footer {
|
||||||
background: var(--accent-color);
|
background: var(--accent-color);
|
||||||
height: 64px;
|
height: 64px;
|
||||||
}
|
}
|
||||||
#main-site {
|
#site-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
#container {
|
main {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
order: 2;
|
order: 2;
|
||||||
min-height: calc(100vh - 64px - 64px);
|
min-height: calc(100vh - 64px - 64px);
|
||||||
|
|
@ -6365,29 +6380,31 @@ class WebPresenter(CRTP) {
|
||||||
order: 1;
|
order: 1;
|
||||||
background: var(--sidebar-color);
|
background: var(--sidebar-color);
|
||||||
}
|
}
|
||||||
`;
|
css";
|
||||||
}
|
}
|
||||||
|
|
||||||
import arsd.dom;
|
import arsd.dom;
|
||||||
Element htmlContainer() {
|
Element htmlContainer() {
|
||||||
auto document = new Document(`<!DOCTYPE html>
|
auto document = new Document(q"html
|
||||||
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>D Application</title>
|
<title>D Application</title>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="header"></div>
|
<header></header>
|
||||||
<div id="main-site">
|
<div id="site-container">
|
||||||
<div id="container"></div>
|
<main></main>
|
||||||
<div id="sidebar"></div>
|
<div id="sidebar"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="footer"></div>
|
<footer></footer>
|
||||||
<script src="script.js"></script>
|
<script src="script.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>`, true, true);
|
</html>
|
||||||
|
html", true, true);
|
||||||
|
|
||||||
return document.requireElementById("container");
|
return document.requireSelector("main");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// typeof(null) (which is also used to represent functions returning `void`) do nothing
|
/// typeof(null) (which is also used to represent functions returning `void`) do nothing
|
||||||
|
|
@ -6452,10 +6469,9 @@ class WebPresenter(CRTP) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/++
|
/++
|
||||||
returns an arsd.dom.Element
|
Returns an element for a particular type
|
||||||
+/
|
+/
|
||||||
auto elementFor(T)(string displayName, string name) {
|
Element elementFor(T)(string displayName, string name) {
|
||||||
import arsd.dom;
|
|
||||||
import std.traits;
|
import std.traits;
|
||||||
|
|
||||||
auto div = Element.make("div");
|
auto div = Element.make("div");
|
||||||
|
|
@ -6498,6 +6514,7 @@ class WebPresenter(CRTP) {
|
||||||
}
|
}
|
||||||
auto i = lbl.addChild("input", name);
|
auto i = lbl.addChild("input", name);
|
||||||
i.attrs.type = "checkbox";
|
i.attrs.type = "checkbox";
|
||||||
|
i.attrs.value = "true";
|
||||||
i.attrs.name = name;
|
i.attrs.name = name;
|
||||||
} else static if(is(T == K[], K)) {
|
} else static if(is(T == K[], K)) {
|
||||||
auto templ = div.addChild("template");
|
auto templ = div.addChild("template");
|
||||||
|
|
@ -6522,9 +6539,8 @@ class WebPresenter(CRTP) {
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// actually returns an arsd.dom.Form
|
/// creates a form for gathering the function's arguments
|
||||||
auto createAutomaticFormForFunction(alias method, T)(T dg) {
|
Form createAutomaticFormForFunction(alias method, T)(T dg) {
|
||||||
import arsd.dom;
|
|
||||||
|
|
||||||
auto form = cast(Form) Element.make("form");
|
auto form = cast(Form) Element.make("form");
|
||||||
|
|
||||||
|
|
@ -6555,10 +6571,8 @@ class WebPresenter(CRTP) {
|
||||||
return form;
|
return form;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// actually returns an arsd.dom.Form
|
/// creates a form for gathering object members (for the REST object thing right now)
|
||||||
auto createAutomaticFormForObject(T)(T obj) {
|
Form createAutomaticFormForObject(T)(T obj) {
|
||||||
import arsd.dom;
|
|
||||||
|
|
||||||
auto form = cast(Form) Element.make("form");
|
auto form = cast(Form) Element.make("form");
|
||||||
|
|
||||||
form.addClass("automatic-form");
|
form.addClass("automatic-form");
|
||||||
|
|
@ -6588,8 +6602,7 @@ class WebPresenter(CRTP) {
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
auto formatReturnValueAsHtml(T)(T t) {
|
Element formatReturnValueAsHtml(T)(T t) {
|
||||||
import arsd.dom;
|
|
||||||
import std.traits;
|
import std.traits;
|
||||||
|
|
||||||
static if(is(T == typeof(null))) {
|
static if(is(T == typeof(null))) {
|
||||||
|
|
@ -6826,11 +6839,23 @@ auto serveApi(T)(string urlPrefix) {
|
||||||
auto obj = new T();
|
auto obj = new T();
|
||||||
obj.initialize(cgi);
|
obj.initialize(cgi);
|
||||||
|
|
||||||
switch(cgi.pathInfo[urlPrefix.length .. $]) {
|
/+
|
||||||
static foreach(methodName; __traits(derivedMembers, T)){{
|
Overload rules:
|
||||||
static if(is(typeof(__traits(getMember, T, methodName)) P == __parameters))
|
Any unique combination of HTTP verb and url path can be dispatched to function overloads
|
||||||
|
statically.
|
||||||
|
|
||||||
|
Moreover, some args vs no args can be overloaded dynamically.
|
||||||
|
+/
|
||||||
|
|
||||||
|
string hack = to!string(cgi.requestMethod) ~ " " ~ cgi.pathInfo[urlPrefix.length .. $];
|
||||||
|
|
||||||
|
switch(hack) {
|
||||||
|
static foreach(methodName; __traits(derivedMembers, T))
|
||||||
|
static foreach(idx, overload; __traits(getOverloads, T, methodName)) {{
|
||||||
|
static if(is(typeof(overload) P == __parameters))
|
||||||
{
|
{
|
||||||
case urlNameForMethod!(__traits(getMember, T, methodName))(urlify(methodName)):
|
case urlNameForMethod!(overload)(urlify(methodName)):
|
||||||
|
/+
|
||||||
int zeroArgOverload = -1;
|
int zeroArgOverload = -1;
|
||||||
int overloadCount = cast(int) __traits(getOverloads, T, methodName).length;
|
int overloadCount = cast(int) __traits(getOverloads, T, methodName).length;
|
||||||
bool calledWithZeroArgs = true;
|
bool calledWithZeroArgs = true;
|
||||||
|
|
@ -6902,6 +6927,7 @@ auto serveApi(T)(string urlPrefix) {
|
||||||
so the method UDAs are irrelevant.
|
so the method UDAs are irrelevant.
|
||||||
+/
|
+/
|
||||||
if(callFunction)
|
if(callFunction)
|
||||||
|
+/
|
||||||
switch(cgi.request("format", "html")) {
|
switch(cgi.request("format", "html")) {
|
||||||
case "html":
|
case "html":
|
||||||
try {
|
try {
|
||||||
|
|
@ -6934,7 +6960,7 @@ auto serveApi(T)(string urlPrefix) {
|
||||||
cgi.setResponseStatus("406 Not Acceptable"); // not exactly but sort of.
|
cgi.setResponseStatus("406 Not Acceptable"); // not exactly but sort of.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}}
|
//}}
|
||||||
cgi.header("Accept: POST"); // FIXME list the real thing
|
cgi.header("Accept: POST"); // FIXME list the real thing
|
||||||
cgi.setResponseStatus("405 Method Not Allowed"); // again, not exactly, but sort of. no overload matched our args, almost certainly due to http verb filtering.
|
cgi.setResponseStatus("405 Method Not Allowed"); // again, not exactly, but sort of. no overload matched our args, almost certainly due to http verb filtering.
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -6960,11 +6986,25 @@ auto serveApi(T)(string urlPrefix) {
|
||||||
}
|
}
|
||||||
|
|
||||||
string urlNameForMethod(alias method)(string def) {
|
string urlNameForMethod(alias method)(string def) {
|
||||||
|
auto verb = Cgi.RequestMethod.GET;
|
||||||
|
bool foundVerb = false;
|
||||||
|
bool foundNoun = false;
|
||||||
static foreach(attr; __traits(getAttributes, method)) {
|
static foreach(attr; __traits(getAttributes, method)) {
|
||||||
static if(is(typeof(attr) == UrlName))
|
static if(is(typeof(attr) == Cgi.RequestMethod)) {
|
||||||
return attr.name;
|
verb = attr;
|
||||||
|
if(foundVerb)
|
||||||
|
assert(0, "Multiple http verbs on one function is not currently supported");
|
||||||
|
foundVerb = true;
|
||||||
}
|
}
|
||||||
return def;
|
static if(is(typeof(attr) == UrlName)) {
|
||||||
|
if(foundNoun)
|
||||||
|
assert(0, "Multiple url names on one function is not currently supported");
|
||||||
|
foundNoun = true;
|
||||||
|
def = attr.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return to!string(verb) ~ " " ~ def;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue