diff --git a/cgi.d b/cgi.d
index ef33624..751ad53 100644
--- a/cgi.d
+++ b/cgi.d
@@ -1,6 +1,9 @@
// FIXME: if an exception is thrown, we shouldn't necessarily cache...
// FIXME: there's some annoying duplication of code in the various versioned mains
+// FIXME: I might make a cgi proxy class which can change things; the underlying one is still immutable
+// but the later one can edit and simplify the api. You'd have to use the subclass tho!
+
/*
void foo(int f, @("test") string s) {}
@@ -3031,9 +3034,14 @@ void cgiMainImpl(alias fun, CustomCgi = Cgi, long maxContentLength = defaultMaxC
uint i;
sockaddr addr;
i = addr.sizeof;
- version(embedded_httpd_processes_accept_after_fork)
+ version(embedded_httpd_processes_accept_after_fork) {
int s = accept(sock, &addr, &i);
- else {
+ int opt = 1;
+ import core.sys.posix.netinet.tcp;
+ // the Cgi class does internal buffering, so disabling this
+ // helps with latency in many cases...
+ setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, opt.sizeof);
+ } else {
int s;
auto readret = read_fd(pipeReadFd, &s, s.sizeof, &s);
if(readret != s.sizeof) {
@@ -3147,6 +3155,9 @@ void cgiMainImpl(alias fun, CustomCgi = Cgi, long maxContentLength = defaultMaxC
sockaddr addr;
i = addr.sizeof;
s = accept(sock, &addr, &i);
+ import core.sys.posix.netinet.tcp;
+ int opt = 1;
+ setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, opt.sizeof);
}
if(FD_ISSET(pipeWriteFd, &write_fds)) {
@@ -5787,6 +5798,63 @@ private string beautify(string name) {
for html, basic types are strings. Arrays are
. Structs are
. Arrays of structs are tables!
+/
+// returns an arsd.dom.Element
+static auto elementFor(T)(string displayName, string name) {
+ import arsd.dom;
+ import std.traits;
+
+ auto div = Element.make("div");
+ div.addClass("form-field");
+
+ static if(is(T == struct)) {
+ if(displayName !is null)
+ div.addChild("span", displayName, "label-text");
+ auto fieldset = div.addChild("fieldset");
+ fieldset.addChild("legend", beautify(T.stringof)); // FIXME
+ fieldset.addChild("input", name);
+ static foreach(idx, memberName; __traits(allMembers, T))
+ static if(__traits(compiles, __traits(getMember, T, memberName).offsetof)) {
+ fieldset.appendChild(elementFor!(typeof(__traits(getMember, T, memberName)))(beautify(memberName), name ~ "." ~ memberName));
+ }
+ } else static if(isSomeString!T || isIntegral!T || isFloatingPoint!T) {
+ Element lbl;
+ if(displayName !is null) {
+ lbl = div.addChild("label");
+ lbl.addChild("span", displayName, "label-text");
+ lbl.appendText(" ");
+ } else {
+ lbl = div;
+ }
+ auto i = lbl.addChild("input", name);
+ i.attrs.name = name;
+ static if(isSomeString!T)
+ i.attrs.type = "text";
+ else
+ i.attrs.type = "number";
+ i.attrs.value = to!string(T.init);
+ } else static if(is(T == K[], K)) {
+ auto templ = div.addChild("template");
+ templ.appendChild(elementFor!(K)(null, name));
+ if(displayName !is null)
+ div.addChild("span", displayName, "label-text");
+ auto btn = div.addChild("button");
+ btn.addClass("add-array-button");
+ btn.attrs.type = "button";
+ btn.innerText = "Add";
+ btn.attrs.onclick = q{
+ var a = document.importNode(this.parentNode.firstChild.content, true);
+ this.parentNode.insertBefore(a, this);
+ };
+ } else static if(is(T == V[K], K, V)) {
+ div.innerText = "assoc array not implemented for automatic form at this time";
+ } else {
+ static assert(0, "unsupported type for cgi call " ~ T.stringof);
+ }
+
+
+ return div;
+}
+
// actually returns an arsd.dom.Form
auto createAutomaticFormForFunction(alias method, T)(T dg) {
import arsd.dom;
@@ -5803,59 +5871,6 @@ auto createAutomaticFormForFunction(alias method, T)(T dg) {
//alias idents = ParameterIdentifierTuple!method;
//alias defaults = ParameterDefaults!method;
- static Element elementFor(T)(string displayName, string name) {
- auto div = Element.make("div");
- div.addClass("form-field");
-
- static if(is(T == struct)) {
- if(displayName !is null)
- div.addChild("span", displayName, "label-text");
- auto fieldset = div.addChild("fieldset");
- fieldset.addChild("legend", beautify(T.stringof)); // FIXME
- fieldset.addChild("input", name);
- static foreach(idx, memberName; __traits(allMembers, T))
- static if(__traits(compiles, __traits(getMember, T, memberName).offsetof)) {
- fieldset.appendChild(elementFor!(typeof(__traits(getMember, T, memberName)))(beautify(memberName), name ~ "." ~ memberName));
- }
- } else static if(isSomeString!T || isIntegral!T || isFloatingPoint!T) {
- Element lbl;
- if(displayName !is null) {
- lbl = div.addChild("label");
- lbl.addChild("span", displayName, "label-text");
- lbl.appendText(" ");
- } else {
- lbl = div;
- }
- auto i = lbl.addChild("input", name);
- i.attrs.name = name;
- static if(isSomeString!T)
- i.attrs.type = "text";
- else
- i.attrs.type = "number";
- i.attrs.value = to!string(T.init);
- } else static if(is(T == K[], K)) {
- auto templ = div.addChild("template");
- templ.appendChild(elementFor!(K)(null, name));
- if(displayName !is null)
- div.addChild("span", displayName, "label-text");
- auto btn = div.addChild("button");
- btn.addClass("add-array-button");
- btn.attrs.type = "button";
- btn.innerText = "Add";
- btn.attrs.onclick = q{
- var a = document.importNode(this.parentNode.firstChild.content, true);
- this.parentNode.insertBefore(a, this);
- };
- } else static if(is(T == V[K], K, V)) {
- div.innerText = "assoc array not implemented for automatic form at this time";
- } else {
- static assert(0, "unsupported type for cgi call " ~ T.stringof);
- }
-
-
- return div;
- }
-
static if(is(typeof(method) P == __parameters))
static foreach(idx, _; P) {{
alias param = P[idx .. idx + 1];
@@ -5871,6 +5886,38 @@ auto createAutomaticFormForFunction(alias method, T)(T dg) {
return form;
}
+// actually returns an arsd.dom.Form
+auto createAutomaticFormForObject(T)(T obj) {
+ import arsd.dom;
+
+ auto form = cast(Form) Element.make("form");
+
+ form.addClass("automatic-form");
+
+ form.addChild("h3", beautify(__traits(identifier, T)));
+
+ import std.traits;
+
+ //Parameters!method params;
+ //alias idents = ParameterIdentifierTuple!method;
+ //alias defaults = ParameterDefaults!method;
+
+ static foreach(idx, memberName; __traits(derivedMembers, T)) {{
+ static if(__traits(compiles, __traits(getMember, obj, memberName).offsetof)) {
+ string displayName = beautify(memberName);
+ static foreach(attr; __traits(getAttributes, __traits(getMember, T, memberName)))
+ static if(is(typeof(attr) == DisplayName))
+ displayName = attr.name;
+ form.appendChild(elementFor!(typeof(__traits(getMember, T, memberName)))(displayName, memberName));
+
+ form.setValue(memberName, to!string(__traits(getMember, obj, memberName)));
+ }}}
+
+ form.addChild("div", Html(``), "submit-button-holder");
+
+ return form;
+}
+
/*
string urlFor(alias func)() {
return __traits(identifier, func);
@@ -5911,6 +5958,8 @@ class MissingArgumentException : Exception {
auto callFromCgi(alias method, T)(T dg, Cgi cgi) {
+ // FIXME: any array of structs should also be settable or gettable from csv as well.
+
// FIXME: think more about checkboxes and bools.
import std.traits;
@@ -6110,7 +6159,32 @@ auto formatReturnValueAsHtml(T)(T t) {
return dl;
} else static if(is(T == E[], E)) {
- static if(is(E == struct)) {
+ static if(is(E : RestObject!Proxy, Proxy)) {
+ // treat RestObject similar to struct
+ auto table = cast(Table) Element.make("table");
+ table.addClass("automatic-data-display");
+ string[] names;
+ static foreach(idx, memberName; __traits(derivedMembers, E))
+ static if(__traits(compiles, __traits(getMember, E, memberName).offsetof)) {
+ names ~= beautify(memberName);
+ }
+ table.appendHeaderRow(names);
+
+ foreach(l; t) {
+ auto tr = table.appendRow();
+ static foreach(idx, memberName; __traits(derivedMembers, E))
+ static if(__traits(compiles, __traits(getMember, E, memberName).offsetof)) {
+ static if(memberName == "id") {
+ string val = to!string(__traits(getMember, l, memberName));
+ tr.addChild("td", Element.make("a", val, E.stringof.toLower ~ "s/" ~ val)); // FIXME
+ } else {
+ tr.addChild("td", formatReturnValueAsHtml(__traits(getMember, l, memberName)));
+ }
+ }
+ }
+
+ return table;
+ } else static if(is(E == struct)) {
// an array of structs is kinda special in that I like
// having those formatted as tables.
auto table = cast(Table) Element.make("table");
@@ -6145,7 +6219,19 @@ auto formatReturnValueAsHtml(T)(T t) {
}
/++
- The base class for the [dispatcher] object support.
+ A web presenter is responsible for rendering things to HTML to be usable
+ in a web browser.
+
+ They are passed as template arguments to the base classes of [WebObject]
+
+ FIXME
++/
+class WebPresenter() {
+
+}
+
+/++
+ The base class for the [dispatcher] function and object support.
+/
class WebObject(Helper = void) {
Cgi cgi;
@@ -6163,7 +6249,14 @@ class WebObject(Helper = void) {
:root {
--mild-border: #ccc;
--middle-border: #999;
+ --accent-color: #e8e8e8;
+ --sidebar-color: #f2f2f2;
}
+ ` ~ genericFormStyling() ~ genericSiteStyling();
+ }
+
+ string genericFormStyling() {
+ return `
table.automatic-data-display {
border-collapse: collapse;
border: solid 1px var(--mild-border);
@@ -6208,6 +6301,39 @@ class WebObject(Helper = void) {
`;
}
+ string genericSiteStyling() {
+ return `
+ * { box-sizing: border-box; }
+ html, body { margin: 0px; }
+ body {
+ font-family: sans-serif;
+ }
+ #header {
+ background: var(--accent-color);
+ height: 64px;
+ }
+ #footer {
+ background: var(--accent-color);
+ height: 64px;
+ }
+ #main-site {
+ display: flex;
+ }
+ #container {
+ flex: 1 1 auto;
+ order: 2;
+ min-height: calc(100vh - 64px - 64px);
+ padding: 4px;
+ padding-left: 1em;
+ }
+ #sidebar {
+ flex: 0 0 16em;
+ order: 1;
+ background: var(--sidebar-color);
+ }
+ `;
+ }
+
import arsd.dom;
Element htmlContainer() {
auto document = new Document(`
@@ -6217,7 +6343,12 @@ class WebObject(Helper = void) {
-
+
+