<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>The New Adventures of Judy2k</title>
    <subtitle>This is the website of Mark Smith, containing technical ramblings and other things.</subtitle>
    <link href="https://www.judy.co.uk/atom.xml" rel="self" type="application/atom+xml"/>
    <link href="https://www.judy.co.uk"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2025-07-28T00:00:00+00:00</updated>
    <id>https://www.judy.co.uk/atom.xml</id>
    <entry xml:lang="en">
        <title>Run Any Project with &#x27;r&#x27;</title>
        <published>2025-07-28T00:00:00+00:00</published>
        <updated>2025-07-28T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://www.judy.co.uk/blog/run-any-project-with-fish/" type="text/html"/>
        <id>https://www.judy.co.uk/blog/run-any-project-with-fish/</id>
        <content type="html">&lt;p&gt;This is an article about &lt;a href=&quot;https:&#x2F;&#x2F;fishshell.com&#x2F;&quot;&gt;fish shell&lt;&#x2F;a&gt; customisation.
Fish is an alternative to bash, zsh, or powershell.
It’s powerful as both an interactive shell, and a scripting language,
and I’ll be demonstrating a little bit of both of these domains.&lt;&#x2F;p&gt;
&lt;p&gt;This article demonstrates the creation of an &lt;code&gt;r&lt;&#x2F;code&gt; abbreviation in fish,
that will automatically expand to the correct “run” command,
based on the files in your current directory.
It might be of use even if you don’t use fish as your shell.
Maybe it will convince you to try it out!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-running-man&quot;&gt;The Running Man&lt;&#x2F;h2&gt;
&lt;p&gt;I work with a bunch of programming languages
– primarily Python, Rust &amp;amp; JavaScript.
Running these projects is all slightly different.&lt;&#x2F;p&gt;
&lt;p&gt;Here are some examples:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Python:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;uv&lt;&#x2F;span&gt;&lt;span&gt; run a_script_or_something.py
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Rust:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span&gt; run
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# JavaScript:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;node&lt;&#x2F;span&gt;&lt;span&gt; run dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Something I’ve been doing for a few years now is to &lt;a href=&quot;https:&#x2F;&#x2F;www.judy.co.uk&#x2F;blog&#x2F;justfile-mongodb&#x2F;&quot;&gt;add a Justfile&lt;&#x2F;a&gt; or a Makefile to each of my projects.
They all follow the same convention, so if I run &lt;code&gt;just run&lt;&#x2F;code&gt; or &lt;code&gt;make run&lt;&#x2F;code&gt;,
it will execute whichever command is appropriate for that particular project.&lt;&#x2F;p&gt;
&lt;p&gt;So that I don’t have to type &lt;code&gt;just run&lt;&#x2F;code&gt; over and over again,
I created an alias.
Now … fish doesn’t &lt;em&gt;really&lt;&#x2F;em&gt; have aliases, it has &lt;em&gt;functions&lt;&#x2F;em&gt;.
The &lt;code&gt;alias&lt;&#x2F;code&gt; fish command just creates a function and puts it in the right place.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;fish&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-fish &quot;&gt;&lt;code class=&quot;language-fish&quot; data-lang=&quot;fish&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# ~&#x2F;.config&#x2F;fish&#x2F;functions&#x2F;jr.fish
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;jr &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;--wraps=&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;just run&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;--description &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;alias jr just run&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;just &lt;&#x2F;span&gt;&lt;span&gt;run $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;argv&lt;&#x2F;span&gt;&lt;span&gt;; 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is fine, but when I checked the &lt;a href=&quot;https:&#x2F;&#x2F;fishshell.com&#x2F;docs&#x2F;current&#x2F;cmds&#x2F;alias.html&quot;&gt;alias documentation&lt;&#x2F;a&gt;,
I saw the following sentence:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want to ease your interactive use, to save typing, consider using an abbreviation instead.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;… so I had to check out &lt;a href=&quot;https:&#x2F;&#x2F;fishshell.com&#x2F;docs&#x2F;current&#x2F;cmds&#x2F;abbr.html&quot;&gt;abbr&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;That’s when I got a bit carried away.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lots-of-abbreviations&quot;&gt;Lots of abbreviations&lt;&#x2F;h2&gt;
&lt;p&gt;I immediately set up a bunch of abbreviations for running different types of projects:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;abbr --add&lt;&#x2F;span&gt;&lt;span&gt; jr  &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;just run&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;abbr --add&lt;&#x2F;span&gt;&lt;span&gt; mr  &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;make run&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;abbr --add&lt;&#x2F;span&gt;&lt;span&gt; uvr &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;uv run&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;abbr --add&lt;&#x2F;span&gt;&lt;span&gt; cr  &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;cargo run&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;abbr --add&lt;&#x2F;span&gt;&lt;span&gt; nr  &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;node run dev&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The great thing about &lt;code&gt;abbr&lt;&#x2F;code&gt; over &lt;code&gt;alias&lt;&#x2F;code&gt; is that whereas &lt;code&gt;alias&lt;&#x2F;code&gt; &lt;em&gt;executes the command&lt;&#x2F;em&gt; when you hit &lt;em&gt;enter&lt;&#x2F;em&gt;,
&lt;code&gt;abbr&lt;&#x2F;code&gt; &lt;em&gt;replaces the abbreviation with the command&lt;&#x2F;em&gt; when you hit &lt;em&gt;space&lt;&#x2F;em&gt;.
That means you can view and edit the command before you run it.&lt;&#x2F;p&gt;
&lt;p&gt;Abbreviations also come with some neat features,
like being able to put the cursor in the right spot after expansion.
That means &lt;code&gt;abbr --add gcm --set-cursor &#x27;git commit -m &amp;quot;%&amp;quot;&#x27;&lt;&#x2F;code&gt; will expand to &lt;code&gt;git commit -m &amp;quot;&amp;quot;&lt;&#x2F;code&gt;,
with the cursor already between the two quotes, ready for you to write a commit message.&lt;&#x2F;p&gt;
&lt;p&gt;But the five abbreviations above are super-wasteful.
They all “run”,
but I need to decide which shortcut to type out,
based on the kind of project I’m working on.&lt;&#x2F;p&gt;
&lt;p&gt;Wouldn’t it be great if I could just type &lt;code&gt;r&lt;&#x2F;code&gt; and have it automatically expanded to the correct command?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;if-i-type-r-run-the-project&quot;&gt;If I type ‘r’, run the project&lt;&#x2F;h2&gt;
&lt;p&gt;Here’s a function I wrote to echo the appropriate command-line based on the available files and folders in my project.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;fish&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-fish &quot;&gt;&lt;code class=&quot;language-fish&quot; data-lang=&quot;fish&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;_expand_run &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-d &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Expand the &amp;#39;run&amp;#39; abbreviation into a project-specific run command.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;test -e &lt;&#x2F;span&gt;&lt;span&gt;Justfile &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Test for a Justfile in the current directory
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;just run
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;test -e &lt;&#x2F;span&gt;&lt;span&gt;Makefile
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;make run
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;test -e &lt;&#x2F;span&gt;&lt;span&gt;Cargo.toml
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;cargo run
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;test -e &lt;&#x2F;span&gt;&lt;span&gt;package.json
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;npm run dev
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;test -e &lt;&#x2F;span&gt;&lt;span&gt;.venv
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;uv run
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This function assumes it is being run from the project’s top-level directory,
i.e. the directory that contains a Justfile or a Cargo.toml file.
(Don’t worry, I’ll come back to this later.)&lt;&#x2F;p&gt;
&lt;p&gt;Abbr allows you to attach a function to an abbreviation with the &lt;code&gt;--function&lt;&#x2F;code&gt; parameter,
like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;fish&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-fish &quot;&gt;&lt;code class=&quot;language-fish&quot; data-lang=&quot;fish&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# When expanding &amp;#39;r&amp;#39;, run `_expand_run` and use whatever that prints out:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;abbr --add &lt;&#x2F;span&gt;&lt;span&gt;r &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;--function &lt;&#x2F;span&gt;&lt;span&gt;_expand_run
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, if I type ‘r’ and hit space,
if I’m in a directory with a Justfile,
it expands to &lt;code&gt;just run&lt;&#x2F;code&gt;.
If, instead, I’m in a directory containing a Cargo.toml file,
it expands to &lt;code&gt;cargo run&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;when-you-re-deep-in-a-project&quot;&gt;When you’re deep in a project&lt;&#x2F;h2&gt;
&lt;p&gt;The function above worked well for me for a while,
but occasionally it … wouldn’t.&lt;&#x2F;p&gt;
&lt;p&gt;As I pointed out above,
a flaw in the function is that it assumes it is being called from a project’s top-level directory.
Most task runners &lt;em&gt;don’t&lt;&#x2F;em&gt; require this.
I can run &lt;code&gt;just run&lt;&#x2F;code&gt; or &lt;code&gt;cargo run&lt;&#x2F;code&gt; from a deeper directory,
and it doesn’t matter if the Justfile or Cargo.toml file is a directory or two above where I’m working,
the command will find and run them &lt;em&gt;as if&lt;&#x2F;em&gt; my current working directory is the project directory.&lt;&#x2F;p&gt;
&lt;p&gt;If I try that with the expansion function above,
it will get to the end of the if-else block without matching,
echo nothing,
and so the line goes blank. Eventually, I got around to writing a function that recursively searches parent directories for a file or directory with a given name.&lt;&#x2F;p&gt;
&lt;p&gt;It occurred to me later that I probably could have gotten an LLM to write the following for me,
but like some kind of &lt;em&gt;programmer&lt;&#x2F;em&gt;, I wrote it by hand:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;fish&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-fish &quot;&gt;&lt;code class=&quot;language-fish&quot; data-lang=&quot;fish&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;project_file &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-d &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Find a file in parent directories&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Find a file in the specified directory (or PWD), or any parent directory.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;#
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Usage:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# project_file [-p START_DIRECTORY] [-v] FILENAME
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;#
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# `project_file FILENAME` will look for &amp;#39;FILENAME&amp;#39; in the PWD or any parent directory.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# `project_file -p START_DIRECTORY FILENAME` will look for FILENAME in START_DIRECTORY,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# and then all parents of START_DIRECTORY.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;#
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# If -v is provided, project_file will print the path of the found file.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# The exit code is 0 if a file was found and 1 otherwise.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;argparse &lt;&#x2F;span&gt;&lt;span&gt;v&#x2F;verbose &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;p&#x2F;path=&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-- &lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;argv
&lt;&#x2F;span&gt;&lt;span&gt;    or &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;set &lt;&#x2F;span&gt;&lt;span&gt;filename $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;argv&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;test -z &lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;filename
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;project_file: missing FILENAME&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &amp;gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-2
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;set -ql &lt;&#x2F;span&gt;&lt;span&gt;_flag_p &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Use the -p value, if provided
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;set &lt;&#x2F;span&gt;&lt;span&gt;p &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;$(path&lt;&#x2F;span&gt;&lt;span&gt; resolve &amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_flag_p&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Default to the working directory
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;set &lt;&#x2F;span&gt;&lt;span&gt;p &amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;PWD&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;seq &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 64&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Avoiding an infinite loop
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;p &lt;&#x2F;span&gt;&lt;span&gt;= &#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# File wasn&amp;#39;t found in any parent directory
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;test -e &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;filename&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# File found. Return
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;set -ql &lt;&#x2F;span&gt;&lt;span&gt;_flag_v
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;filename&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# File not found here. Check in parent.
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;set &lt;&#x2F;span&gt;&lt;span&gt;p &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;$(path&lt;&#x2F;span&gt;&lt;span&gt; dirname $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;project_file: possible infinite loop. aborted&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &amp;gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Why isn’t this part of a standard library?&lt;&#x2F;p&gt;
&lt;p&gt;I then hooked this into my expansion function:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;fish&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-fish &quot;&gt;&lt;code class=&quot;language-fish&quot; data-lang=&quot;fish&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;_expand_run &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-d &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Expand the &amp;#39;run&amp;#39; abbreviation into a project-specific run command.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;project_file &lt;&#x2F;span&gt;&lt;span&gt;Justfile  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Test if this dir or a parent contains Justfile
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;just run
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;project_file &lt;&#x2F;span&gt;&lt;span&gt;Makefile
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;make run
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;project_file &lt;&#x2F;span&gt;&lt;span&gt;Cargo.toml
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;cargo run
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;project_file &lt;&#x2F;span&gt;&lt;span&gt;package.json
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;npm run dev
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;project_file &lt;&#x2F;span&gt;&lt;span&gt;.venv
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;uv run
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;&#x2F;h2&gt;
&lt;p&gt;I’ve made &lt;a href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;judy2k&#x2F;bc359353d0469f0401d677a08e63903b&quot;&gt;my full script&lt;&#x2F;a&gt; available as a gist.
It includes &lt;code&gt;b&lt;&#x2F;code&gt; and &lt;code&gt;t&lt;&#x2F;code&gt; expansions for building and testing projects, too.
Feel free to drop it into your own &lt;code&gt;~&#x2F;.config&#x2F;fish&#x2F;conf.d&#x2F;&lt;&#x2F;code&gt; directory.
Don’t forget to start a new fish session to pick up the script.
You might also want to edit the expansion functions to match your own tools and conventions, too.&lt;&#x2F;p&gt;
&lt;p&gt;I’ve found &lt;code&gt;r&lt;&#x2F;code&gt; for “run”, &lt;code&gt;b&lt;&#x2F;code&gt; for “build”, and &lt;code&gt;t&lt;&#x2F;code&gt; for “test” reduces cognitive load more than I would have expected,
especially when switching between projects.
Don’t expect your friends to be impressed, though
– it’s so fast, they won’t see it unless you explain it to them!&lt;&#x2F;p&gt;
&lt;p&gt;It’s possible to rewrite these expansions as standalone functions or scripts,
but I like the way abbreviations work as a smart autocomplete.
I’ve also considered extending the expansion functions to parse the Justfile or package.json for available tasks.
But I’ve decided to stop for now!&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;abbr&lt;&#x2F;code&gt; command can do a lot more than I’ve shown here.
I highly recommend checking out &lt;a href=&quot;https:&#x2F;&#x2F;fishshell.com&#x2F;docs&#x2F;current&#x2F;cmds&#x2F;abbr.html&quot;&gt;the abbr docs&lt;&#x2F;a&gt; to see what it can do.
I’ve also become intrigued by the ability to call functions based on &lt;a href=&quot;https:&#x2F;&#x2F;fishshell.com&#x2F;docs&#x2F;current&#x2F;language.html#event&quot;&gt;fish events&lt;&#x2F;a&gt;,
but I think I’ll save that for future experiments.&lt;&#x2F;p&gt;
&lt;p&gt;If you found this article useful, or you have suggestions, let me know on &lt;a href=&quot;https:&#x2F;&#x2F;chaos.social&#x2F;@judy2k&quot;&gt;Mastodon&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;bsky.app&#x2F;profile&#x2F;judy.co.uk&quot;&gt;Bluesky&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;!--
## Hire Me!

Did you like this article?
Do you think I sound like the kind of person that may be suitable for your organisation?

**I&#x27;m looking for work as a Senior Software Developer**, either remote or local to Edinburgh.
I have many years of Python and Rust experience (among other things) at all levels.
I also have excellent communications skills!
(Hopefully my writing here illustrates that point.)

You can find out more about me on [this site](&#x2F;),
and you can contact me at [judy@judy.co.uk](mailto:judy@judy.co.uk)
for a copy of my CV.
--&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Using Fortune to Reinforce Habits</title>
        <published>2025-07-23T00:00:00+00:00</published>
        <updated>2025-07-23T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://www.judy.co.uk/blog/using-fortune-to-reinforce-habits/" type="text/html"/>
        <id>https://www.judy.co.uk/blog/using-fortune-to-reinforce-habits/</id>
        <content type="html">&lt;p&gt;A while ago I installed a bunch of new command-line tools,
some of which replaced my existing tools, like &lt;a href=&quot;https:&#x2F;&#x2F;eza.rocks&#x2F;&quot;&gt;&lt;code&gt;eza&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; instead of &lt;code&gt;ls&lt;&#x2F;code&gt;.
Other tools improved the way that I work, such as &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jesseduffield&#x2F;lazygit&quot;&gt;LazyGit&lt;&#x2F;a&gt;
– the only interface to Git that I’ve found to be better that just using &lt;code&gt;git&lt;&#x2F;code&gt; commands.&lt;&#x2F;p&gt;
&lt;p&gt;If you’re looking for some recommendations for a modern command-line,
I can highly recommend &lt;a href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;W8imzQhI-WM&quot;&gt;Supercharge your shell&lt;&#x2F;a&gt; by Justin Mayer,
or &lt;a href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;rWMQ-g2QDsI&quot;&gt;Oxidise your command line&lt;&#x2F;a&gt; from the No Boilerplate YouTube channel.
Many of these tools are written in Rust,
which is a favourite language of mine,
but that shouldn’t make much difference to you!&lt;&#x2F;p&gt;
&lt;p&gt;I had two problems though.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;problem-1-forgetting-to-use-the-tools&quot;&gt;Problem 1: Forgetting to use the tools&lt;&#x2F;h2&gt;
&lt;p&gt;One problem was that muscle-memory meant I kept using the old commands instead of the new ones.
I could fix this by creating aliases, like &lt;code&gt;alias ls=eza&lt;&#x2F;code&gt;.
That would mean that whenever I typed &lt;code&gt;ls&lt;&#x2F;code&gt; the command that &lt;em&gt;actually&lt;&#x2F;em&gt; was run would be &lt;code&gt;eza&lt;&#x2F;code&gt;.
That’s fine with commands which are backwards compatible.
&lt;code&gt;eza&lt;&#x2F;code&gt; supports all (or at least most!) of the flags that &lt;code&gt;ls&lt;&#x2F;code&gt; supports.&lt;&#x2F;p&gt;
&lt;p&gt;The problem was with commands that &lt;em&gt;aren’t&lt;&#x2F;em&gt; compatible.
For example, the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;muesli&#x2F;duf&quot;&gt;&lt;code&gt;duf&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; command is much more powerful than &lt;code&gt;du&lt;&#x2F;code&gt; which I used to use,
and isn’t a drop-in replacement.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;problem-2-forgetting-i-installed-the-tool&quot;&gt;Problem 2: Forgetting I installed the tool&lt;&#x2F;h2&gt;
&lt;p&gt;The other problem was that I kept forgetting that I had installed a tool,
or if I remembered,
then I couldn’t remember what the tool was &lt;em&gt;called&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;While I was working on this post,
I couldn’t remember the name of the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ducaale&#x2F;xh&quot;&gt;&lt;code&gt;xh&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; tool.
It’s an HTTP client for the command line.
It’s a replacement for HTTPie which was a replacement for Curl!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-solution&quot;&gt;The Solution&lt;&#x2F;h2&gt;
&lt;p&gt;I needed some constant reminders that these tools existed on my computer,
and a reminder of what each tool did.&lt;&#x2F;p&gt;
&lt;p&gt;What I imagined was that every time I opened a terminal window,
I would get a short message saying,
“Don’t forget you’ve installed &lt;code&gt;xh&lt;&#x2F;code&gt; – it’s a command-line HTTP client!”&lt;&#x2F;p&gt;
&lt;p&gt;Yes, I really write messages to myself in that chirpy style.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;in-steps-fortune-fortunately&quot;&gt;In steps &lt;code&gt;fortune&lt;&#x2F;code&gt;, fortunately&lt;&#x2F;h2&gt;
&lt;p&gt;I nearly started coding something in Python.
I am a programmer after all.
Then I remembered…&lt;&#x2F;p&gt;
&lt;p&gt;There’s a tool for managing a database of short text snippets,
and selecting snippets at random.
It’s called &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Fortune_(Unix)&quot;&gt;&lt;code&gt;fortune&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and it’s a proper old-school UNIX utility.&lt;&#x2F;p&gt;
&lt;p&gt;These days, fortune isn’t usually installed by default on Linux.
The following are the commands I know for installing fortune on different platforms:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Debian etc.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;apt&lt;&#x2F;span&gt;&lt;span&gt; install fortune-mod
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Fedora etc:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span&gt; dnf install fortune-mod
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Arch:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span&gt; pacman&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -S&lt;&#x2F;span&gt;&lt;span&gt; fortune-mod
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# MacOS:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;brew&lt;&#x2F;span&gt;&lt;span&gt; install fortune
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once it’s installed,
you can see a random snippet by running the &lt;code&gt;fortune&lt;&#x2F;code&gt; command with no parameters.
You’ll get a different snippet each time.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; fortune
&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Earth is a great, big funhouse without the fun.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;--&lt;&#x2F;span&gt;&lt;span&gt; Jeff Berner
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; fortune
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Honesty&lt;&#x2F;span&gt;&lt;span&gt; pays, but it doesn&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;t seem to pay enough to suit some people.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        -- F.M. Hubbard
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I suspect that most people who install fortune just use the data files that come with fortune,
just like I did above.
They contain a bunch of jokes, quotes, technical and philosophical observations.&lt;&#x2F;p&gt;
&lt;p&gt;But you can also create your &lt;em&gt;own&lt;&#x2F;em&gt; data files for fortune.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;create-your-own-fortune-file&quot;&gt;Create your own fortune file&lt;&#x2F;h2&gt;
&lt;p&gt;I created a short text file called “habits”, and added some short snippets to it.
The format isn’t very complicated.
Each snippet is separated by the percent character (%) on its own line, like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;txt&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-txt &quot;&gt;&lt;code class=&quot;language-txt&quot; data-lang=&quot;txt&quot;&gt;&lt;span&gt;Don&amp;#39;t forget about hexyl! A handy binary file viewer.
&lt;&#x2F;span&gt;&lt;span&gt;%
&lt;&#x2F;span&gt;&lt;span&gt;Still using man? Try using *tldr* first!
&lt;&#x2F;span&gt;&lt;span&gt;%
&lt;&#x2F;span&gt;&lt;span&gt;Don&amp;#39;t use *ps* - use *procs*!
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Fortune can’t use this text file as it is.
First, it needs a data (or “dat”) file to be created.
The data file sits alongside the text file
and contains information that allows fortune to look up a random snippet &lt;em&gt;really quickly&lt;&#x2F;em&gt;.
Fortunately the tool that does this processing is packaged with &lt;code&gt;fortune&lt;&#x2F;code&gt;,
and it’s called &lt;code&gt;strfile&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To create a file called “habits.dat” from the file “habits”,
I ran &lt;code&gt;strfile habits habits.dat&lt;&#x2F;code&gt;.
This created “habits.dat” in my current directory.
Now I can run &lt;code&gt;fortune&lt;&#x2F;code&gt; and point it at my text file.
It will find the dat file automatically:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Check that both habits and habits.dat are in the right place:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; ls&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;habits
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;habits.dat
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; fortune habits
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Still&lt;&#x2F;span&gt;&lt;span&gt; using man? Try using *tldr* first!
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; fortune habits
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Don&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;t forget about hexyl! A handy binary file viewer.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;make-it-easy-to-update-the-habits-file&quot;&gt;Make it easy to update the habits file&lt;&#x2F;h2&gt;
&lt;p&gt;I often forget how to update the data file when I’ve updated my fortune text file.
(I had actually forgotten when I started to write this post.)
I created a Makefile to make it easier to update the data file.
Makefiles are also pretty old-school,
but they’re also the perfect tool for this job!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;Makefile&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-Makefile &quot;&gt;&lt;code class=&quot;language-Makefile&quot; data-lang=&quot;Makefile&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Makefile
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;habits.dat&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;habits
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;strfile&lt;&#x2F;span&gt;&lt;span&gt; habits habits.dat
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Just make sure your text editor isn’t going to replace the indent with spaces
– it’s important that it remains a tab character.&lt;&#x2F;p&gt;
&lt;p&gt;I have to admit that &lt;em&gt;my&lt;&#x2F;em&gt; Makefile is slightly more complex than this,
but I’m a nerd. The Makefile above is fine.&lt;&#x2F;p&gt;
&lt;p&gt;After writing this, I can now just run &lt;code&gt;make&lt;&#x2F;code&gt;,
and it will update “habits.dat” if the “habits” file has changed.&lt;&#x2F;p&gt;
&lt;p&gt;At this point,
I filled out my “habits” file with details of all the commands I’d installed.
I stole some content from &lt;a href=&quot;https:&#x2F;&#x2F;tldr.sh&#x2F;&quot;&gt;&lt;code&gt;tldr&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;,
which is super-helpful for learning the basics for a new tool.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; tldr duf
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt; List accessible devices:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;duf
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt; List everything (such as pseudo, duplicate or inaccessible file systems)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;duf --all
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt; Only show specified devices or mount points:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;duf&lt;&#x2F;span&gt;&lt;span&gt; path&#x2F;to&#x2F;directory1 path&#x2F;to&#x2F;directory2 ...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt; Sort the output by a specified criteria:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;duf --sort&lt;&#x2F;span&gt;&lt;span&gt; size|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;used&lt;&#x2F;span&gt;&lt;span&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;avail&lt;&#x2F;span&gt;&lt;span&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;usage
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;automatic-reminders&quot;&gt;Automatic reminders&lt;&#x2F;h2&gt;
&lt;p&gt;Do you remember that I wanted to print out these snippets every time I opened a terminal window?
How you do that depends on the shell you’re running.&lt;&#x2F;p&gt;
&lt;p&gt;First, I moved the directory containing “habits”, “habits.dat”, and my “Makefile” into an appropriate directory.
I chose “~&#x2F;.local&#x2F;habits”.
Afterwards, I could do this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; ls &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;~&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;.local&#x2F;habits
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;habits&lt;&#x2F;span&gt;&lt;span&gt;  habits.dat  Makefile
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you use bash or zsh you can add the following to the end of “~&#x2F;.bashrc” or “~&#x2F;.zshrc”:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# ~&#x2F;.bashrc or ~&#x2F;.zshrc
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# ... existing content goes here ...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fortune ~&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;.local&#x2F;habits
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you’re on &lt;a href=&quot;https:&#x2F;&#x2F;fishshell.com&#x2F;&quot;&gt;fish shell&lt;&#x2F;a&gt;, it’s a bit different.
(You should try fish shell, I absolutely love it!)
In this case, you need to add the above command to a function called &lt;code&gt;fish_greeting&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;fish&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-fish &quot;&gt;&lt;code class=&quot;language-fish&quot; data-lang=&quot;fish&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# ~&#x2F;.config&#x2F;fish&#x2F;functions&#x2F;fish_greeting.fish
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;fish_greeting
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fortune &lt;&#x2F;span&gt;&lt;span&gt;~&#x2F;.local&#x2F;habits
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;However you configure it,
once you’ve made the necessary edits open a new terminal window and you should see a handy reminder!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bonus-points&quot;&gt;Bonus points&lt;&#x2F;h2&gt;
&lt;p&gt;The code above is pretty useful,
but the text doesn’t stand out very much in the console.
I found it was a little too easy to ignore it.&lt;&#x2F;p&gt;
&lt;p&gt;There are a few neat tools for the command line that can format text in interesting ways,
for example, by drawing ascii-art frames around the text.
Two popular ones are &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cowsay-org&#x2F;cowsay&quot;&gt;cowsay&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;boxes.thomasjensen.com&#x2F;&quot;&gt;boxes&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I changed the &lt;code&gt;fortune&lt;&#x2F;code&gt; call so that it pipes the output through &lt;code&gt;boxes&lt;&#x2F;code&gt;.
My command looks like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fortune ~&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;.local&#x2F;habits | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;boxes -d&lt;&#x2F;span&gt;&lt;span&gt; parchment
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;… and the end result looks like this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.judy.co.uk&#x2F;blog&#x2F;using-fortune-to-reinforce-habits&#x2F;end_result_screenshot.png&quot; alt=&quot;A screenshot, showing a nice ascii art parchment, with one of my snippets printed in the middle.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;&#x2F;h2&gt;
&lt;p&gt;Building my own habit reminder as I’ve described above was something that I’ve found surprisingly useful.
I had never really thought of using &lt;code&gt;fortune&lt;&#x2F;code&gt; in this way,
but it’s proven to be quick to set up,
didn’t require any coding,
and it’s helped me to improve how I use the command-line!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;update&quot;&gt;Update&lt;&#x2F;h2&gt;
&lt;p&gt;This post has done some numbers, and apparently inspired some people!
I love that the phrase “habits file” is being used by others as a shorthand for this idea.&lt;&#x2F;p&gt;
&lt;p&gt;If you’d like to configure &lt;a href=&quot;https:&#x2F;&#x2F;www.chezmoi.io&#x2F;&quot;&gt;chezmoi&lt;&#x2F;a&gt; to synchronise your habits files across the different machines you use,
do check out Chris’ post, &lt;a href=&quot;https:&#x2F;&#x2F;offby1.website&#x2F;posts&#x2F;habits-in-the-shell-shared.html&quot;&gt;Habits in the Shell, shared&lt;&#x2F;a&gt;.
Chezmoi is a tool that I also use,
to share config between my computers.
It’s an awesome piece of software,
with a bit of a learning curve.&lt;&#x2F;p&gt;
&lt;p&gt;If you found this tutorial useful, let me know on &lt;a href=&quot;https:&#x2F;&#x2F;chaos.social&#x2F;@judy2k&quot;&gt;Mastodon&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;bsky.app&#x2F;profile&#x2F;judy.co.uk&quot;&gt;Bluesky&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;!--
## Hire Me!

Did you like this article?
Do you think I sound like the kind of person that may be suitable for your organisation?

**I&#x27;m looking for work as a Senior Software Developer**, either remote or local to Edinburgh.
I have many years of Python and Rust experience (among other things) at all levels.
I also have excellent communications skills!
(Hopefully my writing here illustrates that point.)

You can find out more about me on [this site](&#x2F;),
and you can contact me at [judy@judy.co.uk](mailto:judy@judy.co.uk)
for a copy of my CV.
--&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Rust Trait Implementations and References</title>
        <published>2023-02-22T00:00:00+00:00</published>
        <updated>2023-02-22T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://www.judy.co.uk/blog/rust-traits-and-references/" type="text/html"/>
        <id>https://www.judy.co.uk/blog/rust-traits-and-references/</id>
        <content type="html">&lt;p&gt;If you implement a trait on a type,
and then take a reference to that type,
does the reference &lt;em&gt;also&lt;&#x2F;em&gt; implement the trait?&lt;&#x2F;p&gt;
&lt;p&gt;For a while I thought so! Let me explain why.&lt;&#x2F;p&gt;
&lt;p&gt;Sometimes the things that Rust does for you can obfuscate what’s really going on under the hood,
and I discovered one of those recently while having a minor battle with Rust’s type-system.&lt;&#x2F;p&gt;
&lt;p&gt;To illustrate, let me start with a trait called Speaker and an empty struct that implements the trait:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; A trait for things that can speak.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;trait &lt;&#x2F;span&gt;&lt;span&gt;Speaker {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; BasicSpeaker is an empty struct that exists
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; only to implement Speaker.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;BasicSpeaker;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; BasicSpeakers must be able to speak.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span&gt;Speaker &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;BasicSpeaker {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Hello!&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, in my &lt;code&gt;main&lt;&#x2F;code&gt; function, the following code should work:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Create a BasicSpeaker struct:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; speaker = BasicSpeaker;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Call the speak method defined on the BasicSpeaker:
&lt;&#x2F;span&gt;&lt;span&gt;speaker.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And it does! If I run this in &lt;a href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2021&amp;amp;gist=488473d6b8f4fd9bb8ad7ab56c654511&quot;&gt;a playground&lt;&#x2F;a&gt;,
then it prints the message “Hello!”&lt;&#x2F;p&gt;
&lt;p&gt;If I have a &lt;em&gt;reference&lt;&#x2F;em&gt; to a BasicSpeaker,
I can still call &lt;code&gt;speak&lt;&#x2F;code&gt; on it,
because Rust automatically dereferences the variable.
So the code below also works:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Take a reference to the BasicSpeaker:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; speaker_ref: &amp;amp;BasicSpeaker = &amp;amp;speaker;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Call the speak method defined on the BasicSpeaker,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; via the reference:
&lt;&#x2F;span&gt;&lt;span&gt;speaker_ref.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(I know I don’t need to explicitly specify the type of &lt;code&gt;speaker_ref&lt;&#x2F;code&gt; as &lt;code&gt;&amp;amp;BasicSpeaker&lt;&#x2F;code&gt;,
but I want to make it really clear what type the variable is.)&lt;&#x2F;p&gt;
&lt;p&gt;This might lead you to believe
that as well as &lt;code&gt;BasicSpeaker&lt;&#x2F;code&gt; implementing &lt;code&gt;Speaker&lt;&#x2F;code&gt;,
that a reference, &lt;code&gt;&amp;amp;BasicSpeaker&lt;&#x2F;code&gt;, &lt;em&gt;also&lt;&#x2F;em&gt; implements &lt;code&gt;Speaker&lt;&#x2F;code&gt;.
Perhaps by some kind of Rust magic?&lt;&#x2F;p&gt;
&lt;p&gt;Let me test this out more specifically by defining a function that takes a parameter of type &lt;code&gt;impl Speaker&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;speak_to&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span&gt;: impl Speaker) {
&lt;&#x2F;span&gt;&lt;span&gt;    s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Create a BasicSpeaker struct:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; speaker = BasicSpeaker;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Pass speaker to the new function:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;speak_to&lt;&#x2F;span&gt;&lt;span&gt;(speaker);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This works, because &lt;code&gt;BasicSpeaker&lt;&#x2F;code&gt; implements the &lt;code&gt;Speaker&lt;&#x2F;code&gt; trait. You can check it out in another &lt;a href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2021&amp;amp;gist=b66fa6d6192ca9029ad98b414d2ae89f&quot;&gt;playground&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Let’s try the same thing, but this time with a reference to the &lt;code&gt;BasicSpeaker&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Take a reference to the BasicSpeaker:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; speaker_ref: &amp;amp;BasicSpeaker = &amp;amp;speaker;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Pass the reference to `speak_to`:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;speak_to&lt;&#x2F;span&gt;&lt;span&gt;(speaker_ref);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This &lt;em&gt;doesn’t&lt;&#x2F;em&gt; work! You can &lt;a href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2021&amp;amp;gist=886ebf22a84bc543621ddd735014492d&quot;&gt;check it out&lt;&#x2F;a&gt; for yourself.
The error message looks like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;error[E0277]: the trait bound `&amp;amp;BasicSpeaker: Speaker` is not satisfied
&lt;&#x2F;span&gt;&lt;span&gt;  --&amp;gt; src&#x2F;main.rs:31:14
&lt;&#x2F;span&gt;&lt;span&gt;   |
&lt;&#x2F;span&gt;&lt;span&gt;31 |     speak_to(speaker_ref);
&lt;&#x2F;span&gt;&lt;span&gt;   |     -------- ^^^^^^^^^^^ the trait `Speaker` is not implemented for `&amp;amp;BasicSpeaker`
&lt;&#x2F;span&gt;&lt;span&gt;   |     |
&lt;&#x2F;span&gt;&lt;span&gt;   |     required by a bound introduced by this call
&lt;&#x2F;span&gt;&lt;span&gt;   |
&lt;&#x2F;span&gt;&lt;span&gt;   = help: the trait `Speaker` is implemented for `BasicSpeaker`
&lt;&#x2F;span&gt;&lt;span&gt;note: required by a bound in `speak_to`
&lt;&#x2F;span&gt;&lt;span&gt;  --&amp;gt; src&#x2F;main.rs:16:21
&lt;&#x2F;span&gt;&lt;span&gt;   |
&lt;&#x2F;span&gt;&lt;span&gt;16 | fn speak_to(s: impl Speaker) {
&lt;&#x2F;span&gt;&lt;span&gt;   |                     ^^^^^^^ required by this bound in `speak_to`
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;For more information about this error, try `rustc --explain E0277`.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The initial error message is a touch cryptic, but the message next to the first block of code is clearer: “The trait &lt;code&gt;Speaker&lt;&#x2F;code&gt; is not implemented for &lt;code&gt;&amp;amp;BasicSpeaker&lt;&#x2F;code&gt;.”&lt;&#x2F;p&gt;
&lt;p&gt;The earlier code examples demonstrate that you can call methods on a reference that are not implemented &lt;em&gt;on that reference&lt;&#x2F;em&gt;, because Rust silently dereferences the value for you.
Under the hood, Rust does this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Rust converts `speaker_ref.speak()` into:
&lt;&#x2F;span&gt;&lt;span&gt;(*speaker_ref).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;What it &lt;em&gt;doesn’t&lt;&#x2F;em&gt; mean is that a &lt;code&gt;&amp;amp;BasicSpeaker&lt;&#x2F;code&gt;, a &lt;em&gt;reference&lt;&#x2F;em&gt;, implements &lt;code&gt;Speaker&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-direct-solution&quot;&gt;A Direct Solution&lt;&#x2F;h2&gt;
&lt;p&gt;The most direct solution is to implement &lt;code&gt;Speaker&lt;&#x2F;code&gt; on  &lt;code&gt;BasicSpeaker&lt;&#x2F;code&gt; references, like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; BasicSpeaker _references_ must also be able to speak.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span&gt;Speaker &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;&amp;amp;BasicSpeaker {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Hello!&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With this added to the code, it &lt;a href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2021&amp;amp;gist=3e9edfcfaa43d7ef3852aee431dfb863&quot;&gt;compiles and runs&lt;&#x2F;a&gt;.
So it’s a kind of success!
But it’s not ideal.
For one thing, this is basically duplicated code from the previous implementation.
Here’s a slightly improved implementation that just calls through to the underlying struct:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; BasicSpeaker _references_ must also be able to speak.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span&gt;Speaker &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;&amp;amp;BasicSpeaker {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(**&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In case it’s not obvious,
I have to dereference &lt;code&gt;self&lt;&#x2F;code&gt; twice because the function takes &lt;code&gt;&amp;amp;self&lt;&#x2F;code&gt;, and &lt;code&gt;self&lt;&#x2F;code&gt; is &lt;code&gt;&amp;amp;BasicSpeaker&lt;&#x2F;code&gt;.
That means the argument is a &lt;code&gt;&amp;amp;&amp;amp;BasicSpeaker&lt;&#x2F;code&gt; and it has to be dereferenced twice to get the &lt;code&gt;BasicSpeaker&lt;&#x2F;code&gt; that implements &lt;code&gt;speak()&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Okay, so now there’s not so much code duplication,
but there is another problem.&lt;&#x2F;p&gt;
&lt;p&gt;The code &lt;em&gt;only&lt;&#x2F;em&gt; implements &lt;code&gt;Speaker&lt;&#x2F;code&gt; for references to a &lt;code&gt;BasicSpeaker&lt;&#x2F;code&gt; struct.
If I want to define another &lt;code&gt;Speaker&lt;&#x2F;code&gt;, something like &lt;code&gt;NamedSpeaker&lt;&#x2F;code&gt;,
then I’ll have to write that code twice as well - once for &lt;code&gt;NamedSpeaker&lt;&#x2F;code&gt;, and again for &lt;code&gt;&amp;amp;NamedSpeaker&lt;&#x2F;code&gt;. &lt;&#x2F;p&gt;
&lt;p&gt;Excuse my language, but that &lt;em&gt;sucks&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fixing-this-with-a-blanket-implementation&quot;&gt;Fixing This With a Blanket Implementation&lt;&#x2F;h2&gt;
&lt;p&gt;Because Rust &lt;em&gt;doesn’t&lt;&#x2F;em&gt; suck,
it has a feature called blanket implementations for traits.
They’re actually one of my favourite things about Rust.&lt;&#x2F;p&gt;
&lt;p&gt;I can write a blanket implementation that says: “If a struct T implements Speaker, then here is an implementation of Speaker for &amp;amp;T.” And then I can have &lt;a href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2021&amp;amp;gist=8fcdd44f62cd0bfa31deedde1fba09fc&quot;&gt;that implementation&lt;&#x2F;a&gt; pass through the calls to the referenced struct’s definition.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; All references to things that implement Speaker must also be Speakers.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;T&amp;gt; Speaker &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;&amp;amp;T
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;where
&lt;&#x2F;span&gt;&lt;span&gt;    T: Speaker,
&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(**&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or, if you prefer, you can use the following, slightly shorter syntax that means the same thing:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; All references to things that implement Speaker must also be Speakers.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;T: Speaker&amp;gt; Speaker &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;&amp;amp;T {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(**&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;what-i-learned-about-interface-design-from-rust-for-rustaceans&quot;&gt;What I Learned About Interface Design From &lt;em&gt;Rust for Rustaceans&lt;&#x2F;em&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Chapter 3 of Jon Gjengset’s &lt;a href=&quot;https:&#x2F;&#x2F;nostarch.com&#x2F;rust-rustaceans&quot;&gt;Rust for Rustaceans&lt;&#x2F;a&gt; book is called &lt;em&gt;Interface Design&lt;&#x2F;em&gt;,
and it starts out by covering the basics of building ergonomic interfaces in Rust. It states the following:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“When you define a new trait, you’ll usually want to provide blanket implementations as appropriate for that trait for &lt;code&gt;&amp;amp;T where T: Trait&lt;&#x2F;code&gt;, &lt;code&gt;&amp;amp;mut T where T: Trait&lt;&#x2F;code&gt;, and &lt;code&gt;Box&amp;lt;T&amp;gt; where T: Trait&lt;&#x2F;code&gt;. You may be able to implement only some of these depending on what receivers the methods of Trait have.”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This paragraph was a bit of a revelation to me!
Because I haven’t been &lt;em&gt;reading&lt;&#x2F;em&gt; enough Rust code,
I wasn’t familiar with standard practice when &lt;em&gt;writing&lt;&#x2F;em&gt; Rust code.
I had accidentally been writing code that was non-idiomatic and surprising without intending to.&lt;&#x2F;p&gt;
&lt;p&gt;There’s lots of other good advice in that chapter particularly.
I recommend picking up a copy of the book,
either &lt;a href=&quot;https:&#x2F;&#x2F;nostarch.com&#x2F;rust-rustaceans&quot;&gt;directly&lt;&#x2F;a&gt;,
or at your nearest &lt;a href=&quot;https:&#x2F;&#x2F;uk.bookshop.org&#x2F;p&#x2F;books&#x2F;rust-for-rustaceans-idiomatic-programming-for-experienced-developers-jon-gjengset&#x2F;6203983?ean=9781718501850&quot;&gt;independent bookseller&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The excerpt above led me to realise that even though I’ve now written an implementation of &lt;code&gt;Speaker&lt;&#x2F;code&gt; for &lt;code&gt;&amp;amp;BasicSpeaker&lt;&#x2F;code&gt;,
that doesn’t apply to &lt;code&gt;&amp;amp;mut BasicSpeaker&lt;&#x2F;code&gt;!
So this won’t work:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Take a mutable reference to the BasicSpeaker:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; speaker_mut_ref: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; BasicSpeaker = &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; speaker;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Call the speak method defined on the BasicSpeaker,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; via the mut reference:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;speak_to&lt;&#x2F;span&gt;&lt;span&gt;(speaker_mut_ref);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That requires another blanket implementation:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; All mutable references to things that implement Speaker must also be Speakers.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;T&amp;gt; Speaker &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;&amp;amp;mut T
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;where
&lt;&#x2F;span&gt;&lt;span&gt;    T: Speaker,
&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(**&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And just for completeness,
here is the same thing for &lt;code&gt;Box&amp;lt;T&amp;gt;&lt;&#x2F;code&gt;,
for when you want to put a Speaker implementation on the heap:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; All Speakers in boxes must also be Speakers.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;T&amp;gt; Speaker &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;Box&amp;lt;T&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;where
&lt;&#x2F;span&gt;&lt;span&gt;    T: Speaker,
&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(**&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once those blanket implementations are added,
it means that any new implementations of &lt;code&gt;Speaker&lt;&#x2F;code&gt;
are automatically implemented for the new type,
any reference to the type,
and also any &lt;code&gt;Box&lt;&#x2F;code&gt; containing the type!&lt;&#x2F;p&gt;
&lt;p&gt;You can find the &lt;a href=&quot;https:&#x2F;&#x2F;www.judy.co.uk&#x2F;blog&#x2F;rust-traits-and-references&#x2F;(https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2021&amp;amp;gist=e17baa25c03e6e265f3effde2a894c1d)&quot;&gt;full code listing&lt;&#x2F;a&gt; in a Rust Playground,
including a second &lt;code&gt;Speaker&lt;&#x2F;code&gt;-implementing struct.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Because Rust will automatically dereference trait references,
it can look like &lt;em&gt;references&lt;&#x2F;em&gt; to trait implementations are also &lt;em&gt;themselves&lt;&#x2F;em&gt; trait implementations.
But they’re not.
Fortunately, in many cases, you can fix that with some blanket trait implementations.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Rust for Rustaceans&lt;&#x2F;em&gt; states that if your trait interface allows,
you &lt;em&gt;should&lt;&#x2F;em&gt; provide blanket trait implementations for &lt;code&gt;&amp;amp;T&lt;&#x2F;code&gt;, &lt;code&gt;&amp;amp;mut T&lt;&#x2F;code&gt; and &lt;code&gt;Box&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; so that you can pass these types to any function that accepts implementations of your trait.&lt;&#x2F;p&gt;
&lt;p&gt;I certainly plan to follow that advice in future,
in the hope that it will avoid problems using the types I’ve defined.
I’m looking forward to what I learn next in the book!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thanks so much to
&lt;a href=&quot;https:&#x2F;&#x2F;chaos.social&#x2F;@carlton@fosstodon.org&quot;&gt;Carlton Gibson&lt;&#x2F;a&gt;,
&lt;a href=&quot;https:&#x2F;&#x2F;powwau.com&#x2F;en&#x2F;&quot;&gt;Jorge Ortiz Fuentes&lt;&#x2F;a&gt;,
&lt;a href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@dfreniche&quot;&gt;Diego Freniche Brito&lt;&#x2F;a&gt; &amp;amp;
&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;aaronbassett&quot;&gt;Aaron Bassett&lt;&#x2F;a&gt; for reading the draft of this blog post.
Any errors that remain are probably my own fault,
but that doesn’t stop you from blaming one of them if you want to.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Use Justfiles to Work with MongoDB</title>
        <published>2021-05-21T00:00:00+00:00</published>
        <updated>2021-05-21T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://www.judy.co.uk/blog/justfile-mongodb/" type="text/html"/>
        <id>https://www.judy.co.uk/blog/justfile-mongodb/</id>
        <content type="html">&lt;p&gt;I have started to write &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;casey&#x2F;just&quot;&gt;Justfiles&lt;&#x2F;a&gt; for all my new projects.
There’s a good chance you haven’t heard of Just or Justfiles.
They’re very similar to Make and Makefiles.
Whereas Make is designed to build artifacts from source files,
and combine them in potentially complex ways,
Just is “just” a task runner.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;Just can be installed with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;casey&#x2F;just#packages&quot;&gt;a bunch of different package managers&lt;&#x2F;a&gt;,
or you can just download a prebuilt binary.&lt;&#x2F;p&gt;
&lt;p&gt;A short Justfile, containing a single recipe to import some data into a MongoDB cluster looks like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;import:
&lt;&#x2F;span&gt;&lt;span&gt;    mongoimport $MDB_URI --collection recipes recipe_data.json
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The file is literally called &lt;code&gt;Justfile&lt;&#x2F;code&gt;, with no file extension.
You would usually put this file at the root of your project directory.
This will all seem familiar, if you’ve ever worked with Makefiles!&lt;&#x2F;p&gt;
&lt;p&gt;This specifies a &lt;em&gt;recipe&lt;&#x2F;em&gt; called “import”. 
When I ask Just to run the import target, 
it will run the mongoimport command I’ve written on the line below.
The environment variable &lt;code&gt;$MDB_URI&lt;&#x2F;code&gt; specifies the cluster (and database) that the data stored in &lt;code&gt;recipe_data.json&lt;&#x2F;code&gt; will be loaded into.
As long as I’ve set the &lt;code&gt;$MDB_URI&lt;&#x2F;code&gt; environment variable correctly,
then if I go to the directory that holds the Justfile and run the following,
it will run the mongoimport command and load the data into my MongoDB database.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;just&lt;&#x2F;span&gt;&lt;span&gt; import
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Back to that &lt;code&gt;$MDB_URI&lt;&#x2F;code&gt; variable.
Just has this great feature – it &lt;em&gt;automatically&lt;&#x2F;em&gt; loads environment variables from a &lt;code&gt;.env&lt;&#x2F;code&gt; file you put in the same directory.
So if I create a file called &lt;code&gt;.env&lt;&#x2F;code&gt; containing the following,
then when I run the &lt;code&gt;just&lt;&#x2F;code&gt; command, I’ll be able to refer to the &lt;code&gt;$MDB_URI&lt;&#x2F;code&gt; variable,
like I did in the &lt;code&gt;Justfile&lt;&#x2F;code&gt; above.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# .env
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MDB_URI&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;mongodb+srv:&#x2F;&#x2F;username:password@timeseries.abcde.mongodb.com&#x2F;cocktails&lt;&#x2F;span&gt;&lt;span&gt;?&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;retryWrites=true&amp;amp;w=majority
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The idea in this case is that local configuration,
like the connection string for connecting to a MongoDB cluster,
is stored in the &lt;code&gt;.env&lt;&#x2F;code&gt; file.
The &lt;code&gt;.env&lt;&#x2F;code&gt; file is not committed to revision control,
whereas the Justfile can be committed and then reused by other team members.&lt;&#x2F;p&gt;
&lt;p&gt;I’ll often have a handful of targets for setting up my database in any MongoDB project that I’m working on:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;# Justfile
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;# Recreate the database with sample_data.
&lt;&#x2F;span&gt;&lt;span&gt;all: init import
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;# Drop all the collections, and recreate them with associated indexes.
&lt;&#x2F;span&gt;&lt;span&gt;init:
&lt;&#x2F;span&gt;&lt;span&gt;    mongosh $MDB_URI init_database.js
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;# Import recipe and review data into the cluster at $MDB_URI.
&lt;&#x2F;span&gt;&lt;span&gt;import:
&lt;&#x2F;span&gt;&lt;span&gt;    # Load recipe_data.json into the recipes collection:
&lt;&#x2F;span&gt;&lt;span&gt;    mongoimport $MDB_URI --collection recipes recipe_data.json
&lt;&#x2F;span&gt;&lt;span&gt;    # Load review_data.json into the reviews collection:
&lt;&#x2F;span&gt;&lt;span&gt;    mongoimport $MDB_URI --collection reviews review_data.json
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let’s break this down:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Lines beginning with &lt;code&gt;#&lt;&#x2F;code&gt; are comments. They’re (mostly) ignored by Just.&lt;&#x2F;li&gt;
&lt;li&gt;The first target is the default target, so it will be run if you execute &lt;code&gt;just&lt;&#x2F;code&gt; or &lt;code&gt;just all&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;The items after &lt;code&gt;all: &lt;&#x2F;code&gt; are the targets this target depends upon.
They’ll be executed before any commands associated with the &lt;code&gt;all&lt;&#x2F;code&gt; target.
(All doesn’t have any commands associated with it. It’s just there to collect together the other targets and run them in order.)
In this case, executing the &lt;code&gt;all&lt;&#x2F;code&gt; target will first run the &lt;code&gt;init&lt;&#x2F;code&gt; target, and then the &lt;code&gt;import&lt;&#x2F;code&gt; target.&lt;&#x2F;li&gt;
&lt;li&gt;A target is often followed by a list of commands to be run when the target is executed.
The commands should be indented,
but it doesn’t matter if they’re indented by tabs or spaces
(unlike Make, which is notoriously picky and weird!).
Those commands will be run in order when Just executes the target, and if any of them fail, Just will exit with an error.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So with this combination of &lt;code&gt;Justfile&lt;&#x2F;code&gt; and &lt;code&gt;.env&lt;&#x2F;code&gt; files, I’ve gained two things:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;I’ve got a quick and easy way to run commands related to my project.&lt;&#x2F;li&gt;
&lt;li&gt;I’ve got &lt;em&gt;documented&lt;&#x2F;em&gt; commands related to my project, for easy reference.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Speaking of documentation,
now that I added some comments to my Justfile,
if I run &lt;code&gt;just --list&lt;&#x2F;code&gt; I get the following output:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;❯ just --list
&lt;&#x2F;span&gt;&lt;span&gt;Available recipes:
&lt;&#x2F;span&gt;&lt;span&gt;    all    # Recreate the database with sample_data.
&lt;&#x2F;span&gt;&lt;span&gt;    import # Import recipe and review data into the cluster at $MDB_URI.
&lt;&#x2F;span&gt;&lt;span&gt;    init   # Drop all the collections, and recreate them with associated indexes.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It prints out each target, along with the comment above each one.
Self-documenting code!
I ❤ it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-don-t-you-use-make&quot;&gt;Why Don’t You Use Make?&lt;&#x2F;h2&gt;
&lt;p&gt;I used to use Makefiles for this kind of thing in the past.
I continued to use Make even after I became aware of Just;
I couldn’t quite see the benefit of using a less ubiquitous tool.
But since I switched I’ve become a total convert, mostly for the following reasons:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The syntax is much less weird and tricky than Make.
(Make differentiates between indentation by tabs and spaces,
and you end up using both in the same file for different reasons!)&lt;&#x2F;li&gt;
&lt;li&gt;There’s no need to declare a &lt;code&gt;.PHONY&lt;&#x2F;code&gt; target.&lt;&#x2F;li&gt;
&lt;li&gt;Just automatically loads variables from a &lt;code&gt;.env&lt;&#x2F;code&gt; file, if one is available.&lt;&#x2F;li&gt;
&lt;li&gt;Just allows you to pass arguments to a command on the command-line.&lt;&#x2F;li&gt;
&lt;li&gt;Just allows you to write commands in any language you like, not just shell!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;some-useful-snippets&quot;&gt;Some Useful Snippets&lt;&#x2F;h2&gt;
&lt;p&gt;Here are a few useful targets that I’ve found myself using across different projects:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;connect-to-your-database&quot;&gt;Connect To Your Database&lt;&#x2F;h3&gt;
&lt;p&gt;I have the following short recipe in every Justfile!
It runs mongosh against the &lt;code&gt;MDB_URI&lt;&#x2F;code&gt; that’s stored in the local &lt;code&gt;.env&lt;&#x2F;code&gt; configuration,
so you’ll get an interactive connection to your database you can use to run ad-hoc commands against your data.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;# Connect to the configured database
&lt;&#x2F;span&gt;&lt;span&gt;connect:
&lt;&#x2F;span&gt;&lt;span&gt;    mongosh $MDB_URI
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;initialize-your-database-for-development&quot;&gt;Initialize Your Database For Development&lt;&#x2F;h3&gt;
&lt;p&gt;I usually have a JavaScript file that can be run against a new database, with mongosh, 
to initialize collections and indexes in a reproduceable way.
An example init script looks like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; init.js
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Drop the collection if it already exists:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;db&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;scores&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;drop&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Create a unique index on &amp;quot;username&amp;quot;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;db&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;scores&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;createIndex&lt;&#x2F;span&gt;&lt;span&gt;({ &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;}, { unique: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Drop the collection if it already exists:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;db&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;stock_exchange_data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;drop&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Time series collections need to be created ahead-of-time.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Have you tried MongoDB&amp;#39;s new time series collections? They&amp;#39;re awesome!
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;db&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;createCollection&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;stock_exchange_data&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, {
&lt;&#x2F;span&gt;&lt;span&gt;    timeseries: {
&lt;&#x2F;span&gt;&lt;span&gt;        timeField: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;ts&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;        metaField: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;source&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;        granularity: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hours&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then I’ll set up an “init” recipe to run it:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;# Initialize the configured database
&lt;&#x2F;span&gt;&lt;span&gt;init:
&lt;&#x2F;span&gt;&lt;span&gt;    mongosh $MDB_URI init.js
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now I can run &lt;code&gt;just init&lt;&#x2F;code&gt; to run the “init.js” script against my database.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-parameterized-target-to-run-mongosh-scripts&quot;&gt;A Parameterized Target To Run Mongosh Scripts&lt;&#x2F;h3&gt;
&lt;p&gt;If I have a bunch of scripts I may want to run against my database,
I’ll add a “run” recipe that can run any of the scripts.&lt;&#x2F;p&gt;
&lt;p&gt;The following target makes use of &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;casey&#x2F;just#recipe-parameters&quot;&gt;recipe parameters&lt;&#x2F;a&gt;.
In the example below,
“script” is a variable that is passed on the command-line,
and then referred to using &lt;code&gt;{{script}}&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;# Run a script on the configured database
&lt;&#x2F;span&gt;&lt;span&gt;run script:
&lt;&#x2F;span&gt;&lt;span&gt;    mongosh $MDB_URI {{script}}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With the recipe above in your Justfile,
you can execute a mongosh script called “create_view.js” like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;just&lt;&#x2F;span&gt;&lt;span&gt; run create_view.js
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;other-features&quot;&gt;Other Features&lt;&#x2F;h2&gt;
&lt;p&gt;The documentation for Just is very long – a good indication of quite how many features it has!
Nevertheless, it remains relatively straightforward to get up and running.
Some features I’ve found useful:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You can write &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;casey&#x2F;just#writing-recipes-in-other-languages&quot;&gt;recipes in other languages&lt;&#x2F;a&gt;,
like Python or JavaScript.&lt;&#x2F;li&gt;
&lt;li&gt;Create a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;casey&#x2F;just#user-justfiles&quot;&gt;User Justfile&lt;&#x2F;a&gt; that contains recipes you want to be available everywhere.&lt;&#x2F;li&gt;
&lt;li&gt;Just will run, without any dependencies on Linux, MacOS, and Windows.
(Although you will need a shell, if you don’t have a version of &lt;code&gt;sh&lt;&#x2F;code&gt; installed.).
It even includes some handy &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;casey&#x2F;just#path-manipulation&quot;&gt;path manipulation&lt;&#x2F;a&gt; functions for cross-platform path manipulation.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There are a bunch more features.
I recommend you scan &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;casey&#x2F;just&quot;&gt;the documentation&lt;&#x2F;a&gt; to get a better picture of all it can do.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;in-conclusion&quot;&gt;In Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;I’ve seen a bunch of benefits since I started to add Justfiles to all my projects.
It speeds up my day-to-day operations when I’m working with MongoDB (and other tools).
When I come back to a project after a while,
I can see the commands needed to create and work with the database,
by running &lt;code&gt;just --list&lt;&#x2F;code&gt; or just scanning the Justfile.
The ability to document each recipe and easily list them is super-helpful,
and it also provides a way to help others work with the projects I’ve worked on.&lt;&#x2F;p&gt;
&lt;p&gt;The examples in this post are relatively straightforward,
but Just really comes into its own when you want a recipe to run multiple commands,
each with their own list of flags that you would otherwise have to remember.
They’re an excellent alternative to writing shell scripts!&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Automating Blog Deployment with GitHub Actions</title>
        <published>2021-03-12T00:00:00+00:00</published>
        <updated>2021-03-12T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://www.judy.co.uk/blog/second-blog-post/" type="text/html"/>
        <id>https://www.judy.co.uk/blog/second-blog-post/</id>
        <content type="html">&lt;p&gt;Okay! Day 2 of the new blogging platform, and I’ve solved the automated deployment problem using GitHub Actions.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;h1 id=&quot;github-actions-to-test-python&quot;&gt;GitHub Actions to Test Python&lt;&#x2F;h1&gt;
&lt;p&gt;I worked on a small Python project a little while ago and wanted to get CI running with as little effort as possible. I’d heard good things about GitHub Actions (my friend &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;mheap&quot;&gt;Michael Heap&lt;&#x2F;a&gt; is even writing a &lt;a href=&quot;https:&#x2F;&#x2F;actionsbook.com&#x2F;?utm_source=judy2k&quot;&gt;book&lt;&#x2F;a&gt; about them).&lt;&#x2F;p&gt;
&lt;p&gt;That project’s CI issues were solved by following the &lt;a href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;actions&#x2F;guides&#x2F;building-and-testing-python&quot;&gt;instructions&lt;&#x2F;a&gt;. I copied the following YAML into &lt;code&gt;.github&#x2F;worflows&#x2F;main.yml&lt;&#x2F;code&gt; and I was more-or-less done.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Python package
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;jobs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;build&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;strategy&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;matrix&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;python-version&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2.7&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3.5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3.6&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3.7&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3.8&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;actions&#x2F;checkout@v2
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Set up Python ${{ matrix.python-version }}
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;actions&#x2F;setup-python@v2
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;python-version&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;${{ matrix.python-version }}
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Install dependencies
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        python -m pip install --upgrade pip
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        pip install flake8 pytest
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Lint with flake8
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        # stop the build if there are Python syntax errors or undefined names
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Test with pytest
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        pytest
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h1 id=&quot;porting-the-knowledge-to-deployment&quot;&gt;Porting the Knowledge to Deployment&lt;&#x2F;h1&gt;
&lt;p&gt;What I wanted to do this time was quite similar. I wanted to build the site’s static pages, and then, &lt;em&gt;if on the master branch&lt;&#x2F;em&gt; copy the files to my personal server, using SCP.&lt;&#x2F;p&gt;
&lt;p&gt;I found an existing &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;shalzz&#x2F;zola-deploy-action&quot;&gt;Zola deploy action&lt;&#x2F;a&gt; that builds a Zola site, and then deploys to GitHub Pages, but that wasn’t quite what I wanted. Nevertheless, it seemed like a good starting point, so I forked it, and then started hacking on it.&lt;&#x2F;p&gt;
&lt;p&gt;And then I had an epiphany! I already had steps that checked out the code and built it into a static site. All I needed was another small, specific &lt;code&gt;step&lt;&#x2F;code&gt; to SCP to my server. A quick search on GitHub’s &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;marketplace?type=actions&quot;&gt;Action Marketplace&lt;&#x2F;a&gt; showed up the perfect action, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;appleboy&#x2F;scp-action&quot;&gt;appleboy&#x2F;scp-action&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So, now I have the following workflow:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Build and deploy
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;branches&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;master
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;pull_request&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;jobs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;build&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Checkout&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;actions&#x2F;checkout@master
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Build only&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39; 
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;shalzz&#x2F;zola-deploy-action@master
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;env&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BUILD_ONLY&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;deploy&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;github.ref == &amp;#39;refs&#x2F;heads&#x2F;master&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;needs&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;build
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Checkout&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;actions&#x2F;checkout@master
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Build Site&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39; 
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;shalzz&#x2F;zola-deploy-action@master
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;env&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BUILD_ONLY&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Deploy Site
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;appleboy&#x2F;scp-action@master
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;host&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;${{ secrets.HOST }}
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;${{ secrets.USERNAME }}
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;${{ secrets.KEY }}
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;source&lt;&#x2F;span&gt;&lt;span&gt;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;public&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;target&lt;&#x2F;span&gt;&lt;span&gt;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;var&#x2F;lib&#x2F;websites&#x2F;judy.co.uk&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;strip_components&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It’ll build on any push to master, or any new PR. If the commit is to master, then it’ll also deploy to my server using SCP. Using a small set of focused actions (&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Unix_philosophy&quot;&gt;Unix philosophy&lt;&#x2F;a&gt;, anyone?) has led me to something much more simple and maintainable than maintaining my own “build and deploy and …” action.&lt;&#x2F;p&gt;
&lt;p&gt;One thing I’ve noticed is that it’s building the Docker images each time, before spinning up the containers. I haven’t found a way to speed things up by getting around this, but if you know, please &lt;a href=&quot;&#x2F;about&quot;&gt;contact me&lt;&#x2F;a&gt; and let me know!&lt;&#x2F;p&gt;
&lt;p&gt;I have to say that it’s nice when a task that shouldn’t be hard turns out to not actually be hard.
Now I’ve reduced the friction for publishing to my blog, so if you come here in 2025 and this is the last post then there’s nobody else to blame.&lt;&#x2F;p&gt;
&lt;p&gt;I think it’s time for a Negroni.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>The Traditional First Post</title>
        <published>2021-03-11T00:00:00+00:00</published>
        <updated>2021-03-11T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://www.judy.co.uk/blog/first-blog-post/" type="text/html"/>
        <id>https://www.judy.co.uk/blog/first-blog-post/</id>
        <content type="html">&lt;p&gt;I guess it’s traditional to write a first blog post about the new blogging platform.
Tradition, right? Not cliché?&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;h2 id=&quot;my-new-blogging-platform&quot;&gt;My New Blogging Platform&lt;&#x2F;h2&gt;
&lt;p&gt;My main driver this time, moving to &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt;,
has been to simplify the build and deployment process.
I’m halfway there.&lt;&#x2F;p&gt;
&lt;p&gt;So that’s the half that’s &lt;em&gt;done&lt;&#x2F;em&gt;.
The second half, automatic &lt;em&gt;deployment&lt;&#x2F;em&gt;, is a little more complex, and not implemented yet.
But it should be easier, given the availability of the Docker image I mentioned above.&lt;&#x2F;p&gt;
&lt;p&gt;In general, getting the blog set up has been straightforward.
Everything came “out of the box”.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-zola&quot;&gt;What’s Zola?&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt; is a static site generator,
a bit like &lt;a href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;&quot;&gt;Hugo&lt;&#x2F;a&gt;,
and a &lt;a href=&quot;https:&#x2F;&#x2F;www.gatsbyjs.com&#x2F;&quot;&gt;whole&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;nextjs.org&#x2F;&quot;&gt;host&lt;&#x2F;a&gt; of &lt;a href=&quot;https:&#x2F;&#x2F;jekyllrb.com&#x2F;&quot;&gt;others&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;My criteria was that it would be:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Reasonably fast, offer live preview,&lt;&#x2F;li&gt;
&lt;li&gt;Run from a Docker container (so I can automate deployment from a GitHub repo).&lt;&#x2F;li&gt;
&lt;li&gt;It also had to be quick to set up. Otherwise my goldfish attention span will stop me from deploying &lt;em&gt;anything&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Honestly, my guilty secret is the main reason I checked out Zola was because it was written in Rust.
I found &lt;a href=&quot;https:&#x2F;&#x2F;blog.logrocket.com&#x2F;top-3-rust-static-site-generators-and-when-to-use-them&#x2F;&quot;&gt;this comparison&lt;&#x2F;a&gt; of various Rust-developed static site generators helpful.&lt;&#x2F;p&gt;
&lt;p&gt;Zola’s great!
It’s flexible enough for my requirements - I wanted a listing of talks I’ve given as well as blog posts.
The template language, &lt;a href=&quot;https:&#x2F;&#x2F;tera.netlify.app&#x2F;&quot;&gt;Tera&lt;&#x2F;a&gt; is inspired by Jinja2 &amp;amp; Django, two of my favourite templating engines.
(Did you know that Jinja came out of a project to &lt;a href=&quot;https:&#x2F;&#x2F;reinout.vanrees.org&#x2F;weblog&#x2F;2014&#x2F;11&#x2F;14&#x2F;6python-templating.html&quot;&gt;speed up Django templates?&lt;&#x2F;a&gt;)
I quickly found a theme that suited my purposes, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bennetthardwick&#x2F;simple-dev-blog-zola-starter&quot;&gt;simple-dev-blog&lt;&#x2F;a&gt;,
which got me off to a good start,
although I’ll probably tinker with it some more over time.&lt;&#x2F;p&gt;
&lt;p&gt;The new system is built from a Docker image.
With my old, Hugo-based, system, I attempted to Dockerize the build environment,
and while I learned a lot about sticking command-line tools in Docker containers,
I never quite got it up and running.
This was a while ago.
Things may be better now.
The real driver for moving away from Hugo was the templating system,
which I learned to hate with every fibre of my being.
With Zola, I was pleased to discover that a Docker distribution is already available.&lt;&#x2F;p&gt;
&lt;p&gt;I have a certain amount of faith that Zola is powerful enough for my requirements - I’ve already set up templates to show YouTube video previews in the &lt;a href=&quot;&#x2F;talks&quot;&gt;talk listings&lt;&#x2F;a&gt;.
The next things to set up are a listing of upcoming events I’m at,
and a listing of projects that I want to show off.
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;judy2k&#x2F;stupid-python-tricks&quot;&gt;Stupid Python Tricks&lt;&#x2F;a&gt; anyone?&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Getting Started with MongoDB &amp; Rust</title>
        <published>2020-12-14T00:00:00+00:00</published>
        <updated>2020-12-14T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://www.judy.co.uk/talks/getting-started-rust-and-mongodb/" type="text/html"/>
        <id>https://www.judy.co.uk/talks/getting-started-rust-and-mongodb/</id>
        <content type="html">&lt;p&gt;MongoDB is probably the world’s most popular document database. &lt;&#x2F;p&gt;
&lt;p&gt;It may not be well known, but MongoDB released their first driver for Rust back in 2013. Since then regular driver updates have been released, supporting new database features and adding improvements. This year, the driver was rewritten, adding support for Tokio &amp;amp; Async-Std, and is now officially supported. So maybe it’s time to give it a try! &lt;&#x2F;p&gt;
&lt;p&gt;This talk will give an overview of the features of MongoDB, including some features that many think MongoDB doesn’t support, including transactions and joins. I’ll cover:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;What MongoDB is (and isn’t); &lt;&#x2F;li&gt;
&lt;li&gt;The basics of running a MongoDB cluster; &lt;&#x2F;li&gt;
&lt;li&gt;How to effectively use the document model; &lt;&#x2F;li&gt;
&lt;li&gt;Transactions; &lt;&#x2F;li&gt;
&lt;li&gt;Aggregation queries, including joins. &lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;And I’ll show how to use the Rust driver to make use of these features, and cover the areas the Rust driver does not yet support. I’ll also cover some of MongoDB’s lesser-known features, such as GIS data types and queries, and time series queries, at a high level. &lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Pyjamas Conference Advocacy Panel</title>
        <published>2020-12-05T00:00:00+00:00</published>
        <updated>2020-12-05T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://www.judy.co.uk/talks/pyjamas-advocacy-panel/" type="text/html"/>
        <id>https://www.judy.co.uk/talks/pyjamas-advocacy-panel/</id>
        <content type="html">&lt;p&gt;I got to be on a panel with &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;ixek&quot;&gt;Tania Allard&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;Just as good, the panel was run by &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;lais_bsc&quot;&gt;Lais Carvalho&lt;&#x2F;a&gt;.
This panel, arranged at the last minute to fill a slot in the schedule was super-fun for me, at least.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Stupid Things I&#x27;ve Done With Python</title>
        <published>2020-12-05T00:00:00+00:00</published>
        <updated>2020-12-05T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://www.judy.co.uk/talks/stupid-things-python/" type="text/html"/>
        <id>https://www.judy.co.uk/talks/stupid-things-python/</id>
        <content type="html">&lt;p&gt;Sometimes I do stupid things with Python.
I’m not proud of it.&lt;&#x2F;p&gt;
&lt;p&gt;Okay, I &lt;em&gt;am&lt;&#x2F;em&gt; proud of it.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;And I’m going to teach you some of my techniques.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>How to Get On This Stage (and What To Do When You Get There)</title>
        <published>2020-09-18T00:00:00+00:00</published>
        <updated>2020-09-18T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://www.judy.co.uk/talks/how-to-get-on-this-stage-djangocon-eu/" type="text/html"/>
        <id>https://www.judy.co.uk/talks/how-to-get-on-this-stage-djangocon-eu/</id>
        <content type="html">&lt;p&gt;It’s my first ever DjangoCon Europe talk!&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;I’m super proud of this talk.
Applying to speak at a conference,
and then preparing to actually give the talk
(and dealing with rejection) can be frustrating.
I’ve been on the talk selection committee for a few conferences, 
and I wanted to pass on the things I’ve learned about good proposals,
as well as just demystifying the whole process for new speakers.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Everything You Know About MongoDB is Wrong!</title>
        <published>2020-07-25T00:00:00+00:00</published>
        <updated>2020-07-25T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://www.judy.co.uk/talks/everything-you-know-about-mongodb-is-wrong/" type="text/html"/>
        <id>https://www.judy.co.uk/talks/everything-you-know-about-mongodb-is-wrong/</id>
        <content type="html">&lt;p&gt;This 10 minute talk attempts to list and counteract the eight most common myths about MongoDB.&lt;&#x2F;p&gt;
&lt;p&gt;If you didn’t know that MongoDB was a secure, robust, ACID-compliant, transactional, relational database, then you should probably watch this talk.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>It&#x27;s Pythons All The Way Down!</title>
        <published>2019-08-04T00:00:00+00:00</published>
        <updated>2019-08-04T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://www.judy.co.uk/talks/pythons-all-the-way-metaclasses/" type="text/html"/>
        <id>https://www.judy.co.uk/talks/pythons-all-the-way-metaclasses/</id>
        <content type="html">&lt;p&gt;A deep dive into how inheritance and metaclasses work in Python.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;I was quite nervous about this talk,
partly because it was 70 minutes long(!),
and partly because it’s the most advanced topic I’ve ever spoken about.&lt;&#x2F;p&gt;
&lt;p&gt;In the end I was super-pleased with the results.
As I was researching how types and metaclasses work in Python,
I had a couple of epiphanies which I felt really helped make the subject understandable.&lt;&#x2F;p&gt;
&lt;p&gt;When I got home (from Australia!) I discovered my wife had made notes and had a bunch of questions,
which is the most definite sign I’ve ever had that she found the talk interesting.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Publish a Perfect Python Package</title>
        <published>2019-07-10T00:00:00+00:00</published>
        <updated>2019-07-10T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://www.judy.co.uk/talks/publish-a-perfect-python-package/" type="text/html"/>
        <id>https://www.judy.co.uk/talks/publish-a-perfect-python-package/</id>
        <content type="html">&lt;p&gt;An opinionated guide to building and distributing high-quality Python packages.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;&lt;strong&gt;Note that there’s no audio for the first 30 seconds.&lt;&#x2F;strong&gt;
It’s no great loss.&lt;&#x2F;p&gt;
&lt;p&gt;I’ve never been super-happy with the delivery of this talk.
I felt the delivery was a little rough around the edges,
and I had some friends in the audience who were enthusiastically distracting me
(in a nice way)
which meant that I dropped some in-jokes that probably went over the heads of everybody else in the audience.&lt;&#x2F;p&gt;
&lt;p&gt;All the same, the content of the talk is super-practical,
and this talk is one reason I was encouraged to come and work at MongoDB,
so maybe I should give the talk some slack.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>More Than you Ever Wanted to Know about Python Functions</title>
        <published>2018-07-26T00:00:00+00:00</published>
        <updated>2018-07-26T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://www.judy.co.uk/talks/everything-about-python-functions/" type="text/html"/>
        <id>https://www.judy.co.uk/talks/everything-about-python-functions/</id>
        <content type="html">&lt;p&gt;What exactly are functions? Let’s talk about functions, methods, callables and closures - what they are, what you can give them, what they can give you, what you can do with them … and what’s inside.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;You probably think you already know everything about functions, but you probably don’t!&lt;&#x2F;p&gt;
&lt;p&gt;Input &amp;amp; Output: How do you get things in and out of functions? I’ll cover parameters and the myriad of ways they can be specified, provided and accessed - including helpful hints to avoid common mistakes! I’ll cover return values, briefly, along with variable scopes and exceptions.&lt;&#x2F;p&gt;
&lt;p&gt;Closures: What are they, how do they work and how they can affect memory usage.&lt;&#x2F;p&gt;
&lt;p&gt;Methods: How does a method differ from a function, when are they made, how do they work and how to access the function inside every method.&lt;&#x2F;p&gt;
&lt;p&gt;__magic__: Make your own callables from any object!&lt;&#x2F;p&gt;
&lt;p&gt;Introspection: Using modern Python techniques, what can you find out about a function, and what can you do with that information?&lt;&#x2F;p&gt;
&lt;p&gt;Bytecode: What happens if you open up a function and look at its insides? Can you change it and put it back together again? (Spoiler: Yes, you can.)&lt;&#x2F;p&gt;
&lt;p&gt;By the end of this talk, I guarantee* you’ll know more about callables than when you walked in, along with techniques both practical and so extreme your colleagues will never let you merge them to master.&lt;&#x2F;p&gt;
&lt;p&gt;(*This guarantee is legally non-binding and cannot be redeemed in any way.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;notes&quot;&gt;Notes&lt;&#x2F;h2&gt;
&lt;p&gt;This talk was given in Edinburgh, my home town!
It was a really large audience, and a great venue, and I had a blast
… but I also ran out of time and had to rush the end of the talk - which is the most difficult bit!&lt;&#x2F;p&gt;
&lt;p&gt;I gave the talk again at PyCon Ireland, but then had massive A&#x2F;V issues,
so I don’t really have a better version to link to online.
Maybe one day I’ll manage to give a great version of this talk in full.
Until then, this is what you’ve got.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Pythonic Refactoring: Protecting Your Users From Change</title>
        <published>2017-07-14T00:00:00+00:00</published>
        <updated>2017-07-14T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://www.judy.co.uk/talks/pythonic-refactoring-protecting-your-users-from-change/" type="text/html"/>
        <id>https://www.judy.co.uk/talks/pythonic-refactoring-protecting-your-users-from-change/</id>
        <content type="html">&lt;p&gt;So you’ve released a library! Now you need to maintain it. You want to add features, restructure the code, fix bugs, and maybe improve the library’s usability. Your users just want their code to carry on working. That’s okay!&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;This talk will cover techniques in both code and project management to allow you to keep your code moving forwards without breaking your users’ code. It is aimed at developers with a little experience of writing libraries in Python, and will cover some intermediate subjects like function decorators and magic methods.&lt;&#x2F;p&gt;
&lt;p&gt;Refactoring in Python is a mixed bag - on the one hand you have powerful tools like the @property decorator, &lt;strong&gt;dunder&lt;&#x2F;strong&gt; methods, and even metaclasses. On the other hand, because Python code has no concept of private or protected like some other languages, it can be difficult to know what your public interface even is.&lt;&#x2F;p&gt;
&lt;p&gt;I’ll talk about how to identify your public interface, and make that clear to your users. I’ll cover how to structure your tests so you know when you’ve broken your public interface. I’ll discuss how to use some of Python’s language features to trick your users into thinking your code hasn’t changed at all (except for those brilliant new features you’ve just added!). And finally, I’ll cover how you know it’s time to break backwards compatibility and how to break it to your users.&lt;&#x2F;p&gt;
</content>
    </entry>
</feed>
