<?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: Blacksmoke16</title>
    <description>The latest articles on DEV Community by Blacksmoke16 (@blacksmoke16).</description>
    <link>https://dev.to/blacksmoke16</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%2F137697%2F0a3657c7-13ad-4676-84a3-c81b9d2c8fda.jpeg</url>
      <title>DEV Community: Blacksmoke16</title>
      <link>https://dev.to/blacksmoke16</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/blacksmoke16"/>
    <language>en</language>
    <item>
      <title>oq - A portable/performant jq wrapper Part 2</title>
      <dc:creator>Blacksmoke16</dc:creator>
      <pubDate>Sat, 27 Feb 2021 15:35:03 +0000</pubDate>
      <link>https://dev.to/blacksmoke16/oq-a-portable-performant-jq-wrapper-part-2-5h6l</link>
      <guid>https://dev.to/blacksmoke16/oq-a-portable-performant-jq-wrapper-part-2-5h6l</guid>
      <description>&lt;h1&gt;
  
  
  OQ Revisited
&lt;/h1&gt;

&lt;p&gt;Over a year ago I published a &lt;a href="https://dev.to/blacksmoke16/oq-a-portable-performant-jq-wrapper-18ka"&gt;blog post&lt;/a&gt; introducing my &lt;a href="https://github.com/stedolan/jq"&gt;jq&lt;/a&gt; wrapper &lt;a href="https://github.com/blacksmoke16/oq"&gt;oq&lt;/a&gt;.  Given it has been quite a while since then I decided to rerun the benchmarks I did in the original post with the goal of seeing if things changed, both in regards to &lt;code&gt;oq&lt;/code&gt; itself and the greater ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Methodology
&lt;/h2&gt;

&lt;p&gt;I tested the latest versions of the packages in the first post.  I also added the Go version of &lt;code&gt;yq&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The tested packages, and versions, for the benchmarks include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/stedolan/jq"&gt;jq&lt;/a&gt; (&lt;code&gt;1.6&lt;/code&gt;) - Installed via &lt;code&gt;pacman -S jq&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://mikefarah.gitbook.io/yq/"&gt;yq (go)&lt;/a&gt; (&lt;code&gt;4.6.1&lt;/code&gt;) - Downloaded latest binary from the latest &lt;a href="https://github.com/mikefarah/yq/releases"&gt;GH Release&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kislyuk.github.io/yq/"&gt;yq (python)&lt;/a&gt; (&lt;code&gt;2.12.0&lt;/code&gt;) - Installed via &lt;code&gt;pip3 install yq&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://blacksmoke16.github.io/oq/"&gt;oq&lt;/a&gt; (&lt;code&gt;1.2.0&lt;/code&gt;) - Built locally with Crystal &lt;code&gt;0.36.1&lt;/code&gt; via &lt;code&gt;shards build --production --release&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each package was tested against the same 3 benchmarks in my original post, plus an additional benchmark for &lt;code&gt;XML&lt;/code&gt; =&amp;gt; &lt;code&gt;JSON&lt;/code&gt;, mainly since &lt;code&gt;yq (python)&lt;/code&gt; has a dedicated command for handling &lt;code&gt;XML&lt;/code&gt; input/output that I didn't account for in the first post.  The data is gathered via the &lt;a href="https://www.gnu.org/software/time/"&gt;GNU Time&lt;/a&gt; command.  E.g. &lt;code&gt;/usr/bin/time -v&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;OS: &lt;code&gt;5.10.15-1-MANJARO x86_64&lt;/code&gt;&lt;br&gt;
CPU: &lt;code&gt;Intel i7-7700k&lt;/code&gt;&lt;br&gt;
Memory: &lt;code&gt;32GB @ 3,000 MHz&lt;/code&gt;&lt;br&gt;
SSD: &lt;code&gt;Samsung 970 EVO Plus - 1TB&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;This section summarizes the results, with key metrics in table form.  See the appendix for the raw benchmark data.&lt;/p&gt;
&lt;h3&gt;
  
  
  Simple
&lt;/h3&gt;

&lt;p&gt;Simply doing a pass through &lt;code&gt;JSON&lt;/code&gt; =&amp;gt; &lt;code&gt;JSON&lt;/code&gt; then counting the lines on a pretty small input file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"guests"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"numbers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"numbers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Susan"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"numbers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Max Memory (MB)&lt;/th&gt;
&lt;th&gt;Wall Clock Time (seconds)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;jq&lt;/td&gt;
&lt;td&gt;3.04&lt;/td&gt;
&lt;td&gt;0.02&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yq (go)&lt;/td&gt;
&lt;td&gt;6.02&lt;/td&gt;
&lt;td&gt;0.01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yq (python)&lt;/td&gt;
&lt;td&gt;14.728&lt;/td&gt;
&lt;td&gt;0.07&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;oq&lt;/td&gt;
&lt;td&gt;6.536&lt;/td&gt;
&lt;td&gt;0.07&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All packages performed relatively equally, nothing noteworthy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Jeopardy.json
&lt;/h3&gt;

&lt;p&gt;Getting the &lt;code&gt;length&lt;/code&gt; of a 53MB &lt;code&gt;JSON&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;The file used: &lt;a href="https://drive.google.com/file/d/0BwT5wj_P7BKXb2hfM3d2RHU1ckE/"&gt;jeopardy.json&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Max Memory (MB)&lt;/th&gt;
&lt;th&gt;Wall Clock Time (seconds)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;jq&lt;/td&gt;
&lt;td&gt;230.32&lt;/td&gt;
&lt;td&gt;0.66&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yq (go)&lt;/td&gt;
&lt;td&gt;705.292&lt;/td&gt;
&lt;td&gt;2.54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yq (python)&lt;/td&gt;
&lt;td&gt;2,922.996&lt;/td&gt;
&lt;td&gt;108.89&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;oq&lt;/td&gt;
&lt;td&gt;230.304&lt;/td&gt;
&lt;td&gt;0.74&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This test resulted in some more useful data, causing the true performance of each package to be more obvious.  &lt;code&gt;jq&lt;/code&gt; and &lt;code&gt;oq&lt;/code&gt; fared the best, being pretty comparable in both memory usage and execution time.  &lt;code&gt;yq (go)&lt;/code&gt; was the next best, using 3x the memory and took ~3.5x longer than &lt;code&gt;oq&lt;/code&gt;.  &lt;code&gt;yq (python)&lt;/code&gt; was by far the worse; using 12.7x the memory and taking 147x longer than &lt;code&gt;oq&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  YAML =&amp;gt; XML/JSON
&lt;/h3&gt;

&lt;p&gt;Consuming a ~57MB &lt;code&gt;YAML&lt;/code&gt; file, and outputting &lt;code&gt;XML&lt;/code&gt; and &lt;code&gt;JSON&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The file used: &lt;code&gt;sde/bsd/invItems.yaml&lt;/code&gt; from the &lt;a href="https://cdn1.eveonline.com/data/sde/tranquility/sde-20190625-TRANQUILITY.zip"&gt;EVE Online SDE Export&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Max Memory (MB)&lt;/th&gt;
&lt;th&gt;Wall Clock Time (seconds)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;jq&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yq (go) - JSON&lt;/td&gt;
&lt;td&gt;5,202.032&lt;/td&gt;
&lt;td&gt;32.77&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yq (python) - JSON&lt;/td&gt;
&lt;td&gt;5,742.736&lt;/td&gt;
&lt;td&gt;206.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yq (python) - XML&lt;/td&gt;
&lt;td&gt;5,742.704&lt;/td&gt;
&lt;td&gt;217.17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;oq - JSON&lt;/td&gt;
&lt;td&gt;575.6&lt;/td&gt;
&lt;td&gt;14.01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;oq - JSON - SimpleYAML&lt;/td&gt;
&lt;td&gt;334.632&lt;/td&gt;
&lt;td&gt;14.16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;oq - XML&lt;/td&gt;
&lt;td&gt;649.984&lt;/td&gt;
&lt;td&gt;15.77&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;oq - XML - SimpleYAML&lt;/td&gt;
&lt;td&gt;334.504&lt;/td&gt;
&lt;td&gt;15.83&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The final test showed similar metrics as the previous one.  &lt;code&gt;jq&lt;/code&gt; doesn't support non &lt;code&gt;JSON&lt;/code&gt; formats so it was excluded.  &lt;code&gt;oq&lt;/code&gt; did the best with the execution time being fairly stable, while the memory usage was halved when using the new &lt;code&gt;SimpleYAML&lt;/code&gt; format for the input.  Interestingly &lt;code&gt;yq (go)&lt;/code&gt; fared better execution wise in this test, being only 2.34x slower, but using ~15.5x more memory.  &lt;code&gt;yq (python)&lt;/code&gt; once again was the worse, with memory usage slightly worse than &lt;code&gt;yq (go)&lt;/code&gt;, but with an execution time of 15.5x slower than &lt;code&gt;oq&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  XML =&amp;gt; JSON
&lt;/h3&gt;

&lt;p&gt;Consume the output of the last benchmark, and convert it back to &lt;code&gt;JSON&lt;/code&gt;.  This test is mainly to not be biased as &lt;code&gt;yq (python)&lt;/code&gt; includes a dedicated sub-package &lt;code&gt;xq&lt;/code&gt; for handling &lt;code&gt;XML&lt;/code&gt; input/output.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Max Memory (MB)&lt;/th&gt;
&lt;th&gt;Wall Clock Time (seconds)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;jq&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yq (go)&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yq (python)&lt;/td&gt;
&lt;td&gt;795.06&lt;/td&gt;
&lt;td&gt;15.59&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;oq&lt;/td&gt;
&lt;td&gt;2600.964&lt;/td&gt;
&lt;td&gt;20.28&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Neither &lt;code&gt;jq&lt;/code&gt; or &lt;code&gt;yq (go)&lt;/code&gt; support &lt;code&gt;XML&lt;/code&gt; input, so they were excluded.  The dedicated sub-package paid off for &lt;code&gt;yq (python)&lt;/code&gt;, beating &lt;code&gt;oq&lt;/code&gt; by a fair margin in both memory consumption and execution time.  It looks like it's able to stream the &lt;code&gt;XML&lt;/code&gt; input, while &lt;code&gt;XML&lt;/code&gt; input for &lt;code&gt;oq&lt;/code&gt; is the only input format that &lt;em&gt;cannot&lt;/em&gt; be streamed (yet).&lt;/p&gt;

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

&lt;p&gt;It seems the performance of &lt;code&gt;yq (python)&lt;/code&gt; has gotten a bit better since last time.  Shaving off over 2GB of memory and 2 minutes of execution time in the &lt;code&gt;jeopardy.json&lt;/code&gt; benchmark.  However, it still is by far the slowest, and most memory hungry package (when not using &lt;code&gt;XML&lt;/code&gt; as the input format).  &lt;code&gt;yq (go)&lt;/code&gt; is in a better spot, but still has pretty high memory usage depending on the exact use case.  &lt;code&gt;oq&lt;/code&gt; seems to still be the most efficient overall, with both memory and execution time being on par with &lt;code&gt;jq&lt;/code&gt;.  Thanks to its focus on streaming of data when possible (all but &lt;code&gt;XML&lt;/code&gt; input at the moment); &lt;code&gt;oq&lt;/code&gt; is able to use much less memory comparatively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Simple
&lt;/h3&gt;

&lt;h4&gt;
  
  
  jq
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command being timed: &lt;span class="s2"&gt;"jq . data.json | wc -l"&lt;/span&gt;
User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.02
System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.00
Percent of CPU this job got: 100%
Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:00.02
Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 3040
Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 288
Voluntary context switches: 1
Involuntary context switches: 0
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Exit status: 0
31
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  yq (go)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command being timed: &lt;span class="s2"&gt;"./go-yq -Pj e . data.json | wc -l"&lt;/span&gt;
User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.01
System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.00
Percent of CPU this job got: 105%
Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:00.01
Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 6020
Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 617
Voluntary context switches: 76
Involuntary context switches: 2
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Exit status: 0
31
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the &lt;code&gt;-j&lt;/code&gt; and &lt;code&gt;-P&lt;/code&gt; options to replicate the default behavior of the other binaries.&lt;/p&gt;

&lt;h4&gt;
  
  
  yq (python)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command being timed: &lt;span class="s2"&gt;"yq . data.json | wc -l"&lt;/span&gt;
User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.06
System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.00
Percent of CPU this job got: 87%
Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:00.07
Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 14728
Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 4
Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 3637
Voluntary context switches: 43
Involuntary context switches: 1
Swaps: 0
File system inputs: 1328
File system outputs: 0
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Exit status: 0
31
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  oq
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command being timed: &lt;span class="s2"&gt;"oq . data.json | wc -l"&lt;/span&gt;
User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.06
System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.01
Percent of CPU this job got: 104%
Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:00.07
Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 6536
Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 808
Voluntary context switches: 58
Involuntary context switches: 2
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Exit status: 0
31
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Jeopardy.json
&lt;/h3&gt;

&lt;h4&gt;
  
  
  jq
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;216930
Command being timed: &lt;span class="s2"&gt;"jq length jeopardy.json"&lt;/span&gt;
User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.59
System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.06
Percent of CPU this job got: 99%
Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:00.66
Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 230320
Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 59454
Voluntary context switches: 1
Involuntary context switches: 26
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  yq (go)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;216930
Command being timed: &lt;span class="s2"&gt;"./go-yq e length jeopardy.json"&lt;/span&gt;
User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 3.41
System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.22
Percent of CPU this job got: 143%
Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:02.54
Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 705292
Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 176999
Voluntary context switches: 787
Involuntary context switches: 163
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  yq (python)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;216930
Command being timed: &lt;span class="s2"&gt;"yq length jeopardy.json"&lt;/span&gt;
User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 108.62
System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.86
Percent of CPU this job got: 100%
Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 1:48.89
Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 2922996
Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 816903
Voluntary context switches: 13512
Involuntary context switches: 586
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  oq
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;216930
Command being timed: &lt;span class="s2"&gt;"oq length jeopardy.json"&lt;/span&gt;
User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.70
System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.12
Percent of CPU this job got: 110%
Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:00.74
Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 230304
Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 59966
Voluntary context switches: 13569
Involuntary context switches: 7
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  YAML =&amp;gt; XML/JSON
&lt;/h3&gt;

&lt;h4&gt;
  
  
  jq
&lt;/h4&gt;

&lt;p&gt;N/A&lt;/p&gt;

&lt;h4&gt;
  
  
  yq (go)
&lt;/h4&gt;

&lt;h5&gt;
  
  
  JSON
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command being timed: &lt;span class="s2"&gt;"./go-yq -Pj e . invItems.yaml &amp;gt; invItems.go-yq.json"&lt;/span&gt;
User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 47.66
System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 1.46
Percent of CPU this job got: 149%
Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:32.77
Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 5202032
Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 1395040
Voluntary context switches: 37244
Involuntary context switches: 1922
Swaps: 0
File system inputs: 0
File system outputs: 138984
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  yq (python)
&lt;/h4&gt;

&lt;h5&gt;
  
  
  JSON
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command being timed: &lt;span class="s2"&gt;"yq . invItems.yaml &amp;gt; invItems.python-yq.json"&lt;/span&gt;
User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 205.35
System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 1.82
Percent of CPU this job got: 100%
Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 3:26.20
Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 5742736
Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 1587436
Voluntary context switches: 13384
Involuntary context switches: 1836
Swaps: 0
File system inputs: 0
File system outputs: 138984
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  XML
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command being timed: &lt;span class="s2"&gt;"yq -s -x --xml-root items --xml-dtd {"&lt;/span&gt;item&lt;span class="s2"&gt;": .[] | .} invItems.yaml &amp;gt; invItems.python-yq.xml"&lt;/span&gt;
User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 215.17
System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 2.03
Percent of CPU this job got: 100%
Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 3:37.17
Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 5742704
Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 1683522
Voluntary context switches: 32842
Involuntary context switches: 1031
Swaps: 0
File system inputs: 0
File system outputs: 195072
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  oq
&lt;/h4&gt;

&lt;h5&gt;
  
  
  JSON
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command being timed: &lt;span class="s2"&gt;"./oq -i yaml . invItems.yaml &amp;gt; invItems.oq.json"&lt;/span&gt;
User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 12.90
System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 12.24
Percent of CPU this job got: 179%
Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:14.01
Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 575600
Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 231391
Voluntary context switches: 289799
Involuntary context switches: 166
Swaps: 0
File system inputs: 0
File system outputs: 138984
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command being timed: &lt;span class="s2"&gt;"./oq -i simpleyaml . invItems.yaml &amp;gt; invItems.oq.simple.json"&lt;/span&gt;
User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 12.27
System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 12.18
Percent of CPU this job got: 172%
Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:14.16
Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 334632
Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 89229
Voluntary context switches: 2981745
Involuntary context switches: 246
Swaps: 0
File system inputs: 0
File system outputs: 138984
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  XML
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command being timed: &lt;span class="s2"&gt;"./oq -i yaml -o xml --xml-root items . invItems.yaml"&lt;/span&gt;
User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 16.00
System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 12.04
Percent of CPU this job got: 177%
Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:15.77
Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 649984
Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 249933
Voluntary context switches: 381174
Involuntary context switches: 266
Swaps: 0
File system inputs: 0
File system outputs: 195072
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;items&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;item&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;flagID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/flagID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;itemID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/itemID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;locationID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/locationID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ownerID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/ownerID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;quantity&amp;gt;&lt;/span&gt;-1&lt;span class="nt"&gt;&amp;lt;/quantity&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;typeID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/typeID&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;item&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;flagID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/flagID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;itemID&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/itemID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;locationID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/locationID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ownerID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/ownerID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;quantity&amp;gt;&lt;/span&gt;-1&lt;span class="nt"&gt;&amp;lt;/quantity&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;typeID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/typeID&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/items&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command being timed: &lt;span class="s2"&gt;"oq -i simpleyaml -o xml --xml-root items . invItems.yaml"&lt;/span&gt;
User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 15.27
System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 11.99
Percent of CPU this job got: 172%
Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:15.83
Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 334504
Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 89233
Voluntary context switches: 3029485
Involuntary context switches: 298
Swaps: 0
File system inputs: 0
File system outputs: 195072
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  XML =&amp;gt; JSON
&lt;/h3&gt;

&lt;h4&gt;
  
  
  jq
&lt;/h4&gt;

&lt;p&gt;N/A&lt;/p&gt;

&lt;h4&gt;
  
  
  yq (go)
&lt;/h4&gt;

&lt;p&gt;N/A&lt;/p&gt;

&lt;h4&gt;
  
  
  yq (python)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command being timed: &lt;span class="s2"&gt;"xq .items invItems.python-yq.xml &amp;gt; invItems.python-yq.xml.json"&lt;/span&gt;
User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 16.37
System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.39
Percent of CPU this job got: 107%
Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:15.59
Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 795060
Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 341577
Voluntary context switches: 14927
Involuntary context switches: 87
Swaps: 0
File system inputs: 0
File system outputs: 168064
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  oq
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command being timed: &lt;span class="s2"&gt;"./oq -i xml .items invItems.oq.xml &amp;gt; invItems.oq.xml.json"&lt;/span&gt;
User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 20.25
System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 16.87
Percent of CPU this job got: 183%
Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:20.28
Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 2600964
Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 766666
Voluntary context switches: 2117666
Involuntary context switches: 1249
Swaps: 0
File system inputs: 0
File system outputs: 168064
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>jq</category>
      <category>json</category>
      <category>python</category>
      <category>go</category>
    </item>
    <item>
      <title>Utilizing Macros &amp; Annotations in a Web Framework (Part 2)</title>
      <dc:creator>Blacksmoke16</dc:creator>
      <pubDate>Mon, 27 Jul 2020 17:26:16 +0000</pubDate>
      <link>https://dev.to/blacksmoke16/utilizing-macros-annotations-in-a-web-framework-part-2-1453</link>
      <guid>https://dev.to/blacksmoke16/utilizing-macros-annotations-in-a-web-framework-part-2-1453</guid>
      <description>&lt;p&gt;When I first wrote &lt;a href="https://dev.to/blacksmoke16/utilizing-macros-annotations-in-a-web-framework-3abk"&gt;Utilizing Macros &amp;amp; Annotations in a Web Framework&lt;/a&gt; I didn't intend on it being a series.  However since its creation, &lt;a href="https://github.com/athena-framework/athena"&gt;Athena&lt;/a&gt; has evolved quite a bit and so has my knowledge and understanding of both macros and annotations.  Because of this, I wanted to give an updated look at some of the patterns, approaches, and experiences I used/had with macros and annotations when creating the framework.&lt;/p&gt;

&lt;p&gt;Stay tuned for part 3 for a look into how I how annotations and macros to build a compile time service container for Athena's &lt;a href="https://athena-framework.github.io/dependency-injection/Athena/DependencyInjection.html"&gt;DependencyInjection&lt;/a&gt; component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Registering Routes
&lt;/h2&gt;

&lt;p&gt;In the previous article I mentioned that my ideal syntax would be annotating my controllers with a &lt;code&gt;@[ART::Controller]&lt;/code&gt; annotation, then iterate over these controller types to build out the routes.  However, after thinking about it more I actually like how it ended up, using an abstract class and iterating over the children of it.  This not only makes it a bit simpler to define a controller as you don't have to remember to add the annotation, but also allows defining common methods within that abstract class that could be useful to all controllers.  When paired with DI, it also makes it possible to define additional abstract controllers that inherit from the default one that could be reused for endpoints requiring similar dependencies/logic.&lt;/p&gt;

&lt;p&gt;I also found a workaround to being able to iterate over types with a specific annotation without the need for a parent type or module.  Since all types in Crystal inherit from an &lt;code&gt;Object&lt;/code&gt; class, we can combine the &lt;code&gt;all_subclasses&lt;/code&gt; and the &lt;code&gt;select&lt;/code&gt; methods to get the desired output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;annotation&lt;/span&gt; &lt;span class="no"&gt;MyAnn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nd"&gt;@[MyAnn]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;A&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;B&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nd"&gt;@[MyAnn]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;C&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="no"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all_subclasses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotation&lt;/span&gt; &lt;span class="no"&gt;MyAnn&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; [A, C]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Annotation Data at Runtime
&lt;/h2&gt;

&lt;p&gt;One of the main uses of annotations is to store metadata related to a type, method, or instance variable that is accessible at compile time.  This metadata could then be consumed at compile time to do various things.  However there is not a built in way, such as reflection, to also have access to this data at runtime.  A pattern I started using quite a lot combines the &lt;a href="https://crystal-lang.org/api/master/toplevel.html#record(name,*properties)-macro"&gt;record&lt;/a&gt; macro, modules, and the &lt;code&gt;macro included&lt;/code&gt;hook.&lt;/p&gt;

&lt;p&gt;The idea is that you define a module that defines methods within the including type that exposes an array, or some other data structure, of structs representing the data, most likely instance variables, related to that type.  The main benefits of this are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You are using methods, so you are able to access all the instance variables within that type&lt;/li&gt;
&lt;li&gt;You are using structs, so there is minimal performance overhead&lt;/li&gt;
&lt;li&gt;You are using modules, so it works for both structs and classes&lt;/li&gt;
&lt;li&gt;It allows you to use the annotations as a DSL that determine how each struct is instantiated&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Lets take a look at an example from Athena's &lt;a href="https://github.com/athena-framework/serializer"&gt;Serializer&lt;/a&gt; component.  This component defines an &lt;a href="https://athena-framework.github.io/serializer/Athena/Serializer/PropertyMetadata.html"&gt;ASR::PropertyMetadata&lt;/a&gt; struct.  The purpose of this struct is pretty clear; it stores metadata related to a property.  In other words, it describes the state of an instance variable; including its name, type, class it belongs to, etc.  The serializer component also defines the &lt;a href="https://athena-framework.github.io/serializer/Athena/Serializer/Serializable.html"&gt;ASR::Serializable&lt;/a&gt; module, similar to &lt;code&gt;JSON::Serializable&lt;/code&gt;, which defines two main methods: &lt;code&gt;#serialization_properties&lt;/code&gt; and &lt;code&gt;.deserialization_properties&lt;/code&gt;. Instead of having the module define methods tied to a specific format, the &lt;code&gt;ASR::Serializable&lt;/code&gt; module defines generic methods that return arrays of our &lt;code&gt;ASR::PropertyMetadata&lt;/code&gt; object.  This provides a generic interface that could be consumed in a format agnostic manner.&lt;/p&gt;

&lt;p&gt;A example of this concept (cut down for brevity) would look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Define an abstract struct to type our methods with&lt;/span&gt;
&lt;span class="n"&gt;abstract&lt;/span&gt; &lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;PropertyMetadataBase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Define a struct to store information about each instance variable&lt;/span&gt;
&lt;span class="kp"&gt;record&lt;/span&gt; &lt;span class="no"&gt;PropertyMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;IvarType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ClassType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;PropertyMetadataBase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;IvarType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;IvarType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ClassType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ClassType&lt;/span&gt;

&lt;span class="c1"&gt;# Define an annotation similar to `JSON::Field`'s `skip` option&lt;/span&gt;
&lt;span class="n"&gt;annotation&lt;/span&gt; &lt;span class="no"&gt;Skip&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Define our module&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Serializable&lt;/span&gt;
  &lt;span class="k"&gt;macro&lt;/span&gt; &lt;span class="nf"&gt;included&lt;/span&gt;
    &lt;span class="c1"&gt;# :nodoc:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deserialization_properties&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PropertyMetadataBase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="n"&gt;verbatim&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
        &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
          &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="vi"&gt;@type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Skip&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ivar&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
              &lt;span class="sx"&gt;%(PropertyMetadata(#{ivar.type}, #{@type}).new(
                name: #{ivar.name.stringify}
              ))&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="no"&gt;PropertyMetadataBase&lt;/span&gt;
        &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
      &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Define a type to test with&lt;/span&gt;
&lt;span class="kp"&gt;record&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Serializable&lt;/span&gt;

  &lt;span class="nd"&gt;@[Skip]&lt;/span&gt;
  &lt;span class="vi"&gt;@id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;pp&lt;/span&gt; &lt;span class="no"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deserialization_properties&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;# [&lt;/span&gt;
&lt;span class="c1"&gt;#   PropertyMetadata(String, User)(@name="name", @type=String, @class=User),&lt;/span&gt;
&lt;span class="c1"&gt;#   PropertyMetadata(Int32, User)(@name="age", @type=Int32, @class=User)&lt;/span&gt;
&lt;span class="c1"&gt;# ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main benefit of this approach is it combines the benefits of using annotations, with having runtime types to expose the same data.  For example, notice how we reject any instance variable that has the &lt;code&gt;Skip&lt;/code&gt; annotation, and that a &lt;code&gt;PropertyMetadata&lt;/code&gt; instance isn't created for that property.  The logic that consumes the data from this method need not worry about doing that filtering since it all happens at compile time.&lt;/p&gt;

&lt;p&gt;However, there is a limitation with this approach. The logic for handling the &lt;code&gt;Skip&lt;/code&gt; annotation for example, is hardcoded within the module.  Or in other words, there isn't a way to also expose user defined annotations as part of the metadata struct.  Mainly because there isn't currently way to get all the annotations on a given type, method, or instance variable (see &lt;a href="https://github.com/crystal-lang/crystal/issues/9322"&gt;this issue&lt;/a&gt;).  But also there isn't a built in way to represent an annotation (and its data) at runtime.  After a bit of thinking I managed to figure out a pretty decent workaround; use a &lt;code&gt;record&lt;/code&gt; defined by the user to represent the data read off of their custom annotation.&lt;/p&gt;

&lt;p&gt;If you read the &lt;a href="https://dev.to/blacksmoke16/utilizing-macros-annotations-in-a-web-framework-3abk#property-validation"&gt;Property Validation&lt;/a&gt; section from my previous article, you would remember how I had to define that hacky &lt;code&gt;ASSERTIONS&lt;/code&gt; constant so I knew the types of annotations to read, and what properties I should read off of each.  This was because there wasn't a way to get &lt;em&gt;all&lt;/em&gt; of the data within an annotation, you had to access each value by name/index using &lt;code&gt;Annotation#[]&lt;/code&gt;.  Fortunately since that last article,  &lt;code&gt;Annotation#args&lt;/code&gt; and &lt;code&gt;Annotation#named_args&lt;/code&gt; methods have been added (by me 😉 &lt;a href="https://github.com/crystal-lang/crystal/pull/7694"&gt;PR&lt;/a&gt;).  An example implementation of this would look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Define our annotation&lt;/span&gt;
&lt;span class="n"&gt;annotation&lt;/span&gt; &lt;span class="no"&gt;MyAnn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Define a record to store the data related to our annotation&lt;/span&gt;
&lt;span class="kp"&gt;record&lt;/span&gt; &lt;span class="no"&gt;MyAnnConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;
  &lt;span class="c1"&gt;# Apply our annotation to a method,&lt;/span&gt;
  &lt;span class="c1"&gt;# have it return an instance of our struct.&lt;/span&gt;
  &lt;span class="nd"&gt;@[MyAnn(1, name: "Jim")]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;MyAnnConfig&lt;/span&gt;
    &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
      &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="n"&gt;ann&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@def&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotation&lt;/span&gt; &lt;span class="no"&gt;MyAnn&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
      &lt;span class="no"&gt;MyAnnConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;ann&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="p"&gt;%}{{&lt;/span&gt;&lt;span class="n"&gt;ann&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splat&lt;/span&gt;&lt;span class="p"&gt;}},{%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%}{{&lt;/span&gt;&lt;span class="n"&gt;ann&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;named_args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;double_splat&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;pp&lt;/span&gt; &lt;span class="no"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; MyAnnConfig(@active=true, @id=1, @name="Jim")&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is quite a few noteworthy things to mention here.  The most obvious of which is that we can use default values.  Since the &lt;code&gt;active&lt;/code&gt; property was not supplied in the annotation, the default value of &lt;code&gt;true&lt;/code&gt; was used.  We are also able to use both positional and named arguments; positional annotation arguments will be provided as positional arguments and named annotation arguments will be provided as named arguments to the configuration struct.&lt;/p&gt;

&lt;p&gt;Another benefit is we get type safety related to the arguments supplied within the annotation.  If we defined our annotation as &lt;code&gt;@[MyAnn(1, name: "Jim", unknown_value: 192.1)]&lt;/code&gt;, it wouldn't compile with the error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="no"&gt;MyAnnConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"Jim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;unknown_value: &lt;/span&gt;&lt;span class="mf"&gt;192.1&lt;/span&gt;
&lt;span class="o"&gt;^--&lt;/span&gt;
&lt;span class="no"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="n"&gt;overload&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="s1"&gt;'w'&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;unknown_value: &lt;/span&gt;&lt;span class="no"&gt;Float64&lt;/span&gt;

&lt;span class="no"&gt;Overloads&lt;/span&gt; &lt;span class="ss"&gt;are:
&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="no"&gt;MyAnnConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also wouldn't compile if it was missing a value that didn't have a default, or if one of the values was not the correct type.  You would also be able to define additional methods on the configuration struct in additional to the standard getters.&lt;/p&gt;

&lt;p&gt;We can go one step further to allow for creating our configuration structs more dynamically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Define our annotation&lt;/span&gt;
&lt;span class="n"&gt;annotation&lt;/span&gt; &lt;span class="no"&gt;MyAnn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Define a record to store the data related to our annotation&lt;/span&gt;
&lt;span class="kp"&gt;record&lt;/span&gt; &lt;span class="no"&gt;MyAnnConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;# Define a const to hold our annotation types&lt;/span&gt;
&lt;span class="no"&gt;CUSTOM_ANNS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;

&lt;span class="c1"&gt;# Define a macro to "register" our types&lt;/span&gt;
&lt;span class="k"&gt;macro&lt;/span&gt; &lt;span class="nf"&gt;register_ann&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="no"&gt;CUSTOM_ANNS&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Register our annotation&lt;/span&gt;
&lt;span class="n"&gt;register_ann&lt;/span&gt; &lt;span class="no"&gt;MyAnn&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;
  &lt;span class="nd"&gt;@[MyAnn(1, name: "Jim")]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt;
    &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
      &lt;span class="c1"&gt;# Iterate over each of our registered annotations&lt;/span&gt;
      &lt;span class="c1"&gt;# assuming the struct name is ann name + "Config"&lt;/span&gt;
      &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ann_name&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="no"&gt;CUSTOM_ANNS&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
        &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="n"&gt;ann&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@def&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotation&lt;/span&gt; &lt;span class="n"&gt;ann_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
        &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;ann_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="no"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;ann&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="p"&gt;%}{{&lt;/span&gt;&lt;span class="n"&gt;ann&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splat&lt;/span&gt;&lt;span class="p"&gt;}},{%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%}{{&lt;/span&gt;&lt;span class="n"&gt;ann&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;named_args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;double_splat&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
      &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
    &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;pp&lt;/span&gt; &lt;span class="no"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; MyAnnConfig(@active=true, @id=1, @name="Jim")&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does create a naming convention related to our configuration structs, but it's manageable.  I think ideally the configuration struct would be merged with the &lt;code&gt;annotation&lt;/code&gt; definition.  This would centralize things to make the annotation itself the type that is applied and that stores the data at runtime.&lt;/p&gt;

&lt;p&gt;I had the idea to apply this concept of exposing user defined annotations at runtime to both the serializer and routing components of Athena.  The feature would allow for using more advanced annotation based runtime logic within your &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Listeners.html"&gt;ART::Listeners&lt;/a&gt;/&lt;a href="https://athena-framework.github.io/athena/Athena/Routing/ParamConverterInterface.html"&gt;ART::ParamConverterInterface&lt;/a&gt;s and/or &lt;a href="https://athena-framework.github.io/serializer/Athena/Serializer/ExclusionStrategies/ExclusionStrategyInterface.html#annotation-configurations"&gt;ASR::ExclusionStrategies::ExclusionStrategyInterface&lt;/a&gt;s.  Since I wanted this feature in two separate shards, I ended up creating a framework of sorts to make defining, registering, and using these annotation configurations easier as well as share code between the shards.&lt;/p&gt;

&lt;p&gt;This has since been released as part of the Athena &lt;a href="https://github.com/athena-framework/config"&gt;Config&lt;/a&gt; component; which can also be used outside of the Athena ecosystem.  The main concepts are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;a href="https://athena-framework.github.io/config/Athena/Config.html#configuration_annotation(name,*args,&amp;amp;)-macro"&gt;ACF.configuration_annotation&lt;/a&gt; - Used to both define and register an annotation, but also register the related configuration struct behind the scenes&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://athena-framework.github.io/config/Athena/Config/AnnotationConfigurations.html"&gt;ACF::AnnotationConfigurations&lt;/a&gt; - Wraps a hash keyed by annotation class that stores the configuration structs related to each annotation applied to a specific type, method, or instance variable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The wrapping type defines a custom &lt;code&gt;#[]&lt;/code&gt; method that allows accessing the configuration structs in a type safe way.  Athena's serializer component exposes annotation configurations as part of the &lt;code&gt;PropertyMetadata&lt;/code&gt; object.  Lets combine some of the examples to demonstrate how it all fits together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Define an abstract struct to type our methods with&lt;/span&gt;
&lt;span class="n"&gt;abstract&lt;/span&gt; &lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;PropertyMetadataBase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Define a struct to store information about each instance variable&lt;/span&gt;
&lt;span class="kp"&gt;record&lt;/span&gt; &lt;span class="no"&gt;PropertyMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;IvarType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ClassType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;PropertyMetadataBase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;annotation_configurations&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ACF&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;AnnotationConfigurations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;IvarType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;IvarType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ClassType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ClassType&lt;/span&gt;

&lt;span class="c1"&gt;# Define our module&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Serializable&lt;/span&gt;
  &lt;span class="k"&gt;macro&lt;/span&gt; &lt;span class="nf"&gt;included&lt;/span&gt;
    &lt;span class="c1"&gt;# :nodoc:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deserialization_properties&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PropertyMetadataBase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="n"&gt;verbatim&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
        &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
          &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="vi"&gt;@type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ivar&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
              &lt;span class="c1"&gt;# Use the code from the `ACF::AnnotationConfigurations` type to resolve the applied annotations.&lt;/span&gt;
              &lt;span class="n"&gt;annotation_configurations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;

              &lt;span class="no"&gt;ACF&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CUSTOM_ANNOTATIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ann_class&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
                &lt;span class="n"&gt;ann_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ann_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;
                &lt;span class="n"&gt;annotations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;

                &lt;span class="n"&gt;ivar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ann_class&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ann&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
                  &lt;span class="n"&gt;pos_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ann&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"Tuple.new"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ann&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;
                  &lt;span class="n"&gt;named_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ann&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;named_args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"NamedTuple.new"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ann&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;named_args&lt;/span&gt;

                  &lt;span class="n"&gt;annotations&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;ann_class&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;Configuration.new(&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;ann&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;ann&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}#{&lt;/span&gt;&lt;span class="n"&gt;ann&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;named_args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;double_splat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
                &lt;span class="k"&gt;end&lt;/span&gt;

                &lt;span class="n"&gt;annotation_configurations&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ann_class&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;annotations&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; of ACF::AnnotationConfigurations::ConfigurationBase"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
              &lt;span class="k"&gt;end&lt;/span&gt;

              &lt;span class="sx"&gt;%(PropertyMetadata(#{ivar.type}, #{@type}).new(
                name: #{ivar.name.stringify},
                annotation_configurations: ACF::AnnotationConfigurations.new(#{annotation_configurations} of ACF::AnnotationConfigurations::Classes =&amp;gt; Array(ACF::AnnotationConfigurations::ConfigurationBase)),
              ))&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="no"&gt;PropertyMetadataBase&lt;/span&gt;
        &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
      &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Define and register our custom annotations&lt;/span&gt;
&lt;span class="no"&gt;ACF&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configuration_annotation&lt;/span&gt; &lt;span class="no"&gt;MyAnn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="no"&gt;ACF&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configuration_annotation&lt;/span&gt; &lt;span class="no"&gt;OtherAnn&lt;/span&gt;

&lt;span class="c1"&gt;# Define a type to test with&lt;/span&gt;
&lt;span class="kp"&gt;record&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Serializable&lt;/span&gt;

  &lt;span class="nd"&gt;@[MyAnn(1)]&lt;/span&gt;
  &lt;span class="vi"&gt;@id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;

  &lt;span class="nd"&gt;@[MyAnn(2, active: false)]&lt;/span&gt;
  &lt;span class="vi"&gt;@age&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;properties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deserialization_properties&lt;/span&gt;

&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;annotation_configurations&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;MyAnn&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;active&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; true&lt;/span&gt;
&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;annotation_configurations&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;MyAnn&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;active&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; false&lt;/span&gt;

&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;annotation_configurations&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;OtherAnn&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;active&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; Error: undefined method 'active' for OtherAnnConfiguration&lt;/span&gt;
&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;annotation_configurations&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;OtherAnn&lt;/span&gt;&lt;span class="p"&gt;]?&lt;/span&gt;       &lt;span class="c1"&gt;# =&amp;gt; nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added an &lt;code&gt;annotation_configurations&lt;/code&gt; instance variable to our &lt;code&gt;PropertyMetadata&lt;/code&gt; type, used the code from &lt;code&gt;ACF::AnnotationConfigurations&lt;/code&gt; create the annotation configurations instance to provide to the metadata object.  I then used the &lt;code&gt;deserialization_properties&lt;/code&gt; method to access our configurations, using the &lt;code&gt;annotation_configurations&lt;/code&gt; to get a reference to the wrapping type.  The &lt;code&gt;#[]&lt;/code&gt; method accepts the annotation class you wish to fetch.  Notice that you get type safety when using getters for the properties you specified when registering the annotation.  &lt;code&gt;#[]?&lt;/code&gt; is also defined that returns &lt;code&gt;nil&lt;/code&gt; if the specified annotation was not applied.  A &lt;code&gt;#has?(type)&lt;/code&gt; method is also available that returns &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; depending on if any annotations of the provided &lt;em&gt;type&lt;/em&gt; were applied to that property.  In the end this abstraction layer provides a common standardized interface to use in both shards, as well as makes the required naming convention of the configuration structs an implementation detail.  &lt;/p&gt;

&lt;p&gt;This feature greatly increases the flexibility of both the serializer and routing components (or whatever it is implemented within).  An example of how this feature could be used is imagine you define &lt;code&gt;IgnoreOnCreate&lt;/code&gt; and/or &lt;code&gt;IgnoreOnUpdate&lt;/code&gt; configuration annotations.  You could then define an exclusion strategy that injects the &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/RequestStore.html"&gt;ART::RequestStore&lt;/a&gt; to know what &lt;code&gt;HTTP&lt;/code&gt; method the current request is.  Logic could then be implemented that would skip properties with the &lt;code&gt;IgnoreOnCreate&lt;/code&gt; annotation during &lt;code&gt;POST&lt;/code&gt; requests and skip &lt;code&gt;IgnoreOnUpdate&lt;/code&gt; properties on &lt;code&gt;PUT&lt;/code&gt; requests, while still exposing them on &lt;code&gt;GET&lt;/code&gt; requests.  On the routing side of things, you could imagine a &lt;code&gt;Pagination&lt;/code&gt; or &lt;code&gt;RateLimited&lt;/code&gt; annotation used to configure a related event listener.&lt;/p&gt;

&lt;p&gt;As usual feel free to join me in the Athena &lt;a href="https://gitter.im/athena-frameworkcr/community"&gt;Gitter&lt;/a&gt; channel if you have any suggestions, questions, or ideas. I'm also available on Discord (&lt;code&gt;Blacksmoke16#0016&lt;/code&gt;) or via &lt;a href="//mailto:george@dietrich.app"&gt;Email&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>crystal</category>
      <category>metaprogramming</category>
    </item>
    <item>
      <title>Athena 0.9.0</title>
      <dc:creator>Blacksmoke16</dc:creator>
      <pubDate>Sat, 13 Jun 2020 01:18:42 +0000</pubDate>
      <link>https://dev.to/blacksmoke16/athena-0-9-0-i77</link>
      <guid>https://dev.to/blacksmoke16/athena-0-9-0-i77</guid>
      <description>&lt;h1&gt;
  
  
  Athena 0.9.0
&lt;/h1&gt;

&lt;p&gt;With &lt;a href="https://crystal-lang.org/2020/06/09/crystal-0.35.0-released.html"&gt;Crystal 0.35.0&lt;/a&gt; finally released, I'm happy to also announce the release of &lt;a href="https://github.com/athena-framework/athena/releases/tag/v0.9.0"&gt;Athena 0.9.0&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;This release focused on further refining the overall foundation of the framework.  This includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new &lt;a href="https://athena-framework.github.io/athena/Athena/Routing.html#getting-started"&gt;Getting Started&lt;/a&gt; section&lt;/li&gt;
&lt;li&gt;An overhaul of the DI framework&lt;/li&gt;
&lt;li&gt;A refactor of how controller action arguments are resolved&lt;/li&gt;
&lt;li&gt;A refactor of how param converters work&lt;/li&gt;
&lt;li&gt;Compile time route collision detection&lt;/li&gt;
&lt;li&gt;Some additional minor QoL improvements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See the linked types for more details/examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  DI Overhaul
&lt;/h2&gt;

&lt;p&gt;Athena &lt;code&gt;0.9.0&lt;/code&gt; comes bundled with &lt;a href="https://github.com/athena-framework/dependency-injection/releases/tag/v0.2.0"&gt;Athena Dependency Injection 0.2.0&lt;/a&gt;, which overhauls the implementation of the service container; making the usage of it simpler, more feature rich, easier to maintain, and more robust.&lt;/p&gt;

&lt;p&gt;In version &lt;code&gt;0.1.x&lt;/code&gt;, services were required to include the &lt;code&gt;ADI::Service&lt;/code&gt; module, as well as specify any service dependencies within the &lt;a href="https://athena-framework.github.io/dependency-injection/Athena/DependencyInjection/Register.html"&gt;ADI::Register&lt;/a&gt; annotation as position arguments.  This is no longer required as dependencies are now resolved automatically based on type restrictions.  The module is also no longer required.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; I'm using records for brevity, a &lt;code&gt;struct&lt;/code&gt; service behaves differently than a &lt;code&gt;class&lt;/code&gt; service.  Be sure to pick the right one for your use case.  See &lt;a href="https://athena-framework.github.io/dependency-injection/Athena/DependencyInjection/Register.html#overview"&gt;the docs&lt;/a&gt; for more details.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Before&lt;/span&gt;
&lt;span class="nd"&gt;@[ADI::Register]&lt;/span&gt;
&lt;span class="kp"&gt;record&lt;/span&gt; &lt;span class="no"&gt;ServiceOne&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ADI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Service&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nd"&gt;@[ADI::Register("@my_service", true)]&lt;/span&gt;
&lt;span class="kp"&gt;record&lt;/span&gt; &lt;span class="no"&gt;ServiceTwo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ServiceOne&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Bool&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ADI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Service&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# After&lt;/span&gt;
&lt;span class="nd"&gt;@[ADI::Register]&lt;/span&gt;
&lt;span class="kp"&gt;record&lt;/span&gt; &lt;span class="no"&gt;ServiceOne&lt;/span&gt;

&lt;span class="nd"&gt;@[ADI::Register(_debug: true)]&lt;/span&gt;
&lt;span class="kp"&gt;record&lt;/span&gt; &lt;span class="no"&gt;ServiceTwo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ServiceOne&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Bool&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, only services can be auto resolved, however &lt;a href="https://athena-framework.github.io/dependency-injection/Athena/DependencyInjection.html#bind(key,value)-macro"&gt;ADI.bind&lt;/a&gt; can be use to allow auto resolving &lt;a href="https://athena-framework.github.io/dependency-injection/Athena/DependencyInjection/Register.html#scalar-arguments"&gt;Scalar Arguments&lt;/a&gt; and &lt;a href="https://athena-framework.github.io/dependency-injection/Athena/DependencyInjection/Register.html#tagging-services"&gt;Tagged Services&lt;/a&gt;.  &lt;a href="https://athena-framework.github.io/dependency-injection/Athena/DependencyInjection/Register.html#aliasing-services"&gt;Service Aliases&lt;/a&gt; can be used to define a "default" service for a given interface.  Service dependencies can also be declared as &lt;a href="https://athena-framework.github.io/dependency-injection/Athena/DependencyInjection/Register.html#optional-services"&gt;optional&lt;/a&gt; or even based on &lt;a href="https://athena-framework.github.io/dependency-injection/Athena/DependencyInjection/Register.html#generic-services"&gt;Generic Services&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Action Handling Refactor
&lt;/h2&gt;

&lt;p&gt;The logic that resolves a specific action argument has been decoupled from the logic that resolves the arguments for an action.  This is mainly a behind the scenes change, but does come with some user facing changes.  The main one being the request's attributes.  Prior versions of Athena exposed a &lt;code&gt;Hash&lt;/code&gt; on the &lt;code&gt;HTTP::Request&lt;/code&gt; object that could be used to store arbitrary data specific to the request's life-cycle.  This has been replaced with &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/ParameterBag.html"&gt;ART::ParameterBag&lt;/a&gt;; the concept is the same, but the API is better and is more robust.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ART::ParameterBag&lt;/code&gt; is also where the path/query parameters are stored.  In fact, any value stored within it is able to be automatically provided to the controller action.  This is accomplished via &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Arguments/Resolvers/RequestAttribute.html"&gt;ART::Arguments::Resolvers::RequestAttribute&lt;/a&gt; which looks for a value in the bag with the same name as the controller argument.  Custom resolves can also be defined by creating an implementation of &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Arguments/Resolvers/ArgumentValueResolverInterface.html"&gt;ART::Arguments::Resolvers::ArgumentValueResolverInterface&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Param Converter Refactor
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://athena-framework.github.io/athena/Athena/Routing/ParamConverterInterface.html"&gt;ART::ParamConverterInterface&lt;/a&gt;s have undergone a rewrite.  The API has changed to no longer return the converted value.  It should instead, most commonly, be stored in the request's attributes.  Param converters are now also services themselves; this allows using DI to supply any require dependencies needed for the conversion process.  The concept of &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/ParamConverterInterface/ConfigurationInterface.html"&gt;ART::ParamConverterInterface::ConfigurationInterface&lt;/a&gt; has also been introduced to allow defining extra configuration data that should be read from the &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/ParamConverter.html"&gt;ART::ParamConverter&lt;/a&gt; annotation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"athena"&lt;/span&gt;

&lt;span class="nd"&gt;@[ADI::Register]&lt;/span&gt;
&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;MultiplyConverter&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ParamConverterInterface&lt;/span&gt;
  &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;

  &lt;span class="c1"&gt;# :inherit:&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
    &lt;span class="n"&gt;arg_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has?&lt;/span&gt; &lt;span class="n"&gt;arg_name&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt; &lt;span class="n"&gt;arg_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt; &lt;span class="n"&gt;arg_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;by&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ParamConverterController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;
  &lt;span class="nd"&gt;@[ART::Get(path: "/multiply/:num")]&lt;/span&gt;
  &lt;span class="nd"&gt;@[ART::ParamConverter("num", converter: MultiplyConverter, by: 4)]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;
    &lt;span class="n"&gt;num&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;

&lt;span class="c1"&gt;# GET /multiply/3 # =&amp;gt; 12&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A built in &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/TimeConverter.html"&gt;ART::TimeConverter&lt;/a&gt; that converts a date(time) string into a &lt;code&gt;Time&lt;/code&gt; instance has also been added.&lt;/p&gt;

&lt;h2&gt;
  
  
  Route Collision Detection
&lt;/h2&gt;

&lt;p&gt;Athena will now raise a compile time error if two routes share the same path; either statically, or with path arguments in the same locations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;
  &lt;span class="nd"&gt;@[ART::Get(path: "some/path/:id")]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;action1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int64&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OtherController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;
  &lt;span class="nd"&gt;@[ART::Get(path: "some/path/:id")]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;action2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int64&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;

&lt;span class="c1"&gt;# #=&amp;gt; Route action OtherController#action2's path "/some/path/:id" conflicts with TestController#action1's path "/some/path/:id".&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Other Minor Improvements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduced the concept of &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Response/Writer.html"&gt;ART::Response::Writer&lt;/a&gt; to control &lt;em&gt;how&lt;/em&gt; the response content is written to the response IO&lt;/li&gt;
&lt;li&gt;Allow &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Response.html#new(status:HTTP::Status|Int32=HTTP::Status::OK,headers:HTTP::Headers=HTTP::Headers.new,&amp;amp;block:IO-&amp;gt;Nil)-class-method"&gt;ART::Response&lt;/a&gt; to write directly to the response IO&lt;/li&gt;
&lt;li&gt;Add some additional &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Exceptions.html"&gt;ART::Exceptions&lt;/a&gt; types, and make defining custom types easier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As usual feel free to join me in the Athena &lt;a href="https://gitter.im/athena-frameworkcr/community"&gt;Gitter&lt;/a&gt; channel if you have any suggestions, questions, or ideas. I'm also available on Discord (&lt;code&gt;Blacksmoke16#0016&lt;/code&gt;) or via &lt;a href="//mailto:george@dietrich.app"&gt;Email&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>crystal</category>
      <category>ruby</category>
      <category>symfony</category>
      <category>framework</category>
    </item>
    <item>
      <title>The Rebirth of Athena</title>
      <dc:creator>Blacksmoke16</dc:creator>
      <pubDate>Tue, 11 Feb 2020 19:46:35 +0000</pubDate>
      <link>https://dev.to/blacksmoke16/the-rebirth-of-athena-4al0</link>
      <guid>https://dev.to/blacksmoke16/the-rebirth-of-athena-4al0</guid>
      <description>&lt;h1&gt;
  
  
  Rebirth
&lt;/h1&gt;

&lt;p&gt;The past year has been quite the journey for &lt;a href="https://github.com/athena-framework/athena"&gt;Athena&lt;/a&gt;.  It felt like not too long ago when I released version &lt;code&gt;0.1.0&lt;/code&gt;, however now, after a lot of thinking, multiple iterations, and 7 minor versions later, I'm proud to announce the release of &lt;code&gt;0.8.0&lt;/code&gt;, or what I like to call &lt;code&gt;Rebirth&lt;/code&gt;.  This release brings about changes to nearly every part of Athena, both user facing and internally, as well as changes to the &lt;a href="https://github.com/athena-framework/athena/issues/34"&gt;vision&lt;/a&gt; and &lt;a href="https://github.com/athena-framework"&gt;organizational&lt;/a&gt; future of the Athena Framework itself.&lt;/p&gt;

&lt;p&gt;Since its been a while since my last post, I thought I would take this opportunity to highlight some of the changes introduced in this latest version as well as give an update on the roadmap/vision for the framework as a whole.  This blog post isn't intended to be a tutorial on how to use these features, I will however provide plenty of links out to examples or documentation either in the &lt;a href="https://github.com/Blacksmoke16/athena-blog-tutorial"&gt;Blog Demo App&lt;/a&gt;/&lt;a href="https://dev.to/blacksmoke16/creating-a-json-api-with-athena--granite-510i"&gt;Blog Post&lt;/a&gt; or the &lt;a href="https://athena-framework.github.io/athena/Athena/Routing.html"&gt;API docs&lt;/a&gt;.  Also, feel free to join me in the Athena &lt;a href="https://gitter.im/athena-frameworkcr/community"&gt;Gitter&lt;/a&gt; channel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Organizational
&lt;/h2&gt;

&lt;p&gt;As part of this latest release, I took the time to make some organizational changes; this resulted in the creation of the &lt;a href="https://github.com/athena-framework"&gt;athena-framework&lt;/a&gt; Github organization, which will be the home to all of the frameworks components.  Related to this, I  moved some of the components that were included in &lt;code&gt;athena&lt;/code&gt; core into the organization; namely &lt;a href="https://github.com/athena-framework/dependency-injection"&gt;dependency-injection&lt;/a&gt; and &lt;a href="https://github.com/athena-framework/config"&gt;config&lt;/a&gt;.  The main benefit of this is it allows other shards, outside of the Athena Framework to use each component independently from one another.  The longer term goal is creating a common set of useful components that can be used by others, versus everyone creating a slightly different version of the same thing.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;0.8.0&lt;/code&gt; also removed the direct dependencies on &lt;a href="https://github.com/Blacksmoke16/crylog"&gt;crylog&lt;/a&gt; and &lt;a href="https://github.com/Blacksmoke16/CrSerializer"&gt;CrSerializer&lt;/a&gt;.  These, along with &lt;a href="https://github.com/Blacksmoke16/assert"&gt;assert&lt;/a&gt; are going to be reworked to be more DI friendly, as well as work out some issues with their implementations I found along the way.  They will then be moved into the &lt;code&gt;athena-framework&lt;/code&gt; organization.  The plan for how these will be reintegrated into &lt;code&gt;Athena&lt;/code&gt; will be discussed in the DependencyInjection section.&lt;/p&gt;

&lt;p&gt;Athena makes use of namespaces to group common types together, both for organizational and documentation purposes.  This practice however can lead to really long names, such as &lt;code&gt;Athena::Routing::Exceptions::NotFoundException.new ""&lt;/code&gt; as you might have used once or twice.  Unfortunately without an import system, like ES6 for example, there isn't really a way around this.  In order to help alleviate this issue in the mean time, I created various three letter acronyms that point to each component's namespace.  For example, &lt;a href="https://athena-framework.github.io/athena/ART.html"&gt;ART&lt;/a&gt;  for &lt;code&gt;Athena::Routing&lt;/code&gt;, or &lt;a href="https://athena-framework.github.io/athena/AED.html"&gt;AED&lt;/a&gt; for &lt;code&gt;Athena::EventDispatcher&lt;/code&gt;.  Using these aliases in &lt;code&gt;0.8.0&lt;/code&gt; the previous example would now be &lt;code&gt;ART::Exceptions::NotFound.new ""&lt;/code&gt;.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Event Dispatcher
&lt;/h2&gt;

&lt;p&gt;The most impactful change was the implementation of a &lt;a href="https://github.com/athena-framework/event-dispatcher"&gt;Mediator and Observer pattern event library&lt;/a&gt; to handle the core logic of handling a request, as well as a replacement for &lt;code&gt;HTTP::Handler&lt;/code&gt; style middleware.  Athena internally now operates by emitting various &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Events.html"&gt;events&lt;/a&gt; that can be listened on in order to handle a request.  Athena defines various &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Listeners.html"&gt;listeners&lt;/a&gt; itself as well; including for routing, CORS, exceptions, and a view layer, which I will get to soon.  Each event contains information related to the event, for example the &lt;code&gt;Request&lt;/code&gt; event contains the current request, while the &lt;code&gt;Exception&lt;/code&gt; event contains the current request and the exception that was raised.&lt;/p&gt;

&lt;p&gt;The main advantage of this approach is it is much easier to add middleware to the framework since an array of &lt;code&gt;HTTP::Handler&lt;/code&gt; do not have to be supplied up front.  The listener approach is also much more flexible since the order of execution can be more easily controlled, versus being based on the order of the handler array.  Listeners could be defined in external shards and just by requiring it, the listener(s) would automatically be registered.&lt;/p&gt;

&lt;p&gt;The other big advantage is listeners support DI, so other dependencies can be injected into the listener as needed.  An example of this could be a &lt;a href="https://github.com/Blacksmoke16/athena-blog-tutorial/blob/master/src/listeners/security_listener.cr"&gt;security listener&lt;/a&gt; on the &lt;code&gt;Request&lt;/code&gt; event in order to authenticate a request; lookup the corresponding user, and set it within a service that exposes that user to the rest of the application.  Another example could be listening on exceptions in order to log information about them for analytics or monitoring.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[ADI::Register("@logger", tags: ["athena.event_dispatcher.listener"]&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="c1"&gt;# Define a listener to handle authenticating requests.&lt;/span&gt;
&lt;span class="c1"&gt;# Also register it as a service and give it the proper tag for it to be automatically registered.&lt;/span&gt;
&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;MonitoringListener&lt;/span&gt;
  &lt;span class="c1"&gt;# Define the interface to implement the required methods&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;AED&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EventListenerInterface&lt;/span&gt;

  &lt;span class="c1"&gt;# Define this type as a service for DI to pick up.&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ADI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Service&lt;/span&gt;

  &lt;span class="c1"&gt;# Specify that we want to listen on the `Exception` event.&lt;/span&gt;
  &lt;span class="c1"&gt;# The value of the has represents this listener's priority;&lt;/span&gt;
  &lt;span class="c1"&gt;# the higher the value the sooner it gets executed.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribed_events&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;AED&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SubscribedEvents&lt;/span&gt;
    &lt;span class="no"&gt;AED&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SubscribedEvents&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Define our initializer for DI to inject a logger instance.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@logger&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;LoggerInterface&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Define a `#call` method scoped to the `Exception` event.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_dispatcher&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;AED&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EventDispatcherInterface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
     &lt;span class="c1"&gt;# Log the exception message&lt;/span&gt;
     &lt;span class="vi"&gt;@logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;

    &lt;span class="c1"&gt;# Do whatever else you want to do with the event, including emitting other events&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Custom events can also be defined that can be emitted from user code, either from another listener, controller action, etc.  The &lt;code&gt;EventDispatcher&lt;/code&gt; component can also be used independently outside of Athena. &lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency Injection (DI)
&lt;/h2&gt;

&lt;p&gt;The change to a listener based approach originated due to another flaw in the &lt;code&gt;HTTP::Handler&lt;/code&gt; approach; they do not play nicely with DI.  The main issue is that they are instantiated once when you create the &lt;code&gt;HTTP::Server&lt;/code&gt;, outside of the request life-cycle, thus the service container is not available to inject dependencies, nor can the middleware exist as a service itself.&lt;/p&gt;

&lt;p&gt;This fact, along with how common middleware is, made me want to rethink the overall design of Athena to be more DI friendly.  As mentioned in the &lt;a href="https://github.com/athena-framework/athena/issues/34#interfaces-and-di"&gt;Interfaces &amp;amp; DI&lt;/a&gt; section in the vision issue, the ultimate goal is to make Athena solely depend on interfaces versus concrete types.  This not only allows for a better DI implementation, but also make custom implementations, and testing easier.&lt;/p&gt;

&lt;p&gt;The plan for the serializer and logger components is that they are optional; but if installed and required, have an &lt;code&gt;ext&lt;/code&gt;, or &lt;code&gt;plugin&lt;/code&gt;, or something, file that would better integrate it into the rest of the framework.  An example of this could be defining some types from that component as services, or defining a basic implementation of an interface for use within Athena.  For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ext/logger.cr&lt;/span&gt;
&lt;span class="nd"&gt;@[ADI::Register(name: "logger")]&lt;/span&gt;
&lt;span class="c1"&gt;# Define a basic implementation of a logger service for services to inject&lt;/span&gt;
&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Logger&lt;/span&gt;
  &lt;span class="c1"&gt;# Be sure it adheres to the interface.  This also would allow&lt;/span&gt;
  &lt;span class="c1"&gt;# external/third-party code to define their own implementation&lt;/span&gt;
  &lt;span class="c1"&gt;# of the logger service to use&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;LoggerInterface&lt;/span&gt;

  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# some_controller.cr&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"athena/ext/logger"&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SomeController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ADI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Injectable&lt;/span&gt;

  &lt;span class="c1"&gt;# The logger could be injected into anything logging is needed.&lt;/span&gt;
  &lt;span class="c1"&gt;# Each request would have its own logger instance&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@logger&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;LoggerInterface&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nd"&gt;@[ART::Get("some/path")]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;some_path&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
    &lt;span class="vi"&gt;@logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;"Some message"&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Internally, Athena would utilize &lt;a href="https://athena-framework.github.io/dependency-injection/Athena/DependencyInjection/Register.html#optional-dependencies"&gt;optional services&lt;/a&gt; to manage this.  I.e. by default no logging, but if a &lt;code&gt;logger&lt;/code&gt; service is registered and available, use that; such as for debug information on each request, or logging exceptions etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  ErrorRendererInterface
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;ErrorRendererInterface&lt;/code&gt; is a perfect example of how DI fits into the framework.  The &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/ErrorRenderer.html"&gt;default&lt;/a&gt; error renderer will JSON serialize the exception and return that string to the client.  However, this behavior is customizable by &lt;a href="https://athena-framework.github.io/dependency-injection/Athena/DependencyInjection/Register.html#redefining-services"&gt;redefining&lt;/a&gt; the &lt;code&gt;error_renderer&lt;/code&gt; service; if for example you wish to return an HTML error page or something.&lt;/p&gt;

&lt;h2&gt;
  
  
  View Layer
&lt;/h2&gt;

&lt;p&gt;In previous versions of Athena, the response format was not very customizable.  The implementation was tied to some type that defines a &lt;code&gt;render&lt;/code&gt; class method on it.  Once again this does not fit into the DI oriented framework I so desire, so I had to rethink the implementation to better handle that as well as improve the overall flexibility of the framework.&lt;/p&gt;

&lt;p&gt;The solution to this is two fold, the &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Response.html"&gt;ART::Response&lt;/a&gt; type and the &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Events/View.html"&gt;view&lt;/a&gt; event.&lt;/p&gt;

&lt;h3&gt;
  
  
  ART::Response
&lt;/h3&gt;

&lt;p&gt;The concept behind the &lt;code&gt;ART::Response&lt;/code&gt; type is that it represents a "pending" response to the client.  It can be mutated, body rewritten etc, all without affecting the actual &lt;code&gt;HTTP::Server::Response&lt;/code&gt;.  The idea behind it is that a controller action can either return an &lt;code&gt;ART::Response&lt;/code&gt; or not; the behavior of the framework changes slightly if the return value is an &lt;code&gt;ART::Response&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If a controller action returns an &lt;code&gt;ART::Response&lt;/code&gt;, or a subclass of it like &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/RedirectResponse.html"&gt;ART::RedirectResponse&lt;/a&gt;, then that object is used directly as the response to the client; body, status, and headers are simply copied over to the actual &lt;code&gt;HTTP::Server::Response&lt;/code&gt;.  This allows an action to return arbitrary data easily to fulfill simple use cases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;
  &lt;span class="nd"&gt;@[ART::Get("/css")]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;css&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Response&lt;/span&gt;
    &lt;span class="c1"&gt;# A controller action returning CSS.&lt;/span&gt;
    &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s2"&gt;".some_class { color: blue; }"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;headers: &lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"content-type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;MIME&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;".css"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another thing that came from this is the &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Controller.html#render(template)-macro"&gt;render&lt;/a&gt; macro; it provides a simple way to render &lt;code&gt;ECR&lt;/code&gt; templates.  The variables used in the template can come directly from the action's arguments, such as the example in the API docs.  This feature combined with &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/ParamConverterInterface.html"&gt;ParamConverterInterface&lt;/a&gt; can make for a very easy way to render a user's profile for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[ART::Get("/user/:id/profile")]&lt;/span&gt;
&lt;span class="nd"&gt;@[ART::ParamConverter("user", converter: DBConverter(User))]&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Response&lt;/span&gt;
  &lt;span class="c1"&gt;# Render the user profile for the user with the provided ID.&lt;/span&gt;
  &lt;span class="c1"&gt;# The template has access to the full User object.&lt;/span&gt;
  &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"user_profile.ecr"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ART::Events::View
&lt;/h3&gt;

&lt;p&gt;The second part of the view layer is the &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Events/View.html"&gt;view&lt;/a&gt; event, and the corresponding default &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Listeners/View.html"&gt;listener&lt;/a&gt;.  When a controller action's return value is &lt;em&gt;NOT&lt;/em&gt; an &lt;code&gt;ART::Response&lt;/code&gt;, the view event is emitted.  The job of the listeners listening on the event is to convert the resulting value into an &lt;code&gt;ART::Response&lt;/code&gt;.  The default listener does this by JSON serializing the value, by default using the standard library's &lt;code&gt;#to_json&lt;/code&gt; method, or in the future, optionally by the &lt;code&gt;serializer&lt;/code&gt; component if it is installed&lt;/p&gt;

&lt;p&gt;In the future, a format negotiation algorithm will be implemented to call the correct renderer based on the request's &lt;code&gt;Accept&lt;/code&gt; headers.  For now, if you wish to define a global custom format for your routes, you have a few options.  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define a custom &lt;code&gt;view&lt;/code&gt; listener that runs before the default one (future listeners will not run once &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Events/SettableResponse.html#response=(response:ART::Response):Nil-instance-method"&gt;#response=&lt;/a&gt; is called)&lt;/li&gt;
&lt;li&gt;Subclass &lt;code&gt;ART::Response&lt;/code&gt; to encapsulate your logic&lt;/li&gt;
&lt;li&gt;Define a helper method/macro on &lt;code&gt;ART::Controller&lt;/code&gt; to encapsulate your logic&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Overall these changes make Athena a whole lot more flexible, making it viable to render HTML, or anything else that is required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Routing
&lt;/h2&gt;

&lt;p&gt;While not much changed in the routing aspect of Athena, I do want to point out some of the minor changes/additions that did occur.&lt;/p&gt;

&lt;h3&gt;
  
  
  QueryParams
&lt;/h3&gt;

&lt;p&gt;In previous versions, query parameters were included directly within the HTTP method annotation as a &lt;code&gt;NamedTuple&lt;/code&gt;.  In &lt;code&gt;0.8.0&lt;/code&gt;, &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/QueryParam.html"&gt;QueryParams&lt;/a&gt; are now defined by their own dedicated annotation.  They still support constraints, and param converters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[ART::Get("/example")]&lt;/span&gt;
&lt;span class="nd"&gt;@[ART::QueryParam("value")]&lt;/span&gt; &lt;span class="c1"&gt;# Is typed as a string due to `name : String`&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Macro DSL
&lt;/h3&gt;

&lt;p&gt;One of the draws of &lt;a href="http://sinatrarb.com/"&gt;Sinatra&lt;/a&gt;, and by extension, &lt;a href="https://kemalcr.com/"&gt;Kemal&lt;/a&gt; is the super simple syntax.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"/index"&lt;/span&gt;
  &lt;span class="s2"&gt;"INDEX"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of the downsides of Athena that I've heard is its verbosity.  To help with this, I created a similar &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Controller.html#get(path,*args,**named_args,&amp;amp;)-macro"&gt;macro DSL&lt;/a&gt; that simply abstracts the creation of a method and addition of the annotation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;
  &lt;span class="c1"&gt;# Super simple right?&lt;/span&gt;
  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"index"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="s2"&gt;"INDEX"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# It also works with arguments, and other annotations like ParamConverters/QueryParams&lt;/span&gt;
  &lt;span class="nd"&gt;@[ART::QueryParam("value3")]&lt;/span&gt;
  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"values/:value1/:value2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value2&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Float64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value3&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int8&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="s2"&gt;"Value1: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;value1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; - Value2: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;value2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; - Value3: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;value3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can still get pretty verbose if you have many path arguments, with a non &lt;code&gt;String&lt;/code&gt; return type, and some route constraints, but this and the three letter acronym aliases will help reduce the learning curve, and make life a little bit easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Access Raw &lt;code&gt;HTTP::Request&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Before there was not really a way to access the current request object outside of the &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/RequestStore.html"&gt;RequestStore&lt;/a&gt;, and related, not really an easy way to access the raw response body of said request.  In addition, the action argument's name for &lt;code&gt;POST&lt;/code&gt; requests had to be &lt;code&gt;body&lt;/code&gt;.  This was far from ideal and has been improved greatly in &lt;code&gt;0.8.0&lt;/code&gt;.  The raw &lt;code&gt;HTTP::Request&lt;/code&gt; and by extension, its body &lt;code&gt;IO&lt;/code&gt;, can now be accessed by simply typing an action argument to &lt;code&gt;HTTP::Request&lt;/code&gt;.  Athena will see that and provide the raw object to that action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[ART::Post(path: "/foo")]&lt;/span&gt;
&lt;span class="c1"&gt;# The name of the argument can be anything as long as its type is `HTTP::Request`.&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;post_body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;try&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gets_to_end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Future
&lt;/h2&gt;

&lt;p&gt;The overall roadmap for Athena is outlined in the &lt;a href="https://github.com/athena-framework/athena/issues/34"&gt;vision&lt;/a&gt; issue.  For the short term I will continue with fixing any issues, and improving the documentation as needed.  I will also continue working on reworking and moving the remaining components into the Github organization.&lt;/p&gt;

&lt;p&gt;The medium to long term includes the creation of additional components, such as resurrecting the &lt;code&gt;CLI&lt;/code&gt; component, as well as introducing a more structured framework to handle authentication and access control of a given route.&lt;/p&gt;

&lt;p&gt;As usual, any issues/comments/questions, feel free to drop a comment on this article, or come join me in the Athena &lt;a href="https://gitter.im/athena-frameworkcr/community"&gt;Gitter&lt;/a&gt; channel.&lt;/p&gt;

</description>
      <category>crystal</category>
      <category>framework</category>
      <category>symfony</category>
      <category>sinatra</category>
    </item>
    <item>
      <title>Object Validation with Assert</title>
      <dc:creator>Blacksmoke16</dc:creator>
      <pubDate>Thu, 29 Aug 2019 16:43:41 +0000</pubDate>
      <link>https://dev.to/blacksmoke16/object-validation-with-assert-25p6</link>
      <guid>https://dev.to/blacksmoke16/object-validation-with-assert-25p6</guid>
      <description>&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; Oct 21, 2019 Update examples for &lt;code&gt;Assert&lt;/code&gt; &lt;code&gt;v0.2.0&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Assert
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/blacksmoke16/assert"&gt;Assert&lt;/a&gt; is an annotation based object validation library heavily inspired by &lt;a href="https://symfony.com/doc/current/reference/constraints.html"&gt;Symfony Validation Constraint Annotations&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[Assert::NotBlank]&lt;/span&gt;
&lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;

&lt;span class="nd"&gt;@[Assert::GreaterThanOrEqual(value: 0)]&lt;/span&gt;
&lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Validations in Crystal, up until now, have mostly been shard specific.  One shard could have their own implementation for validating POST body data, while an ORM in the same application could be completely different.  &lt;code&gt;Assert&lt;/code&gt; was initially included in &lt;a href="https://github.com/blacksmoke16/CrSerializer"&gt;CrSerializer&lt;/a&gt; but has since gone through a rewrite and re-released as its own shard with the idea of brining some level of standardization that every shard could benefit from.  The main benefit of this would be the ability to use the same validations throughout your application.&lt;/p&gt;

&lt;p&gt;This article is intended to be an overview of features/example use cases.  Most of the example code comes from the &lt;a href="https://blacksmoke16.github.io/assert/Assert.html"&gt;API Docs&lt;/a&gt; which contains more detailed documentation on each feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Assert&lt;/code&gt; was created with flexibility and extensibility in mind, by default it includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multitude of built-in assertions:

&lt;ul&gt;
&lt;li&gt;Email&lt;/li&gt;
&lt;li&gt;Ip&lt;/li&gt;
&lt;li&gt;URL&lt;/li&gt;
&lt;li&gt;Choice&lt;/li&gt;
&lt;li&gt;Sub object/array of objects are all valid&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blacksmoke16.github.io/assert/Assert/Assertions.html"&gt;Etc.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Ability to create/use custom assertions

&lt;ul&gt;
&lt;li&gt;Use generics within custom assertions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Ability to run a subset of assertions on an object&lt;/li&gt;
&lt;li&gt;Control how and when the object is validated&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Usage
&lt;/h3&gt;

&lt;p&gt;In order to use &lt;code&gt;Assert&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add it to your &lt;code&gt;shard.yml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Require it - &lt;code&gt;require "assert"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Include it in your object - &lt;code&gt;include Assert&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An &lt;a href="https://blacksmoke16.github.io/assert/Assert.html"&gt;example usage&lt;/a&gt; can be seen in the API docs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Groups
&lt;/h3&gt;

&lt;p&gt;Each assertion can also be assigned to a group(s) in order to run subsets of assertions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Groups&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Assert&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@group_1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@group_2&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@default_group&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nd"&gt;@[Assert::EqualTo(value: 100, groups: ["group1"]&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
  &lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="n"&gt;group_1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;

  &lt;span class="nd"&gt;@[Assert::EqualTo(value: 200, groups: ["group2"]&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
  &lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="n"&gt;group_2&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;

  &lt;span class="nd"&gt;@[Assert::EqualTo(value: 300)]&lt;/span&gt;
  &lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="nf"&gt;ault_group&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;valid?&lt;/span&gt;                      &lt;span class="c1"&gt;# =&amp;gt; true&lt;/span&gt;
&lt;span class="no"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;valid?&lt;/span&gt;                      &lt;span class="c1"&gt;# =&amp;gt; false&lt;/span&gt;
&lt;span class="no"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;valid?&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;"group1"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;          &lt;span class="c1"&gt;# =&amp;gt; true&lt;/span&gt;
&lt;span class="no"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;valid?&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;         &lt;span class="c1"&gt;# =&amp;gt; true&lt;/span&gt;
&lt;span class="no"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;valid?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"group1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows you to reuse the same class/struct in various scenarios since you can control which assertions run.  For example, running a different set of assertions when a user registers for the first time, versus when they update some of their information.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Assertions &amp;amp; Generics
&lt;/h3&gt;

&lt;p&gt;If your application has some unique validation requirements that the included assertions do not cover; you can create custom assertions.&lt;/p&gt;

&lt;p&gt;A custom assertion is simply a class that inherits from &lt;code&gt;Assert::Assertions::Assertion&lt;/code&gt;, applies the &lt;code&gt;Assert::Assertions::Register&lt;/code&gt; annotation, and implements some methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[Assert::Assertions::Register(annotation: Assert::Exists)]&lt;/span&gt;
&lt;span class="c1"&gt;# A custom assertion that validates if a record exists with the given *id*.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# For example, an ORM model where `.exists?` checks if a record exists with the given PK.&lt;/span&gt;
&lt;span class="c1"&gt;# I.e. `SELECT exists(select 1 from "users" WHERE id = 123);`&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PropertyType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Assertions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Assertion&lt;/span&gt;
  &lt;span class="c1"&gt;# This is a helper macro to make defining the initialize method easier&lt;/span&gt;
  &lt;span class="n"&gt;initializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ss"&gt;actual: &lt;/span&gt;&lt;span class="no"&gt;PropertyType&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;# :inherit:&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;default_message_template&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
    &lt;span class="s2"&gt;"'%{actual}' is not a valid %{property_name}."&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# :inherit:&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;valid?&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Bool&lt;/span&gt;
    &lt;span class="no"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists?&lt;/span&gt; &lt;span class="vi"&gt;@actual&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we're defining a custom assertion called &lt;code&gt;Exists&lt;/code&gt; with the following methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;initializer - A helper macro that defines the initializer.

&lt;ul&gt;
&lt;li&gt;See the &lt;a href="https://blacksmoke16.github.io/assert/Assert/Assertions/Assertion.html"&gt;API Docs&lt;/a&gt; for more information on what the initializer is used for.&lt;/li&gt;
&lt;li&gt;See &lt;a href="https://blacksmoke16.github.io/assert/Assert/Assertions/Assertion.html#initializer(**ivars)-macro"&gt;Assertion.initializer&lt;/a&gt; for more information on the macro itself&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;default_message_template - The error message template to use if the assertion fails and no custom message was provided.

&lt;ul&gt;
&lt;li&gt;Instance variables on the assertion can be used within the template by surrounding the ivar's name in double curly braces.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;valid? - Implements the logic that determines if the property is valid or not.

&lt;ul&gt;
&lt;li&gt;The implementation could be whatever you wish as long as it returns &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also need to apply the &lt;code&gt;Assert::Assertions::Register&lt;/code&gt; annotation, which is used to define the name of the annotation that should be applied to properties.  In this case &lt;code&gt;Assert::Exists&lt;/code&gt;.  Now this assertion is ready to be used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;SomeORM&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Model&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Assert&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nd"&gt;@[Assert::Exists(User)]&lt;/span&gt;
  &lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="n"&gt;author_id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the assertion would run a SQL query to determine if a &lt;code&gt;User&lt;/code&gt; with an &lt;em&gt;id&lt;/em&gt; of 17 exists. &lt;/p&gt;

&lt;p&gt;A Dependency Injection shard, such as &lt;a href="https://github.com/Blacksmoke16/athena"&gt;Athena's DI Module&lt;/a&gt; could also be used to inject the current user/request, or some ACL service into the assertion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Use Cases
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Assert&lt;/code&gt; is not useful unless there are objects that need validating.  For some projects/applications it might not be necessary.  However web frameworks and ORMs could easily benefit, as validations are an important part of both.&lt;/p&gt;

&lt;h3&gt;
  
  
  Web Framework
&lt;/h3&gt;

&lt;p&gt;Web applications have the most apparent need for validations.  This could either be validating the POST body in an API, or validating user input from a form submission.  However, &lt;code&gt;Assert&lt;/code&gt; is not for client side validation.  It would not, for example, prevent a user from entering a string in a numeric form field.  It &lt;em&gt;would&lt;/em&gt; be caught when that form was submitted, and the server runs the validations.&lt;/p&gt;

&lt;p&gt;Lets go over an example of how &lt;code&gt;Assert&lt;/code&gt; could be used within an API using Kemal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"kemal"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"assert"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"json"&lt;/span&gt;

&lt;span class="c1"&gt;# user.cr&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Serializable&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Assert&lt;/span&gt;

  &lt;span class="c1"&gt;# Asserts that their age is &amp;gt;= 0 AND not nil&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::NotNil]&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::GreaterThanOrEqual(value: 0)]&lt;/span&gt;
  &lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;

  &lt;span class="c1"&gt;# Assert their name is not blank&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::NotBlank]&lt;/span&gt;
  &lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;

  &lt;span class="c1"&gt;# Assert their email is not blank AND is a valid format&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::Email(message: "'%{actual}' is not a proper email")]&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::NotBlank]&lt;/span&gt;
  &lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;

  &lt;span class="c1"&gt;# Assert their password is between 7 and 25 characters&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::Size(Range(Int32, Int32), range: 7..25)]&lt;/span&gt;
  &lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;

  &lt;span class="c1"&gt;# Have the object be validated after the it is deserialized&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;after_initialize&lt;/span&gt;
    &lt;span class="n"&gt;validate!&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# users_controller.cr&lt;/span&gt;
&lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="s2"&gt;"/users"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;content_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"application/json"&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_json&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;not_nil!&lt;/span&gt;
  &lt;span class="c1"&gt;# Do stuff with a valid user&lt;/span&gt;

  &lt;span class="c1"&gt;# Return the user as JSON in the response&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ValidationError&lt;/span&gt;
  &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;
  &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Kemal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup makes the validations on &lt;code&gt;User&lt;/code&gt; run after the object has been deserialized from the JSON POST body.  If the object is not valid &lt;code&gt;#validate!&lt;/code&gt; will raise a &lt;code&gt;ValidationError&lt;/code&gt; exception. We are catching that exception and returning the JSON error message back to the user.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Assert&lt;/code&gt; also defines &lt;code&gt;#valid?&lt;/code&gt; and &lt;code&gt;#validate&lt;/code&gt; methods which return a &lt;code&gt;Bool&lt;/code&gt; if it the object is valid, or an array of failed assertions respectively.  The former being most useful if you just want to know if an object is valid, with the latter being most useful if you wanted to do something with the assertion data, such as formatting an error message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  http://localhost:3000/users &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "name": "Jim",
    "age": -1,
    "email": "foobar",
    "password": "monkey123"
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Would return the following error&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Validation tests failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"errors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"'age' should be greater than or equal to '0'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"'foobar' is not a proper email"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JSON error response format can be changed by overriding the &lt;a href="https://blacksmoke16.github.io/assert/Assert/Exceptions/ValidationError.html#to_json(builder:JSON::Builder):Nil-instance-method"&gt;#to_json(builder : JSON::Builder)&lt;/a&gt; method within the exception class; if you wanted to group the errors by the property name for example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ValidationError&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;field&lt;/span&gt; &lt;span class="s2"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;
      &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;field&lt;/span&gt; &lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@message&lt;/span&gt;
      &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;field&lt;/span&gt; &lt;span class="s2"&gt;"errors"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
          &lt;span class="vi"&gt;@failed_assertions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;property_name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;field&lt;/span&gt; &lt;span class="n"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Would output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Validation tests failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"errors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"'age' should be greater than or equal to '0'"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"'foobar' is not a proper email"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This, of course, is just a demonstration.  A framework could easily integrate &lt;code&gt;Assert&lt;/code&gt; to make the process more streamlined.  Such as how its used within &lt;a href="https://github.com/blacksmoke16/athena"&gt;Athena&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[Athena::Routing::Post(path: "users")]&lt;/span&gt;
&lt;span class="nd"&gt;@[Athena::Routing::ParamConverter(param: "body", type: User, converter: Athena::Routing::Converters::RequestBody)]&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;
  &lt;span class="n"&gt;body&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ParamConverter&lt;/code&gt; handles converting the request body into a &lt;code&gt;User&lt;/code&gt; object, any validations on &lt;code&gt;User&lt;/code&gt; will also be executed after it has been deserialized.  The JSON error will be returned automatically if it is not valid.  The controller action will also never execute if the object is not valid.&lt;/p&gt;

&lt;p&gt;While using &lt;code&gt;Assert&lt;/code&gt; may make your objects larger, it removes the need for validation within the controller code as you know can be assured that invalid objects will not make it far.  It also allows you to share the same validation logic from other parts of your application.  Such as an ORM.&lt;/p&gt;

&lt;h3&gt;
  
  
  ORM Models
&lt;/h3&gt;

&lt;p&gt;ORMs models inherently require validation to make sure what is being persisted to the database is correct.  Continuing along with our &lt;code&gt;Kemal&lt;/code&gt; example, we could easily convert our &lt;code&gt;User&lt;/code&gt; object into a &lt;code&gt;Granite&lt;/code&gt; model.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; This is just an example and does not include setting up the ORM&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"granite"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"assert"&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Granite&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Assert&lt;/span&gt;

  &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt;

  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="ss"&gt;primary: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# Asserts that their age is &amp;gt;= 0 AND not nil&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::NotNil]&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::GreaterThanOrEqual(value: 0)]&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;

  &lt;span class="c1"&gt;# Assert their name is not blank&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::NotBlank]&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;

  &lt;span class="c1"&gt;# Assert their email is not blank AND is a valid format&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::Email(message: "'%{actual}' is not a proper email")]&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::NotBlank]&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;

  &lt;span class="c1"&gt;# Assert their password is between 7 and 25 characters&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::Size(Range(Int32, Int32), range: 7..25)]&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;

  &lt;span class="c1"&gt;# Granite includes `JSON::Serializble` by default&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;after_initialize&lt;/span&gt;
    &lt;span class="n"&gt;validate!&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our controller action would also slightly change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="s2"&gt;"/users"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;content_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"application/json"&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_json&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;not_nil!&lt;/span&gt;
  &lt;span class="c1"&gt;# We can now just save the user since we know its valid&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;

  &lt;span class="c1"&gt;# Return the user as JSON in the response&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ValidationError&lt;/span&gt;
  &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;
  &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;POSTing a valid user would return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"monkey123"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Assert&lt;/code&gt;'s benefits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Having a framework agnostic validation library that can be used across shards.

&lt;ul&gt;
&lt;li&gt;Sharing validation logic between your API controllers and ORM models for example.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Easily extensible by the user.

&lt;ul&gt;
&lt;li&gt;Custom assertions can be easily defined without needing to edit any shard's source code.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Easy to integrate into existing frameworks/shards.

&lt;ul&gt;
&lt;li&gt;Current projects/frameworks could easily add &lt;code&gt;Assert&lt;/code&gt; to their objects and control when/how they get validated.

&lt;ul&gt;
&lt;li&gt;Such as the &lt;code&gt;Kemal&lt;/code&gt; example.  Moving validations into the objects and out of the controller actions&lt;/li&gt;
&lt;li&gt;Or an ORM that would make sure the model is valid before saving&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As usual, if you have any questions, feedback, or ideas for new assertions; feel free to message me on the &lt;a href="https://gitter.im/crystal-lang/crystal"&gt;Crystal Gitter&lt;/a&gt;, or create an issue on the &lt;a href="https://github.com/blacksmoke16/assert"&gt;Github Repo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>crystal</category>
      <category>validation</category>
    </item>
    <item>
      <title>Dependency Injection in Crystal</title>
      <dc:creator>Blacksmoke16</dc:creator>
      <pubDate>Sun, 28 Jul 2019 14:17:53 +0000</pubDate>
      <link>https://dev.to/blacksmoke16/dependency-injection-in-crystal-2d66</link>
      <guid>https://dev.to/blacksmoke16/dependency-injection-in-crystal-2d66</guid>
      <description>&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; January 12, 2020 for Athena version &lt;code&gt;0.8.0&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;UPDATE:&lt;/strong&gt; June 16, 2020 for Athena version &lt;code&gt;0.9.0&lt;/code&gt; &lt;/p&gt;
&lt;h1&gt;
  
  
  Dependency Injection (DI)
&lt;/h1&gt;

&lt;p&gt;Dependency Injection can be a powerful tool in making an application easier to test, more expandable, and increase the flexibility of the system's components. &lt;/p&gt;

&lt;p&gt;This article assumes you are somewhat familiar with the concepts of DI.  If not, check out some of these other articles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.freecodecamp.org/news/a-quick-intro-to-dependency-injection-what-it-is-and-when-to-use-it-7578c84fa88f/"&gt;A quick intro to Dependency Injection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackify.com/dependency-inversion-principle/"&gt;Dependency Inversion Principle with Code Examples&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DI is usable in every language, although it is more common in some than others.  Crystal, and Ruby by extension, seem to not utilize DI as much as other languages.  While its out of scope of this article to figure out &lt;em&gt;why&lt;/em&gt; that is, lets go through some useful patterns and examples of &lt;em&gt;how&lt;/em&gt; DI can be useful in your application.&lt;/p&gt;

&lt;p&gt;We'll be using Crystal as our language of choice, along with &lt;a href="https://github.com/athena-framework/athena"&gt;Athena&lt;/a&gt; as our framework.  Athena's DI component is also available as a &lt;a href="https://github.com/athena-framework/dependency-injection"&gt;standalone shard&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Manager
&lt;/h2&gt;

&lt;p&gt;The first example is what I call the "manager" pattern.  This pattern is most useful when dealing with multiple object instances based on a singular class/interface.  I.e. multiple objects instantiated from the same class.&lt;/p&gt;
&lt;h3&gt;
  
  
  Problem Statement
&lt;/h3&gt;

&lt;p&gt;Say you are going to partner with &lt;em&gt;n&lt;/em&gt; other companies.  You want to setup an API endpoint that would expose partner specific data within your system for them to consume.  You also want it to be easy to add partners; requiring minimal to no code changes to do so.&lt;/p&gt;
&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Define a &lt;code&gt;Partner&lt;/code&gt; class that implements common methods/properties that each partner would have in common.&lt;/li&gt;
&lt;li&gt;Define a &lt;code&gt;PartnerManager&lt;/code&gt; class that would be responsible for "managing" the &lt;code&gt;Partner&lt;/code&gt; instances.&lt;/li&gt;
&lt;li&gt;Define a &lt;code&gt;PartnerParamConverter&lt;/code&gt; used to convert a partner's id (obtained from the API route) into a &lt;code&gt;Partner&lt;/code&gt; instance.&lt;/li&gt;
&lt;li&gt;Define a &lt;code&gt;PartnerController&lt;/code&gt; to group of the logic of partner related API endpoints.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"athena"&lt;/span&gt;

&lt;span class="kp"&gt;private&lt;/span&gt; &lt;span class="no"&gt;PARTNER_TAG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"partner"&lt;/span&gt;

&lt;span class="c1"&gt;# Define a type that all partners wil be based off of, then register some partners.&lt;/span&gt;
&lt;span class="nd"&gt;@[ADI::Register(_id: "GOOGLE", name: "google")]&lt;/span&gt;
&lt;span class="nd"&gt;@[ADI::Register(_id: "FACEBOOK", name: "facebook")]&lt;/span&gt;
&lt;span class="kp"&gt;record&lt;/span&gt; &lt;span class="no"&gt;Partner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;

&lt;span class="c1"&gt;# We can also use `ADI.auto_configure` to handle applying the&lt;/span&gt;
&lt;span class="c1"&gt;# correct tag to each `Partner` instance.&lt;/span&gt;
&lt;span class="no"&gt;ADI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;auto_configure&lt;/span&gt; &lt;span class="no"&gt;Partner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;tags: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;PARTNER_TAG&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;

&lt;span class="c1"&gt;# Define another service that will have all partners injected into it.&lt;/span&gt;
&lt;span class="c1"&gt;# This manager will be injected into our param converter to handle&lt;/span&gt;
&lt;span class="c1"&gt;# resolving a `Partner` from their id.&lt;/span&gt;
&lt;span class="nd"&gt;@[ADI::Register(_partners: "!partner")]&lt;/span&gt;
&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;PartnerManager&lt;/span&gt;
  &lt;span class="vi"&gt;@partners&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Partner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Partner&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;partners&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Partner&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;# Create a mapping of partner ID to the partner instance.&lt;/span&gt;
    &lt;span class="n"&gt;partners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;partner&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="vi"&gt;@partners&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;partner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;partner&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Returns a `Partner` with the provided *partner_id* or raises an&lt;/span&gt;
  &lt;span class="c1"&gt;# `ART::Exceptions::NotFound` exception if one could not be found.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;partner_id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Partner&lt;/span&gt;
    &lt;span class="vi"&gt;@partners&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;partner_id&lt;/span&gt;&lt;span class="p"&gt;]?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s2"&gt;"No partner with an ID '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;partner_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' has been registered."&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nd"&gt;@[ADI::Register]&lt;/span&gt;
&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;PartnerParamConverter&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ParamConverterInterface&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@partner_manager&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;PartnerManager&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# :inherit:&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
    &lt;span class="c1"&gt;# Grab the partner's ID from the request's attributes, then resolve it into a Partner.&lt;/span&gt;
    &lt;span class="c1"&gt;# Path/query params are automatically populated into the attributes.&lt;/span&gt;
    &lt;span class="n"&gt;partner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@partner_manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;

    &lt;span class="c1"&gt;# Add the resolved partner object to the request's attributes&lt;/span&gt;
    &lt;span class="c1"&gt;# for it to later be resolved for the controller action argument.&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;partner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Partner&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# The controller that would house all partner related endpoints.&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PartnerController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;
  &lt;span class="nd"&gt;@[ART::ParamConverter("partner", converter: PartnerParamConverter)]&lt;/span&gt;
  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"partner/:id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;partner&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Partner&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Notice we have access to the actual `Partner` object.&lt;/span&gt;
    &lt;span class="c1"&gt;# From here you can do whatever you need with the object.&lt;/span&gt;
    &lt;span class="s2"&gt;"Resolved &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;partner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;

&lt;span class="c1"&gt;# GET /partner/FOO    # =&amp;gt; {"code":404,"message":"No partner with an ID 'FOO' has been registered."}&lt;/span&gt;
&lt;span class="c1"&gt;# GET /partner/GOOGLE # =&amp;gt; "Resolved Google!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;From here, if you wanted to add another partner you would simply register another partner service and everything would just work.  E.x. &lt;code&gt;@[ADI::Register(_id: "YAHOO", name: "yahoo")]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This also makes the code generic.  Nothing specific to any particular partner is defined anywhere other than their instance of the &lt;code&gt;Partner&lt;/code&gt; class.&lt;/p&gt;
&lt;h3&gt;
  
  
  Alternate solutions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Hard code an array of &lt;code&gt;Partner&lt;/code&gt; objects within the controller.

&lt;ul&gt;
&lt;li&gt;This wouldn't be an ideal solution since it would tightly couple the &lt;code&gt;Partner&lt;/code&gt; struct and the &lt;code&gt;PartnerController&lt;/code&gt;.  It shouldn't be the responsibility of the &lt;code&gt;PartnerController&lt;/code&gt; to know all possible partners.  It also would make updating/maintaining/testing of the controller harder due to that extra dependency.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Store the partner data in a database.

&lt;ul&gt;
&lt;li&gt;Since our system isn't actually doing anything in the partner's system, this would just be mostly dead data.  It would only exist for the sole purpose of resolving the ID.  Having unnecessary data is just wasteful.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Plug and Play
&lt;/h2&gt;

&lt;p&gt;The next example isn't so much a pattern, but a core feature of DI.  The ability to change the functionality of a class by just changing what service gets injected into it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Problem Statement
&lt;/h3&gt;

&lt;p&gt;You have a Worker class that will do some work, then write the output to &lt;code&gt;x&lt;/code&gt;.  &lt;code&gt;x&lt;/code&gt; is an external service like Amazon &lt;code&gt;S3&lt;/code&gt;, the local file system, &lt;code&gt;Redis&lt;/code&gt;, etc.  To start this worker will only support one of them, say &lt;code&gt;S3&lt;/code&gt;.  However, we want to be able to design the class in such a way where the implementation is generic and could work with any other service in the future if so desired.&lt;/p&gt;
&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Define an "interface", in Crystal's case we'll use a module, to define the public API of our writers&lt;/li&gt;
&lt;li&gt;Register an initial implementation of the interface for &lt;code&gt;S3&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Register another implementation for &lt;code&gt;Redis&lt;/code&gt;, but default to &lt;code&gt;S3&lt;/code&gt; as the default implementation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Handle injecting the proper writer into our &lt;code&gt;Worker&lt;/code&gt; class&lt;/li&gt;
&lt;li&gt;Create a test for our class.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Initial Interface Implementation
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"athena"&lt;/span&gt;

&lt;span class="c1"&gt;# Define an abstract class that will act as our base interface.&lt;/span&gt;
&lt;span class="c1"&gt;# It also will ensure our subclasses implement the correct method.&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;WriterInterface&lt;/span&gt;
  &lt;span class="n"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Create an implementation of `WriterInterface` for S3 and register it.&lt;/span&gt;
&lt;span class="nd"&gt;@[ADI::Register]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;S3Writer&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;WriterInterface&lt;/span&gt;

  &lt;span class="c1"&gt;# :inherit:&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
    &lt;span class="c1"&gt;# Write the content&lt;/span&gt;
    &lt;span class="s2"&gt;"Wrote data to S3"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Register our worker also as a service, and set it as public.&lt;/span&gt;
&lt;span class="c1"&gt;# Ideally everything would be a service, however in most cases&lt;/span&gt;
&lt;span class="c1"&gt;# at least one service will need to be public in order to be&lt;/span&gt;
&lt;span class="c1"&gt;# the "entrypoint" into the application.&lt;/span&gt;
&lt;span class="nd"&gt;@[ADI::Register(public: true)]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;
  &lt;span class="vi"&gt;@writer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;WriterInterface&lt;/span&gt;

  &lt;span class="c1"&gt;# DI will automatically resolve the correct `WriterInterface` based on the&lt;/span&gt;
  &lt;span class="c1"&gt;# type restriction of the argument, and optionally the argument's name (more on that later).&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;WriterInterface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Manually assign the ivar to make changing the writer instance easier;&lt;/span&gt;
    &lt;span class="c1"&gt;# as we would only have to update these two variables within initialize versus&lt;/span&gt;
    &lt;span class="c1"&gt;# throughout the class.&lt;/span&gt;
    &lt;span class="vi"&gt;@writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;writer&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_work&lt;/span&gt;
    &lt;span class="c1"&gt;# Do some work&lt;/span&gt;
    &lt;span class="vi"&gt;@writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt; &lt;span class="s2"&gt;"did work"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Grab out worker instance from the container and see what work it does.&lt;/span&gt;
&lt;span class="no"&gt;ADI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;do_work&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; Wrote data to S3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;At the moment, since we only have one implementation of the &lt;code&gt;WriterInterface&lt;/code&gt;, this example isn't that much more beneficial than just instantiating the &lt;code&gt;S3Writer&lt;/code&gt; within the &lt;code&gt;Worker&lt;/code&gt;.  However, this changes when we introduce more than one implementation.&lt;/p&gt;
&lt;h4&gt;
  
  
  Multiple Interface Implementations
&lt;/h4&gt;

&lt;p&gt;Based off the previous example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Add another implementation for `Redis`&lt;/span&gt;
&lt;span class="nd"&gt;@[ADI::Register]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RedisWriter&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;WriterInterface&lt;/span&gt;

  &lt;span class="c1"&gt;# :inherit:&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
    &lt;span class="c1"&gt;# Write the content&lt;/span&gt;
    &lt;span class="s2"&gt;"Wrote content to Redis"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, we now have a little problem.  Since &lt;code&gt;ADI&lt;/code&gt; resolves services based on argument type restrictions, and we now have multiple implementations of &lt;code&gt;WriterInterface&lt;/code&gt;, how does it know which one to inject?  We have two options:&lt;/p&gt;

&lt;h5&gt;
  
  
  Aliases
&lt;/h5&gt;

&lt;p&gt;&lt;code&gt;ADI&lt;/code&gt; has the concept of &lt;a href="https://athena-framework.github.io/dependency-injection/Athena/DependencyInjection/Register.html#aliasing-services"&gt;Service Aliases&lt;/a&gt; that allow defining a "default" service to use when the &lt;code&gt;WriterInterface&lt;/code&gt; type restriction is encountered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# The `S3Writer` will now be injected by default&lt;/span&gt;
&lt;span class="c1"&gt;# when the `WriterInterface` type restriction is encountered.&lt;/span&gt;
&lt;span class="nd"&gt;@[ADI::Register(alias: WriterInterface)]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;S3Writer&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Argument Name
&lt;/h5&gt;

&lt;p&gt;While having a default implementation is good the majority of the time, what if we want the &lt;em&gt;other&lt;/em&gt; implementation?  This case can be handled by updating the name of the argument so that the resolution logic would now be based on both the type restriction &lt;em&gt;AND&lt;/em&gt; the name of the argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[ADI::Register(public: true)]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;
  &lt;span class="vi"&gt;@writer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;WriterInterface&lt;/span&gt;

  &lt;span class="c1"&gt;# DI would now automatically inject `RedisWriter` since its a `WriterInterface` instance&lt;/span&gt;
  &lt;span class="c1"&gt;# AND it's service name is `redis_writer`.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redis_writer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;WriterInterface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis_writer&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Testing
&lt;/h4&gt;

&lt;p&gt;One of the major benefits DI allows for is easier testing since nothing is too tightly coupled with anything else, i.e. everything is built upon abstractions aka interfaces.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"spec"&lt;/span&gt;

&lt;span class="c1"&gt;# Create a mock writer.&lt;/span&gt;
&lt;span class="c1"&gt;# This allows mocking the response from the external service as we shouldn't be worried about&lt;/span&gt;
&lt;span class="c1"&gt;# how the other service works since we should only be testing our worker, not its dependencies.&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MockWriter&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;WriterInterface&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
    &lt;span class="s2"&gt;"WROTE_MOCK_DATA"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# We can now test the functionality of our `Writer` type in isolation.&lt;/span&gt;
&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Worker&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"#do_work"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"should do work"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="no"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;MockWriter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;do_work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="s2"&gt;"WROTE_MOCK_DATA"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;Since we defined the type restriction as our &lt;code&gt;WriterInterface&lt;/code&gt; module, our &lt;code&gt;Writer&lt;/code&gt; class is not tightly coupled with any one specific implementation of it.  We are able to easily change which implementation gets injected by either updating our &lt;code&gt;alias&lt;/code&gt;, or simply changing the name of the initializer argument.&lt;/p&gt;

&lt;p&gt;As mentioned previously, one of the main benefits of this is to make the &lt;code&gt;Worker&lt;/code&gt; class depend on upon abstractions as opposed to concrete classes.  Or in other words, prevent a singular implementation from being tightly coupled with the &lt;code&gt;Worker&lt;/code&gt; class. As long as each implementation correctly implements &lt;code&gt;WriterInterface&lt;/code&gt;, the &lt;code&gt;Worker&lt;/code&gt; class shouldn't care about which implementation it's using, just that calling &lt;code&gt;.write&lt;/code&gt; on it, writes the content correctly.&lt;/p&gt;

&lt;p&gt;Each &lt;code&gt;WriterInterface&lt;/code&gt; implementation could also easily inject their own dependencies, such as credentials, API clients etc.&lt;/p&gt;

&lt;p&gt;Another benefit of this pattern is if you have a service that has a singular purpose such as sending an email; you could easily reuse the service.  For example, simply inject the &lt;code&gt;EmailProvider&lt;/code&gt; service, then use it to send emails.  &lt;/p&gt;

&lt;p&gt;DI removes the need to worry about &lt;em&gt;how&lt;/em&gt; and &lt;em&gt;where&lt;/em&gt; objects are getting instantiated.  If the &lt;code&gt;EmailProvider&lt;/code&gt; had dependencies of its own and you were doing &lt;code&gt;sender = EmailProvider.new xxx&lt;/code&gt; each time an email needed sent; that is less than ideal.  DI allows the class to be instantiated once with the given arguments; then it can simply be injected where it's needed.  E.x. &lt;code&gt;def initialize(@sender : EmailProvider); end&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;EmailProvider&lt;/code&gt; service would ideally be tested so you can have confidence that anywhere you inject it, the emails will be sent properly without having to test the same logic multiple times.  Speaking of tests, DI allows us to define a test implementation of &lt;code&gt;WriterInterface&lt;/code&gt; .  When testing a class with dependencies on other services, the dependencies should always be mocked.  This gives you control over &lt;em&gt;how&lt;/em&gt; those services act.  If you didn't mock them out and used the actual implementations your tests would be testing much more than they should.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sharing Data
&lt;/h2&gt;

&lt;p&gt;The last example is going to show how DI can be used to share data between disparate types.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem Statement
&lt;/h3&gt;

&lt;p&gt;You are creating a JSON API.  You recently got to the point of where you need to handle user authentication.  You want to design things in such a way that allows you to access the current user outside of the request context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Define a service that will store current user object&lt;/li&gt;
&lt;li&gt;Set the user on that service within your &lt;code&gt;SecurityListener&lt;/code&gt; handles authorization.

&lt;ul&gt;
&lt;li&gt;Also add some &lt;code&gt;Log&lt;/code&gt; context to include this user's information &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Use this service to expose user information via an endpoint&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"athena"&lt;/span&gt;

&lt;span class="c1"&gt;# Our user object, this would most likely be an ORM model.&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Serializable&lt;/span&gt;

  &lt;span class="kp"&gt;getter&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@customer_id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Simulate a ORM query method.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
    &lt;span class="kp"&gt;new&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Fred"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nd"&gt;@[ADI::Register]&lt;/span&gt;
&lt;span class="c1"&gt;# Our service that will store user the currently logged in user.&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserStorage&lt;/span&gt;
  &lt;span class="kp"&gt;property&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nd"&gt;@[ADI::Register]&lt;/span&gt;
&lt;span class="c1"&gt;# Our custom listener that listens on the Request event.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# It'll handle making sure the user's token is valid, and setting the current user.&lt;/span&gt;
&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;SecurityListener&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;AED&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EventListenerInterface&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribed_events&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;AED&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SubscribedEvents&lt;/span&gt;
    &lt;span class="no"&gt;AED&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SubscribedEvents&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Request&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Set the priority higher so it runs before routing&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@user_storage&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;UserStorage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dispatcher&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;AED&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EventDispatcherInterface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
    &lt;span class="c1"&gt;# Logic to make sure a token is provided.&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"PARSED_TOKEN"&lt;/span&gt;

    &lt;span class="c1"&gt;# Logic to validate the token and get the stored user_id from it.&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="c1"&gt;# Fetch the user from the database&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;

    &lt;span class="c1"&gt;# Add some logging context for future logs&lt;/span&gt;
    &lt;span class="no"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;user_id: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;customer_id: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;customer_id&lt;/span&gt;

    &lt;span class="c1"&gt;# Set the user in user storage.&lt;/span&gt;
    &lt;span class="vi"&gt;@user_storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Register the controller itself as a service,&lt;/span&gt;
&lt;span class="c1"&gt;# NOTE: Controller services must be declared as public.&lt;/span&gt;
&lt;span class="nd"&gt;@[ADI::Register(public: true)]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;
  &lt;span class="c1"&gt;# Inject our `UserStorage` object&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@user_storage&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;UserStorage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# The user storage service could also be injected anywhere else you need the current user.&lt;/span&gt;
  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"user/me"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;return_type: &lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Returning data for &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@user_storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="vi"&gt;@user_storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Start the server&lt;/span&gt;
&lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;

&lt;span class="c1"&gt;# GET /user/me # =&amp;gt; {"id":1,"customer_id":12,"name":"Fred"}&lt;/span&gt;
&lt;span class="c1"&gt;# 2020-06-16T02:25:51.593601Z   INFO - athena.routing: Matched route /user/me -- uri: "/user/me", method: "GET", path_params: {}, query_params: {} -- user_id: 1, customer_id: 12&lt;/span&gt;
&lt;span class="c1"&gt;# 2020-06-16T02:25:51.593677Z   INFO - Returning data for Fred -- user_id: 1, customer_id: 12&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;Notice that since we set some &lt;code&gt;Log&lt;/code&gt; context with our &lt;code&gt;SecurityListener&lt;/code&gt;, all logs after that point, within the same fiber, will include that data.  Also, since our &lt;code&gt;SecurityListener&lt;/code&gt; has a priority of &lt;code&gt;30&lt;/code&gt;, it runs &lt;em&gt;before&lt;/em&gt; Athena's routing logic which has a priority of &lt;code&gt;25&lt;/code&gt;.  This is beneficial since it means we don't even have to invoke the router if the request is unauthorized.&lt;/p&gt;

&lt;p&gt;Similarly to how &lt;code&gt;Log::Context&lt;/code&gt; is fiber specific, the DI container is also fiber specific, or in other words unique per request.  The benefit of this is it allows sharing arbitrary data between your services, without having to worry about resetting when the request is finished.  Notice that the &lt;code&gt;User&lt;/code&gt; object set on &lt;code&gt;UserStorage&lt;/code&gt; within &lt;code&gt;SecurityListener&lt;/code&gt;, is the same one provided to our &lt;code&gt;UserController&lt;/code&gt;.  Since the DI container handles instantiating and providing objects to our services, we can easily provide references to the same object instance in multiple services without needing to manually pass it through as part of the public API.&lt;/p&gt;

&lt;p&gt;The most common way frameworks handle the "current_user" is by reopening &lt;code&gt;HTTP::Server::Context&lt;/code&gt; and adding it there, which works fine most of the time.  But what happens when you want to access the current user somewhere that isn't a controller action or an &lt;code&gt;HTTP::Handler&lt;/code&gt;?  Having something that is able to be easily accessed anywhere it is needed is much more flexible.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;UserStorage&lt;/code&gt; class could also be expanded into say, &lt;code&gt;TokenStorage&lt;/code&gt; which would store the token, which would have a &lt;code&gt;user&lt;/code&gt; method; to handle the logic of resolving a &lt;code&gt;User&lt;/code&gt; object from the token/data in the token.  There could also be different implementations of the &lt;code&gt;Token&lt;/code&gt; class, such as &lt;code&gt;AnonymousToken&lt;/code&gt; if there is not currently a logged in user.  Overall, this approach is just much more flexible than being tied to the request context to access common data, like the current user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Alternate Solutions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Define the &lt;code&gt;current_user&lt;/code&gt; as a class variable.

&lt;ul&gt;
&lt;li&gt;This effectively makes the system not fiber safe and unable to handle more than 1 request at a time.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Store a reference within the database.

&lt;ul&gt;
&lt;li&gt;This would solve the problem of being able to access the user anywhere, but is less than ideal.  If a system is handling many requests, that would equate to a lot of extra unnecessary DB queries.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Store the user within the &lt;code&gt;HTTP::Request&lt;/code&gt; object

&lt;ul&gt;
&lt;li&gt;This is a viable solution assuming you don't need to access said user anywhere outside of the request context.  If for example you wanted to include some user data within an asynchronous message context; how would you provide the user object?  In sort there wouldn't be a clean way to do it without making it part of the public enqueuing API.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;As shown, DI can make your Crystal application less dependent on concrete classes and more dependent on abstractions.  This prevents tight coupling, makes testing easier, and encourages code reuse.  However, this does not mean DI is the best pattern to use 100% of the time.  Being aware of its capabilities and being able to apply the pattern to a well suited problem is the key.&lt;/p&gt;

&lt;p&gt;If anyone has any other alternate solutions (both good and bad), feel free to share them in the comments.  I would be quite interested in seeing how people, those mainly coming from a Ruby background, would solve some of these scenarios.&lt;/p&gt;

&lt;p&gt;As usual feel free to join me in the Athena &lt;a href="https://gitter.im/athena-frameworkcr/community"&gt;Gitter&lt;/a&gt; channel if you have any suggestions, questions, or ideas. I'm also available on Discord (&lt;code&gt;Blacksmoke16#0016&lt;/code&gt;) or via &lt;a href="//mailto:george@dietrich.app"&gt;Email&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>crystal</category>
      <category>di</category>
    </item>
    <item>
      <title>oq - A portable/performant jq wrapper</title>
      <dc:creator>Blacksmoke16</dc:creator>
      <pubDate>Sat, 13 Jul 2019 17:48:58 +0000</pubDate>
      <link>https://dev.to/blacksmoke16/oq-a-portable-performant-jq-wrapper-18ka</link>
      <guid>https://dev.to/blacksmoke16/oq-a-portable-performant-jq-wrapper-18ka</guid>
      <description>&lt;h1&gt;
  
  
  oq
&lt;/h1&gt;

&lt;p&gt;A performant, and portable jq wrapper thats facilitates the consumption and output of formats other than JSON; using jq filters to transform the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I've been using &lt;a href="https://github.com/stedolan/jq"&gt;jq&lt;/a&gt; for a while for transforming a master JSON document into partner dependent structures for their consumption.  However, up until recently all of the partner structures have also been in JSON.  Since &lt;code&gt;jq&lt;/code&gt; does not support outputting XML on its own, I began to look around to see if there were any libraries that would allow using &lt;code&gt;jq&lt;/code&gt; filters to transform the data, but output XML in addition to JSON.  I ended up finding a Python library called &lt;a href="https://github.com/kislyuk/yq"&gt;yq&lt;/a&gt; that seemed to be perfect.&lt;/p&gt;

&lt;p&gt;It supports outputting to XML and JSON while being able to use the same &lt;code&gt;jq&lt;/code&gt; filter for both.  After playing around with it for a while it became clear that, while quite speedy for smaller files, it really struggled with some of the larger documents I needed to process.  The fact that it's Python also complicated things as Python needs to be installed to use it, without going through some extra process to make it a singular binary.  Thus, the idea for a more performant and portable option began to take shape.&lt;/p&gt;

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

&lt;p&gt;Using the relatively new &lt;a href="https://crystal-lang.org/"&gt;Crystal&lt;/a&gt; language; I created &lt;a href="https://github.com/blacksmoke16/oq"&gt;oq&lt;/a&gt; with the primary goals being portability, performance, and to extend the formats that &lt;code&gt;jq&lt;/code&gt; supports.&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;oq&lt;/code&gt; has three additional arguments that sets the input/output formats to use, in additional to the name of the root element if serializing to XML.  All other arguments are passed on to &lt;code&gt;jq&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Consuming JSON and output XML
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{"name": "Jim"}'&lt;/span&gt; | oq &lt;span class="nt"&gt;-o&lt;/span&gt; xml &lt;span class="nb"&gt;.&lt;/span&gt;
&amp;lt;?xml &lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt; &lt;span class="nv"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"UTF-8"&lt;/span&gt;?&amp;gt;
&amp;lt;root&amp;gt;
  &amp;lt;name&amp;gt;Jim&amp;lt;/name&amp;gt;
&amp;lt;/root&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Consuming JSON and output YAML
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{"name": "Jim"}'&lt;/span&gt; | oq &lt;span class="nt"&gt;-o&lt;/span&gt; yaml &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nt"&gt;---&lt;/span&gt;
name: Jim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Consume YAML from a file and output XML
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;data.yaml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Jim&lt;/span&gt;
&lt;span class="na"&gt;numbers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;oq -i yaml -o xml . data.yaml 
&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;root&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Jim&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;numbers&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/numbers&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;numbers&amp;gt;&lt;/span&gt;2&lt;span class="nt"&gt;&amp;lt;/numbers&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;numbers&amp;gt;&lt;/span&gt;3&lt;span class="nt"&gt;&amp;lt;/numbers&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/root&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Consume JSON, transform it, and output XML
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;data.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"guests"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"numbers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"numbers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Susan"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"numbers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;filter&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;.guests&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
  &lt;/span&gt;&lt;span class="nl"&gt;"person"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"@scale"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.scale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"#text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.age&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"favorite_numbers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.numbers&lt;/span&gt;&lt;span class="w"&gt; 
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;oq -o xml --xml-root people -f filter data.json
&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;people&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;person&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;age&lt;/span&gt; &lt;span class="na"&gt;scale=&lt;/span&gt;&lt;span class="s"&gt;"months"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;289&lt;span class="nt"&gt;&amp;lt;/age&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Jim&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;favorite_numbers&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;number&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/number&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;number&amp;gt;&lt;/span&gt;2&lt;span class="nt"&gt;&amp;lt;/number&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;number&amp;gt;&lt;/span&gt;3&lt;span class="nt"&gt;&amp;lt;/number&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/favorite_numbers&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/person&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;person&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;age&lt;/span&gt; &lt;span class="na"&gt;scale=&lt;/span&gt;&lt;span class="s"&gt;"years"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;51&lt;span class="nt"&gt;&amp;lt;/age&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Bob&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;favorite_numbers&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;number&amp;gt;&lt;/span&gt;4&lt;span class="nt"&gt;&amp;lt;/number&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;number&amp;gt;&lt;/span&gt;5&lt;span class="nt"&gt;&amp;lt;/number&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;number&amp;gt;&lt;/span&gt;6&lt;span class="nt"&gt;&amp;lt;/number&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/favorite_numbers&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/person&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;person&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;age&lt;/span&gt; &lt;span class="na"&gt;scale=&lt;/span&gt;&lt;span class="s"&gt;"days"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;31025&lt;span class="nt"&gt;&amp;lt;/age&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Susan&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;favorite_numbers&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;number&amp;gt;&lt;/span&gt;7&lt;span class="nt"&gt;&amp;lt;/number&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;number&amp;gt;&lt;/span&gt;8&lt;span class="nt"&gt;&amp;lt;/number&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;number&amp;gt;&lt;/span&gt;9&lt;span class="nt"&gt;&amp;lt;/number&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/favorite_numbers&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/person&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/people&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The approach on handling the JSON to XML transcoding is based on &lt;a href="https://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html"&gt;this article&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmarks
&lt;/h2&gt;

&lt;p&gt;I also ran some &lt;a href="https://github.com/stedolan/jq/wiki/X---Experimental-Benchmarks"&gt;benchmarks&lt;/a&gt; for &lt;code&gt;jq&lt;/code&gt;, &lt;code&gt;yq&lt;/code&gt;, and &lt;code&gt;oq&lt;/code&gt; to show how they compare in various situations.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;OS: &lt;code&gt;#1 SMP Debian 4.9.168-1+deb9u3 (2019-06-16)&lt;/code&gt;&lt;br&gt;
CPU: &lt;code&gt;Intel i7-7700k&lt;/code&gt;&lt;br&gt;
Memory: &lt;code&gt;32GB @ 3,000 MHz&lt;/code&gt;&lt;br&gt;
SSD: &lt;code&gt;Samsung 850 PRO - 512GB&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Benchmarks are done via the &lt;code&gt;/usr/bin/time -v&lt;/code&gt; command&lt;/p&gt;
&lt;h4&gt;
  
  
  Simple
&lt;/h4&gt;

&lt;p&gt;First, I used the &lt;code&gt;data.json&lt;/code&gt; file to see how they perform simply parsing the file and output itself via the &lt;code&gt;.&lt;/code&gt; filter.&lt;/p&gt;
&lt;h5&gt;
  
  
  jq
&lt;/h5&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nb"&gt;.&lt;/span&gt; data.json | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
    Command being timed: &lt;span class="s2"&gt;"jq . data.json"&lt;/span&gt;
    User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.02
    System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.01
    Percent of CPU this job got: 68%
    Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:00.06
    Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 16236
    Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
    Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 3860
    Voluntary context switches: 224
    Involuntary context switches: 8
    Swaps: 0
    File system inputs: 0
    File system outputs: 0
    Socket messages sent: 0
    Socket messages received: 0
    Signals delivered: 0
    Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
    Exit status: 0
31
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h5&gt;
  
  
  yq
&lt;/h5&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yq &lt;span class="nb"&gt;.&lt;/span&gt; spec/assets/data1.json | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
    Command being timed: &lt;span class="s2"&gt;"yq . data.json"&lt;/span&gt;
    User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.08
    System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.01
    Percent of CPU this job got: 77%
    Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:00.11
    Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 16252
    Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 1
    Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 7179
    Voluntary context switches: 189
    Involuntary context switches: 10
    Swaps: 0
    File system inputs: 1672
    File system outputs: 0
    Socket messages sent: 0
    Socket messages received: 0
    Signals delivered: 0
    Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
    Exit status: 0
31
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h5&gt;
  
  
  oq
&lt;/h5&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oq &lt;span class="nb"&gt;.&lt;/span&gt; data.json | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
    Command being timed: &lt;span class="s2"&gt;"oq . data.json"&lt;/span&gt;
    User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.02
    System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.04
    Percent of CPU this job got: 74%
    Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:00.10
    Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 16140
    Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
    Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 4499
    Voluntary context switches: 306
    Involuntary context switches: 13
    Swaps: 0
    File system inputs: 0
    File system outputs: 0
    Socket messages sent: 0
    Socket messages received: 0
    Signals delivered: 0
    Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
    Exit status: 0
31
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For this first test, all three are pretty much equal, with only a negligible difference in wallclock/memory used.&lt;/p&gt;
&lt;h4&gt;
  
  
  Jeopardy.json (#2)
&lt;/h4&gt;

&lt;p&gt;The next benchmark uses the &lt;code&gt;jeopardy.json&lt;/code&gt; ~56mb file as retrieved in &lt;code&gt;jq&lt;/code&gt;'s benchmark wiki page.&lt;/p&gt;

&lt;p&gt;First up, a simple &lt;code&gt;length jeopardy.json&lt;/code&gt; command.&lt;/p&gt;
&lt;h5&gt;
  
  
  jq
&lt;/h5&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq length jeopardy.json 
216930
    Command being timed: &lt;span class="s2"&gt;"jq length jeopardy.json"&lt;/span&gt;
    User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.64
    System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.10
    Percent of CPU this job got: 97%
    Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:00.76
    Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 230080
    Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
    Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 63213
    Voluntary context switches: 240
    Involuntary context switches: 13
    Swaps: 0
    File system inputs: 0
    File system outputs: 0
    Socket messages sent: 0
    Socket messages received: 0
    Signals delivered: 0
    Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
    Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h5&gt;
  
  
  yq
&lt;/h5&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yq length jeopardy.json 
216930
    Command being timed: &lt;span class="s2"&gt;"yq length jeopardy.json"&lt;/span&gt;
    User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 152.45
    System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 1.27
    Percent of CPU this job got: 100%
    Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 2:33.04
    Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 3853532
    Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
    Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 1117041
    Voluntary context switches: 13708
    Involuntary context switches: 3189
    Swaps: 0
    File system inputs: 0
    File system outputs: 0
    Socket messages sent: 0
    Socket messages received: 0
    Signals delivered: 0
    Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
    Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h5&gt;
  
  
  oq
&lt;/h5&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oq length jeopardy.json 
216930
    Command being timed: &lt;span class="s2"&gt;"oq length jeopardy.json"&lt;/span&gt;
    User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.67
    System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.17
    Percent of CPU this job got: 105%
    Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:00.80
    Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 230224
    Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
    Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 63839
    Voluntary context switches: 13832
    Involuntary context switches: 12
    Swaps: 0
    File system inputs: 0
    File system outputs: 0
    Socket messages sent: 0
    Socket messages received: 0
    Signals delivered: 0
    Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
    Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The big files do not bode well with &lt;code&gt;yq&lt;/code&gt;, with it taking ~190x longer than either &lt;code&gt;oq&lt;/code&gt; or &lt;code&gt;jq&lt;/code&gt;, while also using almost 17x more memory.&lt;/p&gt;
&lt;h4&gt;
  
  
  YAML =&amp;gt; XML
&lt;/h4&gt;

&lt;p&gt;The last benchmark I did was giving both &lt;code&gt;yq&lt;/code&gt; and &lt;code&gt;oq&lt;/code&gt; a large yaml file (~57mb), then having them convert it to XML.  Since &lt;code&gt;jq&lt;/code&gt; can't consume &lt;code&gt;YAML&lt;/code&gt;, I excluded it.&lt;/p&gt;

&lt;p&gt;The file used: &lt;code&gt;invItems.yaml&lt;/code&gt; from the &lt;a href="https://cdn1.eveonline.com/data/sde/tranquility/sde-20190625-TRANQUILITY.zip"&gt;EVE Online SDE Export&lt;/a&gt;.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt;   &lt;span class="na"&gt;flagID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="na"&gt;itemID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="na"&gt;locationID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="na"&gt;ownerID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;-1&lt;/span&gt;
    &lt;span class="na"&gt;typeID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt;   &lt;span class="na"&gt;flagID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="na"&gt;itemID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;locationID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="na"&gt;ownerID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;-1&lt;/span&gt;
    &lt;span class="na"&gt;typeID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  yq
&lt;/h5&gt;

&lt;p&gt;For yq, I had to give it a filter and some extra args for it to output correctly&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yq &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="nt"&gt;--xml-root&lt;/span&gt; items &lt;span class="nt"&gt;--xml-dtd&lt;/span&gt; &lt;span class="s1"&gt;'{"item": .[] | .}'&lt;/span&gt; invItems.yaml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; invItems.yq.xml
    Command being timed: &lt;span class="s2"&gt;"yq -s -x --xml-root items --xml-dtd {"&lt;/span&gt;item&lt;span class="s2"&gt;": .[] | .} invItems.yaml"&lt;/span&gt;
    User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 309.21
    System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 2.76
    Percent of CPU this job got: 100%
    Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 5:11.90
    Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 7817608
    Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
    Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 2262904
    Voluntary context switches: 32918
    Involuntary context switches: 2504
    Swaps: 0
    File system inputs: 0
    File system outputs: 195072
    Socket messages sent: 0
    Socket messages received: 0
    Signals delivered: 0
    Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
    Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;items&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;item&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;flagID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/flagID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;itemID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/itemID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;locationID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/locationID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ownerID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/ownerID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;quantity&amp;gt;&lt;/span&gt;-1&lt;span class="nt"&gt;&amp;lt;/quantity&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;typeID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/typeID&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;item&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;flagID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/flagID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;itemID&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/itemID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;locationID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/locationID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ownerID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/ownerID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;quantity&amp;gt;&lt;/span&gt;-1&lt;span class="nt"&gt;&amp;lt;/quantity&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;typeID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/typeID&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/items&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  oq
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oq &lt;span class="nt"&gt;-i&lt;/span&gt; yaml &lt;span class="nt"&gt;-o&lt;/span&gt; xml &lt;span class="nt"&gt;--xml-root&lt;/span&gt; items &lt;span class="nb"&gt;.&lt;/span&gt; invItems.yaml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; invItems.oq.xml
    Command being timed: &lt;span class="s2"&gt;"oq -i yaml -o xml --xml-root items . invItems.yaml"&lt;/span&gt;
    User &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 20.08
    System &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;: 0.48
    Percent of CPU this job got: 107%
    Elapsed &lt;span class="o"&gt;(&lt;/span&gt;wall clock&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;h:mm:ss or m:ss&lt;span class="o"&gt;)&lt;/span&gt;: 0:19.13
    Average shared text size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average unshared data size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average stack size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Average total size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Maximum resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 1332328
    Average resident &lt;span class="nb"&gt;set &lt;/span&gt;size &lt;span class="o"&gt;(&lt;/span&gt;kbytes&lt;span class="o"&gt;)&lt;/span&gt;: 0
    Major &lt;span class="o"&gt;(&lt;/span&gt;requiring I/O&lt;span class="o"&gt;)&lt;/span&gt; page faults: 0
    Minor &lt;span class="o"&gt;(&lt;/span&gt;reclaiming a frame&lt;span class="o"&gt;)&lt;/span&gt; page faults: 522235
    Voluntary context switches: 30478
    Involuntary context switches: 974
    Swaps: 0
    File system inputs: 0
    File system outputs: 195072
    Socket messages sent: 0
    Socket messages received: 0
    Signals delivered: 0
    Page size &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: 4096
    Exit status: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;items&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;item&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;flagID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/flagID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;itemID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/itemID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;locationID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/locationID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ownerID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/ownerID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;quantity&amp;gt;&lt;/span&gt;-1&lt;span class="nt"&gt;&amp;lt;/quantity&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;typeID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/typeID&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;item&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;flagID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/flagID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;itemID&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/itemID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;locationID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/locationID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ownerID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/ownerID&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;quantity&amp;gt;&lt;/span&gt;-1&lt;span class="nt"&gt;&amp;lt;/quantity&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;typeID&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/typeID&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/items&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly to the &lt;code&gt;jeopary.json&lt;/code&gt; benchmark, &lt;code&gt;yq&lt;/code&gt; just has a hard time dealing with the larger inputs with this test case taking ~16x longer and using almost 6x the memory than &lt;code&gt;oq&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Road to 1.0.0
&lt;/h2&gt;

&lt;p&gt;Since this project is still early in its development, I put together a roadmap of what I would like to get done before calling it &lt;code&gt;1.0.0&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Support XML input format&lt;/li&gt;
&lt;li&gt;Address bugs/issues that arise&lt;/li&gt;
&lt;li&gt;Small feature requests&lt;/li&gt;
&lt;li&gt;Possibly additional formats&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to submit issues/PRs.&lt;/p&gt;

</description>
      <category>crystal</category>
      <category>jq</category>
      <category>json</category>
      <category>python</category>
    </item>
    <item>
      <title>Productive logging with Athena &amp; Crylog</title>
      <dc:creator>Blacksmoke16</dc:creator>
      <pubDate>Sat, 01 Jun 2019 20:03:33 +0000</pubDate>
      <link>https://dev.to/blacksmoke16/productive-logging-with-athena-crylog-3pbe</link>
      <guid>https://dev.to/blacksmoke16/productive-logging-with-athena-crylog-3pbe</guid>
      <description>&lt;h1&gt;
  
  
  &lt;strong&gt;This article was written for an older version of Athena and is no longer valid.  Its content has been integrated into &lt;a href="https://dev.to/blacksmoke16/creating-a-json-api-with-athena--granite-510i"&gt;Creating a JSON API with Athena &amp;amp; Granite&lt;/a&gt; and &lt;a href="https://dev.to/blacksmoke16/dependency-injection-in-crystal-2d66"&gt;Dependency Injection in Crystal&lt;/a&gt;.&lt;/strong&gt;
&lt;/h1&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Also, &lt;code&gt;Crylog&lt;/code&gt; has been deprecated in favor of the standard libraries' &lt;a href="https://crystal-lang.org/api/Log.html"&gt;Log&lt;/a&gt; module.&lt;/strong&gt;
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Logging
&lt;/h1&gt;

&lt;p&gt;Logging is an important tool when creating any application.  Logging adds information that could be useful for debugging issues, doing analytics, or just gathering user behavior data; all of which can be used to inform future decisions.&lt;/p&gt;

&lt;p&gt;However not all logging is equal.  Logging should be setup in such a way so that only useful information is logged, depending on the environment. &lt;br&gt;
For example, a developer working on an application would love to see debug level information so that they have all of the possible information available in order to track down issues and find bottlenecks.  The application running in the production environment should &lt;em&gt;NOT&lt;/em&gt; see debug level logs so that the log files are not cluttered, which causes actual important issues to be lost in the shuffle.&lt;/p&gt;
&lt;h2&gt;
  
  
  Crylog
&lt;/h2&gt;

&lt;p&gt;Crylog is a new flexible logging framework for Crystal based on &lt;a href="https://github.com/Seldaek/monolog/"&gt;Monolog&lt;/a&gt;.  It allows applications to have multiple named loggers, each with various handlers, processors, formatters. &lt;/p&gt;

&lt;p&gt;A handler would be something that does something with a logged message.  For example logging to a file or STDOUT.  But it could also be sending the message to Sentry, or a database etc.&lt;/p&gt;

&lt;p&gt;A processor adds extra data to each message.  A good use case of this would be adding user/customer ids to each logged message that would represent the user which caused that message to be logged.&lt;/p&gt;

&lt;p&gt;Lastly, a formatter of course determines how a message gets serialized.  Formatters allow different styles to be used depending on the handler, as HTML would be great for email handlers but not so much for log files.&lt;/p&gt;

&lt;p&gt;When used together, Crylog allows for various loggers to be customized to handle the various aspects of an application.  Such as only logging messages that are warnings or higher in the production environment, but all messages during development.&lt;/p&gt;
&lt;h2&gt;
  
  
  Athena
&lt;/h2&gt;

&lt;p&gt;Athena is an annotation based web framework which uses Crylog as its logging solution.  For this article I'm going to be demonstrating some of the benefits that Crylog enables when combined with some neat Athena features, such as Dependency Injection.  This will be a continuation of the &lt;a href="https://dev.to/blacksmoke16/creating-a-json-api-with-athena--granite-510i"&gt;Blog Article&lt;/a&gt; I wrote a few months ago.&lt;/p&gt;
&lt;h3&gt;
  
  
  Dependency Injection (DI)
&lt;/h3&gt;

&lt;p&gt;One of the newer features of Athena is a service container layer.  This allows a project to share useful objects, aka services, throughout the project.  These objects live in a special class called the Service Container (SC).  Object instances can be retrieved from the container, or even injected directly into classes as a form of constructor DI.  While this article is not focused on DI specifically, it will be using it.  For more information related to it, see the &lt;a href="https://github.com/Blacksmoke16/athena/blob/master/docs/dependency_injection.md"&gt;Athena Docs&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Agenda
&lt;/h2&gt;

&lt;p&gt;First, lets define some goals we wish to achieve via our logging.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log some informational messages when articles are created/deleted, or a user logins, or a new user registers, along with some contextual information.&lt;/li&gt;
&lt;li&gt;Both scenarios should be logged to a file, but the development environment should also log to STDOUT.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By default Athena will log all messages to STDOUT and a &lt;code&gt;development.log&lt;/code&gt; file when in the &lt;code&gt;development&lt;/code&gt; environment, while the &lt;code&gt;production&lt;/code&gt; environment logs to a &lt;code&gt;production.log&lt;/code&gt; file, but only for warnings and higher.  Also since we want to add some custom logic into our loggers, we'll need to define our own configuration.&lt;/p&gt;
&lt;h3&gt;
  
  
  Security HTTP Handler
&lt;/h3&gt;

&lt;p&gt;In the last tutorial, we created a &lt;code&gt;HTTP::Handler&lt;/code&gt; class to handle authorization around the token used in each request.  I do have plans for some security related features that I use in this article to be built into Athena itself, however for now it is up to the user to define.&lt;/p&gt;

&lt;p&gt;However there are some problems with this approach.  As I left off in the previous tutorial, how would we know what user is logged in within our controller actions?  To solve this we can create a &lt;code&gt;UserStorage&lt;/code&gt; class that will store the current user, so that other classes would have access to it via DI.  Lets create a new directory under &lt;code&gt;src&lt;/code&gt; called &lt;code&gt;services&lt;/code&gt; that I'll put the &lt;code&gt;UserStorage&lt;/code&gt; service.  Next create a new file called &lt;code&gt;user_storage.cr&lt;/code&gt; with the following code.  Be sure to require it in your &lt;code&gt;blog.cr&lt;/code&gt; file as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Blog&lt;/span&gt;
  &lt;span class="nd"&gt;@[Athena::DI::Register]&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserStorage&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ClassService&lt;/span&gt;
    &lt;span class="c1"&gt;# Use a ! property since they'll always be a user defined in our use case.&lt;/span&gt;
    &lt;span class="c1"&gt;# It also defines a nilable getter method to make sure there is a user&lt;/span&gt;
    &lt;span class="kp"&gt;property&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Notice how we are inheriting from &lt;code&gt;Athena::DI::ClassService&lt;/code&gt;, this allows Athena to get a list of all services to register, while the &lt;code&gt;@[Athena::DI::Register]&lt;/code&gt; allows some configuration on how the service is registered.&lt;/p&gt;

&lt;p&gt;Now we can go back to our security handler, get the user storage service, and set the user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Blog&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SecurityHandler&lt;/span&gt;
    &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Handler&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Server&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
      &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="c1"&gt;# Get the user storage service from the container&lt;/span&gt;
      &lt;span class="n"&gt;user_storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"user_storage"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UserStorage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="c1"&gt;# Set the user in user storage&lt;/span&gt;
      &lt;span class="n"&gt;user_storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find!&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

      &lt;span class="c1"&gt;# Call the next handler&lt;/span&gt;
      &lt;span class="n"&gt;call_next&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can go do a similar thing and update the references in the &lt;code&gt;ArticleController&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Blog::Controllers&lt;/span&gt;
  &lt;span class="nd"&gt;@[Athena::Routing::ControllerOptions(prefix: "article")]&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArticleController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Routing&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;
    &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Injectable&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@request_stack&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Routing&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RequestStack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@user_storage&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;UserStorage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="nd"&gt;@[Athena::Routing::Post(path: "")]&lt;/span&gt;
    &lt;span class="nd"&gt;@[Athena::Routing::ParamConverter(param: "body", type: Blog::Models::Article, converter: Athena::Routing::Converters::RequestBody)]&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;
      &lt;span class="c1"&gt;# Sets the owner of the blog post as the current authed user&lt;/span&gt;
      &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@user_storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;
      &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
      &lt;span class="n"&gt;body&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="nd"&gt;@[Athena::Routing::Get(path: "")]&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_articles&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c1"&gt;# We are also using the user in UserStorage as an additional conditional in our query when fetching articles&lt;/span&gt;
      &lt;span class="c1"&gt;# this allows us to only returns articles that belong to the current user.&lt;/span&gt;
      &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:deleted_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:neq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:eq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@user_storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="nd"&gt;@[Athena::Routing::Put(path: "")]&lt;/span&gt;
    &lt;span class="nd"&gt;@[Athena::Routing::ParamConverter(param: "body", type: Blog::Models::Article, converter: Athena::Routing::Converters::RequestBody)]&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;
      &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@user_storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;
      &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
      &lt;span class="n"&gt;body&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="nd"&gt;@[Athena::Routing::Get(path: "/:article_id")]&lt;/span&gt;
    &lt;span class="nd"&gt;@[Athena::Routing::ParamConverter(param: "article", pk_type: Int64, type: Blog::Models::Article, converter: Athena::Routing::Converters::Exists)]&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;
      &lt;span class="n"&gt;article&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="nd"&gt;@[Athena::Routing::Delete(path: "/:article_id")]&lt;/span&gt;
    &lt;span class="nd"&gt;@[Athena::Routing::ParamConverter(param: "article", pk_type: Int64, type: Blog::Models::Article, converter: Athena::Routing::Converters::Exists)]&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete_article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
      &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleted_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utc&lt;/span&gt;
      &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
      &lt;span class="vi"&gt;@request_stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ACCEPTED&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are also able to inject the &lt;code&gt;RequestStack&lt;/code&gt; in order to have access to the request/response.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: Services can only be auto injected, like the &lt;code&gt;ArticleController&lt;/code&gt; if the class gets instantiated within the request/response cycle.  Classes instantiated outside of it, like the &lt;code&gt;SecurityHandler&lt;/code&gt;, do not have access to the same container and services must be fetched manually.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that we have our authentication problem resolved we can move onto adding some logging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Logs
&lt;/h3&gt;

&lt;p&gt;We'll do the two public routes first.  Simply go to your &lt;code&gt;AuthController&lt;/code&gt; and add a &lt;code&gt;Athena.logger.info "User logged in", Crylog::LogContext{"user_id" =&amp;gt; user.id}&lt;/code&gt; before the &lt;code&gt;{token: user.generate_jwt}&lt;/code&gt;.  This would log&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[2019-11-20T01:40:40Z] main.INFO: User logged in {"user_id":1}&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;our message with the id of the user that logged in.  We can do a similar thing in our &lt;code&gt;UserController&lt;/code&gt;.  Add a &lt;code&gt;Athena.logger.info "New user registered", Crylog::LogContext{"user_id" =&amp;gt; body.id, "email" =&amp;gt; body.email, "first_name" =&amp;gt; body.first_name, "last_name" =&amp;gt; body.last_name}&lt;/code&gt; after saving the user model.  This would log that someone registered with some information about that user.&lt;/p&gt;

&lt;p&gt;We could also do the same thing in our &lt;code&gt;ArticleController&lt;/code&gt;, adding like &lt;code&gt;Athena.logger.info "Article ##{body.id} was created", Crylog::LogContext{"user_id" =&amp;gt; @user_storage.user.id}&lt;/code&gt;.  However we could keep things more DRY by using a Crylog Processor since the &lt;code&gt;ArticleController&lt;/code&gt; are authenticated endpoints, which would have a user in the &lt;code&gt;UserStorage&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's create a new directory under &lt;code&gt;src&lt;/code&gt; called &lt;code&gt;logger&lt;/code&gt; that I'll put the processor.  Next create a new file called &lt;code&gt;user_processor.cr&lt;/code&gt; with the following code.  Be sure to require it in your &lt;code&gt;blog.cr&lt;/code&gt; file as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Blog&lt;/span&gt;
  &lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;UserProcessor&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Crylog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Processors&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;LogProcessor&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Crylog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
      &lt;span class="n"&gt;user_storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"user_storage"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UserStorage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="c1"&gt;# Return early if a message was logged in a public endpoint there won't be a user in storage&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user?&lt;/span&gt;

      &lt;span class="c1"&gt;# Add the current user's id to all log messages&lt;/span&gt;
      &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would add the current user's id to every logged message as part of the message's extra property.  It also handles the case where we were logging users logging in and registering, which would not have a user in storage.  Another option would be to define a logger specifically for public stuff, but that is a bit overkill for now.&lt;/p&gt;

&lt;p&gt;Next we need to tell Crylog to use our processor.  We will also handle the 2nd bullet point in our agenda. Within &lt;code&gt;blog.cr&lt;/code&gt; module add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure_logger&lt;/span&gt;
    &lt;span class="c1"&gt;# Create the logs dir if it doesn't exist already.&lt;/span&gt;
    &lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logs_dir&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists?&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logs_dir&lt;/span&gt;
    &lt;span class="no"&gt;Crylog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;handlers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="no"&gt;Crylog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Handlers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;LogHandler&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"development"&lt;/span&gt;
          &lt;span class="c1"&gt;# Log to STDOUT and development log file if in develop env.&lt;/span&gt;
          &lt;span class="n"&gt;handlers&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Crylog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Handlers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IOHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;STDOUT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="n"&gt;handlers&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Crylog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Handlers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IOHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logs_dir&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/development.log"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"production"&lt;/span&gt;
          &lt;span class="c1"&gt;# Log only to a file if in production env.&lt;/span&gt;
          &lt;span class="n"&gt;handlers&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Crylog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Handlers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IOHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logs_dir&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/production.log"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="c1"&gt;# Tell crylog to use our processor on the main logger.&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;processors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UserProcessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="no"&gt;Crylog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Processors&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;LogProcessors&lt;/span&gt;

        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handlers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;handlers&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method tells Crylog how to configure the loggers to use.  As you can see, we are logging to STDOUT and a &lt;code&gt;development.log&lt;/code&gt; file when in the dev environment, but only a &lt;code&gt;production&lt;/code&gt; file when in the prod environment.  Notice we are opening the log file with the &lt;code&gt;a&lt;/code&gt; option; this makes makes it so messages are appended to the log instead of completely overwriting it.  We are also telling it to use our &lt;code&gt;UserProcessor&lt;/code&gt;.  For more information regarding the possible logging configurations, checkout the &lt;a href="https://github.com/Blacksmoke16/crylog/tree/master/docs"&gt;Crylog documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After that, start the server and start doing things to see messages logged.  Messages logged in the &lt;code&gt;ArticleController&lt;/code&gt; will have the current user id included in the message; just like when a user logs in, but without having to explicitly specify it.&lt;/p&gt;

&lt;p&gt;From here additional handlers/processors etc could be defined to also log these messages to Greylog, Sentry, or even send emails if they are of a high enough severity.  Crylog allows for all sorts of flexibility when deciding on how best implement logging for your project.&lt;/p&gt;

&lt;p&gt;Update November 19, 2019 for some minor polish &amp;amp; changes based on previous tutorial/new Athena version&lt;/p&gt;

</description>
      <category>crystal</category>
      <category>logging</category>
    </item>
    <item>
      <title>Utilizing Macros &amp; Annotations in a Web Framework</title>
      <dc:creator>Blacksmoke16</dc:creator>
      <pubDate>Thu, 04 Apr 2019 12:29:23 +0000</pubDate>
      <link>https://dev.to/blacksmoke16/utilizing-macros-annotations-in-a-web-framework-3abk</link>
      <guid>https://dev.to/blacksmoke16/utilizing-macros-annotations-in-a-web-framework-3abk</guid>
      <description>&lt;p&gt;Macros, and especially annotations, are among the least documented features of Crystal.  Because of this, I wanted to take the time and write a little post about how I used annotations and frameworks to build my web framework, focusing on how they are used, not necessarily the framework itself.&lt;/p&gt;

&lt;p&gt;A few months ago I started on, yet another, web framework project. However I wanted to take a different approach, taking into consideration the experiences I have from my other side projects as well as those at my work.  The result of this was &lt;a href="https://github.com/blacksmoke16/athena"&gt;Athena&lt;/a&gt;.  Unlike other frameworks that rely mostly on macros to define the routes, Athena uses annotations &lt;em&gt;with&lt;/em&gt; macros.  This allows for code that is very readable, with route actions being just class methods.  This has a few benefits such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easier to document - Are just methods so doc comments work just fine.&lt;/li&gt;
&lt;li&gt;Easier to test - Are just methods again, can just call your actions directly to test.&lt;/li&gt;
&lt;li&gt;Easier to read/understand - There is no special syntax needed (other than learning the annotations)&lt;/li&gt;
&lt;li&gt;Inheritance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Annotations are use heavily, as the main mechanism to define routes, callbacks, and even CLI commands.  I also used them in my other serialization/validation shard &lt;a href="https://github.com/blacksmoke16/CrSerializer"&gt;CrSerializer&lt;/a&gt; as an alternative way to manage property serialization/validations of models.&lt;/p&gt;

&lt;h1&gt;
  
  
  Macros
&lt;/h1&gt;

&lt;p&gt;Macros are defined as (from the git book), "Methods that receive AST nodes at compile-time and produce code that is pasted into a program." Or in other words, code that you write that expands into Crystal code at compile time and gets inserted into the program.  Macros are almost a language on their own; quite powerful and flexible.  Macros allow you to reduce boilerplate by writing code that can define classes, methods, etc.&lt;/p&gt;

&lt;h1&gt;
  
  
  Annotations
&lt;/h1&gt;

&lt;p&gt;Annotations, one of the least documented but most powerful features in Crystal.  In short, annotations allow types (classes, structs, methods, and properties) to have information added to them that is available at compile time, and readable via macros.&lt;/p&gt;

&lt;p&gt;Annotations are defined similarly to a class or struct, but using the &lt;code&gt;annotation&lt;/code&gt; keyword.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;annotation&lt;/span&gt; &lt;span class="no"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm going to define some scenarios I wanted to solve when setting out with creating Athena.  Then do a walk-through of my thinking and how I used macros and annotations to solve those problems.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Develop a way of dynamically registering routes based on a specific annotation as opposed to using a standard &lt;code&gt;get "/path" do { }&lt;/code&gt; macro for example.&lt;/li&gt;
&lt;li&gt;Ability to easily validate properties on the ORM models by applying annotations to each property, instead of using a &lt;code&gt;validate xxx&lt;/code&gt; macro.&lt;/li&gt;
&lt;li&gt;Similar to 1, but to have self registering CLI commands that get automatically exposed to an &lt;code&gt;OptionParser&lt;/code&gt; interface.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Registering Routes
&lt;/h2&gt;

&lt;p&gt;In order to solve this first obstacle, I had to think.  "How can I get an Array of controllers with routes defined?"&lt;/p&gt;

&lt;p&gt;My ideal solution would have been like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[Athena::Routing::Controller]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyController&lt;/span&gt;
  &lt;span class="nd"&gt;@[Athena::Routing::Get(path: "/me")]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_me&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
    &lt;span class="s2"&gt;"Jim"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nd"&gt;@[Athena::Routing::Post(path: "/user")]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_me&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="ss"&gt;:Routing&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;types&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
  &lt;span class="c1"&gt;# Iterate over all the classes with the `Controller` annotation&lt;/span&gt;
&lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thus, any class that has the &lt;code&gt;Athena:::Routing::Controller&lt;/code&gt; would be known to be a controller, class methods would be annotated to tie a given route to the code that should be executed when the route is matched.  However, this is not currently possible.  See &lt;a href="https://github.com/crystal-lang/crystal/issues/7274"&gt;#7274&lt;/a&gt;.  So I had to think of plan B.&lt;/p&gt;

&lt;p&gt;The Crystal generated API docs were super helpful in this regard, especially &lt;a href="https://crystal-lang.org/api/0.27.2/Crystal/Macros/TypeNode.html"&gt;TypeNode&lt;/a&gt;.  A &lt;code&gt;TypeNode&lt;/code&gt; represents a type in a program, such as &lt;code&gt;String&lt;/code&gt;, or &lt;code&gt;MyController&lt;/code&gt;, etc.  Reading through the available methods, I noticed there was an &lt;code&gt;all_subclasses&lt;/code&gt; method; which returns an array of all the subclasses of that type.  Ah ha!  I then had the idea, similar to Rails and their &lt;code&gt;ApplicationController&lt;/code&gt;, that I could make my own class, have controllers inherit from that, and use &lt;code&gt;all_subclasses&lt;/code&gt; to iterate over all controllers at compile time.  Great!&lt;/p&gt;

&lt;p&gt;This discovery lead to the syntax that is used today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Routing&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;
  &lt;span class="nd"&gt;@[Athena::Routing::Get(path: "/me")]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_me&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
    &lt;span class="s2"&gt;"Jim"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nd"&gt;@[Athena::Routing::Post(path: "/user")]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_me&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However there was a new problem.  Where do I do this?  Turning my attention to the various routers available, such as &lt;a href="https://github.com/amberframework/amber-router"&gt;Amber Router&lt;/a&gt;.  In the examples, each router required the user to define a tree, add routes to it, and find those routes all in the same file.  The key problem here was I needed a way so the &lt;em&gt;user&lt;/em&gt; doesn't have to &lt;em&gt;know&lt;/em&gt; about how to do any of that.  I needed a way to hide the tree creation, adding of routes, and resolving routes behind the scenes.  &lt;/p&gt;

&lt;p&gt;I went back to the Crystal API docs, specifically the &lt;code&gt;HTTP::Handler&lt;/code&gt; class.  This module, when included, requires a class to define a &lt;code&gt;call&lt;/code&gt; method, that will be executed on each HTTP request.  Also, since it is nothing more than a class, I thought I could add an &lt;code&gt;initialize&lt;/code&gt; method that would run once when newing up the handler for the &lt;code&gt;HTTP::Server&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Perfect!  A simplified cut down version of this ultimately ended up looking like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RouteHandler&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Routing&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Handlers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Handler&lt;/span&gt;
  &lt;span class="vi"&gt;@routes&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Amber&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Router&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RouteSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Amber&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Router&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RouteSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Routing&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all_subclasses&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
      &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="nb"&gt;methods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Put&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
      &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;methods&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
        &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
          &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GET"&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
          &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="n"&gt;route_def&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
        &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
          &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"POST"&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
          &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="n"&gt;route_def&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
        &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;

        &lt;span class="c1"&gt;# Action is a record that contains data about the action.&lt;/span&gt;
        &lt;span class="c1"&gt;# Such as params, the action to execute, path, controller etc. &lt;/span&gt;
        &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xxx&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;

        &lt;span class="vi"&gt;@routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;route_def&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:path&lt;/span&gt;&lt;span class="p"&gt;]}},&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
      &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
    &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I first defined an instance variable &lt;code&gt;routes&lt;/code&gt;, which is instantiated to be a new instance of my router tree.  I then defined an &lt;code&gt;initialize&lt;/code&gt; method.  Within this method, I used a macro loop to iterate over the subclasses of the base controller class.  I then used &lt;code&gt;.select&lt;/code&gt; to get an array of actions defined on each controller, specifically looking for class methods annotated with one of the HTTP verb annotations.  This allowed for private class methods to be used within a class, but not expected to be related to a route.  I then set the method and route_definition based on what annotation was matched.  Finally I register the route with the router.&lt;/p&gt;

&lt;p&gt;Annotation fields can be access using the &lt;code&gt;[]&lt;/code&gt; method, either using a &lt;code&gt;String&lt;/code&gt; or &lt;code&gt;Symbol&lt;/code&gt; for named fields, or an &lt;code&gt;Int32&lt;/code&gt; for index based.&lt;/p&gt;

&lt;p&gt;Since this is all macro code, the actual code that gets added to the program at compile time would look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RouteHandler&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Routing&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Handlers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Handler&lt;/span&gt;
  &lt;span class="vi"&gt;@routes&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Amber&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Router&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RouteSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Amber&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Router&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RouteSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="vi"&gt;@routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="s2"&gt;"/GET/me"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;RouteAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"get_me"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xxx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="s2"&gt;"/POST/user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;RouteAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"new_user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xxx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cool!  Now I could define the &lt;code&gt;call&lt;/code&gt; method to handle routing. Again, a simplified trimmed down version of this ended up looking like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Server&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;search_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;
  &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt; &lt;span class="n"&gt;search_key&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;found?&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;not_nil!&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Routing&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NotFoundException&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s2"&gt;"No route found for '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;call_next&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This calls other handlers I defined for CORS, and executing the actual action tied to the given route.  However that doesn't involve any other notable uses of annotations/macros so I'm just going to skip it.  However, If you're interested in learning more about how Athena is setup, feel free to message me on the Crystal Gitter.&lt;/p&gt;

&lt;p&gt;This portion is now complete and could be used similarly to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="no"&gt;RouteHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind_tcp&lt;/span&gt; &lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;
&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Property Validation
&lt;/h2&gt;

&lt;p&gt;The next challenge I wanted to solve was validations, also following an annotation based approach.  The idea was you could add annotations to a property, then on a POST endpoint for example, run those validations.  If the validations pass, pass an instance of your model to the route action, otherwise return a 400.&lt;/p&gt;

&lt;p&gt;I wanted to have this built on top of &lt;code&gt;JSON::Serializable&lt;/code&gt;, so that validations would work out of the box for any class/struct using that as its serialization/deserialization method.&lt;/p&gt;

&lt;p&gt;While it sounds simple. "Just create some validation annotations, add a &lt;code&gt;validate&lt;/code&gt; method that iterates over those annotations like you did for the routes, and there you go."  However, it was not that simple :P&lt;/p&gt;

&lt;p&gt;Originally I came up with the syntax of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[CrSerializer::Assertions(greater_than_or_equal: 0, nil: false)]&lt;/span&gt; 
&lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I could read the fields off the annotation, and run corresponding logic to run the assertions.  &lt;/p&gt;

&lt;p&gt;However it quickly became apparent this approach has some flaws.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The line quickly gets long if there are a lot of assertions.&lt;/li&gt;
&lt;li&gt;There is no easy way to force required fields, must supply &lt;code&gt;min&lt;/code&gt; AND &lt;code&gt;max&lt;/code&gt; for example.&lt;/li&gt;
&lt;li&gt;It's not expandable.  If a user wanted to add their own assertion, I could not possibly know the keys they would define let alone what to do with them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So after some thinking I pivoted in favor of this syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[Assert::NotNil]&lt;/span&gt; 
&lt;span class="nd"&gt;@[Assert::GreaterThanOrEqual(value: 0)]&lt;/span&gt; 
&lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The benefits of this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easier to read, as the lines do not get as long&lt;/li&gt;
&lt;li&gt;More expandable&lt;/li&gt;
&lt;li&gt;Easier to see related fields&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But of course there was some challenges with this approach.  There is currently no way to just get an array of annotations applied to a type.  Each annotations has to be read by its name.  I now needed a way to know the names of each assertion annotation, what fields to read off of it, and a way to tie a given annotation to some specific logic.&lt;/p&gt;

&lt;p&gt;I worked around the first two problems by knowing that constants are available at compile time.  I defined a constant hash, mapping annotation names to an array of fields that would be read off of it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="no"&gt;ASSERTIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="no"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Email&lt;/span&gt;              &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:mode&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="no"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IP&lt;/span&gt;                 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:version&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="no"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Uuid&lt;/span&gt;               &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:versions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:variants&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:strict&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="no"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Url&lt;/span&gt;                &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:protocols&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:relative_protocol&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="no"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RegexMatch&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:match&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since constants are available at compile time, it is possible to use them in a macro loop; which I did to iterate over the name/fields of each assertion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% for ivar in @type.instance_vars %}
  {% for t, v in CrSerializer::Assertions::ASSERTIONS %}
    {% ann = ivar.annotation(t.resolve) %}
    {% if ann %}
      assertions &amp;lt;&amp;lt; {{t.resolve.name.split("::").last.id}}Assertion({{ivar.type.id}}).new({{ivar.stringify}},{{ann[:message]}},{{ivar.id}},{{v.select { |fi| ann[fi] != nil }.map { |f| %(#{f.id}: #{ann[f]}) }.join(',').id}})
    {% end %}
  {% end %}
{% end %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example introduces a new macro concept, &lt;code&gt;@type&lt;/code&gt;.  When used within a macro, &lt;code&gt;@type&lt;/code&gt; represents the current scope or type as a &lt;code&gt;TypeNode&lt;/code&gt;.  In my case, &lt;code&gt;@type&lt;/code&gt; would represent the class that included &lt;code&gt;CrSerializer&lt;/code&gt;.  I used &lt;code&gt;@type.instance_vars&lt;/code&gt; to be able to iterate over each instance variable, read the annotations from it, and add the assertions if any exist.  I also use it to add the type and name of each instance variable to the assertion instantiation.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; &lt;code&gt;@type.instance_vars&lt;/code&gt; only currently works in the context of a instance/class method.  See &lt;a href="https://github.com/crystal-lang/crystal/issues/7504"&gt;#7504&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now that I had a way to handle the first two issues, I still needed a way to tie a given assertion to the logic specific to that assertion.  To do this, I used the key of the &lt;code&gt;ASSERTIONS&lt;/code&gt; hash, plus &lt;code&gt;Assertion&lt;/code&gt; as the class name to instantiate a new class of that type.  For example the logic for &lt;code&gt;Assert::NotNil&lt;/code&gt; would be handled in a class called &lt;code&gt;NotNilAssertion&lt;/code&gt;; which looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotNilAssertion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ActualValueType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Assertion&lt;/span&gt;
  &lt;span class="vi"&gt;@message&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"'{{field}}' should not be null"&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="vi"&gt;@actual&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ActualValueType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;valid?&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Bool&lt;/span&gt;
    &lt;span class="vi"&gt;@actual&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each assertion class has instance variables that map to the fields in the array from the &lt;code&gt;ASSERTIONS&lt;/code&gt; hash.  I use a macro to iterate over those fields, find the ones that are set, then new up an instance of that assertion with those values. I added this to a &lt;code&gt;validate&lt;/code&gt; method that gets added to the type when including the &lt;code&gt;CrSerializer&lt;/code&gt; module.  Again, since this is all macro code, the actual code that gets inserted into the program would look like, based on the example above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
  &lt;span class="n"&gt;assertions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="no"&gt;CrSerializer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Assertions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Assertion&lt;/span&gt;
  &lt;span class="n"&gt;assertions&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;NotNilAssertion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;?).&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
  &lt;span class="n"&gt;assertions&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;GreaterThanOrEqualAssertion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;?).&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;value: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CrSerializer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="n"&gt;assertions&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But wait, if the &lt;code&gt;ASSERTIONS&lt;/code&gt; is a constant, how would this help with allowing users to add their own assertions?&lt;/p&gt;

&lt;p&gt;Using a macro I'm able to define the annotation with the given name, and add properties to the hash at compile time, so that the hash contains the standard assertions plus any assertions defined by the user at runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;macro&lt;/span&gt; &lt;span class="nf"&gt;register_assertion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;CrSerializer::Assertions&lt;/span&gt;
    &lt;span class="n"&gt;annotation&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}};&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="no"&gt;CrSerializer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Assertions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ASSERTIONS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user would use this like &lt;code&gt;register_assertion Assert::MyCustom, [:value, :high_is_good]&lt;/code&gt;, and of course have a &lt;code&gt;MyCustomAssertion&lt;/code&gt; class to handle the logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[Assert::MyCustom(value: 100, high_is_good: true)]&lt;/span&gt;
&lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Auto Registering CLI Commands
&lt;/h2&gt;

&lt;p&gt;This is quite similar to the regiserting routes problem, but I'll include it as another example of what macros can do.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Registry&lt;/span&gt;
  &lt;span class="k"&gt;macro&lt;/span&gt; &lt;span class="nf"&gt;finished&lt;/span&gt;
    &lt;span class="n"&gt;class_getter&lt;/span&gt; &lt;span class="n"&gt;commands&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cli&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cli&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subclasses&lt;/span&gt;&lt;span class="p"&gt;}}{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cli&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subclasses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cli&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This defines a class getter with a value of an array of classes that inherit from the parent &lt;code&gt;Command&lt;/code&gt; class.  The value of the array is built out at compile time by simply calling &lt;code&gt;{{Athena::Cli::Command.subclasses}}&lt;/code&gt;, which returns an array of classes.  This has to be done within a &lt;code&gt;finished&lt;/code&gt; macro, so that the types are known to prevent type mismatches.&lt;/p&gt;

&lt;p&gt;I then used a macro that inserts an &lt;code&gt;OptionParser&lt;/code&gt; that acts as an interface to list/run commands&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;macro&lt;/span&gt; &lt;span class="nf"&gt;register_commands&lt;/span&gt;
  &lt;span class="no"&gt;OptionParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse!&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;banner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Usage: YOUR_BINARY [arguments]"&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"-h"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--help"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Show this help"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"-l"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--list"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"List available commands"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cli&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"-c NAME"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--command=NAME"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Run a command with the given name"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cli&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="no"&gt;ARGV&lt;/span&gt;
      &lt;span class="nb"&gt;exit&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;MigrateEventsCommand&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cli&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Command&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;command_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"migrate:events"&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Migrates legacy events for a given customer"&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_ids&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
    &lt;span class="c1"&gt;# Do stuff for the migration&lt;/span&gt;
    &lt;span class="c1"&gt;# Params are converted from strings to their expected types&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All that has to be done to use the commands is calling the &lt;code&gt;register_commands&lt;/code&gt; macro in your main app file, and create your command struct inheriting from the base command struct.  This will then get picked up by &lt;code&gt;Athena::Cli::Command.subclasses&lt;/code&gt; and registered at compile time.&lt;/p&gt;

&lt;p&gt;Ultimately it could then be used like &lt;code&gt;./MyApp -c migrate:events --customer_id=83726 --event_ids=1,2,3,4,5&lt;/code&gt;.  Which would search the registry for the command and pass the args to it if it was defined.&lt;/p&gt;

&lt;h1&gt;
  
  
  Addendums
&lt;/h1&gt;

&lt;p&gt;I didn't really know what to cover, as it's kind of hard to give examples out of the blue.  I'll leave this section open for future additions.  If there is something specific you would like to see an example of, or explained further feel free to ask.&lt;/p&gt;

&lt;h2&gt;
  
  
  ORM
&lt;/h2&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/jwoertink"&gt;@jwoertink&lt;/a&gt; made me think of another example I can walkthrough to go over the idea of "a reason I'd use them."  I have been working on an annotation version of &lt;a href="https://github.com/amberframework/granite"&gt;Granite&lt;/a&gt;, mostly for the lols, that I can give some examples from.&lt;/p&gt;

&lt;p&gt;Annotations require a different view of how to structure an application as compared to simply using normal macros.  We are all aware of the macro syntax ORMs use to define columns, i.e. &lt;code&gt;field name : String&lt;/code&gt;.  Behind the scenes the macro would &lt;a href="https://github.com/amberframework/granite/blob/master/src/granite/fields.cr#L16"&gt;add that field&lt;/a&gt;'s name, type, and any options supplied to a constant so that at a &lt;a href="https://github.com/amberframework/granite/blob/master/src/granite/fields.cr#L32"&gt;later stage&lt;/a&gt;, once the macro &lt;code&gt;inherited&lt;/code&gt; and &lt;code&gt;finished&lt;/code&gt; hooks run, the constant could be used to create properties for those fields, applying the proper types/options to the property definition.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Begin Personal Opinion&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;However, in my opinion, this is a bit convoluted and is a good example of what type of situation annotations could be useful.  The main point being, what advantage does using &lt;code&gt;field name : String&lt;/code&gt; get you over &lt;code&gt;property name : String&lt;/code&gt; then applying an annotation?  The latter allows you to just have syntax like every other class in your app, inheritance works, can document properties, and third party annotations could be easily applied (like JSON::Field).  Annotations would allow you to remove these extra unnecessary steps of going from macro to constant to property instead of straight to property.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;End Personal Opinion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;However when taking an annotation based approach, you have to look at it from a different point of view.  In the case of an ORM, the main challenge becomes "how can i pass column data around, without adding everything to a mutable constant?"  My solution to this is using &lt;code&gt;records&lt;/code&gt; and building out, at compile time, an immutable array of objects that represents the columns in a model.  These objects would contain data like the name of the column, the type, if its the PK, if its nullable etc.  The type, name, nullable can all come directly from an instance variables &lt;code&gt;TypeNode&lt;/code&gt; without touching annotations.  Annotations come into play when you want to attach non-autogeneratable data to a column, aka property.  Annotations would allow fields/values to be defined on it, that can also be read at compile time when building out our array of records.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;    &lt;span class="kp"&gt;private&lt;/span&gt; &lt;span class="kp"&gt;record&lt;/span&gt; &lt;span class="no"&gt;ColumnInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ColumnBase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nilable&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auto&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;primary&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="nf"&gt;ault&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;T&lt;/span&gt;

    &lt;span class="kp"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;columns&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ColumnBase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="no"&gt;ColumnBase&lt;/span&gt;
      &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
        &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_vars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ivar&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ivar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Granite&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
        &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"Composite primary keys are not yet supported."&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ivar&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ann&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ivar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Granite&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;ann&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;ann&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:primary&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
        &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
          &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;union?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;union_types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
          &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="n"&gt;col_ann&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Granite&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
          &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="n"&gt;auto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;col_ann&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;col_ann&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:auto&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;col_ann&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
          &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="n"&gt;primary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;col_ann&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;col_ann&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:primary&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;col_ann&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
          &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="n"&gt;auto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;col_ann&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:auto&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;primary&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
          &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"Primary key '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' of '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' must be nilable."&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;primary&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nilable?&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
          &lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ColumnInfo&lt;/span&gt;&lt;span class="p"&gt;({{&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}}).&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;({{&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nilable?&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_value&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;
        &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
      &lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;
      &lt;span class="n"&gt;columns&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is what I came up with.  Similar to building out routes, I iterate over the instance variables of the current class, find those with a &lt;code&gt;Granite::Column&lt;/code&gt; annotation, then check various annotation fields to see if they are set, otherwise use defaults.  All the while generating compile time errors if a user adds two primary keys, or has a non nilable PK.&lt;/p&gt;

&lt;p&gt;An example of what the model could look like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;  &lt;span class="nd"&gt;@[Granite::Model(table: "parents", adapter: "my_db")]&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Parent&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Granite&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
    &lt;span class="nd"&gt;@[Granite::Column(primary: true)]&lt;/span&gt;
    &lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;

    &lt;span class="nd"&gt;@[Granite::Column]&lt;/span&gt;
    &lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;

    &lt;span class="nd"&gt;@[Granite::Column(name: "createdAt")]&lt;/span&gt;
    &lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;

    &lt;span class="nd"&gt;@[Granite::Column(name: "updatedAt")]&lt;/span&gt;
    &lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;

    &lt;span class="c1"&gt;# A property that would be read from a request body but _not_ persisted to DB.&lt;/span&gt;
    &lt;span class="kp"&gt;property&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IMHO, much cleaner and not dependent on shard specific macro syntax.&lt;/p&gt;

&lt;p&gt;Hope this helps as well.&lt;/p&gt;

</description>
      <category>crystal</category>
    </item>
    <item>
      <title>Creating a JSON API with Athena &amp; Granite</title>
      <dc:creator>Blacksmoke16</dc:creator>
      <pubDate>Wed, 27 Feb 2019 02:04:13 +0000</pubDate>
      <link>https://dev.to/blacksmoke16/creating-a-json-api-with-athena--granite-510i</link>
      <guid>https://dev.to/blacksmoke16/creating-a-json-api-with-athena--granite-510i</guid>
      <description>&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; April 22, 2019 for Athena version &lt;code&gt;0.6.0&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;UPDATE:&lt;/strong&gt; November 24, 2019 for Athena version &lt;code&gt;0.7.0&lt;/code&gt; and Crystal &lt;code&gt;0.31.1&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;UPDATE:&lt;/strong&gt; February 7, 2020 for Athena version &lt;code&gt;0.8.0&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;UPDATE:&lt;/strong&gt; June 10, 2020 for Athena version &lt;code&gt;0.9.0&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;UPDATE:&lt;/strong&gt; July 11, 2020 for Athena version &lt;code&gt;0.10.0&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Athena
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;A &lt;del&gt;few months ago&lt;/del&gt; (over a year ago at this point) I set out to create a new web framework, but with a few key differences.  I wanted something that would allow for a route's action to be easily documented, tested, and flexible.  I also didn't want to have to deal with the boilerplate of converting route/query/body params into their expected types manually all the time in every route.  Finally, I wanted to take advantage of Crystal's annotations to provide a simple yet flexible DSL for defining routes.&lt;/p&gt;

&lt;p&gt;Taking the all of the frameworks I have experienced into consideration, with big help from &lt;a href="https://github.com/symfony"&gt;Symfony&lt;/a&gt;. The outcome of this idea was &lt;a href="https://github.com/athena-framework/athena"&gt;Athena&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, I wanted to write a blog post showing how a JSON API with Athena would look like in an actual app, outside of general documentation.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tutorial
&lt;/h2&gt;

&lt;p&gt;This tutorial will &lt;em&gt;not&lt;/em&gt; cover any front end work (UI/UX).  It will just assume that the requests coming to the API are coming from a frontend JS framework or something.  Requests are JSON, so it is pretty framework agnostic. &lt;/p&gt;

&lt;p&gt;I'll be taking a pretty slow approach, as to make this tutorial applicable to both new crystalers as well as veterans.&lt;/p&gt;
&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Crystal installed on your machine.  (Latest version as of writing is &lt;code&gt;0.35.1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Your HTTP client of preference.  I'll just be using &lt;code&gt;cURL&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Your IDE/editor of preference.&lt;/li&gt;
&lt;li&gt;Your DB of preference.  I will be using Postgres.

&lt;ul&gt;
&lt;li&gt;(Optional) Docker.  Is what I will be running my DB with.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Agenda
&lt;/h3&gt;

&lt;p&gt;Add the ability to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Register/Login

&lt;ul&gt;
&lt;li&gt;Validate users&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Create/Read/Update/Delete articles

&lt;ul&gt;
&lt;li&gt;Validate articles&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Scaffolding the blog
&lt;/h3&gt;

&lt;p&gt;We can utilize the crystal binary to scaffold out our application.  This will create a new directory with the given name, with the required files for a crystal app; including the basic directory structure, a &lt;code&gt;shard.yml&lt;/code&gt;, and a &lt;code&gt;.gitignore&lt;/code&gt;, all auto generated for us.  I will go ahead and create this in my home directory, then &lt;code&gt;cd&lt;/code&gt; into the newly created directory; ready for the next steps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/
&lt;span class="nv"&gt;$ &lt;/span&gt;crystal init app blog
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ./blog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dependencies
&lt;/h3&gt;

&lt;p&gt;I will be using &lt;a href="https://github.com/amberframework/granite"&gt;Granite&lt;/a&gt; as our ORM of choice to pair with Athena.  Start off by adding the following to your &lt;code&gt;shard.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;I will also be requiring the &lt;code&gt;jwt&lt;/code&gt; shard to generate JWTs to use as our authentication method of choice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; I am using Postgres, and as such am installing the PG shard for use with Granite.  If you are using another DB adapter, you will need to install that shard instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;granite&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;github&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amberframework/granite&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.21.1&lt;/span&gt;
  &lt;span class="na"&gt;pg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;github&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;will/crystal-pg&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.21.1&lt;/span&gt;
  &lt;span class="na"&gt;athena&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;github&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;athena-framework/athena&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.10.0&lt;/span&gt;
  &lt;span class="na"&gt;jwt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;github&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;crystal-community/jwt&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.4.2&lt;/span&gt;
  &lt;span class="na"&gt;assert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;github&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;blacksmoke16/assert&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.2.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then install the required dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;shards &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Defining Our Models &amp;amp; Controllers
&lt;/h3&gt;

&lt;p&gt;Our blog will have two models, and two database tables:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User - Stores users that have registered with our blog&lt;/li&gt;
&lt;li&gt;Article - A blog post authored by a user.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the purpose of this tutorial, I will just be executing the raw SQL in Postgres to create the tables.  There are some migration shards out there that can automate this; could be a future iteration.&lt;/p&gt;

&lt;p&gt;First lets create a new schema to hold our tables, as well as give our DB user access to that database.&lt;/p&gt;

&lt;p&gt;I will be running my PG database using docker, with the following compose file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.1'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:11.2-alpine&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pg&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5432:5432"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_USER=blog_user&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=mYAw3s0meB!log&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_DB=blog&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pg-data:/var/lib/postgresql/data&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pg-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="nv"&gt;"blog"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="nv"&gt;"blog_user"&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;SEARCH_PATH&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="nv"&gt;"blog"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Using Docker is optional, as long as you have a DB to connect to you'll be fine.&lt;/p&gt;

&lt;p&gt;Now that our dependencies are installed, we need to require them, as well as setup our DB connection in our &lt;code&gt;blog.cr&lt;/code&gt; file.  It should look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Register an adapter to connect to our DB&lt;/span&gt;
&lt;span class="no"&gt;Granite&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Connections&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Granite&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Adapter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"my_blog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="s2"&gt;"postgres://blog_user:mYAw3s0meB!log@localhost:5432/blog?currentSchema=blog"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Require some standard library things we'll need&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"crypto/bcrypt/password"&lt;/span&gt;

&lt;span class="c1"&gt;# Require our ORM and DB adapter&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"granite"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"granite/adapter/pg"&lt;/span&gt;

&lt;span class="c1"&gt;# Require Athena&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"athena"&lt;/span&gt;

&lt;span class="c1"&gt;# This will eventually be replaced by Athena's validation component&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"assert"&lt;/span&gt;

&lt;span class="c1"&gt;# Require JWT shard&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"jwt"&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Blog&lt;/span&gt;
  &lt;span class="no"&gt;VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.10.0"&lt;/span&gt;

  &lt;span class="c1"&gt;# Runs the HTTP server with the default settings&lt;/span&gt;
  &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  User Model
&lt;/h4&gt;

&lt;p&gt;Next lets think about what columns would be required for our user:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;id : Int64 - auto-generated ID to uniquely identity each user&lt;/li&gt;
&lt;li&gt;first_name : String - first name of the user&lt;/li&gt;
&lt;li&gt;last_name : String - last name of the user&lt;/li&gt;
&lt;li&gt;email : String - email of the user, also used for login.  Should be unique for each user&lt;/li&gt;
&lt;li&gt;password : String - the user's password&lt;/li&gt;
&lt;li&gt;created_at : Time - when the user was created&lt;/li&gt;
&lt;li&gt;updated_at : Time - when the user was updated (name/email/password change)&lt;/li&gt;
&lt;li&gt;deleted_at : Time - when the user was deleted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Translating this into a SQL statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="nv"&gt;"blog"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"users"&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;"id"&lt;/span&gt;         &lt;span class="n"&gt;BIGSERIAL&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"first_name"&lt;/span&gt;  &lt;span class="nb"&gt;TEXT&lt;/span&gt;      &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"last_name"&lt;/span&gt;  &lt;span class="nb"&gt;TEXT&lt;/span&gt;      &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"email"&lt;/span&gt;      &lt;span class="nb"&gt;TEXT&lt;/span&gt;      &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"password"&lt;/span&gt;   &lt;span class="nb"&gt;TEXT&lt;/span&gt;      &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"created_at"&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nv"&gt;"updated_at"&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nv"&gt;"deleted_at"&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that our table is created, we can move on to create our first model.  I will start by making a new directory to store our models; as well as creating our &lt;code&gt;user.cr&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ./src/models
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; ./src/models/user.cr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using our list as reference I will create the user model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[ASRA::ExclusionPolicy(:all)]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Granite&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ASR&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Serializable&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Assert&lt;/span&gt;

  &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="s2"&gt;"my_blog"&lt;/span&gt;
  &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt;

  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;primary: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;last_name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;deleted_at&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things to point out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;connection&lt;/code&gt; macro defines which adapter this model should use to connect to the database.  The value passed to the macro is the same as the name set when registering the DB adapter in &lt;code&gt;blog.cr&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;I added a &lt;code&gt;Models&lt;/code&gt; namespace just to add some organization and help separate the docs.  Because of this be sure to add a &lt;code&gt;include Models&lt;/code&gt; within the &lt;code&gt;Blog&lt;/code&gt; module in &lt;code&gt;blog.cr&lt;/code&gt; as well as a &lt;code&gt;require "./models/*"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We'll get back to the &lt;code&gt;include ASR::Serializable&lt;/code&gt; and &lt;code&gt;include Assert&lt;/code&gt; later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's do a little recap.  What have we done so far?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Registered our adapter to connect to our DB.&lt;/li&gt;
&lt;li&gt;Required all the needed shards.&lt;/li&gt;
&lt;li&gt;Created our &lt;code&gt;User&lt;/code&gt; model and table.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that all of these beginning steps are done, we can now create our &lt;code&gt;user_controller&lt;/code&gt; to hold our routes to create a user.&lt;/p&gt;

&lt;h4&gt;
  
  
  User Controller
&lt;/h4&gt;

&lt;p&gt;Similarly as before, I will create a new directory to hold our controllers, as well as create our &lt;code&gt;user_controller.cr&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ./src/controllers
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; ./src/controllers/user_controller.cr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controllers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UserController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm also namespacing the controllers, so be sure to &lt;code&gt;include Controllers&lt;/code&gt;, as well as a &lt;code&gt;require "./controllers/*"&lt;/code&gt; in your &lt;code&gt;blog.cr&lt;/code&gt; file. The first endpoint I will create will be a &lt;code&gt;POST /user&lt;/code&gt; endpoint in order to add users to our database.  To do this, add the following code to the &lt;code&gt;UserController&lt;/code&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[ART::Post("user")]&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Athena's route definitions are a bit different than what you may be used to.  Athena uses Crystal's annotations.  The top annotation defines a &lt;code&gt;POST&lt;/code&gt; endpoint with the path &lt;code&gt;/user&lt;/code&gt; and sets the route's action to the &lt;code&gt;new_user&lt;/code&gt; method.  However, Athena is not able to automatically provide complex types, such as our &lt;code&gt;User&lt;/code&gt; object to our action; we must make use of a &lt;code&gt;ParamConverter&lt;/code&gt; to accomplish this.  A &lt;code&gt;ParamConverter&lt;/code&gt; allows defining custom logic responsible for converting data within a request into another type for the action to use.  In this example, convert the request body into an instance of our &lt;code&gt;User&lt;/code&gt; model; lets go ahead and create that now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Define our converter, register it as a service, inheriting from the base interface struct.&lt;/span&gt;
&lt;span class="nd"&gt;@[ADI::Register]&lt;/span&gt;
&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Converters&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RequestBody&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ParamConverterInterface&lt;/span&gt;
  &lt;span class="c1"&gt;# Define a customer configuration for this converter.&lt;/span&gt;
  &lt;span class="c1"&gt;# This allows us to provide a `model` field within the annotation&lt;/span&gt;
  &lt;span class="c1"&gt;# in order to define _what_ model should be used on deserialization.&lt;/span&gt;
  &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Granite&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;

  &lt;span class="c1"&gt;# :inherit:&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
    &lt;span class="c1"&gt;# Be sure to handle any possible exceptions here to return more helpful errors to the client.&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BadRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s2"&gt;"Request body is empty"&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;try&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gets_to_end&lt;/span&gt;

    &lt;span class="c1"&gt;# Deserialize the object, based on the type provided in the annotation&lt;/span&gt;
    &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_json&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;

    &lt;span class="c1"&gt;# Run the validations&lt;/span&gt;
    &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate!&lt;/span&gt;

    &lt;span class="c1"&gt;# Add the resolved object to the request's attributes&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ValidationError&lt;/span&gt;
    &lt;span class="c1"&gt;# Raise a 422 error if the object failed its validations&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UnprocessableEntity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Athena uses a lot of interfaces in order to make types more DI friendly, easier to test, etc.  The interface only requires a single method &lt;code&gt;apply(request : HTTP::Request, configuration : Configuration) : Nil&lt;/code&gt; whose sole purpose is to apply the conversion logic to the provided &lt;em&gt;request&lt;/em&gt;, based on the provided &lt;em&gt;configuration&lt;/em&gt;.  A converter is simply a struct that inherits from &lt;code&gt;ART::ParamConverterInterface&lt;/code&gt;.  We'll cover the &lt;code&gt;@[ADI::Register]&lt;/code&gt; annotation a bit later.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;RequestBody&lt;/code&gt; converter also makes use of Athena's error handling system.  Athena provides a set of common HTTP exceptions inheriting from &lt;code&gt;ART::Exceptions::HTTPException&lt;/code&gt;, children of this type are assumed to map to an &lt;code&gt;HTTP&lt;/code&gt; error; custom children can also be added.  Non &lt;code&gt;HTTPException&lt;/code&gt;s return a 500 unless rescued as you would normally.  By default the exceptions are JSON serialized, but can be customized if so desired.&lt;/p&gt;

&lt;p&gt;Most commonly, param converters will want to store the converted values within the request's attributes.  The attributes are held within an &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/ParameterBag.html"&gt;ART::ParameterBag&lt;/a&gt; instance.  The &lt;code&gt;ParameterBag&lt;/code&gt; is a container for storing key/value pairs; which can be used to store arbitrary data within the context of a request.  By default, Athena will look in the request's attributes for a value with the same name as an action argument; this include path/query params, or any custom values stored in it. &lt;/p&gt;

&lt;p&gt;Now that we have our converter defined we can go ahead to implement it on our &lt;code&gt;new_user&lt;/code&gt; route.  Simply apply the annotation, the first argument maps to the name of the action argument the converter should be applied against, while the &lt;code&gt;converter&lt;/code&gt; named argument accepts the specific &lt;code&gt;ParamConverter.class&lt;/code&gt; we want to use.  Any extra configuration for this converter can also be defined.  In this case we are specifying that we want to deserialize the request body into a &lt;code&gt;User&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;Since the param converter supplies an actual &lt;code&gt;User&lt;/code&gt; model object, we can just call &lt;code&gt;.save&lt;/code&gt; in our action to save the given object, then just return the user object.  We can also use the &lt;code&gt;ART::View&lt;/code&gt; annotation to make our action a bit more REST friendly by having the action return a &lt;code&gt;201 Created&lt;/code&gt; status code instead of the standard &lt;code&gt;200 OK&lt;/code&gt;.  Our &lt;code&gt;new_user&lt;/code&gt; action now looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[ART::Post("user")]&lt;/span&gt;
&lt;span class="nd"&gt;@[ART::View(status: :created)]&lt;/span&gt;
&lt;span class="nd"&gt;@[ART::ParamConverter("user", converter: Blog::Converters::RequestBody, model: Blog::Models::User)]&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point we are now able to create users via our &lt;code&gt;POST /user&lt;/code&gt; endpoint.  Lets give it a try.&lt;/p&gt;

&lt;p&gt;Start the HTTP server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;crystal ./src/blog.cr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets register a user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:3000/user &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
  "first_name": "foo",
  "last_name": "bar",
  "email": "fakeemail@domain.com",
  "password": "monkey123"
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Success!  The user was persisted and now has an id and timestamps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"first_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"last_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fakeemail@domain.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"monkey123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-07-11T22:42:33Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-07-11T22:42:33Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However there are a few problems with the current implementation.  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;What would stop someone from setting their password/name/email as an empty string?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sure we could rely upon the front end for the validation, but that is easy to bypass.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We probably shouldn't be displaying the user's password in cleartext, let alone return it in the response.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What happens if someone were to POST twice with the same email?  Since we are using the email as our user facing unique identifier, we should handle this.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Lets go back to our &lt;code&gt;User&lt;/code&gt; model to address some of these issues.  This is where &lt;code&gt;ASR::Serializable&lt;/code&gt; and &lt;code&gt;Assert&lt;/code&gt; comes into use. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; &lt;code&gt;Assert&lt;/code&gt; will eventually be moved into the &lt;code&gt;athena-framework&lt;/code&gt; organization as an independent component for validation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can update our model to look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[ASRA::ExclusionPolicy(:all)]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Granite&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ASR&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Serializable&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Assert&lt;/span&gt;

  &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="s2"&gt;"my_blog"&lt;/span&gt;
  &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt;

  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;

  &lt;span class="nd"&gt;@[ASRA::Expose]&lt;/span&gt;
  &lt;span class="nd"&gt;@[ASRA::ReadOnly]&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;primary: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;

  &lt;span class="nd"&gt;@[ASRA::Expose]&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::NotBlank]&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;

  &lt;span class="nd"&gt;@[ASRA::Expose]&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::NotBlank]&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;last_name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;

  &lt;span class="nd"&gt;@[ASRA::Expose]&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::NotBlank]&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::Email(mode: :html5)]&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;

  &lt;span class="nd"&gt;@[ASRA::IgnoreOnSerialize]&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::Size(Range(Int32, Int32), range: 8..25, min_message: "Your password is too short", max_message: "Your password is too long")]&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;

  &lt;span class="nd"&gt;@[ASRA::Expose]&lt;/span&gt;
  &lt;span class="nd"&gt;@[ASRA::ReadOnly]&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;

  &lt;span class="nd"&gt;@[ASRA::Expose]&lt;/span&gt;
  &lt;span class="nd"&gt;@[ASRA::ReadOnly]&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;

  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;deleted_at&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also update your &lt;code&gt;new_user&lt;/code&gt; action to be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[ART::Post("user")]&lt;/span&gt;
&lt;span class="nd"&gt;@[ART::ParamConverter("user", converter: Blog::Converters::RequestBody, model: Blog::Models::User)]&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Conflict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s2"&gt;"A user with this email already exists."&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists?&lt;/span&gt; &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these changes we have addressed issues 1 and 3 that we identified earlier.  We will solve issue 2 shortly after an explanation of what is going on.&lt;/p&gt;

&lt;p&gt;Firstly we included &lt;code&gt;ASR::Serializable&lt;/code&gt; and &lt;code&gt;Assert&lt;/code&gt; in order to add enhanced serialization and assertion functionality.  Next, we added an annotation to the class of our &lt;code&gt;User&lt;/code&gt; model.  &lt;code&gt;@[ASRA::ExclusionPolicy(:all)]&lt;/code&gt;.  This annotation alters the overall serialization strategy for the model.  In this case, it will &lt;em&gt;only&lt;/em&gt; serialize fields that are exposed via &lt;code&gt;@[ASRA::Expose]&lt;/code&gt;.  This is handy, especially for larger models, to make it easier to only serialize the expected fields, as well as prevent the serialization of other instance variables included via other modules for example.   &lt;/p&gt;

&lt;p&gt;I also added the &lt;code&gt;@[ASRA::IgnoreOnSerialize]&lt;/code&gt; annotation to the &lt;code&gt;password&lt;/code&gt; property.  This tells Athena's serializer that the password is allowed to be deserialized, but should &lt;em&gt;NOT&lt;/em&gt; be serialized.&lt;/p&gt;

&lt;p&gt;Next, I have added annotations to expose the fields that we wish to be returned.  I also added a &lt;code&gt;@[ASRA::ReadOnly]&lt;/code&gt; to the &lt;code&gt;id&lt;/code&gt; field and exposed timestamp fields, which prevents that property from being deserialized; since it's managed by the database.&lt;/p&gt;

&lt;p&gt;I also added additional annotations to the fields we wish to validate.  I am asserting that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;first_name&lt;/code&gt; field is not blank&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;last_name&lt;/code&gt; field is not blank&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;email&lt;/code&gt; is not blank AND is a valid email&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;password&lt;/code&gt; is between 8 and 25 characters long&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, I added a &lt;code&gt;User.exists? email: user.email&lt;/code&gt; query in the &lt;code&gt;UserController&lt;/code&gt; to check if a user exists with the given email, and throw a proper error message if one does.&lt;/p&gt;

&lt;p&gt;Lets test it out!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:3000/user &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
  "first_name": "foo",
  "last_name": "",
  "email": "",
  "password": "monkey"
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;produces the following response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;422&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Validation tests failed: 'last_name' should not be blank, 'email' is not a valid email address, 'email' should not be blank, Your password is too short"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tada! Easy validation of your models.  Also, trying to POST a user with an email that was used before now produces this error&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;409&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A user with this email already exists."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Issue 2 can be solved by adding a &lt;code&gt;before_save&lt;/code&gt; callback on our model&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[ASRA::ExclusionPolicy(:all)]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Granite&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;

  &lt;span class="n"&gt;before_save&lt;/span&gt; &lt;span class="ss"&gt;:hash_password&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hash_password&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@password&lt;/span&gt;
      &lt;span class="vi"&gt;@password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Crypto&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Bcrypt&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will execute and hash the password before the model is saved.&lt;/p&gt;

&lt;h4&gt;
  
  
  Auth Controller
&lt;/h4&gt;

&lt;p&gt;At this point we now have a &lt;code&gt;POST /user&lt;/code&gt; endpoint that would be paired with a front end form for user registration.  But in order for the user to be "logged in" we need to do something to tell the front end that there is an active session.  There are a multiple of ways to do this: setting a JWT token in a cookie, generating a session key and storing that in our user table, or returning a JWT token to the front end after receiving a correct username and password for the front end to store in some form of HTML5 storage.  For the purposes of this I am going to go with the latter option, and return a JWT token for the front end to handle.&lt;/p&gt;

&lt;p&gt;To start, I am going to create a new controller file under our &lt;code&gt;./src/controllers&lt;/code&gt; directory to hold our logic for signing in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; ./src/controllers/auth_controller.cr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am also going to take this time to show off some additional features; namely working with the raw &lt;code&gt;HTTP::Request&lt;/code&gt; object, and introduce &lt;code&gt;ART::Response&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controllers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;AuthController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;
  &lt;span class="c1"&gt;# Type hinting an action argument to `HTTP::Request` will supply the current request object.&lt;/span&gt;
  &lt;span class="nd"&gt;@[ART::Post("login")]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Response&lt;/span&gt;
    &lt;span class="c1"&gt;# Raise an exception if there is no request body&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BadRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s2"&gt;"Missing request body."&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;

    &lt;span class="c1"&gt;# Parse the request body into an HTTP::Params object&lt;/span&gt;
    &lt;span class="n"&gt;form_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gets_to_end&lt;/span&gt;

    &lt;span class="c1"&gt;# Handle missing form values&lt;/span&gt;
    &lt;span class="n"&gt;handle_invalid_auth_credentials&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;form_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;]?&lt;/span&gt;
    &lt;span class="n"&gt;handle_invalid_auth_credentials&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;form_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;]?&lt;/span&gt;

    &lt;span class="c1"&gt;# Find a user with the given ID&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt; &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;

    &lt;span class="c1"&gt;# Raise a 401 error if either a user isn't found or the password does not match&lt;/span&gt;
    &lt;span class="n"&gt;handle_invalid_auth_credentials&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Crypto&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Bcrypt&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;password&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# If an `ART::Response` is returned then it is used as is for the response,&lt;/span&gt;
    &lt;span class="c1"&gt;# otherwise, like the other endpoints, the response value is by default JSON serialized&lt;/span&gt;
    &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;token: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_jwt&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;headers: &lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"content-type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_invalid_auth_credentials&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
    &lt;span class="c1"&gt;# Raise a 401 error if values are missing, or are invalid;&lt;/span&gt;
    &lt;span class="c1"&gt;# this also handles setting an appropiate `www-authenticate` header&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Unauthorized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s2"&gt;"Invalid username and/or password."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Basic realm=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;My Blog&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The raw request object can be obtained by type hinting an action argument as &lt;code&gt;HTTP::Request&lt;/code&gt;, Athena will then know to provide the request object when executing the action.  We then validate all the required fields are present, and a user was found with the given credentials; otherwise we return a 401 error for the front end to handle.&lt;/p&gt;

&lt;p&gt;One thing to note is the return type in this action is &lt;code&gt;ART::Response&lt;/code&gt;.  At a high level, the implementation of Athena is simply attempting to convert an &lt;code&gt;HTTP::Request&lt;/code&gt; into an &lt;code&gt;ART::Response&lt;/code&gt;.  If an action returns an &lt;code&gt;ART::Response&lt;/code&gt; then the request is essentially finished and returned as is, (assuming no listeners alter it further, more on that later).  Otherwise, like our other actions, if the return type is not an &lt;code&gt;ART::Response&lt;/code&gt;, the resulting value goes through the view layer in order to have that value converted into an &lt;code&gt;ART::Response&lt;/code&gt;.  By default this is JSON serializing it, but it can be customized if so desired.&lt;/p&gt;

&lt;p&gt;Next, we will need to implement the &lt;code&gt;generate_jwt&lt;/code&gt; method on our user object, which will look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_jwt&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
  &lt;span class="no"&gt;JWT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="s2"&gt;"user_id"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vi"&gt;@id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"exp"&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utc&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;week&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_unix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"iat"&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_unix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"SECRET"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;:hs512&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method will generate a JWT with a body including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The id of the user&lt;/li&gt;
&lt;li&gt;An expiration date of now + 1 week&lt;/li&gt;
&lt;li&gt;The time the JWT was created&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I generated a secure string and exported it as an env variable to act as the secret key to sign the token.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;MY_SECURE_STRING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is just a simple example, and not representative of how to best use JWT tokens.  If this were for real you could include other claims or change the details to best fit your use cases.  &lt;/p&gt;

&lt;p&gt;After restarting the server and sending a request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:3000/login &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'content-type: application/x-www-form-urlencoded'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'email=fakeemail%40domain.com&amp;amp;password=monkey123'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get a JSON object back with your JWT token within it.  Success!  However, if you used invalid credentials you would get a 401 error back.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invalid username and/or password"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since our imaginary front end will be storing this token in local storage on the browser, we don't really have a use for a &lt;code&gt;/logout&lt;/code&gt; endpoint.  However, if you wanted to make one you could have it issue a request &lt;em&gt;before&lt;/em&gt; deleting the token from the front end.  This way you tell your server that a given user logged out if you had other tasks/cleanup to do.&lt;/p&gt;

&lt;h4&gt;
  
  
  Article Model
&lt;/h4&gt;

&lt;p&gt;At this point we are able to register new users, allow users to login, all the while validating and throwing helpful errors.&lt;/p&gt;

&lt;p&gt;The next item on the agenda will be to create our &lt;code&gt;Article&lt;/code&gt; model, table, and controller.  I'll go a bit faster now as it'll be quite similar as before.&lt;/p&gt;

&lt;p&gt;Next lets think about what columns would be required for an article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;id : Int64 - auto-generated ID to uniquely identity each article&lt;/li&gt;
&lt;li&gt;user_id : Int64 - the user that authored the article&lt;/li&gt;
&lt;li&gt;title : String - title of the article&lt;/li&gt;
&lt;li&gt;body : String - the body&lt;/li&gt;
&lt;li&gt;created_at : Time - when the article was created&lt;/li&gt;
&lt;li&gt;updated_at : Time - when the article was updated&lt;/li&gt;
&lt;li&gt;deleted_at : Time - when the article was deleted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Translating this into a SQL statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="nv"&gt;"blog"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"articles"&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;"id"&lt;/span&gt;         &lt;span class="n"&gt;BIGSERIAL&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"user_id"&lt;/span&gt;    &lt;span class="nb"&gt;BIGINT&lt;/span&gt;    &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="nv"&gt;"blog"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"title"&lt;/span&gt;      &lt;span class="nb"&gt;TEXT&lt;/span&gt;      &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"body"&lt;/span&gt;       &lt;span class="nb"&gt;TEXT&lt;/span&gt;      &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"created_at"&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nv"&gt;"updated_at"&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nv"&gt;"deleted_at"&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that our table is created, we can move on to create our second model.  I will first create the &lt;code&gt;article.cr&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; ./src/models/article.cr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[ASRA::ExclusionPolicy(:all)]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Granite&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ASR&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Serializable&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Assert&lt;/span&gt;

  &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="n"&gt;my_blog&lt;/span&gt;
  &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="s2"&gt;"articles"&lt;/span&gt;

  &lt;span class="nd"&gt;@[ASRA::Expose]&lt;/span&gt;
  &lt;span class="nd"&gt;@[ASRA::ReadOnly]&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;

  &lt;span class="nd"&gt;@[ASRA::Expose]&lt;/span&gt;
  &lt;span class="nd"&gt;@[ASRA::ReadOnly]&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;primary: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;

  &lt;span class="nd"&gt;@[ASRA::Expose]&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::NotBlank]&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::NotNil]&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;

  &lt;span class="nd"&gt;@[ASRA::Expose]&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::NotBlank]&lt;/span&gt;
  &lt;span class="nd"&gt;@[Assert::NotNil]&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;

  &lt;span class="nd"&gt;@[ASRA::Expose]&lt;/span&gt;
  &lt;span class="nd"&gt;@[ASRA::ReadOnly]&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;

  &lt;span class="nd"&gt;@[ASRA::Expose]&lt;/span&gt;
  &lt;span class="nd"&gt;@[ASRA::ReadOnly]&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;

  &lt;span class="nd"&gt;@[ASRA::ReadOnly]&lt;/span&gt;
  &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;deleted_at&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This model is very similar to our &lt;code&gt;User&lt;/code&gt; model with the main difference being the &lt;code&gt;belongs_to user : User&lt;/code&gt; macro.  This macro expands and creates the &lt;code&gt;user_id&lt;/code&gt; field.  It also creates a getter and setter to retrieve and set the related user object.  In this case, the person who authored the article.&lt;/p&gt;

&lt;p&gt;We'll also want to go back to the &lt;code&gt;User&lt;/code&gt; model and add &lt;code&gt;has_many articles : Article&lt;/code&gt; to it, below the table definition.  This defines a method that would return an array of that user's articles.  E.x. &lt;code&gt;articles = user.articles&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next up, the &lt;code&gt;ArticleController&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Article Controller
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; ./src/controllers/article_controller.cr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controllers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ArticleController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;
  &lt;span class="nd"&gt;@[ART::Post("article")]&lt;/span&gt;
  &lt;span class="nd"&gt;@[ART::View(status: :created)]&lt;/span&gt;
  &lt;span class="nd"&gt;@[ART::ParamConverter("article", converter: Blog::Converters::RequestBody, model: Blog::Models::Article)]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;
    &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
    &lt;span class="n"&gt;article&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, this looks nearly the same as the &lt;code&gt;new_user&lt;/code&gt; action in our &lt;code&gt;UserController&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Making a request to create an article with a &lt;code&gt;user_id&lt;/code&gt; of the id of your user, and the token retrieved earlier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:3000/article &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'authorization: Bearer TOKEN'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
    "user_id": 1,
    "title": "My Athena Blog",
    "body": "Athena makes developing JSON APIs easy!"
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Successfully creates the article:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My Athena Blog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Athena makes developing JSON APIs easy!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-07-11T22:57:56Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-07-11T22:57:56Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great!  We can now create articles.  However, do you see a problem with this implementation?  There is no validation around the &lt;code&gt;user_id&lt;/code&gt;, nor is there any authorization to prevent random people from creating articles for any user they want.  Lets work on adding some authorization checks into our request flow, utilizing the generated JWT token we got a little while ago when we "logged in".  &lt;/p&gt;

&lt;h3&gt;
  
  
  Authorization
&lt;/h3&gt;

&lt;p&gt;One of the core points of JWT is that once verified, using our secret key and checking the claims in the body, it can be assured that it is a valid token and that we should process the request.  Also, since this is a REST API, we'll need to enable CORS to allow our front end to actually &lt;em&gt;make&lt;/em&gt; requests to it.  We can accomplish the latter by enabling Athena's CORS listener.&lt;/p&gt;

&lt;p&gt;We just need to simply define some configuration on how we want the listener to operate, see &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Config/CORS.html"&gt;ART::Config::CORS&lt;/a&gt; for additional configuration information.  Create a file in the root of your application named &lt;code&gt;athena.yml&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;routing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;cors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;allow_credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;allow_origin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;https://api.myblog.com&lt;/span&gt;
    &lt;span class="na"&gt;allow_methods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POST&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PUT&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DELETE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Athena uses &lt;a href="https://github.com/athena-framework/event-dispatcher"&gt;Athena::EventDispatcher&lt;/a&gt; to handle tapping into the request/response life-cycle, as opposed to the more standard &lt;code&gt;HTTP::Handler&lt;/code&gt; approach.&lt;/p&gt;

&lt;p&gt;When processing a request, Athena emits various &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Events.html"&gt;events&lt;/a&gt; that can be listened on to handle the request early, like the &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Listeners/CORS.html"&gt;CORS&lt;/a&gt; listener, or for adding additional information to the response, like headers/cookies etc.  A good example of this would be to tap into when an unhandled exception occurs for logging purposes.&lt;/p&gt;

&lt;p&gt;For our goal of authenticating a user, we will create a listener on the &lt;a href="https://athena-framework.github.io/athena/Athena/Routing/Events/Request.html"&gt;Request&lt;/a&gt; event.  This will be used to validate there is a token present and that it is valid.  Lets get started.&lt;/p&gt;

&lt;p&gt;First, lets make a new directory to store our handler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ./src/listeners
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; ./src/listeners/security_listener.cr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Listeners&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SecurityListener&lt;/span&gt;
  &lt;span class="c1"&gt;# Define the interface to implement the required methods&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;AED&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EventListenerInterface&lt;/span&gt;

  &lt;span class="c1"&gt;# Specify that we want to listen on the `Request` event.&lt;/span&gt;
  &lt;span class="c1"&gt;# The value of the has represents this listener's priority;&lt;/span&gt;
  &lt;span class="c1"&gt;# the higher the value the sooner it gets executed.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribed_events&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;AED&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SubscribedEvents&lt;/span&gt;
    &lt;span class="no"&gt;AED&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SubscribedEvents&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Request&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Define a `#call` method scoped to the `Request` event.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_dispatcher&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;AED&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EventDispatcherInterface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
    &lt;span class="c1"&gt;# Allow POST user and POST login through since they are public&lt;/span&gt;
    &lt;span class="c1"&gt;# In the future Athena will most likely have a more structured way to handle auth&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"POST"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"/user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/login"&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;includes?&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# Return a 401 error if the token is missing or malformed&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Unauthorized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s2"&gt;"Missing bearer token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Bearer realm=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;My Blog&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth_header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;try&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;auth_header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;starts_with?&lt;/span&gt; &lt;span class="s2"&gt;"Bearer "&lt;/span&gt;

    &lt;span class="c1"&gt;# Get the JWT token from the Bearer header&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auth_header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lchop&lt;/span&gt; &lt;span class="s2"&gt;"Bearer "&lt;/span&gt;

    &lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="c1"&gt;# Validate the token&lt;/span&gt;
      &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JWT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"SECRET"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;:hs512&lt;/span&gt;
    &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="n"&gt;decode_error&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;JWT&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DecodeError&lt;/span&gt;
      &lt;span class="c1"&gt;# Throw a 401 error if the JWT token is invalid&lt;/span&gt;
      &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Unauthorized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s2"&gt;"Invalid token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Bearer realm=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;My Blog&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that our listener is defined, we're faced with some new problems.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do we tell Athena to use it?&lt;/li&gt;
&lt;li&gt;How can we make the rest of our application aware of the currently authenticated user? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both of these problems are solved via another feature of Athena, dependency injection (DI).  Athena uses &lt;a href="https://github.com/athena-framework/dependency-injection"&gt;Athena::DependencyInjection&lt;/a&gt; to make sharing useful objects easy.  While I'm going to cover the main points of DI in this article, see &lt;a href="https://dev.to/blacksmoke16/dependency-injection-in-crystal-2d66"&gt;Dependency Injection in Crystal&lt;/a&gt;, in addition to the API docs within the shard, for a more detailed example of it in action.&lt;/p&gt;

&lt;p&gt;A service container contains instances of various useful object, aka services.  These services can then be supplied to other services without having to manually instantiate everything.  It also allows for types to be tested more easily since they can depend on abstractions (interfaces) versus concrete types.  In our case it'll allow us to define a service that will store the currently authenticated user in order to have access to it in the rest of the application.&lt;/p&gt;

&lt;p&gt;First let's define a type to store the user, aka &lt;code&gt;UserStorage&lt;/code&gt;.  We'll make a new directory to store our services that don't fit better anywhere else.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ./src/services
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; ./src/services/user_storeage.cr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# The ADI::Register annotation tells the DI component how this service should be registered&lt;/span&gt;
&lt;span class="nd"&gt;@[ADI::Register]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UserStorage&lt;/span&gt;
  &lt;span class="c1"&gt;# Use a ! property since they'll always be a user defined in our use case.&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;# It also provides a `user?` getter in cases where it might not be.&lt;/span&gt;
  &lt;span class="kp"&gt;property&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we defined this as a &lt;code&gt;class&lt;/code&gt;, it makes it so the same instance is injected into each type, i.e. the user initially set will remain set until the request is finished.  A &lt;code&gt;struct&lt;/code&gt; on the other hand would cause a copy of the service to be injected.&lt;/p&gt;

&lt;p&gt;Be sure to require our new directory in &lt;code&gt;src/blog.cr&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now lets update our security listener, I omitted the lines that didn't change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Define and register a listener to handle authenticating requests.&lt;/span&gt;
&lt;span class="nd"&gt;@[ADI::Register]&lt;/span&gt;
&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Listeners&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SecurityListener&lt;/span&gt;
  &lt;span class="c1"&gt;# Define the interface to implement the required methods&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;AED&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EventListenerInterface&lt;/span&gt;

  &lt;span class="c1"&gt;# Define our initializer for DI to inject the user storage.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@user_storage&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;UserStorage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Define a `#call` method scoped to the `Request` event.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_dispatcher&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;AED&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EventDispatcherInterface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="c1"&gt;# Set the user in user storage&lt;/span&gt;
    &lt;span class="vi"&gt;@user_storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find!&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By simply annotating the type with &lt;code&gt;@[ADI::Register]&lt;/code&gt;, Athena handles "wiring" everything up for us.  Our required &lt;code&gt;UserStorage&lt;/code&gt; dependency is automatically resolved and injected based on type restriction.  The listener is also registered automatically since it implements &lt;code&gt;AED::EventListenerInterface&lt;/code&gt;, via &lt;a href="https://athena-framework.github.io/dependency-injection/Athena/DependencyInjection.html#auto_configure(type,options)-macro"&gt;ADI.auto_configure&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now that we are authenticating the requests, we can move onto adding the ability to read/update/delete our articles.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;ArticleController&lt;/code&gt; now looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# The `ART::Prefix` annotation will add the given prefix to each route in the controller.&lt;/span&gt;
&lt;span class="c1"&gt;# We also register the controller itself as a service in order to allow injecting our `UserStorage` object.&lt;/span&gt;
&lt;span class="c1"&gt;# NOTE: The controller service must be declared as public.  In the future this will happen behind the scenes&lt;/span&gt;
&lt;span class="c1"&gt;# but for now it cannot be done automatically.&lt;/span&gt;
&lt;span class="nd"&gt;@[ART::Prefix("article")]&lt;/span&gt;
&lt;span class="nd"&gt;@[ADI::Register(public: true)]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controllers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ArticleController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;
  &lt;span class="c1"&gt;# Define our initializer for DI&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@user_storage&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UserStorage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nd"&gt;@[ART::Post(path: "")]&lt;/span&gt;
  &lt;span class="nd"&gt;@[ART::View(status: :created)]&lt;/span&gt;
  &lt;span class="nd"&gt;@[ART::ParamConverter("article", converter: Blog::Converters::RequestBody, model: Blog::Models::Article)]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;
    &lt;span class="c1"&gt;# Set the owner of the article to the currently authenticated user&lt;/span&gt;
    &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@user_storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;
    &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
    &lt;span class="n"&gt;article&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nd"&gt;@[ART::Get(path: "")]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_articles&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# We are also using the user in UserStorage as an additional conditional in our query when fetching articles&lt;/span&gt;
    &lt;span class="c1"&gt;# this allows us to only returns articles that belong to the current user.&lt;/span&gt;
    &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:deleted_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:neq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:eq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@user_storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nd"&gt;@[ART::Put(path: "")]&lt;/span&gt;
  &lt;span class="nd"&gt;@[ART::ParamConverter("article", converter: Blog::Converters::RequestBody, model: Blog::Models::Article)]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;
    &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
    &lt;span class="n"&gt;article&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nd"&gt;@[ART::Get("/:id")]&lt;/span&gt;
  &lt;span class="nd"&gt;@[ART::ParamConverter("article", converter: Blog::Converters::DB, model: Blog::Models::Article)]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;
    &lt;span class="n"&gt;article&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nd"&gt;@[ART::Delete("/:id")]&lt;/span&gt;
  &lt;span class="nd"&gt;@[ART::ParamConverter("article", converter: Blog::Converters::DB, model: Blog::Models::Article)]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete_article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
    &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleted_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utc&lt;/span&gt;
    &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These additional methods will allow for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Listing all the current user's articles&lt;/li&gt;
&lt;li&gt;Updating an article&lt;/li&gt;
&lt;li&gt;Getting a specific article&lt;/li&gt;
&lt;li&gt;Deleting a specific article&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The latter two are making use of a new converter; &lt;code&gt;DB&lt;/code&gt;.  This will do a DB query to find a record of the provided type with the provided &lt;code&gt;id&lt;/code&gt;, otherwise returns a &lt;code&gt;404&lt;/code&gt; error.  The code for that is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nd"&gt;@[ADI::Register]&lt;/span&gt;
&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Converters&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DB&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ParamConverterInterface&lt;/span&gt;
  &lt;span class="c1"&gt;# Define a customer configuration for this converter.&lt;/span&gt;
  &lt;span class="c1"&gt;# This allows us to provide a `model` field within the annotation&lt;/span&gt;
  &lt;span class="c1"&gt;# in order to define _what_ model should be queried for.&lt;/span&gt;
  &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Granite&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;

  &lt;span class="c1"&gt;# :inherit:&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;# Be sure to handle any possible exceptions here to return more helpful errors to the client.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
    &lt;span class="c1"&gt;# Grab the `id` path parameter from the request's attributes&lt;/span&gt;
    &lt;span class="n"&gt;primary_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;

    &lt;span class="c1"&gt;# Raise a 404 if a record with the provided ID does not exist&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s2"&gt;"An item with the provided ID could not be found"&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt; &lt;span class="n"&gt;primary_key&lt;/span&gt;

    &lt;span class="c1"&gt;# Set the resolved model within the request's attributes&lt;/span&gt;
    &lt;span class="c1"&gt;# with a key matching the name of the argument within the converter annotation&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Updating DB models can be a bit tricky in some cases due to how Crystal handles deserialization.  In other frameworks it would be required to include &lt;em&gt;ALL&lt;/em&gt; the properties of the model in the &lt;code&gt;PUT&lt;/code&gt; body, even those managed by the database that should not be editable, such as the &lt;code&gt;id&lt;/code&gt; or timestamps.  Athena's serializer includes the concept of &lt;a href="https://athena-framework.github.io/serializer/Athena/Serializer/ObjectConstructorInterface.html"&gt;Object Constructors&lt;/a&gt;; which determine how a new object is constructed during deserialization.  In our case, we could define a custom constructor that would source the object from the database, making it so we don't need to include the timestamps, or other non-editable properties within our &lt;code&gt;PUT&lt;/code&gt; request.&lt;/p&gt;

&lt;p&gt;Within &lt;code&gt;request_body_converter.cr&lt;/code&gt; add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Define a custom `ASR::ObjectConstructorInterface` to allow sourcing the model from the database&lt;/span&gt;
&lt;span class="c1"&gt;# as part of `PUT` requests, and if the type is a `Granite::Base`.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Alias our service to `ASR::ObjectConstructorInterface` so ours gets injected instead.&lt;/span&gt;
&lt;span class="nd"&gt;@[ADI::Register(alias: ASR::ObjectConstructorInterface)]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DBObjectConstructor&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Athena&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Serializer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ObjectConstructorInterface&lt;/span&gt;

  &lt;span class="c1"&gt;# Inject `ART::RequestStore` in order to have access to the current request.&lt;/span&gt;
  &lt;span class="c1"&gt;# Also inject `ASR::InstantiateObjectConstructor` to act as our fallback constructor.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@request_store&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RequestStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@fallback_constructor&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ASR&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;InstantiateObjectConstructor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# :inherit:&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;navigator&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ASR&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Navigators&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DeserializationNavigatorInterface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;properties&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ASR&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PropertyMetadataBase&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ASR&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Fallback on the default object constructor if the type is not a `Granite` model.&lt;/span&gt;
    &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;Granite&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="vi"&gt;@fallback_constructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;construct&lt;/span&gt; &lt;span class="n"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# Fallback on the default object constructor if the current request is not a `PUT`.&lt;/span&gt;
    &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@request_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"PUT"&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="vi"&gt;@fallback_constructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;construct&lt;/span&gt; &lt;span class="n"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# Lookup the object from the database; assume the object has an `id` property.&lt;/span&gt;
    &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;as_i&lt;/span&gt;

    &lt;span class="c1"&gt;# Return a `404` error if no record exists with the given ID.&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s2"&gt;"An item with the provided ID could not be found."&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;

    &lt;span class="c1"&gt;# Apply the updated properties to the retrieved record&lt;/span&gt;
    &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt; &lt;span class="n"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;

    &lt;span class="c1"&gt;# Return the object&lt;/span&gt;
    &lt;span class="n"&gt;object&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Make the compiler happy when we want to allow any Granite model to be deserializable.&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Granite&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ASR&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Model&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This type allows us to source the original object from the database, then apply the updated values to it as opposed to creating a whole new object from the request body.  The DB logic is only applied to &lt;code&gt;Granite::Base&lt;/code&gt; types on &lt;code&gt;PUT&lt;/code&gt; requests, everything else uses the default behavior of creating a new object with based on the data within the request body.&lt;/p&gt;

&lt;p&gt;We can then update our &lt;code&gt;RequestBody&lt;/code&gt; converter to look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Define our converter, register it as a service, inheriting from the base interface struct.&lt;/span&gt;
&lt;span class="nd"&gt;@[ADI::Register]&lt;/span&gt;
&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Converters&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RequestBody&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ParamConverterInterface&lt;/span&gt;
  &lt;span class="c1"&gt;# Define a custom configuration for this converter.&lt;/span&gt;
  &lt;span class="c1"&gt;# This allows us to provide a `model` field within the annotation&lt;/span&gt;
  &lt;span class="c1"&gt;# in order to define _what_ model should be used on deserialization.&lt;/span&gt;
  &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Granite&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;

  &lt;span class="c1"&gt;# Inject the Serializer instance into our converter.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@serializer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ASR&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SerializerInterface&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# :inherit:&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Nil&lt;/span&gt;
    &lt;span class="c1"&gt;# Be sure to handle any possible exceptions here to return more helpful errors to the client.&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BadRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s2"&gt;"Request body is empty."&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;try&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gets_to_end&lt;/span&gt;

    &lt;span class="c1"&gt;# Deserialize the object, based on the type provided in the annotation.&lt;/span&gt;
    &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@serializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deserialize&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:json&lt;/span&gt;

    &lt;span class="c1"&gt;# Run the validations.&lt;/span&gt;
    &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate!&lt;/span&gt;

    &lt;span class="c1"&gt;# Add the resolved object to the request's attributes.&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ValidationError&lt;/span&gt;
    &lt;span class="c1"&gt;# Return a `422` error if the object failed its validations.&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UnprocessableEntity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here you would want to update &lt;code&gt;ArticleController#update_article&lt;/code&gt; with some ACL logic, such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;  &lt;span class="nd"&gt;@[ART::Put(path: "")]&lt;/span&gt;
  &lt;span class="nd"&gt;@[ART::ParamConverter("article", converter: Blog::Converters::RequestBody, model: Blog::Models::Article)]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;
    &lt;span class="c1"&gt;# Ensure that a user cannot edit someone else's article&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ART&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Exceptions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Forbidden&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s2"&gt;"Only the author of the article can edit it."&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="vi"&gt;@user_storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
    &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
    &lt;span class="n"&gt;article&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we're done!  I hope this was a good introduction to Athena and its features.  If there is anything in specific you would like to see regarding Granite/Athena, or if I missed something, feel free to leave a comment or join the Athena &lt;a href="https://gitter.im/athena-frameworkcr/community"&gt;Gitter&lt;/a&gt; channel.&lt;/p&gt;

&lt;p&gt;The full code for this tutorial is available on &lt;a href="https://github.com/Blacksmoke16/athena-blog-tutorial/tree/0.10.0"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>crystal</category>
      <category>json</category>
      <category>symfony</category>
    </item>
  </channel>
</rss>
