refactor: rename DTree to DValue
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// Minimal exported helper used by the `unit_call()` demo page.
|
||||
|
||||
EXPORT DTree* test_func(DTree* call_param)
|
||||
EXPORT DValue* test_func(DValue* call_param)
|
||||
{
|
||||
print("HELLO FROM TEST FUNCTION");
|
||||
return(0);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree p;
|
||||
DValue p;
|
||||
p.set(context.params);
|
||||
|
||||
StringList routes = {"index", "dashboard", "themes", "dashboard", "workspace/projects"};
|
||||
@@ -10,15 +10,15 @@ RENDER(Request& context)
|
||||
StringList sorted_routes = list_sort(unique_routes);
|
||||
StringList labels = map(sorted_routes, [](String route) { return(to_upper(replace(route, "/", " / "))); });
|
||||
|
||||
DTree cards;
|
||||
DTree card;
|
||||
DValue cards;
|
||||
DValue card;
|
||||
card["title"] = "Dashboard"; card["section"] = "app"; card["href"] = "?dashboard"; cards.push(card); card.clear();
|
||||
card["title"] = "Themes"; card["section"] = "app"; card["href"] = "?themes"; cards.push(card); card.clear();
|
||||
card["title"] = "Docs"; card["section"] = "reference"; card["href"] = "../doc/index.uce"; cards.push(card);
|
||||
|
||||
DTree app_cards = dtree_filter(cards, [](DTree item, String key) { return(item["section"].to_string() == "app"); });
|
||||
DTree titles = dtree_map(app_cards, [](DTree item, String key) { DTree out; out = item["title"].to_string(); return(out); });
|
||||
DTree grouped = dtree_group_by(cards, [](DTree item, String key) { return(item["section"].to_string()); });
|
||||
DValue app_cards = dv_filter(cards, [](DValue item, String key) { return(item["section"].to_string() == "app"); });
|
||||
DValue titles = dv_map(app_cards, [](DValue item, String key) { DValue out; out = item["title"].to_string(); return(out); });
|
||||
DValue grouped = dv_group_by(cards, [](DValue item, String key) { return(item["section"].to_string()); });
|
||||
|
||||
<><html>
|
||||
<head>
|
||||
@@ -30,7 +30,7 @@ RENDER(Request& context)
|
||||
<h2>Collection Helpers</h2>
|
||||
<p>Small data-shaping helpers are useful for route lists, nav records, cards, and other render-adjacent structures.</p>
|
||||
<div class="system-info"><h3>StringList route labels</h3><pre><?= join(labels, "\n") ?></pre></div>
|
||||
<div class="system-info"><h3>DTree app card titles</h3><pre><?= json_encode(titles) ?></pre></div>
|
||||
<div class="system-info"><h3>DValue app card titles</h3><pre><?= json_encode(titles) ?></pre></div>
|
||||
<div class="system-info"><h3>Grouped cards</h3><pre><?= json_encode(grouped) ?></pre></div>
|
||||
<details>
|
||||
<summary>Request Parameters</summary>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree card_props;
|
||||
DValue card_props;
|
||||
card_props["title"] = "Component Example";
|
||||
card_props["body"] = "This card body comes from context.props and is rendered through component().";
|
||||
|
||||
DTree named_props;
|
||||
DValue named_props;
|
||||
named_props["title"] = "Named Render Example";
|
||||
named_props["body"] = "This content is rendered through the COMPONENT:BODY(Request& context) entry point.";
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ void test_demo_render_restricted_json(Request& context, String title, String ris
|
||||
{
|
||||
context.set_status(403, "Restricted");
|
||||
context.header["Content-Type"] = "application/json; charset=utf-8";
|
||||
DTree payload;
|
||||
DValue payload;
|
||||
payload["ok"].set_bool(false);
|
||||
payload["error"] = "restricted";
|
||||
payload["title"] = title;
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree t;
|
||||
DValue t;
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
DTree
|
||||
DValue
|
||||
</h1>
|
||||
|
||||
<h2>Value Types</h2>
|
||||
@@ -33,21 +33,21 @@ RENDER(Request& context)
|
||||
|
||||
<pre><?
|
||||
|
||||
DTree scalar_text;
|
||||
DValue scalar_text;
|
||||
scalar_text = "42.75";
|
||||
print("text -> f64: ", scalar_text.to_f64(), "\n");
|
||||
print("text -> u64: ", scalar_text.to_u64(), "\n");
|
||||
print("text -> s64: ", scalar_text.to_s64(), "\n");
|
||||
|
||||
DTree bool_text;
|
||||
DValue bool_text;
|
||||
bool_text = "yes";
|
||||
print("yes -> bool: ", bool_text.to_bool() ? "true" : "false", "\n");
|
||||
|
||||
DTree single_value_map;
|
||||
DValue single_value_map;
|
||||
single_value_map["value"] = "123";
|
||||
print("{value:123} -> u64: ", single_value_map.to_u64(), "\n");
|
||||
|
||||
DTree headers;
|
||||
DValue headers;
|
||||
headers["Content-Type"] = "text/plain";
|
||||
headers["X-Test"] = "123";
|
||||
print("map -> StringMap:\n", var_dump(headers.to_stringmap()), "\n");
|
||||
@@ -58,12 +58,12 @@ RENDER(Request& context)
|
||||
|
||||
<pre><?
|
||||
|
||||
DTree lookup;
|
||||
DValue lookup;
|
||||
print("has(user) before create: ", lookup.has("user") ? "true" : "false", "\n");
|
||||
print("key(user) before create: ", lookup.key("user") ? "present" : "missing", "\n");
|
||||
lookup.get_or_create("user")->set("Ada");
|
||||
print("has(user) after create: ", lookup.has("user") ? "true" : "false", "\n");
|
||||
if(DTree* user = lookup.key("user"))
|
||||
if(DValue* user = lookup.key("user"))
|
||||
print("key(user) after create: ", user->to_string(), "\n");
|
||||
|
||||
?></pre>
|
||||
@@ -90,11 +90,11 @@ RENDER(Request& context)
|
||||
|
||||
<pre><?
|
||||
|
||||
DTree assoc_left;
|
||||
DValue assoc_left;
|
||||
assoc_left["name"] = "left";
|
||||
assoc_left["shared"] = "left-value";
|
||||
|
||||
DTree assoc_right;
|
||||
DValue assoc_right;
|
||||
assoc_right["shared"] = "right-value";
|
||||
assoc_right["extra"] = "new-value";
|
||||
|
||||
@@ -108,15 +108,15 @@ RENDER(Request& context)
|
||||
|
||||
<pre><?
|
||||
|
||||
DTree list_left;
|
||||
DValue list_left;
|
||||
list_left.set_array();
|
||||
DTree a; a = "A"; list_left.push(a);
|
||||
DTree b; b = "B"; list_left.push(b);
|
||||
DValue a; a = "A"; list_left.push(a);
|
||||
DValue b; b = "B"; list_left.push(b);
|
||||
|
||||
DTree list_right;
|
||||
DValue list_right;
|
||||
list_right.set_array();
|
||||
DTree c; c = "C"; list_right.push(c);
|
||||
DTree d; d = "D"; list_right.push(d);
|
||||
DValue c; c = "C"; list_right.push(c);
|
||||
DValue d; d = "D"; list_right.push(d);
|
||||
|
||||
print("left:\n", var_dump(list_left), "\n");
|
||||
print("right:\n", var_dump(list_right), "\n");
|
||||
@@ -11,7 +11,7 @@ RENDER(Request& context)
|
||||
test_demo_render_restricted_html(context, "File Append", "write to server-side files");
|
||||
return;
|
||||
}
|
||||
DTree t;
|
||||
DValue t;
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree t;
|
||||
DValue t;
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree t;
|
||||
DValue t;
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
|
||||
+4
-4
@@ -12,7 +12,7 @@ void render_card(String url, String title, String desc)
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree p;
|
||||
DValue p;
|
||||
p.set(context.params);
|
||||
bool allow_server_demos = test_demo_request_allowed(context);
|
||||
|
||||
@@ -35,10 +35,10 @@ RENDER(Request& context)
|
||||
<? if(allow_server_demos) { render_card("error-reporting.uce", "Error Reporting", "Error handling and output"); } ?>
|
||||
|
||||
<div class="grid-heading">Data Types & Parsing</div>
|
||||
<? render_card("dtree.uce", "DTree", "Dynamic hierarchical data tree"); ?>
|
||||
<? render_card("dvalue.uce", "DValue", "Dynamic hierarchical data tree"); ?>
|
||||
<? render_card("json.uce", "JSON", "Parse and encode JSON data"); ?>
|
||||
<? render_card("xml.uce", "XML", "Structural XML encode/decode with DTree"); ?>
|
||||
<? render_card("yaml.uce", "YAML", "Concise config files with DTree"); ?>
|
||||
<? render_card("xml.uce", "XML", "Structural XML encode/decode with DValue"); ?>
|
||||
<? render_card("yaml.uce", "YAML", "Concise config files with DValue"); ?>
|
||||
<? render_card("preprocessor-comments.uce", "Preprocessor Comments", "Regression coverage for comment parsing in templates"); ?>
|
||||
<? render_card("regex.uce", "Regular Expressions", "PCRE2 matching, captures, replacement, and splitting"); ?>
|
||||
<? render_card("string.uce", "String", "String operations"); ?>
|
||||
|
||||
+2
-2
@@ -2,13 +2,13 @@
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree t;
|
||||
DValue t;
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
DTree/JSON
|
||||
DValue/JSON
|
||||
</h1>
|
||||
|
||||
JSON:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree options;
|
||||
DValue options;
|
||||
options["components"][":::warning"] = "components/markdown/warning";
|
||||
options["components"]["node.code_block"] = "components/markdown/code_block";
|
||||
|
||||
@@ -9,7 +9,7 @@ RENDER(Request& context)
|
||||
file_get_contents("markdown-example.md")
|
||||
);
|
||||
|
||||
DTree ast = markdown_to_ast(markdown_src, options);
|
||||
DValue ast = markdown_to_ast(markdown_src, options);
|
||||
String html = markdown_to_html(markdown_src, options);
|
||||
|
||||
<>
|
||||
|
||||
@@ -9,7 +9,7 @@ RENDER(Request& context)
|
||||
test_demo_render_restricted_html(context, "MemcacheD", "inspect and mutate local memcached state");
|
||||
return;
|
||||
}
|
||||
DTree t;
|
||||
DValue t;
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
|
||||
@@ -28,13 +28,13 @@ COMPONENT:PROBE(Request& context)
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree first;
|
||||
DValue first;
|
||||
first["label"] = "First component call";
|
||||
|
||||
DTree second;
|
||||
DValue second;
|
||||
second["label"] = "Second component call in the same request";
|
||||
|
||||
DTree third;
|
||||
DValue third;
|
||||
third["label"] = "Third component call through unit_call(\"COMPONENT:PROBE\")";
|
||||
|
||||
<><html>
|
||||
|
||||
+3
-3
@@ -19,8 +19,8 @@ void regex_bool_row(String label, bool result, bool expect)
|
||||
RENDER(Request& context)
|
||||
{
|
||||
String text = "Contact ops@example.test or tag #uce, #docs, and café.";
|
||||
DTree first_email = regex_search("(?<user>[A-Za-z0-9._%+-]+)@(?<host>[A-Za-z0-9.-]+)", text);
|
||||
DTree tags = regex_search_all("#(?<tag>[A-Za-z0-9_]+)", text);
|
||||
DValue first_email = regex_search("(?<user>[A-Za-z0-9._%+-]+)@(?<host>[A-Za-z0-9.-]+)", text);
|
||||
DValue tags = regex_search_all("#(?<tag>[A-Za-z0-9_]+)", text);
|
||||
StringList pieces = regex_split("\\s*,\\s*", "uce, components, markdown");
|
||||
|
||||
<>
|
||||
@@ -38,7 +38,7 @@ RENDER(Request& context)
|
||||
Regular Expressions
|
||||
</h1>
|
||||
|
||||
<p>UCE regex functions use PCRE2 and return ordinary UCE strings, lists, and DTree values.</p>
|
||||
<p>UCE regex functions use PCRE2 and return ordinary UCE strings, lists, and DValue values.</p>
|
||||
<p>sample = <code><?= text ?></code></p>
|
||||
|
||||
<h2>Validation And Search</h2>
|
||||
|
||||
@@ -23,7 +23,7 @@ RENDER(Request& context)
|
||||
return;
|
||||
}
|
||||
|
||||
DTree notes = sqlite_query(db, "select id, body, created_at from notes order by id desc limit 10");
|
||||
DValue notes = sqlite_query(db, "select id, body, created_at from notes order by id desc limit 10");
|
||||
String error = sqlite_error(db);
|
||||
?><html>
|
||||
<head>
|
||||
@@ -41,7 +41,7 @@ RENDER(Request& context)
|
||||
<? if(error != "ok" && error != "connected") { ?><pre><?= error ?></pre><? } ?>
|
||||
</div>
|
||||
<div class="test-grid">
|
||||
<? notes.each([&](DTree note, String key) { ?>
|
||||
<? notes.each([&](DValue note, String key) { ?>
|
||||
<div class="test-card">
|
||||
<strong>#<?= note["id"].to_string() ?> <?= note["created_at"].to_string() ?></strong>
|
||||
<span><?= note["body"].to_string() ?></span>
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ RENDER(Request& context)
|
||||
test_demo_render_restricted_html(context, "Tasks", "spawn and inspect background tasks");
|
||||
return;
|
||||
}
|
||||
DTree t;
|
||||
DValue t;
|
||||
|
||||
String task_name = first(context.get["task-name"], "example-task");
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ RENDER(Request& context)
|
||||
test_demo_render_restricted_html(context, "Task repeat", "schedule recurring background tasks");
|
||||
return;
|
||||
}
|
||||
DTree t;
|
||||
DValue t;
|
||||
|
||||
String task_name = first(context.get["task-name"], "example-task");
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ String unit_status_class(String compile_status, String error_status)
|
||||
return("status-muted");
|
||||
}
|
||||
|
||||
String unit_flag_label(DTree value)
|
||||
String unit_flag_label(DValue value)
|
||||
{
|
||||
return(value.to_string() == "(true)" ? "✅" : "❌");
|
||||
}
|
||||
@@ -61,7 +61,7 @@ RENDER(Request& context)
|
||||
if(selected_path == "" && unit_paths.size() > 0)
|
||||
selected_path = unit_paths.front();
|
||||
|
||||
DTree selected_info = unit_info(selected_path);
|
||||
DValue selected_info = unit_info(selected_path);
|
||||
String selected_compile_status = selected_info["compile_status"].to_string();
|
||||
String selected_error_status = selected_info["error_status"].to_string();
|
||||
String selected_status_class = unit_status_class(selected_compile_status, selected_error_status);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
RENDER(Request& context)
|
||||
{
|
||||
bool allow_mutation = test_demo_request_allowed(context);
|
||||
DTree payload;
|
||||
DValue payload;
|
||||
|
||||
if(context.get["compile"] != "")
|
||||
{
|
||||
@@ -19,11 +19,11 @@ RENDER(Request& context)
|
||||
|
||||
payload["current"] = unit_info();
|
||||
|
||||
DTree units;
|
||||
DValue units;
|
||||
units.set_array();
|
||||
for(auto& unit_path : units_list())
|
||||
{
|
||||
DTree item;
|
||||
DValue item;
|
||||
item = unit_path;
|
||||
units.push(item);
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ String clean_chat_field(String raw, u32 max_length)
|
||||
return(value);
|
||||
}
|
||||
|
||||
DTree chat_event(Request& context, String type, String body)
|
||||
DValue chat_event(Request& context, String type, String body)
|
||||
{
|
||||
DTree event;
|
||||
DValue event;
|
||||
event["type"] = type;
|
||||
event["body"] = body;
|
||||
event["connection_id"] = context.params["WS_CONNECTION_ID"];
|
||||
@@ -159,7 +159,7 @@ WS(Request& context)
|
||||
return;
|
||||
}
|
||||
|
||||
DTree payload = json_decode(context.in);
|
||||
DValue payload = json_decode(context.in);
|
||||
String type = clean_chat_field(payload["type"].to_string(), 24);
|
||||
String name = clean_chat_field(payload["name"].to_string(), 32);
|
||||
String body = clean_chat_field(payload["body"].to_string(), 500);
|
||||
@@ -183,7 +183,7 @@ WS(Request& context)
|
||||
context.connection["last_type"] = type;
|
||||
context.connection["last_body"] = body;
|
||||
context.connection["message_count"] = (f64)(message_count + 1);
|
||||
DTree event = chat_event(context, "message", body);
|
||||
DValue event = chat_event(context, "message", body);
|
||||
ws_send(json_encode(event));
|
||||
return;
|
||||
}
|
||||
|
||||
+9
-9
@@ -2,31 +2,31 @@
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree book;
|
||||
DValue book;
|
||||
book["name"] = "book";
|
||||
book["attrs"]["id"] = "b1";
|
||||
book["attrs"]["type"] = "reference";
|
||||
|
||||
DTree title;
|
||||
DValue title;
|
||||
title["name"] = "title";
|
||||
title["text"] = "UCE & XML";
|
||||
book["children"].push(title);
|
||||
|
||||
DTree chapter;
|
||||
DValue chapter;
|
||||
chapter["name"] = "chapter";
|
||||
chapter["attrs"]["number"] = "1";
|
||||
chapter["text"] = "Structural conversion without schema validation.";
|
||||
book["children"].push(chapter);
|
||||
|
||||
String encoded = xml_encode(book);
|
||||
DTree decoded = xml_decode(encoded);
|
||||
DValue decoded = xml_decode(encoded);
|
||||
|
||||
DTree simple;
|
||||
DValue simple;
|
||||
simple["title"] = "Hello";
|
||||
simple["count"] = "3";
|
||||
|
||||
String incoming = "<note priority=\"high\"><to>UCE</to><body><![CDATA[5 < 6]]></body><symbol>AB</symbol></note>";
|
||||
DTree incoming_tree = xml_decode(incoming);
|
||||
DValue incoming_tree = xml_decode(incoming);
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
@@ -35,12 +35,12 @@ RENDER(Request& context)
|
||||
XML
|
||||
</h1>
|
||||
|
||||
<p><code>xml_encode()</code> and <code>xml_decode()</code> convert between XML strings and element-shaped <code>DTree</code> values without schema validation.</p>
|
||||
<p><code>xml_encode()</code> and <code>xml_decode()</code> convert between XML strings and element-shaped <code>DValue</code> values without schema validation.</p>
|
||||
|
||||
<h2>Encoded Element Tree</h2>
|
||||
<pre><?= encoded ?></pre>
|
||||
|
||||
<h2>Decoded DTree</h2>
|
||||
<h2>Decoded DValue</h2>
|
||||
<pre><?= json_encode(decoded) ?></pre>
|
||||
|
||||
<h2>Simple Map Encoding</h2>
|
||||
@@ -49,7 +49,7 @@ RENDER(Request& context)
|
||||
<h2>Decode Existing XML</h2>
|
||||
<p>Input XML:</p>
|
||||
<pre><?= incoming ?></pre>
|
||||
<p>Decoded DTree:</p>
|
||||
<p>Decoded DValue:</p>
|
||||
<pre><?= json_encode(incoming_tree) ?></pre>
|
||||
|
||||
<p>
|
||||
|
||||
+7
-7
@@ -12,19 +12,19 @@ RENDER(Request& context)
|
||||
" - cache\n"
|
||||
"message: |\n"
|
||||
" Keep config files readable.\n"
|
||||
" Load them as DTree values.\n";
|
||||
" Load them as DValue values.\n";
|
||||
|
||||
DTree cfg = yaml_decode(source);
|
||||
DValue cfg = yaml_decode(source);
|
||||
String encoded = yaml_encode(cfg);
|
||||
DTree roundtrip = yaml_decode(encoded);
|
||||
DValue roundtrip = yaml_decode(encoded);
|
||||
|
||||
DTree generated;
|
||||
DValue generated;
|
||||
generated["database"]["host"] = "localhost";
|
||||
generated["database"]["port"] = (f64)3306;
|
||||
generated["features"]["components"].set_bool(true);
|
||||
generated["features"]["markdown"].set_bool(true);
|
||||
|
||||
DTree theme;
|
||||
DValue theme;
|
||||
theme = "clean";
|
||||
generated["themes"].push(theme);
|
||||
theme = "compact";
|
||||
@@ -37,12 +37,12 @@ RENDER(Request& context)
|
||||
YAML
|
||||
</h1>
|
||||
|
||||
<p><code>yaml_encode()</code> and <code>yaml_decode()</code> convert concise config-style YAML to and from <code>DTree</code> values.</p>
|
||||
<p><code>yaml_encode()</code> and <code>yaml_decode()</code> convert concise config-style YAML to and from <code>DValue</code> values.</p>
|
||||
|
||||
<h2>Config Source</h2>
|
||||
<pre><?= source ?></pre>
|
||||
|
||||
<h2>Decoded DTree</h2>
|
||||
<h2>Decoded DValue</h2>
|
||||
<pre><?= json_encode(cfg) ?></pre>
|
||||
|
||||
<h2>Encoded Again</h2>
|
||||
|
||||
+2
-2
@@ -14,12 +14,12 @@ RENDER(Request& context)
|
||||
mkdir(base);
|
||||
mkdir(extract_dir);
|
||||
|
||||
DTree entries;
|
||||
DValue entries;
|
||||
entries["hello.txt"] = "Hello from a generated ZIP archive.\n";
|
||||
entries["notes/readme.txt"] = "zip_create(), zip_list(), zip_read(), and zip_extract() are available to UCE pages.\n";
|
||||
zip_create(archive, entries);
|
||||
|
||||
DTree listing = zip_list(archive);
|
||||
DValue listing = zip_list(archive);
|
||||
String hello = zip_read(archive, "hello.txt");
|
||||
zip_extract(archive, extract_dir);
|
||||
String extracted = file_get_contents(path_join(extract_dir, "notes/readme.txt"));
|
||||
|
||||
@@ -2,14 +2,14 @@ Types
|
||||
|
||||
0_Request
|
||||
array_merge
|
||||
0_DTree
|
||||
dtree_filter
|
||||
dtree_group_by
|
||||
dtree_keys
|
||||
dtree_map
|
||||
dtree_omit
|
||||
dtree_pick
|
||||
dtree_values
|
||||
0_DValue
|
||||
dv_filter
|
||||
dv_group_by
|
||||
dv_keys
|
||||
dv_map
|
||||
dv_omit
|
||||
dv_pick
|
||||
dv_values
|
||||
each
|
||||
get_by_path
|
||||
has
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
:title
|
||||
DTree
|
||||
DValue
|
||||
|
||||
:sig
|
||||
DTree
|
||||
DValue
|
||||
|
||||
:see
|
||||
>types
|
||||
@@ -10,23 +10,23 @@ get_by_path
|
||||
json_decode
|
||||
|
||||
:content
|
||||
`DTree` is UCE's general-purpose structured value type. It is the runtime's default container for nested data such as configuration trees, call payloads, decoded JSON, connection state, and metadata returned by runtime helpers.
|
||||
`DValue` is UCE's general-purpose structured value type. It is the runtime's default container for nested data such as configuration trees, call payloads, decoded JSON, connection state, and metadata returned by runtime helpers.
|
||||
|
||||
## Value Kinds
|
||||
|
||||
`DTree` can hold:
|
||||
`DValue` can hold:
|
||||
|
||||
- `String`
|
||||
- `f64`
|
||||
- `bool`
|
||||
- pointer values
|
||||
- nested child `DTree` values in a map-shaped container
|
||||
- nested child `DValue` values in a map-shaped container
|
||||
|
||||
Map-shaped `DTree` values can also represent list-like data when their keys are numeric strings in sequence.
|
||||
Map-shaped `DValue` values can also represent list-like data when their keys are numeric strings in sequence.
|
||||
|
||||
## Where It Appears
|
||||
|
||||
You will encounter `DTree` throughout the runtime, especially in:
|
||||
You will encounter `DValue` throughout the runtime, especially in:
|
||||
|
||||
- `context.cfg`
|
||||
- `context.props`
|
||||
@@ -47,7 +47,7 @@ You will encounter `DTree` throughout the runtime, especially in:
|
||||
- `.to_bool(default)` performs best-effort boolean conversion.
|
||||
- `.to_stringmap()` converts a map-shaped tree into `StringMap`.
|
||||
|
||||
All read accessors are `const` and never modify the tree; they work directly on `const DTree&` values such as `each()` callback parameters. Every `to_*` conversion takes an optional default that is returned when the value is missing or cannot be converted — see the individual pages (`to_string`, `to_s64`, `to_u64`, `to_f64`, `to_bool`) for the exact rules:
|
||||
All read accessors are `const` and never modify the tree; they work directly on `const DValue&` values such as `each()` callback parameters. Every `to_*` conversion takes an optional default that is returned when the value is missing or cannot be converted — see the individual pages (`to_string`, `to_s64`, `to_u64`, `to_f64`, `to_bool`) for the exact rules:
|
||||
|
||||
```cpp
|
||||
String title = context.props["title"].to_string("Untitled");
|
||||
@@ -56,7 +56,7 @@ s64 page_size = context.cfg.get_by_path("app/page_size").to_s64(25);
|
||||
|
||||
`operator[]` creates missing entries, just like `std::map`. `.has()` and `.key()` are the non-mutating lookup helpers, and `.get_by_path()` is the non-creating traversal helper.
|
||||
|
||||
`json_decode()` currently stores JSON numbers as string-valued `DTree` nodes, so typed numeric conversion is the normal way to consume those values.
|
||||
`json_decode()` currently stores JSON numbers as string-valued `DValue` nodes, so typed numeric conversion is the normal way to consume those values.
|
||||
|
||||
References are dereferenced automatically in most normal reads.
|
||||
|
||||
@@ -100,9 +100,9 @@ Useful inspection helpers include:
|
||||
|
||||
## each()
|
||||
|
||||
`each(std::function<void (const DTree& t, String key)> f)` iterates over the current tree value (see the `each` page for details).
|
||||
`each(std::function<void (const DValue& t, String key)> f)` iterates over the current tree value (see the `each` page for details).
|
||||
|
||||
For map-shaped `DTree` values, the callback runs once per child entry and receives:
|
||||
For map-shaped `DValue` values, the callback runs once per child entry and receives:
|
||||
|
||||
- `t` as the child value, by const reference (no copy)
|
||||
- `key` as the child key
|
||||
@@ -113,7 +113,7 @@ For non-map values, `each()` still invokes the callback once:
|
||||
- `key` is an empty string
|
||||
|
||||
```cpp
|
||||
context.connection["items"].each([&](const DTree& item, String key) {
|
||||
context.connection["items"].each([&](const DValue& item, String key) {
|
||||
print(key, ": ", item.to_string(), "\n");
|
||||
});
|
||||
```
|
||||
@@ -127,7 +127,7 @@ u64 compiled_mtime = unit_info("test/hello.uce")["compiled_mtime"].to_u64();
|
||||
|
||||
bool dark_mode = context.props["dark_mode"].to_bool();
|
||||
|
||||
if(DTree* user = payload.key("user")) {
|
||||
if(DValue* user = payload.key("user")) {
|
||||
print(user->to_json());
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ ws_message
|
||||
ws_connection_id
|
||||
ws_connections
|
||||
ws_send
|
||||
0_DTree
|
||||
0_DValue
|
||||
StringMap
|
||||
UploadedFile
|
||||
|
||||
@@ -120,7 +120,7 @@ Raw request body. For WebSocket handlers, this is the current message payload.
|
||||
Use this for JSON APIs:
|
||||
|
||||
```cpp
|
||||
DTree body = json_decode(context.in);
|
||||
DValue body = json_decode(context.in);
|
||||
```
|
||||
|
||||
### `context.uploaded_files`
|
||||
@@ -165,7 +165,7 @@ Related helpers:
|
||||
|
||||
### `context.call`
|
||||
|
||||
Type: `DTree`
|
||||
Type: `DValue`
|
||||
|
||||
General request-local scratch/configuration tree. It is shared by the page, components, and unit calls participating in the current request. Use it for app-level request state, fragments, router results, page type, page title, and other values that need to be read by later components.
|
||||
|
||||
@@ -181,7 +181,7 @@ Prefer clear top-level names when state is app-wide (`route`, `fragments`) and n
|
||||
|
||||
### `context.cfg`
|
||||
|
||||
Type: `DTree`
|
||||
Type: `DValue`
|
||||
|
||||
Request-local structured configuration. The runtime does not fill this with application config by default; application code may assign it during boot/setup:
|
||||
|
||||
@@ -199,19 +199,19 @@ String site_name = context.cfg.get_by_path("site/name").to_string();
|
||||
|
||||
### `context.props`
|
||||
|
||||
Type: `DTree`
|
||||
Type: `DValue`
|
||||
|
||||
Invocation-local props for `component()`, `component_render()`, and macro-style `unit_call()` entrypoints. During a component call, the runtime temporarily replaces `context.props` with the props passed to that component and restores the previous value after the call returns.
|
||||
|
||||
```cpp
|
||||
DTree props;
|
||||
DValue props;
|
||||
props["title"] = "Dashboard";
|
||||
print(component("components/card", props, context));
|
||||
```
|
||||
|
||||
### `context.connection`
|
||||
|
||||
Type: `DTree`
|
||||
Type: `DValue`
|
||||
|
||||
WebSocket connection-local state. Mutations persist across `WS(Request& context)` calls for the same socket.
|
||||
|
||||
@@ -394,7 +394,7 @@ RENDER(Request& context)
|
||||
RENDER(Request& context)
|
||||
{
|
||||
context.header["Content-Type"] = "application/json";
|
||||
DTree response;
|
||||
DValue response;
|
||||
response["ok"].set_bool(true);
|
||||
print(json_encode(response));
|
||||
}
|
||||
@@ -413,7 +413,7 @@ RENDER(Request& context)
|
||||
### Component props
|
||||
|
||||
```cpp
|
||||
DTree props;
|
||||
DValue props;
|
||||
props["title"] = "Welcome";
|
||||
print(component("components/card", props, context));
|
||||
```
|
||||
|
||||
@@ -46,12 +46,12 @@ Inside the handler, the usual `Request& context` fields are available:
|
||||
|
||||
CLI responses default to `text/plain; charset=utf-8`, but the handler may set headers and status explicitly.
|
||||
|
||||
Use `cli_input(context)` to merge query parameters, form parameters, and JSON POST fields into one `DTree`.
|
||||
Use `cli_input(context)` to merge query parameters, form parameters, and JSON POST fields into one `DValue`.
|
||||
|
||||
```cpp
|
||||
CLI(Request& context)
|
||||
{
|
||||
DTree input = cli_input(context);
|
||||
DValue input = cli_input(context);
|
||||
String action = first(input["action"].to_string(), "ping");
|
||||
if(action == "ping")
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ UCE reassembles fragmented messages before calling `WS(Request& context)`. Text
|
||||
|
||||
## Connection State
|
||||
|
||||
`context.connection` is a broker-owned `DTree` for the current socket. It starts empty for a new client and persists across later `WS(Request& context)` calls on that same connection.
|
||||
`context.connection` is a broker-owned `DValue` for the current socket. It starts empty for a new client and persists across later `WS(Request& context)` calls on that same connection.
|
||||
|
||||
## Message Data
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
:sig
|
||||
StringMap array_merge(StringMap a, StringMap b)
|
||||
DTree array_merge(DTree a, DTree b)
|
||||
DValue array_merge(DValue a, DValue b)
|
||||
|
||||
:params
|
||||
a : left-hand source map or tree
|
||||
@@ -9,7 +9,7 @@ return value : merged result
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
0_DValue
|
||||
StringMap
|
||||
json_decode
|
||||
|
||||
@@ -18,7 +18,7 @@ Merges two maps or trees using PHP-like merge behavior.
|
||||
|
||||
For `StringMap`, keys from `b` overwrite keys from `a`.
|
||||
|
||||
For `DTree`, string keys from `b` overwrite keys from `a`. Numeric keys are appended and reindexed when either side behaves like a list.
|
||||
For `DValue`, string keys from `b` overwrite keys from `a`. Numeric keys are appended and reindexed when either side behaves like a list.
|
||||
|
||||
This helper is the closest UCE equivalent to PHP `array_merge()` for common request, config, and JSON-shaped data.
|
||||
|
||||
|
||||
@@ -21,4 +21,4 @@ CLI(Request& context)
|
||||
}
|
||||
```
|
||||
|
||||
For commands that need multiple values or typed reads, prefer calling `cli_input(context)` once and reading the returned `DTree` directly.
|
||||
For commands that need multiple values or typed reads, prefer calling `cli_input(context)` once and reading the returned `DValue` directly.
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
cli_input
|
||||
|
||||
:sig
|
||||
DTree cli_input(Request& context)
|
||||
DValue cli_input(Request& context)
|
||||
|
||||
:see
|
||||
>1_CLI
|
||||
>json_decode
|
||||
>DTree
|
||||
>DValue
|
||||
|
||||
:content
|
||||
Returns a structured parameter tree for a `CLI(Request& context)` invocation.
|
||||
|
||||
`cli_input()` merges simple command inputs into one `DTree`:
|
||||
`cli_input()` merges simple command inputs into one `DValue`:
|
||||
|
||||
1. query parameters from `context.get`
|
||||
2. form parameters from `context.post`
|
||||
@@ -25,7 +25,7 @@ If the JSON body is a scalar or array instead of an object, the decoded value is
|
||||
```cpp
|
||||
CLI(Request& context)
|
||||
{
|
||||
DTree input = cli_input(context);
|
||||
DValue input = cli_input(context);
|
||||
String action = first(input["action"].to_string(), "help");
|
||||
|
||||
if(action == "echo")
|
||||
|
||||
@@ -12,7 +12,7 @@ unit_render
|
||||
3_C++ Preprocessor
|
||||
map
|
||||
filter
|
||||
dtree_filter
|
||||
dv_filter
|
||||
|
||||
:content
|
||||
UCE is server-first C++ with a small template preprocessor. It does not try to be React, but several concepts map cleanly.
|
||||
@@ -47,8 +47,8 @@ The function library includes small collection helpers for common route/menu/car
|
||||
```cpp
|
||||
auto visible = filter(routes, [](String route) { return(route != "admin"); });
|
||||
auto labels = map(visible, [](String route) { return(to_upper(route)); });
|
||||
DTree app_items = dtree_filter(menu, [](DTree item, String key) { return(item["section"].to_string() == "app"); });
|
||||
DTree by_section = dtree_group_by(menu, [](DTree item, String key) { return(item["section"].to_string()); });
|
||||
DValue app_items = dv_filter(menu, [](DValue item, String key) { return(item["section"].to_string() == "app"); });
|
||||
DValue by_section = dv_group_by(menu, [](DValue item, String key) { return(item["section"].to_string()); });
|
||||
```
|
||||
|
||||
Use these when the transformation communicates intent. Prefer explicit loops when side effects or multi-step validation are the main concern.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
String component(String name, [DTree props], [Request& context])
|
||||
String component(String name, [DValue props], [Request& context])
|
||||
|
||||
:see
|
||||
>ob
|
||||
@@ -37,7 +37,7 @@ When a component unit defines `ONCE(Request& context)`, the runtime calls that h
|
||||
Default component handler:
|
||||
|
||||
```cpp
|
||||
DTree props;
|
||||
DValue props;
|
||||
props["title"] = "Status";
|
||||
|
||||
<><?: component("workspace/panel", props, context) ?></>
|
||||
@@ -46,7 +46,7 @@ props["title"] = "Status";
|
||||
Named component handler:
|
||||
|
||||
```cpp
|
||||
DTree props;
|
||||
DValue props;
|
||||
props["title"] = "System";
|
||||
props["body"] = "Healthy";
|
||||
|
||||
@@ -76,7 +76,7 @@ COMPONENT:BODY(Request& context)
|
||||
Preparing props in C++ before rendering:
|
||||
|
||||
```cpp
|
||||
DTree props;
|
||||
DValue props;
|
||||
props["items"][0] = "alpha";
|
||||
props["items"][1] = "beta";
|
||||
props["items"][2] = "gamma";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
void component_render(String name, [DTree props], [Request& context])
|
||||
void component_render(String name, [DValue props], [Request& context])
|
||||
|
||||
:see
|
||||
>ob
|
||||
@@ -24,7 +24,7 @@ Use `component_render()` when you want to write component output directly from C
|
||||
## Example
|
||||
|
||||
```cpp
|
||||
DTree props;
|
||||
DValue props;
|
||||
props["body"] = "Hello";
|
||||
|
||||
component_render("components/card:BODY", props, context);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
:title
|
||||
dtree_filter
|
||||
dv_filter
|
||||
|
||||
:sig
|
||||
DTree dtree_filter(DTree tree, function<bool (DTree, String)> f)
|
||||
DValue dv_filter(DValue tree, function<bool (DValue, String)> f)
|
||||
|
||||
:see
|
||||
StringList
|
||||
0_DTree
|
||||
0_DValue
|
||||
filter
|
||||
|
||||
:content
|
||||
@@ -15,7 +15,7 @@ Keeps children for which f returns true. List-like input stays list-like.
|
||||
These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
|
||||
|
||||
```cpp
|
||||
DTree visible = dtree_filter(items, [](DTree item, String key) { return(item["hidden"].to_bool() == false); });
|
||||
DValue visible = dv_filter(items, [](DValue item, String key) { return(item["hidden"].to_bool() == false); });
|
||||
```
|
||||
|
||||
Prefer these helpers over open-coded loops when the transformation itself is the important part of the code. Use an explicit loop when mutation, error handling, or side effects are the main concern.
|
||||
@@ -1,12 +1,12 @@
|
||||
:title
|
||||
dtree_group_by
|
||||
dv_group_by
|
||||
|
||||
:sig
|
||||
DTree dtree_group_by(DTree tree, function<String (DTree, String)> f)
|
||||
DValue dv_group_by(DValue tree, function<String (DValue, String)> f)
|
||||
|
||||
:see
|
||||
StringList
|
||||
0_DTree
|
||||
0_DValue
|
||||
filter
|
||||
|
||||
:content
|
||||
@@ -15,7 +15,7 @@ Groups children into list-like buckets by the string returned from f.
|
||||
These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
|
||||
|
||||
```cpp
|
||||
DTree by_section = dtree_group_by(menu, [](DTree item, String key) { return(item["section"].to_string()); });
|
||||
DValue by_section = dv_group_by(menu, [](DValue item, String key) { return(item["section"].to_string()); });
|
||||
```
|
||||
|
||||
Prefer these helpers over open-coded loops when the transformation itself is the important part of the code. Use an explicit loop when mutation, error handling, or side effects are the main concern.
|
||||
@@ -1,21 +1,21 @@
|
||||
:title
|
||||
dtree_pick
|
||||
dv_keys
|
||||
|
||||
:sig
|
||||
DTree dtree_pick(DTree tree, StringList keys)
|
||||
StringList dv_keys(DValue tree)
|
||||
|
||||
:see
|
||||
StringList
|
||||
0_DTree
|
||||
0_DValue
|
||||
filter
|
||||
|
||||
:content
|
||||
Copies only selected keys from a DTree map.
|
||||
Returns map keys from a DValue. Scalar values produce an empty list.
|
||||
|
||||
These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
|
||||
|
||||
```cpp
|
||||
DTree public_user = dtree_pick(user, {"name", "avatar"});
|
||||
StringList keys = dv_keys(context.cfg["menu"]);
|
||||
```
|
||||
|
||||
Prefer these helpers over open-coded loops when the transformation itself is the important part of the code. Use an explicit loop when mutation, error handling, or side effects are the main concern.
|
||||
@@ -1,12 +1,12 @@
|
||||
:title
|
||||
dtree_map
|
||||
dv_map
|
||||
|
||||
:sig
|
||||
DTree dtree_map(DTree tree, function<DTree (DTree, String)> f)
|
||||
DValue dv_map(DValue tree, function<DValue (DValue, String)> f)
|
||||
|
||||
:see
|
||||
StringList
|
||||
0_DTree
|
||||
0_DValue
|
||||
filter
|
||||
|
||||
:content
|
||||
@@ -15,7 +15,7 @@ Transforms each child. List-like input stays list-like; map input keeps keys.
|
||||
These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
|
||||
|
||||
```cpp
|
||||
DTree titles = dtree_map(items, [](DTree item, String key) { DTree out; out = item["title"].to_string(); return(out); });
|
||||
DValue titles = dv_map(items, [](DValue item, String key) { DValue out; out = item["title"].to_string(); return(out); });
|
||||
```
|
||||
|
||||
Prefer these helpers over open-coded loops when the transformation itself is the important part of the code. Use an explicit loop when mutation, error handling, or side effects are the main concern.
|
||||
@@ -1,21 +1,21 @@
|
||||
:title
|
||||
dtree_omit
|
||||
dv_omit
|
||||
|
||||
:sig
|
||||
DTree dtree_omit(DTree tree, StringList keys)
|
||||
DValue dv_omit(DValue tree, StringList keys)
|
||||
|
||||
:see
|
||||
StringList
|
||||
0_DTree
|
||||
0_DValue
|
||||
filter
|
||||
|
||||
:content
|
||||
Copies a DTree map except for selected keys.
|
||||
Copies a DValue map except for selected keys.
|
||||
|
||||
These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
|
||||
|
||||
```cpp
|
||||
DTree safe_user = dtree_omit(user, {"password_hash"});
|
||||
DValue safe_user = dv_omit(user, {"password_hash"});
|
||||
```
|
||||
|
||||
Prefer these helpers over open-coded loops when the transformation itself is the important part of the code. Use an explicit loop when mutation, error handling, or side effects are the main concern.
|
||||
@@ -1,21 +1,21 @@
|
||||
:title
|
||||
dtree_keys
|
||||
dv_pick
|
||||
|
||||
:sig
|
||||
StringList dtree_keys(DTree tree)
|
||||
DValue dv_pick(DValue tree, StringList keys)
|
||||
|
||||
:see
|
||||
StringList
|
||||
0_DTree
|
||||
0_DValue
|
||||
filter
|
||||
|
||||
:content
|
||||
Returns map keys from a DTree. Scalar values produce an empty list.
|
||||
Copies only selected keys from a DValue map.
|
||||
|
||||
These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
|
||||
|
||||
```cpp
|
||||
StringList keys = dtree_keys(context.cfg["menu"]);
|
||||
DValue public_user = dv_pick(user, {"name", "avatar"});
|
||||
```
|
||||
|
||||
Prefer these helpers over open-coded loops when the transformation itself is the important part of the code. Use an explicit loop when mutation, error handling, or side effects are the main concern.
|
||||
@@ -1,21 +1,21 @@
|
||||
:title
|
||||
dtree_values
|
||||
dv_values
|
||||
|
||||
:sig
|
||||
DTree dtree_values(DTree tree)
|
||||
DValue dv_values(DValue tree)
|
||||
|
||||
:see
|
||||
StringList
|
||||
0_DTree
|
||||
0_DValue
|
||||
filter
|
||||
|
||||
:content
|
||||
Returns child values as a list-like DTree.
|
||||
Returns child values as a list-like DValue.
|
||||
|
||||
These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
|
||||
|
||||
```cpp
|
||||
DTree menu_items = dtree_values(context.cfg["menu"]);
|
||||
DValue menu_items = dv_values(context.cfg["menu"]);
|
||||
```
|
||||
|
||||
Prefer these helpers over open-coded loops when the transformation itself is the important part of the code. Use an explicit loop when mutation, error handling, or side effects are the main concern.
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
void DTree::each(function<void (const DTree& item, String key)> f) const
|
||||
void DValue::each(function<void (const DValue& item, String key)> f) const
|
||||
|
||||
:params
|
||||
f : callback invoked per child with the child node and its key
|
||||
@@ -7,26 +7,26 @@ return value : none
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
dtree_map
|
||||
dtree_filter
|
||||
dtree_keys
|
||||
0_DValue
|
||||
dv_map
|
||||
dv_filter
|
||||
dv_keys
|
||||
is_list
|
||||
|
||||
:content
|
||||
Iterates a `DTree`. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
Iterates a `DValue`. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
For map-shaped nodes the callback runs once per child, in key order, receiving the child and its key. For scalar nodes it runs exactly once with the node itself and an empty key.
|
||||
|
||||
The callback receives the child as `const DTree&` — no copy is made, and all read accessors (`to_string()`, `to_s64()`, `get_by_path()`, ...) work on it directly:
|
||||
The callback receives the child as `const DValue&` — no copy is made, and all read accessors (`to_string()`, `to_s64()`, `get_by_path()`, ...) work on it directly:
|
||||
|
||||
```cpp
|
||||
rows.each([&](const DTree& row, String key) {
|
||||
rows.each([&](const DValue& row, String key) {
|
||||
out("<li>", html_escape(row.get_by_path("title").to_string("Untitled")), "</li>");
|
||||
});
|
||||
```
|
||||
|
||||
Declaring the callback parameter as plain `DTree` also works but deep-copies every child; prefer `const DTree&`. To transform or filter into a new tree, use `dtree_map()` / `dtree_filter()` instead of mutating inside the callback.
|
||||
Declaring the callback parameter as plain `DValue` also works but deep-copies every child; prefer `const DValue&`. To transform or filter into a new tree, use `dv_map()` / `dv_filter()` instead of mutating inside the callback.
|
||||
|
||||
List-shaped trees (see `is_list`) iterate in numeric index order, matching `json_encode()` and the other serializers. Keyed maps iterate in string key order.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ page_runtime_error : UCE page rendered (status 500) after a recovered fault or u
|
||||
:see
|
||||
>runtime
|
||||
0_Request
|
||||
0_DTree
|
||||
0_DValue
|
||||
set_status
|
||||
|
||||
:content
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
:sig
|
||||
DTree DTree::get_by_path(String path, String delim = "/") const
|
||||
DValue DValue::get_by_path(String path, String delim = "/") const
|
||||
|
||||
:params
|
||||
path : slash-delimited path to traverse
|
||||
delim : optional path separator
|
||||
return value : the resolved child node, or an empty `DTree` when the path cannot be followed
|
||||
return value : the resolved child node, or an empty `DValue` when the path cannot be followed
|
||||
|
||||
:see
|
||||
0_DTree
|
||||
0_DValue
|
||||
0_Request
|
||||
>types
|
||||
json_decode
|
||||
@@ -15,9 +15,9 @@ has
|
||||
to_string
|
||||
|
||||
:content
|
||||
Traverses a nested `DTree` without creating missing keys. This is a read accessor: it is `const` and never modifies the tree, unlike `operator[]`, which creates missing entries.
|
||||
Traverses a nested `DValue` without creating missing keys. This is a read accessor: it is `const` and never modifies the tree, unlike `operator[]`, which creates missing entries.
|
||||
|
||||
Empty path segments are ignored, so leading and trailing `/` characters are harmless. If any intermediate node is not a map or a segment is missing, `get_by_path()` returns an empty `DTree`.
|
||||
Empty path segments are ignored, so leading and trailing `/` characters are harmless. If any intermediate node is not a map or a segment is missing, `get_by_path()` returns an empty `DValue`.
|
||||
|
||||
A missing path therefore reads like an empty value — combine it with the `to_*` default arguments to express a fallback in one call:
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
bool DTree::has(String s) const
|
||||
bool DValue::has(String s) const
|
||||
|
||||
:params
|
||||
s : child key to test
|
||||
@@ -7,13 +7,13 @@ return value : true when the node is map-shaped and contains the key
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
0_DValue
|
||||
get_by_path
|
||||
each
|
||||
is_array
|
||||
|
||||
:content
|
||||
Tests whether a map-shaped `DTree` contains a child key, without creating it. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
Tests whether a map-shaped `DValue` contains a child key, without creating it. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
Returns `false` for scalar nodes and for missing keys.
|
||||
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
:sig
|
||||
bool DTree::is_array() const
|
||||
bool DValue::is_array() const
|
||||
|
||||
:params
|
||||
return value : true when the node is map-shaped
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
0_DValue
|
||||
is_list
|
||||
has
|
||||
each
|
||||
|
||||
:content
|
||||
Tests whether a `DTree` node is map-shaped, i.e. holds nested child values rather than a scalar. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
Tests whether a `DValue` node is map-shaped, i.e. holds nested child values rather than a scalar. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
`is_array()` is true for both list-like and keyed containers; use `is_list()` to distinguish the two:
|
||||
|
||||
```cpp
|
||||
DTree rows = sqlite_query(db, "select * from notes");
|
||||
DValue rows = sqlite_query(db, "select * from notes");
|
||||
if(rows.is_array())
|
||||
{
|
||||
rows.each([&](const DTree& row, String key) {
|
||||
rows.each([&](const DValue& row, String key) {
|
||||
// ...
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
:sig
|
||||
bool DTree::is_list() const
|
||||
bool DValue::is_list() const
|
||||
|
||||
:params
|
||||
return value : true when the node is a sequential, numerically indexed container
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
0_DValue
|
||||
is_array
|
||||
push
|
||||
each
|
||||
dtree_values
|
||||
dv_values
|
||||
|
||||
:content
|
||||
Tests whether a map-shaped `DTree` represents a list: its keys are the numeric strings `"0"`, `"1"`, `"2"`, ... in unbroken sequence. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
Tests whether a map-shaped `DValue` represents a list: its keys are the numeric strings `"0"`, `"1"`, `"2"`, ... in unbroken sequence. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
Containers built with `push()` are lists. An empty container counts as a list when it was created with `set_array()` or `push()`; a keyed map (or a map with gaps in its numeric keys) is `is_array()` but not `is_list()`.
|
||||
|
||||
```cpp
|
||||
DTree items;
|
||||
DValue items;
|
||||
items.push(first_item);
|
||||
items.push(second_item);
|
||||
// items.is_list() == true
|
||||
@@ -27,7 +27,7 @@ items["custom"] = "x";
|
||||
// items.is_list() == false, items.is_array() == true
|
||||
```
|
||||
|
||||
`dtree_map()` and `dtree_filter()` use this distinction to decide whether results re-index from zero or keep their original keys.
|
||||
`dv_map()` and `dv_filter()` use this distinction to decide whether results re-index from zero or keep their original keys.
|
||||
|
||||
## Related Concepts
|
||||
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
:sig
|
||||
DTree json_decode(String s)
|
||||
DValue json_decode(String s)
|
||||
|
||||
:params
|
||||
s : string containing JSON data
|
||||
return value : a DTree object containing the deserialized JSON data
|
||||
return value : a DValue object containing the deserialized JSON data
|
||||
|
||||
:see
|
||||
0_DTree
|
||||
0_DValue
|
||||
json_encode
|
||||
to_bool
|
||||
to_f64
|
||||
to_u64
|
||||
|
||||
:content
|
||||
Deserializes `s` into a `DTree` structure.
|
||||
Deserializes `s` into a `DValue` structure.
|
||||
|
||||
The returned structure is usually consumed through `DTree` accessors such as:
|
||||
The returned structure is usually consumed through `DValue` accessors such as:
|
||||
|
||||
- `tree["key"].to_string()`
|
||||
- `tree["count"].to_u64()`
|
||||
@@ -23,10 +23,10 @@ The returned structure is usually consumed through `DTree` accessors such as:
|
||||
|
||||
Current runtime behavior:
|
||||
|
||||
- JSON objects and arrays become map-shaped `DTree` values.
|
||||
- JSON booleans become native `bool` `DTree` values.
|
||||
- JSON strings become native `String` `DTree` values.
|
||||
- JSON numbers currently deserialize as string-valued `DTree` nodes, so typed conversions such as `to_f64()` and `to_u64()` are the normal way to read numeric content.
|
||||
- JSON objects and arrays become map-shaped `DValue` values.
|
||||
- JSON booleans become native `bool` `DValue` values.
|
||||
- JSON strings become native `String` `DValue` values.
|
||||
- JSON numbers currently deserialize as string-valued `DValue` nodes, so typed conversions such as `to_f64()` and `to_u64()` are the normal way to read numeric content.
|
||||
|
||||
Related:
|
||||
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
:sig
|
||||
String json_encode(String s)
|
||||
String json_encode(DTree t)
|
||||
String json_encode(DValue t)
|
||||
|
||||
:params
|
||||
s : string to encode as a JSON string literal
|
||||
t : DTree object to be serialized
|
||||
t : DValue object to be serialized
|
||||
return value : string containing the JSON result
|
||||
|
||||
:see
|
||||
>types
|
||||
json_decode
|
||||
0_DTree
|
||||
0_DValue
|
||||
String
|
||||
html_escape
|
||||
|
||||
:content
|
||||
Serializes either a `String` or a `DTree` into JSON notation.
|
||||
Serializes either a `String` or a `DValue` into JSON notation.
|
||||
|
||||
When passed a `String`, `json_encode()` returns a quoted and escaped JSON string literal.
|
||||
|
||||
When passed a `DTree`, scalar values are serialized directly and nested map values are emitted as JSON objects.
|
||||
When passed a `DValue`, scalar values are serialized directly and nested map values are emitted as JSON objects.
|
||||
|
||||
Related:
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ bool list_every(StringList items, function<bool (String)> f)
|
||||
|
||||
:see
|
||||
StringList
|
||||
0_DTree
|
||||
0_DValue
|
||||
filter
|
||||
|
||||
:content
|
||||
|
||||
@@ -6,7 +6,7 @@ String list_find(StringList items, function<bool (String)> f, String fallback =
|
||||
|
||||
:see
|
||||
StringList
|
||||
0_DTree
|
||||
0_DValue
|
||||
filter
|
||||
|
||||
:content
|
||||
|
||||
@@ -6,7 +6,7 @@ bool list_some(StringList items, function<bool (String)> f)
|
||||
|
||||
:see
|
||||
StringList
|
||||
0_DTree
|
||||
0_DValue
|
||||
filter
|
||||
|
||||
:content
|
||||
|
||||
@@ -6,7 +6,7 @@ StringList list_sort(StringList items)
|
||||
|
||||
:see
|
||||
StringList
|
||||
0_DTree
|
||||
0_DValue
|
||||
filter
|
||||
|
||||
:content
|
||||
|
||||
@@ -6,7 +6,7 @@ StringList list_unique(StringList items)
|
||||
|
||||
:see
|
||||
StringList
|
||||
0_DTree
|
||||
0_DValue
|
||||
filter
|
||||
|
||||
:content
|
||||
|
||||
@@ -14,7 +14,7 @@ return value : a new list containing the transformed values
|
||||
>string
|
||||
filter
|
||||
StringList
|
||||
0_DTree
|
||||
0_DValue
|
||||
|
||||
:content
|
||||
Returns a new list by calling `f` for each item in `items`.
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
:sig
|
||||
DTree markdown_to_ast(String src)
|
||||
DTree markdown_to_ast(String src, DTree options)
|
||||
DValue markdown_to_ast(String src)
|
||||
DValue markdown_to_ast(String src, DValue options)
|
||||
|
||||
:params
|
||||
src : markdown source text
|
||||
options : optional markdown options tree
|
||||
return value : a `DTree` document AST
|
||||
return value : a `DValue` document AST
|
||||
|
||||
:see
|
||||
markdown_to_html
|
||||
component
|
||||
component_render
|
||||
json_encode
|
||||
0_DTree
|
||||
0_DValue
|
||||
|
||||
:content
|
||||
Parses Markdown source into a structured `DTree` document tree.
|
||||
Parses Markdown source into a structured `DValue` document tree.
|
||||
|
||||
The parser targets a practical GitHub-flavored subset by default:
|
||||
|
||||
@@ -67,8 +67,8 @@ Common inline nodes:
|
||||
Example:
|
||||
|
||||
```uce
|
||||
DTree options = json_decode("{\"components\":{\":::warning\":\"components/markdown/warning\"}}");
|
||||
DTree ast = markdown_to_ast(file_get_contents("README.md"), options);
|
||||
DValue options = json_decode("{\"components\":{\":::warning\":\"components/markdown/warning\"}}");
|
||||
DValue ast = markdown_to_ast(file_get_contents("README.md"), options);
|
||||
print(json_encode(ast));
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
:sig
|
||||
String markdown_to_html(String src)
|
||||
String markdown_to_html(String src, DTree options)
|
||||
String markdown_to_html(String src, DValue options)
|
||||
|
||||
:params
|
||||
src : markdown source text
|
||||
@@ -26,7 +26,7 @@ By default the function aims at a practical GitHub-flavored Markdown target, inc
|
||||
Example:
|
||||
|
||||
```uce
|
||||
DTree options;
|
||||
DValue options;
|
||||
options["components"][":::warning"] = "components/markdown/warning";
|
||||
options["components"]["node.code_block"] = "components/markdown/code_block";
|
||||
String html = markdown_to_html(file_get_contents("guide.md"), options);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
DTree mysql_query(MySQL* m, String q, StringMap params)
|
||||
DValue mysql_query(MySQL* m, String q, StringMap params)
|
||||
|
||||
:params
|
||||
m : pointer to an active MySQL connection struct
|
||||
@@ -18,13 +18,13 @@ Executes a MySQL query and returns the resulting data, if any.
|
||||
```cpp
|
||||
StringMap params;
|
||||
params["email"] = "ada@example.test";
|
||||
DTree rows = mysql_query(m,
|
||||
DValue rows = mysql_query(m,
|
||||
"select id, email from users where email = :email",
|
||||
params
|
||||
);
|
||||
```
|
||||
|
||||
The result is returned as a `DTree`, which makes it easy to iterate through rows and read fields with the usual `DTree` accessors.
|
||||
The result is returned as a `DValue`, which makes it easy to iterate through rows and read fields with the usual `DValue` accessors.
|
||||
|
||||
Related:
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
:sig
|
||||
DTree regex_search(String pattern, String subject)
|
||||
DTree regex_search(String pattern, String subject, String flags)
|
||||
DValue regex_search(String pattern, String subject)
|
||||
DValue regex_search(String pattern, String subject, String flags)
|
||||
|
||||
:params
|
||||
pattern : PCRE2 regular expression pattern
|
||||
subject : string to search
|
||||
flags : optional regex flags
|
||||
return value : a DTree describing the first match
|
||||
return value : a DValue describing the first match
|
||||
|
||||
:see
|
||||
>regex
|
||||
@@ -14,7 +14,7 @@ regex_match
|
||||
regex_search_all
|
||||
regex_replace
|
||||
regex_split
|
||||
0_DTree
|
||||
0_DValue
|
||||
|
||||
:content
|
||||
Searches `subject` for the first occurrence of `pattern` and returns structured match data.
|
||||
@@ -22,7 +22,7 @@ Searches `subject` for the first occurrence of `pattern` and returns structured
|
||||
Example:
|
||||
|
||||
```uce
|
||||
DTree match = regex_search(
|
||||
DValue match = regex_search(
|
||||
"(?<user>[A-Za-z0-9._%+-]+)@(?<host>[A-Za-z0-9.-]+)",
|
||||
"Contact ops@example.test"
|
||||
);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
:sig
|
||||
DTree regex_search_all(String pattern, String subject)
|
||||
DTree regex_search_all(String pattern, String subject, String flags)
|
||||
DValue regex_search_all(String pattern, String subject)
|
||||
DValue regex_search_all(String pattern, String subject, String flags)
|
||||
|
||||
:params
|
||||
pattern : PCRE2 regular expression pattern
|
||||
subject : string to search
|
||||
flags : optional regex flags
|
||||
return value : a DTree containing all non-overlapping matches
|
||||
return value : a DValue containing all non-overlapping matches
|
||||
|
||||
:see
|
||||
>regex
|
||||
@@ -14,7 +14,7 @@ regex_match
|
||||
regex_search
|
||||
regex_replace
|
||||
regex_split
|
||||
0_DTree
|
||||
0_DValue
|
||||
|
||||
:content
|
||||
Finds every non-overlapping match of `pattern` in `subject`.
|
||||
@@ -22,9 +22,9 @@ Finds every non-overlapping match of `pattern` in `subject`.
|
||||
Example:
|
||||
|
||||
```uce
|
||||
DTree tags = regex_search_all("#(?<tag>[A-Za-z0-9_]+)", "Ship #uce and #docs");
|
||||
DValue tags = regex_search_all("#(?<tag>[A-Za-z0-9_]+)", "Ship #uce and #docs");
|
||||
|
||||
tags["matches"].each([](DTree match, String key) {
|
||||
tags["matches"].each([](DValue match, String key) {
|
||||
print(match["named"]["tag"].to_string(), "\n");
|
||||
});
|
||||
```
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
request_query_route
|
||||
|
||||
:sig
|
||||
DTree request_query_route(Request& context, String default_path = "index")
|
||||
DValue request_query_route(Request& context, String default_path = "index")
|
||||
|
||||
:see
|
||||
request_query_path
|
||||
@@ -21,7 +21,7 @@ Fields:
|
||||
- `valid`: boolean; true when `l_path` is safe
|
||||
|
||||
```cpp
|
||||
DTree route = request_query_route(context);
|
||||
DValue route = request_query_route(context);
|
||||
if(route["valid"].to_bool())
|
||||
print("route=", route["l_path"].to_string());
|
||||
```
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
:sig
|
||||
DTree sqlite_query(SQLite* db, String q)
|
||||
DTree sqlite_query(SQLite* db, String q, StringMap params)
|
||||
DValue sqlite_query(SQLite* db, String q)
|
||||
DValue sqlite_query(SQLite* db, String q, StringMap params)
|
||||
|
||||
:params
|
||||
db : pointer to an active SQLite connection
|
||||
q : SQL statement
|
||||
params : optional named parameter map
|
||||
return value : list of result rows as a DTree
|
||||
return value : list of result rows as a DValue
|
||||
|
||||
:see
|
||||
>sqlite
|
||||
@@ -14,10 +14,10 @@ sqlite_connect
|
||||
sqlite_error
|
||||
sqlite_insert_id
|
||||
sqlite_affected_rows
|
||||
0_DTree
|
||||
0_DValue
|
||||
|
||||
:content
|
||||
Executes one SQLite statement and returns result rows as a `DTree` array. Multi-statement SQL strings are rejected so migrations cannot silently run only their first statement.
|
||||
Executes one SQLite statement and returns result rows as a `DValue` array. Multi-statement SQL strings are rejected so migrations cannot silently run only their first statement.
|
||||
|
||||
Use named parameters with `:name` placeholders only. Positional `?` placeholders and SQLite's other named marker forms (`@name`, `$name`) are rejected so UCE SQLite queries use the same placeholder style as the MySQL helper. UCE binds parameters with SQLite prepared statements; it does not substitute values into the SQL string.
|
||||
|
||||
@@ -26,12 +26,12 @@ SQLite* db = sqlite_connect("/tmp/app.sqlite");
|
||||
|
||||
StringMap params;
|
||||
params["email"] = "ada@example.test";
|
||||
DTree rows = sqlite_query(db,
|
||||
DValue rows = sqlite_query(db,
|
||||
"select id, email from users where email = :email",
|
||||
params
|
||||
);
|
||||
```
|
||||
|
||||
Result rows are objects keyed by column name. SQLite integer, float, text, blob, and null values are converted to DTree values. Blob values are returned as byte strings.
|
||||
Result rows are objects keyed by column name. SQLite integer, float, text, blob, and null values are converted to DValue values. Blob values are returned as byte strings.
|
||||
|
||||
For statements that do not return rows, inspect `sqlite_affected_rows()` or `sqlite_insert_id()` after the call.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
bool DTree::to_bool(bool default_value = false) const
|
||||
bool DValue::to_bool(bool default_value = false) const
|
||||
|
||||
:params
|
||||
default_value : returned when the value is missing or empty
|
||||
@@ -7,14 +7,14 @@ return value : the value as a boolean, or `default_value`
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
0_DValue
|
||||
json_decode
|
||||
to_f64
|
||||
to_u64
|
||||
to_string
|
||||
|
||||
:content
|
||||
Reads a `DTree` value as a boolean. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
Reads a `DValue` value as a boolean. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
String values such as `true`, `yes`, `on`, and `1` read as true. Values such as `false`, `no`, `off`, `0`, and `null` read as false. Numeric values read as true when non-zero.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
f64 DTree::to_f64(f64 default_value = 0) const
|
||||
f64 DValue::to_f64(f64 default_value = 0) const
|
||||
|
||||
:params
|
||||
default_value : returned when the value is missing or cannot be parsed as a number
|
||||
@@ -7,7 +7,7 @@ return value : the value as a floating-point number, or `default_value`
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
0_DValue
|
||||
json_decode
|
||||
float_val
|
||||
to_bool
|
||||
@@ -15,7 +15,7 @@ to_s64
|
||||
to_u64
|
||||
|
||||
:content
|
||||
Reads a `DTree` value as a floating-point number. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
Reads a `DValue` value as a floating-point number. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
String values are trimmed and parsed permissively: numeric forms and the boolean words understood by `to_bool()` both convert. Boolean values become `1.0` or `0.0`.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
String DTree::to_json(char quote_char = '"') const
|
||||
String DValue::to_json(char quote_char = '"') const
|
||||
|
||||
:params
|
||||
quote_char : quote character used around string output
|
||||
@@ -7,13 +7,13 @@ return value : the scalar as a single JSON token
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
0_DValue
|
||||
json_encode
|
||||
json_decode
|
||||
to_string
|
||||
|
||||
:content
|
||||
Renders a single scalar `DTree` value as a JSON token. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
Renders a single scalar `DValue` value as a JSON token. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
Strings are escaped and quoted, numbers render as numeric literals, and booleans render as `true` / `false`.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
s64 DTree::to_s64(s64 default_value = 0) const
|
||||
s64 DValue::to_s64(s64 default_value = 0) const
|
||||
|
||||
:params
|
||||
default_value : returned when the value is missing or cannot be parsed as a number
|
||||
@@ -7,14 +7,14 @@ return value : the value as a signed 64-bit integer, or `default_value`
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
0_DValue
|
||||
to_u64
|
||||
to_f64
|
||||
to_bool
|
||||
to_string
|
||||
|
||||
:content
|
||||
Reads a `DTree` value as a signed integer. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
Reads a `DValue` value as a signed integer. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
String values are trimmed and parsed permissively: plain integers, floating-point forms (truncated toward zero), and the boolean words understood by `to_bool()` (`yes` reads as `1`) all convert. Results outside the `s64` range clamp to the range boundaries. Boolean values become `1` or `0`.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
String DTree::to_string(String default_value = "") const
|
||||
String DValue::to_string(String default_value = "") const
|
||||
|
||||
:params
|
||||
default_value : returned when the node holds no usable text
|
||||
@@ -7,7 +7,7 @@ return value : the scalar content as text, or `default_value`
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
0_DValue
|
||||
to_s64
|
||||
to_f64
|
||||
to_bool
|
||||
@@ -15,7 +15,7 @@ to_json
|
||||
get_by_path
|
||||
|
||||
:content
|
||||
Reads a `DTree` value as text. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
Reads a `DValue` value as text. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
`default_value` is returned when the value is missing or not text-convertible:
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
:sig
|
||||
StringMap DTree::to_stringmap() const
|
||||
StringMap DValue::to_stringmap() const
|
||||
|
||||
:params
|
||||
return value : a flat `StringMap` projection of the node
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
0_DValue
|
||||
StringMap
|
||||
to_string
|
||||
dtree_keys
|
||||
dtree_values
|
||||
dv_keys
|
||||
dv_values
|
||||
|
||||
:content
|
||||
Converts a `DTree` into a flat `StringMap`. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
Converts a `DValue` into a flat `StringMap`. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
- Map-shaped nodes produce one entry per child, with each child read via `to_string()`. Nested maps flatten to empty strings — this is a one-level projection, not a serializer.
|
||||
- A non-empty scalar produces a single `"value"` entry holding the scalar.
|
||||
@@ -25,7 +25,7 @@ Use this when handing request- or config-shaped data to APIs that take `StringMa
|
||||
|
||||
```cpp
|
||||
StringMap params = context.props["filters"].to_stringmap();
|
||||
DTree rows = sqlite_query(db, "select * from notes where author = :author", params);
|
||||
DValue rows = sqlite_query(db, "select * from notes where author = :author", params);
|
||||
```
|
||||
|
||||
For a faithful representation of nested data, use `json_encode()` instead.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
u64 DTree::to_u64(u64 default_value = 0) const
|
||||
u64 DValue::to_u64(u64 default_value = 0) const
|
||||
|
||||
:params
|
||||
default_value : returned when the value is missing or cannot be parsed as a number
|
||||
@@ -7,7 +7,7 @@ return value : the value as an unsigned 64-bit integer, or `default_value`
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
0_DValue
|
||||
json_decode
|
||||
int_val
|
||||
to_bool
|
||||
@@ -15,7 +15,7 @@ to_s64
|
||||
to_f64
|
||||
|
||||
:content
|
||||
Reads a `DTree` value as an unsigned integer. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
Reads a `DValue` value as an unsigned integer. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
String values are trimmed and parsed permissively, like `to_s64()`. Boolean values become `1` or `0`. Negative values clamp to `0`, and results above the `u64` range clamp to the maximum.
|
||||
|
||||
@@ -34,7 +34,7 @@ u64 limit = context.get["limit"].to_u64(50);
|
||||
u64 owner_id = row.get_by_path("owner/id").to_u64();
|
||||
```
|
||||
|
||||
Use this for counts, identifiers, limits, timestamps, and other non-negative numeric values stored inside a `DTree`.
|
||||
Use this for counts, identifiers, limits, timestamps, and other non-negative numeric values stored inside a `DValue`.
|
||||
|
||||
## Related Concepts
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
:sig
|
||||
DTree* unit_call(String file_name, String function_name, DTree* call_param = null)
|
||||
DValue* unit_call(String file_name, String function_name, DValue* call_param = null)
|
||||
|
||||
:params
|
||||
file_name : UCE file to load and execute
|
||||
function_name : name of the function to invoke
|
||||
call_param : optional, call parameter
|
||||
return value : DTree* returned from function
|
||||
return value : DValue* returned from function
|
||||
|
||||
:see
|
||||
>ob
|
||||
@@ -20,7 +20,7 @@ Calls an exported function inside another UCE file.
|
||||
|
||||
Use `unit_call()` when you need structured data exchange between units rather than rendered HTML output.
|
||||
|
||||
The callee must expose an `EXPORT` function whose name matches `function_name`. Arguments are passed through `call_param`, and the return value is a `DTree*` owned by the callee.
|
||||
The callee must expose an `EXPORT` function whose name matches `function_name`. Arguments are passed through `call_param`, and the return value is a `DValue*` owned by the callee.
|
||||
|
||||
`unit_call()` also understands the request-bound UCE entrypoint names:
|
||||
|
||||
@@ -31,7 +31,7 @@ The callee must expose an `EXPORT` function whose name matches `function_name`.
|
||||
- `ONCE`
|
||||
- `INIT`
|
||||
|
||||
When `function_name` matches one of those macro-style entrypoints, `unit_call()` does not look for a plain `EXPORT DTree* ...` function. Instead, it translates the name to the generated C++ symbol, uses the current `Request` context, and passes `call_param` into `context.props`, matching the normal component invocation model.
|
||||
When `function_name` matches one of those macro-style entrypoints, `unit_call()` does not look for a plain `EXPORT DValue* ...` function. Instead, it translates the name to the generated C++ symbol, uses the current `Request` context, and passes `call_param` into `context.props`, matching the normal component invocation model.
|
||||
|
||||
For `RENDER...` and `COMPONENT...`, the unit's `ONCE(Request& context)` hook is still honored automatically before the selected handler runs.
|
||||
|
||||
@@ -39,7 +39,7 @@ Example:
|
||||
|
||||
```cpp
|
||||
// export a function
|
||||
EXPORT DTree* test_func(DTree* call_param)
|
||||
EXPORT DValue* test_func(DValue* call_param)
|
||||
{
|
||||
print("HELLO FROM TEST FUNCTION");
|
||||
return(0);
|
||||
@@ -52,7 +52,7 @@ unit_call("call_file_funcs.uce", "test_func");
|
||||
Calling a named component handler through `unit_call()`:
|
||||
|
||||
```cpp
|
||||
DTree props;
|
||||
DValue props;
|
||||
props["title"] = "Diagnostics";
|
||||
props["body"] = "Ready";
|
||||
|
||||
@@ -62,7 +62,7 @@ unit_call("components/card.uce", "COMPONENT:BODY", &props);
|
||||
Calling a page render handler through `unit_call()`:
|
||||
|
||||
```cpp
|
||||
DTree props;
|
||||
DValue props;
|
||||
props["section"] = "summary";
|
||||
|
||||
unit_call("reports/summary.uce", "RENDER", &props);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
DTree unit_info(String path = "")
|
||||
DValue unit_info(String path = "")
|
||||
|
||||
:params
|
||||
path : optional UCE unit path. If empty, uses the current executing unit.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
:sig
|
||||
String var_dump(StringMap t, String prefix = "", String postfix = "\n")
|
||||
String var_dump(StringList t, String prefix = "", String postfix = "\n")
|
||||
String var_dump(DTree t, String prefix = "", String postfix = "\n")
|
||||
String var_dump(DValue t, String prefix = "", String postfix = "\n")
|
||||
|
||||
:params
|
||||
t : object to be dumped into a string
|
||||
@@ -9,7 +9,7 @@ return value : string containing a human-friendly representation of 't'
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
0_DValue
|
||||
StringMap
|
||||
json_encode
|
||||
print
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
:sig
|
||||
DTree xml_decode(String s)
|
||||
DValue xml_decode(String s)
|
||||
|
||||
:params
|
||||
s : XML source string
|
||||
return value : element-shaped DTree
|
||||
return value : element-shaped DValue
|
||||
|
||||
:see
|
||||
>markup
|
||||
xml_encode
|
||||
json_decode
|
||||
0_DTree
|
||||
0_DValue
|
||||
String
|
||||
|
||||
:content
|
||||
Parses a simple XML document into a structured `DTree`.
|
||||
Parses a simple XML document into a structured `DValue`.
|
||||
|
||||
`xml_decode()` is intentionally small. It does not validate schemas, DTDs, namespaces, or document types. It parses the first root element and returns the same structural element shape accepted by `xml_encode()`.
|
||||
|
||||
@@ -31,7 +31,7 @@ node["children"] = list of child element nodes
|
||||
Example:
|
||||
|
||||
```uce
|
||||
DTree book = xml_decode("<book id=\"b1\"><title>UCE & XML</title></book>");
|
||||
DValue book = xml_decode("<book id=\"b1\"><title>UCE & XML</title></book>");
|
||||
|
||||
book["name"].to_string(); // book
|
||||
book["attrs"]["id"].to_string(); // b1
|
||||
@@ -42,7 +42,7 @@ book["children"]["0"]["text"].to_string(); // UCE & XML
|
||||
CDATA and numeric entities are folded into text:
|
||||
|
||||
```uce
|
||||
DTree note = xml_decode("<note><![CDATA[5 < 6]]><symbol>AB</symbol></note>");
|
||||
DValue note = xml_decode("<note><![CDATA[5 < 6]]><symbol>AB</symbol></note>");
|
||||
|
||||
note["text"].to_string(); // 5 < 6
|
||||
note["children"]["0"]["text"].to_string(); // AB
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
:sig
|
||||
String xml_encode(DTree t)
|
||||
String xml_encode(DTree t, String root_name)
|
||||
String xml_encode(DValue t)
|
||||
String xml_encode(DValue t, String root_name)
|
||||
|
||||
:params
|
||||
t : tree to serialize as XML
|
||||
@@ -11,11 +11,11 @@ return value : XML string
|
||||
>markup
|
||||
xml_decode
|
||||
json_encode
|
||||
0_DTree
|
||||
0_DValue
|
||||
html_escape
|
||||
|
||||
:content
|
||||
Serializes a `DTree` into a simple XML string.
|
||||
Serializes a `DValue` into a simple XML string.
|
||||
|
||||
`xml_encode()` does not validate against a schema, DTD, or namespace rules. It is a structural converter for application data, similar in spirit to `json_encode()`.
|
||||
|
||||
@@ -33,11 +33,11 @@ node["children"] = list of child element nodes
|
||||
Example:
|
||||
|
||||
```uce
|
||||
DTree book;
|
||||
DValue book;
|
||||
book["name"] = "book";
|
||||
book["attrs"]["id"] = "b1";
|
||||
|
||||
DTree title;
|
||||
DValue title;
|
||||
title["name"] = "title";
|
||||
title["text"] = "UCE & XML";
|
||||
book["children"].push(title);
|
||||
@@ -54,7 +54,7 @@ The result is:
|
||||
For simple map/list/scalar trees, `xml_encode()` creates ordinary child elements:
|
||||
|
||||
```uce
|
||||
DTree payload;
|
||||
DValue payload;
|
||||
payload["title"] = "Hello";
|
||||
payload["count"] = "3";
|
||||
|
||||
@@ -73,4 +73,4 @@ Notes:
|
||||
- Text and attribute values are escaped.
|
||||
- List values use repeated `<item>` children.
|
||||
- Empty elements serialize as self-closing tags.
|
||||
- Map child order follows `DTree` map iteration order.
|
||||
- Map child order follows `DValue` map iteration order.
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
:sig
|
||||
DTree yaml_decode(String s)
|
||||
DValue yaml_decode(String s)
|
||||
|
||||
:params
|
||||
s : YAML source string
|
||||
return value : decoded DTree
|
||||
return value : decoded DValue
|
||||
|
||||
:see
|
||||
>markup
|
||||
yaml_encode
|
||||
json_decode
|
||||
xml_decode
|
||||
0_DTree
|
||||
0_DValue
|
||||
|
||||
:content
|
||||
Parses a practical YAML subset into a `DTree`.
|
||||
Parses a practical YAML subset into a `DValue`.
|
||||
|
||||
`yaml_decode()` is designed for concise UCE config files. It intentionally avoids full YAML schema behavior and does not support anchors, aliases, tags, directives, or complex inline collection syntax.
|
||||
|
||||
@@ -30,7 +30,7 @@ String source = "app:\n"
|
||||
" - site\n"
|
||||
" - cache\n";
|
||||
|
||||
DTree cfg = yaml_decode(source);
|
||||
DValue cfg = yaml_decode(source);
|
||||
|
||||
cfg["app"]["name"].to_string(); // UCE Starter
|
||||
cfg["app"]["debug"].to_bool(); // true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
String yaml_encode(DTree t)
|
||||
String yaml_encode(DValue t)
|
||||
|
||||
:params
|
||||
t : tree to serialize as YAML
|
||||
@@ -10,10 +10,10 @@ return value : YAML string
|
||||
yaml_decode
|
||||
json_encode
|
||||
xml_encode
|
||||
0_DTree
|
||||
0_DValue
|
||||
|
||||
:content
|
||||
Serializes a `DTree` into a compact YAML string for configuration files.
|
||||
Serializes a `DValue` into a compact YAML string for configuration files.
|
||||
|
||||
`yaml_encode()` focuses on the ordinary data shapes UCE apps use for config: nested maps, lists, strings, booleans, and numeric `f64` values. It does not emit YAML tags, anchors, aliases, or custom schema features.
|
||||
|
||||
@@ -22,12 +22,12 @@ Try the live example in the [YAML demo](../demo/yaml.uce).
|
||||
Example:
|
||||
|
||||
```uce
|
||||
DTree cfg;
|
||||
DValue cfg;
|
||||
cfg["app"]["name"] = "UCE Starter";
|
||||
cfg["app"]["debug"].set_bool(true);
|
||||
cfg["app"]["port"] = (f64)8080;
|
||||
|
||||
DTree path;
|
||||
DValue path;
|
||||
path = "site";
|
||||
cfg["app"]["paths"].push(path);
|
||||
path = "cache";
|
||||
@@ -50,8 +50,8 @@ app:
|
||||
|
||||
Notes:
|
||||
|
||||
- Map keys are emitted in `DTree` map iteration order.
|
||||
- Map keys are emitted in `DValue` map iteration order.
|
||||
- Strings are left plain when safe and quoted when needed.
|
||||
- Multiline strings are emitted as literal block scalars with `|`.
|
||||
- Empty strings are emitted as `""` so they are not confused with YAML null.
|
||||
- Decoded numeric-looking config values are strings unless the original `DTree` value was explicitly numeric.
|
||||
- Decoded numeric-looking config values are strings unless the original `DValue` value was explicitly numeric.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
bool zip_create(String zip_file_name, DTree entries)
|
||||
bool zip_create(String zip_file_name, DValue entries)
|
||||
|
||||
:params
|
||||
zip_file_name : path to the archive to create
|
||||
@@ -11,7 +11,7 @@ return value : `true` when the archive is written
|
||||
zip_list
|
||||
zip_read
|
||||
zip_extract
|
||||
DTree
|
||||
DValue
|
||||
|
||||
:content
|
||||
Creates a ZIP archive at `zip_file_name`.
|
||||
@@ -19,7 +19,7 @@ Creates a ZIP archive at `zip_file_name`.
|
||||
`entries` can be a simple map where each key is the archive member name and each value is the file content:
|
||||
|
||||
```uce
|
||||
DTree entries;
|
||||
DValue entries;
|
||||
entries["hello.txt"] = "Hello ZIP";
|
||||
entries["nested/readme.txt"] = "Nested file";
|
||||
zip_create("/tmp/example.zip", entries);
|
||||
@@ -28,7 +28,7 @@ zip_create("/tmp/example.zip", entries);
|
||||
For list-shaped input or when the map key should not be the member name, each child can provide explicit fields:
|
||||
|
||||
```uce
|
||||
DTree item;
|
||||
DValue item;
|
||||
item["name"] = "data/value.txt";
|
||||
item["content"] = "42";
|
||||
entries["ignored-key"] = item;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
:sig
|
||||
DTree zip_list(String zip_file_name)
|
||||
DValue zip_list(String zip_file_name)
|
||||
|
||||
:params
|
||||
zip_file_name : path to the ZIP archive to inspect
|
||||
return value : DTree containing archive metadata and an `entries` array
|
||||
return value : DValue containing archive metadata and an `entries` array
|
||||
|
||||
:see
|
||||
>sys
|
||||
zip_create
|
||||
zip_read
|
||||
zip_extract
|
||||
DTree
|
||||
DValue
|
||||
|
||||
:content
|
||||
Reads the central directory from `zip_file_name` and returns structured metadata.
|
||||
@@ -33,6 +33,6 @@ Each entry contains:
|
||||
Example:
|
||||
|
||||
```uce
|
||||
DTree info = zip_list("/tmp/example.zip");
|
||||
DValue info = zip_list("/tmp/example.zip");
|
||||
print(json_encode(info));
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree error = context.call["error"];
|
||||
DValue error = context.call["error"];
|
||||
|
||||
<>
|
||||
<!doctype html>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree error = context.call["error"];
|
||||
DValue error = context.call["error"];
|
||||
String signal_name = error["signal_name"].to_string();
|
||||
|
||||
<>
|
||||
|
||||
@@ -6,7 +6,7 @@ COMPONENT(Request& context)
|
||||
String subtitle = first(context.props["subtitle"].to_string(), "Choose your preferred authentication method");
|
||||
String callback_url = first(context.props["callback_url"].to_string(), app_link("auth/callback", context));
|
||||
|
||||
DTree services = context.props["services"];
|
||||
DValue services = context.props["services"];
|
||||
if(services.get_type_name() != "array" || services["google"]["name"].to_string() == "")
|
||||
{
|
||||
services["google"]["name"] = "Google";
|
||||
@@ -40,7 +40,7 @@ COMPONENT(Request& context)
|
||||
<h2><?= title ?></h2>
|
||||
<label><?= subtitle ?></label>
|
||||
</div>
|
||||
<? services.each([&](DTree service, String service_key) {
|
||||
<? services.each([&](DValue service, String service_key) {
|
||||
String client_id = context.props[service_key + "_client_id"].to_string();
|
||||
String handler_name = ascii_safe_name(service_key);
|
||||
String service_name = service["name"].to_string();
|
||||
|
||||
@@ -34,9 +34,9 @@ String data_format_bytes(String raw, bool disk = false)
|
||||
return(String(buf) + " " + units[unit_index]);
|
||||
}
|
||||
|
||||
DTree data_format_value(DTree value, DTree row, DTree column)
|
||||
DValue data_format_value(DValue value, DValue row, DValue column)
|
||||
{
|
||||
DTree result;
|
||||
DValue result;
|
||||
String format = column["format"].to_string();
|
||||
String raw = value.to_string();
|
||||
String sort_value = raw;
|
||||
@@ -98,7 +98,7 @@ COMPONENT:SUMMARY_METRICS(Request& context)
|
||||
</div>
|
||||
<? } ?>
|
||||
<div class="dashboard-stat-grid">
|
||||
<? context.props["items"].each([&](DTree item, String key) {
|
||||
<? context.props["items"].each([&](DValue item, String key) {
|
||||
String tone = first(item["tone"].to_string(), "info");
|
||||
String href = item["href"].to_string();
|
||||
String tag = href != "" ? "a" : "div";
|
||||
@@ -162,7 +162,7 @@ COMPONENT:SORTABLE_TABLE(Request& context)
|
||||
String subtitle = context.props["subtitle"].to_string();
|
||||
String empty_label = first(context.props["empty_label"].to_string(), "No data available");
|
||||
|
||||
DTree init_options;
|
||||
DValue init_options;
|
||||
init_options["storageKey"] = first(context.props["storage_key"].to_string(), "starter.sort." + table_id);
|
||||
if(context.props["sort"]["column"].to_string() != "")
|
||||
{
|
||||
@@ -182,7 +182,7 @@ COMPONENT:SORTABLE_TABLE(Request& context)
|
||||
<table id="<?= table_id ?>" class="u-sortable-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<? context.props["columns"].each([&](DTree column, String key) {
|
||||
<? context.props["columns"].each([&](DValue column, String key) {
|
||||
String align = first(column["align"].to_string(), "left");
|
||||
bool sortable = column["sortable"].to_string() == "" || column["sortable"].to_string() == "1";
|
||||
?><th scope="col" class="align-<?= align ?>"<?= sortable ? "" : " data-sortable=\"false\"" ?>><?= first(column["label"].to_string(), column["key"].to_string(), "Column") ?></th><?
|
||||
@@ -193,13 +193,13 @@ COMPONENT:SORTABLE_TABLE(Request& context)
|
||||
<? if(context.props["rows"].get_type_name() != "array" || context.props["rows"]["0"].get_type_name() != "array") { ?>
|
||||
<tr data-empty-row="1"><td colspan="<?= context.props["columns"]._map.size() == 0 ? "1" : std::to_string(context.props["columns"]._map.size()) ?>" class="muted"><?= empty_label ?></td></tr>
|
||||
<? } ?>
|
||||
<? context.props["rows"].each([&](DTree row, String key) {
|
||||
<? context.props["rows"].each([&](DValue row, String key) {
|
||||
if(row.get_type_name() != "array")
|
||||
return;
|
||||
?><tr><?
|
||||
context.props["columns"].each([&](DTree column, String ckey) {
|
||||
context.props["columns"].each([&](DValue column, String ckey) {
|
||||
String col_key = column["key"].to_string();
|
||||
DTree formatted = data_format_value(row[col_key], row, column);
|
||||
DValue formatted = data_format_value(row[col_key], row, column);
|
||||
?><td class="align-<?= first(column["align"].to_string(), "left") ?>" data-sort-value="<?= formatted["sort"].to_string() ?>"><?= formatted["display"].to_string() ?></td><?
|
||||
});
|
||||
?></tr><?
|
||||
@@ -221,7 +221,7 @@ COMPONENT:DATA_TABLE(Request& context)
|
||||
{
|
||||
String table_id = first(context.props["id"].to_string(), "data-grid-" + std::to_string((u64)time()));
|
||||
|
||||
DTree columns = context.props["columns"];
|
||||
DValue columns = context.props["columns"];
|
||||
if(columns.get_type_name() != "array")
|
||||
columns.set_array();
|
||||
bool has_explicit_columns = columns.is_list() &&
|
||||
@@ -230,8 +230,8 @@ COMPONENT:DATA_TABLE(Request& context)
|
||||
columns["0"]["field"].to_string() != "";
|
||||
if(!has_explicit_columns && context.props["items"]["0"].get_type_name() == "array")
|
||||
{
|
||||
context.props["items"]["0"].each([&](DTree value, String key) {
|
||||
DTree col;
|
||||
context.props["items"]["0"].each([&](DValue value, String key) {
|
||||
DValue col;
|
||||
col["field"] = key;
|
||||
col["headerName"] = key;
|
||||
col["sortable"].set_bool(true);
|
||||
@@ -242,30 +242,30 @@ COMPONENT:DATA_TABLE(Request& context)
|
||||
}
|
||||
if(!columns.is_list())
|
||||
{
|
||||
DTree normalized_columns;
|
||||
DValue normalized_columns;
|
||||
normalized_columns.set_array();
|
||||
columns.each([&](DTree column, String key) {
|
||||
columns.each([&](DValue column, String key) {
|
||||
if(column.get_type_name() == "array")
|
||||
normalized_columns.push(column);
|
||||
});
|
||||
columns = normalized_columns;
|
||||
}
|
||||
|
||||
DTree row_data = context.props["items"];
|
||||
DValue row_data = context.props["items"];
|
||||
if(row_data.get_type_name() != "array")
|
||||
row_data.set_array();
|
||||
if(!row_data.is_list())
|
||||
{
|
||||
DTree normalized_rows;
|
||||
DValue normalized_rows;
|
||||
normalized_rows.set_array();
|
||||
row_data.each([&](DTree row, String key) {
|
||||
row_data.each([&](DValue row, String key) {
|
||||
if(row.get_type_name() == "array")
|
||||
normalized_rows.push(row);
|
||||
});
|
||||
row_data = normalized_rows;
|
||||
}
|
||||
|
||||
DTree grid_options;
|
||||
DValue grid_options;
|
||||
grid_options["pagination"].set_bool(true);
|
||||
grid_options["paginationPageSize"] = first(context.props["options"]["paginationPageSize"].to_string(), "20");
|
||||
grid_options["paginationPageSizeSelector"].set_bool(false);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#load "../../lib/app.uce"
|
||||
|
||||
void marketing_default_features(DTree& features)
|
||||
void marketing_default_features(DValue& features)
|
||||
{
|
||||
if(features.get_type_name() == "array" && features["0"]["title"].to_string() != "")
|
||||
return;
|
||||
DTree item;
|
||||
DValue item;
|
||||
|
||||
item["icon"] = "⚡";
|
||||
item["title"] = "Lightning Fast";
|
||||
@@ -194,7 +194,7 @@ COMPONENT:HERO_SECTION(Request& context)
|
||||
|
||||
COMPONENT:FEATURES_GRID(Request& context)
|
||||
{
|
||||
DTree features = context.props["features"];
|
||||
DValue features = context.props["features"];
|
||||
marketing_default_features(features);
|
||||
|
||||
<>
|
||||
@@ -204,7 +204,7 @@ COMPONENT:FEATURES_GRID(Request& context)
|
||||
<p>Discover the powerful features that make development a breeze</p>
|
||||
</div>
|
||||
<div class="features-grid">
|
||||
<? features.each([&](DTree feature, String key) {
|
||||
<? features.each([&](DValue feature, String key) {
|
||||
?><div class="feature-card">
|
||||
<div class="feature-icon"><?: feature["icon"].to_string() ?></div>
|
||||
<h3 class="feature-title"><?= feature["title"].to_string() ?></h3>
|
||||
@@ -288,10 +288,10 @@ COMPONENT:FEATURES_GRID(Request& context)
|
||||
|
||||
COMPONENT:STATS_SECTION(Request& context)
|
||||
{
|
||||
DTree stats = context.props["stats"];
|
||||
DValue stats = context.props["stats"];
|
||||
if(stats.get_type_name() != "array" || stats["0"]["label"].to_string() == "")
|
||||
{
|
||||
DTree item;
|
||||
DValue item;
|
||||
item["number"] = "99.9%"; item["label"] = "Uptime"; stats.push(item); item.clear();
|
||||
item["number"] = "500ms"; item["label"] = "Average Response"; stats.push(item); item.clear();
|
||||
item["number"] = "50K+"; item["label"] = "Active Users"; stats.push(item); item.clear();
|
||||
@@ -306,7 +306,7 @@ COMPONENT:STATS_SECTION(Request& context)
|
||||
<p>Join thousands of developers who have chosen our framework</p>
|
||||
</div>
|
||||
<div class="stats-grid">
|
||||
<? stats.each([&](DTree stat, String key) {
|
||||
<? stats.each([&](DValue stat, String key) {
|
||||
?><div class="stat-item">
|
||||
<div class="stat-number"><?= stat["number"].to_string() ?></div>
|
||||
<div class="stat-label"><?= stat["label"].to_string() ?></div>
|
||||
@@ -364,10 +364,10 @@ COMPONENT:STATS_SECTION(Request& context)
|
||||
|
||||
COMPONENT:BRANDS_SHOWCASE(Request& context)
|
||||
{
|
||||
DTree logos = context.props["logos"];
|
||||
DValue logos = context.props["logos"];
|
||||
if(logos.get_type_name() != "array" || logos["0"]["name"].to_string() == "")
|
||||
{
|
||||
DTree item;
|
||||
DValue item;
|
||||
for(s32 i = 1; i <= 6; i++)
|
||||
{
|
||||
item["name"] = "Brand " + std::to_string(i);
|
||||
@@ -383,7 +383,7 @@ COMPONENT:BRANDS_SHOWCASE(Request& context)
|
||||
<div class="brands-container">
|
||||
<h3 class="brands-title"><?= title ?></h3>
|
||||
<div class="brands-grid">
|
||||
<? logos.each([&](DTree logo, String key) {
|
||||
<? logos.each([&](DValue logo, String key) {
|
||||
?><div class="brand-item">
|
||||
<img src="<?= logo["url"].to_string() ?>" alt="<?= logo["name"].to_string() ?>" />
|
||||
</div><?
|
||||
@@ -410,10 +410,10 @@ COMPONENT:BRANDS_SHOWCASE(Request& context)
|
||||
|
||||
COMPONENT:TESTIMONIALS(Request& context)
|
||||
{
|
||||
DTree testimonials = context.props["testimonials"];
|
||||
DValue testimonials = context.props["testimonials"];
|
||||
if(testimonials.get_type_name() != "array" || testimonials["0"]["name"].to_string() == "")
|
||||
{
|
||||
DTree item;
|
||||
DValue item;
|
||||
item["name"] = "Sarah Johnson"; item["role"] = "Senior Developer at TechCorp"; item["avatar"] = "img/cat01.jpg"; item["content"] = "This framework has revolutionized our development process."; item["rating"] = "5"; testimonials.push(item); item.clear();
|
||||
item["name"] = "Michael Chen"; item["role"] = "CTO at StartupX"; item["avatar"] = "img/cat02.jpg"; item["content"] = "We switched from our legacy system and saw immediate improvements."; item["rating"] = "5"; testimonials.push(item); item.clear();
|
||||
item["name"] = "Emily Rodriguez"; item["role"] = "Full Stack Developer"; item["avatar"] = "img/cat03.jpg"; item["content"] = "The documentation is excellent and the learning curve is gentle."; item["rating"] = "5"; testimonials.push(item);
|
||||
@@ -427,7 +427,7 @@ COMPONENT:TESTIMONIALS(Request& context)
|
||||
<p>Don't just take our word for it - hear from the community</p>
|
||||
</div>
|
||||
<div class="testimonials-grid">
|
||||
<? testimonials.each([&](DTree testimonial, String key) {
|
||||
<? testimonials.each([&](DValue testimonial, String key) {
|
||||
?><div class="testimonial-card">
|
||||
<div class="testimonial-content">
|
||||
<div class="quote-icon">"</div>
|
||||
@@ -473,11 +473,11 @@ COMPONENT:TESTIMONIALS(Request& context)
|
||||
|
||||
COMPONENT:PRICING_TABLE(Request& context)
|
||||
{
|
||||
DTree plans = context.props["plans"];
|
||||
DValue plans = context.props["plans"];
|
||||
if(plans.get_type_name() != "array" || plans["0"]["name"].to_string() == "")
|
||||
{
|
||||
DTree plan;
|
||||
DTree feature;
|
||||
DValue plan;
|
||||
DValue feature;
|
||||
|
||||
plan["name"] = "Starter"; plan["price"] = "Free"; plan["period"] = ""; plan["description"] = "Perfect for small projects and learning"; plan["cta"] = "Start Free"; feature = "Up to 3 projects"; plan["features"].push(feature); feature = "Community support"; plan["features"].push(feature); feature = "Basic components"; plan["features"].push(feature); plans.push(plan); plan.clear();
|
||||
plan["name"] = "Pro"; plan["price"] = "$24"; plan["period"] = "/mo"; plan["description"] = "For serious products and teams"; plan["cta"] = "Choose Pro"; plan["popular"].set_bool(true); feature = "Unlimited projects"; plan["features"].push(feature); feature = "Priority support"; plan["features"].push(feature); feature = "Advanced components"; plan["features"].push(feature); plans.push(plan); plan.clear();
|
||||
@@ -492,7 +492,7 @@ COMPONENT:PRICING_TABLE(Request& context)
|
||||
<p>Choose the plan that fits your product stage.</p>
|
||||
</div>
|
||||
<div class="pricing-grid">
|
||||
<? plans.each([&](DTree plan, String key) {
|
||||
<? plans.each([&](DValue plan, String key) {
|
||||
bool popular = plan["popular"].to_string() != "";
|
||||
?><div class="pricing-card<?= popular ? " popular" : "" ?>">
|
||||
<? if(popular) { ?><div class="popular-badge">Most Popular</div><? } ?>
|
||||
@@ -505,7 +505,7 @@ COMPONENT:PRICING_TABLE(Request& context)
|
||||
<p class="plan-description"><?= plan["description"].to_string() ?></p>
|
||||
</div>
|
||||
<ul class="plan-features">
|
||||
<? plan["features"].each([&](DTree feature, String feature_key) { ?><li><?= feature.to_string() ?></li><? }); ?>
|
||||
<? plan["features"].each([&](DValue feature, String feature_key) { ?><li><?= feature.to_string() ?></li><? }); ?>
|
||||
</ul>
|
||||
<div class="plan-footer">
|
||||
<button class="btn <?= popular ? "btn-primary" : "btn-outline" ?> btn-large plan-cta"><?= first(plan["cta"].to_string(), "Choose Plan") ?></button>
|
||||
|
||||
@@ -107,7 +107,7 @@ COMPONENT(Request& context)
|
||||
<div id="theme-menu" class="theme-menu" hidden>
|
||||
<div class="theme-menu-title">Available Themes</div>
|
||||
<div class="theme-menu-list">
|
||||
<? context.cfg.get_by_path("theme/options").each([&](DTree theme_info, String theme_key) {
|
||||
<? context.cfg.get_by_path("theme/options").each([&](DValue theme_info, String theme_key) {
|
||||
StringMap params;
|
||||
params["theme"] = theme_key;
|
||||
String href = app_link(route_path == "index" ? "" : route_path, params, context);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
DTree asset_props;
|
||||
DValue asset_props;
|
||||
asset_props["css"]["0"] = "themes/common/css/gauges.css";
|
||||
asset_props["js"]["0"] = "components/gauges/common.js";
|
||||
print(component("../theme/assets", asset_props, context));
|
||||
@@ -11,7 +11,7 @@ COMPONENT(Request& context)
|
||||
String title = context.props["title"].to_string();
|
||||
String subtitle = context.props["subtitle"].to_string();
|
||||
String style = context.props["style"].to_string();
|
||||
DTree scale = context.props["scale"];
|
||||
DValue scale = context.props["scale"];
|
||||
|
||||
<>
|
||||
<div class="arcgauge-set" id="<?= gauge_attr_id(gauge_id) ?>" style="<?= style ?>">
|
||||
@@ -22,9 +22,9 @@ COMPONENT(Request& context)
|
||||
</div>
|
||||
<? } ?>
|
||||
<div class="arcgauge-grid">
|
||||
<? context.props["items"].each([&](DTree item, String item_id_raw) {
|
||||
DTree merged = scale;
|
||||
item.each([&](DTree value, String key) { merged[key] = value; });
|
||||
<? context.props["items"].each([&](DValue item, String item_id_raw) {
|
||||
DValue merged = scale;
|
||||
item.each([&](DValue value, String key) { merged[key] = value; });
|
||||
String item_id = gauge_attr_id(item_id_raw);
|
||||
f64 value = float_val(first(merged["value"].to_string(), "0"));
|
||||
f64 max_value = float_val(first(merged["max"].to_string(), "100"));
|
||||
|
||||
@@ -21,10 +21,10 @@ String gauge_attr_id(String value)
|
||||
return(ascii_safe_name(value));
|
||||
}
|
||||
|
||||
String gauge_range_color(DTree ranges, f64 value)
|
||||
String gauge_range_color(DValue ranges, f64 value)
|
||||
{
|
||||
String matched = "";
|
||||
ranges.each([&](DTree range, String key) {
|
||||
ranges.each([&](DValue range, String key) {
|
||||
f64 from_value = float_val(first(range["from"].to_string(), "-999999999"));
|
||||
f64 to_value = float_val(first(range["to"].to_string(), "999999999"));
|
||||
if(value >= from_value && value <= to_value && matched == "")
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
DTree asset_props;
|
||||
DValue asset_props;
|
||||
asset_props["css"]["0"] = "themes/common/css/gauges.css";
|
||||
asset_props["js"]["0"] = "components/gauges/common.js";
|
||||
print(component("../theme/assets", asset_props, context));
|
||||
@@ -12,7 +12,7 @@ COMPONENT(Request& context)
|
||||
String title = context.props["title"].to_string();
|
||||
String subtitle = context.props["subtitle"].to_string();
|
||||
f64 size = float_val(first(context.props["size"].to_string(), "200"));
|
||||
DTree scale = context.props["scale"];
|
||||
DValue scale = context.props["scale"];
|
||||
f64 scale_angle_start = float_val(first(scale["angle_start"].to_string(), std::to_string(-gauge_pi())));
|
||||
f64 scale_angle_end = float_val(first(scale["angle_end"].to_string(), "0"));
|
||||
f64 img_height = float_val(first(context.props["img_height"].to_string(), std::to_string(size * std::max(cos(scale_angle_start), cos(scale_angle_end)))));
|
||||
@@ -26,9 +26,9 @@ COMPONENT(Request& context)
|
||||
</div>
|
||||
<? } ?>
|
||||
<div class="needlegauge-grid">
|
||||
<? context.props["items"].each([&](DTree item, String item_id_raw) {
|
||||
DTree merged = scale;
|
||||
item.each([&](DTree value, String key) { merged[key] = value; });
|
||||
<? context.props["items"].each([&](DValue item, String item_id_raw) {
|
||||
DValue merged = scale;
|
||||
item.each([&](DValue value, String key) { merged[key] = value; });
|
||||
String item_id = gauge_attr_id(item_id_raw);
|
||||
String label = first(merged["label"].to_string(), item_id_raw);
|
||||
String tooltip = merged["tooltip"].to_string();
|
||||
@@ -85,7 +85,7 @@ COMPONENT(Request& context)
|
||||
String segments_html = "";
|
||||
if(merged["color"].get_type_name() == "array")
|
||||
{
|
||||
merged["color"].each([&](DTree range, String key) {
|
||||
merged["color"].each([&](DValue range, String key) {
|
||||
f64 range_from = std::max(float_val(first(range["from"].to_string(), std::to_string(min_value))), min_value);
|
||||
f64 range_to = std::min(float_val(first(range["to"].to_string(), std::to_string(max_value))), max_value);
|
||||
f64 angle_from = angle_start + ((range_from - min_value) / vrange) * (angle_end - angle_start);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
DTree asset_props;
|
||||
DValue asset_props;
|
||||
asset_props["css"]["0"] = "themes/common/css/gauges.css";
|
||||
asset_props["js"]["0"] = "components/gauges/common.js";
|
||||
print(component("../theme/assets", asset_props, context));
|
||||
@@ -17,8 +17,8 @@ COMPONENT(Request& context)
|
||||
String subtitle = context.props["subtitle"].to_string();
|
||||
String layout = first(context.props["layout"].to_string(), "horizontal");
|
||||
String bar_color_default = context.props["bar-color"].to_string();
|
||||
DTree scale = context.props["scale"];
|
||||
DTree markers = context.props["markers"];
|
||||
DValue scale = context.props["scale"];
|
||||
DValue markers = context.props["markers"];
|
||||
|
||||
StringList default_palette;
|
||||
default_palette.push_back("var(--success, #10b981)");
|
||||
@@ -37,9 +37,9 @@ COMPONENT(Request& context)
|
||||
<? } ?>
|
||||
<? if(layout == "horizontal") { ?>
|
||||
<div class="progressbar-grid progressbar-grid-horizontal">
|
||||
<? context.props["items"].each([&](DTree bar, String bar_id) {
|
||||
DTree merged = scale;
|
||||
bar.each([&](DTree item, String key) { merged[key] = item; });
|
||||
<? context.props["items"].each([&](DValue bar, String bar_id) {
|
||||
DValue merged = scale;
|
||||
bar.each([&](DValue item, String key) { merged[key] = item; });
|
||||
String item_id = gauge_attr_id(bar_id);
|
||||
String merged_item_style = item_style;
|
||||
if(merged["style"].to_string() != "")
|
||||
@@ -73,7 +73,7 @@ COMPONENT(Request& context)
|
||||
<div class="progressbar-background progressbar-background-horizontal">
|
||||
<div class="progressbar-bar" id="<?= gauge_attr_id(gauge_id) ?>-<?= item_id ?>-bar"
|
||||
style="background-color: <?= resolved_color ?>; <?= bar_style ?> opacity: <?= opacity ?>; width: <?= std::to_string(pct_value) ?>%;"></div>
|
||||
<? markers.each([&](DTree marker, String marker_id) {
|
||||
<? markers.each([&](DValue marker, String marker_id) {
|
||||
f64 marker_value = float_val(first(marker["value"].to_string(), "0"));
|
||||
f64 marker_pct = gauge_clamp_value(((marker_value - min_value) / vrange) * 100.0, 0.0, 100.0);
|
||||
String marker_color = first(marker["color"].to_string(), "var(--primary-light)");
|
||||
@@ -89,9 +89,9 @@ COMPONENT(Request& context)
|
||||
</div>
|
||||
<? } else { ?>
|
||||
<div class="progressbar-grid progressbar-grid-vertical" style="--progressbar-height: <?= first(context.props["height"].to_string(), "240") ?>px;">
|
||||
<? context.props["items"].each([&](DTree bar, String bar_id) {
|
||||
DTree merged = scale;
|
||||
bar.each([&](DTree item, String key) { merged[key] = item; });
|
||||
<? context.props["items"].each([&](DValue bar, String bar_id) {
|
||||
DValue merged = scale;
|
||||
bar.each([&](DValue item, String key) { merged[key] = item; });
|
||||
String item_id = gauge_attr_id(bar_id);
|
||||
String merged_item_style = item_style;
|
||||
if(merged["style"].to_string() != "")
|
||||
@@ -122,7 +122,7 @@ COMPONENT(Request& context)
|
||||
<div class="progressbar-background progressbar-background-vertical">
|
||||
<div class="progressbar-bar" id="<?= gauge_attr_id(gauge_id) ?>-<?= item_id ?>-bar"
|
||||
style="background-color: <?= resolved_color ?>; <?= bar_style ?> opacity: <?= opacity ?>; height: <?= std::to_string(pct_value) ?>%;"></div>
|
||||
<? markers.each([&](DTree marker, String marker_id) {
|
||||
<? markers.each([&](DValue marker, String marker_id) {
|
||||
f64 marker_value = float_val(first(marker["value"].to_string(), "0"));
|
||||
f64 marker_pct = gauge_clamp_value(((marker_value - min_value) / vrange) * 100.0, 0.0, 100.0);
|
||||
String marker_color = first(marker["color"].to_string(), "var(--primary-light)");
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
StarterUser users(context);
|
||||
DTree user = users.current();
|
||||
DValue user = users.current();
|
||||
bool signed_in = user["email"].to_string() != "";
|
||||
String theme_key = context.cfg.get_by_path("theme/key").to_string();
|
||||
String wrapper_class = first(context.props["wrapper_class"].to_string(), theme_key == "localfirst" ? "admin-account-card" : "nav-account nav-menu");
|
||||
|
||||
@@ -18,7 +18,7 @@ void app_asset_tag_once(Request& context, String kind, String path)
|
||||
}
|
||||
}
|
||||
|
||||
void app_asset_tags(Request& context, String kind, DTree assets)
|
||||
void app_asset_tags(Request& context, String kind, DValue assets)
|
||||
{
|
||||
String scalar = assets.to_string();
|
||||
if(scalar != "")
|
||||
@@ -26,7 +26,7 @@ void app_asset_tags(Request& context, String kind, DTree assets)
|
||||
app_asset_tag_once(context, kind, scalar);
|
||||
return;
|
||||
}
|
||||
assets.each([&](const DTree& item, String key) {
|
||||
assets.each([&](const DValue& item, String key) {
|
||||
app_asset_tag_once(context, kind, item.to_string());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ COMPONENT(Request& context)
|
||||
if(nav_class == "" && (context.cfg.get_by_path("theme/key").to_string() == "portal-dark" || context.cfg.get_by_path("theme/key").to_string() == "dark" || context.cfg.get_by_path("theme/key").to_string() == "retro-gaming"))
|
||||
nav_class = "nav-shell";
|
||||
|
||||
DTree account_props;
|
||||
DValue account_props;
|
||||
if(context.props["account_wrapper_class"].to_string() != "")
|
||||
account_props["wrapper_class"] = context.props["account_wrapper_class"];
|
||||
if(context.props["links_wrapper_class"].to_string() != "")
|
||||
@@ -18,7 +18,7 @@ COMPONENT(Request& context)
|
||||
<nav<?: nav_class != "" ? " class=\"" + html_escape(nav_class) + "\"" : "" ?>>
|
||||
<div class="nav-menu">
|
||||
<a href="<?= app_link("", context) ?>"><?= context.cfg.get_by_path("site/name").to_string() ?></a>
|
||||
<? context.cfg.get_by_path("menu").each([&](DTree menu_item, String menu_key) {
|
||||
<? context.cfg.get_by_path("menu").each([&](DValue menu_item, String menu_key) {
|
||||
if(menu_item["hidden"].to_string() == "1")
|
||||
return;
|
||||
String href = menu_item["external"].to_string() != "" ? "/" + menu_key : app_link(menu_key, context);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
DTree get_config()
|
||||
DValue get_config()
|
||||
{
|
||||
DTree config;
|
||||
DValue config;
|
||||
|
||||
config["site"]["name"] = "UCE Starter";
|
||||
config["site"]["default_page_title"] = "Home";
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
#load "lib/app.uce"
|
||||
|
||||
DTree starter_router_result(String file, String param = "")
|
||||
DValue starter_router_result(String file, String param = "")
|
||||
{
|
||||
DTree result;
|
||||
DValue result;
|
||||
result["file"] = file;
|
||||
if(param != "")
|
||||
result["param"] = param;
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree starter_router_resolve(Request& context)
|
||||
DValue starter_router_resolve(Request& context)
|
||||
{
|
||||
String lpath = context.call["route"]["l_path"].to_string();
|
||||
if(lpath == "")
|
||||
return(DTree());
|
||||
return(DValue());
|
||||
|
||||
String exact = "views/" + lpath + ".uce";
|
||||
if(file_exists(exact))
|
||||
@@ -34,14 +34,14 @@ DTree starter_router_resolve(Request& context)
|
||||
return(starter_router_result(parent_index, param));
|
||||
}
|
||||
|
||||
return(DTree());
|
||||
return(DValue());
|
||||
}
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
app_init(context);
|
||||
|
||||
DTree resolved = starter_router_resolve(context);
|
||||
DValue resolved = starter_router_resolve(context);
|
||||
|
||||
ob_start();
|
||||
if(resolved["file"].to_string() != "")
|
||||
@@ -52,7 +52,7 @@ RENDER(Request& context)
|
||||
}
|
||||
else
|
||||
{
|
||||
DTree notfound_props;
|
||||
DValue notfound_props;
|
||||
notfound_props["message"] = "The requested page does not exist.";
|
||||
print(component("components/basic/notfound", notfound_props, context));
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ void app_init(Request& context)
|
||||
context.call["route"]["page"] = context.params["ROUTE_PAGE"];
|
||||
context.call["route"]["valid"].set_bool(context.params["ROUTE_VALID"] != "0");
|
||||
|
||||
DTree config = get_config();
|
||||
DValue config = get_config();
|
||||
session_start();
|
||||
String requested_theme = first(context.get["theme"], context.session["app_theme"], config["theme"]["key"].to_string());
|
||||
if(config["theme"]["options"][requested_theme].get_type_name() != "array")
|
||||
@@ -53,8 +53,8 @@ void app_init(Request& context)
|
||||
if(context.get["theme"] != "")
|
||||
context.session["app_theme"] = requested_theme;
|
||||
|
||||
DTree selected_theme = config["theme"]["options"][requested_theme];
|
||||
selected_theme.each([&](DTree item, String key) {
|
||||
DValue selected_theme = config["theme"]["options"][requested_theme];
|
||||
selected_theme.each([&](DValue item, String key) {
|
||||
config["theme"][key] = item;
|
||||
});
|
||||
config["theme"]["key"] = requested_theme;
|
||||
|
||||
@@ -50,9 +50,9 @@ struct AppUser
|
||||
return(hash);
|
||||
}
|
||||
|
||||
static DTree read_json_file(String file_name)
|
||||
static DValue read_json_file(String file_name)
|
||||
{
|
||||
DTree result;
|
||||
DValue result;
|
||||
if(!file_exists(file_name))
|
||||
return(result);
|
||||
String raw = trim(file_get_contents(file_name));
|
||||
@@ -61,24 +61,24 @@ struct AppUser
|
||||
return(json_decode(raw));
|
||||
}
|
||||
|
||||
static bool write_json_file(String file_name, DTree data)
|
||||
static bool write_json_file(String file_name, DValue data)
|
||||
{
|
||||
mkdir(dirname(file_name));
|
||||
return(file_put_contents(file_name, json_encode(data)));
|
||||
}
|
||||
|
||||
DTree load(String email)
|
||||
DValue load(String email)
|
||||
{
|
||||
DTree user = read_json_file(file_name(email));
|
||||
DValue user = read_json_file(file_name(email));
|
||||
if(user.get_type_name() != "array")
|
||||
return(DTree());
|
||||
return(DValue());
|
||||
user["id"] = normalize_email(email);
|
||||
return(user);
|
||||
}
|
||||
|
||||
DTree create(String email, String password)
|
||||
DValue create(String email, String password)
|
||||
{
|
||||
DTree result;
|
||||
DValue result;
|
||||
email = normalize_email(email);
|
||||
password = trim(password);
|
||||
result["message"] = "";
|
||||
@@ -95,7 +95,7 @@ struct AppUser
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree existing = load(email);
|
||||
DValue existing = load(email);
|
||||
if(existing.get_type_name() == "array" && existing["email"].to_string() != "")
|
||||
{
|
||||
result["message"] = "user_exists";
|
||||
@@ -103,12 +103,12 @@ struct AppUser
|
||||
}
|
||||
|
||||
String salt = gen_sha1(std::to_string((u64)time()) + ":" + std::to_string((u64)(time_precise() * 1000000.0)) + ":" + session_id_create()).substr(0, 24);
|
||||
DTree user;
|
||||
DValue user;
|
||||
user["email"] = email;
|
||||
user["salt"] = salt;
|
||||
user["password_hash"] = password_hash(password, salt);
|
||||
user["created"] = std::to_string((u64)time());
|
||||
DTree role;
|
||||
DValue role;
|
||||
role = "user";
|
||||
user["roles"].push(role);
|
||||
|
||||
@@ -120,14 +120,14 @@ struct AppUser
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree sign_in(String email, String password)
|
||||
DValue sign_in(String email, String password)
|
||||
{
|
||||
DTree result;
|
||||
DValue result;
|
||||
result["result"].set_bool(false);
|
||||
email = normalize_email(email);
|
||||
password = trim(password);
|
||||
|
||||
DTree user = load(email);
|
||||
DValue user = load(email);
|
||||
if(user.get_type_name() != "array" || user["email"].to_string() == "")
|
||||
{
|
||||
result["message"] = "no_such_user";
|
||||
@@ -161,7 +161,7 @@ struct AppUser
|
||||
String user_id = context.session[session_key()];
|
||||
if(user_id == "")
|
||||
return(false);
|
||||
DTree user = load(user_id);
|
||||
DValue user = load(user_id);
|
||||
if(user["email"].to_string() == "")
|
||||
{
|
||||
context.session.erase(session_key());
|
||||
@@ -171,11 +171,11 @@ struct AppUser
|
||||
return(true);
|
||||
}
|
||||
|
||||
DTree current()
|
||||
DValue current()
|
||||
{
|
||||
if(is_signed_in())
|
||||
return(context.call["app"]["current_user"]);
|
||||
return(DTree());
|
||||
return(DValue());
|
||||
}
|
||||
|
||||
void logout()
|
||||
|
||||
@@ -4,10 +4,10 @@ COMPONENT(Request& context)
|
||||
{
|
||||
String main_html = context.call["fragments"]["main"].to_string();
|
||||
|
||||
DTree nav_props;
|
||||
DValue nav_props;
|
||||
nav_props["account_wrapper_class"] = "nav-account nav-menu";
|
||||
|
||||
DTree footer_props;
|
||||
DValue footer_props;
|
||||
|
||||
<>
|
||||
<!doctype html>
|
||||
|
||||
@@ -4,7 +4,7 @@ COMPONENT(Request& context)
|
||||
{
|
||||
String main_html = context.call["fragments"]["main"].to_string();
|
||||
|
||||
DTree footer_props;
|
||||
DValue footer_props;
|
||||
|
||||
<>
|
||||
<!doctype html>
|
||||
|
||||
@@ -5,10 +5,10 @@ COMPONENT(Request& context)
|
||||
String main_html = context.call["fragments"]["main"].to_string();
|
||||
String current_path = context.call["route"]["l_path"].to_string();
|
||||
|
||||
DTree global_props;
|
||||
DValue global_props;
|
||||
global_props["cookie_consent"].set_bool(false);
|
||||
|
||||
DTree account_props;
|
||||
DValue account_props;
|
||||
account_props["wrapper_class"] = "admin-account-card";
|
||||
account_props["links_wrapper_class"] = "admin-account-links";
|
||||
account_props["name_class"] = "admin-account-name";
|
||||
@@ -28,7 +28,7 @@ COMPONENT(Request& context)
|
||||
<img class="admin-nav-logo-img" src="<?= app_asset_url("themes/localfirst/img/local_first_logo.png", context) ?>" alt="<?= context.cfg.get_by_path("site/name").to_string() ?>" />
|
||||
</a>
|
||||
</div>
|
||||
<? context.cfg.get_by_path("menu").each([&](DTree menu_item, String menu_key) {
|
||||
<? context.cfg.get_by_path("menu").each([&](DValue menu_item, String menu_key) {
|
||||
if(menu_item["hidden"].to_string() == "1")
|
||||
return;
|
||||
bool active = menu_key != "" && (current_path == menu_key || current_path.rfind(menu_key + "/", 0) == 0);
|
||||
|
||||
@@ -4,10 +4,10 @@ COMPONENT(Request& context)
|
||||
{
|
||||
String main_html = context.call["fragments"]["main"].to_string();
|
||||
|
||||
DTree nav_props;
|
||||
DValue nav_props;
|
||||
nav_props["account_wrapper_class"] = "nav-account nav-menu";
|
||||
|
||||
DTree footer_props;
|
||||
DValue footer_props;
|
||||
|
||||
<>
|
||||
<!doctype html>
|
||||
|
||||
@@ -4,7 +4,7 @@ COMPONENT(Request& context)
|
||||
{
|
||||
String main_html = context.call["fragments"]["main"].to_string();
|
||||
|
||||
DTree footer_props;
|
||||
DValue footer_props;
|
||||
footer_props["inner_class"] = "footer-inner";
|
||||
|
||||
<>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user