This commit is contained in:
2026-01-25 19:02:02 +01:00
parent a830238d11
commit 1ee28a1644
92 changed files with 10955 additions and 621 deletions
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+90 -18
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
@@ -115,7 +129,17 @@
<p>The <code>scheduler</code> module manages the async event loop and fiber scheduling. It is the foundation for all async operations in Wren-CLI.</p>
<pre><code>import "scheduler" for Scheduler</code></pre>
<pre><code>import "scheduler" for Scheduler, Future</code></pre>
<div class="admonition note">
<div class="admonition-title">Quick Reference</div>
<p>The scheduler module enables async programming with <code>async</code> and <code>await</code>:</p>
<ul>
<li><code>async { code }</code> — Create an async function</li>
<li><code>await fn()</code> — Call async function and wait for result</li>
<li><code>fn.call()</code> — Start async function, return Future (for concurrent execution)</li>
</ul>
</div>
<h2>Scheduler Class</h2>
@@ -241,6 +265,48 @@ System.print(await square(8)) // 64</code></pre>
<p>The <code>fn(args)</code> syntax only works directly after <code>await</code>. Outside of <code>await</code>, use <code>fn.call(args)</code> to invoke async functions.</p>
</div>
<h2>Future Class</h2>
<div class="class-header">
<h3>Future</h3>
<p>Represents a pending async result</p>
</div>
<p>A <code>Future</code> is returned when you call an async function with <code>.call()</code> instead of using <code>await</code> directly. This enables concurrent execution by starting multiple operations without waiting.</p>
<h3>Creating Futures</h3>
<pre><code>import "scheduler" for Scheduler, Future
var double = async { |x| x * 2 }
// Direct call with await - waits immediately
var result = await double(21)
// Using .call() returns a Future - does not wait
var future = double.call(21)
// Later, await the future to get the result
var result = await future</code></pre>
<h3>Concurrent Execution Pattern</h3>
<pre><code>import "scheduler" for Scheduler, Future
import "web" for Client
var fetch = async { |url| Client.get(url) }
// Start all requests at once (returns Futures)
var f1 = fetch.call("https://api.example.com/users")
var f2 = fetch.call("https://api.example.com/posts")
var f3 = fetch.call("https://api.example.com/comments")
// All three requests are now running concurrently
// Wait for each result
var users = await f1
var posts = await f2
var comments = await f3</code></pre>
<h2>How Async Works</h2>
<p>Wren-CLI uses an event loop (libuv) for non-blocking I/O. Here is how async operations work:</p>
@@ -266,28 +332,34 @@ class Timer {
<h2>Examples</h2>
<h3>Sequential vs Parallel</h3>
<p>Operations are sequential by default:</p>
<pre><code>import "http" for Http
<h3>Sequential vs Concurrent Execution</h3>
<p>Using <code>await fn(args)</code> executes operations sequentially:</p>
<pre><code>import "scheduler" for Scheduler, Future
import "web" for Client
// These run one after another (sequential)
var r1 = Http.get("https://api.example.com/1")
var r2 = Http.get("https://api.example.com/2")
var r3 = Http.get("https://api.example.com/3")</code></pre>
var fetch = async { |url| Client.get(url) }
<p>For parallel operations, use multiple fibers:</p>
<pre><code>import "http" for Http
// Sequential: each request waits for the previous one
var r1 = await fetch("https://api.example.com/1")
var r2 = await fetch("https://api.example.com/2")
var r3 = await fetch("https://api.example.com/3")</code></pre>
var fibers = [
Fiber.new { Http.get("https://api.example.com/1") },
Fiber.new { Http.get("https://api.example.com/2") },
Fiber.new { Http.get("https://api.example.com/3") }
]
<p>Using <code>.call()</code> enables concurrent execution:</p>
<pre><code>import "scheduler" for Scheduler, Future
import "web" for Client
// Start all fibers
for (f in fibers) f.call()
var fetch = async { |url| Client.get(url) }
// Results are already available when fibers complete</code></pre>
// Start all requests at once (concurrent)
var f1 = fetch.call("https://api.example.com/1")
var f2 = fetch.call("https://api.example.com/2")
var f3 = fetch.call("https://api.example.com/3")
// All three requests are running in parallel
// Now wait for results
var r1 = await f1
var r2 = await f2
var r3 = await f3</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+562 -3
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
@@ -115,7 +129,7 @@
<p>The <code>web</code> module provides a web framework for building HTTP servers and a client for making HTTP requests. It includes routing, middleware, sessions, static file serving, and class-based views.</p>
<pre><code>import "web" for Application, Router, Request, Response, View, Session, Client</code></pre>
<pre><code>import "web" for Application, Router, Request, Response, View, Session, Client, WebSocketResponse</code></pre>
<h2>Application Class</h2>
@@ -177,6 +191,21 @@
<p>Serves static files from a directory.</p>
<pre><code>app.static_("/assets", "./public")</code></pre>
<div class="method-signature">
<span class="method-name">websocket</span>(<span class="param">path</span>, <span class="param">handler</span>) → <span class="type">Application</span>
</div>
<p>Registers a WebSocket handler for the given path. Handler receives Request and should use WebSocketResponse to handle the connection.</p>
<pre><code>app.websocket("/ws", Fn.new { |req|
var ws = WebSocketResponse.new()
ws.prepare(req)
while (ws.isOpen) {
var msg = ws.receive()
if (msg == null || msg.isClose) break
if (msg.isText) ws.send("Echo: " + msg.text)
}
return ws
})</code></pre>
<div class="method-signature">
<span class="method-name">use</span>(<span class="param">middleware</span>) → <span class="type">Application</span>
</div>
@@ -345,8 +374,300 @@
var data = request.json
return Response.json({"created": data})
}
websocket(request) {
var ws = WebSocketResponse.new()
ws.prepare(request)
ws.iterate(Fn.new { |msg|
if (msg.isText) ws.send("Echo: " + msg.text)
})
return ws
}
}</code></pre>
<h2>WebSocketResponse Class</h2>
<div class="class-header">
<h3>WebSocketResponse</h3>
<p>Server-side WebSocket connection handler (aiohttp-style API)</p>
</div>
<p>The <code>WebSocketResponse</code> class provides a server-side WebSocket handler following the aiohttp-style API pattern. It handles the WebSocket handshake, frame encoding/decoding, and provides convenient methods for sending and receiving messages.</p>
<h3>Constructor</h3>
<div class="method-signature">
<span class="method-name">WebSocketResponse.new</span>() → <span class="type">WebSocketResponse</span>
</div>
<p>Creates a new WebSocket response handler. Call <code>prepare(request)</code> to complete the handshake before sending or receiving messages.</p>
<h3>Properties</h3>
<table>
<tr>
<th>Property</th>
<th>Type</th>
<th>Description</th>
</tr>
<tr>
<td><code>isPrepared</code></td>
<td>Bool</td>
<td>True if the WebSocket handshake has been performed</td>
</tr>
<tr>
<td><code>isOpen</code></td>
<td>Bool</td>
<td>True if the connection is open and ready for messages</td>
</tr>
</table>
<h3>Connection Methods</h3>
<div class="method-signature">
<span class="method-name">prepare</span>(<span class="param">request</span>) → <span class="type">WebSocketResponse</span>
</div>
<p>Performs the WebSocket handshake using the request's socket. Must be called before sending or receiving messages. Returns <code>this</code> for method chaining.</p>
<ul class="param-list">
<li><span class="param-name">request</span> <span class="param-type">(Request)</span> - The HTTP request containing the WebSocket upgrade headers</li>
</ul>
<pre><code>var ws = WebSocketResponse.new()
ws.prepare(request)</code></pre>
<h3>Sending Methods</h3>
<div class="method-signature">
<span class="method-name">send</span>(<span class="param">text</span>)
</div>
<div class="method-signature">
<span class="method-name">sendText</span>(<span class="param">text</span>)
</div>
<p>Sends a text message to the client. Both methods are equivalent.</p>
<ul class="param-list">
<li><span class="param-name">text</span> <span class="param-type">(String)</span> - Text message to send</li>
</ul>
<pre><code>ws.send("Hello, client!")
ws.sendText("Another message")</code></pre>
<div class="method-signature">
<span class="method-name">sendJson</span>(<span class="param">data</span>)
</div>
<p>Serializes the data to JSON and sends it as a text message. Convenience method for JSON-based protocols.</p>
<ul class="param-list">
<li><span class="param-name">data</span> <span class="param-type">(Map|List)</span> - Data to serialize and send</li>
</ul>
<pre><code>ws.sendJson({"type": "update", "value": 42})
ws.sendJson(["item1", "item2", "item3"])</code></pre>
<div class="method-signature">
<span class="method-name">sendBinary</span>(<span class="param">bytes</span>)
</div>
<p>Sends a binary message to the client.</p>
<ul class="param-list">
<li><span class="param-name">bytes</span> <span class="param-type">(List)</span> - List of bytes to send</li>
</ul>
<pre><code>ws.sendBinary([0x01, 0x02, 0x03, 0x04])
var imageData = File.readBytes("image.png")
ws.sendBinary(imageData)</code></pre>
<h3>Receiving Methods</h3>
<div class="method-signature">
<span class="method-name">receive</span>() → <span class="type">WebSocketMessage|null</span>
</div>
<p>Receives the next message from the client. Blocks until a message arrives. Returns a <code>WebSocketMessage</code> object, or <code>null</code> if the connection closed unexpectedly. Ping frames are automatically answered with pong.</p>
<pre><code>var msg = ws.receive()
if (msg != null && msg.isText) {
System.print("Got: %(msg.text)")
}</code></pre>
<div class="method-signature">
<span class="method-name">receiveJson</span>() → <span class="type">Map|List|WebSocketMessage|null</span>
</div>
<p>Receives a text message and parses it as JSON. Returns the parsed JSON data, the close frame if the connection was closed, or <code>null</code> if the connection closed unexpectedly. Aborts if a binary frame is received.</p>
<pre><code>var data = ws.receiveJson()
if (data is Map && data.containsKey("type")) {
if (data["type"] == "message") {
System.print("Message: %(data["text"])")
}
}</code></pre>
<div class="method-signature">
<span class="method-name">receiveText</span>() → <span class="type">String|null</span>
</div>
<p>Receives a message and returns only the text content. Returns <code>null</code> if the message is not a text frame, if the connection was closed, or if the connection closed unexpectedly.</p>
<pre><code>var text = ws.receiveText()
if (text != null) {
System.print("Received: %(text)")
}</code></pre>
<div class="method-signature">
<span class="method-name">receiveBinary</span>() → <span class="type">List|null</span>
</div>
<p>Receives a message and returns only the binary content. Returns <code>null</code> if the message is not a binary frame, if the connection was closed, or if the connection closed unexpectedly.</p>
<pre><code>var bytes = ws.receiveBinary()
if (bytes != null) {
System.print("Received %(bytes.count) bytes")
}</code></pre>
<div class="method-signature">
<span class="method-name">iterate</span>(<span class="param">callback</span>)
</div>
<p>Helper method that loops while the connection is open, calling the callback for each received message (excluding close frames). Simplifies the common receive loop pattern.</p>
<ul class="param-list">
<li><span class="param-name">callback</span> <span class="param-type">(Fn)</span> - Function to call with each WebSocketMessage</li>
</ul>
<pre><code>ws.iterate(Fn.new { |msg|
if (msg.isText) {
ws.send("Echo: " + msg.text)
}
})</code></pre>
<h3>Control Methods</h3>
<div class="method-signature">
<span class="method-name">ping</span>()
</div>
<div class="method-signature">
<span class="method-name">ping</span>(<span class="param">data</span>)
</div>
<p>Sends a ping frame to check if the client is still connected. Payload must be 125 bytes or less.</p>
<ul class="param-list">
<li><span class="param-name">data</span> <span class="param-type">(String|List)</span> - Optional payload data</li>
</ul>
<div class="method-signature">
<span class="method-name">pong</span>(<span class="param">data</span>)
</div>
<p>Sends a pong frame. Usually automatic in response to ping, but can be sent manually.</p>
<div class="method-signature">
<span class="method-name">close</span>()
</div>
<div class="method-signature">
<span class="method-name">close</span>(<span class="param">code</span>, <span class="param">reason</span>)
</div>
<p>Closes the WebSocket connection. Default code is 1000 (normal closure).</p>
<ul class="param-list">
<li><span class="param-name">code</span> <span class="param-type">(Num)</span> - Close status code (1000-4999)</li>
<li><span class="param-name">reason</span> <span class="param-type">(String)</span> - Human-readable close reason</li>
</ul>
<pre><code>ws.close()
ws.close(1000, "Goodbye")</code></pre>
<h3>WebSocketMessage Reference</h3>
<p>The <code>receive()</code> method returns a <code>WebSocketMessage</code> object with the following properties:</p>
<table>
<tr>
<th>Property</th>
<th>Type</th>
<th>Description</th>
</tr>
<tr>
<td><code>opcode</code></td>
<td>Num</td>
<td>Frame opcode (1=text, 2=binary, 8=close, 9=ping, 10=pong)</td>
</tr>
<tr>
<td><code>fin</code></td>
<td>Bool</td>
<td>True if this is the final fragment</td>
</tr>
<tr>
<td><code>isText</code></td>
<td>Bool</td>
<td>True if this is a text frame</td>
</tr>
<tr>
<td><code>isBinary</code></td>
<td>Bool</td>
<td>True if this is a binary frame</td>
</tr>
<tr>
<td><code>isClose</code></td>
<td>Bool</td>
<td>True if this is a close frame</td>
</tr>
<tr>
<td><code>isPing</code></td>
<td>Bool</td>
<td>True if this is a ping frame</td>
</tr>
<tr>
<td><code>isPong</code></td>
<td>Bool</td>
<td>True if this is a pong frame</td>
</tr>
<tr>
<td><code>text</code></td>
<td>String</td>
<td>Payload as string (for text frames)</td>
</tr>
<tr>
<td><code>bytes</code></td>
<td>List</td>
<td>Payload as byte list</td>
</tr>
<tr>
<td><code>closeCode</code></td>
<td>Num</td>
<td>Close status code (for close frames)</td>
</tr>
<tr>
<td><code>closeReason</code></td>
<td>String</td>
<td>Close reason text (for close frames)</td>
</tr>
</table>
<h3>Close Codes Reference</h3>
<table>
<tr>
<th>Code</th>
<th>Meaning</th>
</tr>
<tr>
<td>1000</td>
<td>Normal closure</td>
</tr>
<tr>
<td>1001</td>
<td>Going away (server shutdown)</td>
</tr>
<tr>
<td>1002</td>
<td>Protocol error</td>
</tr>
<tr>
<td>1003</td>
<td>Unsupported data type</td>
</tr>
<tr>
<td>1005</td>
<td>No status received</td>
</tr>
<tr>
<td>1006</td>
<td>Abnormal closure</td>
</tr>
<tr>
<td>1011</td>
<td>Server error</td>
</tr>
<tr>
<td>4000-4999</td>
<td>Application-specific codes</td>
</tr>
</table>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Ping frames received from clients are automatically answered with pong frames. You typically do not need to handle ping/pong manually unless implementing custom keepalive logic.</p>
</div>
<h2>Session Class</h2>
<div class="class-header">
@@ -551,6 +872,244 @@ var postResponse = Client.post("https://api.example.com/users", {
var data = Json.parse(postResponse["body"])</code></pre>
<h3>Concurrent HTTP Requests</h3>
<p>Use the <code>async</code> keyword to create futures and <code>await</code> to wait for results. Each request runs in its own fiber and executes in parallel while waiting for I/O.</p>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
var urls = [
"https://api.example.com/users",
"https://api.example.com/posts",
"https://api.example.com/comments"
]
var futures = []
for (url in urls) {
futures.add(async { Client.get(url) })
}
var responses = []
for (future in futures) {
responses.add(await future)
}
for (i in 0...urls.count) {
System.print("%(urls[i]): %(responses[i]["status"])")
}</code></pre>
<h3>Parallel Batch Requests</h3>
<p>For batch operations, create all futures first, then await them. The requests execute concurrently.</p>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "json" for Json
var fetchUrl = async { |url| Client.get(url) }
class BatchClient {
static getAll(urls) {
var futures = []
for (url in urls) {
futures.add(async { Client.get(url) })
}
var results = []
for (f in futures) {
results.add(await f)
}
return results
}
static postAll(requests) {
var futures = []
for (req in requests) {
futures.add(async { Client.post(req["url"], req["options"]) })
}
var results = []
for (f in futures) {
results.add(await f)
}
return results
}
}
var urls = [
"https://api.example.com/endpoint1",
"https://api.example.com/endpoint2",
"https://api.example.com/endpoint3"
]
var results = BatchClient.getAll(urls)
for (result in results) {
System.print("Status: %(result["status"])")
}</code></pre>
<h3>Parameterized Async Functions</h3>
<p>Create reusable async functions with parameters using <code>async { |args| ... }</code>. There are two ways to invoke these functions:</p>
<ul>
<li><strong><code>await fn(args)</code></strong> — Direct call, waits immediately (sequential execution)</li>
<li><strong><code>fn.call(args)</code></strong> — Returns a Future, starts execution without waiting (concurrent execution)</li>
</ul>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "json" for Json
var fetchJson = async { |url|
var response = Client.get(url)
return Json.parse(response["body"])
}
var postJson = async { |url, data|
var response = Client.post(url, {"json": data})
return Json.parse(response["body"])
}
// DIRECT CALLING (preferred for sequential operations)
// Waits for each request to complete before continuing
var user = await fetchJson("https://api.example.com/user/1")
System.print(user["name"])
// CONCURRENT EXECUTION (use .call() to start without waiting)
// Both requests run at the same time
var f1 = fetchJson.call("https://api.example.com/posts")
var f2 = fetchJson.call("https://api.example.com/comments")
// Now wait for results
var posts = await f1
var comments = await f2</code></pre>
<div class="admonition tip">
<div class="admonition-title">Direct Calling vs .call()</div>
<p>Use <code>await fn(args)</code> when you want sequential execution. Use <code>fn.call(args)</code> when you want to start multiple operations concurrently, then <code>await</code> their results later.</p>
</div>
<h3>WebSocket Echo Server</h3>
<pre><code>import "web" for Application, Response, WebSocketResponse
var app = Application.new()
app.get("/", Fn.new { |req|
return Response.html("&lt;script&gt;var ws=new WebSocket('ws://localhost:8080/ws');ws.onmessage=e=&gt;console.log(e.data);ws.onopen=()=&gt;ws.send('hello');&lt;/script&gt;")
})
app.websocket("/ws", Fn.new { |req|
var ws = WebSocketResponse.new()
ws.prepare(req)
while (ws.isOpen) {
var msg = ws.receive()
if (msg == null || msg.isClose) break
if (msg.isText) {
ws.send("Echo: " + msg.text)
}
}
ws.close()
return ws
})
app.run("0.0.0.0", 8080)</code></pre>
<h3>WebSocket JSON API</h3>
<pre><code>import "web" for Application, Response, WebSocketResponse
var app = Application.new()
app.websocket("/api", Fn.new { |req|
var ws = WebSocketResponse.new()
ws.prepare(req)
ws.sendJson({"type": "welcome", "message": "Connected to API"})
while (ws.isOpen) {
var data = ws.receiveJson()
if (data == null) break
if (data is Map &amp;&amp; data["type"] == "ping") {
ws.sendJson({"type": "pong", "timestamp": data["timestamp"]})
} else if (data is Map &amp;&amp; data["type"] == "echo") {
ws.sendJson({"type": "echo", "data": data["data"]})
}
}
return ws
})
app.run("0.0.0.0", 8080)</code></pre>
<h3>WebSocket Binary Data Transfer</h3>
<pre><code>import "web" for Application, Response, WebSocketResponse
import "io" for File
var app = Application.new()
app.websocket("/binary", Fn.new { |req|
var ws = WebSocketResponse.new()
ws.prepare(req)
ws.send("Ready to receive binary data")
while (ws.isOpen) {
var msg = ws.receive()
if (msg == null || msg.isClose) break
if (msg.isBinary) {
System.print("Received %(msg.bytes.count) bytes")
ws.sendJson({"received": msg.bytes.count, "status": "ok"})
} else if (msg.isText) {
if (msg.text == "send-file") {
var data = File.readBytes("example.bin")
ws.sendBinary(data)
}
}
}
return ws
})
app.run("0.0.0.0", 8080)</code></pre>
<h3>WebSocket in Class-Based View</h3>
<pre><code>import "web" for Application, Response, View, WebSocketResponse
class ChatView is View {
get(request) {
return Response.html("&lt;h1&gt;Chat Room&lt;/h1&gt;&lt;p&gt;Connect via WebSocket&lt;/p&gt;")
}
websocket(request) {
var ws = WebSocketResponse.new()
ws.prepare(request)
ws.sendJson({"type": "connected", "room": "general"})
ws.iterate(Fn.new { |msg|
if (msg.isText) {
var data = Json.parse(msg.text)
if (data["type"] == "message") {
ws.sendJson({
"type": "broadcast",
"from": data["from"],
"text": data["text"]
})
}
}
})
return ws
}
}
var app = Application.new()
app.addView("/chat", ChatView)
app.run("0.0.0.0", 8080)</code></pre>
<div class="admonition warning">
<div class="admonition-title">WebSocket Error Handling</div>
<p>Always check for <code>null</code> or close frames when receiving messages. The connection can close at any time due to network issues or client disconnection. Wrap WebSocket handling in proper error handling to prevent server crashes.</p>
</div>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Sessions are stored in memory by default. Data is lost when the server restarts. For production, consider persisting session data to a database.</p>
@@ -558,8 +1117,8 @@ var data = Json.parse(postResponse["body"])</code></pre>
</article>
<footer class="page-footer">
<a href="markdown.html" class="prev">markdown</a>
<a href="index.html" class="next">Overview</a>
<a href="wdantic.html" class="prev">wdantic</a>
<a href="websocket.html" class="next">websocket</a>
</footer>
</main>
</div>
+14
View File
@@ -85,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+548
View File
@@ -0,0 +1,548 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Async Patterns - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="module-overview.html">Module Architecture</a></li>
<li><a href="pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="c-backed-module.html">C-Backed Modules</a></li>
<li><a href="foreign-classes.html">Foreign Classes</a></li>
<li><a href="async-patterns.html" class="active">Async Patterns</a></li>
<li><a href="testing.html">Writing Tests</a></li>
<li><a href="documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">Contributing</a>
<span class="separator">/</span>
<span>Async Patterns</span>
</nav>
<article>
<h1>Async Patterns</h1>
<p>Wren-CLI uses libuv for non-blocking I/O. Wren fibers suspend during I/O operations and resume when complete. This section explains how to implement async operations in C-backed modules.</p>
<h2>The Scheduler/Fiber Pattern</h2>
<p>The standard pattern for async operations involves:</p>
<ol>
<li>A public Wren method that users call</li>
<li>An internal foreign method (ending with <code>_</code>) that takes a fiber handle</li>
<li>C code that stores the fiber handle and schedules async work</li>
<li>A libuv callback that resumes the fiber with the result</li>
</ol>
<h2>Basic Example</h2>
<h3>Wren Interface</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "scheduler" for Scheduler
class AsyncFile {
foreign static read_(path, fiber)
static read(path) {
return Scheduler.await_ { read_(path, Fiber.current) }
}
}</code></pre>
<p>The public <code>read</code> method wraps the internal <code>read_</code> method. <code>Scheduler.await_</code> suspends the current fiber until the async operation completes.</p>
<h3>Naming Convention</h3>
<ul>
<li>Internal methods end with <code>_</code> (e.g., <code>read_</code>, <code>write_</code>)</li>
<li>Public methods have clean names (e.g., <code>read</code>, <code>write</code>)</li>
<li>Internal methods take a fiber as the last argument</li>
</ul>
<h2>C Implementation Structure</h2>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;uv.h&gt;
#include "asyncfile.h"
#include "wren.h"
#include "vm.h"
typedef struct {
WrenVM* vm;
WrenHandle* fiber;
uv_fs_t req;
char* path;
uv_buf_t buffer;
} ReadRequest;
void asyncFileRead(WrenVM* vm) {
const char* path = wrenGetSlotString(vm, 1);
WrenHandle* fiber = wrenGetSlotHandle(vm, 2);
ReadRequest* request = (ReadRequest*)malloc(sizeof(ReadRequest));
request->vm = vm;
request->fiber = fiber;
request->path = strdup(path);
request->req.data = request;
uv_loop_t* loop = getLoop();
uv_fs_open(loop, &request->req, path, UV_FS_O_RDONLY, 0, onFileOpened);
}
void onFileOpened(uv_fs_t* req) {
ReadRequest* request = (ReadRequest*)req->data;
uv_fs_req_cleanup(req);
if (req->result < 0) {
resumeWithError(request, "Failed to open file.");
return;
}
int fd = (int)req->result;
uv_fs_fstat(getLoop(), &request->req, fd, onFileStat);
}
// ... additional callbacks for fstat, read, close ...</code></pre>
<h2>Fiber Handle Management</h2>
<h3>Capturing the Fiber</h3>
<pre><code>WrenHandle* fiber = wrenGetSlotHandle(vm, 2);</code></pre>
<p>The fiber handle is obtained from the slot where it was passed. This handle must be stored for later use.</p>
<h3>Resuming the Fiber</h3>
<p>Use the scheduler's resume mechanism:</p>
<pre><code>void resumeWithResult(ReadRequest* request, const char* result) {
WrenVM* vm = request->vm;
schedulerResume(request->fiber, true);
wrenReleaseHandle(vm, request->fiber);
wrenEnsureSlots(vm, 1);
wrenSetSlotString(vm, 0, result);
free(request->path);
free(request);
}
void resumeWithError(ReadRequest* request, const char* error) {
WrenVM* vm = request->vm;
schedulerResume(request->fiber, false);
wrenReleaseHandle(vm, request->fiber);
wrenEnsureSlots(vm, 1);
wrenSetSlotString(vm, 0, error);
free(request->path);
free(request);
}</code></pre>
<h3>schedulerResume</h3>
<pre><code>void schedulerResume(WrenHandle* fiber, bool success);</code></pre>
<ul>
<li><code>fiber</code>: The fiber handle to resume</li>
<li><code>success</code>: true if the operation succeeded, false for error</li>
</ul>
<p>After calling <code>schedulerResume</code>, the value in slot 0 becomes the return value (for success) or error message (for failure).</p>
<h2>libuv Integration</h2>
<h3>Getting the Event Loop</h3>
<pre><code>uv_loop_t* loop = getLoop();</code></pre>
<p>The <code>getLoop()</code> function returns the global libuv event loop used by Wren-CLI.</p>
<h3>Common libuv Operations</h3>
<table>
<tr>
<th>Operation</th>
<th>libuv Function</th>
</tr>
<tr>
<td>File read</td>
<td><code>uv_fs_read</code></td>
</tr>
<tr>
<td>File write</td>
<td><code>uv_fs_write</code></td>
</tr>
<tr>
<td>TCP connect</td>
<td><code>uv_tcp_connect</code></td>
</tr>
<tr>
<td>DNS lookup</td>
<td><code>uv_getaddrinfo</code></td>
</tr>
<tr>
<td>Timer</td>
<td><code>uv_timer_start</code></td>
</tr>
<tr>
<td>Process spawn</td>
<td><code>uv_spawn</code></td>
</tr>
</table>
<h3>Request Data Pattern</h3>
<p>Store context in the <code>data</code> field of libuv requests:</p>
<pre><code>typedef struct {
WrenVM* vm;
WrenHandle* fiber;
// ... operation-specific data ...
} MyRequest;
MyRequest* req = malloc(sizeof(MyRequest));
req->vm = vm;
req->fiber = fiber;
uvReq.data = req;</code></pre>
<p>In callbacks, retrieve the context:</p>
<pre><code>void onComplete(uv_xxx_t* uvReq) {
MyRequest* req = (MyRequest*)uvReq->data;
// ...
}</code></pre>
<h2>Complete Async File Read Example</h2>
<h3>asyncfile.wren</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "scheduler" for Scheduler
class AsyncFile {
foreign static read_(path, fiber)
foreign static write_(path, content, fiber)
static read(path) {
return Scheduler.await_ { read_(path, Fiber.current) }
}
static write(path, content) {
return Scheduler.await_ { write_(path, content, Fiber.current) }
}
}</code></pre>
<h3>asyncfile.c (simplified)</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;uv.h&gt;
#include "asyncfile.h"
#include "wren.h"
#include "vm.h"
#include "scheduler.h"
typedef struct {
WrenVM* vm;
WrenHandle* fiber;
uv_fs_t req;
uv_file fd;
char* buffer;
size_t size;
} FileRequest;
static void cleanupRequest(FileRequest* request) {
if (request->buffer) free(request->buffer);
wrenReleaseHandle(request->vm, request->fiber);
free(request);
}
static void onReadComplete(uv_fs_t* req) {
FileRequest* request = (FileRequest*)req->data;
uv_fs_req_cleanup(req);
if (req->result < 0) {
schedulerResume(request->fiber, false);
wrenEnsureSlots(request->vm, 1);
wrenSetSlotString(request->vm, 0, "Read failed.");
} else {
request->buffer[req->result] = '\0';
schedulerResume(request->fiber, true);
wrenEnsureSlots(request->vm, 1);
wrenSetSlotString(request->vm, 0, request->buffer);
}
uv_fs_close(getLoop(), req, request->fd, NULL);
cleanupRequest(request);
}
static void onFileStatComplete(uv_fs_t* req) {
FileRequest* request = (FileRequest*)req->data;
uv_fs_req_cleanup(req);
if (req->result < 0) {
schedulerResume(request->fiber, false);
wrenEnsureSlots(request->vm, 1);
wrenSetSlotString(request->vm, 0, "Stat failed.");
uv_fs_close(getLoop(), req, request->fd, NULL);
cleanupRequest(request);
return;
}
request->size = req->statbuf.st_size;
request->buffer = (char*)malloc(request->size + 1);
uv_buf_t buf = uv_buf_init(request->buffer, request->size);
uv_fs_read(getLoop(), &request->req, request->fd, &buf, 1, 0, onReadComplete);
}
static void onFileOpenComplete(uv_fs_t* req) {
FileRequest* request = (FileRequest*)req->data;
uv_fs_req_cleanup(req);
if (req->result < 0) {
schedulerResume(request->fiber, false);
wrenEnsureSlots(request->vm, 1);
wrenSetSlotString(request->vm, 0, "Open failed.");
cleanupRequest(request);
return;
}
request->fd = (uv_file)req->result;
uv_fs_fstat(getLoop(), &request->req, request->fd, onFileStatComplete);
}
void asyncFileRead(WrenVM* vm) {
const char* path = wrenGetSlotString(vm, 1);
WrenHandle* fiber = wrenGetSlotHandle(vm, 2);
FileRequest* request = (FileRequest*)malloc(sizeof(FileRequest));
request->vm = vm;
request->fiber = fiber;
request->buffer = NULL;
request->req.data = request;
uv_fs_open(getLoop(), &request->req, path, UV_FS_O_RDONLY, 0, onFileOpenComplete);
}</code></pre>
<h2>Error Handling in Async Operations</h2>
<h3>libuv Errors</h3>
<p>Check <code>req->result</code> for negative values:</p>
<pre><code>if (req->result < 0) {
const char* msg = uv_strerror((int)req->result);
schedulerResume(request->fiber, false);
wrenSetSlotString(request->vm, 0, msg);
return;
}</code></pre>
<h3>Propagating to Wren</h3>
<p>Use <code>schedulerResume(fiber, false)</code> for errors. The value in slot 0 becomes the error that <code>Scheduler.await_</code> throws as a runtime error.</p>
<h2>Timer Example</h2>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
typedef struct {
WrenVM* vm;
WrenHandle* fiber;
uv_timer_t timer;
} TimerRequest;
static void onTimerComplete(uv_timer_t* timer) {
TimerRequest* request = (TimerRequest*)timer->data;
schedulerResume(request->fiber, true);
wrenReleaseHandle(request->vm, request->fiber);
wrenEnsureSlots(request->vm, 1);
wrenSetSlotNull(request->vm, 0);
uv_close((uv_handle_t*)timer, NULL);
free(request);
}
void timerSleep(WrenVM* vm) {
double ms = wrenGetSlotDouble(vm, 1);
WrenHandle* fiber = wrenGetSlotHandle(vm, 2);
TimerRequest* request = (TimerRequest*)malloc(sizeof(TimerRequest));
request->vm = vm;
request->fiber = fiber;
request->timer.data = request;
uv_timer_init(getLoop(), &request->timer);
uv_timer_start(&request->timer, onTimerComplete, (uint64_t)ms, 0);
}</code></pre>
<h2>Multiple Concurrent Operations</h2>
<p>Each async operation gets its own request structure and fiber. Multiple operations can run concurrently:</p>
<pre><code>import "asyncfile" for AsyncFile
var fiber1 = Fiber.new {
var a = AsyncFile.read("a.txt")
System.print("A: %(a.count) bytes")
}
var fiber2 = Fiber.new {
var b = AsyncFile.read("b.txt")
System.print("B: %(b.count) bytes")
}
fiber1.call()
fiber2.call()</code></pre>
<h2>Best Practices</h2>
<ul>
<li><strong>Always release fiber handles</strong>: Call <code>wrenReleaseHandle</code> after resuming</li>
<li><strong>Cleanup on all paths</strong>: Free resources in both success and error cases</li>
<li><strong>Use uv_fs_req_cleanup</strong>: Required after filesystem operations</li>
<li><strong>Close handles properly</strong>: Use <code>uv_close</code> for handles like timers and sockets</li>
<li><strong>Check for VM validity</strong>: The VM pointer should remain valid during callbacks</li>
</ul>
<h2>Debugging Tips</h2>
<ul>
<li>Add logging in callbacks to trace execution flow</li>
<li>Verify libuv error codes with <code>uv_strerror</code></li>
<li>Use <code>make debug</code> build for symbols</li>
<li>Check for memory leaks with valgrind</li>
</ul>
<h2>Next Steps</h2>
<p>See <a href="testing.html">Writing Tests</a> for testing async operations, including the <code>// skip:</code> annotation for tests that require network access.</p>
</article>
<footer class="page-footer">
<a href="foreign-classes.html" class="prev">Foreign Classes</a>
<a href="testing.html" class="next">Writing Tests</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
+540
View File
@@ -0,0 +1,540 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>C-Backed Modules - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="module-overview.html">Module Architecture</a></li>
<li><a href="pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="c-backed-module.html" class="active">C-Backed Modules</a></li>
<li><a href="foreign-classes.html">Foreign Classes</a></li>
<li><a href="async-patterns.html">Async Patterns</a></li>
<li><a href="testing.html">Writing Tests</a></li>
<li><a href="documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">Contributing</a>
<span class="separator">/</span>
<span>C-Backed Modules</span>
</nav>
<article>
<h1>C-Backed Modules</h1>
<p>C-backed modules implement foreign methods in C, providing access to system libraries, native performance, or functionality not available in pure Wren. Examples: json, io, net, crypto, tls, sqlite, base64.</p>
<h2>Step 1: Create the Wren Interface</h2>
<p>Create <code>src/module/&lt;name&gt;.wren</code> with foreign method declarations:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
class Counter {
foreign static create()
foreign static increment(handle)
foreign static getValue(handle)
foreign static destroy(handle)
static use(fn) {
var handle = Counter.create()
var result = fn.call(handle)
Counter.destroy(handle)
return result
}
}</code></pre>
<p>The <code>foreign</code> keyword indicates the method is implemented in C. Non-foreign methods can provide higher-level Wren wrappers around the foreign primitives.</p>
<h2>Step 2: Generate the .wren.inc</h2>
<pre><code>python3 util/wren_to_c_string.py src/module/counter.wren.inc src/module/counter.wren</code></pre>
<h2>Step 3: Create the C Implementation</h2>
<p>Create <code>src/module/counter.c</code>:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
#include &lt;stdlib.h&gt;
#include "counter.h"
#include "wren.h"
typedef struct {
int value;
} Counter;
void counterCreate(WrenVM* vm) {
Counter* counter = (Counter*)malloc(sizeof(Counter));
if (!counter) {
wrenSetSlotNull(vm, 0);
return;
}
counter->value = 0;
wrenSetSlotDouble(vm, 0, (double)(uintptr_t)counter);
}
void counterIncrement(WrenVM* vm) {
double handle = wrenGetSlotDouble(vm, 1);
Counter* counter = (Counter*)(uintptr_t)handle;
counter->value++;
}
void counterGetValue(WrenVM* vm) {
double handle = wrenGetSlotDouble(vm, 1);
Counter* counter = (Counter*)(uintptr_t)handle;
wrenSetSlotDouble(vm, 0, counter->value);
}
void counterDestroy(WrenVM* vm) {
double handle = wrenGetSlotDouble(vm, 1);
Counter* counter = (Counter*)(uintptr_t)handle;
free(counter);
}</code></pre>
<h2>Step 4: Create the C Header</h2>
<p>Create <code>src/module/counter.h</code>:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
#ifndef counter_h
#define counter_h
#include "wren.h"
void counterCreate(WrenVM* vm);
void counterIncrement(WrenVM* vm);
void counterGetValue(WrenVM* vm);
void counterDestroy(WrenVM* vm);
#endif</code></pre>
<h2>Step 5: Register in modules.c</h2>
<p>Edit <code>src/cli/modules.c</code>:</p>
<h3>Add the Include</h3>
<pre><code>#include "counter.wren.inc"</code></pre>
<h3>Add Extern Declarations</h3>
<pre><code>extern void counterCreate(WrenVM* vm);
extern void counterIncrement(WrenVM* vm);
extern void counterGetValue(WrenVM* vm);
extern void counterDestroy(WrenVM* vm);</code></pre>
<h3>Add the Module Entry</h3>
<pre><code>MODULE(counter)
CLASS(Counter)
STATIC_METHOD("create()", counterCreate)
STATIC_METHOD("increment(_)", counterIncrement)
STATIC_METHOD("getValue(_)", counterGetValue)
STATIC_METHOD("destroy(_)", counterDestroy)
END_CLASS
END_MODULE</code></pre>
<h2>Step 6: Update the Makefile</h2>
<p>Edit <code>projects/make/wren_cli.make</code>:</p>
<h3>Add to OBJECTS</h3>
<pre><code>OBJECTS += $(OBJDIR)/counter.o</code></pre>
<h3>Add Compilation Rule</h3>
<pre><code>$(OBJDIR)/counter.o: ../../src/module/counter.c
@echo $(notdir $&lt;)
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$&lt;"</code></pre>
<h2>Step 7: Build and Test</h2>
<pre><code>make clean && make build
python3 util/test.py counter</code></pre>
<h2>Wren/C Data Exchange</h2>
<h3>Getting Values from Wren</h3>
<pre><code>const char* str = wrenGetSlotString(vm, 1);
double num = wrenGetSlotDouble(vm, 1);
bool b = wrenGetSlotBool(vm, 1);
void* foreign = wrenGetSlotForeign(vm, 0);
WrenHandle* handle = wrenGetSlotHandle(vm, 1);
int count = wrenGetSlotCount(vm);
WrenType type = wrenGetSlotType(vm, 1);</code></pre>
<h3>Setting Return Values</h3>
<pre><code>wrenSetSlotString(vm, 0, "result");
wrenSetSlotDouble(vm, 0, 42.0);
wrenSetSlotBool(vm, 0, true);
wrenSetSlotNull(vm, 0);
wrenSetSlotNewList(vm, 0);</code></pre>
<h3>Working with Lists</h3>
<pre><code>wrenSetSlotNewList(vm, 0);
wrenSetSlotString(vm, 1, "item");
wrenInsertInList(vm, 0, -1, 1);
int count = wrenGetListCount(vm, 0);
wrenGetListElement(vm, 0, index, 1);</code></pre>
<h3>Working with Maps</h3>
<pre><code>wrenSetSlotNewMap(vm, 0);
wrenSetSlotString(vm, 1, "key");
wrenSetSlotDouble(vm, 2, 123);
wrenSetMapValue(vm, 0, 1, 2);</code></pre>
<h3>Ensuring Slots</h3>
<pre><code>wrenEnsureSlots(vm, 5);</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Slot 0 is used for the return value and (for instance methods) the receiver. Arguments start at slot 1.</p>
</div>
<h2>Error Handling</h2>
<p>Use <code>wrenAbortFiber</code> to report errors:</p>
<pre><code>void myMethod(WrenVM* vm) {
const char* path = wrenGetSlotString(vm, 1);
FILE* file = fopen(path, "r");
if (!file) {
wrenSetSlotString(vm, 0, "Failed to open file.");
wrenAbortFiber(vm, 0);
return;
}
// ... process file ...
}</code></pre>
<p>The error message is set in slot 0, then <code>wrenAbortFiber</code> is called with the slot containing the message.</p>
<h2>Type Checking</h2>
<p>Verify argument types before using them:</p>
<pre><code>void myMethod(WrenVM* vm) {
if (wrenGetSlotType(vm, 1) != WREN_TYPE_STRING) {
wrenSetSlotString(vm, 0, "Argument must be a string.");
wrenAbortFiber(vm, 0);
return;
}
const char* str = wrenGetSlotString(vm, 1);
// ...
}</code></pre>
<p>WrenType values: <code>WREN_TYPE_BOOL</code>, <code>WREN_TYPE_NUM</code>, <code>WREN_TYPE_FOREIGN</code>, <code>WREN_TYPE_LIST</code>, <code>WREN_TYPE_MAP</code>, <code>WREN_TYPE_NULL</code>, <code>WREN_TYPE_STRING</code>, <code>WREN_TYPE_UNKNOWN</code>.</p>
<h2>Method Signature Rules</h2>
<table>
<tr>
<th>Wren Declaration</th>
<th>Signature String</th>
</tr>
<tr>
<td><code>foreign static foo()</code></td>
<td><code>"foo()"</code></td>
</tr>
<tr>
<td><code>foreign static foo(a)</code></td>
<td><code>"foo(_)"</code></td>
</tr>
<tr>
<td><code>foreign static foo(a, b)</code></td>
<td><code>"foo(_,_)"</code></td>
</tr>
<tr>
<td><code>foreign foo()</code></td>
<td><code>"foo()"</code> with <code>METHOD</code></td>
</tr>
<tr>
<td><code>foreign name</code> (getter)</td>
<td><code>"name"</code></td>
</tr>
<tr>
<td><code>foreign name=(v)</code> (setter)</td>
<td><code>"name=(_)"</code></td>
</tr>
</table>
<h2>Complete Example</h2>
<p>A more realistic example parsing hexadecimal strings:</p>
<h3>hex.wren</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
class Hex {
foreign static encode(bytes)
foreign static decode(str)
static isValid(str) {
for (c in str) {
var code = c.bytes[0]
var valid = (code >= 48 && code <= 57) ||
(code >= 65 && code <= 70) ||
(code >= 97 && code <= 102)
if (!valid) return false
}
return str.count \% 2 == 0
}
}</code></pre>
<h3>hex.c</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include "hex.h"
#include "wren.h"
static const char HEX_CHARS[] = "0123456789abcdef";
void hexEncode(WrenVM* vm) {
const char* input = wrenGetSlotString(vm, 1);
size_t len = strlen(input);
char* output = (char*)malloc(len * 2 + 1);
if (!output) {
wrenSetSlotString(vm, 0, "Memory allocation failed.");
wrenAbortFiber(vm, 0);
return;
}
for (size_t i = 0; i < len; i++) {
unsigned char c = (unsigned char)input[i];
output[i * 2] = HEX_CHARS[(c >> 4) & 0xF];
output[i * 2 + 1] = HEX_CHARS[c & 0xF];
}
output[len * 2] = '\0';
wrenSetSlotString(vm, 0, output);
free(output);
}
static int hexCharToInt(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
return -1;
}
void hexDecode(WrenVM* vm) {
const char* input = wrenGetSlotString(vm, 1);
size_t len = strlen(input);
if (len \% 2 != 0) {
wrenSetSlotString(vm, 0, "Hex string must have even length.");
wrenAbortFiber(vm, 0);
return;
}
char* output = (char*)malloc(len / 2 + 1);
if (!output) {
wrenSetSlotString(vm, 0, "Memory allocation failed.");
wrenAbortFiber(vm, 0);
return;
}
for (size_t i = 0; i < len; i += 2) {
int high = hexCharToInt(input[i]);
int low = hexCharToInt(input[i + 1]);
if (high < 0 || low < 0) {
free(output);
wrenSetSlotString(vm, 0, "Invalid hex character.");
wrenAbortFiber(vm, 0);
return;
}
output[i / 2] = (char)((high << 4) | low);
}
output[len / 2] = '\0';
wrenSetSlotString(vm, 0, output);
free(output);
}</code></pre>
<h3>hex.h</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
#ifndef hex_h
#define hex_h
#include "wren.h"
void hexEncode(WrenVM* vm);
void hexDecode(WrenVM* vm);
#endif</code></pre>
<h3>modules.c entries</h3>
<pre><code>#include "hex.wren.inc"
extern void hexEncode(WrenVM* vm);
extern void hexDecode(WrenVM* vm);
MODULE(hex)
CLASS(Hex)
STATIC_METHOD("encode(_)", hexEncode)
STATIC_METHOD("decode(_)", hexDecode)
END_CLASS
END_MODULE</code></pre>
<h2>Common Pitfalls</h2>
<ul>
<li><strong>Stale .wren.inc</strong>: Always regenerate after editing the <code>.wren</code> file</li>
<li><strong>Signature mismatch</strong>: The string in <code>STATIC_METHOD("name(_)", fn)</code> must exactly match the Wren declaration's arity</li>
<li><strong>Slot management</strong>: Always call <code>wrenEnsureSlots(vm, n)</code> before using high-numbered slots</li>
<li><strong>Memory leaks</strong>: Free allocated memory before returning</li>
<li><strong>String lifetime</strong>: Strings from <code>wrenGetSlotString</code> are valid only until the next Wren API call</li>
<li><strong>make clean</strong>: Required after adding new object files</li>
</ul>
<h2>Checklist</h2>
<ul>
<li>Created <code>src/module/&lt;name&gt;.wren</code> with foreign declarations</li>
<li>Generated <code>.wren.inc</code></li>
<li>Created <code>src/module/&lt;name&gt;.c</code></li>
<li>Created <code>src/module/&lt;name&gt;.h</code></li>
<li>Added <code>#include</code> for <code>.wren.inc</code> in modules.c</li>
<li>Added extern declarations in modules.c</li>
<li>Added <code>MODULE</code>/<code>CLASS</code>/<code>METHOD</code> block in modules.c</li>
<li>Added <code>OBJECTS</code> entry in Makefile</li>
<li>Added compilation rule in Makefile</li>
<li>Built with <code>make clean && make build</code></li>
<li>Created tests and example</li>
<li>Created documentation page</li>
</ul>
<h2>Next Steps</h2>
<ul>
<li><a href="foreign-classes.html">Foreign Classes</a> - for native resource management</li>
<li><a href="async-patterns.html">Async Patterns</a> - for non-blocking I/O operations</li>
</ul>
</article>
<footer class="page-footer">
<a href="pure-wren-module.html" class="prev">Pure-Wren Modules</a>
<a href="foreign-classes.html" class="next">Foreign Classes</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
+498
View File
@@ -0,0 +1,498 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documentation - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="module-overview.html">Module Architecture</a></li>
<li><a href="pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="c-backed-module.html">C-Backed Modules</a></li>
<li><a href="foreign-classes.html">Foreign Classes</a></li>
<li><a href="async-patterns.html">Async Patterns</a></li>
<li><a href="testing.html">Writing Tests</a></li>
<li><a href="documentation.html" class="active">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">Contributing</a>
<span class="separator">/</span>
<span>Documentation</span>
</nav>
<article>
<h1>Documentation</h1>
<p>The Wren-CLI manual is hand-written HTML in <code>manual/</code>. There is no generation step. Every page is a standalone HTML file sharing common CSS and JavaScript.</p>
<h2>HTML Template</h2>
<p>Every page follows this structure:</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;!-- retoor &lt;retoor@molodetz.nl&gt; --&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;[Page Title] - Wren-CLI Manual&lt;/title&gt;
&lt;link rel="stylesheet" href="../css/style.css"&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;button class="mobile-menu-toggle"&gt;Menu&lt;/button&gt;
&lt;div class="container"&gt;
&lt;aside class="sidebar"&gt;
&lt;div class="sidebar-header"&gt;
&lt;h1&gt;&lt;a href="../index.html"&gt;Wren-CLI&lt;/a&gt;&lt;/h1&gt;
&lt;div class="version"&gt;v0.4.0&lt;/div&gt;
&lt;/div&gt;
&lt;nav class="sidebar-nav"&gt;
&lt;!-- Auto-generated by sync_sidebar.py --&gt;
&lt;/nav&gt;
&lt;/aside&gt;
&lt;main class="content"&gt;
&lt;nav class="breadcrumb"&gt;
&lt;a href="../index.html"&gt;Home&lt;/a&gt;
&lt;span class="separator"&gt;/&lt;/span&gt;
&lt;a href="index.html"&gt;API Reference&lt;/a&gt;
&lt;span class="separator"&gt;/&lt;/span&gt;
&lt;span&gt;[Current Page]&lt;/span&gt;
&lt;/nav&gt;
&lt;article&gt;
&lt;h1&gt;[Page Title]&lt;/h1&gt;
&lt;!-- Content --&gt;
&lt;/article&gt;
&lt;footer class="page-footer"&gt;
&lt;a href="[prev].html" class="prev"&gt;[Previous]&lt;/a&gt;
&lt;a href="[next].html" class="next"&gt;[Next]&lt;/a&gt;
&lt;/footer&gt;
&lt;/main&gt;
&lt;/div&gt;
&lt;script src="../js/main.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h2>Sidebar Navigation</h2>
<p>The sidebar is automatically synchronized across all pages by <code>util/sync_sidebar.py</code>. Never edit the sidebar manually.</p>
<h3>Adding a New Module to the Sidebar</h3>
<ol>
<li>Create the module's HTML page in <code>manual/api/</code></li>
<li>Run <code>make sync-manual</code></li>
</ol>
<p>The sync script automatically discovers new API pages and adds them to the sidebar in alphabetical order.</p>
<h3>Adding a New Section</h3>
<p>To add a new top-level section (like "Contributing"), edit <code>util/sync_sidebar.py</code> and add an entry to the <code>SECTIONS</code> list:</p>
<pre><code>{
"title": "New Section",
"directory": "new-section",
"pages": [
("index.html", "Overview"),
("page1.html", "Page One"),
("page2.html", "Page Two"),
]
},</code></pre>
<h2>API Page Structure</h2>
<p>API documentation pages follow a consistent structure:</p>
<pre><code>&lt;h1&gt;modulename&lt;/h1&gt;
&lt;p&gt;Description paragraph.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import "modulename" for ClassName&lt;/code&gt;&lt;/pre&gt;
&lt;div class="class-header"&gt;&lt;h2&gt;Class: ClassName&lt;/h2&gt;&lt;/div&gt;
&lt;h3&gt;Static Methods&lt;/h3&gt;
&lt;div class="method-signature"&gt;
&lt;span class="method-name"&gt;ClassName.methodName&lt;/span&gt;(&lt;span class="param"&gt;arg&lt;/span&gt;)
&amp;rarr; &lt;span class="type"&gt;ReturnType&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Method description.&lt;/p&gt;
&lt;h3&gt;Examples&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import "modulename" for ClassName
var result = ClassName.methodName("hello")
System.print(result)&lt;/code&gt;&lt;/pre&gt;</code></pre>
<h2>CSS Classes</h2>
<table>
<tr>
<th>Class</th>
<th>Usage</th>
</tr>
<tr>
<td><code>.method-signature</code></td>
<td>Method signature box</td>
</tr>
<tr>
<td><code>.method-name</code></td>
<td>Method name within signature</td>
</tr>
<tr>
<td><code>.param</code></td>
<td>Parameter name</td>
</tr>
<tr>
<td><code>.type</code></td>
<td>Return type</td>
</tr>
<tr>
<td><code>.class-header</code></td>
<td>Class section header</td>
</tr>
<tr>
<td><code>.param-list</code></td>
<td>Parameter description list</td>
</tr>
<tr>
<td><code>.toc</code></td>
<td>Table of contents box</td>
</tr>
<tr>
<td><code>.admonition</code></td>
<td>Note/warning/tip box</td>
</tr>
<tr>
<td><code>.example-output</code></td>
<td>Expected output display</td>
</tr>
</table>
<h2>Method Signature Format</h2>
<p>Static method:</p>
<pre><code>&lt;div class="method-signature"&gt;
&lt;span class="method-name"&gt;ClassName.methodName&lt;/span&gt;(&lt;span class="param"&gt;arg1&lt;/span&gt;, &lt;span class="param"&gt;arg2&lt;/span&gt;)
&amp;rarr; &lt;span class="type"&gt;String&lt;/span&gt;
&lt;/div&gt;</code></pre>
<p>Instance method:</p>
<pre><code>&lt;div class="method-signature"&gt;
&lt;span class="method-name"&gt;instance.methodName&lt;/span&gt;(&lt;span class="param"&gt;arg&lt;/span&gt;)
&amp;rarr; &lt;span class="type"&gt;Bool&lt;/span&gt;
&lt;/div&gt;</code></pre>
<p>Property (getter):</p>
<pre><code>&lt;div class="method-signature"&gt;
&lt;span class="method-name"&gt;instance.propertyName&lt;/span&gt;
&amp;rarr; &lt;span class="type"&gt;Num&lt;/span&gt;
&lt;/div&gt;</code></pre>
<h2>Admonition Blocks</h2>
<p>Use admonitions for notes, warnings, and tips:</p>
<h3>Note</h3>
<pre><code>&lt;div class="admonition note"&gt;
&lt;div class="admonition-title"&gt;Note&lt;/div&gt;
&lt;p&gt;Additional information.&lt;/p&gt;
&lt;/div&gt;</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Additional information.</p>
</div>
<h3>Warning</h3>
<pre><code>&lt;div class="admonition warning"&gt;
&lt;div class="admonition-title"&gt;Warning&lt;/div&gt;
&lt;p&gt;Important caution.&lt;/p&gt;
&lt;/div&gt;</code></pre>
<div class="admonition warning">
<div class="admonition-title">Warning</div>
<p>Important caution.</p>
</div>
<h3>Tip</h3>
<pre><code>&lt;div class="admonition tip"&gt;
&lt;div class="admonition-title"&gt;Tip&lt;/div&gt;
&lt;p&gt;Helpful suggestion.&lt;/p&gt;
&lt;/div&gt;</code></pre>
<h2>Parameter Lists</h2>
<p>For methods with complex parameters:</p>
<pre><code>&lt;div class="param-list"&gt;
&lt;div class="param-item"&gt;
&lt;span class="param-name"&gt;path&lt;/span&gt;
&lt;span class="param-type"&gt;String&lt;/span&gt;
&lt;p&gt;The file path to read.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="param-item"&gt;
&lt;span class="param-name"&gt;encoding&lt;/span&gt;
&lt;span class="param-type"&gt;String&lt;/span&gt;
&lt;p&gt;The character encoding (default: "utf-8").&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</code></pre>
<h2>Tables</h2>
<p>Use tables for options, type mappings, or comparisons:</p>
<pre><code>&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"r"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"w"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write mode&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;</code></pre>
<h2>Footer Navigation</h2>
<p>Every page has previous/next navigation:</p>
<pre><code>&lt;footer class="page-footer"&gt;
&lt;a href="previous.html" class="prev"&gt;Previous Page&lt;/a&gt;
&lt;a href="next.html" class="next"&gt;Next Page&lt;/a&gt;
&lt;/footer&gt;</code></pre>
<p>Update the footer on adjacent pages when adding new pages.</p>
<h2>Breadcrumb Navigation</h2>
<p>Shows the page hierarchy:</p>
<pre><code>&lt;nav class="breadcrumb"&gt;
&lt;a href="../index.html"&gt;Home&lt;/a&gt;
&lt;span class="separator"&gt;/&lt;/span&gt;
&lt;a href="index.html"&gt;API Reference&lt;/a&gt;
&lt;span class="separator"&gt;/&lt;/span&gt;
&lt;span&gt;io&lt;/span&gt;
&lt;/nav&gt;</code></pre>
<h2>Syncing the Sidebar</h2>
<pre><code>make sync-manual</code></pre>
<p>This runs <code>util/sync_sidebar.py</code> which:</p>
<ol>
<li>Discovers all API module pages</li>
<li>Builds the complete sidebar HTML</li>
<li>Updates the <code>&lt;nav class="sidebar-nav"&gt;</code> in every HTML file</li>
<li>Sets the <code>active</code> class on the current page's link</li>
</ol>
<p>The script is idempotent - running it twice produces the same result.</p>
<h2>Adding a New API Page</h2>
<ol>
<li>Create <code>manual/api/&lt;name&gt;.html</code></li>
<li>Use the template structure above</li>
<li>Run <code>make sync-manual</code></li>
<li>Update footer prev/next on adjacent pages</li>
<li>Add entry to <code>manual/api/index.html</code></li>
</ol>
<h2>Example: Minimal API Page</h2>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;!-- retoor &lt;retoor@molodetz.nl&gt; --&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;mymodule - Wren-CLI Manual&lt;/title&gt;
&lt;link rel="stylesheet" href="../css/style.css"&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;button class="mobile-menu-toggle"&gt;Menu&lt;/button&gt;
&lt;div class="container"&gt;
&lt;aside class="sidebar"&gt;
&lt;div class="sidebar-header"&gt;
&lt;h1&gt;&lt;a href="../index.html"&gt;Wren-CLI&lt;/a&gt;&lt;/h1&gt;
&lt;div class="version"&gt;v0.4.0&lt;/div&gt;
&lt;/div&gt;
&lt;nav class="sidebar-nav"&gt;
&lt;/nav&gt;
&lt;/aside&gt;
&lt;main class="content"&gt;
&lt;nav class="breadcrumb"&gt;
&lt;a href="../index.html"&gt;Home&lt;/a&gt;
&lt;span class="separator"&gt;/&lt;/span&gt;
&lt;a href="index.html"&gt;API Reference&lt;/a&gt;
&lt;span class="separator"&gt;/&lt;/span&gt;
&lt;span&gt;mymodule&lt;/span&gt;
&lt;/nav&gt;
&lt;article&gt;
&lt;h1&gt;mymodule&lt;/h1&gt;
&lt;p&gt;The &lt;code&gt;mymodule&lt;/code&gt; module provides...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import "mymodule" for MyClass&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;MyClass&lt;/h2&gt;
&lt;h3&gt;Static Methods&lt;/h3&gt;
&lt;div class="method-signature"&gt;
&lt;span class="method-name"&gt;MyClass.process&lt;/span&gt;(&lt;span class="param"&gt;input&lt;/span&gt;)
&amp;rarr; &lt;span class="type"&gt;String&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Processes the input and returns a result.&lt;/p&gt;
&lt;h3&gt;Examples&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import "mymodule" for MyClass
var result = MyClass.process("hello")
System.print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;/article&gt;
&lt;footer class="page-footer"&gt;
&lt;a href="math.html" class="prev"&gt;math&lt;/a&gt;
&lt;a href="net.html" class="next"&gt;net&lt;/a&gt;
&lt;/footer&gt;
&lt;/main&gt;
&lt;/div&gt;
&lt;script src="../js/main.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h2>Checklist for New Documentation</h2>
<ul>
<li>Author comment on line 2</li>
<li>Correct title in <code>&lt;title&gt;</code> and <code>&lt;h1&gt;</code></li>
<li>Proper breadcrumb navigation</li>
<li>Import statement example</li>
<li>All methods documented with signatures</li>
<li>Working code examples</li>
<li>Footer with prev/next links</li>
<li>Ran <code>make sync-manual</code></li>
<li>Updated adjacent page footers</li>
<li>Added to index page if applicable</li>
</ul>
</article>
<footer class="page-footer">
<a href="testing.html" class="prev">Writing Tests</a>
<a href="../api/index.html" class="next">API Reference</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
+511
View File
@@ -0,0 +1,511 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Foreign Classes - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="module-overview.html">Module Architecture</a></li>
<li><a href="pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="c-backed-module.html">C-Backed Modules</a></li>
<li><a href="foreign-classes.html" class="active">Foreign Classes</a></li>
<li><a href="async-patterns.html">Async Patterns</a></li>
<li><a href="testing.html">Writing Tests</a></li>
<li><a href="documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">Contributing</a>
<span class="separator">/</span>
<span>Foreign Classes</span>
</nav>
<article>
<h1>Foreign Classes</h1>
<p>Foreign classes allow Wren objects to hold native C data. This is used when a Wren object needs to manage resources like file handles, network sockets, database connections, or any native data structure.</p>
<h2>When to Use Foreign Classes</h2>
<ul>
<li>Wrapping system resources (files, sockets, processes)</li>
<li>Managing native library objects</li>
<li>Storing data structures more complex than Wren's built-in types</li>
<li>Resources requiring explicit cleanup</li>
</ul>
<h2>Architecture</h2>
<p>A foreign class has three components:</p>
<ol>
<li><strong>Allocate function</strong>: Called when an instance is created via <code>construct new()</code></li>
<li><strong>Finalize function</strong>: Called when the garbage collector frees the instance</li>
<li><strong>Instance methods</strong>: Operate on the foreign data</li>
</ol>
<h2>Basic Pattern</h2>
<h3>Wren Interface</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
class Buffer {
foreign construct new(size)
foreign write(data)
foreign read()
foreign size
foreign clear()
}</code></pre>
<p>The constructor uses <code>foreign construct</code> to trigger allocation.</p>
<h3>C Implementation</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include "buffer.h"
#include "wren.h"
typedef struct {
char* data;
size_t size;
size_t capacity;
} Buffer;
void bufferAllocate(WrenVM* vm) {
Buffer* buffer = (Buffer*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(Buffer));
double capacity = wrenGetSlotDouble(vm, 1);
buffer->capacity = (size_t)capacity;
buffer->size = 0;
buffer->data = (char*)malloc(buffer->capacity);
if (!buffer->data) {
buffer->capacity = 0;
}
}
void bufferFinalize(void* data) {
Buffer* buffer = (Buffer*)data;
if (buffer->data) {
free(buffer->data);
buffer->data = NULL;
}
}
void bufferWrite(WrenVM* vm) {
Buffer* buffer = (Buffer*)wrenGetSlotForeign(vm, 0);
const char* str = wrenGetSlotString(vm, 1);
size_t len = strlen(str);
if (buffer->size + len > buffer->capacity) {
wrenSetSlotString(vm, 0, "Buffer overflow.");
wrenAbortFiber(vm, 0);
return;
}
memcpy(buffer->data + buffer->size, str, len);
buffer->size += len;
}
void bufferRead(WrenVM* vm) {
Buffer* buffer = (Buffer*)wrenGetSlotForeign(vm, 0);
char* copy = (char*)malloc(buffer->size + 1);
if (!copy) {
wrenSetSlotNull(vm, 0);
return;
}
memcpy(copy, buffer->data, buffer->size);
copy[buffer->size] = '\0';
wrenSetSlotString(vm, 0, copy);
free(copy);
}
void bufferSize(WrenVM* vm) {
Buffer* buffer = (Buffer*)wrenGetSlotForeign(vm, 0);
wrenSetSlotDouble(vm, 0, (double)buffer->size);
}
void bufferClear(WrenVM* vm) {
Buffer* buffer = (Buffer*)wrenGetSlotForeign(vm, 0);
buffer->size = 0;
}</code></pre>
<h3>Registration</h3>
<pre><code>MODULE(buffer)
CLASS(Buffer)
ALLOCATE(bufferAllocate)
FINALIZE(bufferFinalize)
METHOD("write(_)", bufferWrite)
METHOD("read()", bufferRead)
METHOD("size", bufferSize)
METHOD("clear()", bufferClear)
END_CLASS
END_MODULE</code></pre>
<h2>Memory Management</h2>
<h3>wrenSetSlotNewForeign</h3>
<pre><code>void* wrenSetSlotNewForeign(WrenVM* vm, int slot, int classSlot, size_t size);</code></pre>
<ul>
<li><code>slot</code>: Where to place the new instance (usually 0)</li>
<li><code>classSlot</code>: Slot containing the class (usually 0 for the current class)</li>
<li><code>size</code>: Size of the native data structure</li>
</ul>
<p>Returns a pointer to the allocated memory. This memory is managed by Wren's garbage collector.</p>
<h3>Finalize Function Signature</h3>
<pre><code>void myFinalize(void* data);</code></pre>
<p>The finalize function receives only a pointer to the foreign data, not the VM. This means:</p>
<ul>
<li>No Wren API calls in finalize</li>
<li>Cannot throw errors</li>
<li>Must be fast (GC is running)</li>
<li>Free any resources allocated in allocate or methods</li>
</ul>
<h3>Accessing Foreign Data</h3>
<p>In instance methods, use <code>wrenGetSlotForeign</code> on slot 0:</p>
<pre><code>void bufferMethod(WrenVM* vm) {
Buffer* buffer = (Buffer*)wrenGetSlotForeign(vm, 0);
// buffer points to the struct created in bufferAllocate
}</code></pre>
<h2>File Handle Example</h2>
<p>A practical example wrapping a file handle:</p>
<h3>filehandle.wren</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
class FileHandle {
foreign construct open(path, mode)
foreign read()
foreign write(data)
foreign close()
foreign isOpen
}</code></pre>
<h3>filehandle.c</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include "filehandle.h"
#include "wren.h"
typedef struct {
FILE* file;
char* path;
} FileHandle;
void fileHandleAllocate(WrenVM* vm) {
FileHandle* handle = (FileHandle*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(FileHandle));
const char* path = wrenGetSlotString(vm, 1);
const char* mode = wrenGetSlotString(vm, 2);
handle->path = strdup(path);
handle->file = fopen(path, mode);
if (!handle->file) {
wrenSetSlotString(vm, 0, "Failed to open file.");
wrenAbortFiber(vm, 0);
}
}
void fileHandleFinalize(void* data) {
FileHandle* handle = (FileHandle*)data;
if (handle->file) {
fclose(handle->file);
handle->file = NULL;
}
if (handle->path) {
free(handle->path);
handle->path = NULL;
}
}
void fileHandleRead(WrenVM* vm) {
FileHandle* handle = (FileHandle*)wrenGetSlotForeign(vm, 0);
if (!handle->file) {
wrenSetSlotString(vm, 0, "File not open.");
wrenAbortFiber(vm, 0);
return;
}
fseek(handle->file, 0, SEEK_END);
long size = ftell(handle->file);
fseek(handle->file, 0, SEEK_SET);
char* content = (char*)malloc(size + 1);
if (!content) {
wrenSetSlotString(vm, 0, "Memory allocation failed.");
wrenAbortFiber(vm, 0);
return;
}
fread(content, 1, size, handle->file);
content[size] = '\0';
wrenSetSlotString(vm, 0, content);
free(content);
}
void fileHandleWrite(WrenVM* vm) {
FileHandle* handle = (FileHandle*)wrenGetSlotForeign(vm, 0);
const char* data = wrenGetSlotString(vm, 1);
if (!handle->file) {
wrenSetSlotString(vm, 0, "File not open.");
wrenAbortFiber(vm, 0);
return;
}
size_t written = fwrite(data, 1, strlen(data), handle->file);
wrenSetSlotDouble(vm, 0, (double)written);
}
void fileHandleClose(WrenVM* vm) {
FileHandle* handle = (FileHandle*)wrenGetSlotForeign(vm, 0);
if (handle->file) {
fclose(handle->file);
handle->file = NULL;
}
}
void fileHandleIsOpen(WrenVM* vm) {
FileHandle* handle = (FileHandle*)wrenGetSlotForeign(vm, 0);
wrenSetSlotBool(vm, 0, handle->file != NULL);
}</code></pre>
<h3>Usage</h3>
<pre><code>import "filehandle" for FileHandle
var file = FileHandle.open("test.txt", "w")
file.write("Hello, World!")
file.close()
file = FileHandle.open("test.txt", "r")
System.print(file.read())
file.close()</code></pre>
<h2>Multiple Foreign Classes</h2>
<p>A module can have multiple foreign classes:</p>
<pre><code>MODULE(database)
CLASS(Connection)
ALLOCATE(connectionAllocate)
FINALIZE(connectionFinalize)
METHOD("query(_)", connectionQuery)
METHOD("close()", connectionClose)
END_CLASS
CLASS(Statement)
ALLOCATE(statementAllocate)
FINALIZE(statementFinalize)
METHOD("bind(_,_)", statementBind)
METHOD("execute()", statementExecute)
END_CLASS
END_MODULE</code></pre>
<h2>Resource Safety Patterns</h2>
<h3>Early Close</h3>
<p>Always check if resource is still valid:</p>
<pre><code>void handleMethod(WrenVM* vm) {
Handle* h = (Handle*)wrenGetSlotForeign(vm, 0);
if (!h->resource) {
wrenSetSlotString(vm, 0, "Handle already closed.");
wrenAbortFiber(vm, 0);
return;
}
// ...
}</code></pre>
<h3>Double-Free Prevention</h3>
<p>Set pointers to NULL after freeing:</p>
<pre><code>void handleClose(WrenVM* vm) {
Handle* h = (Handle*)wrenGetSlotForeign(vm, 0);
if (h->resource) {
resource_free(h->resource);
h->resource = NULL;
}
}</code></pre>
<h3>Defensive Finalize</h3>
<p>Always handle partially constructed objects:</p>
<pre><code>void handleFinalize(void* data) {
Handle* h = (Handle*)data;
if (h->resource) {
resource_free(h->resource);
}
if (h->name) {
free(h->name);
}
}</code></pre>
<h2>Common Pitfalls</h2>
<ul>
<li><strong>Forgetting FINALIZE</strong>: Memory leaks for any allocated resources</li>
<li><strong>Using VM in finalize</strong>: Causes undefined behavior</li>
<li><strong>Wrong slot for foreign data</strong>: Instance methods get <code>this</code> in slot 0</li>
<li><strong>Static methods on foreign class</strong>: Use <code>STATIC_METHOD</code> macro, but note that <code>wrenGetSlotForeign</code> is not available (no instance)</li>
</ul>
<h2>Checklist</h2>
<ul>
<li><code>foreign construct</code> in Wren class</li>
<li>Allocate function uses <code>wrenSetSlotNewForeign</code></li>
<li>Finalize function frees all resources</li>
<li><code>ALLOCATE</code> and <code>FINALIZE</code> in registration</li>
<li>Instance methods use <code>METHOD</code> (not <code>STATIC_METHOD</code>)</li>
<li>Methods access foreign data via slot 0</li>
<li>All methods check resource validity</li>
</ul>
<h2>Next Steps</h2>
<p>For I/O-bound foreign classes, see <a href="async-patterns.html">Async Patterns</a> to integrate with the libuv event loop.</p>
</article>
<footer class="page-footer">
<a href="c-backed-module.html" class="prev">C-Backed Modules</a>
<a href="async-patterns.html" class="next">Async Patterns</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
+254
View File
@@ -0,0 +1,254 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contributing - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="index.html" class="active">Overview</a></li>
<li><a href="module-overview.html">Module Architecture</a></li>
<li><a href="pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="c-backed-module.html">C-Backed Modules</a></li>
<li><a href="foreign-classes.html">Foreign Classes</a></li>
<li><a href="async-patterns.html">Async Patterns</a></li>
<li><a href="testing.html">Writing Tests</a></li>
<li><a href="documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<span>Contributing</span>
</nav>
<article>
<h1>Contributing</h1>
<p>This guide explains how to contribute new modules to Wren-CLI, write tests, and add documentation. Whether you are adding a pure-Wren module or implementing C-backed foreign methods, this section provides step-by-step instructions.</p>
<div class="toc">
<h4>In This Section</h4>
<ul>
<li><a href="module-overview.html">Module Architecture</a> - Project structure and artifact matrix</li>
<li><a href="pure-wren-module.html">Pure-Wren Modules</a> - Step-by-step for Wren-only modules</li>
<li><a href="c-backed-module.html">C-Backed Modules</a> - Implementing foreign methods in C</li>
<li><a href="foreign-classes.html">Foreign Classes</a> - Native resource management</li>
<li><a href="async-patterns.html">Async Patterns</a> - Scheduler/Fiber and libuv integration</li>
<li><a href="testing.html">Writing Tests</a> - Test structure and annotations</li>
<li><a href="documentation.html">Documentation</a> - Writing manual pages</li>
</ul>
</div>
<h2>Prerequisites</h2>
<p>Before contributing, ensure you have:</p>
<ul>
<li>A working build environment (see <a href="../getting-started/installation.html">Installation</a>)</li>
<li>Python 3 (for utility scripts)</li>
<li>Basic understanding of <a href="../language/index.html">Wren syntax</a></li>
<li>For C-backed modules: familiarity with C and the Wren embedding API</li>
</ul>
<h2>Module Types</h2>
<p>Wren-CLI supports two types of modules:</p>
<table>
<tr>
<th>Type</th>
<th>Description</th>
<th>Files Required</th>
</tr>
<tr>
<td>Pure-Wren</td>
<td>Modules written entirely in Wren. Examples: argparse, html, jinja, http, markdown, dataset, web, websocket, wdantic, uuid, tempfile</td>
<td><code>.wren</code>, <code>.wren.inc</code>, modules.c entry</td>
</tr>
<tr>
<td>C-Backed</td>
<td>Modules with foreign methods implemented in C. Examples: json, io, net, crypto, tls, sqlite, base64</td>
<td><code>.wren</code>, <code>.wren.inc</code>, <code>.c</code>, <code>.h</code>, modules.c entry, Makefile entry</td>
</tr>
</table>
<h2>Workflow Checklist</h2>
<h3>Pure-Wren Module</h3>
<ol>
<li>Create <code>src/module/&lt;name&gt;.wren</code></li>
<li>Generate <code>.wren.inc</code> with <code>python3 util/wren_to_c_string.py</code></li>
<li>Add <code>#include</code> and <code>MODULE</code> block in <code>src/cli/modules.c</code></li>
<li>Build with <code>make clean && make build</code></li>
<li>Create tests in <code>test/&lt;name&gt;/</code></li>
<li>Create example in <code>example/&lt;name&gt;_demo.wren</code></li>
<li>Create <code>manual/api/&lt;name&gt;.html</code></li>
<li>Run <code>make sync-manual</code></li>
<li>Verify with <code>python3 util/test.py &lt;name&gt;</code></li>
</ol>
<h3>C-Backed Module</h3>
<ol>
<li>All pure-Wren steps, plus:</li>
<li>Create <code>src/module/&lt;name&gt;.c</code> with foreign method implementations</li>
<li>Create <code>src/module/&lt;name&gt;.h</code> with function declarations</li>
<li>Add extern declarations in <code>modules.c</code></li>
<li>Add <code>CLASS</code>/<code>METHOD</code> registrations with correct signatures</li>
<li>Add <code>OBJECTS</code> and compilation rule to <code>projects/make/wren_cli.make</code></li>
</ol>
<h2>Build Commands</h2>
<pre><code>make build # Release build
make debug # Debug build
make clean # Clean artifacts
make tests # Build and run all tests
make sync-manual # Sync sidebar across manual pages</code></pre>
<h2>Directory Structure</h2>
<pre><code>src/
cli/
modules.c # Foreign function registry
vm.c # VM and module loading
module/
&lt;name&gt;.wren # Wren interface source
&lt;name&gt;.wren.inc # Generated C string literal
&lt;name&gt;.c # C implementation (if foreign methods)
&lt;name&gt;.h # C header (if foreign methods)
test/
&lt;name&gt;/
&lt;feature&gt;.wren # Test files with annotations
example/
&lt;name&gt;_demo.wren # Usage demonstrations
manual/
api/&lt;name&gt;.html # API documentation</code></pre>
<h2>Getting Help</h2>
<p>If you encounter issues while contributing:</p>
<ul>
<li>Review existing modules as reference implementations</li>
<li>Check the test files for expected behavior patterns</li>
<li>Consult the <a href="module-overview.html">Module Architecture</a> section for detailed artifact requirements</li>
</ul>
<h2>Next Steps</h2>
<p>Start with the <a href="module-overview.html">Module Architecture</a> section to understand the project structure, then proceed to the appropriate module guide based on your needs.</p>
</article>
<footer class="page-footer">
<a href="../howto/error-handling.html" class="prev">Error Handling</a>
<a href="module-overview.html" class="next">Module Architecture</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
+349
View File
@@ -0,0 +1,349 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Module Architecture - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="module-overview.html" class="active">Module Architecture</a></li>
<li><a href="pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="c-backed-module.html">C-Backed Modules</a></li>
<li><a href="foreign-classes.html">Foreign Classes</a></li>
<li><a href="async-patterns.html">Async Patterns</a></li>
<li><a href="testing.html">Writing Tests</a></li>
<li><a href="documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">Contributing</a>
<span class="separator">/</span>
<span>Module Architecture</span>
</nav>
<article>
<h1>Module Architecture</h1>
<p>This section explains how Wren-CLI modules are structured, what files each module type requires, and how the build system processes them.</p>
<h2>Project Structure</h2>
<pre><code>src/
cli/
main.c # Entry point
vm.c # VM initialization, libuv event loop, module loading
modules.c # Foreign function registry, module registration
modules.h # Public interface for module loading/binding
path.c # Cross-platform path manipulation
module/
&lt;name&gt;.wren # Wren interface source
&lt;name&gt;.wren.inc # Generated C string literal (do not edit)
&lt;name&gt;.c # C implementation (only for foreign methods)
&lt;name&gt;.h # C header (only for foreign methods)
test/
&lt;modulename&gt;/
&lt;testname&gt;.wren # Test files with inline annotations
example/
&lt;modulename&gt;_demo.wren # Comprehensive usage demonstrations
manual/
api/ # One HTML file per module
deps/
wren/ # Wren language VM
libuv/ # Async I/O library
cjson/ # JSON parsing library
sqlite/ # SQLite database library</code></pre>
<h2>Module Artifact Matrix</h2>
<p>Every built-in module has up to six artifacts:</p>
<table>
<tr>
<th>Artifact</th>
<th>Path</th>
<th>Required?</th>
</tr>
<tr>
<td>Wren source</td>
<td><code>src/module/&lt;name&gt;.wren</code></td>
<td>Always</td>
</tr>
<tr>
<td>Generated C string</td>
<td><code>src/module/&lt;name&gt;.wren.inc</code></td>
<td>Always</td>
</tr>
<tr>
<td>C implementation</td>
<td><code>src/module/&lt;name&gt;.c</code></td>
<td>Only if foreign methods</td>
</tr>
<tr>
<td>C header</td>
<td><code>src/module/&lt;name&gt;.h</code></td>
<td>Only if .c exists</td>
</tr>
<tr>
<td>Registration in modules.c</td>
<td><code>src/cli/modules.c</code></td>
<td>Always</td>
</tr>
<tr>
<td>Makefile object entry</td>
<td><code>projects/make/wren_cli.make</code></td>
<td>Only if .c exists</td>
</tr>
</table>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Pure-Wren modules (argparse, html, jinja, http, markdown, dataset, web, websocket, wdantic, uuid, tempfile, repl) skip the C files and Makefile entry. C-backed modules (json, io, net, crypto, tls, sqlite, base64, etc.) need all six.</p>
</div>
<h2>The .wren to .wren.inc Pipeline</h2>
<p>Module source code is embedded directly into the compiled binary as C string literals. The script <code>util/wren_to_c_string.py</code> performs this conversion.</p>
<h3>What It Does</h3>
<ol>
<li>Reads the <code>.wren</code> file line by line</li>
<li>Escapes <code>\</code> to <code>\\</code> and <code>"</code> to <code>\"</code></li>
<li>Wraps each line in C string literal quotes with <code>\n</code> appended</li>
<li>Outputs a <code>.wren.inc</code> file with a <code>static const char*</code> variable</li>
</ol>
<h3>Variable Naming</h3>
<p>The variable name is derived from the filename: <code>foo.wren</code> becomes <code>fooModuleSource</code>. Prefixes <code>opt_</code> and <code>wren_</code> are stripped automatically.</p>
<h3>Usage</h3>
<pre><code>python3 util/wren_to_c_string.py src/module/foo.wren.inc src/module/foo.wren</code></pre>
<div class="admonition warning">
<div class="admonition-title">Warning</div>
<p>Every time a <code>.wren</code> file is edited, its <code>.wren.inc</code> must be regenerated. If forgotten, the binary will contain stale module source.</p>
</div>
<h2>Module Registration in modules.c</h2>
<p><code>src/cli/modules.c</code> has three sections to update:</p>
<h3>Section 1: Include the .wren.inc</h3>
<p>Add at the top of the file with other includes:</p>
<pre><code>#include "mymodule.wren.inc"</code></pre>
<h3>Section 2: Extern Declarations</h3>
<p>Only required for C-backed modules:</p>
<pre><code>extern void mymoduleDoSomething(WrenVM* vm);
extern void mymoduleComplexOp(WrenVM* vm);</code></pre>
<h3>Section 3: Module Array Entry</h3>
<p>For pure-Wren modules:</p>
<pre><code>MODULE(mymodule)
END_MODULE</code></pre>
<p>For C-backed modules with static methods:</p>
<pre><code>MODULE(mymodule)
CLASS(MyClass)
STATIC_METHOD("doSomething(_)", mymoduleDoSomething)
STATIC_METHOD("complexOp_(_,_,_)", mymoduleComplexOp)
END_CLASS
END_MODULE</code></pre>
<p>For foreign classes with allocation/finalization:</p>
<pre><code>MODULE(mymodule)
CLASS(MyForeignClass)
ALLOCATE(myClassAllocate)
FINALIZE(myClassFinalize)
METHOD("doThing(_)", myClassDoThing)
END_CLASS
END_MODULE</code></pre>
<h3>Registration Macros Reference</h3>
<table>
<tr>
<th>Macro</th>
<th>Usage</th>
</tr>
<tr>
<td><code>MODULE(name)</code> / <code>END_MODULE</code></td>
<td>Module boundary, name must match import string</td>
</tr>
<tr>
<td><code>CLASS(name)</code> / <code>END_CLASS</code></td>
<td>Class boundary, name must match Wren class name</td>
</tr>
<tr>
<td><code>STATIC_METHOD("sig", fn)</code></td>
<td>Bind static foreign method</td>
</tr>
<tr>
<td><code>METHOD("sig", fn)</code></td>
<td>Bind instance foreign method</td>
</tr>
<tr>
<td><code>ALLOCATE(fn)</code></td>
<td>Foreign class constructor</td>
</tr>
<tr>
<td><code>FINALIZE(fn)</code></td>
<td>Foreign class destructor</td>
</tr>
</table>
<h3>Method Signature Format</h3>
<p>The signature string must exactly match what Wren expects:</p>
<ul>
<li><code>"methodName(_)"</code> - one argument</li>
<li><code>"methodName(_,_)"</code> - two arguments</li>
<li><code>"propertyName"</code> - getter (no parentheses)</li>
<li><code>"propertyName=(_)"</code> - setter</li>
</ul>
<h2>Core Components</h2>
<h3>vm.c</h3>
<p>VM initialization, libuv event loop integration, module resolution. The <code>loadModule()</code> function first checks <code>wren_modules/</code> on disk, then falls back to <code>loadBuiltInModule()</code> which serves embedded <code>.wren.inc</code> strings. The <code>resolveModule()</code> function handles simple imports (bare names to built-in) and relative imports (<code>./</code>, <code>../</code> to file path resolution).</p>
<h3>modules.c</h3>
<p>Central registry of all built-in modules. Contains the <code>modules[]</code> array with module/class/method metadata, the <code>.wren.inc</code> includes, extern declarations for C functions, and lookup functions (<code>findModule</code>, <code>findClass</code>, <code>findMethod</code>).</p>
<h2>Event Loop</h2>
<p>All I/O is async via libuv. Wren fibers suspend during I/O operations and resume when complete. The event loop runs after script interpretation via <code>uv_run(loop, UV_RUN_DEFAULT)</code>.</p>
<h2>System Dependencies</h2>
<ul>
<li>OpenSSL (<code>libssl</code>, <code>libcrypto</code>) - required for TLS/HTTPS support</li>
<li>pthreads, dl, m - linked automatically</li>
</ul>
<h2>Next Steps</h2>
<p>Now that you understand the architecture, proceed to:</p>
<ul>
<li><a href="pure-wren-module.html">Pure-Wren Modules</a> - for modules without C code</li>
<li><a href="c-backed-module.html">C-Backed Modules</a> - for modules with foreign methods</li>
</ul>
</article>
<footer class="page-footer">
<a href="index.html" class="prev">Overview</a>
<a href="pure-wren-module.html" class="next">Pure-Wren Modules</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
+375
View File
@@ -0,0 +1,375 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pure-Wren Modules - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="module-overview.html">Module Architecture</a></li>
<li><a href="pure-wren-module.html" class="active">Pure-Wren Modules</a></li>
<li><a href="c-backed-module.html">C-Backed Modules</a></li>
<li><a href="foreign-classes.html">Foreign Classes</a></li>
<li><a href="async-patterns.html">Async Patterns</a></li>
<li><a href="testing.html">Writing Tests</a></li>
<li><a href="documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">Contributing</a>
<span class="separator">/</span>
<span>Pure-Wren Modules</span>
</nav>
<article>
<h1>Pure-Wren Modules</h1>
<p>Pure-Wren modules are written entirely in Wren, with no C code required. They may depend on other modules (both pure-Wren and C-backed) but do not implement any foreign methods themselves.</p>
<p>Examples of pure-Wren modules: argparse, html, jinja, http, markdown, dataset, web, websocket, wdantic, uuid, tempfile.</p>
<h2>Step 1: Create the Wren Source</h2>
<p>Create <code>src/module/&lt;name&gt;.wren</code> with your module implementation:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
class Utils {
static capitalize(str) {
if (str.count == 0) return str
return str[0].upcase + str[1..-1]
}
static reverse(str) {
var result = ""
for (i in (str.count - 1)..0) {
result = result + str[i]
}
return result
}
static repeat(str, times) {
var result = ""
for (i in 0...times) {
result = result + str
}
return result
}
static words(str) {
return str.split(" ").where {|w| w.count > 0 }.toList
}
}</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Always include the author comment on the first line. Class names should be capitalized and descriptive.</p>
</div>
<h2>Step 2: Generate the .wren.inc</h2>
<p>Convert the Wren source to a C string literal:</p>
<pre><code>python3 util/wren_to_c_string.py src/module/utils.wren.inc src/module/utils.wren</code></pre>
<p>This creates <code>src/module/utils.wren.inc</code> containing:</p>
<pre><code>static const char* utilsModuleSource =
"// retoor &lt;retoor@molodetz.nl&gt;\n"
"\n"
"class Utils {\n"
" static capitalize(str) {\n"
// ... rest of the source
;</code></pre>
<div class="admonition warning">
<div class="admonition-title">Warning</div>
<p>Never edit <code>.wren.inc</code> files directly. Always edit the <code>.wren</code> source and regenerate.</p>
</div>
<h2>Step 3: Register in modules.c</h2>
<p>Edit <code>src/cli/modules.c</code> to register the new module.</p>
<h3>Add the Include</h3>
<p>Near the top of the file with other includes:</p>
<pre><code>#include "utils.wren.inc"</code></pre>
<h3>Add the Module Entry</h3>
<p>In the <code>modules[]</code> array:</p>
<pre><code>MODULE(utils)
END_MODULE</code></pre>
<p>For pure-Wren modules, the block is empty. No class or method registrations are needed because there are no foreign bindings.</p>
<h2>Step 4: Build</h2>
<pre><code>make clean && make build</code></pre>
<p>The clean build ensures the new module is properly included.</p>
<h2>Step 5: Test</h2>
<p>Create the test directory:</p>
<pre><code>mkdir -p test/utils</code></pre>
<p>Create test files with inline annotations. For example, <code>test/utils/capitalize.wren</code>:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "utils" for Utils
System.print(Utils.capitalize("hello")) // expect: Hello
System.print(Utils.capitalize("WORLD")) // expect: WORLD
System.print(Utils.capitalize("")) // expect:</code></pre>
<p>Run the tests:</p>
<pre><code>python3 util/test.py utils</code></pre>
<h2>Step 6: Create Example</h2>
<p>Create <code>example/utils_demo.wren</code>:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "utils" for Utils
System.print("=== Utils Demo ===\n")
System.print("--- Capitalize ---")
System.print(Utils.capitalize("hello"))
System.print(Utils.capitalize("wren"))
System.print("\n--- Reverse ---")
System.print(Utils.reverse("hello"))
System.print(Utils.reverse("12345"))
System.print("\n--- Repeat ---")
System.print(Utils.repeat("ab", 3))
System.print(Utils.repeat("-", 10))
System.print("\n--- Words ---")
var sentence = "The quick brown fox"
var wordList = Utils.words(sentence)
for (word in wordList) {
System.print(" - %(word)")
}</code></pre>
<h2>Step 7: Add Documentation</h2>
<p>Create <code>manual/api/utils.html</code> following the API page template. See the <a href="documentation.html">Documentation</a> section for details.</p>
<h2>Step 8: Sync the Sidebar</h2>
<pre><code>make sync-manual</code></pre>
<p>This updates the sidebar navigation across all manual pages to include the new module.</p>
<h2>Step 9: Verify</h2>
<pre><code>python3 util/test.py utils
bin/wren_cli example/utils_demo.wren</code></pre>
<h2>Complete Example</h2>
<p>Here is a more realistic pure-Wren module that builds on existing modules:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "json" for Json
import "io" for File
class Config {
static load(path) {
var content = File.read(path)
return Json.parse(content)
}
static save(path, data) {
var content = Json.stringify(data, 2)
File.write(path, content)
}
static get(path, key) {
var config = Config.load(path)
return config[key]
}
static set(path, key, value) {
var config = {}
if (File.exists(path)) {
config = Config.load(path)
}
config[key] = value
Config.save(path, config)
}
}</code></pre>
<p>This module depends on <code>json</code> and <code>io</code>, demonstrating how pure-Wren modules compose functionality from other modules.</p>
<h2>Common Patterns</h2>
<h3>Static Utility Classes</h3>
<p>Most pure-Wren modules use static methods for stateless utilities:</p>
<pre><code>class StringUtils {
static trim(s) { ... }
static pad(s, width) { ... }
}</code></pre>
<h3>Factory Classes</h3>
<p>For stateful objects, use instance methods:</p>
<pre><code>class Builder {
construct new() {
_parts = []
}
add(part) {
_parts.add(part)
return this
}
build() { _parts.join("") }
}</code></pre>
<h3>Wrapping Async Operations</h3>
<p>Pure-Wren modules can wrap async operations from C-backed modules:</p>
<pre><code>import "http" for Http
class Api {
static get(endpoint) {
return Http.get("https://api.example.com" + endpoint)
}
}</code></pre>
<h2>Checklist</h2>
<ul>
<li>Created <code>src/module/&lt;name&gt;.wren</code> with author comment</li>
<li>Generated <code>.wren.inc</code> with util script</li>
<li>Added <code>#include</code> in modules.c</li>
<li>Added <code>MODULE</code>/<code>END_MODULE</code> block in modules.c</li>
<li>Built with <code>make clean && make build</code></li>
<li>Created tests in <code>test/&lt;name&gt;/</code></li>
<li>Created example in <code>example/&lt;name&gt;_demo.wren</code></li>
<li>Created documentation page</li>
<li>Ran <code>make sync-manual</code></li>
<li>All tests pass</li>
</ul>
<h2>Next Steps</h2>
<p>If your module requires native functionality not available through existing modules, see <a href="c-backed-module.html">C-Backed Modules</a>.</p>
</article>
<footer class="page-footer">
<a href="module-overview.html" class="prev">Module Architecture</a>
<a href="c-backed-module.html" class="next">C-Backed Modules</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
+442
View File
@@ -0,0 +1,442 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Writing Tests - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="module-overview.html">Module Architecture</a></li>
<li><a href="pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="c-backed-module.html">C-Backed Modules</a></li>
<li><a href="foreign-classes.html">Foreign Classes</a></li>
<li><a href="async-patterns.html">Async Patterns</a></li>
<li><a href="testing.html" class="active">Writing Tests</a></li>
<li><a href="documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">Contributing</a>
<span class="separator">/</span>
<span>Writing Tests</span>
</nav>
<article>
<h1>Writing Tests</h1>
<p>Wren-CLI uses inline annotations in test files to specify expected behavior. The test runner <code>util/test.py</code> parses these annotations and verifies the output.</p>
<h2>Test Directory Structure</h2>
<pre><code>test/
&lt;modulename&gt;/
&lt;feature&gt;.wren # Functional tests
error_&lt;scenario&gt;.wren # Error case tests (one runtime error each)</code></pre>
<p>Each module has its own directory under <code>test/</code>. Test files are named descriptively based on what they test.</p>
<h2>Running Tests</h2>
<pre><code># Build and run all tests
make tests
# Run tests for a specific module
python3 util/test.py json
# Run a specific test file (prefix match)
python3 util/test.py json/parse
# Run with debug binary
python3 util/test.py --suffix=_d</code></pre>
<h2>Test Annotations</h2>
<table>
<tr>
<th>Annotation</th>
<th>Purpose</th>
<th>Expected Exit Code</th>
</tr>
<tr>
<td><code>// expect: output</code></td>
<td>Assert stdout line (matched in order)</td>
<td>0</td>
</tr>
<tr>
<td><code>// expect error</code></td>
<td>Assert compile error on this line</td>
<td>65</td>
</tr>
<tr>
<td><code>// expect error line N</code></td>
<td>Assert compile error on line N</td>
<td>65</td>
</tr>
<tr>
<td><code>// expect runtime error: msg</code></td>
<td>Assert runtime error with exact message</td>
<td>70</td>
</tr>
<tr>
<td><code>// expect handled runtime error: msg</code></td>
<td>Assert caught runtime error</td>
<td>0</td>
</tr>
<tr>
<td><code>// stdin: text</code></td>
<td>Feed text to stdin (repeatable)</td>
<td>-</td>
</tr>
<tr>
<td><code>// skip: reason</code></td>
<td>Skip this test file</td>
<td>-</td>
</tr>
<tr>
<td><code>// nontest</code></td>
<td>Ignore this file</td>
<td>-</td>
</tr>
</table>
<h2>Basic Test Example</h2>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "json" for Json
var obj = {"name": "test", "value": 42}
var str = Json.stringify(obj)
var parsed = Json.parse(str)
System.print(parsed["name"]) // expect: test
System.print(parsed["value"]) // expect: 42</code></pre>
<p>Each <code>// expect:</code> annotation asserts that the corresponding line of output matches exactly.</p>
<h2>Multiple Expect Annotations</h2>
<p>Multiple expectations are matched in order:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "utils" for Utils
System.print(Utils.capitalize("hello")) // expect: Hello
System.print(Utils.capitalize("world")) // expect: World
System.print(Utils.capitalize("UPPER")) // expect: UPPER
System.print(Utils.capitalize("")) // expect:</code></pre>
<p>The empty <code>// expect:</code> matches an empty line.</p>
<h2>Runtime Error Tests</h2>
<p>For testing error conditions, create a separate file per error case:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "json" for Json
Json.parse("invalid json") // expect runtime error: Invalid JSON.</code></pre>
<div class="admonition warning">
<div class="admonition-title">Warning</div>
<p>Only one runtime error per test file. The test runner tracks a single expected error. Split multiple error cases into separate files.</p>
</div>
<h3>Error Line Matching</h3>
<p>The runtime error annotation must be on the line that calls the aborting code. The runner checks the stack trace for a <code>test/...</code> path and matches the line number.</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "io" for File
var content = File.read("/nonexistent/path") // expect runtime error: Cannot open file.</code></pre>
<h2>Compile Error Tests</h2>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
class Test {
foo( // expect error
}</code></pre>
<p>Or specify a different line number:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
// This is valid
var x = 1
class Broken {
// expect error line 7
foo(</code></pre>
<h2>Stdin Input</h2>
<p>Use <code>// stdin:</code> to provide input:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "io" for Stdin
// stdin: hello
// stdin: world
var line1 = Stdin.readLine()
var line2 = Stdin.readLine()
System.print(line1) // expect: hello
System.print(line2) // expect: world</code></pre>
<p>Multiple <code>// stdin:</code> lines are concatenated with newlines.</p>
<h2>Skipping Tests</h2>
<p>Use <code>// skip:</code> for tests that cannot run in all environments:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
// skip: Requires network access
import "http" for Http
var response = Http.get("https://example.com")
System.print(response.status) // expect: 200</code></pre>
<h2>Non-Test Files</h2>
<p>Use <code>// nontest</code> for helper files that should not be run as tests:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
// nontest
class TestHelper {
static setup() { ... }
}</code></pre>
<h2>Test File Organization</h2>
<h3>Feature Tests</h3>
<p>Test each feature in a dedicated file:</p>
<pre><code>test/json/
parse.wren # Basic parsing
parse_nested.wren # Nested objects/arrays
stringify.wren # JSON serialization
stringify_pretty.wren # Pretty printing
types.wren # Type handling</code></pre>
<h3>Error Tests</h3>
<p>One error per file, named with <code>error_</code> prefix:</p>
<pre><code>test/json/
error_invalid_syntax.wren
error_unexpected_eof.wren
error_invalid_escape.wren</code></pre>
<h2>Handled Runtime Error</h2>
<p>For testing error handling where errors are caught:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "json" for Json
var result = Fiber.new {
Json.parse("bad")
}.try()
if (result.error) {
System.print("Caught error") // expect: Caught error
} // expect handled runtime error: Invalid JSON.</code></pre>
<h2>Test Timeout</h2>
<p>Each test file has a 15-second timeout. If a test hangs (e.g., waiting for input that never comes), it will be killed.</p>
<h2>Test Discovery</h2>
<p>The test runner discovers tests by:</p>
<ol>
<li>Walking the <code>test/</code> directory recursively</li>
<li>Filtering by <code>.wren</code> extension</li>
<li>Converting paths to relative paths from <code>test/</code></li>
<li>Checking if path starts with the filter argument</li>
</ol>
<p>This means:</p>
<ul>
<li><code>python3 util/test.py json</code> runs everything in <code>test/json/</code></li>
<li><code>python3 util/test.py io/file</code> runs everything in <code>test/io/file/</code></li>
<li>Subdirectories are supported for organizing large test suites</li>
</ul>
<h2>Example Test Suite</h2>
<pre><code>test/mymodule/
basic.wren
advanced.wren
edge_cases.wren
error_null_input.wren
error_invalid_type.wren
error_overflow.wren</code></pre>
<h3>basic.wren</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "mymodule" for MyClass
System.print(MyClass.process("hello")) // expect: HELLO
System.print(MyClass.process("world")) // expect: WORLD
System.print(MyClass.length("test")) // expect: 4</code></pre>
<h3>error_null_input.wren</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "mymodule" for MyClass
MyClass.process(null) // expect runtime error: Input cannot be null.</code></pre>
<h2>Debugging Failing Tests</h2>
<ol>
<li>Run the test manually: <code>bin/wren_cli test/mymodule/failing.wren</code></li>
<li>Check the actual output versus expected</li>
<li>Verify annotations are on correct lines</li>
<li>For runtime errors, ensure annotation is on the calling line</li>
</ol>
<h2>Best Practices</h2>
<ul>
<li>Test one concept per file when possible</li>
<li>Use descriptive file names</li>
<li>Always include the author comment</li>
<li>Test edge cases (empty strings, null, large values)</li>
<li>Test error conditions in separate files</li>
<li>Keep tests simple and focused</li>
</ul>
<h2>Next Steps</h2>
<p>After writing tests, add documentation in <a href="documentation.html">Documentation</a>.</p>
</article>
<footer class="page-footer">
<a href="async-patterns.html" class="prev">Async Patterns</a>
<a href="documentation.html" class="next">Documentation</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
+41 -16
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -74,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -88,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+41 -16
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -74,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -88,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+41 -16
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -74,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -88,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+41 -16
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -74,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -88,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+250 -234
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -72,8 +83,9 @@
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Template Rendering</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -88,6 +100,19 @@
<li><a href="error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
@@ -102,22 +127,155 @@
<article>
<h1>Async Programming</h1>
<p>Wren-CLI uses fibers for concurrent operations. Understanding fibers is key to writing efficient async code.</p>
<p>Wren-CLI provides <code>async</code> and <code>await</code> keywords for writing concurrent code. This guide covers the essential patterns for async programming.</p>
<h2>Create a Fiber</h2>
<pre><code>var fiber = Fiber.new {
System.print("Hello from fiber!")
<h2>Create an Async Function</h2>
<pre><code>import "scheduler" for Scheduler, Future
var getValue = async { 42 }
var result = await getValue()
System.print(result) // 42</code></pre>
<h2>Async Functions with Parameters</h2>
<pre><code>import "scheduler" for Scheduler, Future
var double = async { |x| x * 2 }
var add = async { |a, b| a + b }
System.print(await double(21)) // 42
System.print(await add(3, 4)) // 7</code></pre>
<h2>Direct Calling vs .call()</h2>
<p>There are two ways to invoke async functions:</p>
<ul>
<li><code>await fn(args)</code> — Direct call, waits immediately (sequential)</li>
<li><code>fn.call(args)</code> — Returns Future, starts without waiting (concurrent)</li>
</ul>
<pre><code>import "scheduler" for Scheduler, Future
var slow = async { |n|
Timer.sleep(100)
return n
}
fiber.call()</code></pre>
// SEQUENTIAL: Each call waits before the next starts
var a = await slow(1)
var b = await slow(2)
var c = await slow(3) // Total: ~300ms
<h2>Fibers with Return Values</h2>
<pre><code>var fiber = Fiber.new {
return 42
// CONCURRENT: All calls start at once, then wait for results
var f1 = slow.call(1)
var f2 = slow.call(2)
var f3 = slow.call(3)
var r1 = await f1
var r2 = await f2
var r3 = await f3 // Total: ~100ms</code></pre>
<h2>Sequential HTTP Requests</h2>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "json" for Json
var fetchJson = async { |url|
var response = Client.get(url)
return Json.parse(response["body"])
}
var result = fiber.call()
System.print("Result: %(result)") // 42</code></pre>
// Each request waits for the previous one
var user = await fetchJson("https://api.example.com/user/1")
var posts = await fetchJson("https://api.example.com/posts")
var comments = await fetchJson("https://api.example.com/comments")
System.print(user["name"])
System.print(posts.count)
System.print(comments.count)</code></pre>
<h2>Concurrent HTTP Requests</h2>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "json" for Json
var fetchJson = async { |url|
var response = Client.get(url)
return Json.parse(response["body"])
}
// Start all requests at once
var f1 = fetchJson.call("https://api.example.com/user/1")
var f2 = fetchJson.call("https://api.example.com/posts")
var f3 = fetchJson.call("https://api.example.com/comments")
// Wait for results (requests run in parallel)
var user = await f1
var posts = await f2
var comments = await f3
System.print(user["name"])
System.print(posts.count)
System.print(comments.count)</code></pre>
<h2>Batch Processing</h2>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
var urls = [
"https://api.example.com/1",
"https://api.example.com/2",
"https://api.example.com/3",
"https://api.example.com/4",
"https://api.example.com/5"
]
// Start all requests concurrently
var futures = []
for (url in urls) {
futures.add(async { Client.get(url) })
}
// Collect results
var responses = []
for (f in futures) {
responses.add(await f)
}
for (i in 0...urls.count) {
System.print("%(urls[i]): %(responses[i]["status"])")
}</code></pre>
<h2>Reusable Batch Fetcher</h2>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "json" for Json
var fetchJson = async { |url|
var response = Client.get(url)
return Json.parse(response["body"])
}
class BatchFetcher {
static getAll(urls) {
var futures = []
for (url in urls) {
futures.add(fetchJson.call(url))
}
var results = []
for (f in futures) {
results.add(await f)
}
return results
}
}
var urls = [
"https://api.example.com/users",
"https://api.example.com/posts",
"https://api.example.com/comments"
]
var results = BatchFetcher.getAll(urls)
for (result in results) {
System.print(result)</code></pre>
<h2>Sleep/Delay</h2>
<pre><code>import "timer" for Timer
@@ -126,133 +284,77 @@ System.print("Starting...")
Timer.sleep(1000) // Wait 1 second
System.print("Done!")</code></pre>
<h2>Sequential HTTP Requests</h2>
<pre><code>import "http" for Http
<h2>Async with Error Handling</h2>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "json" for Json
var r1 = Http.get("https://api.example.com/1")
var r2 = Http.get("https://api.example.com/2")
var r3 = Http.get("https://api.example.com/3")
System.print(r1.json)
System.print(r2.json)
System.print(r3.json)</code></pre>
<h2>Parallel HTTP Requests</h2>
<pre><code>import "http" for Http
var urls = [
"https://api.example.com/1",
"https://api.example.com/2",
"https://api.example.com/3"
]
var fibers = []
for (url in urls) {
fibers.add(Fiber.new { Http.get(url) })
var safeFetch = async { |url|
var fiber = Fiber.new {
var response = Client.get(url)
return Json.parse(response["body"])
}
var result = fiber.try()
if (fiber.error) {
return {"error": fiber.error}
}
return {"data": result}
}
for (fiber in fibers) {
fiber.call()
}
for (fiber in fibers) {
System.print(fiber.value)
var result = await safeFetch("https://api.example.com/data")
if (result["error"]) {
System.print("Error: %(result["error"])")
} else {
System.print("Data: %(result["data"])")
}</code></pre>
<h2>Run Task in Background</h2>
<pre><code>import "timer" for Timer
<h2>Retry with Backoff</h2>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "timer" for Timer
var backgroundTask = Fiber.new {
for (i in 1..5) {
System.print("Background: %(i)")
Timer.sleep(500)
}
}
var fetchWithRetry = async { |url, maxRetries|
var attempt = 0
var delay = 1000
backgroundTask.call()
while (attempt < maxRetries) {
var fiber = Fiber.new { Client.get(url) }
var result = fiber.try()
System.print("Main thread continues...")
Timer.sleep(3000)</code></pre>
<h2>Wait for Multiple Operations</h2>
<pre><code>import "http" for Http
var fetchAll = Fn.new { |urls|
var results = []
var fibers = []
for (url in urls) {
fibers.add(Fiber.new { Http.get(url) })
}
for (fiber in fibers) {
fiber.call()
}
for (fiber in fibers) {
results.add(fiber.value)
}
return results
}
var responses = fetchAll.call([
"https://api.example.com/a",
"https://api.example.com/b"
])
for (r in responses) {
System.print(r.statusCode)
}</code></pre>
<h2>Timeout Pattern</h2>
<pre><code>import "timer" for Timer
import "datetime" for DateTime
var withTimeout = Fn.new { |operation, timeoutMs|
var start = DateTime.now()
var result = null
var done = false
var workFiber = Fiber.new {
result = operation.call()
done = true
}
workFiber.call()
while (!done) {
var elapsed = (DateTime.now() - start).milliseconds
if (elapsed >= timeoutMs) {
Fiber.abort("Operation timed out")
if (!fiber.error && result["status"] == 200) {
return result
}
Timer.sleep(10)
attempt = attempt + 1
System.print("Attempt %(attempt) failed, retrying...")
Timer.sleep(delay)
delay = delay * 2
}
return result
Fiber.abort("All %(maxRetries) attempts failed")
}
var result = withTimeout.call(Fn.new {
Timer.sleep(500)
return "completed"
}, 1000)
var response = await fetchWithRetry("https://api.example.com/data", 3)
System.print("Success: %(response["status"])")</code></pre>
System.print(result)</code></pre>
<h2>Polling Pattern</h2>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "timer" for Timer
import "json" for Json
<h2>Polling Loop</h2>
<pre><code>import "timer" for Timer
import "http" for Http
var pollUntilReady = Fn.new { |url, maxAttempts|
var pollUntilReady = async { |url, maxAttempts|
var attempts = 0
while (attempts < maxAttempts) {
attempts = attempts + 1
System.print("Attempt %(attempts)...")
System.print("Checking status (attempt %(attempts))...")
var response = Http.get(url)
if (response.json["ready"]) {
return response.json
var response = Client.get(url)
var data = Json.parse(response["body"])
if (data["status"] == "ready") {
return data
}
Timer.sleep(2000)
@@ -261,22 +363,23 @@ var pollUntilReady = Fn.new { |url, maxAttempts|
return null
}
var result = pollUntilReady.call("https://api.example.com/status", 10)
var result = await pollUntilReady("https://api.example.com/job/123", 10)
if (result) {
System.print("Ready: %(result)")
System.print("Job completed: %(result)")
} else {
System.print("Timed out waiting for ready state")
System.print("Timed out waiting for job")
}</code></pre>
<h2>Rate Limiting</h2>
<pre><code>import "timer" for Timer
import "http" for Http
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "timer" for Timer
var rateLimitedFetch = Fn.new { |urls, delayMs|
var rateLimitedFetch = async { |urls, delayMs|
var results = []
for (url in urls) {
var response = Http.get(url)
var response = Client.get(url)
results.add(response)
Timer.sleep(delayMs)
}
@@ -284,104 +387,17 @@ var rateLimitedFetch = Fn.new { |urls, delayMs|
return results
}
var responses = rateLimitedFetch.call([
var urls = [
"https://api.example.com/1",
"https://api.example.com/2",
"https://api.example.com/3"
], 1000)
]
var responses = await rateLimitedFetch(urls, 500)
for (r in responses) {
System.print(r.statusCode)
System.print(r["status"])
}</code></pre>
<h2>Producer/Consumer Pattern</h2>
<pre><code>import "timer" for Timer
var queue = []
var running = true
var producer = Fiber.new {
for (i in 1..10) {
queue.add(i)
System.print("Produced: %(i)")
Timer.sleep(200)
}
running = false
}
var consumer = Fiber.new {
while (running || queue.count > 0) {
if (queue.count > 0) {
var item = queue.removeAt(0)
System.print("Consumed: %(item)")
}
Timer.sleep(100)
}
}
producer.call()
consumer.call()
System.print("Done!")</code></pre>
<h2>Retry with Exponential Backoff</h2>
<pre><code>import "timer" for Timer
import "http" for Http
var retryWithBackoff = Fn.new { |operation, maxRetries|
var attempt = 0
var delay = 1000
while (attempt < maxRetries) {
var fiber = Fiber.new { operation.call() }
var result = fiber.try()
if (!fiber.error) {
return result
}
attempt = attempt + 1
System.print("Attempt %(attempt) failed, retrying in %(delay)ms...")
Timer.sleep(delay)
delay = delay * 2
}
Fiber.abort("All %(maxRetries) attempts failed")
}
var response = retryWithBackoff.call(Fn.new {
return Http.get("https://api.example.com/data")
}, 3)
System.print(response.json)</code></pre>
<h2>Concurrent WebSocket Handling</h2>
<pre><code>import "websocket" for WebSocket, WebSocketMessage
import "timer" for Timer
var ws = WebSocket.connect("ws://localhost:8080")
var receiver = Fiber.new {
while (true) {
var message = ws.receive()
if (message == null) break
if (message.opcode == WebSocketMessage.TEXT) {
System.print("Received: %(message.payload)")
}
}
}
var sender = Fiber.new {
for (i in 1..5) {
ws.send("Message %(i)")
Timer.sleep(1000)
}
ws.close()
}
receiver.call()
sender.call()</code></pre>
<h2>Graceful Shutdown</h2>
<pre><code>import "signal" for Signal
import "timer" for Timer
@@ -404,12 +420,12 @@ System.print("Cleanup complete, exiting.")</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Wren fibers are cooperative, not preemptive. A fiber runs until it explicitly yields, calls an async operation, or completes. Long-running computations should periodically yield to allow other fibers to run.</p>
<p>Always import <code>Scheduler</code> and <code>Future</code> from the scheduler module when using <code>async</code> and <code>await</code>. The syntax requires these classes to be in scope.</p>
</div>
<div class="admonition tip">
<div class="admonition-title">See Also</div>
<p>For more on fibers, see the <a href="../language/fibers.html">Fibers language guide</a> and the <a href="../api/scheduler.html">Scheduler module reference</a>.</p>
<p>For more details, see the <a href="../api/scheduler.html">Scheduler API reference</a> and the <a href="../api/web.html">Web module</a> for HTTP client examples.</p>
</div>
</article>
+42 -17
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -72,8 +83,9 @@
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Template Rendering</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -88,6 +100,19 @@
<li><a href="error-handling.html" class="active">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+42 -17
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -72,8 +83,9 @@
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Template Rendering</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -88,6 +100,19 @@
<li><a href="error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+42 -17
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -72,8 +83,9 @@
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Template Rendering</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -88,6 +100,19 @@
<li><a href="error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+42 -17
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -72,8 +83,9 @@
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Template Rendering</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -88,6 +100,19 @@
<li><a href="error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+42 -17
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -72,8 +83,9 @@
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Template Rendering</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -88,6 +100,19 @@
<li><a href="error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+42 -17
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -72,8 +83,9 @@
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Template Rendering</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -88,6 +100,19 @@
<li><a href="error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+14
View File
@@ -85,6 +85,7 @@
<li><a href="tutorials/database-app.html">Database App</a></li>
<li><a href="tutorials/template-rendering.html">Templates</a></li>
<li><a href="tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -99,6 +100,19 @@
<li><a href="howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="contributing/index.html">Overview</a></li>
<li><a href="contributing/module-overview.html">Module Architecture</a></li>
<li><a href="contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="contributing/testing.html">Writing Tests</a></li>
<li><a href="contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+41 -16
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -74,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -88,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+41 -16
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -74,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -88,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+41 -16
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -74,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -88,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+41 -16
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -74,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -88,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+41 -16
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -74,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -88,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+41 -16
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -74,6 +85,7 @@
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
<li><a href="../tutorials/web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
@@ -88,6 +100,19 @@
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
+49 -18
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -72,14 +83,34 @@
<li><a href="http-client.html">HTTP Client</a></li>
<li><a href="websocket-chat.html">WebSocket Chat</a></li>
<li><a href="database-app.html">Database App</a></li>
<li><a href="template-rendering.html">Template Rendering</a></li>
<li><a href="template-rendering.html">Templates</a></li>
<li><a href="cli-tool.html" class="active">CLI Tool</a></li>
<li><a href="web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
@@ -733,7 +764,7 @@ SHA256: a3f2e8b9c4d5...</code></pre>
<footer class="page-footer">
<a href="template-rendering.html" class="prev">Template Rendering</a>
<a href="../howto/index.html" class="next">How-To Guides</a>
<a href="web-server.html" class="next">Web Server</a>
</footer>
</main>
</div>
+48 -17
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -72,14 +83,34 @@
<li><a href="http-client.html">HTTP Client</a></li>
<li><a href="websocket-chat.html">WebSocket Chat</a></li>
<li><a href="database-app.html" class="active">Database App</a></li>
<li><a href="template-rendering.html">Template Rendering</a></li>
<li><a href="template-rendering.html">Templates</a></li>
<li><a href="cli-tool.html">CLI Tool</a></li>
<li><a href="web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
+134 -17
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -72,14 +83,34 @@
<li><a href="http-client.html" class="active">HTTP Client</a></li>
<li><a href="websocket-chat.html">WebSocket Chat</a></li>
<li><a href="database-app.html">Database App</a></li>
<li><a href="template-rendering.html">Template Rendering</a></li>
<li><a href="template-rendering.html">Templates</a></li>
<li><a href="cli-tool.html">CLI Tool</a></li>
<li><a href="web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
@@ -398,6 +429,92 @@ if (result["error"]) {
System.print("Success: %(result["data"]["title"])")
}</code></pre>
<h2>Step 9: Concurrent HTTP Requests</h2>
<p>When you need to fetch data from multiple endpoints, sequential requests can be slow. Use <code>async</code> and <code>await</code> to run requests concurrently:</p>
<pre><code>import "http" for Http
import "scheduler" for Scheduler, Future
import "json" for Json
var fetchJson = async { |url|
var response = Http.get(url)
return response.json
}
// SEQUENTIAL: Each request waits for the previous one
System.print("--- Sequential requests ---")
var user = await fetchJson("https://jsonplaceholder.typicode.com/users/1")
var posts = await fetchJson("https://jsonplaceholder.typicode.com/posts?userId=1")
var todos = await fetchJson("https://jsonplaceholder.typicode.com/todos?userId=1")
System.print("User: %(user["name"])")
System.print("Posts: %(posts.count)")
System.print("Todos: %(todos.count)")</code></pre>
<p>For concurrent execution, use <code>.call()</code> to start requests without waiting, then <code>await</code> the results:</p>
<pre><code>import "http" for Http
import "scheduler" for Scheduler, Future
import "json" for Json
var fetchJson = async { |url|
var response = Http.get(url)
return response.json
}
// CONCURRENT: All requests start at once
System.print("--- Concurrent requests ---")
var f1 = fetchJson.call("https://jsonplaceholder.typicode.com/users/1")
var f2 = fetchJson.call("https://jsonplaceholder.typicode.com/posts?userId=1")
var f3 = fetchJson.call("https://jsonplaceholder.typicode.com/todos?userId=1")
// Wait for results (requests run in parallel)
var user = await f1
var posts = await f2
var todos = await f3
System.print("User: %(user["name"])")
System.print("Posts: %(posts.count)")
System.print("Todos: %(todos.count)")</code></pre>
<h3>Batch Fetching with Concurrent Requests</h3>
<p>For fetching multiple URLs dynamically, create futures in a loop:</p>
<pre><code>import "http" for Http
import "scheduler" for Scheduler, Future
import "json" for Json
var fetchJson = async { |url|
var response = Http.get(url)
return response.json
}
var userIds = [1, 2, 3, 4, 5]
// Start all requests concurrently
var futures = []
for (id in userIds) {
futures.add(fetchJson.call("https://jsonplaceholder.typicode.com/users/%(id)"))
}
// Collect results
var users = []
for (f in futures) {
users.add(await f)
}
System.print("Fetched %(users.count) users:")
for (user in users) {
System.print(" - %(user["name"]) (%(user["email"]))")
}</code></pre>
<div class="admonition tip">
<div class="admonition-title">Direct Calling vs .call()</div>
<p>Use <code>await fn(args)</code> for sequential execution (waits immediately). Use <code>fn.call(args)</code> to start without waiting, enabling concurrent execution.</p>
</div>
<h2>Complete Example</h2>
<p>Here is a complete, production-ready API client:</p>
+58 -17
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -72,14 +83,34 @@
<li><a href="http-client.html">HTTP Client</a></li>
<li><a href="websocket-chat.html">WebSocket Chat</a></li>
<li><a href="database-app.html">Database App</a></li>
<li><a href="template-rendering.html">Template Rendering</a></li>
<li><a href="template-rendering.html">Templates</a></li>
<li><a href="cli-tool.html">CLI Tool</a></li>
<li><a href="web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
@@ -141,6 +172,15 @@
<span class="tag">subprocess</span>
</div>
</div>
<div class="card">
<h3><a href="web-server.html">Building a Web Server</a></h3>
<p>Build HTTP servers with routing, sessions, middleware, and REST APIs using the web module.</p>
<div class="card-meta">
<span class="tag">web</span>
<span class="tag">http</span>
</div>
</div>
</div>
<h2>Learning Path</h2>
@@ -153,6 +193,7 @@
<li><strong>Template Rendering</strong> - Learn the Jinja template system</li>
<li><strong>WebSocket Chat</strong> - Advanced async patterns with fibers</li>
<li><strong>CLI Tool</strong> - Bringing it all together in a real application</li>
<li><strong>Web Server</strong> - Build complete web applications with the web module</li>
</ol>
<h2>Prerequisites</h2>
+48 -17
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -72,14 +83,34 @@
<li><a href="http-client.html">HTTP Client</a></li>
<li><a href="websocket-chat.html">WebSocket Chat</a></li>
<li><a href="database-app.html">Database App</a></li>
<li><a href="template-rendering.html" class="active">Template Rendering</a></li>
<li><a href="template-rendering.html" class="active">Templates</a></li>
<li><a href="cli-tool.html">CLI Tool</a></li>
<li><a href="web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>
File diff suppressed because it is too large Load Diff
+48 -17
View File
@@ -42,27 +42,38 @@
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/argparse.html">argparse</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/dataset.html">dataset</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/fswatch.html">fswatch</a></li>
<li><a href="../api/html.html">html</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/markdown.html">markdown</a></li>
<li><a href="../api/math.html">math</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sysinfo.html">sysinfo</a></li>
<li><a href="../api/tempfile.html">tempfile</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/udp.html">udp</a></li>
<li><a href="../api/uuid.html">uuid</a></li>
<li><a href="../api/wdantic.html">wdantic</a></li>
<li><a href="../api/web.html">web</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
</ul>
</div>
<div class="section">
@@ -72,14 +83,34 @@
<li><a href="http-client.html">HTTP Client</a></li>
<li><a href="websocket-chat.html" class="active">WebSocket Chat</a></li>
<li><a href="database-app.html">Database App</a></li>
<li><a href="template-rendering.html">Template Rendering</a></li>
<li><a href="template-rendering.html">Templates</a></li>
<li><a href="cli-tool.html">CLI Tool</a></li>
<li><a href="web-server.html">Web Server</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Contributing</span>
<ul>
<li><a href="../contributing/index.html">Overview</a></li>
<li><a href="../contributing/module-overview.html">Module Architecture</a></li>
<li><a href="../contributing/pure-wren-module.html">Pure-Wren Modules</a></li>
<li><a href="../contributing/c-backed-module.html">C-Backed Modules</a></li>
<li><a href="../contributing/foreign-classes.html">Foreign Classes</a></li>
<li><a href="../contributing/async-patterns.html">Async Patterns</a></li>
<li><a href="../contributing/testing.html">Writing Tests</a></li>
<li><a href="../contributing/documentation.html">Documentation</a></li>
</ul>
</div>
</nav>