Skip to content

Commit 6f525fe

Browse files
committed
Work on advanced content rendering
1 parent 081c22e commit 6f525fe

15 files changed

Lines changed: 1340 additions & 41 deletions

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# MOORC binary selection via environment variable MOORC_TYPE
22
# Options: cargo (default), direct, docker
3-
MOORC_TYPE ?= docker
3+
MOORC_TYPE ?= cargo
44

55
ifeq ($(MOORC_TYPE),cargo)
66
SRC_DIRECTORY = src

src/block.moo

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,58 @@ object BLOCK
99
return <this, {@args}>;
1010
endverb
1111

12-
verb render_as (this none this) owner: HACKER flags: "rxd"
13-
{content_type, event} = args;
12+
verb compose (this none this) owner: HACKER flags: "rxd"
13+
{render_for, content_type, event} = args;
1414
result = {};
1515
for line_no in [1..length(this)]
1616
content = this[line_no];
1717
if (typeof(content) == STR)
1818
result = {@result, content};
1919
elseif (typeof(content) == FLYWEIGHT)
20-
result = {@result, content:render_as(@args)};
20+
result = {@result, content:compose(@args)};
2121
else
2222
raise(E_TYPE);
2323
endif
2424
endfor
25-
return result;
25+
"Return appropriate content flyweight based on content type";
26+
if (content_type == 'text_plain)
27+
return $text_plain:mk(@result);
28+
elseif (content_type == 'text_markdown || content_type == 'text_djot)
29+
"For markdown, pass elements but mark as block content";
30+
return $text_markdown:mk_block(@result);
31+
elseif (content_type == 'text_html)
32+
"For HTML, create paragraph elements";
33+
html_content = $text_html:mk();
34+
for line in (result)
35+
if (typeof(line) == FLYWEIGHT)
36+
"Extract the content from HTML flyweights to nest properly";
37+
line_content = {};
38+
for item in (line)
39+
line_content = {@line_content, item};
40+
endfor
41+
if (length(line_content) == 1)
42+
p_element = {"p", {}, line_content[1]};
43+
else
44+
p_element = {"p", {}, @line_content};
45+
endif
46+
else
47+
p_element = {"p", {}, line};
48+
endif
49+
html_content = html_content:append_element(p_element);
50+
endfor
51+
return html_content;
52+
else
53+
"Default to text_plain";
54+
return $text_plain:mk(@result);
55+
endif
2656
endverb
2757

2858
verb test_multiline_render (this none this) owner: HACKER flags: "rxd"
2959
lines = this:mk("a", "b", "c");
30-
result = lines:render_as("text/plain", true);
60+
result = lines:compose(player, 'text_plain, $nothing);
61+
typeof(result) != FLYWEIGHT && raise(E_ASSERT, "compose should return flyweight: " + toliteral(result));
3162
length(result) != 3 && raise(E_ASSERT, "content wrong length: " + toliteral(result));
63+
rendered = result:render();
64+
typeof(rendered) != LIST && raise(E_ASSERT, "render should return list: " + toliteral(rendered));
3265
endverb
3366
endobject

src/constants.moo

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,8 @@ define BLOCK = #16;
2020
define LOOK = #17;
2121
define LIST_PROTO = #18;
2222
define THING = #19;
23+
define TITLE = #20;
24+
define TEXT_PLAIN = #21;
25+
define TEXT_HTML = #22;
26+
define TEXT_MARKDOWN = #23;
27+
define CONTENT_TESTS = #24;

src/content_tests.moo

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
object CONTENT_TESTS
2+
name: "Content Architecture Unit Tests"
3+
parent: ROOT
4+
location: FIRST_ROOM
5+
owner: HACKER
6+
readable: true
7+
8+
override description = "Comprehensive unit tests for the flyweight-based content architecture.";
9+
10+
verb test_text_plain_basic (this none this) owner: HACKER flags: "rxd"
11+
"Test basic TEXT_PLAIN functionality";
12+
content = $text_plain:mk("Hello", "World");
13+
typeof(content) != FLYWEIGHT && raise(E_ASSERT, "mk should return flyweight");
14+
length(content) != 2 && raise(E_ASSERT, "Should have 2 elements");
15+
"Test append_element";
16+
content2 = content:append_element("!");
17+
length(content2) != 3 && raise(E_ASSERT, "Should have 3 elements after append");
18+
"Test rendering";
19+
rendered = content2:render();
20+
typeof(rendered) != LIST && raise(E_ASSERT, "Rendered should be list");
21+
length(rendered) == 3 && rendered[1] == "Hello" && rendered[2] == "World" && rendered[3] == "!" || raise(E_ASSERT, "Content wrong: " + toliteral(rendered));
22+
endverb
23+
24+
verb test_text_html_basic (this none this) owner: HACKER flags: "rxd"
25+
"Test basic TEXT_HTML functionality";
26+
p_elem = $text_html:mk_element("p", {}, "Hello World");
27+
typeof(p_elem) != FLYWEIGHT && raise(E_ASSERT, "mk_element should return flyweight");
28+
"Test rendering single element";
29+
rendered = p_elem:render();
30+
typeof(rendered) != LIST && raise(E_ASSERT, "Rendered should be list");
31+
length(rendered) == 1 || raise(E_ASSERT, "Should be single HTML string");
32+
"<p>Hello World</p>" in rendered[1] || raise(E_ASSERT, "Should contain p element");
33+
"Test element with attributes";
34+
div_elem = $text_html:mk_element("div", {"class", "test", "id", "main"}, "Content");
35+
div_rendered = div_elem:render();
36+
"class=\"test\"" in div_rendered[1] || raise(E_ASSERT, "Should contain class attribute: " + toliteral(div_rendered[1]));
37+
"id=\"main\"" in div_rendered[1] || raise(E_ASSERT, "Should contain id attribute");
38+
endverb
39+
40+
verb test_content_composition (this none this) owner: HACKER flags: "rxd"
41+
"Test combining different content elements";
42+
html_content = $text_html:mk();
43+
p1 = {"p", {}, "First paragraph"};
44+
p2 = {"p", {}, "Second paragraph"};
45+
combined = html_content:append_element(p1):append_element(p2);
46+
length(combined) == 2 || raise(E_ASSERT, "Should have 2 elements");
47+
rendered = combined:render();
48+
typeof(rendered) == LIST && length(rendered) == 1 || raise(E_ASSERT, "Should be single HTML string");
49+
"<p>First paragraph</p>" in rendered[1] || raise(E_ASSERT, "Should contain first paragraph");
50+
"<p>Second paragraph</p>" in rendered[1] || raise(E_ASSERT, "Should contain second paragraph");
51+
endverb
52+
53+
verb test_block_composition (this none this) owner: HACKER flags: "rxd"
54+
"Test BLOCK composition with different content types";
55+
block = $block:mk("Line 1", "Line 2", "Line 3");
56+
"Test text_plain composition";
57+
plain_content = block:compose(player, 'text_plain, $nothing);
58+
typeof(plain_content) == FLYWEIGHT || raise(E_ASSERT, "Should return flyweight");
59+
length(plain_content) == 3 || raise(E_ASSERT, "Should have 3 lines");
60+
"Test HTML composition";
61+
html_content = block:compose(player, 'text_html, $nothing);
62+
typeof(html_content) == FLYWEIGHT || raise(E_ASSERT, "Should return flyweight");
63+
html_rendered = html_content:render();
64+
typeof(html_rendered) == LIST && length(html_rendered) == 1 || raise(E_ASSERT, "Should be single HTML string");
65+
"Verify all lines are in separate p tags";
66+
html_string = html_rendered[1];
67+
"<p>Line 1</p>" in html_string || raise(E_ASSERT, "Should contain Line 1 in p tag");
68+
"<p>Line 2</p>" in html_string || raise(E_ASSERT, "Should contain Line 2 in p tag");
69+
"<p>Line 3</p>" in html_string || raise(E_ASSERT, "Should contain Line 3 in p tag");
70+
"Test markdown composition";
71+
md_content = block:compose(player, 'text_markdown, $nothing);
72+
typeof(md_content) == FLYWEIGHT || raise(E_ASSERT, "Should return flyweight");
73+
md_rendered = md_content:render();
74+
typeof(md_rendered) == LIST || raise(E_ASSERT, "Should be list");
75+
endverb
76+
77+
verb test_title_composition (this none this) owner: HACKER flags: "rxd"
78+
"Test TITLE composition with different content types";
79+
title = $title:mk("Important Title");
80+
"Test text_plain composition";
81+
plain_content = title:compose(player, 'text_plain, $nothing);
82+
typeof(plain_content) == FLYWEIGHT || raise(E_ASSERT, "Should return flyweight");
83+
plain_rendered = plain_content:render();
84+
"Important Title" in plain_rendered[1] || raise(E_ASSERT, "Should contain title text");
85+
"Test HTML composition";
86+
html_content = title:compose(player, 'text_html, $nothing);
87+
typeof(html_content) == FLYWEIGHT || raise(E_ASSERT, "Should return flyweight");
88+
html_rendered = html_content:render();
89+
typeof(html_rendered) == LIST && length(html_rendered) == 1 || raise(E_ASSERT, "Should be single HTML string");
90+
"<em>Important Title</em>" in html_rendered[1] || raise(E_ASSERT, "Should contain em tag");
91+
"Test markdown composition";
92+
md_content = title:compose(player, 'text_markdown, $nothing);
93+
typeof(md_content) == FLYWEIGHT || raise(E_ASSERT, "Should return flyweight");
94+
md_rendered = md_content:render();
95+
"*Important Title*" in md_rendered[1] || raise(E_ASSERT, "Should contain markdown emphasis");
96+
endverb
97+
98+
verb test_nested_flyweights (this none this) owner: HACKER flags: "rxd"
99+
"Test composition with nested flyweight content";
100+
title = $title:mk("Page Title");
101+
block = $block:mk("First line", "Second line");
102+
"Test combining title and block in HTML";
103+
html_content = $text_html:mk();
104+
title_html = title:compose(player, 'text_html, $nothing);
105+
block_html = block:compose(player, 'text_html, $nothing);
106+
combined = html_content:append_element(title_html):append_element(block_html);
107+
rendered = combined:render();
108+
typeof(rendered) == LIST && length(rendered) == 1 || raise(E_ASSERT, "Should be single HTML string");
109+
html_string = rendered[1];
110+
"<em>Page Title</em>" in html_string || raise(E_ASSERT, "Should contain title em tag");
111+
"<p>First line</p>" in html_string || raise(E_ASSERT, "Should contain first paragraph");
112+
"<p>Second line</p>" in html_string || raise(E_ASSERT, "Should contain second paragraph");
113+
endverb
114+
115+
verb test_xml_structure_validation (this none this) owner: HACKER flags: "rxd"
116+
"Test that generated HTML is valid XML";
117+
title = $title:mk("Test Title");
118+
block = $block:mk("Test content");
119+
title_html = title:compose(player, 'text_html, $nothing);
120+
block_html = block:compose(player, 'text_html, $nothing);
121+
combined = $text_html:mk():append_element(title_html):append_element(block_html);
122+
rendered = combined:render();
123+
"Try to parse the generated HTML as XML";
124+
try
125+
wrapped = "<root>" + rendered[1] + "</root>";
126+
parsed = xml_parse(wrapped);
127+
typeof(parsed) == LIST || raise(E_ASSERT, "Should parse as valid XML");
128+
length(parsed) >= 1 || raise(E_ASSERT, "Should have root element");
129+
except (ANY)
130+
raise(E_ASSERT, "Generated HTML should be valid XML: " + toliteral(rendered[1]));
131+
endtry
132+
endverb
133+
134+
verb test_content_type_isolation (this none this) owner: HACKER flags: "rxd"
135+
"Test that different content types don't interfere with each other";
136+
block = $block:mk("Same content");
137+
plain_result = block:compose(player, 'text_plain, $nothing);
138+
html_result = block:compose(player, 'text_html, $nothing);
139+
md_result = block:compose(player, 'text_markdown, $nothing);
140+
"Verify they're all different flyweight types";
141+
typeof(plain_result) == FLYWEIGHT || raise(E_ASSERT, "Plain should be flyweight");
142+
typeof(html_result) == FLYWEIGHT || raise(E_ASSERT, "HTML should be flyweight");
143+
typeof(md_result) == FLYWEIGHT || raise(E_ASSERT, "Markdown should be flyweight");
144+
"Verify they render differently";
145+
plain_rendered = plain_result:render();
146+
html_rendered = html_result:render();
147+
md_rendered = md_result:render();
148+
plain_rendered[1] != html_rendered[1] || raise(E_ASSERT, "Plain and HTML should be different");
149+
plain_rendered[1] != md_rendered[1] || raise(E_ASSERT, "Plain and markdown should be different");
150+
html_rendered[1] != md_rendered[1] || raise(E_ASSERT, "HTML and markdown should be different");
151+
endverb
152+
153+
verb test_error_handling (this none this) owner: HACKER flags: "rxd"
154+
"Test proper error handling";
155+
"Test empty title rejection";
156+
`$title:mk() ! E_INVARG => false' || raise(E_ASSERT, "Should reject empty titles");
157+
"Test invalid title args";
158+
`$title:mk("a", "b") ! E_INVARG => false' || raise(E_ASSERT, "Should reject multiple title args");
159+
"Test invalid HTML element";
160+
try
161+
$text_html:mk_element(123, {}, "content");
162+
raise(E_ASSERT, "Should reject non-string tag names");
163+
except (E_TYPE)
164+
"Expected error";
165+
endtry
166+
endverb
167+
168+
verb test_title_block_render (this none this) owner: HACKER flags: "rxd"
169+
"Test BLOCK with title flyweight and string";
170+
title = $title:mk("Room Title");
171+
block = $block:mk(title, "Description text");
172+
plain_rendered = block:compose(player, 'text_plain, $nothing):render();
173+
typeof(plain_rendered) == LIST || raise(E_ASSERT, "Plain should produce list: " + toliteral(plain_rendered));
174+
length(plain_rendered) >= 2 || raise(E_ASSERT, "Plain should have at least 2 elements: " + toliteral(plain_rendered));
175+
md_rendered = block:compose(player, 'text_markdown, $nothing):render();
176+
typeof(md_rendered) == LIST || raise(E_ASSERT, "Markdown should produce list: " + toliteral(md_rendered));
177+
length(md_rendered) >= 2 || raise(E_ASSERT, "Markdown should have at least 2 elements: " + toliteral(md_rendered));
178+
"Check that we don't have everything joined into one element";
179+
title_and_desc_joined = false;
180+
for item in (md_rendered)
181+
if ("Room Title" in item && "Description text" in item)
182+
title_and_desc_joined = true;
183+
endif
184+
endfor
185+
!title_and_desc_joined || raise(E_ASSERT, "Title and description should be separate elements, got: " + toliteral(md_rendered));
186+
endverb
187+
endobject

src/event.moo

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,30 +22,42 @@ object EVENT
2222
endverb
2323

2424
verb transform_for (this none this) owner: HACKER flags: "rxd"
25-
"Call 'render_as(content_type, this)' on all content, and append into a final string.";
26-
{render_for, ?content_type = "text/plain"} = args;
25+
"Call 'compose(content_type, this)' on all content, then render to final string format.";
26+
{render_for, ?content_type = 'text_plain} = args;
2727
if (!this:validate())
2828
raise(E_INVARG);
2929
endif
30-
results = {};
30+
"Get the appropriate content flyweight delegate";
31+
if (content_type == 'text_plain)
32+
content_flyweight = $text_plain:mk();
33+
elseif (content_type == 'text_html)
34+
content_flyweight = $text_html:mk();
35+
elseif (content_type == 'text_markdown || content_type == 'text_djot)
36+
content_flyweight = $text_markdown:mk();
37+
else
38+
content_flyweight = $text_plain:mk();
39+
endif
40+
"Compose all entries and add to content flyweight";
3141
for entry in (this)
3242
if (typeof(entry) == FLYWEIGHT)
33-
entry = entry:render_as(render_for, content_type, this);
34-
endif
35-
if (typeof(entry) == STR)
36-
results = entry:append_to_paragraph(@results);
37-
elseif (typeof(entry) == LIST)
38-
for index in [1..length(entry)]
39-
results = {@(entry[index]):append_to_paragraph(@results)};
40-
if (index != length(entry))
41-
results = {@results, ""};
42-
endif
43-
endfor
43+
composed_entry = entry:compose(render_for, content_type, this);
44+
if (typeof(composed_entry) == FLYWEIGHT)
45+
"Extract elements from the composed flyweight and add them";
46+
for element in (composed_entry)
47+
content_flyweight = content_flyweight:append_element(element);
48+
endfor
49+
else
50+
"Composed entry is not a flyweight (e.g., SUB returns string), add directly";
51+
content_flyweight = content_flyweight:append_element(composed_entry);
52+
endif
53+
elseif (typeof(entry) == STR)
54+
content_flyweight = content_flyweight:append_element(entry);
4455
else
4556
raise(E_TYPE, "Invalid type in event content", entry);
4657
endif
4758
endfor
48-
return results;
59+
"Render the final content";
60+
return content_flyweight:render();
4961
endverb
5062

5163
verb validate (this none this) owner: HACKER flags: "rxd"

0 commit comments

Comments
 (0)