<?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: Richard Hainsworth</title>
    <description>The latest articles on DEV Community by Richard Hainsworth (@finanalyst).</description>
    <link>https://dev.to/finanalyst</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%2F1092941%2Fc6ef2a70-407f-4727-a008-1daab9100ab2.png</url>
      <title>DEV Community: Richard Hainsworth</title>
      <link>https://dev.to/finanalyst</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/finanalyst"/>
    <language>en</language>
    <item>
      <title>Enumerating in RakuDoc v2</title>
      <dc:creator>Richard Hainsworth</dc:creator>
      <pubDate>Sun, 15 Feb 2026 11:31:14 +0000</pubDate>
      <link>https://dev.to/finanalyst/enumerating-in-rakudoc-v2-5d8j</link>
      <guid>https://dev.to/finanalyst/enumerating-in-rakudoc-v2-5d8j</guid>
      <description>&lt;p&gt;We needed a legal document with numbered &lt;em&gt;Articles&lt;/em&gt;, and the document needed to be in MarkDown for github issues, and in html, so naturally the source should be in RakuDoc.&lt;/p&gt;

&lt;p&gt;Although numbered headings and items were specified in RakuDoc v1 (aka POD6), they were never implemented. In RakuDoc v2 there was progress, and together with some tweaking of the &lt;em&gt;numitem&lt;/em&gt; templates, it was easy to write the RakuDoc source.&lt;/p&gt;

&lt;p&gt;However, when we were revising RakuDoc, Damian Conway had some extra ideas about generalising enumeration, including ideas about adding alias definitions so that the numbered block could be referenced later in the text.&lt;/p&gt;

&lt;p&gt;Since it had been so easy to tweak the &lt;em&gt;numitem&lt;/em&gt; templates, I thought it would be easy upgrade the specification of RakuDoc v2 to get these generalisations. Was I ever so wrong!!!! Ask a genius with decades of language design experience for a nice design and you get an effusion of ideas and extensions that make RakuDoc better than any editor I have ever used, but with a simplicity that makes it easy for a document author to understand.&lt;/p&gt;

&lt;p&gt;Another result of the redesign is to make the underlying specification of RakuDoc much clearer, something I will cover later.&lt;/p&gt;

&lt;p&gt;During the development process, my daughter mentioned that her friend had just finished a PhD dissertation and was complaining about how much time she needed to spend reformatting the text because the numbering kept getting out of sync. The problem with most editors is that most of the effort goes on perfecting the user-facing interface, while the underlying format is created &lt;em&gt;ad hoc&lt;/em&gt;, and enumeration is an addition. Getting the underlying structure right will make subsequent rendering easier. &lt;/p&gt;

&lt;p&gt;Just as the design was ending and the renderer passing most tests, I casually mentioned citations would be a good extension. The "standards" for citations number in the thousands! .oO(The &lt;a href="https://www.owleyes.org/text/jabberwocky/read/text-poem#root-22123-1" rel="noopener noreferrer"&gt;Jabberwocky&lt;/a&gt; ) But Damian &lt;em&gt;snicker-snacked&lt;/em&gt; his &lt;em&gt;vorpal blade&lt;/em&gt;, reducing the monstrous tangle to something easier to use. RakuDoc v2 now has a &lt;code&gt;=citation&lt;/code&gt; block and &lt;code&gt;Q&amp;lt;&amp;gt;&lt;/code&gt; markup to insert quoted citations. I will cover these additions in the next blog (as in: when I get the renderer to work with the new ideas).&lt;/p&gt;

&lt;p&gt;Back to enumerating RakuDoc, here are some examples to illustrate the new functionality.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Suppose you have a formula (or code or map or table or item in a list) and you want to number it, and then reference the number in the text? Then another formula gets added into the text before, well you would want the references to update as  well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Suppose you have tables that you want to be enumerated separately from headings? But also by preference you want them to be enumerated in sequence with headings? That is the prefix to the table enumeration is the heading number.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Suppose you want Chinese, Roman, or Bengali numbering?&lt;br&gt;
AND suppose you also want the numbering to be a mix of numerals and other characters, such as brackets? This is a common requirement in legal documents.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Suppose you want Arbitrary words before after or in between the numbers? For example, &lt;em&gt;Article 1.&lt;/em&gt;, &lt;em&gt;Article 2.&lt;/em&gt; etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Suppose you want to have the paragraphs numbered? Sometimes you might want the paragraphs numbered in sequence from the start of the document, and sometimes you want the numbering to restart after a new heading.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Suppose you want to have the Tables and the Formulae numbered in sequence? Or perhaps, for a section that explains some aspect of one formula, you want to number the formulae separately, but then return to the original sequence after that section?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The updated RakuDoc v2 allows for all of these possibilities, while also providing for sensible default option values.&lt;/p&gt;

&lt;p&gt;Moreover, RakuDoc allows for custom blocks, eg. LeafletMap - a map block that exposes the marvelous Leaflet library. Now it is an automatic part of the specification that prefixing a custom block with &lt;strong&gt;num&lt;/strong&gt; (numLeafletMap) will number the caption of the block.&lt;/p&gt;

&lt;p&gt;An HTML rendering of the new RakuDoc v2 specification containing the enumerated functionality can be found at &lt;a href="https://htmlpreview.github.io/?https://github.com/Raku/RakuDoc-GAMMA/blob/numalias-clarification/rakudoc_v2.html" rel="noopener noreferrer"&gt;RakuDoc enumeration branch&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the RakuDoc paradigms
&lt;/h2&gt;

&lt;p&gt;In the specification of RakuDoc, there are discussions about &lt;em&gt;directives&lt;/em&gt;, &lt;em&gt;blocks&lt;/em&gt;, &lt;em&gt;metaoptions&lt;/em&gt;, and a section on &lt;code&gt;=config&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since the syntax for a &lt;em&gt;directive&lt;/em&gt; is &lt;strong&gt;almost&lt;/strong&gt; the same as the syntax of a &lt;em&gt;block&lt;/em&gt;, it is not immediately apparent what the difference is. As we developed the generic enumeration, it became much clearer what the difference is. &lt;/p&gt;

&lt;p&gt;Furthermore, the older specification covered multi-level headings and items. This functionality is now extended to all blocks, so &lt;code&gt;=numtable2&lt;/code&gt; or &lt;code&gt;=numcode3&lt;/code&gt; will be numbered in parts from the previous instance of &lt;code&gt;=table&lt;/code&gt; (the equivalent of &lt;code&gt;table1&lt;/code&gt;) or &lt;code&gt;=code&lt;/code&gt;. This means that we need to carefully distinguish between the &lt;strong&gt;block base&lt;/strong&gt; (eg., &lt;code&gt;table&lt;/code&gt; or &lt;code&gt;code&lt;/code&gt;), and the &lt;strong&gt;block level&lt;/strong&gt; (eg., 1, 2, 3). Together the &lt;em&gt;base&lt;/em&gt; and the &lt;em&gt;level&lt;/em&gt; create a &lt;strong&gt;blocktype&lt;/strong&gt;. These distinctions, although implied, were not so clear in the older specification.&lt;/p&gt;

&lt;p&gt;Let's return to the &lt;code&gt;=config&lt;/code&gt; directive. The first parameter of &lt;code&gt;=config&lt;/code&gt; is the name of a &lt;em&gt;blocktype&lt;/em&gt;. Note that the &lt;code&gt;num&lt;/code&gt; prefix is not a part of the &lt;em&gt;blocktype&lt;/em&gt; and only indicates that the enumeration associated with the instance needs to be rendered. Consequently, the presence or absence of 'num' in the config parameter has no significance. The next &lt;code&gt;=config&lt;/code&gt; parameters are all, and may only be, metadata options. &lt;/p&gt;

&lt;p&gt;The function of the &lt;code&gt;=config&lt;/code&gt; directive is to &lt;em&gt;distribute&lt;/em&gt; the named metadata options, and their values, to the named blocktype. In addition, the same metadata option can be specified on a blocktype instance (using either the &lt;code&gt;=for&lt;/code&gt; or &lt;code&gt;=begin&lt;/code&gt; directives), in which case it takes precedence over the option in the config. A consequence of this paradigm is that each metadata option has to have a semantic significance in the context of a blocktype instance.&lt;/p&gt;

&lt;p&gt;One of the design aims was to give a document author the choice of restarting the enumeration for some blocktype when another blocktype is encountered. For example, the original specfication for &lt;code&gt;=numitem&lt;/code&gt; included the idea that whenever a sequence of &lt;code&gt;=numitem&lt;/code&gt;s was encountered, they would form an ordered set, and that if another block, such as a paragraph, was encountered, the enumeration would be restarted.&lt;/p&gt;

&lt;p&gt;This means that the occurrence of a &lt;code&gt;=head&lt;/code&gt; restarts the &lt;code&gt;item&lt;/code&gt; counter. This cannot be controlled within the handler of a block. Consequently, any option affecting block counting needs to be distinct from the rendering of the blocks themselves. &lt;/p&gt;

&lt;p&gt;After some design iterations, a new &lt;em&gt;directive&lt;/em&gt; called &lt;code&gt;=counter&lt;/code&gt; was introduced. A directive, as opposed to a block, affects all subsequent blocks in the RakuDoc source, within the same scope. A block, and the metadata options operating on the blocktype, only affects its immediate contents.&lt;/p&gt;

&lt;p&gt;Further, it became clear that a block and its counter were different objects, although for simplicity they have the same name. For most needs, an author does not need to know that a counter and a block are different, except that when a new counter is needed, the difference becomes necessary. In a similar way, for many purposes it doesn't matter whether a number is an integer or a string, except when it does. Raku allows for things to become complicated when the author needs it to be.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to experiment with enumerations?
&lt;/h2&gt;

&lt;p&gt;To make it easier to experiment with RakuDoc and the enumerated functionality, I have developed a Docker image called &lt;code&gt;browser-editor&lt;/code&gt;. It is based on an Alpine image and contains &lt;em&gt;raku&lt;/em&gt;, &lt;em&gt;Cro&lt;/em&gt;, and the latest version of &lt;code&gt;Rakuast::RakuDoc::Render&lt;/code&gt; (currently not yet in the fez system).&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;podman&lt;/code&gt;, the image can be put into a container and run locally on Linux based systems, thus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;podman pull docker.io/finanalyst/browser-editor:latest
podman run -d -v .:/browser/publication --rm -name rb docker.io/finanalyst/browser-editor:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Mac silicon, a small change is needed to indicate the platform inside the docker image is based on Linux, thus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;podman run -d -v .:/browser/publication --rm --platform linux/amd64 --name rb docker.io/finanalyst/browser-editor:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In both cases the container is given the arbitrary name &lt;code&gt;rb&lt;/code&gt;, and so when it is time to stop the container, the following is sufficient:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;podman stop rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The directory that the container is started from will then linked to the container's &lt;code&gt;/browser/publication&lt;/code&gt; directory, and changes and new RakuDoc source files will be saved in that directory.&lt;/p&gt;

&lt;p&gt;A RakuDoc source can then be edited and the HTML rendering is created on the fly, by pointing a browser at (setting the browser URL to) &lt;code&gt;localhost:3000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You should see something like the following in the browser:&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%2Fk3x5jvweu1l0awig8sww.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%2Fk3x5jvweu1l0awig8sww.png" alt=" " width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two frameworks exist: one needing no internet connection - a minimal single file rendering, and another that uses plugins to expose some useful third-party libraries and the more sophisticated Bulma CSS framework. The choice is toggled using the 'online' button.&lt;/p&gt;

&lt;p&gt;Try selecting 'online' and copying in the following test source by Damian Conway to see some different effects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=begin rakudoc
=TITLE Taster source
=numitem One
=numitem Two
=counter item :restart(42)
=numitem Three
=counter item :restart
=numitem Four
=counter item :prefix&amp;lt;head3&amp;gt;
=numitem Five
=counter item :!restart
=para ???
=numitem Six

=numpara
S&amp;lt;Now is the winter of our "no content"
Made glorious summer by Camelia’s bloom;
And all backlog that lour’d upon our docs
In deep commits is buried, link’d, and tagged.
Our nightly builds now wear triumphant green,
Our failing tests to passing smiles are turn’d;
Grim sighs of “ere next Yule” have chang’d to grins,
And hacker dread to blog posts boldly strung.&amp;gt;

=numpara
S&amp;lt;But I—long nurs’d on RFCs and hope,
Deform’d by specs that shifted as I read,
Unfit for idle scripts or stable sleep—
Am set, since long delays are now no more,
To ship Raku...and break the world anew.&amp;gt;

=numcode
$x = any &amp;lt;1 2 3&amp;gt;;

=for numcode :caption&amp;lt;Same thing, just labelled&amp;gt;
$x = any &amp;lt;1 2 3&amp;gt;;


=for numformula :caption&amp;lt;This means nothing!&amp;gt;
x^2 = y_j + \sum z_i

=for numformula :caption&amp;lt;This means nothing!&amp;gt; :alt&amp;lt; xH&amp;lt;2&amp;gt; = yJ&amp;lt;1&amp;gt; + E&amp;lt;GREEK CAPITAL LETTER SIGMA&amp;gt; zJ&amp;lt;i&amp;gt; &amp;gt;
x^2 = y_j + \sum z_i

=for numformula :alt&amp;lt; (1+x)H&amp;lt;n&amp;gt; = E&amp;lt;GREEK CAPITAL LETTER SIGMA&amp;gt; H&amp;lt;n&amp;gt;CJ&amp;lt;i&amp;gt; xH&amp;lt;i&amp;gt; &amp;gt; :caption&amp;lt;This is actually right&amp;gt;
(1+x)^n = \sum_{i=0}^n {n \choose i} x^i

=for numinput
Hey type something:

=numoutput
You typed: hsdkhldkhskdhasd

=counter numtable :restart(33456)
=begin numtable :caption&amp;lt;Encryption table&amp;gt; :form&amp;lt;Example %R: %C:pc&amp;gt;
=row
=cell A
=cell B
=cell C
=row
=cell D
=cell E
=cell F
=end numtable

=end rakudoc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should look like the following (or at least the top part).&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%2Fjz5as2qt1ds7w6hlqmep.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%2Fjz5as2qt1ds7w6hlqmep.png" alt=" " width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The example shows some features like numbering paragraphs, code examples and tables. But you will also see the difference between the two rendering contexts. The online version has access to an online latex rendering resource, so the formula are nicely rendered, whilst the off-line version only renders to the raw formula or a character-based alternative - if provided.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coding the examples in the introduction
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Adding an alias to a block
&lt;/h3&gt;

&lt;p&gt;Suppose we want to enumerate a code sample and then refer to it later, we can do the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=begin rakudoc :!toc
=TITLE Enumerations
=for numcode :numalias&amp;lt;SAY_EX&amp;gt; :lang&amp;lt;raku&amp;gt;
my %h = &amp;lt;one two three&amp;gt; Z=&amp;gt; ^3;
say %h;

=numoutput {one =&amp;gt; 0, three =&amp;gt; 2, two =&amp;gt; 1}

=for numcode :numalias&amp;lt;PUT_EX&amp;gt; :lang&amp;lt;raku&amp;gt;
my %h = &amp;lt;one two three&amp;gt; Z=&amp;gt; ^3;
put %h;

=for numoutput                                                                                                                                    
one     0
three   2
two     1

The difference between A&amp;lt;SAY_EX&amp;gt; and A&amp;lt;PUT_EX&amp;gt; is that C&amp;lt;say&amp;gt; and C&amp;lt;put&amp;gt; use different methods to convert the C&amp;lt;%h&amp;gt; structure into printable strings.
=end rakudoc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the browser-editor image, we will get an HTML rendering something like:&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%2Fk0txyosqpbehs2loi324.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%2Fk0txyosqpbehs2loi324.png" alt=" " width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;=begin rakudoc :!toc&lt;/code&gt; and &lt;code&gt;=end rakudoc&lt;/code&gt; are used to &lt;em&gt;top and tail&lt;/em&gt; the RakuDoc example above because the HTML renderer expects a complete Raku program, and a RakuDoc source (currently) needs the RakuDoc to be within a &lt;code&gt;=rakudoc&lt;/code&gt; block. The &lt;code&gt;:!toc&lt;/code&gt; switches off the automatic Table of Contents that the HTML utility of the &lt;code&gt;Rakuast::RakuDoc::Render&lt;/code&gt; distribution generates. (Try removing &lt;code&gt;:!toc&lt;/code&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Prefixing an enumeration with another counter
&lt;/h3&gt;

&lt;p&gt;Suppose we want out tables to have enumerations that align with the enumerations of the headings. So this means we are requiring a different sort of behaviour from the counter associated with the &lt;em&gt;table&lt;/em&gt; block base. Consequently, we need to use the &lt;code&gt;=counter&lt;/code&gt; directive. Also note that we are prefixing with the &lt;code&gt;head2&lt;/code&gt; block, which has an implicit prefix of &lt;code&gt;head1&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;=begin rakudoc :!toc
=TITLE Prefixes
=counter table :prefix&amp;lt;head&amp;gt;
=numhead First title
=numhead2 Sub first title
=for numtable :caption&amp;lt;First table&amp;gt;
| one | two |
=for numtable :caption&amp;lt;Second table&amp;gt;
| three | four |
=numhead Second Title
=counter table :restart(6)
=for numtable :caption&amp;lt;Third table&amp;gt;
| five | six | seven | eight |
=for numtable2 :caption&amp;lt;Subordinate table&amp;gt;
| nine | ten |
| nine | ten |
=end rakudoc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Comments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A table must have some content, and the 'visual' table format is the minimum possible&lt;/li&gt;
&lt;li&gt;In order to make the prefix numbering a bit clearer, I manually restarted the &lt;code&gt;table&lt;/code&gt; counter to &lt;code&gt;6&lt;/code&gt; before &lt;em&gt;Third table&lt;/em&gt;. &lt;/li&gt;
&lt;/ul&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%2Fm3sj41l703ospwnwfq3u.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%2Fm3sj41l703ospwnwfq3u.png" alt=" " width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Enumerations with different numbering systems
&lt;/h3&gt;

&lt;p&gt;To illustrate the multi-lingual ability of RakuDoc (and Raku in general), lets use Chinese, Roman and Bengali for multilevel headings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=begin rakudoc :!toc
=TITLE Some non-Arabic numerals
=config head :form&amp;lt; %Z %D &amp;gt;
=config head2 :form&amp;lt; ｢%Z｣%R %D &amp;gt;
=config head3 :form&amp;lt; ｢%Z｣%R｢%B｣ %D &amp;gt;
=numhead First title
=numhead Second title
=numhead3 Sub-sub-head one (implies the sub-head)
=numhead3 Sub-sub-head two
=numhead2 Sub-head first explicit
=numhead2 Sub-head second explicit
=for numformula2 :form&amp;lt;%T %Z.%R. &amp;gt;
\begin{align*}
\sum_{i=1}^{k+1} i^{3}
&amp;amp;= \biggl(\sum_{i=1}^{n} i^{3}\biggr) +  i^3\\
&amp;amp;= \frac{k^{2}(k+1)^{2}}{4} + (k+1)^3 \\
\end{align*}
=end rakudoc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will produce an HTML rendering a bit like&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%2F74qul9kyvvuv3ar92cyl.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%2F74qul9kyvvuv3ar92cyl.png" alt=" " width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Including arbitrary text
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;:numform&lt;/code&gt; option is very flexible and in fact the brackets in the previous example are arbitrary text. The following RakuDoc source&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=begin rakudoc :!toc
=TITLE Arbitrary text in enumeration
=config head :form&amp;lt; Article %N. %D &amp;gt;
=config head2 :form&amp;lt; Article %N.%r. %D &amp;gt;
=config head3 :form&amp;lt; Article %N.%r｢%Z｣ %D &amp;gt;
=numhead First title
=numhead Second title
=numhead3 Sub-sub-head one (implies the sub-head)
=numhead3 Sub-sub-head two
=numhead2 Sub-head first explicit
=numhead2 Sub-head second explicit
=end rakudoc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;will produce something like:&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%2Fe170xc86qal4m176m1cz.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%2Fe170xc86qal4m176m1cz.png" alt=" " width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Numbering paragraphs
&lt;/h3&gt;

&lt;p&gt;In RakuDoc, paragraphs as in all text editors, are strings of text ending in a blank line or a new block (for RakuDoc). There is no need to mark a paragraph block, but it will be equivalent to a block marked &lt;code&gt;=para&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;By default, each paragraph is numbered from the start of the document. In order to see the enumeration, the paragraph does need to be started with a &lt;code&gt;=numpara&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For this example, we want to number paragraphs in each section marked with a heading. So we need to tell the counter which conditions it is restarted by; in this case after every &lt;code&gt;=head&lt;/code&gt; (by default a bare block name has a level of 1). There are both &lt;code&gt;:restart-after&lt;/code&gt; and &lt;code&gt;:restart-except-after&lt;/code&gt; conditions, but that's all we'll say about them here.&lt;/p&gt;

&lt;p&gt;The RakuDoc source is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=begin rakudoc :!toc
=TITLE Restarting after headings
=counter para :restart-after&amp;lt;head&amp;gt;
=head First heading
=numpara First line
=numpara Second line
=numpara Third line
=head Second heading
=numpara First line 2
=numpara Second line 2
=head2 Subordinate heading does not restart the paragraphs
=numpara Third line 2
=numpara Fourth para
=end rakudoc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source will yield something like:&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%2Fex646enjfrotdchyiphj.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%2Fex646enjfrotdchyiphj.png" alt=" " width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom counters and block scopes
&lt;/h3&gt;

&lt;p&gt;We want to number code and formulae using the same counter, but also we want to explain some formula with a special numbering that is then &lt;em&gt;forgotten&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Since all &lt;code&gt;=config&lt;/code&gt; and &lt;code&gt;=counter&lt;/code&gt; statements are scoped, all we need to do to get the special numbering is to create a &lt;code&gt;=section&lt;/code&gt;, which introduces a new block scope. The previous config/counter options continue, unless explicitly overridden.&lt;/p&gt;

&lt;p&gt;However, it would not be useful if upon exiting a section, the numbering of a counter reverts to the value before the section. So there is a subtle distinction between the &lt;em&gt;counter&lt;/em&gt; and the value of the count it contains. The &lt;em&gt;counter&lt;/em&gt; options, such as its prefix, and when it is restarted, are scoped, but the value of the counter is not scoped. In order to reset the counter value, the counter has to be reset.&lt;/p&gt;

&lt;p&gt;RakuDoc v2 clarified the idea of custom blocks. Previously it was implied that developers could have their own blocks, but in V.2 the difference between &lt;em&gt;built in&lt;/em&gt; and &lt;em&gt;custom&lt;/em&gt; blocks was clarified: custom blocks must contain a mix of upper and lower case letters (more precisely characters with the Unicode properties &lt;em&gt;Lu&lt;/em&gt; and &lt;em&gt;Ll&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;The enumeration options use this idea by creating custom counters. Since this is something that managed by a block, the &lt;code&gt;:counter&lt;/code&gt; option is provided to the appropriate blocks using a &lt;code&gt;=config&lt;/code&gt; directive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=begin rakudoc :!toc
=TITLE Using custom counnters
=config formula :counter&amp;lt; FormulaeTables &amp;gt;
=config table :counter&amp;lt; FormulaeTables &amp;gt;
=numhead First title
=numtable 
| one | two | three |
=numtable
| more | tabula data |
| 5 | 6 |
| 7 | 8 |
=numformula E = mc^2
=numformula e^{\pi i} = -1

=begin section
=config table :counter&amp;lt; Derivation &amp;gt; :form&amp;lt;Aside: %T %N&amp;gt;

=numhead2 Some explanation
=numtable
| this | data | is | more | detailed |
| 1 | 2 | 3 | 4 | 5 |
=end section

=numhead Continuing
=numtable
| here | we | resume |
=end rakudoc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will produce something like&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%2Filgn2tm7d2yar3ta8dkm.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%2Filgn2tm7d2yar3ta8dkm.png" alt=" " width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Afterword
&lt;/h2&gt;

&lt;p&gt;We have only just completed the revised specification, and the Renderer has only just been developed. It is inevitable that there will be flaws, and the styling choices may not be liked by everyone. &lt;/p&gt;

&lt;p&gt;Even so, we think this new upgrade of RakuDoc v2 will prove to have a much wider applicability than just a documentation aid to Raku programs.&lt;/p&gt;

&lt;p&gt;The docker image allows anyone to experiment with the new RakuDoc functionality immediately without needing to install the whole Raku / Cro / Rakuast::RakuDoc::Render stack and some dependencies, such as Dart Sass, locally.&lt;/p&gt;

</description>
      <category>rakulang</category>
      <category>rakudoc</category>
    </item>
    <item>
      <title>Create a minimal site with Elucid8</title>
      <dc:creator>Richard Hainsworth</dc:creator>
      <pubDate>Tue, 02 Sep 2025 21:59:43 +0000</pubDate>
      <link>https://dev.to/finanalyst/create-a-minimal-site-with-elucid8-1gf8</link>
      <guid>https://dev.to/finanalyst/create-a-minimal-site-with-elucid8-1gf8</guid>
      <description>&lt;p&gt;The Elucid8 system can be used to create websites based on RakuDoc. The article is to show how to create a minimal website.&lt;/p&gt;

&lt;p&gt;Elucid8 ("elucidate") is still being developed, and more information can be found in the &lt;a href="https://github.com/elucid8-org" rel="noopener noreferrer"&gt;Github elucid8-org repositories&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The following need to be present:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A recent version of Rakudo v.2025.01 or later.&lt;/li&gt;
&lt;li&gt;Dart sass - &lt;a href="https://sass-lang.com/dart-sass/" rel="noopener noreferrer"&gt;see Sass website&lt;/a&gt; for installation instructions. (It will be used by a Elucid8 plugin, so it should be globally visible)&lt;/li&gt;
&lt;li&gt;Elucid8::Build - &lt;code&gt;zef install Elucid8::Build&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Elucid8::Run-locally - &lt;code&gt;zef install Elucid8::Run-locally&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Assuming that the &lt;code&gt;zef&lt;/code&gt; installs &lt;code&gt;bin/&lt;/code&gt; files to a location in the PATH, then the utilities in the next section should run without problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up minimal site
&lt;/h2&gt;

&lt;p&gt;In an empty directory (to be concrete, lets call it &lt;code&gt;webdir&lt;/code&gt;), which will be the root for the website build, run the following&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;elucid8-setup&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gather-sources&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;elucid8-build&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;run-locally&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then point a browser at &lt;code&gt;localhost:5000&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That is all for the minimum site.&lt;/p&gt;

&lt;p&gt;Now change the file &lt;code&gt;site-source/en/index.rakudoc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then run &lt;code&gt;elucid8-build; run-locally&lt;/code&gt; again to see the changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Explanation of steps
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Step 1 Minimal files
&lt;/h4&gt;

&lt;p&gt;Here &lt;em&gt;elucid8-setup&lt;/em&gt; copies resources in the &lt;code&gt;Elucid8::Build&lt;/code&gt; distribution to create the minimal configuration entries in &lt;code&gt;webdir&lt;/code&gt;,&lt;br&gt;
a sample text and some minimal plugins.&lt;/p&gt;

&lt;p&gt;The directory structure under &lt;code&gt;webdir&lt;/code&gt; will be something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- config/
  - 01-base.raku
  - 02-plugins.raku
  - 03-plugin-options.raku
  - 04-repos
- misc/
- site-sources/
  - en/
    - index.rakudoc
    - examples.rakudoc

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Since Elucid8 is being built from the bottom up to be multi-lingual, it is intended that all of these directories can be named in a local variation. The file &lt;code&gt;config/01-base.raku&lt;/code&gt; contains the tokens that are used within &lt;em&gt;Elucid8::Build&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Step 2. Gathering sources
&lt;/h4&gt;

&lt;p&gt;It is intended that each set of language sources will be in a different repo. &lt;code&gt;gather-sources&lt;/code&gt; looks at &lt;code&gt;config/04-repos&lt;/code&gt; for files and repo information, clones repositories, runs &lt;code&gt;git blame&lt;/code&gt; against them and stores a file description in &lt;code&gt;misc/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After this step, &lt;code&gt;website&lt;/code&gt; will contain the file &lt;code&gt;misc/repo-info.rakuon&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For the minimal website, the RakuDoc v2 specification is pulled from the Raku repository. This document also shows many of the features of RakuDoc.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;config/04-repos&lt;/code&gt; shows an example of how to map documents from the repository to subdirectories within &lt;code&gt;publication&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3 Build
&lt;/h4&gt;

&lt;p&gt;At this step, the build process starts. A processor engine is created that uses the plugins described in &lt;code&gt;config/02-plugins&lt;/code&gt;. These are run with options described in &lt;code&gt;config/03-plugin-options&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After this step, &lt;code&gt;misc/&lt;/code&gt; also contains &lt;code&gt;ui-dictionary.rakuon&lt;/code&gt;, which will have the English version of the UI tokens. When this dictionary is appropriately edited, other languages will be available.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Elucid8 makes a distinction between the language of the UI and the language of the contents.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A new directory &lt;code&gt;publication/&lt;/code&gt; has now appeared, and this will contain the HTML version of the index file. &lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4
&lt;/h3&gt;

&lt;p&gt;At this step, a Cro app is run that takes the HTML in &lt;code&gt;publication&lt;/code&gt; and serves the files to &lt;code&gt;localhost:3000&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customisation
&lt;/h2&gt;

&lt;p&gt;There are many ways to customise the site:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;make sure to change &lt;code&gt;config/03-plugin-options&lt;/code&gt; and the &lt;code&gt;root-domain&lt;/code&gt; field of SiteMap, so that the SEO map points to your website.&lt;/li&gt;
&lt;li&gt;adding RakuDoc sources, remember to add links in index.rakudoc&lt;/li&gt;
&lt;li&gt;adding plugins to create new RakuDoc blocks&lt;/li&gt;
&lt;li&gt;use the ListFiles block to automatically include in &lt;code&gt;index.rakudoc&lt;/code&gt; all the other RakuDoc sources in your &lt;code&gt;local-sources/&lt;/code&gt; directory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples of what can be done can be seen in &lt;a href="https://github.com/elucid8-org/sandpit" rel="noopener noreferrer"&gt;Sandpit repo, for Raku documentation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rakulang</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A coordinated dance to identify the editor</title>
      <dc:creator>Richard Hainsworth</dc:creator>
      <pubDate>Fri, 25 Apr 2025 14:47:44 +0000</pubDate>
      <link>https://dev.to/finanalyst/a-coordinated-dance-to-identify-the-editor-2h4b</link>
      <guid>https://dev.to/finanalyst/a-coordinated-dance-to-identify-the-editor-2h4b</guid>
      <description>&lt;h2&gt;
  
  
  Adding login logic - rationale
&lt;/h2&gt;

&lt;p&gt;In a &lt;a href="https://dev.to/finanalyst/editing-rakudoc-croing-it-to-github-1507"&gt;previous article&lt;/a&gt;, I described how to take a web page source file, edit it, then send the edited source back to Github.&lt;/p&gt;

&lt;p&gt;In that post, the authorisation token &lt;code&gt;id-token&lt;/code&gt; was one I generated for myself as a Github user. It would also be possible to assign to the &lt;code&gt;hallowed glade&lt;/code&gt; (see previous article) an authorisation for a pseudo user or bot specifically set up to take user generated editing.&lt;/p&gt;

&lt;p&gt;However, when the edit is merged into the source, the author of the commit would be the bot, and not the author of the edit.&lt;/p&gt;

&lt;p&gt;Suppose, though, we want a 'credits' page listing authors and the number of commits they have made. We could run a command in the repo such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git log --pretty="%an %n" | sort | uniq -c | sort -n -r
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and filter the results into a table. But authors who have entered our &lt;em&gt;hallowed glade&lt;/em&gt; will not be listed.&lt;/p&gt;

&lt;p&gt;Another concern is spamming. If we require a community user to have a Github identity, then Github will handle authorisation. If there is spamming by a &lt;em&gt;forest troll&lt;/em&gt;, it is in Github's best interest to hold that troll to account.&lt;/p&gt;

&lt;p&gt;This post explains (mostly to myself) the steps that are needed to insert a login layer.&lt;/p&gt;

&lt;p&gt;When I finally got the whole thing working, it was like a weird dance between three different entities. Looking at the documentation for the first time, all the steps seemed odd and unrelated, but once they were all put into motion, there is a logic to the whole thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;We already have a &lt;em&gt;hallowed glade&lt;/em&gt;, which is the &lt;code&gt;suggestion_box&lt;/code&gt; server. The server runs in a docker container, and has a Cro server to handle input from a websocket, a Cro client to interact with Github to create a new branch, store the edited file, and raise a Pull Request.&lt;/p&gt;

&lt;p&gt;Github allows the owner of a repo to register a 'Github app' and assign it permissions. So, the token granted during the authorisation process is a combination of the permissions of the &lt;em&gt;app&lt;/em&gt; and those of a user.&lt;/p&gt;

&lt;p&gt;It is always scary for a user to have to authorise someone else to do something on their behalf, so here is what Github says about the authorisation token:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A token has the same capabilities to access resources and perform actions on those resources that the owner of the token has, and is further limited by any scopes or permissions granted to the token. A token cannot grant additional access capabilities to a user. &lt;a href="https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-with-a-github-app-on-behalf-of-a-user" rel="noopener noreferrer"&gt;Github authorisation documentation&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Coordination
&lt;/h2&gt;

&lt;p&gt;Here is diagram of the interaction between the browser, the server (in our case &lt;em&gt;suggestion_box&lt;/em&gt;) and Github from a great &lt;a href="https://medium.com/@tony.infisical/guide-to-using-oauth-2-0-to-access-github-api-818383862591" rel="noopener noreferrer"&gt;article by Tony on OAuth2&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%2Fdmv1ptby80ryytxlhfg0.webp" 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%2Fdmv1ptby80ryytxlhfg0.webp" alt="Browser/Server/Github" width="720" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Suppose we add in the request that if an editor wants to make edits on several documents (which means the page is refreshed), then it is inefficient for Github to generate an &lt;em&gt;id-token&lt;/em&gt; for each page.&lt;/p&gt;

&lt;p&gt;We can keep an object in local storage with the name of the editor and the time the first submission is accepted.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that Github issues its web tokens for a fixed period of time, by default 8 hours.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A pre-requisite is that the &lt;code&gt;Github app&lt;/code&gt;, which is our &lt;em&gt;suggestion-box&lt;/em&gt; is already registered with Github.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps
&lt;/h2&gt;

&lt;p&gt;The brief laid out above suggests the following series of steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When a submission is made, the &lt;code&gt;editor&lt;/code&gt; field in the submission form must be consistent with Github rules:

&lt;ul&gt;
&lt;li&gt;Github has a user name policy of only alphanumeric characters + &lt;code&gt;-&lt;/code&gt;, with a minimum of three characters and a maximum of 39. &lt;/li&gt;
&lt;li&gt;Letters are case insensitive and other characters are replaced by &lt;code&gt;-&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The browser checks to see if the &lt;em&gt;editor&lt;/em&gt; has made a successful submission within the last 8 hours, and that 10 minutes is still left.

&lt;ul&gt;
&lt;li&gt;if there is no or insufficient time, the local storage is deleted.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If the &lt;em&gt;editor&lt;/em&gt; has not been verified, the &lt;em&gt;editor&lt;/em&gt; is sent to the Github login page, together with a &lt;code&gt;state&lt;/code&gt; string that contains information for the &lt;em&gt;suggestion_box&lt;/em&gt; to be able to issue a &lt;code&gt;successful submission&lt;/code&gt; message back to the browser.&lt;/li&gt;
&lt;li&gt;Whether or not the &lt;em&gt;editor&lt;/em&gt; has been verified, and in parallel with the Github verification, the suggestion information is sent to the &lt;em&gt;suggestion_box&lt;/em&gt; as outlined in my previous article.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Inside the &lt;em&gt;suggestion_box&lt;/em&gt; server:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;em&gt;editor&lt;/em&gt; is verified to see whether an &lt;em&gt;id-token&lt;/em&gt; with enough time (10 minutes) is available. If so, the suggestion is queued, and a successful message is sent back together with the remaining time on the token. &lt;/li&gt;
&lt;li&gt;If there is not enough time left, a failure message is sent back from the server to the browser.&lt;/li&gt;
&lt;li&gt;If there is no &lt;em&gt;id-token&lt;/em&gt; and ten minutes have passed after the suggestion was received with no message from Github, a failure message is sent back to the browser.&lt;/li&gt;
&lt;li&gt;When a message is received from Github, it is compared with waiting suggestions. If there is not a match between the &lt;em&gt;state&lt;/em&gt; field of a suggestion, the Github message is ignored. &lt;/li&gt;
&lt;li&gt;If the &lt;em&gt;state&lt;/em&gt; matches a waiting suggestion, the &lt;em&gt;suggestion-box&lt;/em&gt; server starts the process to get a &lt;em&gt;id-token&lt;/em&gt;. If successful, the suggestion is queued and a message is sent back to the browser containing the success and the expiration time.

&lt;ul&gt;
&lt;li&gt;if unsuccessful, a failure message is sent back to the browser&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this scheme,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the browser &lt;strong&gt;never&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;sees&lt;/em&gt;&lt;/strong&gt; the editor's &lt;em&gt;id-token&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;the actual name provided by the editor in the suggestion form does not have to be the Github name of the editor because the Github authorisation is independent of the &lt;em&gt;editor's&lt;/em&gt; name, but future submissions must use the same editor name to use the &lt;em&gt;id-token&lt;/em&gt;, which is time-limited in any case.&lt;/li&gt;
&lt;li&gt;an editor may edit several files in one session.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Cro setup
&lt;/h2&gt;

&lt;p&gt;In the previous post, the Cro app had a route for the &lt;em&gt;websocket&lt;/em&gt; and a Client section to handle putting suggestions into Github. &lt;/p&gt;

&lt;p&gt;We need to modify the Client section to use the &lt;em&gt;editor's&lt;/em&gt; &lt;em&gt;id-token&lt;/em&gt;, but essentially it remains the same.&lt;/p&gt;

&lt;p&gt;We also need to add a route which is used by Github to send login information to the server. In addition, there needs to be a way for the webserver route and the authorisation route to interact. &lt;/p&gt;

&lt;p&gt;These modifications imply a shared resource to match &lt;em&gt;editor-name&lt;/em&gt;, &lt;em&gt;id-token&lt;/em&gt;, &lt;em&gt;id-token&lt;/em&gt; expiration date. Since Cro assumes concurrency, the storage has to be thread-safe and ensure that only one thread at a time can access it. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Although the Cro documentation uses &lt;code&gt;OO::Monitor&lt;/code&gt; for this purpose, I prefer the simpler &lt;code&gt;Method::Protected&lt;/code&gt; module. The &lt;code&gt;is protected&lt;/code&gt; trait ensures that only one thread at a time can access the shared resource. For example,&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Method::Protected;
class Editor-Store {
    #| has key = editor, with two attributes :token and :time (a Date::Time)
    has %!storage;
    #| if False, makes sure editor key is deleted
    method is-editor-active( $editor --&amp;gt; Bool) is protected {
        return False unless %!storage{ $editor }:exists;
        return True if %!storage{ $editor }&amp;lt;expiration&amp;gt; &amp;gt; (now.DateTime + Duration.new(10 * 60));
        sink %!storage{$editor}:delete;
        return False
    }
    #| if there is no editor, an emptry string is returned
    method get-token( $editor --&amp;gt; Str) is protected {
        if self.is-editor-active( $editor ) { %!storage{$editor}&amp;lt;token&amp;gt; }
        else { '' }
    }
    #| expiration date
    method expiration( $editor ) is protected { %!storage{$editor} }
    #| returns the remaining time in seconds as an integer
    method time-remaining( $editor --&amp;gt; Int ) is protected {
        if self.is-editor-active( $editor ) { ( %!storage{ $editor }&amp;lt;expiration&amp;gt; - now.DateTime).Int }
        else { 0 }
    }
    #| adds editor
    method add-editor( Str $editor, DateTime $expiration, Str $token ) is protected {
        %!storage{$editor} = :$expiration, :$token;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Websocket route of the Cro app needed refactoring completely. Instead of getting all the information for an edit from the browser, typically, the edit suggestion comes first but the &lt;em&gt;editor&lt;/em&gt; and their &lt;em&gt;id-token&lt;/em&gt; comes later. So the Webocket needs to &lt;a href="https://docs.raku.org/language/concurrency#Promises" rel="noopener noreferrer"&gt;create a promise&lt;/a&gt; and also to put a timer on it.&lt;/p&gt;

&lt;p&gt;My first-draft solution is (the code needs to be cleaned up somewhat):&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; 'suggestion_box' {
            web-socket :json, -&amp;gt; $incoming {
                supply whenever $incoming -&amp;gt; $message {
                    my $json = await $message.body;
                    # first filter out the handshake signal for opening a websocket
                    if $json&amp;lt;loaded&amp;gt; {
                        say strftime(DateTime.now, '%v %R') ~ ': connection made'
                            if $debug;
                        emit({ :connection&amp;lt;Confirmed&amp;gt; })
                    }
                    else {
                        if $debug {
                            say strftime(DateTime.now, '%v %R') ~ ': got suggestion, now at ' ~ +@suggestions;
                            for $json.kv -&amp;gt; $k, $v {
                                say "KEY $k =&amp;gt;\n$v"
                            }
                            say "edit suggestion finished\n";
                        }
                        my $response = sanitise( $json ); # sanitise returns an error message or 'ok'
                        my $editor := $json&amp;lt;editor&amp;gt;;
                        if $response ne 'OK' {
                            emit( {
                                :timestamp( DateTime.now.Str ),
                                :$response,
                                :$editor,
                            })
                        }
                        # handle the socket with an active editor
                        elsif $store.is-editor-active($editor) {
                            say strftime(DateTime.now, '%v %R') ~ ': editor is registered' if $debug;
                            # too little time left for token
                            if $store.time-remaining( $editor ) &amp;lt;= 10 * 60 {
                                say strftime(DateTime.now, '%v %R') ~ ': not enough time' if $debug;
                                emit( {
                                    :timestamp( DateTime.now.Str ),
                                    :response&amp;lt;TooLittleTimeOnToken&amp;gt;,
                                    :$editor,
                                })
                            }
                            else {
                                say strftime(DateTime.now, '%v %R') ~ ': handling with stored token' if $debug;
                                $json&amp;lt;id-token&amp;gt; = $store.get-token($editor);
                                @suggestions.push: $json;
                                emit( {
                                    :timestamp( DateTime.now.Str),
                                    :response&amp;lt;OK&amp;gt;,
                                    :$editor,
                                    :expiration( strftime($store.expiration($editor), '%v %R'))
                                } )
                            }
                        }
                        # the editor does not have a token, but may have in some period of time
                        else {
                            say strftime(DateTime.now, '%v %R') ~ ': editor without authorisation' if $debug;
                            my $timestamp;
                            my $response = 'NoAuthorisation';
                            my $expiration = '';
                            my $token = '';
                            my Promise $tapped-out .= new;
                            my $tap = $see-new-auths.tap( -&amp;gt; %a {
                                if %a&amp;lt;editor&amp;gt; eq $editor {
                                    $timestamp = DateTime.now.Str;
                                    $response = 'OK';
                                    $token = %a&amp;lt;token&amp;gt;;
                                    $expiration = %a&amp;lt;expiration&amp;gt;;
                                    $tapped-out.keep;
                                }
                            });
                            await Promise.anyof(
                                $tapped-out,
                                my $timer = Promise.in($auth-wait-time).then: {
                                    $response = 'NoAuthorisation';
                                }
                            ).then( { $tap.close } );
                            if $response eq 'OK' {
                                $json&amp;lt;id-token&amp;gt; = $token;
                                @suggestions.push: $json;
                                say strftime(DateTime.now, '%v %R') ~ ": authorising suggestion {$json.raku}, now at " ~ +@suggestions if $debug;
                            }
                            emit( %( :$timestamp, :$response, :$expiration, :$editor) )
                        }
                    }
                }
            }

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;$tapped-out&lt;/code&gt; Promise is created so that it can be kept with &lt;code&gt;.keep&lt;/code&gt; inside the code that listens for an authorisation event.&lt;/p&gt;

&lt;p&gt;Then a separate Promise composed of the &lt;em&gt;$tapped-out&lt;/em&gt; Promise and a timer Promise is created so that whichever comes first triggers the next step. If the timer exits before an authorisation, then the edit suggestion is discarded, otherwise it is combined with the &lt;em&gt;id-token&lt;/em&gt; and queued.&lt;/p&gt;

&lt;p&gt;In the code section above, the webserver is a supply and when the &lt;code&gt;emit&lt;/code&gt; sub is called, the hash argument is mapped by Cro into a JSON object and returned to the browser that has connected to the Cro app. So it can be picked by the Javascript's websocket's &lt;code&gt;onmessage&lt;/code&gt; function, and the data used by the browser program.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Authorisation event
&lt;/h2&gt;

&lt;p&gt;When a &lt;em&gt;Github app&lt;/em&gt; is registered, a route is supplied for the authorisation data. Consequently, the server section of the Cro app has to be set up to service this route. I chose the route &lt;code&gt;/raku-auth&lt;/code&gt; (and in my code comes before the websocket, but since we are dealing with concurrent processes, the order of websocket and &lt;em&gt;raku-auth&lt;/em&gt; is irrelevant):&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; 'raku-auth', :%params {
            CATCH {
                default {
                    content 'text/html', '&amp;lt;h1&amp;gt;Raku documentation&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Authorisation error.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Please report&amp;lt;/p&amp;gt;';
                    say 'error is: ', .message;
                    for .backtrace.reverse {
                        next if .file.starts-with('SETTING::');
                        next unless .subname;
                        say "  in block { .subname } at { .file } line { .line }";
                        last if .file.starts-with('NQP::')
                    }
                }
            }
            my %decoded = from-json( base64-decode( %params&amp;lt;state&amp;gt;).decode );
            say strftime(DateTime.now, '%v %R') ~ ': got from Github params: ', %params , ' state decoded: ', %decoded
                if $debug;
            my $editor = %decoded&amp;lt;editor&amp;gt;;
            my $resp = await Cro::HTTP::Client.post(
                "https://github.com/login/oauth/access_token",
                query =&amp;gt; %(
                    :$client_id,
                    :$client_secret,
                    :code( %params&amp;lt;code&amp;gt; ),
                ),
            );
            # Github returns an object with keys access_token, expires_in (&amp;amp; others not needed)
            my $body = await $resp.body;
            my %data = $body.decode.split('&amp;amp;').map(|*.split("=",2));
            # first store the data for future suggestions
            my $token = %data&amp;lt;access_token&amp;gt;;
            my $expiration = now.DateTime + Duration.new( %data&amp;lt;expires_in&amp;gt;);
            $store.add-editor($editor, $expiration, $token );
            # next put the data in a stream for suggestions that have already arrived
            $auth-stream.emit( %( :$editor, :$expiration, :$token ) );
            content 'text/html', '&amp;lt;h1&amp;gt;Raku documentation&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Editing has been authorised.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Thank you&amp;lt;/p&amp;gt;';
        }
        get -&amp;gt; 'suggestion_box' {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;CATCH&lt;/code&gt; phaser is only ever triggered if there is an error processing the route. If it is triggered, then the string after &lt;code&gt;content&lt;/code&gt; is  sent back to Github, which displays it in the tab containing the authorisation button. The HTML could be improved.&lt;/p&gt;

&lt;p&gt;At the end of the code, the &lt;code&gt;content&lt;/code&gt; sub similarly sends back an HTML string to indicated authorisation is successful.&lt;/p&gt;

&lt;p&gt;When a route has a query appended to it, the Cro route function &lt;code&gt;get&lt;/code&gt; captures the data into a named hash, which I have called &lt;code&gt;%params&lt;/code&gt;. Github recommends that a &lt;code&gt;state&lt;/code&gt; variable is sent when sending the user to Github for authorisation. I have chosen to send the name submitted by the editor with Base64 encoding. Since the editor name in the form and the user's Github id could be different, this adds some complexity to improve security.&lt;/p&gt;

&lt;p&gt;I have to say that the Cro syntax is easy to work with. When the data is transmitted as a JSON, there is the named &lt;code&gt;:body&lt;/code&gt; field, and with the data is transmitted as a query, there is the named &lt;code&gt;:query&lt;/code&gt; field. Otherwise the &lt;em&gt;get&lt;/em&gt; sub has a consistent syntax. Compare this to a cURL command.&lt;/p&gt;

&lt;p&gt;The parameters sent to the route include a &lt;code&gt;code&lt;/code&gt; field, which is then returned (again as a query) to Github. When it receives the code, it returns the &lt;code&gt;id-token&lt;/code&gt; for the editor. This two-fold handshake makes it difficult to impersonate someone else. &lt;/p&gt;

&lt;p&gt;In addition, Github returns the number of seconds the &lt;em&gt;id-token&lt;/em&gt; is valid for. So this needs to be combined into a DateTime both for the &lt;em&gt;suggestion-box&lt;/em&gt; server and the browser.&lt;/p&gt;

&lt;p&gt;Finally, the editor name, id-token and expiration date are both stored in a thread-safe Hash, and placed in an event stream into which the websocket has tapped. &lt;/p&gt;

&lt;p&gt;Raku's concurent structures make it easy to set up the event loop into which the &lt;em&gt;raku-auth&lt;/em&gt; route injects information and the &lt;em&gt;websocket&lt;/em&gt; listens for information. At the start of the code we have&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my $auth-stream = Supplier.new;
my $see-new-auths = $auth-stream.Supply;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As can be seen from the code fragments, the &lt;em&gt;raku-auth&lt;/em&gt; route has the line &lt;code&gt;$auth-stream.emit( %(...) )&lt;/code&gt; and the &lt;em&gt;websocket&lt;/em&gt; code has the line &lt;code&gt;my $tap = $see-new-auths.tap( -&amp;gt; %a { ... })&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The first stanza supplies an item, in this case as hash, while the second listens for all items supplied. The second then determines which item to react to. &lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping secrets
&lt;/h2&gt;

&lt;p&gt;One of the pieces of data required by a &lt;em&gt;Github app&lt;/em&gt; is a 'secret' for the app. There are also several items to be provided to the Cro app.&lt;/p&gt;

&lt;p&gt;Since this Cro app is intended for a docker container, the configuration data is conveyed in Environment variables. So at the start of the app, several secret variables are extracted as (just an example here):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my $client_id = %*ENV&amp;lt;CLIENT_ID&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and these data can be temporarily saved in an environment file &lt;code&gt;env-file&lt;/code&gt;. Then when the docker image is invoked the data can be supplied:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo docker run -d --rm --env-file env-file my-docker-image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Treading on toes
&lt;/h2&gt;

&lt;p&gt;As can be seen from the diagram above, getting authorisation is delicate dance, and it took me days to stop the browser, Github and the suggestion-box treading on each others' toes.&lt;/p&gt;

&lt;p&gt;First, the interaction between Github and the &lt;em&gt;suggestion-box&lt;/em&gt; server to exchange a &lt;code&gt;code&lt;/code&gt; for an &lt;code&gt;id-token&lt;/code&gt; are all conducted using the query format. That is a url ending &lt;code&gt;?data-item=stuff&amp;amp;item-two=nonsense&lt;/code&gt;, which is in turn Base64 encoded. All the Github API calls use JSON data with authorisation headers. &lt;/p&gt;

&lt;p&gt;In hindsight, we can recognise that the OAuth protocol is an industry standard, while the Github API protocols are not, so can be different. But it took me a while to work out what the strange data was being received by the server. I did not come across this behaviour as being explicitly documented.&lt;/p&gt;

&lt;p&gt;Second, Github requires that the &lt;em&gt;suggestion-box&lt;/em&gt; server is registered by the &lt;code&gt;owner&lt;/code&gt; of the &lt;strong&gt;repo&lt;/strong&gt; as a &lt;code&gt;Github app&lt;/code&gt; and specific permissions need to be allocated to it. There are dozens of permissions in over a dozen categories and the correct ones have to be given to the &lt;em&gt;Github app&lt;/em&gt;. I thought - mistakenly - that the permissions for &lt;em&gt;Pull requests&lt;/em&gt; was what I needed.&lt;/p&gt;

&lt;p&gt;Actually this was the last bug I had to overcome and it took a couple of days to figure out that the error was not in the server code, but that I had not allocated enough permissions.&lt;/p&gt;

&lt;p&gt;To summarise, the &lt;em&gt;suggestion-box&lt;/em&gt; server uses four separate Github API calls and the permissions are different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;api.github.com/repos/{$repo-name}/git/ref/heads/main&lt;/code&gt; to get the commit sha for the &lt;em&gt;repo-name&lt;/em&gt; - this is public data and the permission is mandatory for an App, so it does not need to be specifically added&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;api.github.com/repos/{$repo-name}/git/refs&lt;/code&gt; (with data) to create a reference - this requires the &lt;code&gt;Contents&lt;/code&gt; permission with read/write&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;api.github.com/repos/{$repo-name}/contents/{$file-path}&lt;/code&gt; to supply the edited - this requires the &lt;code&gt;Contents&lt;/code&gt; permission&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;api.github.com/repos/{$repo-name}/pulls&lt;/code&gt; to create a pull request of the new branch - this requires the &lt;code&gt;Pull Request&lt;/code&gt; permission&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Some 'simple' requests have complex solutions. Although Cro and Raku's concurrent structures take a while to understand, they are easy to apply.&lt;/p&gt;

</description>
      <category>raku</category>
      <category>github</category>
      <category>rakulang</category>
      <category>cro</category>
    </item>
    <item>
      <title>Editing RakuDoc, CRO'ing it to Github</title>
      <dc:creator>Richard Hainsworth</dc:creator>
      <pubDate>Mon, 14 Apr 2025 20:54:26 +0000</pubDate>
      <link>https://dev.to/finanalyst/editing-rakudoc-croing-it-to-github-1507</link>
      <guid>https://dev.to/finanalyst/editing-rakudoc-croing-it-to-github-1507</guid>
      <description>&lt;h2&gt;
  
  
  All I want is to correct a one-letter typo
&lt;/h2&gt;

&lt;p&gt;The ask, expressed by our website users, is simple enough, edit a source from a GitHub repo in a browser, then save the edited version back.&lt;/p&gt;

&lt;p&gt;How difficult is that? It's easy to point the browser at the file in the repo, and GitHub has an editor ...&lt;/p&gt;

&lt;p&gt;The problem comes from authorisation, and bad actors wanting to add stupid stuff to files - like: I'm the brilliantish shop for high-ent wazzoo's. Come take a look at &lt;code&gt;http://rip-me-off.darkweb.cosmo&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;Github's editor is easy to use - if you have commit permissions on the repo, otherwise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you need to clone the repo,&lt;/li&gt;
&lt;li&gt;correct the one letter typo in a single file,&lt;/li&gt;
&lt;li&gt;raise a PR. 
Which is fine if you know what a 'clone' is, or a 'repo' is, or a 'PR'. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;&lt;u&gt;But its only one letter!??&lt;/u&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Also, our website text source is written in RakuDoc (not MarkDown, but that's another story) and so it would be nice to be able to see what the change looks like. As always a seemingly trivial mistake, such as deleting a &lt;code&gt;&amp;lt;&lt;/code&gt; in the wrong place, can have severe effects.&lt;/p&gt;

&lt;p&gt;So, it would be nice to see an HTML rendering of the edited text before submitting it back.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first steps into the forest
&lt;/h2&gt;

&lt;p&gt;It seems doable:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get the content of the file from Github - there is a simple URL and the Javascript &lt;code&gt;fetch&lt;/code&gt; function. &lt;/li&gt;
&lt;li&gt;Put the content into an online editor - there are a few editor libraries, which give you all sorts of control; I chose one.&lt;/li&gt;
&lt;li&gt;Get the content back from the editor at some point, send it to a renderer - I put my &lt;code&gt;Rakuast::RakuDoc::Render&lt;/code&gt; into a docker container, wrote a very simple &lt;code&gt;Cro&lt;/code&gt; app that has all the logic for a websocket. The app expects RakuDoc source, and sends back the rendered HTML, which is then added into a &lt;code&gt;div&lt;/code&gt; next to the online editor. &lt;/li&gt;
&lt;li&gt;Send the edited version of the file back to Github - oh. I'm lost in the forest.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'll not explain tasks 1-3 above and focus on task 4.&lt;/p&gt;

&lt;h2&gt;
  
  
  The path through the forest
&lt;/h2&gt;

&lt;p&gt;While getting the content is easy, returning it is hard. The API documentation is written for developers who understand Github and HTTP requests. &lt;/p&gt;

&lt;p&gt;I spent two days wandering though the API documentation, searching for tutorials, (and Github's own discussion forum seems to be flooded with trolls, so is not a good place to look).&lt;/p&gt;

&lt;p&gt;I even resorted - gasp - to ChatGPT. Whilst the code it suggested was not remotely what I wanted, the 'solutions' demonstrated a remarkably simple truth. Perhaps I should have realised it on my own, but in all my documentation searches I had not read a hint about it.&lt;/p&gt;

&lt;p&gt;The simple truth: getting a patch of a file to the repo maintainers could &lt;em&gt;only&lt;/em&gt; be done in a &lt;strong&gt;sequence&lt;/strong&gt; of steps, not the single step that I was looking for.&lt;/p&gt;

&lt;p&gt;It is obvious that Github wants to ensure that only authorised users can change the state of a repo. Authorisation is accomplished by accompanying any request for a change with a &lt;strong&gt;&lt;em&gt;token&lt;/em&gt;&lt;/strong&gt; that has a limited duration, is unique to a recognised user on Github, and has permissions for the repo. &lt;/p&gt;

&lt;p&gt;While it is possible to get and use such a token for an arbitrary user, by sending them to Github to log in, the process is an added layer of complexity. For simplicity, we shall consider that a token has been generated and call it &lt;code&gt;id-token&lt;/code&gt;. (For development, I used one generated for myself)&lt;/p&gt;

&lt;h2&gt;
  
  
  The steps on the map
&lt;/h2&gt;

&lt;p&gt;In generalised terms, here are the steps to sending a suggestion for editing a file to the Github repo. (I'll explain some of the jargon with each step).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Obtain the contents of the &lt;code&gt;file&lt;/code&gt; and its &lt;code&gt;sha&lt;/code&gt; from &lt;code&gt;repo&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Github stores all the information developers use in &lt;em&gt;files&lt;/em&gt;. Each is tagged with a &lt;em&gt;sha&lt;/em&gt;, and is located in a &lt;em&gt;repository&lt;/em&gt; or &lt;em&gt;repo&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Actually Github has a two layer structure of &lt;em&gt;owner&lt;/em&gt;/&lt;em&gt;repo-name&lt;/em&gt;, so in this article, when I say &lt;em&gt;repo&lt;/em&gt;, I mean the combination of &lt;em&gt;owner&lt;/em&gt;/&lt;em&gt;repo-name&lt;/em&gt;. For example, the site I am working on uses the source from the &lt;code&gt;Raku/doc&lt;/code&gt; Github storage, where &lt;code&gt;Raku&lt;/code&gt; is the organisation that owns the storage, and &lt;code&gt;doc&lt;/code&gt; is the repo in which Raku holds its documentation suite.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;sha&lt;/code&gt; is the encrypted sum of the entire file, and is presented as a 40 digit hexadecimal string. The maths of &lt;em&gt;shas&lt;/em&gt; is quite interesting but not relevant here. Suffice it to say that if a file is changed at all, the &lt;em&gt;shas&lt;/em&gt; of the two files (before the edit and after it) will be different (for the pedantic, I'll add 'with high probability').&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;In fact&lt;/em&gt;, this step can all be done in the browser because there is no change in state of the Github storage.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Obtain the latest &lt;code&gt;sha&lt;/code&gt; or &lt;code&gt;commit&lt;/code&gt; for the &lt;code&gt;repo&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Just as the contents of one file at one point in time can be identified by a &lt;em&gt;sha&lt;/em&gt; of the file, so too can the contents of the whole &lt;em&gt;repo&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new &lt;code&gt;branch&lt;/code&gt; or reference point in the &lt;em&gt;repo&lt;/em&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In order to retain both the existing content of a &lt;em&gt;repo&lt;/em&gt; and the suggested new content, the new content is held in a new part of the &lt;em&gt;repo&lt;/em&gt;, and this new 'section' is called a &lt;strong&gt;branch&lt;/strong&gt;. It can also be thought of as a reference point in the history of the &lt;em&gt;repo&lt;/em&gt;. At some point the maintainer of the &lt;em&gt;repo&lt;/em&gt; can accept the new content (merging it into the main part of the &lt;em&gt;repo&lt;/em&gt;), or reject it.&lt;/li&gt;
&lt;li&gt;Creating a &lt;em&gt;branch&lt;/em&gt; changes the state of the &lt;em&gt;repo&lt;/em&gt;, so Github only allows a recognised agent to do this. So, this step has to be accompanied by an &lt;em&gt;id-token&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;The information needed by Github for this step is

&lt;ol&gt;
&lt;li&gt;The name of the &lt;em&gt;repo&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;sha&lt;/em&gt; of the &lt;em&gt;repo&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;An &lt;em&gt;id-token&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;A name for the &lt;em&gt;branch&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;A description of the &lt;em&gt;branch&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Move a copy in &lt;code&gt;BASE64&lt;/code&gt; format of the edited file to the new branch&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;BASE64&lt;/em&gt; is a way of coding a file so that it can be transmitted safely across the internet.&lt;/li&gt;
&lt;li&gt;This step needs

&lt;ol&gt;
&lt;li&gt;The name of the repo&lt;/li&gt;
&lt;li&gt;The name of the branch&lt;/li&gt;
&lt;li&gt;The path of the file inside the branch&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;sha&lt;/em&gt; of the &lt;strong&gt;original&lt;/strong&gt; file&lt;/li&gt;
&lt;li&gt;The new contents of the &lt;em&gt;file&lt;/em&gt; in &lt;em&gt;BASE64&lt;/em&gt; format&lt;/li&gt;
&lt;li&gt;An &lt;em&gt;id-token&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Raise a &lt;code&gt;PR&lt;/code&gt; for the &lt;em&gt;branch&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;PR&lt;/code&gt; is a &lt;strong&gt;&lt;em&gt;pull request&lt;/em&gt;&lt;/strong&gt;, and it is a suggestion to the &lt;em&gt;repo&lt;/em&gt; maintainer to merge the suggested changes of the file into the main content of the &lt;em&gt;repo&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;This&lt;/strong&gt; is the end result we want.&lt;/li&gt;
&lt;li&gt;A branch may contain changes to many files (not just one), and so a &lt;em&gt;PR&lt;/em&gt; is for the whole branch (for the pedantic: 'typically').&lt;/li&gt;
&lt;li&gt;This step needs

&lt;ol&gt;
&lt;li&gt;An &lt;em&gt;id-token&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;repo&lt;/em&gt; name&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;branch&lt;/em&gt; name&lt;/li&gt;
&lt;li&gt;A title for the &lt;em&gt;PR&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;A description of the &lt;em&gt;PR&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;Even though only one letter in one file may need to be changed, the API is set up (reasonably) for many changes to many files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beware ... the trolls and bogey men
&lt;/h2&gt;

&lt;p&gt;Can we do this in the browser? Uh, well ... yes, but &lt;strong&gt;No&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The problem is that everything in a browser can be examined by the world. So if you put an &lt;em&gt;id-token&lt;/em&gt; in a browser, it can be extracted from the browser, and then a bad actor can use the token to do silly stuff.&lt;/p&gt;

&lt;p&gt;So, each of the steps above which require an &lt;em&gt;id-token&lt;/em&gt; have to be executed from a place that can be defended, such as inside a container where the &lt;em&gt;id-token&lt;/em&gt; can be hidden and renewed on a regular basis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a hallowed glade in the forest
&lt;/h2&gt;

&lt;p&gt;The solution is to create a safe location - hallowed glade - in which the &lt;em&gt;id-token&lt;/em&gt; can be stored, and from which the calls to Github can safely be executed.&lt;/p&gt;

&lt;p&gt;So let us create a container with a Cro application. The container will have a websocket to accept the edited file from the browser, and then separately interact with Github.&lt;/p&gt;

&lt;p&gt;The Cro documentation was written by a genius for developers with experience, unlike me. They are daunting and confusing - it took days of work to figure things out. However, Cro does make it easy to set up the steps listed above.&lt;/p&gt;

&lt;p&gt;The first thing to realise is that &lt;code&gt;Cro::HTTP::Client&lt;/code&gt; and &lt;code&gt;Cro::HTTP::Server&lt;/code&gt; / &lt;code&gt;Cro::HTTP::Router&lt;/code&gt; are - at least externally - independent of each other, but we will need both. &lt;/p&gt;

&lt;p&gt;We will need the &lt;strong&gt;&lt;em&gt;Server&lt;/em&gt;&lt;/strong&gt; to listen for the data coming down the websocket, and then &lt;strong&gt;&lt;em&gt;Client&lt;/em&gt;&lt;/strong&gt; to move the edited file to the Github repo. &lt;/p&gt;

&lt;p&gt;Since step 1 above is done in-browser, it needs to be implemented in Javascript. I will not deal with that here, except to say, that when the content is fetched from Github, the &lt;em&gt;sha&lt;/em&gt; of the file is saved. The in-browser code finally sends along a websocket a JSON object with the following fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;repo    - the owner/repo-name combination&lt;/li&gt;
&lt;li&gt;path    - the path of the file inside the repo &lt;/li&gt;
&lt;li&gt;sha     - the sha of the file&lt;/li&gt;
&lt;li&gt;content - the &lt;em&gt;edited&lt;/em&gt; content in BASE64 form&lt;/li&gt;
&lt;li&gt;editor  - the person editing the file&lt;/li&gt;
&lt;li&gt;comment - why the edits are made&lt;/li&gt;
&lt;li&gt;patch   - a Patch between the original and edited files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last three are not strictly needed, but can be used for some sanity testing.&lt;/p&gt;

&lt;p&gt;Lets assume the web socket has a route &lt;code&gt;suggestion_box&lt;/code&gt;, then the following is the code for a Cro application to get the data&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Cro::HTTP::Log::File;
use Cro::HTTP::Server;
use Cro::HTTP::Router;
use Cro::HTTP::Client;
use Cro::HTTP::Router::WebSocket;
use DateTime::strftime; # a helper to provide nice time formats

my $patch-limit = 5 * 2 ** 10; # 5k limit of chars
my $comment-limit = 1 * 2 ** 10; # 1k limit of chars
my $pr-update-duration = 10 * 60 * 60; # every ten minutes

my $host = '0.0.0.0';
my $port = 60005;
my @suggestions;
my $busy = False;

my Cro::Service $http = Cro::HTTP::Server.new(
    http =&amp;gt; &amp;lt;1.1&amp;gt;,
    :$host,
    :$port,
    application =&amp;gt; routes(),
    after =&amp;gt; [
        Cro::HTTP::Log::File.new(logs =&amp;gt; $*OUT, errors =&amp;gt; $*ERR)
    ]
    );
say strftime(DateTime.now, '%v %R') ~ ': starting up.';

$http.start; # this starts the server part of the application

# the following is an asynchronous structure which listens for events
# then does the block for that event. The main event is to stop 
# the app when a control signal is raised.
react {
    whenever signal(SIGINT) {
        $http.stop;
        say strftime(DateTime.now, '%v %R') ~ ': closing down.';
        done;
    }
    whenever Supply.interval($pr-update-duration) {
        # every pr-update-duration seconds, this block is run
        # it calls the code for raising a PR
        unless $busy {
            $busy = True;
            raise-pr( @suggestions.pop ) while @suggestions;
            $busy = False;
        }
    }
}
# these routes are what define the application
sub routes() {
    route {
        get -&amp;gt; 'suggestion_box' { # the route for the websocket
            web-socket :json, -&amp;gt; $incoming {
                supply whenever $incoming -&amp;gt; $message {
                    my %json = await $message.body;
                    if %json&amp;lt;sha&amp;gt; { # no sha, no play
                        my $response = sanitise( $json );
                            # place limitations on the incoming data
                            # add 'cancel' to ignore data
                        @suggestions.push: $json.hash.clone
                                unless $json&amp;lt;cancel&amp;gt;;
                            # store the data to be sent as PR
                        emit({
                            :timestamp( strftime(DateTime.now, '%v %R')),
                            :$response
                        })
                            # send back to the websocket JSON with
                            # a time stamp and a reponse code
                    }
                    elsif $json&amp;lt;loaded&amp;gt; {
                        # send back a handshake when websocket opens
                        emit({ :connection&amp;lt;Confirmed&amp;gt; })
                    }
                }
            }
        }
    }
}
sub sanitise( %edit --&amp;gt; Str ) {
    my $response = 'OK';
    for %edit.keys {
        when 'editor' { 
            %edit&amp;lt;editor&amp;gt; .= subst(/\W/, '_', :g)
                          .substr( ^21 ) 
        }
        when 'patch' {
            if %edit&amp;lt;patch&amp;gt;.chars &amp;gt; $patch-limit {
                %edit&amp;lt;cancel&amp;gt; = True;
                $response = 'TooManyChanges'
            }
        }
        when 'comment' { %edit&amp;lt;comment&amp;gt; .= substr(^$comment-limit) }
        when &amp;lt;sha repo content&amp;gt;.any  {}
        default       { %edit{ $_ }:delete } 
                   # remove unwanted fields
    }
    $response
}
sub raise-pr( %an-edit ) { ... }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Server code handles all the interaction between the browser and the 'Hallowed Circle'. &lt;/p&gt;

&lt;p&gt;Now we execute the remaining steps ( 2 - 5 ) of the Github sequence.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sub raise-pr( %edit ) {
    #| Define the secret token. When the container is run,
    #| the token can be passed as an environment parameter. 
    #| Later, we can refactor to obtain it to identify the
    #| user making the edits.
    my $id-token = %*ENV&amp;lt;ID_TOKEN&amp;gt;;
    my $base = 'https://api.github.com/repos';
    # create a descriptive branch name
    my $branch = strftime(DateTime.now, '%v')
            ~ "_{ %edit&amp;lt;editor&amp;gt; }";
    # get the repo information
    my $resp = await Cro::HTTP::Client.get(
        "/{%edit&amp;lt;repo&amp;gt;}/git/ref/heads/main"
    );
    my %data = await $resp.body;
    my $commit = %data&amp;lt;object&amp;gt;&amp;lt;sha&amp;gt;;
    # create a new branch
    $resp = await Cro::HTTP::Client.post(
        "$base/{%edit&amp;lt;repo&amp;gt;}/git/refs",
        content-type =&amp;gt; 'application/vnd.github+json',
        auth =&amp;gt; { bearer =&amp;gt; $id-token },
        headers =&amp;gt; [ X-GitHub-Api-Version =&amp;gt; '2022-11-28' ],
        body =&amp;gt; %(:sha($commit), :ref("refs/heads/$branch"))
    );
    %data = await $resp.body;
    # update file into new branch
    $resp = await Cro::HTTP::Client.put(
        "$base/{%edit&amp;lt;repo&amp;gt;}/contents/{%edit&amp;lt;path&amp;gt;}",
        content-type =&amp;gt; 'application/vnd.github+json',
        auth =&amp;gt; { bearer =&amp;gt; $id-token },
        headers =&amp;gt; [ X-GitHub-Api-Version =&amp;gt; '2022-11-28' ],
        body =&amp;gt; %(
            :sha(%edit&amp;lt;sha&amp;gt;),
            :$branch,
            :content(%edit&amp;lt;content&amp;gt;),
            :message(%edit&amp;lt;comment&amp;gt;),
        )
    );
    %data = await $resp.body;
    # should check to make sure OK
    # Raise a PR for the new branch
    $resp = await Cro::HTTP::Client.post(
        "$base/{%edit&amp;lt;repo&amp;gt;}/pulls",
        content-type =&amp;gt; 'application/vnd.github+json',
        auth =&amp;gt; { bearer =&amp;gt; $id-token },
        headers =&amp;gt; [ X-GitHub-Api-Version =&amp;gt; '2022-11-28' ],
        body =&amp;gt; %(
            :title("Web edit of {%edit&amp;lt;path&amp;gt;}"),
            :head($branch),
            :base&amp;lt;main&amp;gt;,
            :body("Edit suggested by ｢{%edit&amp;lt;editor&amp;gt;}｣ because ｢{%edit&amp;lt;comment&amp;gt;｣}"),
        )
    );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Github documentation for the PR was particularly confusing because &lt;code&gt;head&lt;/code&gt;, &lt;code&gt;base&lt;/code&gt;, and &lt;code&gt;body&lt;/code&gt; did not seem to be intuitive taken on their own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exiting the wood
&lt;/h2&gt;

&lt;p&gt;I have documented my journey into and out of the woods using as little jargon as possible. My purpose in doing this was to describe it so I could understand it myself, and also in the hope that the gods of the internet (aka search engines) will guide someone else who may have a similar set of issues.&lt;/p&gt;

&lt;p&gt;Naturally, the container is part of a bigger system, and it needs to be tested. But that raises more questions, so I will only be brief.&lt;/p&gt;

&lt;p&gt;It is not so easy to test a websocket. My approach was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to create a custom container for Cro,&lt;/li&gt;
&lt;li&gt;add the service.raku program above.&lt;/li&gt;
&lt;li&gt;The docker container is run locally (using Podman Desktop), so the websocket host is &lt;code&gt;localhost&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A small html file with a websocket and a way to pass the data needed for the suggestion-box is loaded as a file into a browser.&lt;/li&gt;
&lt;li&gt;A small Github repo is created with a file to edit.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rakulang</category>
      <category>cro</category>
      <category>github</category>
    </item>
    <item>
      <title>Ryuu - a Japanese dragon</title>
      <dc:creator>Richard Hainsworth</dc:creator>
      <pubDate>Tue, 30 Jan 2024 18:30:58 +0000</pubDate>
      <link>https://dev.to/finanalyst/ryuu-a-japanese-dragon-2e7m</link>
      <guid>https://dev.to/finanalyst/ryuu-a-japanese-dragon-2e7m</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A follow up to &lt;a href="https://dev.to/finanalyst/creating-a-new-programming-language-draig-503p"&gt;the Welsh dragon&lt;/a&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;p&gt;Firing up another localisation&lt;br&gt;&lt;br&gt;
Steps to Ryuu&lt;br&gt;&lt;br&gt;
Comments on the Raku program&lt;br&gt;&lt;br&gt;
More generally about localisation of coding&lt;br&gt;&lt;br&gt;
If you want to make Ryuu better?  &lt;/p&gt;


&lt;h2&gt;
  
  
  Firing up another localisation
&lt;/h2&gt;

&lt;p&gt;In my previous blog about Y Ddraig, I created a localisation of the Raku Language in Welsh. During a recent conversation, someone mentioned there may be interest in a Japanese localisation, so I thought I would try the same techniques.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I do not speak or read or have ever studied Japanese. The localisation given below will be about as clunky and awkward as any can be. I imagine there may be some hilarious stupidities as well.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So to be clear, this article is about a proof of concept rather than a real effort to create a production-ready program.&lt;/p&gt;

&lt;p&gt;However, it took me 40 minutes from start to finish, including setting up &lt;a href="https://github.com/finanalyst/rakuast-L10N-JA"&gt;the github repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since I like dragons, I named the Japanese cousin to Raku 'Ryuu'. It's a whimsy, not to be treated with much seriousness.&lt;/p&gt;
&lt;h2&gt;
  
  
  Steps to Ryuu
&lt;/h2&gt;

&lt;p&gt;Basically I created a github repo, copied my existing &lt;a href="https://github.com/finanalyst/rakuast-L10N-CY"&gt;Welsh localisation&lt;/a&gt; and changed &lt;strong&gt;CY&lt;/strong&gt; to &lt;strong&gt;JA&lt;/strong&gt;, and &lt;strong&gt;draig&lt;/strong&gt; to &lt;strong&gt;ryuu&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Within the &lt;code&gt;automation/&lt;/code&gt; directory I used the translation technique explained for Welsh to create the &lt;code&gt;JA&lt;/code&gt; file from the template. The &lt;code&gt;translated.txt&lt;/code&gt; file needed some manual cleaning, because the English word &lt;em&gt;for&lt;/em&gt; has multiple Japanese equivalents. I chose one more or less at random. In addition, Google translate did some strange things to the order of words and numbers in a line.&lt;/p&gt;

&lt;p&gt;After adapting the files in the &lt;code&gt;bin/&lt;/code&gt; directory, and installing the distribution with Raku's &lt;code&gt;zef&lt;/code&gt; utility, I ran &lt;code&gt;tr2ryuu&lt;/code&gt; on the Raku program &lt;code&gt;simple.raku&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A comment about my Welsh blog was that the program in Y Ddraig was not all in Welsh. And here the program is not all in Japanese.&lt;/p&gt;

&lt;p&gt;Remember that the user-facing part of a program will be in the language of the user, in this case it is English. However, the coder-facing part of the program will be in the language of the coder. Below, the coder interface is in Japanese (or rather my ham-fisted attempt at Japanese).&lt;/p&gt;

&lt;p&gt;The following is the result (which I put in a file called simple.ryuu):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;私の $choice;
私の $continue;
私の @bad = &amp;lt;damn stupid nutcase&amp;gt;;
リピート {
    $choice = プロンプト "Type something, like a number, or a string: ";
    言う "You typed in ｢" ~ ($choice ~~ 任意(@bad) ?? "*" × $choice.文字 !! $choice) ~ "｣";
    与えられた $choice {
        いつ "dragon" {
            言う "which is 'draig' in Welsh"
        }
        いつ 任意(@bad) {
            言う "wash your mouth with soap"
        }
        いつ IntStr {
            言う "which evaluates to an integer ", $choice
        }
        いつ RatStr {
            言う "which evaluates to a rational number ", $choice
        }
        デフォルト {
            言う "which does not evaluate to a number "
        }
    }
    $continue = プロンプト "Try again? If not type N: "
} まで $continue 当量 任意(&amp;lt;N n&amp;gt;)

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

&lt;/div&gt;



&lt;p&gt;What is amazing to me is that when I ran &lt;code&gt;ryuu simple.ryuu&lt;/code&gt;, the program ran without error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comments on the Raku program
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;simple.raku&lt;/code&gt; program is obviously trivial, but what I wanted to show are some interesting Raku features. Note how I created an array of words with &lt;code&gt;@bad = &amp;lt;damn stupid nutcase&amp;gt;;&lt;/code&gt;, and then later I tested to see whether an input word was one of the array elements.&lt;/p&gt;

&lt;p&gt;The Raku idiom &lt;code&gt;いつ 任意(@bad)&lt;/code&gt; or in English &lt;code&gt;when any( @bad )&lt;/code&gt; compares the topic variable, in this case the input value, with each array element and creates a junction of Boolean results. The 'any' effectively &lt;code&gt;or&lt;/code&gt;'s the result to collapse the junction.&lt;/p&gt;

&lt;p&gt;Junctions are not common in programming languages, so I thought if there would be problems, then it would be there. So I was surprised to find my Raku program works without error in another language.&lt;/p&gt;

&lt;h2&gt;
  
  
  More generally about localisation of coding
&lt;/h2&gt;

&lt;p&gt;All the major coding languages are in English. There are, however, coders from all over the world, and the majority of those from non-English speaking nations would have needed to learn English before (or at the same time as) they learnt coding.&lt;/p&gt;

&lt;p&gt;We are thus creating a new technological elite: those who can understand English (or some subset of it), and those who cannot. The more coding becomes an essential part of life, the greater the ability divide between coders (who speak English) and non-coders will become.&lt;/p&gt;

&lt;p&gt;The aim of localising a programming language is to provide an entry into coding in a form that is more accessible to every human being, whatever their natural language.&lt;/p&gt;

&lt;p&gt;However, the aim of this approach is not to eliminate English at every level of complexity, but to provide a sufficiently rich language for most normal coding and educational needs.&lt;/p&gt;

&lt;p&gt;In addition, by having a canonical language (Raku, which is based on English) into which all localised languages can be translated, what we get is a universal auxiliary language together with a universality of being able to code.&lt;/p&gt;

&lt;p&gt;Having a single auxiliary language means that a non-English speaking person writing in a localised coding language can translate the program with the problem into Raku, have a developer on the other side of the globe find the problem, and suggest a solution in code, then for that solution to be translated back into the local language.&lt;/p&gt;

&lt;p&gt;Naturally, a person who wants to learn more about coding, or who needs to delve deeper into the workings of a module, will need to learn English. Learning wider to learn deeper is a normal part of the educational experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you want to make Ryuu better?
&lt;/h2&gt;

&lt;p&gt;Ryuu or however it should be called, absolutely is in need of Tender loving care. Please feel free to use the github issues or PR processes to suggest better translations.&lt;/p&gt;

&lt;p&gt;At some stage, Ryuu will join the official Raku localisations.&lt;/p&gt;

</description>
      <category>rakulang</category>
      <category>l10n</category>
      <category>japanese</category>
    </item>
    <item>
      <title>Creating a new programming language - Draig</title>
      <dc:creator>Richard Hainsworth</dc:creator>
      <pubDate>Mon, 15 Jan 2024 08:18:08 +0000</pubDate>
      <link>https://dev.to/finanalyst/creating-a-new-programming-language-draig-503p</link>
      <guid>https://dev.to/finanalyst/creating-a-new-programming-language-draig-503p</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Actually creating a localization of an existing programming language in an existing human language&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Table of Contents
&lt;/h3&gt;

&lt;p&gt;Introduction&lt;br&gt;
Considerations&lt;br&gt;
The plan for y Ddraig (the dragon in Welsh)&lt;br&gt;
Constraints and first steps&lt;br&gt;
Forwards into Draig and running&lt;br&gt;
Completing the translation&lt;br&gt;
Backwards to canonical form&lt;br&gt;
Drawbacks&lt;/p&gt;


&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Nearly all programming languages that are widely used in the world today have English as their base human language.&lt;/p&gt;

&lt;p&gt;This means that a young person living in a non-English environment must first learn English (if only a limited sub-set of English), and then learn the skills needed for coding. This puts the majority of the humanity at a disadvantage.&lt;/p&gt;

&lt;p&gt;Would it not be useful to create programming languages that use the script and words of human languages, but which compile into programs that will run with state of the art computer software?&lt;/p&gt;

&lt;p&gt;Here is how I created a Welsh cousin of Raku, and I called it &lt;strong&gt;y Ddraig&lt;/strong&gt; - or &lt;strong&gt;&lt;em&gt;The dragon&lt;/em&gt;&lt;/strong&gt;.&lt;sup&gt;1&lt;/sup&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Considerations
&lt;/h2&gt;

&lt;p&gt;There are some practical obstacles to creating any new programming language, and here are some of the ameliorating reasons why the &lt;strong&gt;&lt;em&gt;Raku Programming Language&lt;/em&gt;&lt;/strong&gt; is a good choice to base a new one on.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Different human languages use different writing systems and most need extra letters not covered by the ASCII set&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; The Unicode system has eliminated the problems of displaying and storing different writing systems.&lt;/li&gt;
&lt;li&gt; The Raku language has Unicode support at every level. Every number, operator, keyword, function etc can be written with Unicode symbols. There are a very few exceptions, such as &lt;code&gt;;&lt;/code&gt;, &lt;code&gt;,&lt;/code&gt;, and &lt;code&gt;{}&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Different operating systems&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; There are at least three major operating systems widely used in the world: Window, PC Linux, and Mac OS.&lt;/li&gt;
&lt;li&gt; Raku runs on all three&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;All professional programmers are proficient in English, and so can answer questions about program errors in English. The number of programmers speaking Welsh is quite small, and the same would be true for many other human languages.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; This is precisely the problem we are trying to resolve: making the coding profession a possibility for humanity as a whole. But there is a vicious circle, professional programmers work in English, so how can they help a person in another language?&lt;/li&gt;
&lt;li&gt; The circle can be broken if the interface language, which is the one that programmers work in, can be varied, whilst the code that is run on a computer is completely independent of the interface language.&lt;/li&gt;
&lt;li&gt; If the interface language can be easily changed, then a program coded in Welsh, but with a problem, can be easily translated (whilst continuing to work as before) into English. The problem can be resolved, corrected, and the solution changed (still working) back to Welsh.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The plan for &lt;strong&gt;y Ddraig&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Whilst the plan is to create &lt;strong&gt;&lt;em&gt;y Ddraig&lt;/em&gt;&lt;/strong&gt; as a language that can be used with as little English as possible, there are several stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;First is to create a localization (L10N) of Raku, or a module called &lt;code&gt;L10N::CY&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Inside a Raku program, all that will be needed for a completely Welsh program is for the first line to be use L10N::CY;&lt;/li&gt;
&lt;li&gt; All subsequent lines will be in Welsh, but will compile and run as a normal Raku program.&lt;/li&gt;
&lt;li&gt; The program can be easily translated into English, and English Raku programs translated in to Welsh. Simple utilities are given below to do this.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next, the operating system has to be adapted so that a executable called &lt;code&gt;draig&lt;/code&gt; is available, which will also mean that in a graphic interface (GUI), double clicking on a file with a file-extension of &lt;code&gt;.draig&lt;/code&gt; will run &lt;strong&gt;&lt;em&gt;Raku&lt;/em&gt;&lt;/strong&gt; with the &lt;em&gt;L10N::CY&lt;/em&gt; module already loaded. This is trivial.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Constraints and first steps
&lt;/h2&gt;

&lt;p&gt;For personal reasons, I stopped using Windows on my PC, and I use Ubuntu Linux exclusively. So, where there are terminal sessions, I shall be showing how I created &lt;strong&gt;&lt;em&gt;Y ddraig&lt;/em&gt;&lt;/strong&gt; using a Linux terminal.&lt;/p&gt;

&lt;p&gt;Since &lt;strong&gt;&lt;em&gt;Y ddraig&lt;/em&gt;&lt;/strong&gt; is a Raku cousin, or technically a Raku localization, the Raku language needs to be installed. In addition, it needs to be a version of the language released after December 2023. Information about the installation of Raku, and its package manager &lt;strong&gt;zef&lt;/strong&gt;, can be found &lt;a href="https://raku.org"&gt;on the Raku website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The first stage is to create the &lt;code&gt;L10N::CY&lt;/code&gt; module. It is simply a normal Raku module, which is then installed with the &lt;code&gt;zef&lt;/code&gt; package manager.&lt;/p&gt;

&lt;p&gt;Raku module development is conventionally done by creating a github repository. Working with &lt;strong&gt;&lt;em&gt;git&lt;/em&gt;&lt;/strong&gt; is quite simple for the basic functionality, but there is a long learning curve when working with others. But none of that is the topic here.&lt;/p&gt;

&lt;p&gt;Elizabeth Mattijsen, who is responsible for all this Raku internationalization magic, has created a template internationalization module for the Klingon language (yep: aliens get to be the first to use localizations of a Terran computer language)&lt;sup&gt;2&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;So I git cloned the Klingon, and created a github repo for the Welsh. My git nick is &lt;em&gt;finanalyst&lt;/em&gt;, so here's the terminal code lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/lizmat/L10N-TLH.git rakuast-L10N-Klingon 
git clone https://github.com/finanalyst/rakuast-L10N-CY.git rakuast-L10N-Welsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the following, I shall call Elizabeth's repo, the Klingon repo, and mine, the Welsh repo. If you want to create your own language, the convention being followed is to name the language according to an ISO 639-1 supported language code, at least for the foreseeable future. You should also think of an filename extension (like &lt;code&gt;.draig&lt;/code&gt; here) for programs in the new language (Raku cousin).&lt;/p&gt;

&lt;p&gt;The two critical parts of the module are &lt;code&gt;update-localization&lt;/code&gt;, and a root text file which we will call the &lt;strong&gt;&lt;em&gt;localization map&lt;/em&gt;&lt;/strong&gt;. It should be named by the language code. Here it is called &lt;code&gt;CY&lt;/code&gt; for &lt;em&gt;Cymraeg&lt;/em&gt; or the Welsh language, for Klingon, it is &lt;code&gt;TLH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;update-localization&lt;/code&gt; utility in from the Klingon repo looks for a repo root directory file with 2 or 3 upper case characters. This is taken as the &lt;em&gt;localization map&lt;/em&gt; and is automatically converted into all the magical modules.&lt;/p&gt;

&lt;p&gt;The biggest step is to translate the terms to be stored in &lt;code&gt;CY&lt;/code&gt;. The template for the &lt;em&gt;localization map&lt;/em&gt; can be found at &lt;a href="https://github.com/Raku/L10N/TEMPLATE"&gt;Github Raku localizations&lt;/a&gt;. To get this as a local text file, I used the following terminal code to download the template in to my working directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl 'https://raw.githubusercontent.com/Raku/L10N/main/TEMPLATE' &amp;gt; CY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pristine form of &lt;code&gt;CY&lt;/code&gt; contains a few lines of comment (starting with the characters '# ', note the space), and then a number of sections starting with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# KEY        TRANSLATION
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within each section there is a key and then an English Raku keyword, eg.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#adverb-pc-delete         delete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that it has been commented out with single &lt;code&gt;#&lt;/code&gt;. This means that the &lt;code&gt;update-localization&lt;/code&gt; utility will ignore the line.&lt;/p&gt;

&lt;p&gt;Now comes the translation part. Each significant commented line (a line with &lt;code&gt;#&lt;/code&gt; and no space at the start) has two parts: a KEY and a TRANSLATION, with some spaces between them. The translation process is to substitute the English Raku keyword with the Welsh word, and remove the &lt;code&gt;#&lt;/code&gt;. For example, the first significant line becomes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;adverb-pc-delete                 dileu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When starting the translation process, and to see how the system works, it is sufficient to translate a minimum number of keys. (Eg., for the &lt;em&gt;Draig&lt;/em&gt; program below, I only need eleven words.)&lt;/p&gt;

&lt;p&gt;Once I have enough key words for the program, all that is needed is to run &lt;code&gt;./update-localization&lt;/code&gt;. This then creates a directory tree under &lt;code&gt;lib/&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forwards into Draig and running
&lt;/h2&gt;

&lt;p&gt;Here is a short program in Raku (English cousin), which we store in a file called 'simple.raku' in the root directory of the repo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my $choice;
my $continue;
my @bad = &amp;lt;damn stupid nutcase&amp;gt;;

repeat {
    $choice = prompt 'Type something, like a number, or a string: ';
    say 'You typed in ｢' ~ ( $choice ~~ any( @bad ) ?? '*' x $choice.chars !! $choice) ~ '｣';
    given $choice {
        when 'dragon' { say "which is 'draig' in Welsh" }
        when any( @bad ) { say "wash your mouth with soap" }
        when IntStr { say "which evaluates to an integer ", $choice }
        when RatStr { say "which evaluates to a rational number ", $choice }
        default { say "which does not evaluate to a number "}
    }
    $continue = prompt 'Try again? If not type N: ';
} until $continue eq any(&amp;lt;N n&amp;gt;) ;

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

&lt;/div&gt;



&lt;p&gt;Try running it in a terminal where the working directory is the root directory of the repo, thus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;raku simple.raku
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you input some words, it will tell you the input is a string, if you input something naughty (well only one of the three words 'damn stupid nutcase'), you will get another response, and then there are responses depending on whether the number is an integer or a rational.&lt;/p&gt;

&lt;p&gt;The code uses 11 keywords, which I translated and put into &lt;code&gt;CY&lt;/code&gt;. Obviously, there are many strings that form the user interface, and these are hard-coded in this program in English. We are concerned at the moment with the infrastructure keywords that form the programming language.&lt;/p&gt;

&lt;p&gt;Now lets translate the Raku program using a simple Raku utility called &lt;code&gt;tr2draig&lt;/code&gt;. &lt;br&gt;
We shall specify here that the Raku program is of the form &lt;code&gt;somename.raku&lt;/code&gt; and that we want a Draig program of the form &lt;code&gt;somename.draig&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The utility is the following Raku script:&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
sub MAIN(
  $filename where *.IO.f  #= source file to be localized to Welsh
) {
    $filename.IO.extension('draig').spurt: $filename.IO.slurp.AST.DEPARSE("CY")
}

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

&lt;/div&gt;



&lt;p&gt;Breaking the program down, &lt;code&gt;#!/usr/bin/env raku&lt;/code&gt; is standard for a script with execute permission.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$filename where *.IO.f  #= ...&lt;/code&gt; is a nice Raku idiom for a program called from a terminal. The program expects a string that names a file. It checks that the filename exists and is of type 'f'. If not, then an error message will be provided from the comment following &lt;code&gt;#=&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$filename.IO.extension('draig').spurt:&lt;/code&gt; takes the filename, creates a new file with the extension 'draig' replacing the previous extension (which was 'raku'), then spurts text into it, the text it uses being generated by the expression after the &lt;code&gt;:&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$filename.IO.slurp.AST.DEPARSE("CY")&lt;/code&gt; takes the filename (which has extension 'raku'), makes it into a filehandle, slurps (sucks) in the text that is in the file, parses the text as a Raku program into an Abstract Symbol Tree (AST), and then &lt;code&gt;deparses&lt;/code&gt; the symbol tree using the new Welsh keywords into a new program with Welsh.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For reasons related to distributing Raku software, I have placed the utility in the &lt;code&gt;bin/&lt;/code&gt; directory. There are two ways to get a copy of these files, either by creating a clone of my Github repository (the url is given above), or by installing the Raku distribution, as &lt;code&gt;zef install "L10N::CY"&lt;/code&gt;. If &lt;em&gt;zef&lt;/em&gt; is set up in a typical way, then the utilities below can be run without specifying the path.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The translation utility is run like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bin/tr2draig simple.raku
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This produces a file &lt;code&gt;simple.draig&lt;/code&gt;, which contains&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fy $choice;
fy $continue;
fy @bad = &amp;lt;damn stupid nutcase&amp;gt;;
ailadrodd {
    $choice = prydlon "Type something, like a number, or a string: ";
    dywedyd "You typed in ｢" ~ ($choice ~~ unrhyw(@bad) ?? "*" x $choice.golosg !! $choice) ~ "｣";
    a-roddwyd $choice {
        pryd "dragon" {
            dywedyd "which is 'draig' in Welsh"
        }
        pryd unrhyw(@bad) {
            dywedyd "wash your mouth with soap"
        }
        pryd IntStr {
            dywedyd "which evaluates to an integer ", $choice
        }
        pryd RatStr {
            dywedyd "which evaluates to a rational number ", $choice
        }
        rhagosodedig {
            dywedyd "which does not evaluate to a number "
        }
    }
    $continue = prydlon "Try again? If not type N: "
} hyd $continue eq unrhyw(&amp;lt;N n&amp;gt;)

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

&lt;/div&gt;



&lt;p&gt;Now we want a way to run &lt;code&gt;draig&lt;/code&gt; programs. The easiest way is create another Raku program &lt;code&gt;draig&lt;/code&gt;, which we place in the &lt;code&gt;bin/&lt;/code&gt; directory. &lt;code&gt;bin/draig&lt;/code&gt; has the following 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
sub draig(*@_) {
    %*ENV&amp;lt;RAKUDO_RAKUAST&amp;gt; = 1;
    %*ENV&amp;lt;RAKUDO_OPT&amp;gt;     = '-ML10N::CY';
    run $*EXECUTABLE, @_;
}

multi sub MAIN() {
    draig
}

multi sub MAIN(
  $filename where *.IO.f  #= source file to be run in Welsh
) {
    draig $filename
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a gloss of the program:&lt;br&gt;
&lt;code&gt;sub draig(*@_) {...&lt;/code&gt; This is a helper subroutine called later. It sets up environment variables, and preloads the localization module, before running Raku with the Welsh keywords. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;multi sub MAIN()&lt;/code&gt; runs the sub draig (above) when no program is given. This puts the user into a REPL, where statements can be input directly, parsed and run immediately. However, &lt;code&gt;draig&lt;/code&gt; will run using the Welsh keywords.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;multi sub MAIN(&lt;br&gt;
  $filename where *.IO.f  #= source file to be run in Welsh&lt;br&gt;
)&lt;/code&gt; handles the case when &lt;code&gt;draig&lt;/code&gt; is given a filename. As explained above, the filename is tested for existence.&lt;/p&gt;

&lt;p&gt;Now try running &lt;code&gt;bin/draig simple.draig&lt;/code&gt; in a terminal.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the &lt;code&gt;RakuAST-L10N-CY&lt;/code&gt; distribution has been installed with &lt;code&gt;zef&lt;/code&gt;, then all you will need is &lt;code&gt;draig simple.draig&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The running code produces exactly the same output as the English Raku program. The user interface output is still in English, and for completeness, I should translate all of the text strings to Welsh as well.&lt;/p&gt;
&lt;h2&gt;
  
  
  Completing the translation
&lt;/h2&gt;

&lt;p&gt;At this point, we can translate any English version of a Raku program into a Draig program, and &lt;code&gt;draig&lt;/code&gt; will run it, but only if the Raku program uses the 11 keywords I translated.&lt;/p&gt;

&lt;p&gt;In order to create a full localization, all of the &lt;strong&gt;Translation&lt;/strong&gt; values need to be converted to Welsh. The &lt;strong&gt;first&lt;/strong&gt; step (and I really must re-emphasise it is a first step) is to use an automated translation tool. A correct localization will need first-language Welsh speakers to go through the &lt;code&gt;CY&lt;/code&gt; file and correct the translations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At the time of writing, the localization has not been properly verified, so it has not yet been added to the official Raku localizations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For the automated translation, I have created the directory &lt;code&gt;automation/&lt;/code&gt;. I again downloaded the TEMPLATE into a &lt;code&gt;CY&lt;/code&gt; file in the &lt;code&gt;automation/&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;I have written some automation helper utilities, namely:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;find-untranslated&lt;/code&gt;, takes a CY file and splits it into two new files, with line numbers at the start of each line to help match later. One file is &lt;code&gt;partial.txt&lt;/code&gt; with the starting key and comment lines, and the second file is &lt;code&gt;to-be-translated.txt&lt;/code&gt;. Both contain approximately 700 lines.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;combine-translated&lt;/code&gt;, takes &lt;code&gt;partial.txt&lt;/code&gt; and another file &lt;code&gt;translated.txt&lt;/code&gt; (see below) to create a new CY file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Next I copy/pasted the lines for translation (from the file &lt;code&gt;to-be-translated.txt&lt;/code&gt; into &lt;a href="https://translate.google.co.uk/?sl=auto&amp;amp;tl=cy&amp;amp;op=translate"&gt;Google's translate to Welsh page&lt;/a&gt;. The operation took a couple of copy/pastes due to size limitations, but the text is not overly large.&lt;/p&gt;

&lt;p&gt;The translated text can be copied straight back to a new file (translated.txt), and then recombined with &lt;code&gt;partials.txt&lt;/code&gt; to create CY.&lt;/p&gt;
&lt;h2&gt;
  
  
  Backwards to canonical form
&lt;/h2&gt;

&lt;p&gt;As mentioned above, suppose a Welsh-speaker using &lt;code&gt;y Ddraig&lt;/code&gt; &lt;br&gt;
runs into a programming problem, a syntax error or logic not working as the programmer assumes. An English speaking programmer will probably not be able to help. &lt;/p&gt;

&lt;p&gt;But ... &lt;em&gt;.draig&lt;/em&gt; program can be retranslated back to the canonical form of Raku. This is done by a utility called &lt;code&gt;tr2raku&lt;/code&gt;. It is almost the inverse of &lt;code&gt;tr2draig&lt;/code&gt;, but instead of replacing the file extension &lt;code&gt;.draig&lt;/code&gt; with &lt;code&gt;.raku&lt;/code&gt;, we add it on to the filename so that its clear it is a canonicalisation of a Raku cousin.&lt;/p&gt;

&lt;p&gt;The utility &lt;code&gt;bin/tr2raku&lt;/code&gt; contains the following contents.&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
sub MAIN(
 $filename where *.IO.f  #= Welsh source file to be turned to canonical form
) {
   $filename.IO.extension('raku', :0parts).spurt: $filename.IO.slurp.AST("CY").DEPARSE
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference can be seen that the language signifier (&lt;code&gt;CY&lt;/code&gt;) is a parameter to the &lt;code&gt;AST&lt;/code&gt; method, rather than the &lt;code&gt;DEPARSE&lt;/code&gt; method. &lt;/p&gt;

&lt;p&gt;There should be no reason why this recipe cannot be applied to Mandarin, Hindi, or Japanese.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drawbacks
&lt;/h2&gt;

&lt;p&gt;The problems stem from the development history of Raku. Error messages are in English, and so Raku cousins, like Draig, will have English error messages. &lt;/p&gt;

&lt;p&gt;The problem is not insurmountable, but it will take a lot of translator hours.&lt;/p&gt;

&lt;p&gt;Another problem is that helper modules, for example, &lt;code&gt;JSON::Fast&lt;/code&gt;, which imports/exports structured data from/to  &lt;code&gt;.json&lt;/code&gt; files into Raku data structures. The module has two main methods &lt;code&gt;to-json&lt;/code&gt; and &lt;code&gt;from-json&lt;/code&gt;. These names are set by the module, not by Raku.&lt;/p&gt;

&lt;p&gt;A program in &lt;code&gt;y Ddraig&lt;/code&gt; will be able to access all Raku modules without restriction, but it will need to use the canonical (English) names.&lt;/p&gt;

&lt;p&gt;However, if many Raku localizations come into being, and a user base for them develops, these are all soluble problems.&lt;/p&gt;




&lt;p&gt;Footnotes&lt;/p&gt;

&lt;h6&gt;
  
  
  1
&lt;/h6&gt;

&lt;p&gt;A reader may wonder why the language is &lt;em&gt;Y ddraig&lt;/em&gt;, but &lt;em&gt;draig&lt;/em&gt; is given in dictionaries as the translation for &lt;em&gt;dragon&lt;/em&gt;. Well ..., &lt;em&gt;draig&lt;/em&gt; is a feminine word, and the definite particle &lt;em&gt;Y&lt;/em&gt; triggers a mutation in the next feminine word, so &lt;em&gt;d&lt;/em&gt; mutates to &lt;em&gt;dd&lt;/em&gt;.&lt;/p&gt;

&lt;h6&gt;
  
  
  2
&lt;/h6&gt;

&lt;p&gt;My next project is to create a localization with Egyptian hieroglyphs &lt;/p&gt;

</description>
      <category>rakulang</category>
      <category>internationalization</category>
      <category>programming</category>
    </item>
    <item>
      <title>RakuDoc revision open to comment</title>
      <dc:creator>Richard Hainsworth</dc:creator>
      <pubDate>Mon, 31 Jul 2023 23:00:00 +0000</pubDate>
      <link>https://dev.to/finanalyst/rakudoc-revision-open-to-comment-4hjb</link>
      <guid>https://dev.to/finanalyst/rakudoc-revision-open-to-comment-4hjb</guid>
      <description>&lt;p&gt;The second stage in the process to update RakuDoc is now over and the third (GAMMA review) stage is starting. In order not to repeat some history, please take a look at &lt;a href="https://dev.to/finanalyst/revising-rakudoc-29d4"&gt;Revising Rakudoc&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;An online version is available of &lt;a href="https://new-raku.finanalyst.org/language/rakudoc"&gt;the proposed RakuDoc language&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The whole of &lt;a href="//docs.raku.org"&gt;the Raku documentation suite&lt;/a&gt; is written in RakuDoc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improving on a good design
&lt;/h2&gt;

&lt;p&gt;About half of the original design ideas outlined in &lt;a href="https://design.raku.org/S26.html"&gt;S26&lt;/a&gt; were documented in &lt;a href="https://docs.raku.org/language/pod"&gt;current POD6&lt;/a&gt;. Some of the ideas were available, but not documented. Some instructions were not realised at all. &lt;/p&gt;

&lt;p&gt;It should be remembered that RakuDoc is parsed by the compiler (eg. Rakudo) as part of a Raku program, and is then rendered by the renderer (eg. &lt;code&gt;Raku::Pod::Render&lt;/code&gt;) into (for example) HTML. When I use the word 'implemented', I mean that a RakuDoc instruction is properly parsed and rendered. Some of the instructions defined in S26 were parsed by Rakudo, but &lt;strong&gt;&lt;em&gt;not rendered&lt;/em&gt;&lt;/strong&gt;, and some were &lt;strong&gt;&lt;em&gt;not parsed&lt;/em&gt;&lt;/strong&gt; properly or at all, so could not be rendered. &lt;/p&gt;

&lt;p&gt;The revision process has therefore identified and rectified the parsing deficiencies, and identified the rendering flaws. RakuDoc is correctly parsed only on the most recent versions of Rakudo, which at the time of writing has yet to be released. &lt;code&gt;Raku::Pod::Render&lt;/code&gt; still does not handle RakuDoc in its entirety.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Two use cases
&lt;/h2&gt;

&lt;p&gt;It became clear that the RakuDoc serves two inter-related use cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Documenting code for developing and maintaining software. 

&lt;ul&gt;
&lt;li&gt;explanations about variables, methods, etc.&lt;/li&gt;
&lt;li&gt;another program that uses the software as a dependency should be able to access these explanations&lt;/li&gt;
&lt;li&gt;data which should be included with the software but not be hard coded into it, for example, a unit test needs to populate a data structure with test data.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Documenting the software for use by another user.

&lt;ul&gt;
&lt;li&gt;Good documentation should have tutorials, code snippets, headings, Tables of content, etc&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Tables
&lt;/h2&gt;

&lt;p&gt;RakuDoc had a simple table markup, which is very similar to the Markdown syntax. It worked, but the simplicity of the syntax was at the cost of flexibility. &lt;/p&gt;

&lt;p&gt;Looking around at other ways of specifying a table, we identified two paradigms (there may be more), namely the one used by HTML and the one used by the GTK grid widget. Both of them allow for cells that span more than one column or row, and both allow for embedding (eg. a table inside a cell of a table).&lt;/p&gt;

&lt;p&gt;After several iterations, a new &lt;em&gt;procedural&lt;/em&gt; model was created and rendered. The design allows for spanning and embedding, but it also allows an author to specify a table row by row, or column by column, or even using a mixture of both.&lt;/p&gt;

&lt;p&gt;An example showing a markup using both rows and columns can be seen in &lt;a href="https://new-raku.finanalyst.org/language/rakudoc#Procedural_description_of_tables"&gt;the online draft&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Semantic blocks
&lt;/h2&gt;

&lt;p&gt;A semantic block is a section of text that should be easily available to another software tool, or can be moved around the final document. &lt;/p&gt;

&lt;p&gt;For example, a section on the authors of a document (including contact or affiliations) is most easily written at the top of the document, but often it is better to place the information towards the bottom of the text. &lt;/p&gt;

&lt;p&gt;This is done by creating a semantic block (simply by making the calling the block in uppercase letters). The block can be hidden from view by adding the metadata option &lt;code&gt;:hidden&lt;/code&gt;. All the data is placed in a special structure.&lt;/p&gt;

&lt;p&gt;The rendered text can be placed in the document later using the &lt;code&gt;P&amp;lt;&amp;gt;&lt;/code&gt; instruction, or it can be accessed by another tool that may only be wanting the VERSION or LICENSE. &lt;/p&gt;

&lt;h2&gt;
  
  
  More metadata options
&lt;/h2&gt;

&lt;p&gt;One of the strengths of RakuDoc is the ability to add optional metadata to blocks of text.&lt;/p&gt;

&lt;p&gt;The new version of the defining document explains this concept in more detail. Metadata options are optional, with reasonable defaults being assumed. This means that a short form of the block is sufficient in most cases.&lt;/p&gt;

&lt;p&gt;In the description above, the option &lt;code&gt;:hidden&lt;/code&gt; was mentioned. Another example, is &lt;code&gt;:caption&lt;/code&gt;. Suppose you want to write a semantic block called &lt;code&gt;=AUTHORS&lt;/code&gt; at the start of the document, but you want for it to appear later in the document as &lt;code&gt;Article authors&lt;/code&gt;, then you could specify it as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=for AUTHORS :caption&amp;lt;Article authors&amp;gt; :hidden
A. N. Writer, socMedia nic @psuedonym
M. Z. Orator, socMedia nic @politician

Article text continues

Pages later

P&amp;lt;semantic: AUTHORS&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is possible to include a link &lt;code&gt;L&amp;lt;for reference see | #A very long title somewhere in the text&amp;gt;&lt;/code&gt; where the text on the right-hand side of the &lt;code&gt;|&lt;/code&gt; is a heading. However, this can become tiresome if you want to include several links to the same place. &lt;/p&gt;

&lt;p&gt;So, a metadata option &lt;code&gt;:id&lt;/code&gt; can be included in a heading. This allows you to do the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=for head3 :id&amp;lt;lnk&amp;gt;
How to correctly link to other places in a manual

Pages of text

Properly linking is important, L&amp;lt;see for example|#lnk&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Doing things in line
&lt;/h2&gt;

&lt;p&gt;RakuDoc has instructions for block level text, such as headings, paragraphs, code. &lt;/p&gt;

&lt;p&gt;Typically blocks will be included in the Table of Contents. &lt;br&gt;
It also has &lt;strong&gt;&lt;em&gt;markup&lt;/em&gt;&lt;/strong&gt; instructions that work in line, and which do not (typically) affect the ToC. &lt;/p&gt;

&lt;p&gt;For example, a simple markup instruction is &lt;code&gt;C&amp;lt; text &amp;gt;&lt;/code&gt;, which renders like &lt;code&gt;text&lt;/code&gt;. I have used the Markdown equivalent here. In RakuDoc, everything between the &lt;code&gt;C&amp;lt;&lt;/code&gt; and &lt;code&gt;&amp;gt;&lt;/code&gt; is verbatim and styled differently to normal text, just like the Markdown code quotes. However, RakuDoc also has &lt;code&gt;V&amp;lt; text &amp;gt;&lt;/code&gt; which treats everything inside the angle brackets as verbatim but does not style it differently.&lt;/p&gt;

&lt;p&gt;A new markup instruction in RakuDoc is &lt;code&gt;M&amp;lt; text | metadata&amp;gt;&lt;/code&gt;. A renderer will place the &lt;em&gt;text&lt;/em&gt; in the rendered text, but will also provide a mechanism for the user to take the metadata and provide new functionality. For instance, &lt;code&gt;M&amp;lt; fa-copy | font awesome v5 &amp;gt;&lt;/code&gt; could be interpreted to insert the &lt;code&gt;font-awesome&lt;/code&gt; icon called &lt;code&gt;fa-copy&lt;/code&gt; into the text. Or &lt;code&gt;M&amp;lt; Buy now | PayPal, database-id &amp;gt;&lt;/code&gt; could expose the API for the PayPal payment platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  How not to be confusing
&lt;/h2&gt;

&lt;p&gt;RakuDoc is inherently customisable. It is also designed to be output neutral (although at the moment HTML is the most common output form). Semantic blocks can be invented within a document, and a renderer can allow for other user-defined blocks and markup instructions to be created. &lt;/p&gt;

&lt;p&gt;However, RakuDoc is specific about naming rules. A built-in block must be all lower case, and renderers should not allow user-defined blocks to use all lower case. A semantic block is all upper case. And a user-defined block must have at least one upper-case letter and one lower-case letter.&lt;/p&gt;

&lt;p&gt;All markup instructions, which are inline instructions, must be a single Unicode character with the property UPPER. Built-in markup instructions are the ASCII characters and &lt;code&gt;Δ&lt;/code&gt;. All other codes can be used.&lt;/p&gt;

&lt;p&gt;The naming rules have been created to ensure that even if a user-defined block or markup becomes popular, it is not a part of the RakuDoc standard. Renderers are only required to implement the RakuDoc standard, and may render other blocks, or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;These are some of the interesting additions to RakuDoc that are being proposed. There are more.&lt;/p&gt;

&lt;p&gt;Since the Gamma review stage is now underway, it is almost certain that there may be more changes because the revision is now open to the Raku community for comment and requests. Discussion is open both for the language design and for the explanation of the design. &lt;/p&gt;

&lt;p&gt;As might be admitted, community requests for changes to the overall design will face significant resistance from the main authors in order to maintain backwards compatibility with the previous version of RakuDoc, and the integrity of the underlying paradigms. New block or inline instructions will be more readily considered, but requests for examples, explanation, and greater clarity will be very much appreciated.&lt;/p&gt;

</description>
      <category>rakulang</category>
      <category>documentation</category>
      <category>rakudoc</category>
    </item>
    <item>
      <title>Revising Rakudoc</title>
      <dc:creator>Richard Hainsworth</dc:creator>
      <pubDate>Fri, 30 Jun 2023 23:00:00 +0000</pubDate>
      <link>https://dev.to/finanalyst/revising-rakudoc-29d4</link>
      <guid>https://dev.to/finanalyst/revising-rakudoc-29d4</guid>
      <description>&lt;p&gt;In the earliest days of Raku, Damian Conway specified a documentation markup language to accompany it. Since it was modeled on Perl's POD it was called &lt;strong&gt;&lt;code&gt;&amp;lt;sound of trumpets and dramatic pause&amp;gt;&lt;/code&gt;&lt;/strong&gt; POD6.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://design.raku.org/S26.html"&gt;Specification of POD6 (S26)&lt;/a&gt; was mostly incorporated without much extra explanation in the documentation suite. In this way, the description of POD6 was itself was an illustration of many of the features it documented, and some that it did not document.&lt;/p&gt;

&lt;p&gt;Since Raku is defined by its test suite, and not its documentation, there were other details of POD6 in the tests that were not documented, even in S26. &lt;/p&gt;

&lt;p&gt;Raku developed and morphed, but POD6 remained. The tooling for rendering the documentation sources needed updating, and the documentation site had to be modernised.&lt;/p&gt;

&lt;h1&gt;
  
  
  Upgrading the renderer
&lt;/h1&gt;

&lt;p&gt;A project of mine was to upgrade the basic renderer that would transform POD6 to HTML, but allow for developers to customise the templates for each type of POD6 block type. (The first &lt;code&gt;Pod::To::HTML&lt;/code&gt; renderer hard-coded representations of POD6 markup, eg. &lt;code&gt;B&amp;lt;this is bold&amp;gt;&lt;/code&gt; was &lt;code&gt;&amp;lt;strong&amp;gt;this is bold&amp;lt;/strong&amp;gt;&lt;/code&gt; and could not be changed.) &lt;/p&gt;

&lt;p&gt;It turned out that S26 allowed for much more than had been included in the first documentation sources, including custom blocks and custom markup.&lt;/p&gt;

&lt;p&gt;The project to upgrade the original HTML renderer morphed into &lt;a href="https://github.com/finanalyst/raku-pod-render"&gt;Raku::Pod::Render&lt;/a&gt;, and transforming a directory full of individual documentation sources into an interlinked and searchable set of documents required another layer of tooling &lt;a href="https://github.com/finanalyst/collection"&gt;Collection&lt;/a&gt;. For example, collecting together all the pages that can be grouped as tutorials, or reference, or language, and creating a separate page for them automatically. &lt;/p&gt;

&lt;p&gt;I covered these two projects in &lt;a href="https://www.youtube.com/watch?v=Rmuyb9kSQhY"&gt;a presentation to RakuCon 2022&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some of the original ideas in S26 had not been implemented, such as aliases and generic numbering. Other ideas had become outdated, such as a way to specify document encoding, which is now solved with Unicode. &lt;/p&gt;

&lt;p&gt;In addition, RakuAST (see &lt;a href="https://dev.to/lizmat/rakuast-for-early-adopters-576n"&gt;RakuAST for early adopters&lt;/a&gt; ) is on the horizon, which will radically change the speed of documentation processing.&lt;/p&gt;

&lt;p&gt;There are also two implementations of POD6, one in Raku and one in Javascript, namely &lt;a href="https://github.com/podlite/podlite-desktop"&gt;Alexandr Zahatski's Podlite&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Introducing Rakudoc
&lt;/h1&gt;

&lt;p&gt;This was an ideal time to revisit POD6 and recast it into Rakudoc - new name for the markup language, and its new file extension ".rakudoc".&lt;/p&gt;

&lt;p&gt;I was invited to the first Raku Core Summit and I put together a presentation about the changes I thought needed to be made based on my own experience, but also using comments from other developers.&lt;/p&gt;

&lt;p&gt;We came to a number of consensus agreements about the minimal changes that were needed, and some extra functionality to handle new questions, such as documentation versioning.&lt;/p&gt;

&lt;p&gt;It was also clear that Rakudoc (aka POD6) has two separate parts: components that interact closely with the program being documented, and components that will be rendered separately into HTML (or an ebook). The documentation file needs to make this clear. &lt;/p&gt;

&lt;p&gt;I have now written the first draft of the revision and the documentation file that encapsulates it. An HTML version can be found at &lt;a href="https://new-raku.finanalyst.org/language/rakudoc"&gt;new-raku.finanalyst.org/language/rakudoc&lt;/a&gt;, alongside the &lt;a href="https://new-raku.finanalyst.org/language/pod"&gt;old documentation file&lt;/a&gt; and the &lt;a href="https://new-raku.finanalyst.org/language/tables"&gt;simple table implementation&lt;/a&gt;. I am planning future blogs to describe some of the proposed revisions.&lt;/p&gt;

&lt;p&gt;However, none of the revisions will break existing POD6, so Rakudoc should be backwards compatible with POD6. The version at &lt;code&gt;new-raku&lt;/code&gt; is a VERY early first draft, and it will go through several review stages.&lt;/p&gt;

&lt;p&gt;The first Raku Core Summit was organised by &lt;a href="https://mastodon.social/@lizmat"&gt;Elizabeth Mattijsen&lt;/a&gt; and hosted by Elizabeth and Wendy at their home. It was a really good meeting and I am sincerely grateful for their generosity and hospitality. The summit was also supported by &lt;a href="https://www.perlfoundation.org/"&gt;The Perl and Raku Foundation&lt;/a&gt;, &lt;a href="https://rootprompt.at"&gt;Rootprompt&lt;/a&gt;, and &lt;a href="https://www.edument.se"&gt;Edument&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>raku</category>
      <category>documentation</category>
      <category>rakulang</category>
    </item>
  </channel>
</rss>
