<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fernando Correa de Oliveira</title>
    <description>The latest articles on DEV Community by Fernando Correa de Oliveira (@fco).</description>
    <link>https://dev.to/fco</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F72963%2F505ad491-5e4a-4912-8ee8-11a7901ad50f.jpeg</url>
      <title>DEV Community: Fernando Correa de Oliveira</title>
      <link>https://dev.to/fco</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fco"/>
    <language>en</language>
    <item>
      <title>Crolite: filling the "Mojolicious::Lite" gap in the Cro ecosystem</title>
      <dc:creator>Fernando Correa de Oliveira</dc:creator>
      <pubDate>Sun, 28 Sep 2025 00:28:31 +0000</pubDate>
      <link>https://dev.to/fco/crolite-filling-the-mojoliciouslite-gap-in-the-cro-ecosystem-4ebi</link>
      <guid>https://dev.to/fco/crolite-filling-the-mojoliciouslite-gap-in-the-cro-ecosystem-4ebi</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When I start something new in Raku with Cro, I almost always begin with a mental sketch: two or three routes, a response shape, maybe a typed segment. In the Perl world I leaned heavily on &lt;code&gt;Mojolicious::Lite&lt;/code&gt; for that prototyping phase. In Cro—powerful and modular as it is—I missed an immediate &lt;strong&gt;“lite mode”&lt;/strong&gt;: no manual wiring of server, pipeline, and router just to test a thought. Out of that friction came &lt;strong&gt;Crolite&lt;/strong&gt;: a thin layer that re‑exports Cro's routing keywords and adds a multi &lt;code&gt;MAIN&lt;/code&gt; with quick exploration commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Crolite Is
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Goal:&lt;/strong&gt; Make minimal HTTP prototypes trivial while ideas mature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approach:&lt;/strong&gt; Reuse Cro's router directly; no new DSL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deliverable:&lt;/strong&gt; A collected &lt;code&gt;RouteSet&lt;/code&gt; + a tiny embedded CLI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Philosophy:&lt;/strong&gt; “Start now; graduate later to a full Cro app.”&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;From a local checkout (inside the project directory):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zef &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once published:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zef &lt;span class="nb"&gt;install &lt;/span&gt;Crolite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  First Example
&lt;/h2&gt;

&lt;p&gt;File &lt;code&gt;example.raku&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Crolite;

get -&amp;gt; $any {
    content 'text/plain', "Hello: $any";
}

delete -&amp;gt; 'thing', Int $id {
    content 'application/json', %( :$id );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List derived routes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;raku example.raku routes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run a development server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;raku example.raku &lt;span class="nt"&gt;--host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;127.0.0.1 &lt;span class="nt"&gt;--port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3000 daemon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test a route without a persistent daemon (ephemeral in‑memory request):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;raku example.raku GET /thing/42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Suggested Workflow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Sketch routes and response formats.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;raku app.raku routes&lt;/code&gt; to confirm patterns.&lt;/li&gt;
&lt;li&gt;Fire single requests: &lt;code&gt;raku app.raku GET /foo/123&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Iterate until it stabilizes.&lt;/li&gt;
&lt;li&gt;Promote to a full Cro project if you need richer middleware, structured logging, TLS, etc.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  CLI Options
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;routes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print summary of registered endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[--host=0.0.0.0] [--port=10000] daemon&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start simple Cro server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET &amp;lt;path&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single in‑memory GET&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST &amp;lt;path&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single POST (no custom body)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PUT &amp;lt;path&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single PUT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DELETE &amp;lt;path&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single DELETE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--method=&amp;lt;VERB&amp;gt; http &amp;lt;path&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generic form for any method&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Stop the daemon with &lt;code&gt;Ctrl+C&lt;/code&gt; (SIGINT is trapped for graceful shutdown).&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamic Segments &amp;amp; Typing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Crolite;

get -&amp;gt; 'greet', Str $name {
    content 'text/plain', "Hi $name!";
}

post -&amp;gt; 'sum', Int $a, Int $b {
    content 'application/json', %( total =&amp;gt; $a + $b );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;raku app.raku GET /greet/Ana
raku app.raku POST /sum/2/5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Returning JSON
&lt;/h2&gt;

&lt;p&gt;Just produce a &lt;code&gt;Hash&lt;/code&gt; or &lt;code&gt;Map&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;get -&amp;gt; 'status' {
    content 'application/json', %( service =&amp;gt; 'ok', ts =&amp;gt; DateTime.now );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Hooks (Before / After)
&lt;/h2&gt;

&lt;p&gt;If you manually add &lt;code&gt;before&lt;/code&gt; or &lt;code&gt;after&lt;/code&gt; handlers to the underlying &lt;code&gt;RouteSet&lt;/code&gt;, Crolite includes them when composing the application for &lt;code&gt;daemon&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Crolite;

$*CRO-ROUTE-SET.before: {
    # Simple logging / auth stub
    proceed;
}

get -&amp;gt; 'ping' { content 'text/plain', 'pong' }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  More Complete Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Crolite;

get -&amp;gt; 'health' {
    content 'application/json', %( ok =&amp;gt; True );
}

put -&amp;gt; 'echo', Str $msg {
    content 'text/plain', $msg.uc;
}

post -&amp;gt; 'calc', Int $x, Int $y {
    content 'application/json', %( sum =&amp;gt; $x + $y, prod =&amp;gt; $x * $y );
}

delete -&amp;gt; 'soft', Int $id {
    content 'application/json', %( deleted =&amp;gt; $id, soft =&amp;gt; True );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quick ping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;raku app.raku GET /health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;raku app.raku &lt;span class="nt"&gt;--port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4000 daemon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ergonomics While Prototyping
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rapid changes:&lt;/strong&gt; Save &amp;amp; re‑run; no auto‑reloader (yet).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspection:&lt;/strong&gt; &lt;code&gt;routes&lt;/code&gt; surfaces path typos immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic requests:&lt;/strong&gt; Avoids opening another terminal for &lt;code&gt;curl&lt;/code&gt; just to see a body.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Current Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No structured logging out of the box.&lt;/li&gt;
&lt;li&gt;No built‑in TLS / websockets / streaming presets.&lt;/li&gt;
&lt;li&gt;No hot reload.&lt;/li&gt;
&lt;li&gt;Experimental API (may shift).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to Migrate to a Full Cro App
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You need chained middleware (auth, tracing, rate limiting).&lt;/li&gt;
&lt;li&gt;You require richer body parsing / serialization customization.&lt;/li&gt;
&lt;li&gt;You integrate multiple services or supervised components.&lt;/li&gt;
&lt;li&gt;You need observability (metrics, distributed tracing, advanced logs).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Possible Future Roadmap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Optional reload in &lt;code&gt;daemon&lt;/code&gt; mode.&lt;/li&gt;
&lt;li&gt;Alternate tabular output for &lt;code&gt;routes&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Lightweight latency / metrics helper.&lt;/li&gt;
&lt;li&gt;Test skeleton generator for promotion phase.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick Testing Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use the embedded CLI to validate the contract before formal tests.&lt;/li&gt;
&lt;li&gt;When formalizing, reuse &lt;code&gt;Cro::HTTP::Test&lt;/code&gt; (mirrors what the CLI verbs do).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Crolite does not compete with the full flexibility of a structured Cro project; it lowers the &lt;strong&gt;time to first useful response&lt;/strong&gt; when exploring HTTP ideas in Raku. If you also miss the lightness of &lt;code&gt;Mojolicious::Lite&lt;/code&gt;, try making the first step of each spike just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Crolite;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, once the shape hardens, graduate to something more robust.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Suggestions, issues, and PRs welcome.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rakulang</category>
      <category>cro</category>
      <category>mojolicious</category>
      <category>http</category>
    </item>
    <item>
      <title>Typed, Named Endpoints for Cro (with HTMX Helpers)</title>
      <dc:creator>Fernando Correa de Oliveira</dc:creator>
      <pubDate>Sun, 21 Sep 2025 22:14:23 +0000</pubDate>
      <link>https://dev.to/fco/typed-named-endpoints-for-cro-with-htmx-helpers-g9j</link>
      <guid>https://dev.to/fco/typed-named-endpoints-for-cro-with-htmx-helpers-g9j</guid>
      <description>&lt;p&gt;Cro’s HTTP router is great at declaring routes, but it doesn’t provide a first‑class way to reference those routes elsewhere in your app. Cro::HTTP::RouterUtils fills that gap: it lets you reference endpoints by name, build typed-safe paths, generate HTMX attributes, redirect to routes, and even call the underlying implementation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stable references to routes by name (or auto‑named fallback)&lt;/li&gt;
&lt;li&gt;Typed &lt;code&gt;path()&lt;/code&gt; builder validates parameter types&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hx-attrs()&lt;/code&gt; renders HTMX attributes with the correct method and URL&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;redirect-to()&lt;/code&gt; returns a Cro redirect to the endpoint&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;call()&lt;/code&gt; invokes the route implementation directly (handy for tests)&lt;/li&gt;
&lt;li&gt;Supports &lt;code&gt;include&lt;/code&gt; with prefixes seamlessly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/FCO/Cro-HTTP-RouterUtils" rel="noopener noreferrer"&gt;https://github.com/FCO/Cro-HTTP-RouterUtils&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zef &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--depsonly&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Cro::HTTP::RouterUtils;

my $app = route {
    # Name a route via a named sub
    get my sub greet-path('greet', $name) {
        content 'text/plain', "Hello, $name!"
    }

    # Use the endpoint by name
    get -&amp;gt; 'links' {
        my $ep = endpoints('greet-path');
        content 'text/html', qq:to/END/
            &amp;lt;a href="{ $ep.path(:name&amp;lt;alice&amp;gt;) }"&amp;gt;alice&amp;lt;/a&amp;gt;
            &amp;lt;a href="#" { $ep.hx-attrs(:name&amp;lt;bob&amp;gt;, :trigger&amp;lt;click&amp;gt;) }&amp;gt;bob&amp;lt;/a&amp;gt;
        END
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Naming and Discovering Endpoints
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Named endpoints: give your route a function name and reference it with &lt;code&gt;endpoints('your-name')&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Auto‑named endpoints: when no name is provided, keys are generated from method and path signature, e.g. &lt;code&gt;get_greet&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Auto-named
get -&amp;gt; 'greet', Str :$name { 200 }
endpoints('get_greet').path;  # =&amp;gt; "/greet"

# Named
get my sub greet-path('greet', $name) { "Hello, $name!" }
endpoints('greet-path').path(:name&amp;lt;alice&amp;gt;);  # =&amp;gt; "/greet/alice"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Includes with prefixes are supported transparently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;include external =&amp;gt; other-routes;   # /external prefix applied

endpoints('external-ep1').method;   # "GET"
endpoints('external-ep1').path;     # "/external/returns-ok"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Typed Path Building
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;path(*%values)&lt;/code&gt; enforces your route’s typed parameters; missing or invalid values throw.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;get my sub sum('sum', Int $a, Int $b) { $a + $b }

my $ep = endpoints('sum');
$ep.path(:a(1), :b(2));          # "/sum/1/2"
$ep.path(:a("x"), :b(2));        # throws (type mismatch)
$ep.path(:a(1));                 # throws (missing parameter)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  HTMX Helpers
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;hx-attrs(:args…)&lt;/code&gt; returns a space-separated string of HTMX attributes. It uses the endpoint’s HTTP method by default (e.g., &lt;code&gt;hx-get&lt;/code&gt;) and the built URL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;a href="#"
   { endpoints('greet-path').hx-attrs(
       :name&amp;lt;alice&amp;gt;,
       :trigger&amp;lt;click&amp;gt;,
       :target&amp;lt;#out&amp;gt;,
       :swap&amp;lt;'outerHTML settle:200ms'&amp;gt;,
       :push-url&amp;lt;true&amp;gt;,
       :on{ click =&amp;gt; "console.log(\"clicked\")" }
     )
   }&amp;gt;
  Load Alice
&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Highlights supported:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request URL/method: &lt;code&gt;method&lt;/code&gt; override; parameters via &lt;code&gt;:name&amp;lt;...&amp;gt;&lt;/code&gt; etc.&lt;/li&gt;
&lt;li&gt;Core: &lt;code&gt;trigger&lt;/code&gt;, &lt;code&gt;target&lt;/code&gt;, &lt;code&gt;confirm&lt;/code&gt;, &lt;code&gt;indicator&lt;/code&gt;, &lt;code&gt;swap&lt;/code&gt;, &lt;code&gt;oob&lt;/code&gt; (as &lt;code&gt;hx-swap-oob&lt;/code&gt;), &lt;code&gt;boost&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Navigation: &lt;code&gt;push-url&lt;/code&gt; (Bool|Str), &lt;code&gt;replace-url&lt;/code&gt; (Bool|Str)&lt;/li&gt;
&lt;li&gt;Selection: &lt;code&gt;select&lt;/code&gt;, &lt;code&gt;select-oob&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;JSON: &lt;code&gt;vals&lt;/code&gt;, &lt;code&gt;headers&lt;/code&gt;, &lt;code&gt;request&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Flags: &lt;code&gt;disable&lt;/code&gt;, &lt;code&gt;validate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Misc: &lt;code&gt;disabled-elt&lt;/code&gt;, &lt;code&gt;disinherit&lt;/code&gt;, &lt;code&gt;encoding&lt;/code&gt;, &lt;code&gt;ext&lt;/code&gt;, &lt;code&gt;history&lt;/code&gt;, &lt;code&gt;history-elt&lt;/code&gt;, &lt;code&gt;include&lt;/code&gt;, &lt;code&gt;inherit&lt;/code&gt;, &lt;code&gt;params&lt;/code&gt;, &lt;code&gt;prompt&lt;/code&gt;, &lt;code&gt;sync&lt;/code&gt;, &lt;code&gt;vars&lt;/code&gt; (deprecated)&lt;/li&gt;
&lt;li&gt;Events: &lt;code&gt;:on{ event =&amp;gt; "code" }&lt;/code&gt; emits &lt;code&gt;hx-on:event='code'&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example minimal output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hx-get='/greet/alice' hx-trigger='click' hx-target='#out'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Redirects
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;get -&amp;gt; 'redir' {
    endpoints('greet-path').redirect-to: :name&amp;lt;ok&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Calling the Implementation
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;call(|args)&lt;/code&gt; invokes the underlying route implementation. Literal path segments are auto-injected; you pass only the non-literal parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;get my sub ret('ret') { 42 }
get my sub sum('sum', Int $a, Int $b) { $a + $b }

endpoints('ret').call;        # 42
endpoints('sum').call(2, 3);  # 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great for unit tests of pure route logic. If you depend on Cro’s pipeline, prefer &lt;code&gt;Cro::HTTP::Test&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full Example
&lt;/h2&gt;

&lt;p&gt;See &lt;code&gt;examples/example.raku&lt;/code&gt; and &lt;code&gt;examples/ExampleRoute.rakumod&lt;/code&gt; in the repo. Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;raku examples/example.raku
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then visit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/form&lt;/code&gt; for a classic form&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/links&lt;/code&gt; for &lt;code&gt;&amp;lt;a href&amp;gt;&lt;/code&gt; links built from endpoints&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/links-htmx&lt;/code&gt; for HTMX-driven links&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Errors and Guarantees
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Unknown endpoint name: throws.&lt;/li&gt;
&lt;li&gt;Missing/invalid path params: throws with a clear message.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;call()&lt;/code&gt; auto-injects literal path segments; you provide the rest.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Isn’t in Cro
&lt;/h2&gt;

&lt;p&gt;Cro focuses on routing and request handling. This utility adds “endpoint as a value” ergonomics—stable references, typed path building, HTMX helpers, and redirect/call helpers—while staying a thin layer on top of &lt;code&gt;Cro::HTTP::Router&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix: Include With Prefix Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# examples/ExampleRoute.rakumod
use Cro::HTTP::RouterUtils;

sub other-routes is export {
  route {
    get  my sub external-ep1("returns-ok")  { content "text/plain", "OK" }
    post my sub external-ep2("using-post")  { content "text/plain", "OK" }
  }
}

# elsewhere
include external =&amp;gt; other-routes;
endpoints('external-ep1').path;  # "/external/returns-ok"
endpoints('external-ep2').path;  # "/external/using-post"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;—&lt;br&gt;
Made with Cro::HTTP::RouterUtils (Raku).&lt;/p&gt;

</description>
      <category>rakulang</category>
      <category>cro</category>
      <category>htmx</category>
    </item>
    <item>
      <title>From ASTs to RakuAST to ASTQuery</title>
      <dc:creator>Fernando Correa de Oliveira</dc:creator>
      <pubDate>Sun, 14 Sep 2025 00:39:59 +0000</pubDate>
      <link>https://dev.to/fco/from-asts-to-rakuast-to-astquery-c3f</link>
      <guid>https://dev.to/fco/from-asts-to-rakuast-to-astquery-c3f</guid>
      <description>&lt;p&gt;Precise code search and transformation for Raku&lt;/p&gt;

&lt;p&gt;Raku’s RakuAST opens up a powerful way to analyze and transform code by working directly with its Abstract Syntax Tree (AST). ASTQuery builds on that by offering a compact, expressive query language to find the nodes you care about,&lt;br&gt;
capture them, and even drive compile-time rewrites.&lt;/p&gt;

&lt;p&gt;This guide explains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What ASTs are and why they matter&lt;/li&gt;
&lt;li&gt;What RakuAST provides&lt;/li&gt;
&lt;li&gt;How to search ASTs and build macro-like passes&lt;/li&gt;
&lt;li&gt;How ASTQuery’s selector language works&lt;/li&gt;
&lt;li&gt;Practical examples: queries, captures, attribute filters, and rewrites&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  ASTs, Briefly
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What: An AST is a structured, typed tree that represents your code after parsing (e.g., “call”, “operator
application”, “variable”).&lt;/li&gt;
&lt;li&gt;Why: Compilers, linters, and refactoring tools operate on ASTs because they capture code semantics, not just text.
This enables robust search and safe transformations.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What Is RakuAST?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Raku’s AST: RakuAST is the new, structured representation of Raku code. It exposes node types like &lt;code&gt;RakuAST::Call&lt;/code&gt;,
&lt;code&gt;RakuAST::ApplyInfix&lt;/code&gt;, &lt;code&gt;RakuAST::Var&lt;/code&gt;, and more.&lt;/li&gt;
&lt;li&gt;Access: &lt;code&gt;my $ast = $code.AST;&lt;/code&gt; for strings, or &lt;code&gt;$*CU&lt;/code&gt; for the current compilation unit in a &lt;code&gt;CHECK&lt;/code&gt; phaser.&lt;/li&gt;
&lt;li&gt;Status: RakuAST is still experimental. Some node fields may not be &lt;code&gt;rw&lt;/code&gt; on your Rakudo; rebuild/replace enclosing
nodes when needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Why Search ASTs?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Beyond grep: Find “function calls with an Int somewhere under args”, not just text matches.&lt;/li&gt;
&lt;li&gt;Safer refactors: Target particular node shapes and attributes to avoid false positives.&lt;/li&gt;
&lt;li&gt;Automated upgrades: Write codemods that transform legacy patterns into new APIs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Macro-Like Passes (Compiler-Time Rewrites)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use a &lt;code&gt;CHECK&lt;/code&gt; phaser with &lt;code&gt;use experimental :rakuast;&lt;/code&gt; to inspect/modify &lt;code&gt;$*CU&lt;/code&gt; before runtime.&lt;/li&gt;
&lt;li&gt;Typical flow:
1) Fetch &lt;code&gt;$*CU&lt;/code&gt;
2) Query nodes with ASTQuery
3) Mutate nodes (or rebuild if fields aren’t &lt;code&gt;rw&lt;/code&gt;)
(How mutable RakuAST needs to be is still being discussed)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example: Add '!!!' at the end of every &lt;code&gt;say&lt;/code&gt; call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use experimental :rakuast;
use ASTQuery;

CHECK {
    my $ast = $*CU;
    for $ast.&amp;amp;ast-query(Q|.call#say|).list {
        .args.push: RakuAST::StrLiteral.new: "!!!";
    }
}
say "some text"; # prints "some text!!!"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ASTQuery, at a Glance
&lt;/h2&gt;

&lt;p&gt;• Query language: Describe node kinds, relationships (child/descendant/ancestor), and attributes succinctly.&lt;br&gt;
• Captures: Name nodes you want to retrieve with $name.&lt;br&gt;
• Functions: Reusable predicates referenced with &amp;amp;name.&lt;br&gt;
• Programmatic API: ast-query and ast-matcher.&lt;br&gt;
• CLI: Query files and print results in a readable form.&lt;/p&gt;
&lt;h2&gt;
  
  
  Quickstart
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use ASTQuery;

my $code = q:to/CODE/;
    sub f($x) { }
    f 42;
    say 1 * 3;
CODE

my $ast = $code.AST;

# Find Apply operator nodes where left=1 and right=3
my $ops = $ast.&amp;amp;ast-query('.apply-operator[left=1, right=3]');
say $ops.list;

# Find calls that have an Int somewhere under args
my $calls = $ast.&amp;amp;ast-query('&amp;amp;is-call[args=&amp;gt;&amp;gt;&amp;gt;.int]');
say $calls.list;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Selector Language
&lt;/h2&gt;

&lt;p&gt;Node description format:&lt;/p&gt;

&lt;p&gt;RakuAST::Class::Name.group#id[attr1, attr2=attrvalue]$name&amp;amp;function&lt;/p&gt;

&lt;p&gt;Components:&lt;/p&gt;

&lt;p&gt;• RakuAST::Class::Name: Optional full class name.&lt;br&gt;
• .group: Optional node group (alias to multiple classes).&lt;br&gt;
• #id: Optional id value compared against the node’s id field (per-type mapping).&lt;br&gt;
• [attributes]: Optional attribute matchers (see below).&lt;br&gt;
• $name: Optional capture name (one per node part).&lt;br&gt;
• &amp;amp;function: Optional function matcher (compose with AND when multiple).&lt;/p&gt;

&lt;p&gt;Relationship operators:&lt;/p&gt;

&lt;p&gt;• &lt;code&gt;&amp;gt;&lt;/code&gt;: Left has right as a child.&lt;br&gt;
• &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;: Left has right as a descendant, skipping only ignorable nodes.&lt;br&gt;
• &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt;: Left has right as a descendant (any nodes allowed between).&lt;br&gt;
• &lt;code&gt;&amp;lt;&lt;/code&gt;: Right is the parent of left.&lt;br&gt;
• &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;: Right is an ancestor of left, skipping only ignorable nodes.&lt;br&gt;
• &lt;code&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/code&gt;: Right is an ancestor of left (any nodes allowed between).&lt;br&gt;
• Note: The space operator is no longer used.&lt;/p&gt;

&lt;p&gt;Ignorable nodes (skipped by &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;/&lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;• RakuAST::Block, RakuAST::Blockoid, RakuAST::StatementList,&lt;br&gt;
RakuAST::Statement::Expression, RakuAST::ArgList&lt;/p&gt;

&lt;p&gt;Attribute relation operators (start traversal from attribute value when it is a RakuAST node):&lt;/p&gt;

&lt;p&gt;• &lt;code&gt;[attr=&amp;gt;MATCH]&lt;/code&gt;    child&lt;br&gt;
• &lt;code&gt;[attr=&amp;gt;&amp;gt;MATCH]&lt;/code&gt;   descendant via ignorable nodes&lt;br&gt;
• &lt;code&gt;[attr=&amp;gt;&amp;gt;&amp;gt;MATCH]&lt;/code&gt;  descendant (any nodes)&lt;/p&gt;

&lt;p&gt;Attribute value operators (compare against a literal, identifier, or regex literal):&lt;/p&gt;

&lt;p&gt;• &lt;code&gt;[attr~=value]&lt;/code&gt;   contains (substring) or regex match&lt;br&gt;
• &lt;code&gt;[attr^=value]&lt;/code&gt;   starts-with&lt;br&gt;
• &lt;code&gt;[attr$=value]&lt;/code&gt;   ends-with&lt;br&gt;
• &lt;code&gt;[attr*=/regex/]&lt;/code&gt; regex literal&lt;/p&gt;

&lt;p&gt;Notes:&lt;br&gt;
• When an attribute holds a RakuAST node, the matcher walks nested nodes via configured id fields to reach a comparable&lt;br&gt;
leaf (e.g., .call[name] → Name’s identifier).&lt;br&gt;
• Non-existent attributes never match.&lt;/p&gt;

&lt;p&gt;Captures:&lt;/p&gt;

&lt;p&gt;• Append $name to capture the current node part, e.g., &lt;code&gt;.call#say$call&lt;/code&gt; then access with &lt;code&gt;$match&amp;lt;call&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Functions:&lt;/p&gt;

&lt;p&gt;• Use &lt;code&gt;&amp;amp;name&lt;/code&gt; to apply reusable predicates; multiple functions compose with AND.&lt;/p&gt;
&lt;h2&gt;
  
  
  Built-in Groups and Functions
&lt;/h2&gt;

&lt;p&gt;Common groups:&lt;/p&gt;

&lt;p&gt;• &lt;code&gt;.call&lt;/code&gt; → RakuAST::Call&lt;br&gt;
• &lt;code&gt;.apply-operator&lt;/code&gt; → RakuAST::ApplyInfix|ApplyListInfix|ApplyPostfix|Ternary&lt;br&gt;
• &lt;code&gt;.operator&lt;/code&gt; → RakuAST::Infixish|Prefixish|Postfixish&lt;br&gt;
• &lt;code&gt;.conditional&lt;/code&gt; → RakuAST::Statement::IfWith|Unless|Without&lt;br&gt;
• &lt;code&gt;.variable&lt;/code&gt;, &lt;code&gt;.variable-usage&lt;/code&gt;, &lt;code&gt;.variable-declaration&lt;/code&gt;&lt;br&gt;
• &lt;code&gt;.statement&lt;/code&gt;, &lt;code&gt;.expression&lt;/code&gt;, &lt;code&gt;.int&lt;/code&gt;, &lt;code&gt;.str&lt;/code&gt;, &lt;code&gt;.ignorable&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Built-in &amp;amp;functions:&lt;/p&gt;

&lt;p&gt;• &lt;code&gt;&amp;amp;is-call&lt;/code&gt;, &lt;code&gt;&amp;amp;is-operator&lt;/code&gt;, &lt;code&gt;&amp;amp;is-apply-operator&lt;/code&gt;&lt;br&gt;
• &lt;code&gt;&amp;amp;is-assignment&lt;/code&gt;, &lt;code&gt;&amp;amp;is-conditional&lt;/code&gt;&lt;br&gt;
• &lt;code&gt;&amp;amp;has-var&lt;/code&gt;, &lt;code&gt;&amp;amp;has-call&lt;/code&gt;, &lt;code&gt;&amp;amp;has-int&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://github.com/FCO/ASTQuery/blob/main/REFERENCE.md" rel="noopener noreferrer"&gt;REFERENCE.md&lt;/a&gt; for the full, authoritative list of groups, functions, and id fields.&lt;/p&gt;
&lt;h2&gt;
  
  
  ID Fields (#id) and How Matching Works
&lt;/h2&gt;

&lt;p&gt;• Each RakuAST type maps to an “id field” used for #id comparisons (e.g., &lt;code&gt;RakuAST::Call&lt;/code&gt; uses &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;RakuAST::Infix&lt;/code&gt; uses&lt;br&gt;
&lt;code&gt;operator&lt;/code&gt;, &lt;code&gt;literals&lt;/code&gt; use &lt;code&gt;value&lt;/code&gt;).&lt;br&gt;
• When comparing attributes whose value is a RakuAST node, ASTQuery walks down by id fields until reaching a leaf value&lt;br&gt;
to compare.&lt;br&gt;
• For variable declarations, bare ids strip sigils for comparison:&lt;br&gt;
 • &lt;code&gt;.variable-declaration#x&lt;/code&gt; matches &lt;code&gt;my $x&lt;/code&gt;, even though the declaration’s name includes the sigil internally (if needed, you can always use &lt;code&gt;[name="$x"]&lt;/code&gt;).&lt;/p&gt;
&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;

&lt;p&gt;Find specific infix applications (left=1, right=3):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my $code = q{
    for ^10 {
        if $_ %% 2 {
            say 1 * 3;
        }
    }
};
my $ast = $code.AST;

my $result = $ast.&amp;amp;ast-query: Q|.apply-operator[left=1, right=3]|;

# ast-query returns a ASTQuery::Match object
say $result.list;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you print the object itself, instead of getting the list of matched nodes, it will print something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddnscjn8ydryhymr1kzs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddnscjn8ydryhymr1kzs.png" alt=" " width="800" height="159"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use ancestor operator &lt;code&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/code&gt; with captures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my $result = $ast.&amp;amp;ast-query('RakuAST::Infix &amp;lt;&amp;lt;&amp;lt; .conditional$cond .int#2$int');
say $result.list;  # infix nodes
say $result.hash;  # captured 'cond' and 'int'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0idijyfdatbwk2om7o52.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0idijyfdatbwk2om7o52.png" alt=" " width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Parent operator &lt;code&gt;&amp;lt;&lt;/code&gt; and capturing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my $result = $ast.&amp;amp;ast-query('RakuAST::Infix &amp;lt; .apply-operator[right=2]$op');
say $result&amp;lt;op&amp;gt;;   # ApplyInfix nodes with right=2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Descendant operator &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; and capturing a variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my $result = $ast.&amp;amp;ast-query('.call &amp;gt;&amp;gt;&amp;gt; RakuAST::Var$var');
say $result.list;  # call nodes
say $result.hash;  # captured 'var'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attribute relation traversal (from attribute node):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Calls that have an Int somewhere under args:
my $calls = $ast.&amp;amp;ast-query('&amp;amp;is-call[args=&amp;gt;&amp;gt;&amp;gt;.int]');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attribute value operators:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Calls whose name contains "sa" (e.g., say)
my $q1 = $ast.&amp;amp;ast-query('.call[name~= "sa"]');

# Calls whose name starts with "s"
my $q2 = $ast.&amp;amp;ast-query('.call[name^= "s"]');

# Calls whose name ends with "y"
my $q3 = $ast.&amp;amp;ast-query('.call[name$= "y"]');

# Calls whose name matches /sa.*/
my $q4 = $ast.&amp;amp;ast-query('.call[name*=/sa.*/]');

Capturing and retrieving nodes:

my $m = $ast.&amp;amp;ast-query('.call#say$call');
my $call-node = $m&amp;lt;call&amp;gt;;
my @matched = $m.list;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reusable Function Matchers
&lt;/h2&gt;

&lt;p&gt;Register a function and use it via &amp;amp;name:&lt;/p&gt;

&lt;p&gt;• From a compiled matcher:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my $m = ast-matcher('.call#f');
new-function('&amp;amp;f-call', $m);
$ast.&amp;amp;ast-query('&amp;amp;f-call');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;• From a callable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new-function('&amp;amp;single-argument-call' =&amp;gt; -&amp;gt; $n {
    $n.^name.starts-with('RakuAST::Call')
    &amp;amp;&amp;amp; $n.args.defined
    &amp;amp;&amp;amp; $n.args.args.defined
    &amp;amp;&amp;amp; $n.args.args.elems == 1
});
$ast.&amp;amp;ast-query('&amp;amp;single-argument-call');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;• From a selector string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new-function('&amp;amp;var-decl' =&amp;gt; '.variable-declaration');
$ast.&amp;amp;ast-query('&amp;amp;var-decl');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Programmatic API
&lt;/h2&gt;

&lt;p&gt;• &lt;code&gt;ast-query($ast, Str $selector) / ast-query($ast, $matcher)&lt;/code&gt;: Run a query and get an ASTQuery::Match (acts like&lt;br&gt;
Positional + Associative).&lt;br&gt;
• &lt;code&gt;ast-matcher(Str $selector)&lt;/code&gt;: Compile a selector once and reuse it.&lt;br&gt;
• &lt;code&gt;new-function($name, $callable|$matcher|$selector)&lt;/code&gt;: Register &amp;amp;name.&lt;br&gt;
• &lt;code&gt;add-ast-group($name, @classes)&lt;/code&gt; / &lt;code&gt;add-to-ast-group($name, *@classes)&lt;/code&gt;: Define/extend group aliases.&lt;br&gt;
• &lt;code&gt;set-ast-id($class, $id-method)&lt;/code&gt;: Configure which attribute is used as the id for #id and nested value matching.&lt;/p&gt;

&lt;h2&gt;
  
  
  CLI Usage
&lt;/h2&gt;

&lt;p&gt;• Run against a directory or single file:&lt;br&gt;
 • &lt;code&gt;ast-query.raku 'SELECTOR' [path]&lt;/code&gt;&lt;br&gt;
• If path is omitted, it scans the current directory recursively.&lt;br&gt;
• Extensions scanned: raku, rakumod, rakutest, rakuconfig, p6, pl6, pm6.&lt;br&gt;
• Example:&lt;br&gt;
 • &lt;code&gt;ast-query.raku '.call#say &amp;gt;&amp;gt;&amp;gt; .int' lib/&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging Selectors
&lt;/h2&gt;

&lt;p&gt;• Set ASTQUERY_DEBUG=1 to print a colored tree of matcher decisions, including deparsed node snippets and pass/fail per&lt;br&gt;
validator step. This helps understand why a node matched—or didn’t.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes and Caveats
&lt;/h2&gt;

&lt;p&gt;• RakuAST is experimental. It's still being discussed how mutable it will be.&lt;br&gt;
• Regex flags in /.../ literals aren’t supported in attribute value operators yet.&lt;br&gt;
• The old “space operator” for relationships is deprecated; use the explicit operators (&lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;ASTQuery lets you describe meaningful shapes in RakuAST—calls, operators, variables, and more—compose those&lt;br&gt;
descriptions, capture the nodes you want, and apply them to everything from precise code search to automated&lt;br&gt;
compiler-time refactorings. It’s a compact tool for robust code understanding and transformation.&lt;/p&gt;

&lt;p&gt;• Repo: &lt;a href="https://github.com/FCO/ASTQuery" rel="noopener noreferrer"&gt;https://github.com/FCO/ASTQuery&lt;/a&gt;&lt;br&gt;
• See &lt;a href="https://github.com/FCO/ASTQuery/blob/main/REFERENCE.md" rel="noopener noreferrer"&gt;REFERENCE.md&lt;/a&gt; for the complete catalog of groups, built-in functions, and id fields.&lt;/p&gt;

</description>
      <category>ast</category>
      <category>rakulang</category>
      <category>astquery</category>
    </item>
    <item>
      <title>🎮 ECS in Raku: A Toy Framework for Entities, Components, and Systems</title>
      <dc:creator>Fernando Correa de Oliveira</dc:creator>
      <pubDate>Sun, 27 Jul 2025 19:36:03 +0000</pubDate>
      <link>https://dev.to/fco/ecs-in-raku-a-toy-framework-for-entities-components-and-systems-nmm</link>
      <guid>https://dev.to/fco/ecs-in-raku-a-toy-framework-for-entities-components-and-systems-nmm</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Note&lt;/strong&gt;: This is a personal experiment. I’m not experienced in game development or ECS, and I’ve only recently learned about these concepts. This framework is not production-ready, and the API is still in flux. But I’d love your feedback! 🙏&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🧠 What is ECS?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;ECS&lt;/strong&gt; stands for &lt;strong&gt;Entity-Component-System&lt;/strong&gt;, a popular architecture pattern in game development.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Entities&lt;/strong&gt; are just unique identifiers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Components&lt;/strong&gt; are data — like position, velocity, health, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Systems&lt;/strong&gt; are the logic that runs on entities with certain components.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of having objects with both data and behavior (like in OOP), ECS separates those concerns cleanly. It encourages &lt;strong&gt;data-driven&lt;/strong&gt; design and enables powerful querying and parallelism (though we’re far from that in this toy project).&lt;/p&gt;




&lt;h2&gt;
  
  
  🧱 What’s an Archetype?
&lt;/h2&gt;

&lt;p&gt;This ECS implementation uses the concept of &lt;strong&gt;archetypes&lt;/strong&gt;, which means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Entities are grouped by the exact combination of components (and optionally tags) they have.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This means the world knows: &lt;em&gt;"All entities with &lt;code&gt;position&lt;/code&gt; and &lt;code&gt;velocity&lt;/code&gt;, but not &lt;code&gt;health&lt;/code&gt; are in this group."&lt;/em&gt;&lt;br&gt;&lt;br&gt;
This makes querying more efficient and predictable — we only iterate over relevant entities per system.&lt;/p&gt;

&lt;p&gt;Archetypes are typically used in high-performance ECS engines (like Unity’s DOTS or Bevy in Rust), but here it also simplifies reasoning about how entities are grouped.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why?
&lt;/h2&gt;

&lt;p&gt;Recently, I came across the idea of &lt;strong&gt;Entity Component System&lt;/strong&gt; (ECS) architectures, and it instantly clicked with my love for declarative APIs and composable logic.&lt;/p&gt;

&lt;p&gt;So I decided to implement a minimal ECS framework in &lt;a href="https://raku.org" rel="noopener noreferrer"&gt;Raku&lt;/a&gt; — not for performance or production use, but just to explore the paradigm and learn from it. And who knows? Maybe others in the Raku community will enjoy hacking on it too.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Demo 🎬
&lt;/h2&gt;

&lt;p&gt;Here’s a simple bouncing animation built with this ECS framework and &lt;a href="https://raku.land/zef:raku-community-modules/Raylib::Bindings" rel="noopener noreferrer"&gt;Raylib::Bindings&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa05q50nbs2manl8n5mvj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa05q50nbs2manl8n5mvj.gif" alt="Bouncing Camelia" width="600" height="279"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Code 🧩
&lt;/h2&gt;

&lt;p&gt;Here’s the full example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Raylib::Bindings;
use ECS;

constant $screen-width  = 1024;
constant $screen-height = 450;
my $white               = init-white;
my $background          = init-skyblue;
init-window($screen-width, $screen-height, "Bouncing Camelias");

my $string         = "./camelia.png";
my $camelia        = load-image($string);
my $camelia-height = $camelia.height;
my $camelia-width  = $camelia.width;
my $camelia-pos    = Vector2.init: $camelia-width/2e0, $camelia-height/2e0;
my $texture        = load-texture-from-image($camelia);
unload-image($camelia);

set-target-fps(60);
END {
    unload-texture($texture);
    close-window;
}

# We define a few basic vector operators to help with math:
sub term:&amp;lt;vector2-zero&amp;gt; { Vector2.init: 0e0, 0e0 }

multi infix:&amp;lt;+&amp;gt;(Vector2 $a, Vector2 $b) { Vector2.init: $a.x + $b.x, $a.y + $b.y }
multi infix:&amp;lt;-&amp;gt;(Vector2 $a, Vector2 $b) { Vector2.init: $a.x - $b.x, $a.y - $b.y }
multi infix:&amp;lt;*&amp;gt;(Vector2 $a, Numeric $i) { Vector2.init: $a.x * $i, $a.y * $i }
multi infix:&amp;lt;/&amp;gt;(Vector2 $a, Numeric $i) { Vector2.init: $a.x / $i, $a.y / $i }

# Then comes the fun part: defining the ECS world.

my $world = world {
    component position =&amp;gt; Vector2;
    component velocity =&amp;gt; Vector2;

    entity "camelia";

    # Input system: spawn a new “camelia” on mouse click
    system "click", :when{is-mouse-button-pressed MOUSE_BUTTON_LEFT}, -&amp;gt; {
        world-self.new-camelia:
            :position(get-mouse-position - $camelia-pos),
            :velocity(vector2-zero),
        ;
    }

    system-group "input", &amp;lt;click&amp;gt;;

    # Movement, gravity and bounce logic
    system "move", -&amp;gt; :$position! is rw, :$velocity! {
        using-params -&amp;gt; Num $delta {
            $position += $velocity * $delta
        }
    }

    system "bounce", -&amp;gt; :$position! where *.y &amp;gt;= $screen-height - $camelia-height.Num, :$velocity! where *.y &amp;gt; 0 {
        $velocity.y *= -.8
    }

    system "gravity", -&amp;gt; :$velocity! {
        using-params -&amp;gt; Num $delta {
            $velocity.y += 100 * $delta;
        }
    }

    system-group "physics", &amp;lt;move gravity bounce&amp;gt;;

    # Draw each camelia
    system "draw", -&amp;gt; :$position! {
        draw-texture-v $texture, $position, $white;
    }
}

    system "draw", -&amp;gt; :$position! {

# And finally the game loop:
until window-should-close {
    $world.input;
    $world.physics: get-frame-time;
    begin-drawing;
    clear-background $background;
    $world.draw;
    draw-fps 10, 10;
    end-drawing;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Understanding the ECS Framework API
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;world&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;world&lt;/code&gt; function is the entry point to define your ECS universe. Inside it, you declare your components, entity types, systems, and system groups. It returns an object that you will use to create entities and run your systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;component&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;component&lt;/code&gt; function defines a component that entities can have. You can call it in two ways:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;component position =&amp;gt; Vector2;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or more concisely&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;component Color;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the second case, the name of the component will be automatically derived from the type by converting it to kebab-case (e.g., Color becomes "color"). This helps reduce repetition when the type name already describes the data well.&lt;/p&gt;

&lt;h3&gt;
  
  
  entity
&lt;/h3&gt;

&lt;p&gt;The entity function defines a named entity type. This name becomes a tag automatically added to all instances of that entity. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;entity "ball";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this, you can create new entities with $world.new-ball(...), and those entities will be tagged with "ball".&lt;/p&gt;

&lt;h3&gt;
  
  
  system
&lt;/h3&gt;

&lt;p&gt;The system keyword defines a system — a unit of logic that processes entities. By default, a system automatically performs a query based on its parameters: it runs once per entity that has all the required components and tags.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;system "gravity", -&amp;gt; :$velocity! { ... }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This system runs once per frame for each entity with the velocity component.&lt;/p&gt;

&lt;p&gt;However, if you pass a :condition or :when parameter, the system behaves differently: it no longer queries entities, and runs only once per frame (or tick), executing only when the condition is true. This is ideal for global events like input, timers, or other non-entity-specific logic.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;system "click", :when { is-mouse-button-pressed MOUSE_BUTTON_LEFT }, -&amp;gt; {
    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This system executes once per frame only if the left mouse button is pressed.&lt;/p&gt;

&lt;h3&gt;
  
  
  system-group
&lt;/h3&gt;

&lt;p&gt;The system-group function defines a reusable group of systems. This allows you to bundle related systems and execute them together. You can call the group like a method, optionally passing arguments that will be forwarded to any using-params blocks inside the systems.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;system-group "physics", &amp;lt;move gravity bounce&amp;gt;;
...
$world.physics: get-frame-time;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  using-params
&lt;/h3&gt;

&lt;p&gt;The using-params function allows a system to access parameters passed when the system or system group is invoked. This is useful for values like frame delta time or external input.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;system "gravity", -&amp;gt; :$velocity! {
    using-params -&amp;gt; Num $delta {
        $velocity.y += 100 * $delta;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the system needs a time delta value to apply acceleration due to gravity. It gets the value passed to the system group (e.g., physics: get-frame-time).&lt;/p&gt;

&lt;h3&gt;
  
  
  current-entity
&lt;/h3&gt;

&lt;p&gt;Inside a system or query, you can call current-entity to get the entity object currently being processed. This is useful for adding or removing tags or other manipulations.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if some-condition {
    current-entity.add-tag: "jumping";
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you fine-grained control over the entity’s state and behavior beyond component data.&lt;/p&gt;

&lt;p&gt;world-self&lt;/p&gt;

&lt;p&gt;Inside a system or query, world-self gives you access to the current world instance. You can use it to create or modify entities, trigger systems, or manage state globally.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;world-self.new-ball: :position(...), :velocity(...);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows a system to spawn new entities as part of its logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Framework 🛠️
&lt;/h2&gt;

&lt;p&gt;Here are some things you should know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It’s written entirely in Raku.&lt;/li&gt;
&lt;li&gt;The world is declared with world { ... }.&lt;/li&gt;
&lt;li&gt;Components are just names mapped to types.&lt;/li&gt;
&lt;li&gt;Systems are defined using system with a name and a sub signature.&lt;/li&gt;
&lt;li&gt;The system’s parameters are automatically injected from matching entities.&lt;/li&gt;
&lt;li&gt;The using-params block lets you access runtime values (like delta time).&lt;/li&gt;
&lt;li&gt;Tag filtering, conditions, and entity creation are built-in.&lt;/li&gt;
&lt;li&gt;It’s not optimized in any way — it’s designed to be fun and expressive.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What’s next? 🚧
&lt;/h2&gt;

&lt;p&gt;Nothing is fixed. The API might change. This is just the beginning of a small experiment. If you’re curious about ECS, or if you have experience with game development and want to help shape a more solid design, please get in touch!&lt;/p&gt;

&lt;p&gt;You can find the project here:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/FCO/ECS" rel="noopener noreferrer"&gt;https://github.com/FCO/ECS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to open issues, create examples, or criticize design decisions.&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>rakulang</category>
      <category>ecs</category>
      <category>gamedev</category>
      <category>framework</category>
    </item>
    <item>
      <title>🚀 Bringing Structural Protocols to Raku</title>
      <dc:creator>Fernando Correa de Oliveira</dc:creator>
      <pubDate>Sat, 19 Jul 2025 01:24:56 +0000</pubDate>
      <link>https://dev.to/fco/bringing-structural-protocols-to-raku-2ook</link>
      <guid>https://dev.to/fco/bringing-structural-protocols-to-raku-2ook</guid>
      <description>&lt;p&gt;Recently on &lt;code&gt;IRC&lt;/code&gt; someone asked how to add methods to basic types, which reminded me of an old experimental project of mine: Protocol. Its goal is to explore a structural style of interface in Raku—distinct from Raku’s nominal roles—echoing how Go’s interfaces work by method shape rather than explicit declaration.  ￼ ￼ ￼&lt;/p&gt;

&lt;h2&gt;
  
  
  🧩 Structural vs. Nominal (Go Inspiration vs. Raku Today)
&lt;/h2&gt;

&lt;p&gt;Raku roles are nominal: you explicitly compose (&lt;code&gt;does&lt;/code&gt;) a &lt;code&gt;role&lt;/code&gt; to promise a bundle of behavior; roles package partial behavior for reuse and are mixed in at compile- or run-time.  ￼ ￼ ￼&lt;br&gt;
Go interfaces are structural: any type whose method set matches the interface implicitly satisfies it—no prior annotation—providing “if it quacks…” flexibility with static checking.  ￼ ￼ ￼&lt;br&gt;
Structural typing differs from duck typing: structural typing is verified statically (at compile/analyze time), while duck typing relies on runtime method availability.  ￼ ￼ ￼&lt;/p&gt;
&lt;h2&gt;
  
  
  📘 What Protocol Originally Did
&lt;/h2&gt;

&lt;p&gt;Originally, declaring a protocol like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Protocol;

protocol Nameable {
    method name { ... }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;created a &lt;code&gt;subset&lt;/code&gt; of &lt;code&gt;Any&lt;/code&gt; that type-checks values by verifying the presence of a name method (the ... &lt;code&gt;yada&lt;/code&gt; marks a required placeholder).  ￼ ￼ ￼&lt;br&gt;
Subsets in Raku re-dispatch to their base type but enforce constraints at assignment / parameter binding; Protocol leveraged that to express a structural “has-method(s)” check without nominal composition.  ￼ ￼ ￼&lt;br&gt;
At first, providing concrete method bodies inside a protocol (i.e. anything other than &lt;code&gt;yada&lt;/code&gt;) was disallowed—protocols were strictly about requirements.  ￼ ￼ ￼&lt;/p&gt;
&lt;h2&gt;
  
  
  🔄 Why Evolve Beyond Pure Subsets?
&lt;/h2&gt;

&lt;p&gt;In practice you often want lightweight structural acceptance plus helper / default methods that build upon the required minimal surface (a pattern familiar from roles’ default methods or convenience wrappers around Go interface method sets).  ￼ ￼ ￼&lt;/p&gt;
&lt;h2&gt;
  
  
  🛠️ The New Behavior: Class Generation When Helpers Exist
&lt;/h2&gt;

&lt;p&gt;If a protocol now includes any concrete method bodies, Protocol generates a class (instead of only a subset) so those methods can be mixed in via coercion when needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Protocol;

protocol Nameable {
    method name { ... }  # required
    method description {
        [
            "Type: { $.^name }",
            "Name: { $.name }"
        ].join: "\n"
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;Nameable&lt;/code&gt; now packages both a structural requirement (&lt;code&gt;name&lt;/code&gt;) and a concrete helper (&lt;code&gt;description&lt;/code&gt;) that becomes available after coercion/mix-in.  ￼ ￼ ￼&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 Pure Structural Checking Only? Use &lt;code&gt;ProtocolSubset&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Need only to assert “this value has method name” (no helper methods)? Parameterize with &lt;code&gt;ProtocolSubset&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my Nameable[ProtocolSubset] $with-name = Person.new: :name&amp;lt;Fernando&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This uses subset semantics: compile/runtime binding checks ensure the method’s existence, while the underlying object remains unchanged and un-augmented.  ￼ ￼ ￼&lt;/p&gt;

&lt;h3&gt;
  
  
  ✨ Need the Helper Methods? Use &lt;code&gt;ProtocolCoerce&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;To use helpers like &lt;code&gt;.description&lt;/code&gt;, request coercion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sub print-description(Nameable[ProtocolCoerce] $_) {
    say .description;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ProtocolCoerce&lt;/code&gt; mixes in the generated class so its concrete methods become callable—conceptually similar to mixing a role into an instance on demand.  ￼ ￼ ￼&lt;/p&gt;

&lt;h3&gt;
  
  
  🧪 About the Mixed Value vs. Original Object
&lt;/h3&gt;

&lt;p&gt;Raku’s mixin mechanism produces a new (reblessed) object when adding &lt;code&gt;roles&lt;/code&gt;/&lt;code&gt;mixins&lt;/code&gt;; the coerced value your function receives may differ in identity from the original variable, though it forwards behavior/state as defined by Raku’s &lt;code&gt;mixin&lt;/code&gt; &lt;code&gt;metaobject&lt;/code&gt; and caching rules.  ￼ ￼ ￼&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Design Trade-Offs (Why This Split Helps)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ergonomics: Structural checking (&lt;code&gt;ProtocolSubset&lt;/code&gt;) validates method presence without retrofitting roles into legacy or 3rd-party types.  ￼ ￼ ￼&lt;/li&gt;
&lt;li&gt;Extensibility: Helper methods embedded directly reduce boilerplate vs. writing separate wrapper roles or utility subs.  ￼ ￼ ￼&lt;/li&gt;
&lt;li&gt;Clarity &amp;amp; Safety: Explicit choice between mere validation (&lt;code&gt;ProtocolSubset&lt;/code&gt;) and augmentation (&lt;code&gt;ProtocolCoerce&lt;/code&gt;) avoids hidden behavior changes.  ￼ ￼ ￼&lt;/li&gt;
&lt;li&gt;Leverage Existing Semantics: Builds atop Raku subsets, mixins, and role composition models rather than inventing a wholly foreign mechanism.  ￼ ￼ ￼&lt;/li&gt;
&lt;li&gt;Parametric Pattern: Parameterization mirrors broader parametric / &lt;code&gt;mixin&lt;/code&gt; patterns in the ecosystem, easing conceptual load.  ￼ ￼ ￼&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🆚 Quick Comparison Snapshot
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;Raku Role (Nominal)&lt;/th&gt;
&lt;th&gt;Go Interface (Structural)&lt;/th&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Require methods&lt;/td&gt;
&lt;td&gt;Consumer &lt;code&gt;does&lt;/code&gt; role; nominal link&lt;/td&gt;
&lt;td&gt;Any type with matching method set&lt;/td&gt;
&lt;td&gt;Subset check: structural (&lt;code&gt;ProtocolSubset&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add helper methods&lt;/td&gt;
&lt;td&gt;Role can define defaults&lt;/td&gt;
&lt;td&gt;Helpers usually on concrete type&lt;/td&gt;
&lt;td&gt;Concrete methods in protocol ⇒ coercible class&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retroactively apply w/out editing source&lt;/td&gt;
&lt;td&gt;Mix role (instance/class)&lt;/td&gt;
&lt;td&gt;Implicit satisfaction&lt;/td&gt;
&lt;td&gt;Structural subset + optional coercion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avoid nominal coupling&lt;/td&gt;
&lt;td&gt;No (explicit name)&lt;/td&gt;
&lt;td&gt;Yes (implicit)&lt;/td&gt;
&lt;td&gt;Yes (shape-based check)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Opt-in behavior augmentation&lt;/td&gt;
&lt;td&gt;Role mixin&lt;/td&gt;
&lt;td&gt;Not via interface itself&lt;/td&gt;
&lt;td&gt;ProtocolCoerce parameter&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each cell reflects documented capabilities of roles, subsets, mixins, and structural interfaces.  ￼ ￼ ￼&lt;/p&gt;

&lt;h2&gt;
  
  
  💬 Structural Interfaces in Go (Deeper Dive)
&lt;/h2&gt;

&lt;p&gt;A Go value satisfies an interface automatically if it implements the interface’s method set—no implements clause—enabling decoupled design and interface-oriented APIs; this is a compile-time structural check distinct from dynamic duck typing.  ￼ ￼ ￼&lt;br&gt;
Community discussions and educational material reiterate the static structural nature (and its contrast with duck typing) emphasizing that interface satisfaction is derived from shape not explicit declarations.  ￼ ￼ ￼&lt;/p&gt;

&lt;h2&gt;
  
  
  🧪 Example Recap (Putting It Together)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Define structural requirements (and optionally helpers) in a protocol.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;Nameable[ProtocolSubset]&lt;/code&gt; where you just need a guarantee the argument responds to .name.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;Nameable[ProtocolCoerce]&lt;/code&gt; when you also want &lt;code&gt;.description&lt;/code&gt; (or other helper methods) to exist via a safe mixin/coercion process.
These steps leverage Raku’s subset validation and mixin metaobject to approximate Go-like structural acceptance with optional augmentation.  ￼ ￼ ￼&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🏁 Conclusion 🎉
&lt;/h2&gt;

&lt;p&gt;Protocol aims to blend Go-style structural typing’s flexibility with Raku’s powerful role, subset, and mixin machinery—cleanly separating validation (&lt;code&gt;ProtocolSubset&lt;/code&gt;) from augmentation (&lt;code&gt;ProtocolCoerce&lt;/code&gt;) so you can retrofit interfaces onto existing code while progressively layering reusable helper behavior.  ￼ ￼ ￼&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Cromponent new features</title>
      <dc:creator>Fernando Correa de Oliveira</dc:creator>
      <pubDate>Fri, 11 Jul 2025 22:17:32 +0000</pubDate>
      <link>https://dev.to/fco/cromponent-new-features-3bhf</link>
      <guid>https://dev.to/fco/cromponent-new-features-3bhf</guid>
      <description>&lt;p&gt;Cromponent 🎉 now lets your components bind cookies, query-string params, headers and HTTP-auth credentials directly in the method signature 🍪❓📑🔐 and push live HTML over WebSockets with two tiny hooks 🔌🛰️.&lt;br&gt;
Paired with HTMX on the client, that means real-time Raku apps with zero JavaScript 🚀. Below you’ll find the new API plus a complete “live poll” example. Dive in! 🏊‍♂️&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Why Cromponent? 🤔✨&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cromponent glues together Cro’s reactive HTTP / WebSocket server and Cro Templates to give Raku developers a component abstraction à la modern SPA frameworks – but rendered on the server and streamed as HTML fragments.&lt;br&gt;
The latest release focuses on state &amp;amp; interaction: declarative request-context binding and built-in WebSocket wiring. ❤️‍🔥&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Prerequisites at a Glance 📚🔎&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;1-Liner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HTMX ⚡&lt;/td&gt;
&lt;td&gt;Enriches plain HTML with AJAX, SSE &amp;amp; WebSockets via attributes.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;hx-put, hx-target, hx-ext="ws"&lt;/code&gt;, …&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cro 🧩&lt;/td&gt;
&lt;td&gt;Reactive HTTP/WebSocket server &amp;amp; router for Raku.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;route { … }&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cro Templates 🖋️&lt;/td&gt;
&lt;td&gt;HTML-centric templating DSL compiled on first use.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;template 'view.crotmp', %data&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Red ORM 🗄️&lt;/td&gt;
&lt;td&gt;DB-agnostic ORM with migrations &amp;amp; relationships.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;model Foo { has Int $.id is serial … }&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic vars 🌐&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;$*foo&lt;/code&gt; passes per-request context through deep calls.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;$*user&lt;/code&gt; in our example&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt; New API: Context Traits 🍪❓📑🔐&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Any method tagged is accessible becomes an endpoint and auto-binds request data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;method profile(
    Str :$uuid   is cookie,
    Str :$q      is query,
    Str :$accept is header,
    Str :$creds  is auth
) is accessible { ... }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Zero plumbing 🛠️&lt;/li&gt;
&lt;li&gt;Type-safe input 🛡️&lt;/li&gt;
&lt;li&gt;Self-documenting signatures 📜&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Route pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/ComponentName/&amp;lt;ids…&amp;gt;/profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; New API: Automatic WebSockets 🔌🛰️

&lt;ol&gt;
&lt;li&gt;Channel key – &lt;code&gt;method IDS { $!id }&lt;/code&gt; groups browsers in the same room.&lt;/li&gt;
&lt;li&gt;Re-render hook – &lt;code&gt;method REDRAW(:$*user is cookie) { $.Str }&lt;/code&gt; returns fresh HTML when you redraw $component.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;|WebSocket&amp;gt;&lt;/code&gt; cromponent adds &lt;code&gt;hx-ext="ws" ws-connect="/ws"&lt;/code&gt; and the htmx-ws extension swaps fragments for you.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;No manual frames, pings or reconnect loops – Cro &amp;amp; HTMX handle all that jazz. 🎷&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Building a Live Poll 🗳️⚡&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;5.1.  Router &amp;amp; DB Setup 🛣️&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my $routes = route {
    red-defaults "SQLite";
    PollItem.^add-cromponent-routes;   # auto REST 🆓
    WebSocket.^add-cromponent-routes;  # auto WS  🆓

    get -&amp;gt; 'polls', Str :$*user is cookie = UUID.new.Str {
        response.set-cookie: :name&amp;lt;user&amp;gt;, :value($*user);
        template 'polls.crotmp', { :$*user, :@polls = Poll.^all }
    }
}
Cro::HTTP::Server.new(:host&amp;lt;0.0.0.0&amp;gt;, :port(2000), :$routes).start;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First visit ➡️ generates UUID ➡️ saves in 🍪 ➡️ available as $*user.&lt;/p&gt;

&lt;p&gt;5.2.  Poll Component 📊&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model Poll does Cromponent {
    has UInt $.id    is serial;
    has Str  $.descr is column;
    has      @.items is relationship(*.poll-id, :model&amp;lt;PollItem&amp;gt;);
    has      @.votes is relationship(*.poll-id, :model&amp;lt;PollVote&amp;gt;);

    method LOAD($id)       { Poll.^load: $id }  # rebuild
    method IDS             { $!id }             # WS room
    method REDRAW(:$*user) { $.Str }            # push update

    method did-user-vote($who = $*user) {
        ?@.votes.first: *.user eq $who
    }

    method RENDER { … }   # the template
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5.3.  PollItem Component ✅&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;method vote(Str :$*user is cookie)
      is accessible { :http-method&amp;lt;PUT&amp;gt;, :returns-cromponent } {
    red-do :transaction, {
        $!votes++;
        self.^save;
        $!poll.votes.create: :$*user;
        redraw $!poll;      # broadcast 🌍
        $!poll
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HTMX button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;
  &lt;span class="na"&gt;hx-put=&lt;/span&gt;&lt;span class="s"&gt;"/poll-item/42/vote"&lt;/span&gt;
  &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"closest .poll"&lt;/span&gt;
  &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;🗳️ Vote&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5.4.  WebSocket-Enabled Page 🖥️&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;Boilerplate&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;:title&lt;/span&gt;&lt;span class="err"&gt;('&lt;/span&gt;&lt;span class="na"&gt;Polls&lt;/span&gt; &lt;span class="err"&gt;🗳️'),&lt;/span&gt; &lt;span class="na"&gt;:htmx&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;:style-sheets&lt;/span&gt;&lt;span class="err"&gt;('/&lt;/span&gt;&lt;span class="na"&gt;css&lt;/span&gt;&lt;span class="err"&gt;'))&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;WebSocket&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h6&amp;gt;&lt;/span&gt;Logged in as &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 🙋‍♂️&lt;span class="nt"&gt;&amp;lt;/h6&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;@.&lt;/span&gt;&lt;span class="na"&gt;polls&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;&amp;amp;&lt;/span&gt;&lt;span class="na"&gt;HTML&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="na"&gt;_&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/polls"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;All polls 📋&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; Generated Endpoints Recap 🛤️&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/polls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;manual route&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/polls/&amp;lt;id&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;manual route&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/poll-item/&amp;lt;id&amp;gt;/vote&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PollItem.vote&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/cromponent-ws&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;WebSocket&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One call to &lt;code&gt;.^add-cromponent-routes&lt;/code&gt; ➡️ endpoints galore! 🎊&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Key Take-aways 💡&lt;/li&gt;
&lt;li&gt;Context traits (&lt;code&gt;is cookie&lt;/code&gt; 🍪, &lt;code&gt;is query&lt;/code&gt; ❓, &lt;code&gt;is header&lt;/code&gt; 📑, &lt;code&gt;is auth&lt;/code&gt; 🔐) shred boilerplate.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IDS&lt;/code&gt; + &lt;code&gt;REDRAW&lt;/code&gt; inject WebSocket magic with two methods. ✨&lt;/li&gt;
&lt;li&gt;Everything lives in one file: model + template + behaviour – still plain Raku, still testable. 🧪&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Result: back-end-driven UX with instant updates and zero JavaScript. 🚀&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Further Reading 📚&lt;/li&gt;
&lt;li&gt;🌟 Cromponent → &lt;a href="https://github.com/FCO/Cromponent" rel="noopener noreferrer"&gt;https://github.com/FCO/Cromponent&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📜 Cro docs → &lt;a href="https://cro.services" rel="noopener noreferrer"&gt;https://cro.services&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🗄️ Red ORM → &lt;a href="https://github.com/FCO/Red" rel="noopener noreferrer"&gt;https://github.com/FCO/Red&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;⚡ HTMX → &lt;a href="https://htmx.org" rel="noopener noreferrer"&gt;https://htmx.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🔌 htmx-ws extension → &lt;a href="https://htmx.org/extensions/ws/" rel="noopener noreferrer"&gt;https://htmx.org/extensions/ws/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🧠 Dynamic vars in Raku → &lt;a href="https://docs.raku.org/language/variables#Dynamic_variables" rel="noopener noreferrer"&gt;https://docs.raku.org/language/variables#Dynamic_variables&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Happy hacking – and may your components stay Crom-pact! 🦀🎈&lt;/p&gt;

</description>
      <category>rakulang</category>
      <category>webcomponents</category>
      <category>htmx</category>
    </item>
    <item>
      <title>Deps</title>
      <dc:creator>Fernando Correa de Oliveira</dc:creator>
      <pubDate>Sat, 07 Jun 2025 03:08:13 +0000</pubDate>
      <link>https://dev.to/fco/deps-453b</link>
      <guid>https://dev.to/fco/deps-453b</guid>
      <description>&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Deps is a minimalist Raku module for exploring dependency injection patterns through a clear, trait-based API. It offers both low‑level and high‑level interfaces, supports named registrations, trait‑driven function injection, and automatic instantiation of classes, while providing precise control over resolution lifecycles—&lt;strong&gt;New&lt;/strong&gt;, &lt;strong&gt;Store&lt;/strong&gt;, and &lt;strong&gt;Scope&lt;/strong&gt;—and resolution precedence via &lt;strong&gt;priority&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Motivation and Background
&lt;/h2&gt;

&lt;p&gt;Dependency injection decouples construction of dependencies from their use, improving modularity, testability, and configuration flexibility. Deps serves as an educational playground for understanding inversion of control in Raku and experimenting with DI concepts without the complexity of heavy frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Overview of Deps
&lt;/h2&gt;

&lt;p&gt;Deps exposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Low‑level API&lt;/strong&gt;: Manually register and retrieve dependencies by type or name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block‑scoped DSL&lt;/strong&gt;: Use &lt;code&gt;deps { ... }&lt;/code&gt; to create nested containers and perform inline registrations or imports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trait‑based injection&lt;/strong&gt;: Apply &lt;code&gt;is injected&lt;/code&gt; to functions/subroutines to have parameters auto‑provided.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic instantiation&lt;/strong&gt;: Construct classes by matching attributes to registered values.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Core Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 Low‑Level API
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my Deps $deps .= new;
$deps.register: Bla;
$deps.register:
    -&amp;gt; Int $a, Int :$b --&amp;gt; Str { "$a - $b" },
    :name&amp;lt;value&amp;gt;
;
$deps.register: 13;
$deps.register: 42, :name&amp;lt;a&amp;gt;;
# Resolve
$deps.get(Bla);          # Bla.new(value =&amp;gt; "42 - 13", a =&amp;gt; 42, b =&amp;gt; 13)
$deps.get(Int, :name&amp;lt;a&amp;gt;); # 42
$deps.get(Int);          # 13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.2 Block‑Scoped DSL
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;deps {
   injectable C.new: :13attr;
   injected my C $obj;
   # $obj == C.new(attr =&amp;gt; 13)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.3 Function‑Level Injection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sub handle-request(UInt $id) is injected {
  injectable UserIdStorage.new: :$id;
  process-request
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parameters typed as registered types are injected at call time.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 Automatic Instantiation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Example { has Int $.x; has Str $.y }
deps {
  injectable 7;
  injectable "hello", :name&amp;lt;y&amp;gt;;
  instantiate(Example);    # Example.new(x =&amp;gt; 7, y =&amp;gt; "hello")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Resolution Lifecycles
&lt;/h2&gt;

&lt;p&gt;Deps supports three built‑in lifecycles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New&lt;/strong&gt; (transient): the default; each call to &lt;code&gt;.get&lt;/code&gt; creates a fresh instance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store&lt;/strong&gt; (singleton): one instance per container; reused on every &lt;code&gt;.get&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope&lt;/strong&gt; (scoped): one instance per &lt;code&gt;deps { ... }&lt;/code&gt; block; fresh when entering a new block but reused within it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Specify with &lt;code&gt;:lifecycle&amp;lt;New|Store|Scope&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Transient (default)
$deps.register: -&amp;gt; Int $n --&amp;gt; Int { $n * 2 };

# Singleton
$deps.register: MyService.new, :lifecycle&amp;lt;Store&amp;gt;;

# Scoped
$deps.register: RequestToken.new, :lifecycle&amp;lt;Scope&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Priority
&lt;/h2&gt;

&lt;p&gt;When multiple providers match a type (and optional name), &lt;code&gt;:priority&amp;lt;Strict|Defer&amp;gt;&lt;/code&gt; determines which wins.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$deps.register:
    FileLogger.new,
    :lifecycle&amp;lt;Store&amp;gt;,
    :priority&amp;lt;defer&amp;gt;
;
$deps.register:
    ConsoleLogger.new,
    :lifecycle&amp;lt;Store&amp;gt;,
    :priority&amp;lt;strict&amp;gt;
;
# .get(Logger) returns ConsoleLogger
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Combined Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;role Repo {}
class RealRepo does Repo { has Str $.dsn }
class MockRepo does Repo { }

my Deps $deps .= new;

# Real (singleton)
$deps.register:
    RealRepo.new(
        |(dsn =&amp;gt; $_ with %*ENV&amp;lt;DB&amp;gt;)
    ),
    :lifecycle&amp;lt;store&amp;gt;,
    :priority&amp;lt;defer&amp;gt;,
    :name&amp;lt;repo&amp;gt;
;

# Mock (transient)
$deps.register:
    MockRepo,
    :lifecycle&amp;lt;new&amp;gt;,
    :priority&amp;lt;strict&amp;gt;,
    :name&amp;lt;repo&amp;gt;
;

deps-scope :parent($deps), {
  injected my Repo $repo;   # MockRepo wins by priority
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Conclusion and Future Directions
&lt;/h2&gt;

&lt;p&gt;With its lightweight core and idiomatic Raku traits, Deps is ideal for learning DI patterns. Correct lifecycle names—&lt;strong&gt;New&lt;/strong&gt;, &lt;strong&gt;Store&lt;/strong&gt;, and &lt;strong&gt;Scope&lt;/strong&gt;—and built‑in &lt;strong&gt;priority&lt;/strong&gt; give precise control over object lifetimes and resolution. Future enhancements may include circular dependency detection, named scopes, and asynchronous resolution workflows.&lt;/p&gt;

</description>
      <category>dependencyinversion</category>
      <category>rakulang</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Testing Raku Applications with Cro, Red, and RedFactory</title>
      <dc:creator>Fernando Correa de Oliveira</dc:creator>
      <pubDate>Sun, 27 Apr 2025 03:02:09 +0000</pubDate>
      <link>https://dev.to/fco/testing-raku-applications-with-cro-red-and-redfactory-g3e</link>
      <guid>https://dev.to/fco/testing-raku-applications-with-cro-red-and-redfactory-g3e</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Automated testing is crucial in modern software development. It helps ensure quality and prevent regressions by quickly verifying if the system continues working after code changes. Unlike manual tests, automated tests can be run repeatedly, ensuring consistency and speeding up validation. Moreover, they improve reliability by confirming that software inputs and outputs remain correct and aligned with requirements.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore how to test applications written in &lt;a href="http://raku.org" rel="noopener noreferrer"&gt;Raku&lt;/a&gt; using three main tools: &lt;a href="http://cro.raku.org" rel="noopener noreferrer"&gt;Cro&lt;/a&gt;, &lt;a href="http://github.com/FCO/Red" rel="noopener noreferrer"&gt;Red&lt;/a&gt;, and &lt;a href="http://github.com/FCO/RedFactory" rel="noopener noreferrer"&gt;RedFactory&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://cro.raku.org" rel="noopener noreferrer"&gt;Cro&lt;/a&gt; is a set of libraries for building reactive services (such as web applications and HTTP APIs) in &lt;a href="http://raku.org" rel="noopener noreferrer"&gt;Raku&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://github.com/FCO/Red" rel="noopener noreferrer"&gt;Red&lt;/a&gt; is an Object-Relational Mapper (ORM) for &lt;a href="http://raku.org" rel="noopener noreferrer"&gt;Raku&lt;/a&gt;. &lt;a href="http://github.com/FCO/Red" rel="noopener noreferrer"&gt;Red&lt;/a&gt; allows you to interact with relational databases using &lt;a href="http://raku.org" rel="noopener noreferrer"&gt;Raku&lt;/a&gt; objects instead of writing raw SQL, bringing several advantages like type safety, consistency, and easier schema evolution.&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://github.com/FCO/RedFactory" rel="noopener noreferrer"&gt;RedFactory&lt;/a&gt; is a helper library designed to work with &lt;a href="http://github.com/FCO/Red" rel="noopener noreferrer"&gt;Red&lt;/a&gt;, making it easy to generate consistent test data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Combining &lt;a href="http://cro.raku.org" rel="noopener noreferrer"&gt;Cro&lt;/a&gt;, &lt;a href="http://github.com/FCO/Red" rel="noopener noreferrer"&gt;Red&lt;/a&gt;, and &lt;a href="http://github.com/FCO/RedFactory" rel="noopener noreferrer"&gt;RedFactory&lt;/a&gt;, we can create web applications in Raku backed by a database and write efficient automated tests for them. Let’s walk step-by-step through setting up a simple project with these tools, defining database models, using factories to generate test data, and writing automated tests—including full HTTP integration tests using Cro::HTTP::Test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Environment
&lt;/h2&gt;

&lt;p&gt;To set up a basic project using Cro, Red, and RedFactory:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install Raku and dependencies:
Ensure you have Raku installed. Then install Cro, Red, and RedFactory using zef:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zef &lt;span class="nb"&gt;install &lt;/span&gt;cro Red RedFactory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create a new Cro project:
Cro provides a CLI tool to generate project skeletons:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cro stub http MyApp myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a project under myapp/ with initial files and structure.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add Red to the project:
Import Red into your main script or modules:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Red:api&amp;lt;2&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure the database connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;red-defaults "SQLite", database =&amp;gt; "myapp.db";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Install and configure RedFactory:
You already installed RedFactory in step 1. Create a file like lib/Factories.rakumod where you will define factories for your models.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now your environment is ready: a basic Cro project running, Red handling database interaction, and RedFactory set up to create test data easily.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Models with Red
&lt;/h2&gt;

&lt;p&gt;Models represent database tables as Raku classes. Let’s define a simple model for children, storing their name and country:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Red:api&amp;lt;2&amp;gt;;

model Child {
    has UInt $.id       is serial;
    has Str  $.name     is column;
    has Str  $.country  is column;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;is serial&lt;/code&gt; marks the ID as an auto-increment primary key.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;is column&lt;/code&gt; indicates the attribute maps to a database column.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before using the model, ensure the table exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Child.^create-table: :unless-exists;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can create records:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Child.^create: name =&amp;gt; "Alice", country =&amp;gt; "Brazil";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or query them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for Child.^all -&amp;gt; $child {
    say $child.name, " - ", $child.country;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Red abstracts away SQL, making database operations much simpler and safer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Factories with RedFactory
&lt;/h2&gt;

&lt;p&gt;Factories allow us to create consistent test data easily. Define them like this in Factories.rakumod:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use RedFactory;
use Child;

factory "child", :model(Child), {
    .name    = "Test";
    .country = "Unknown";
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can create test records in one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my $child = factory-create "child";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or override attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;factory-create "child", :name&amp;lt;Alice&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can even create multiple records:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;factory-create 5, "child";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Factories make tests cleaner and easier to maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to Cro::HTTP::Test
&lt;/h2&gt;

&lt;p&gt;Testing HTTP routes without running a real server is made easy by Cro::HTTP::Test.&lt;/p&gt;

&lt;p&gt;This module allows you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Execute your routes in-memory.&lt;/li&gt;
&lt;li&gt;Send simulated HTTP requests.&lt;/li&gt;
&lt;li&gt;Check responses declaratively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Test;
use Cro::HTTP::Test;
use MyApp::Routes;

test-service routes(), {
    test get("/hello/World"),
        status       =&amp;gt; 200,
        content-type =&amp;gt; 'text/plain',
        body         =&amp;gt; "Hello, World!";
};
done-testing;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;test-service wraps your routes for testing.&lt;br&gt;
test sends a request and checks expectations like status code, content type, and body.&lt;/p&gt;

&lt;p&gt;No need to bind to ports or manage real HTTP connections—everything is fast and isolated.&lt;/p&gt;
&lt;h2&gt;
  
  
  Writing Tests
&lt;/h2&gt;

&lt;p&gt;Let’s use RedFactory and Cro::HTTP::Test together.&lt;/p&gt;

&lt;p&gt;Imagine we have this API route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;get -&amp;gt; 'children', 'count', $country {
    my $count = Child.^all.grep(*.country eq $country).elems;
    content 'application/json', { :count($count) };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can test it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Test;
use Cro::HTTP::Test;
use Factories;
use MyApp::Routes;

factory-run {
    Child.^create-table: :unless-exists;

    .create: 10, "child", :country&amp;lt;UK&amp;gt;;

    test-service routes(), {
        test get("/children/count/UK"),
            status       =&amp;gt; 200,
            content-type =&amp;gt; 'application/json',
            json         =&amp;gt; { :count(10) };
    };
}
done-testing;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;factory-run sets up a fresh SQLite in-memory database.&lt;/li&gt;
&lt;li&gt;We create 10 children from the UK.&lt;/li&gt;
&lt;li&gt;Then we test that the /children/count/UK route correctly returns 10.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This shows how Cro, Red, and RedFactory work together cleanly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full Integration Example
&lt;/h2&gt;

&lt;p&gt;Suppose our API has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GET /children — list all children.&lt;/li&gt;
&lt;li&gt;POST /children — create a new child.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can test the full flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Test;
use Cro::HTTP::Test;
use Factories;
use MyApp::Routes;

factory-run {
    Child.^create-table: :unless-exists;

    test-service routes(), {
        test get("/children"),
            status =&amp;gt; 200,
            content-type =&amp;gt; 'application/json',
            json =&amp;gt; [];
    };

    my %new-data = ( name =&amp;gt; "John", country =&amp;gt; "Portugal" );
    test-service routes(), {
        test post("/children", json =&amp;gt; %new-data),
            status =&amp;gt; 201,
            content-type =&amp;gt; 'application/json',
            json =&amp;gt; { :name("John"), :country("Portugal"), *%_ };
    };

    test-service routes(), {
        test get("/children"),
            status =&amp;gt; 200,
            json =&amp;gt; [ { :name("John"), :country("Portugal"), *%_ } ];
    };
}
done-testing;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with an empty list.&lt;/li&gt;
&lt;li&gt;Add a child via POST.&lt;/li&gt;
&lt;li&gt;Confirm the list now contains the new child.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simple, clean, and fully automated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Isolated Database for Tests:
Always use a test database, ideally in-memory. factory-run with RedFactory makes this easy.&lt;/li&gt;
&lt;li&gt;Use Factories:
Define clear factories for your models to make tests readable.&lt;/li&gt;
&lt;li&gt;Separate Unit and Integration Tests:
Keep logic-only tests separate from HTTP integration tests.&lt;/li&gt;
&lt;li&gt;Check Only Necessary Parts:
Use partial JSON assertions with *%_ to avoid fragile tests.&lt;/li&gt;
&lt;li&gt;Organize Code:
Keep routes modular and tests structured.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good practices help you keep tests maintainable, readable, and fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we showed how to test Raku applications using Cro, Red, and RedFactory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up a project environment.&lt;/li&gt;
&lt;li&gt;Define database models.&lt;/li&gt;
&lt;li&gt;Create factories to generate test data.&lt;/li&gt;
&lt;li&gt;Use Cro::HTTP::Test to simulate HTTP requests and verify responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Red ORM helps you avoid boilerplate SQL and interact with the database elegantly.&lt;br&gt;
RedFactory makes test data creation effortless.&lt;br&gt;
Cro::HTTP::Test allows fast, isolated HTTP testing.&lt;/p&gt;

&lt;p&gt;If you’re building web applications in Raku, combining these tools will dramatically boost your productivity and confidence in your code quality.&lt;/p&gt;

&lt;p&gt;Give Red a try in your next Raku project—you’ll love how much easier database interactions and testing become!&lt;/p&gt;

</description>
      <category>rakulang</category>
      <category>redorm</category>
      <category>cro</category>
      <category>testing</category>
    </item>
    <item>
      <title>Introducing MCP: A Protocol for Modular AI Assistants and Raku's Potential</title>
      <dc:creator>Fernando Correa de Oliveira</dc:creator>
      <pubDate>Fri, 18 Apr 2025 18:59:01 +0000</pubDate>
      <link>https://dev.to/fco/introducing-mcp-a-protocol-for-modular-ai-assistants-and-rakus-potential-19gp</link>
      <guid>https://dev.to/fco/introducing-mcp-a-protocol-for-modular-ai-assistants-and-rakus-potential-19gp</guid>
      <description>&lt;h2&gt;
  
  
  🤖 What is MCP?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;MCP (Model Context Protocol)&lt;/strong&gt; is an open and evolving protocol (see &lt;a href="https://modelcontextprotocol.io/introduction" rel="noopener noreferrer"&gt;modelcontextprotocol.io&lt;/a&gt;) that defines how large language models (LLMs) can understand and interact with structured external capabilities.&lt;/p&gt;

&lt;p&gt;Instead of relying solely on natural language parsing and undocumented APIs, MCP enables LLMs to introspect and call &lt;strong&gt;tools&lt;/strong&gt;, retrieve and reason with &lt;strong&gt;resources&lt;/strong&gt;, and invoke reusable &lt;strong&gt;prompts&lt;/strong&gt;. This introduces clarity, reusability, and extensibility into AI assistant systems.&lt;/p&gt;

&lt;p&gt;MCP defines three types of components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🛠️ &lt;strong&gt;Tools&lt;/strong&gt; – callable actions or APIs, defined with input/output schemas&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;Resources&lt;/strong&gt; – contextual data or runtime configuration for the assistant&lt;/li&gt;
&lt;li&gt;✉️ &lt;strong&gt;Prompts&lt;/strong&gt; – snippets of guiding instructions, either static or code-generated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are described using JSON-compatible schemas and loaded at runtime by a supporting server. The protocol encourages modular design and agent composability.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Note:&lt;/strong&gt; This post presents an experimental implementation of an MCP-compatible framework for the Raku programming language. It is a work in progress, and community input is welcome.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  💡 Why Raku?
&lt;/h2&gt;

&lt;p&gt;Raku is well-suited to building MCP tooling due to its:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧠 Expressive multi-paradigm syntax&lt;/li&gt;
&lt;li&gt;🧰 Built-in meta-object protocol (MOP)&lt;/li&gt;
&lt;li&gt;🔍 Introspection and trait support&lt;/li&gt;
&lt;li&gt;🛡️ Strong type system with gradual typing&lt;/li&gt;
&lt;li&gt;📁 Support for resource embedding via &lt;code&gt;%?RESOURCES&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These features allow developers to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧾 Derive schemas from code automatically&lt;/li&gt;
&lt;li&gt;✔️ Perform advanced validation using traits&lt;/li&gt;
&lt;li&gt;📝 Annotate and document APIs inline&lt;/li&gt;
&lt;li&gt;📚 Embed metadata and logic in clean, declarative structures&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧱 Overview of the Raku Framework
&lt;/h2&gt;

&lt;p&gt;The goal is to provide an ergonomic, modular Raku framework to define and serve MCP-compatible components. The core elements are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔧 Tools can be simple exported subs or classes that &lt;code&gt;does MCP::Tool&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;📦 Resources can be classes, files (via &lt;code&gt;%?RESOURCES&lt;/code&gt;), or environment variables&lt;/li&gt;
&lt;li&gt;✨ Prompts can be strings or code-defined classes&lt;/li&gt;
&lt;li&gt;📄 A &lt;code&gt;mcp.json&lt;/code&gt; file describes each group’s capabilities&lt;/li&gt;
&lt;li&gt;🌐 A CLI manages scaffolding, execution, and bundling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Execution is powered by servers (e.g. STDIO or HTTP), and applications can load multiple groups at once.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔨 Defining Tools
&lt;/h2&gt;

&lt;h3&gt;
  
  
  As Functions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#| Adds two integers and returns the result
sub add(
    Int $a, #= The first integer
    Int $b  #= The second integer
    --&amp;gt; Int
) is export {
    $a + $b
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This produces an MCP schema and validates input before execution. Traits like &lt;code&gt;is different(0)&lt;/code&gt; can further restrict behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  As Classes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unit class WeatherLookup::Tool::Forecast does MCP::Tool;

has Str $.location;

#| Returns a simple forecast string
method call(--&amp;gt; Str) {
    "The weather in $!location will be sunny."
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;MCP::Tool&lt;/code&gt; provides a &lt;code&gt;Callable&lt;/code&gt; interface and derives the schema from class attributes and the return type of &lt;code&gt;call()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Via Module + Exported Function
&lt;/h3&gt;

&lt;p&gt;To support legacy or utility modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"function"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Math::Utils"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"function"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"add"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📚 Resources
&lt;/h2&gt;

&lt;p&gt;Resources provide configuration, secrets, or contextual data to the assistant. You can define them as:&lt;/p&gt;

&lt;h3&gt;
  
  
  Embedded Class
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"resources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My::Class::Name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  File (referenced from &lt;code&gt;%?RESOURCES&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"city-list"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"resource"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"data/cities.json"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Environment Variable
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"api-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"API_KEY"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧠 Prompts
&lt;/h2&gt;

&lt;p&gt;Prompts can be:&lt;/p&gt;

&lt;h3&gt;
  
  
  A Code Class
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class GreetingPrompt {
  method run() {
    "Hello! How can I assist you today?"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A Static String
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"greeting"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hello! How can I assist you today?"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mcp create prompt greeting &lt;span class="nt"&gt;--string&lt;/span&gt; &lt;span class="s2"&gt;"Hello! How can I assist you today?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧩 Group Structure and &lt;code&gt;mcp.json&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Each MCP &lt;strong&gt;group&lt;/strong&gt; is a Raku module with a &lt;code&gt;resources/mcp.json&lt;/code&gt; file listed in &lt;code&gt;META6.json&lt;/code&gt;. This file declares the group's:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🛠️ Tools&lt;/li&gt;
&lt;li&gt;📦 Resources&lt;/li&gt;
&lt;li&gt;✉️ Prompts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"WeatherLookup::Tool::Forecast"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"function"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Math::Utils"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"function"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"add"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prompts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The module should implement &lt;code&gt;MCP::Group&lt;/code&gt; and define:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unit class WeatherLookup does MCP::Group;
method group-data { %?RESOURCES&amp;lt;mcp.json&amp;gt; }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This exposes the tools, resources, and prompts using helper methods.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ CLI and Example Workflow
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;mcp create&lt;/code&gt; commands not only generate boilerplate code but also update the group's &lt;code&gt;resources/mcp.json&lt;/code&gt; file automatically. This file is the core of group definition and gets listed in &lt;code&gt;META6.json&lt;/code&gt; so the framework knows how to load tools, prompts, and resources at runtime.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mcp new WeatherLookup
&lt;span class="nb"&gt;cd &lt;/span&gt;WeatherLookup
mcp create tool forecast
mcp create resource cities &lt;span class="nt"&gt;--file&lt;/span&gt; cities.json
mcp create resource api-key &lt;span class="nt"&gt;--env&lt;/span&gt; API_KEY
mcp create prompt greeting &lt;span class="nt"&gt;--string&lt;/span&gt; &lt;span class="s2"&gt;"Hello!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mcp run WeatherLookup ExtraUtils &lt;span class="nt"&gt;--server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;stdio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🤝 Want to Help?
&lt;/h2&gt;

&lt;p&gt;This idea is still &lt;strong&gt;experimental&lt;/strong&gt;. We're exploring how to best represent and serve LLM functionality in Raku using the MCP protocol.&lt;/p&gt;

&lt;p&gt;📣 Join us in the &lt;code&gt;#raku&lt;/code&gt; channel on &lt;a href="https://libera.chat/" rel="noopener noreferrer"&gt;Libera.Chat IRC&lt;/a&gt; or drop your thoughts on GitHub.&lt;/p&gt;

&lt;p&gt;Whether you want to test ideas, write components, improve the CLI, or critique design—your help is welcome!&lt;/p&gt;

&lt;p&gt;💡 Let’s build something great together!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A Raku Cromponent to Render Highlighted Code — Powered by RakuAST</title>
      <dc:creator>Fernando Correa de Oliveira</dc:creator>
      <pubDate>Mon, 14 Apr 2025 04:10:15 +0000</pubDate>
      <link>https://dev.to/fco/a-raku-cromponent-to-render-highlighted-code-powered-by-rakuast-2dhn</link>
      <guid>https://dev.to/fco/a-raku-cromponent-to-render-highlighted-code-powered-by-rakuast-2dhn</guid>
      <description>&lt;p&gt;Following up from the &lt;a href="https://github.com/FCO/Discuss" rel="noopener noreferrer"&gt;Raku Codeboard&lt;/a&gt; post, here’s a small but exciting addition: a new &lt;strong&gt;Cromponent&lt;/strong&gt; to render Raku code with syntax-aware highlighting — but not by guessing or string-parsing. This one uses &lt;strong&gt;RakuAST&lt;/strong&gt; to actually deparse the code and generate a formatted view.&lt;/p&gt;

&lt;p&gt;The Cromponent comes from a new experimental project called &lt;strong&gt;&lt;a href="https://github.com/FCO/RakuCode" rel="noopener noreferrer"&gt;RakuCode&lt;/a&gt;&lt;/strong&gt;. It's an early-stage attempt to use RakuAST for analyzing and rendering code — and while it’s not ready for every use case, it’s already quite promising.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 The Goal
&lt;/h2&gt;

&lt;p&gt;This Cromponent renders Raku code inside your app (like Codeboard), but instead of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;blindly printing text&lt;/li&gt;
&lt;li&gt;or adding basic syntax coloring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…it parses the code with &lt;strong&gt;RakuAST&lt;/strong&gt;, and then &lt;strong&gt;deparses&lt;/strong&gt; it using a custom renderer. That means you’re not just styling code — you’re formatting the actual language structure.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ How It Works
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/FCO/RakuCode" rel="noopener noreferrer"&gt;RakuCode&lt;/a&gt; is itself a Cromponent, so usage is simple and clean. Here's an example taken from the README:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;:use Boilerplate&amp;gt;
&amp;lt;:use RakuCode&amp;gt;

&amp;lt;|Boilerplate(:htmx, :title('Testing Raku Code Component'), :style-sheets('/css'))&amp;gt;
    &amp;lt;|RakuCode&amp;gt;
        class :: {
            has UInt $!id;
            has Str  $.key;
            has      $.value;
        }
    &amp;lt;/|&amp;gt;
&amp;lt;/|&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This uses &lt;code&gt;&amp;lt;|RakuCode&amp;gt; ... &amp;lt;/|&amp;gt;&lt;/code&gt; to embed code that will be parsed using RakuAST and displayed in a formatted way. This allows you to display actual Raku code with structure-aware formatting in any Cromponent-based view.&lt;/p&gt;

&lt;p&gt;No client-side syntax highlighter is needed. This is all done on the server using real AST analysis.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧪 Limitations
&lt;/h2&gt;

&lt;p&gt;This is very early. Some known issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It doesn’t yet support everything in the Raku language&lt;/li&gt;
&lt;li&gt;Some edge cases might fail to parse&lt;/li&gt;
&lt;li&gt;Error messages can be improved&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But it works well for basic expressions, function definitions, variable usage, and more.&lt;/p&gt;

&lt;p&gt;Even this small feature opens the door to things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Properly formatted code snippets&lt;/li&gt;
&lt;li&gt;Static analysis tools&lt;/li&gt;
&lt;li&gt;Teaching tools for understanding the structure of Raku&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🤝 Want to Help?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/FCO/RakuCode" rel="noopener noreferrer"&gt;RakuCode&lt;/a&gt; is open-source and very early. If you like where this is going, contributions are welcome — whether you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Want to extend the renderer&lt;/li&gt;
&lt;li&gt;Help parse new AST node types&lt;/li&gt;
&lt;li&gt;Fix bugs or improve error handling&lt;/li&gt;
&lt;li&gt;Write docs or tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the repo and feel free to open issues or PRs.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔚 Wrap-up
&lt;/h2&gt;

&lt;p&gt;This is just the beginning. Integrating &lt;strong&gt;RakuAST&lt;/strong&gt; into UI components is a powerful concept, and Cromponent makes it easy to plug in clean rendering into your Raku web apps.&lt;/p&gt;

&lt;p&gt;With RakuCode, we can explore what it means to truly understand and render code — not just decorate it.&lt;/p&gt;

&lt;p&gt;👉 Try it out: &lt;a href="https://github.com/FCO/RakuCode" rel="noopener noreferrer"&gt;https://github.com/FCO/RakuCode&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me know what you'd like to see next!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building Raku Codeboard: A Simple Full-Stack App with Red, Cromponent, and HTMX</title>
      <dc:creator>Fernando Correa de Oliveira</dc:creator>
      <pubDate>Tue, 08 Apr 2025 02:37:03 +0000</pubDate>
      <link>https://dev.to/fco/building-raku-codeboard-a-simple-full-stack-app-with-red-cromponent-and-htmx-30k9</link>
      <guid>https://dev.to/fco/building-raku-codeboard-a-simple-full-stack-app-with-red-cromponent-and-htmx-30k9</guid>
      <description>&lt;p&gt;Welcome to a walkthrough of &lt;strong&gt;Raku Codeboard&lt;/strong&gt; — a small but complete full-stack web application built entirely in Raku. It lets you submit, store, and discuss code or comments under different topics. This blog post explains how it works using your actual code and shows how it leverages &lt;strong&gt;Red&lt;/strong&gt;, &lt;strong&gt;Cromponent&lt;/strong&gt;, and &lt;strong&gt;HTMX&lt;/strong&gt; together.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Source Code:&lt;/strong&gt; &lt;a href="https://github.com/FCO/Discuss" rel="noopener noreferrer"&gt;https://github.com/FCO/Discuss&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 What is Raku Codeboard?
&lt;/h2&gt;

&lt;p&gt;Raku Codeboard is a lightweight app where users can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create &lt;strong&gt;topics&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Post &lt;strong&gt;messages&lt;/strong&gt; under each topic (text or code)&lt;/li&gt;
&lt;li&gt;See a list of all &lt;strong&gt;people&lt;/strong&gt; who participated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Red&lt;/strong&gt; for ORM/database mapping&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cromponent&lt;/strong&gt; for reusable web components&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTMX&lt;/strong&gt; for declarative interactivity&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧱 app.raku
&lt;/h2&gt;

&lt;p&gt;This is your full &lt;code&gt;app.raku&lt;/code&gt;, setting up Cro and wiring routes to Cromponents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/env raku

use lib "lib";
use lib "bin/lib";
use Cro::HTTP::Router;
use Cro::HTTP::Server;
use Cro::WebApp::Template;
use Red:api&amp;lt;2&amp;gt;;

use Topic;
use Topics;
use People;
use Person;
use Message;

my $routes = route {
    red-defaults "SQLite";
    my $schema = schema(Topic, Person, Message).create;

    Topic.^add-cromponent-routes;
    Topics.^add-cromponent-routes;
    People.^add-cromponent-routes;

    template-location "resources/";
    get  -&amp;gt; {
        template "index.crotmp", %(
            topics =&amp;gt; Topic.^all,
            people =&amp;gt; Person.^all,
        )
    }
}

my Cro::Service $http = Cro::HTTP::Server.new(
    http =&amp;gt; &amp;lt;1.1&amp;gt;,
    host =&amp;gt; "0.0.0.0",
    port =&amp;gt; 3013,
    application =&amp;gt; $routes,
);

$http.start;
say "Listening at http://0.0.0.0:3013";
react {
    whenever signal(SIGINT) {
        say "Shutting down...";
        $http.stop;
        done;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧩 index.crotmp
&lt;/h2&gt;

&lt;p&gt;Your template renders two Cromponents: &lt;code&gt;&amp;lt;&amp;amp;People&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;&amp;amp;Topics&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;:use&lt;/span&gt; &lt;span class="na"&gt;Boilerplate&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;Boilerplate&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;:title&lt;/span&gt;&lt;span class="err"&gt;('&lt;/span&gt;&lt;span class="na"&gt;Todo&lt;/span&gt; &lt;span class="na"&gt;-&lt;/span&gt; &lt;span class="na"&gt;test&lt;/span&gt; &lt;span class="na"&gt;cromponent&lt;/span&gt;&lt;span class="err"&gt;'),&lt;/span&gt; &lt;span class="na"&gt;:htmx&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
         &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nc"&gt;.topics&lt;/span&gt;&lt;span class="nd"&gt;:empty:before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"No topics yet. Be the first to start one!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nc"&gt;.messages&lt;/span&gt;&lt;span class="nd"&gt;:empty:before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"No messages yet. Be the first to create one!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nc"&gt;.people&lt;/span&gt;&lt;span class="nd"&gt;:empty:before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"No people yet. Be the first one!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
        &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/topics"&lt;/span&gt;
        &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/topics"&lt;/span&gt;
        &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;".main"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Topics
    &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
        &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/people"&lt;/span&gt;
        &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/people"&lt;/span&gt;
        &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;".main"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        People
    &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Raku Codeboard&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"main"&lt;/span&gt; &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"load"&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/topics"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📦 Cromponents and Models
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Topics.rakumod
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Cromponent;
use Topic;

unit class Topics does Cromponent;

has @.topics = Topic.^all;

method LOAD { ::?CLASS.new }

method RENDER {
    Q:to/HTML/;
    &amp;lt;h2&amp;gt;Create a new topic&amp;lt;/h2&amp;gt;
    &amp;lt;form
        method="post"
        action="/topic"
        hx-post="topic"
        hx-target=".topics"
        hx-swap="beforeend"
        hx-on::after-request="this.reset()"
    &amp;gt;
        &amp;lt;input type="text" name="nick" placeholder="Your nick" required&amp;gt;
        &amp;lt;input type="text" name="title" placeholder="Topic title" required&amp;gt;
        &amp;lt;button&amp;gt;Start Topic&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;

    &amp;lt;h2&amp;gt;Topics&amp;lt;/h2&amp;gt;
    &amp;lt;div class="topics"&amp;gt;&amp;lt;@.topics.Seq&amp;gt;&amp;lt;&amp;amp;HTML(.Card)&amp;gt;&amp;lt;/@&amp;gt;&amp;lt;/div&amp;gt;
    HTML
}

sub EXPORT { Topics.^exports }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Topic.rakumod
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Red:api&amp;lt;2&amp;gt;;
use Cromponent;
use Person;
use Topic::Card;

unit model Topic does Cromponent is table&amp;lt;topic&amp;gt;;

has UInt $.id        is serial;
has Str  $.title     is unique{ :nullable };
has      @.messages  is relationship(*.topic-id, :model&amp;lt;Message&amp;gt;);
has UInt $.author-id is referencing(*.id, :model&amp;lt;Person&amp;gt;);
has UInt $.author    is relationship(*.author-id, :model&amp;lt;Person&amp;gt;);

method LOAD(UInt() $id) { ::?CLASS.^load: $id }
method DELETE           { $.^delete }
method CREATE(Str :$nick, Str :$title) {
    Topic.^create(:$title, :author{ :$nick }).Card
}

method message(Str :$body!, Str :$nick!, Bool :$code = False) is accessible{ :http-method&amp;lt;POST&amp;gt; } {
    $.messages.create: :$body, :$code, :author{ :$nick }
}

method RENDER {
    Q:to/HTML/;
    &amp;lt;div class="topic"&amp;gt;
        &amp;lt;strong&amp;gt;&amp;lt;.title&amp;gt;&amp;lt;/strong&amp;gt; &amp;lt;small&amp;gt;by &amp;lt;.author.nick&amp;gt;&amp;lt;/small&amp;gt;
        &amp;lt;div class=messages&amp;gt;&amp;lt;@.messages.Seq&amp;gt;&amp;lt;&amp;amp;HTML($_)&amp;gt;&amp;lt;/@&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;form
            method="POST"
            action="/topic/&amp;lt;.id&amp;gt;/message"
            hx-post="/topic/&amp;lt;.id&amp;gt;/message"
            hx-target=".main"
            hx-on::after-request="this.reset()"
        &amp;gt;
            User name: &amp;lt;input name="nick"&amp;gt;&amp;lt;br&amp;gt;
            &amp;lt;input type="checkbox" name="code"&amp;gt; code&amp;lt;br&amp;gt;
            &amp;lt;textarea name="body"&amp;gt;&amp;lt;/textarea&amp;gt;&amp;lt;br&amp;gt;
            &amp;lt;button&amp;gt;Send&amp;lt;/button&amp;gt;
        &amp;lt;/form&amp;gt;
    &amp;lt;/div&amp;gt;
    HTML
}

method Card {
    Topic::Card.new: :$!id, :$!title, :$!author
}

sub EXPORT { Topic.^exports }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Topic::Card.rakumod
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Red;
use Cromponent;

unit model Topic::Card does Cromponent;

has UInt $.id     is required;
has Str  $.title  is required;
has      $.author is required;

method RENDER {
    Q:to/HTML/
    &amp;lt;div class="topic"&amp;gt;
        &amp;lt;a
            href="/topic/&amp;lt;.id&amp;gt;"
            hx-get="/topic/&amp;lt;.id&amp;gt;"
            hx-target=".main"
        &amp;gt;
            &amp;lt;strong&amp;gt;&amp;lt;.title&amp;gt;&amp;lt;/strong&amp;gt;
        &amp;lt;/a&amp;gt;
        &amp;lt;small&amp;gt;by &amp;lt;.author.nick&amp;gt;&amp;lt;/small&amp;gt;
    &amp;lt;/div&amp;gt;
    HTML
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Message.rakumod
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Red;
use Cromponent;

unit model Message does Cromponent is table&amp;lt;message&amp;gt;;

has UInt $.id;
has UInt $.topic-id is referencing(*.id, :model&amp;lt;Topic&amp;gt;);
has $.topic is relationship(*.topic-id, :model&amp;lt;Topic&amp;gt;);
has Str $.body;
has UInt $.author-id is referencing(*.id, :model&amp;lt;Person&amp;gt;);
has $.author is relationship(*.author-id, :model&amp;lt;Person&amp;gt;);
has Bool $.code;

method LOAD(Str() $id) { ::?CLASS.^load: $id }
method DELETE          { $.^delete }

method RENDER {
    Q:to/HTML/
    &amp;lt;div class="message"&amp;gt;
        &amp;lt;.author.nick&amp;gt;&amp;lt;br&amp;gt;
        &amp;lt;.body&amp;gt;
    &amp;lt;/dev&amp;gt;
    HTML
}

subset Text is sub-model of Message where *.code.not;
subset Code is sub-model of Message where *.code.so;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  People.rakumod
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Cromponent;
use Person;

unit class People does Cromponent;

has @.people = Person.^all;

method LOAD { ::?CLASS.new }

method RENDER {
    Q:to/HTML/;
    &amp;lt;h2&amp;gt;People&amp;lt;/h2&amp;gt;
    &amp;lt;div class="people"&amp;gt;&amp;lt;@.people.Seq&amp;gt;&amp;lt;div&amp;gt;&amp;lt;&amp;amp;HTML($_)&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/@&amp;gt;&amp;lt;/div&amp;gt;
    HTML
}

sub EXPORT { People.^exports }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔁 HTMX + Cromponent + Red
&lt;/h2&gt;

&lt;p&gt;Everything works together like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Forms use &lt;code&gt;hx-post&lt;/code&gt; to Cromponent route methods (e.g., &lt;code&gt;CREATE&lt;/code&gt;, &lt;code&gt;message&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Cromponent methods use &lt;strong&gt;Red&lt;/strong&gt; to insert into the database&lt;/li&gt;
&lt;li&gt;They return a Cromponent that gets rendered to HTML&lt;/li&gt;
&lt;li&gt;HTMX places that HTML back into the DOM (e.g., &lt;code&gt;.main&lt;/code&gt; or &lt;code&gt;.topics&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All routes are declared using &lt;code&gt;.^add-cromponent-routes&lt;/code&gt;, so you don’t need to write routing logic by hand.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Summary
&lt;/h2&gt;

&lt;p&gt;This app is fully functional and real — and it’s all in Raku. It combines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Red&lt;/strong&gt; for database modeling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cromponent&lt;/strong&gt; for reusable UI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTMX&lt;/strong&gt; for user interactivity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cro&lt;/strong&gt; for the HTTP layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔗 &lt;a href="https://github.com/FCO/Discuss" rel="noopener noreferrer"&gt;https://github.com/FCO/Discuss&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me know what you’d like to add!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>htmx</category>
      <category>red</category>
      <category>rakulang</category>
    </item>
    <item>
      <title>The Evolution of Web Component Modules in Raku: A Journey of Diverse Approaches</title>
      <dc:creator>Fernando Correa de Oliveira</dc:creator>
      <pubDate>Sun, 30 Mar 2025 02:39:02 +0000</pubDate>
      <link>https://dev.to/fco/the-evolution-of-web-component-modules-in-raku-a-journey-of-diverse-approaches-1i7g</link>
      <guid>https://dev.to/fco/the-evolution-of-web-component-modules-in-raku-a-journey-of-diverse-approaches-1i7g</guid>
      <description>&lt;p&gt;Over the past several years, the &lt;a href="https://raku.org/" rel="noopener noreferrer"&gt;Raku&lt;/a&gt; community has explored a variety of approaches to building web interfaces. Each module represents a distinct set of design choices and trade-offs, reflecting different philosophies and priorities. In this post, we’ll review key milestones in the evolution of web component modules in &lt;a href="https://raku.org/" rel="noopener noreferrer"&gt;Raku&lt;/a&gt; and provide code examples that illustrate how each solution works in practice.&lt;/p&gt;




&lt;h2&gt;
  
  
  2017 – &lt;a href="https://github.com/FCO/p6-react" rel="noopener noreferrer"&gt;p6-react&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/FCO/p6-react" rel="noopener noreferrer"&gt;p6-react&lt;/a&gt; was one of the earliest experiments in bringing a component-based approach to &lt;a href="https://raku.org/" rel="noopener noreferrer"&gt;Raku&lt;/a&gt; web development. Inspired by modern front-end frameworks, &lt;a href="https://github.com/FCO/p6-react" rel="noopener noreferrer"&gt;p6-react&lt;/a&gt; enables developers to write server-side components with a declarative syntax. The following example demonstrates how to create reusable components using the &lt;a href="https://github.com/FCO/p6-react" rel="noopener noreferrer"&gt;p6-react&lt;/a&gt; style:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Component;
use Slang;

component Item {
    has Str $.data;
    method render {
        &amp;lt;li&amp;gt;
            {{$.data}}
        &amp;lt;/li&amp;gt;
    }
}

component UlList {
    has Str @.items;
    method render {
        &amp;lt;ul&amp;gt;
            {{
                do for @.items -&amp;gt; $item {
                    &amp;lt;Item data={{$item}} /&amp;gt;
                }
            }}
        &amp;lt;/ul&amp;gt;
    }
}

say UlList.new(:items&amp;lt;bla ble bli&amp;gt;).render.render
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the Item component renders a list item (&lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;) using its data, while &lt;code&gt;UlList&lt;/code&gt; composes a set of Item components into an unordered list (&lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt;). This approach leverages a clear, nested component structure that promotes code reuse and declarative UI construction.&lt;/p&gt;




&lt;h2&gt;
  
  
  2018 – &lt;a href="https://github.com/FCO/MemoizedDOM" rel="noopener noreferrer"&gt;MemoizedDOM&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/FCO/MemoizedDOM" rel="noopener noreferrer"&gt;MemoizedDOM&lt;/a&gt; emerged in 2018 with a fresh perspective by focusing on a functional approach to HTML generation. Borrowing ideas from memoization techniques, &lt;a href="https://github.com/FCO/MemoizedDOM" rel="noopener noreferrer"&gt;MemoizedDOM&lt;/a&gt; emphasizes the declarative construction of web interfaces. It leverages tools like &lt;code&gt;rakudo.js&lt;/code&gt; and integrates optionally with platforms such as &lt;code&gt;webperl6&lt;/code&gt; and &lt;code&gt;6pad&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The example below demonstrates a more advanced application—a dynamic todo list—with interactive form handling and live updates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# App.rakumod
use MemoizedDOM;
use Todo;

class App does Tag {
    has @.todos;
    has Str $.new-title = "";

    method render {
        form(
            :event{
                :submit{
                    .preventDefault;
                    @!todos.push: {
                        :title($!new-title),
                        :!done
                    };
                    $!new-title = "";
                    self.call-render
                }
            },
            ul({ do for @!todos -&amp;gt; (Str :$title!, Bool :$done! is rw) {
                Todo(:$title, :$done, :toggle{ $done = !$done; self.call-render })
            }}),
            input(
                :type&amp;lt;text&amp;gt;,
                :event{
                    :keyup{ $!new-title = .&amp;lt;target&amp;gt;&amp;lt;value&amp;gt; }
                },
                :value{ $!new-title }
            ),
            input(
                :type&amp;lt;submit&amp;gt;,
                :value&amp;lt;OK&amp;gt;,
            )
        )
    }
}


# Todo.rakumod
use MemoizedDOM;

class Todo does Tag {
    has Str  $.title;
    has Bool $.done is rw;
    has      &amp;amp;.toggle is required;

    method render {
        li(
            :style{
                $!done
                ?? { :textDecoration&amp;lt;line-through&amp;gt;, :opacity&amp;lt;0.3&amp;gt; }
                !! { :textDecoration&amp;lt;none&amp;gt;        , :opacity&amp;lt;1&amp;gt;   }
            },
            :event{
                :click( &amp;amp;!toggle )
            },
            input(
                :type&amp;lt;checkbox&amp;gt;,
                :checked{ $!done },
            ),
            $!title
        )
    }
}


# todo.raku
use App;

EVAL :lang&amp;lt;JavaScript&amp;gt;, 'HTMLElement.prototype.defined = function() { return true }';
EVAL :lang&amp;lt;JavaScript&amp;gt;, 'document.defined = function() { return true }';

my \document = EVAL :lang&amp;lt;JavaScript&amp;gt;, 'return document';

my $app = App(:todos[
    {:title&amp;lt;bla&amp;gt;, :!done},
    {:title&amp;lt;ble&amp;gt;, :done },
    {:title&amp;lt;bli&amp;gt;, :!done},
]);

$app.mount-on: document.getElementById: 'todoapp';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example shows how &lt;a href="https://github.com/FCO/MemoizedDOM" rel="noopener noreferrer"&gt;MemoizedDOM&lt;/a&gt; can be used to build an interactive todo list. It demonstrates declarative UI rendering, event handling, and live updates through memoization. The functional style emphasizes a clear separation between data and view, though it may require a shift in mindset compared to traditional object-oriented approaches.&lt;/p&gt;




&lt;h2&gt;
  
  
  2023 – &lt;a href="https://github.com/FCO/HTML-Component" rel="noopener noreferrer"&gt;HTML::Component&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/FCO/HTML-Component" rel="noopener noreferrer"&gt;HTML::Component&lt;/a&gt; took a different turn by embracing an object-oriented design to build HTML structures. Instead of relying solely on functions, this module uses methods and role composition to generate HTML elements. This approach harnesses &lt;a href="https://raku.org/" rel="noopener noreferrer"&gt;Raku&lt;/a&gt;’s strengths in object composition and method chaining, offering a distinct set of trade-offs.&lt;/p&gt;

&lt;p&gt;The following example demonstrates a complete todo list application using &lt;a href="https://github.com/FCO/HTML-Component" rel="noopener noreferrer"&gt;HTML::Component&lt;/a&gt;. It is divided across multiple modules to showcase component loading, dynamic updates, and integration with HTMX for seamless interactivity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# examples/todo/Todo.rakumod
use HTML::Component;
use HTML::Component::Endpoint;

unit class Todo does HTML::Component;

my @todos;

has UInt   $.id = ++$;
has Str()  $.description is required;
has Bool() $.done = False;

submethod TWEAK(|) {
  @todos[$!id - 1] := self;
}

method LOAD(UInt() :$id) {
  @todos[$id - 1]
}

multi method new($description) { self.new: :$description, |%_ }

method RENDER($_) {
  .li:
    :htmx-endpoint(self.toggle),
    :hx-swap&amp;lt;outerHTML&amp;gt;,
    :class&amp;lt;todo&amp;gt;,
    {
      .input-checkbox:
        :checked($!done),
      ;
      if $!done {
        .del: $!description
      } else {
        .add-child: $!description
      }
    }
  ;
}

method toggle is endpoint{ :return-component } {
  $!done .= not
}



# examples/todo/TodoList.rakumod
use HTML::Component::Endpoint;
use HTML::Component;
use HTML::Component::Boilerplate;
use HTML::Component::Traits;
use Todo;

unit class TodoList does HTML::Component;

method new(|)  { $ //= self.bless }
method LOAD(|) { self.new }

has UInt $.id = ++$;
has Todo @.todos;

method RENDER($_) {
  .ol: {
    .add-children: @!todos;
  }
  .form: self.new-todo;
}

method new-todo(
  Str :$description! is no-label, #= What should be done?
) is endpoint{ :verb&amp;lt;POST&amp;gt;, :redirect&amp;lt;/&amp;gt; } {
  @!todos.push: Todo.new: :$description;
}



# examples/todo/App.rakumod
use TodoList;
use HTML::Component;
use HTML::Component::Boilerplate;
unit class App does HTML::Component;

method RENDER($) {
  boilerplate
    :title("My TODO list"),
    :body{
      .script: :src&amp;lt;https://unpkg.com/htmx.org@1.9.10&amp;gt;;
      .add-child: TodoList.new;
    }
  ;
}



# examples/todo/cro-todo.raku
use Cro::HTTP::Log::File;
use Cro::HTTP::Server;
use Cro::HTTP::Router;
use HTML::Component::CroRouter;
use Cro::HTTP::Log::File;
use lib "examples";
use App;

my $route = route {
    root-component App.new
}

my $app = Cro::HTTP::Server.new(
    host =&amp;gt; '127.0.0.1',
    port =&amp;gt; 10000,
    application =&amp;gt; $route,
    after =&amp;gt; [
        Cro::HTTP::Log::File.new(logs =&amp;gt; $*OUT, errors =&amp;gt; $*ERR)
    ],
  );

$app.start;
say "Listening at http://127.0.0.1:10000";

react whenever signal(SIGINT) {
    $app.stop;
    exit;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;a href="https://github.com/FCO/HTML-Component" rel="noopener noreferrer"&gt;HTML::Component&lt;/a&gt; example is more elaborate, demonstrating how to split a todo list application into multiple components. It showcases dynamic component registration and loading, interactive endpoints using HTMX, and the integration of boilerplate templates to create a robust web application.&lt;/p&gt;




&lt;h2&gt;
  
  
  2024 – &lt;a href="https://github.com/FCO/Cromponent" rel="noopener noreferrer"&gt;Cromponent&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/FCO/Cromponent" rel="noopener noreferrer"&gt;Cromponent&lt;/a&gt; offers a flexible way to create web components using &lt;code&gt;Cro&lt;/code&gt; &lt;code&gt;templates&lt;/code&gt;. It stands out by supporting three complementary modes of integration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Template-Only Usage: Use a &lt;a href="https://github.com/FCO/Cromponent" rel="noopener noreferrer"&gt;Cromponent&lt;/a&gt; as a “fancy substitute for cro-template sub/macro.”&lt;/li&gt;
&lt;li&gt;Data Embedding: Pass a &lt;a href="https://github.com/FCO/Cromponent" rel="noopener noreferrer"&gt;Cromponent&lt;/a&gt; as a value to a template so that printing it yields its rendered HTML.&lt;/li&gt;
&lt;li&gt;Auto-Generated Routes: Automatically create &lt;a href="https://cro.raku.org/" rel="noopener noreferrer"&gt;Cro&lt;/a&gt; &lt;code&gt;endpoints&lt;/code&gt; for your components, enabling dynamic, &lt;code&gt;REST&lt;/code&gt;-like interactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below is the simplest example taken directly from &lt;a href="https://github.com/FCO/Cromponent" rel="noopener noreferrer"&gt;Cromponent&lt;/a&gt;’s README, demonstrating the template-only approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Cromponent;
class AComponent does Cromponent {
    has $.data;

    method RENDER {
        Q:to/END/
        &amp;lt;h1&amp;gt;&amp;lt;.data&amp;gt;&amp;lt;/h1&amp;gt;
        END
    }
}

sub EXPORT { AComponent.^exports }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;code&gt;AComponent&lt;/code&gt; is defined as a &lt;a href="https://github.com/FCO/Cromponent" rel="noopener noreferrer"&gt;Cromponent&lt;/a&gt; that renders an &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tag with its data. This concise setup highlights &lt;a href="https://github.com/FCO/Cromponent" rel="noopener noreferrer"&gt;Cromponent&lt;/a&gt;’s elegance: by simply defining a &lt;code&gt;RENDER&lt;/code&gt; method using a &lt;a href="https://cro.raku.org/" rel="noopener noreferrer"&gt;Cro&lt;/a&gt; &lt;code&gt;template&lt;/code&gt;, you can quickly build components that are reusable within your &lt;a href="https://cro.raku.org/" rel="noopener noreferrer"&gt;Cro&lt;/a&gt; applications. Moreover, &lt;a href="https://github.com/FCO/Cromponent" rel="noopener noreferrer"&gt;Cromponent&lt;/a&gt;’s versatility allows its use in embedding within templates or even auto-generating HTTP routes for dynamic web interfaces.&lt;/p&gt;




&lt;h2&gt;
  
  
  2025 – &lt;a href="https://rakujourney.wordpress.com/2025/03/30/the-harc-stack/" rel="noopener noreferrer"&gt;Air&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://rakujourney.wordpress.com/2025/03/30/the-harc-stack/" rel="noopener noreferrer"&gt;Air&lt;/a&gt; represents a fresh direction in the evolution of Raku’s web component ecosystem. &lt;a href="https://rakujourney.wordpress.com/2025/03/30/the-harc-stack/" rel="noopener noreferrer"&gt;Air&lt;/a&gt; merges automatic routing and functional HTML generation, aiming for a clear, declarative approach to building web pages. Below is an example demonstrating how you can use &lt;a href="https://rakujourney.wordpress.com/2025/03/30/the-harc-stack/" rel="noopener noreferrer"&gt;Air&lt;/a&gt; to define a full web page that dynamically renders content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/env raku

use Air::Functional :BASE;
use Air::Base;

my &amp;amp;index = &amp;amp;page.assuming( #:REFRESH(1),
    title       =&amp;gt; 'hÅrc',
    description =&amp;gt; 'HTMX, Air, Red, Cro',
    footer      =&amp;gt; footer p ['Aloft on ', b safe '&amp;amp;Aring;ir'],
);

my &amp;amp;planets = &amp;amp;table.assuming(
    :thead[["Planet", "Diameter (km)",
            "Distance to Sun (AU)", "Orbit (days)"],],
    :tbody[["Mercury",  "4,880", "0.39",  "88"],
           ["Venus"  , "12,104", "0.72", "225"],
           ["Earth"  , "12,742", "1.00", "365"],
           ["Mars"   ,  "6,779", "1.52", "687"],],
    :tfoot[["Average",  "9,126", "0.91", "341"],],
);

sub SITE is export {
    site #:bold-color&amp;lt;blue&amp;gt;,
        index
            main
                div [
                    h3 'Planetary Table';
                    planets;
                ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this &lt;a href="https://rakujourney.wordpress.com/2025/03/30/the-harc-stack/" rel="noopener noreferrer"&gt;Air&lt;/a&gt; example, the code demonstrates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Declarative Page Composition: Using &lt;code&gt;&amp;amp;page.assuming&lt;/code&gt; to create a page with a title, description, and footer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data-Driven Tables: Constructing an HTML table via &lt;code&gt;&amp;amp;table.assuming&lt;/code&gt; with structured data for headers, body, and footers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Functional Composition: The SITE subroutine assembles the complete page layout by combining various components, resulting in a dynamic page rendered in a functional style.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://rakujourney.wordpress.com/2025/03/30/the-harc-stack/" rel="noopener noreferrer"&gt;Air&lt;/a&gt;’s approach illustrates the potential for combining HTMX-enhanced interactivity with a concise, functional programming model, adding another valuable tool to the diverse landscape of &lt;a href="https://raku.org/" rel="noopener noreferrer"&gt;Raku&lt;/a&gt; web component modules.&lt;/p&gt;




&lt;h2&gt;
  
  
  Other Noteworthy Projects
&lt;/h2&gt;

&lt;p&gt;The evolution of web component modules in &lt;a href="https://raku.org/" rel="noopener noreferrer"&gt;Raku&lt;/a&gt; is further enriched by additional projects that explore diverse integrations and methodologies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/FCO/Cro-WebSocket-WebComponents-test" rel="noopener noreferrer"&gt;Cro-WebSocket-WebComponents-test&lt;/a&gt;: Investigates the integration of &lt;a href="https://cro.raku.org/" rel="noopener noreferrer"&gt;Cro&lt;/a&gt; with WebSockets to enable live, reactive updates within web components.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/FCO/proact/" rel="noopener noreferrer"&gt;Proact&lt;/a&gt;: Introduces reactive programming paradigms into web development, offering a creative approach to managing dynamic interfaces.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these projects reflects different priorities—whether it’s seamless interactivity, modularity, or declarative design—allowing developers to select the tool that best fits their specific needs and preferences.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The history of web component modules in &lt;a href="https://raku.org/" rel="noopener noreferrer"&gt;Raku&lt;/a&gt; is a testament to the community’s commitment to experimentation and innovation. From &lt;a href="https://github.com/FCO/p6-react" rel="noopener noreferrer"&gt;p6-react&lt;/a&gt;’s declarative, component-based structure to &lt;a href="https://github.com/FCO/MemoizedDOM" rel="noopener noreferrer"&gt;MemoizedDOM&lt;/a&gt;’s functional approach, from &lt;a href="https://github.com/FCO/HTML-Component" rel="noopener noreferrer"&gt;HTML::Component&lt;/a&gt;’s object-oriented, HTMX-powered applications to &lt;a href="https://github.com/FCO/Cromponent" rel="noopener noreferrer"&gt;Cromponent&lt;/a&gt;’s versatile &lt;a href="https://cro.raku.org/" rel="noopener noreferrer"&gt;Cro&lt;/a&gt; template integration, and finally to &lt;a href="https://rakujourney.wordpress.com/2025/03/30/the-harc-stack/" rel="noopener noreferrer"&gt;Air&lt;/a&gt;’s functional, data-driven page composition—each module brings its own set of trade-offs and benefits. Rather than comparing these solutions as better or worse, it is more productive to view them as complementary approaches addressing different design goals and use cases.&lt;/p&gt;

&lt;p&gt;I probably have not found all modules related to web components in &lt;a href="https://raku.org/" rel="noopener noreferrer"&gt;Raku&lt;/a&gt;. If you remember any other module that should be cited here, please let me know—I’d love to hear your suggestions and will create a new post to cover them!&lt;/p&gt;

&lt;p&gt;As the ecosystem continues to evolve, &lt;a href="https://raku.org/" rel="noopener noreferrer"&gt;Raku&lt;/a&gt; developers have a rich set of tools at their disposal, each contributing to a diverse and dynamic web development landscape.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
