Andrew's Blog Vim, Rust, Ruby, and assorted tech. https://andrewra.dev/ Thu, 04 Dec 2025 13:28:08 +0100 Thu, 04 Dec 2025 13:28:08 +0100 Jekyll v4.3.3 Sending Build Output to Vim <p>Vim is actually quite easy to build from source with a <code class="language-plaintext highlighter-rouge">make install</code>. You can edit <code class="language-plaintext highlighter-rouge">src/Makefile</code> to enable features and custom language extensions. Turns out, I hadn’t scrolled through that file in a while, since version <a href="https://github.com/vim/vim/commit/e42a6d250907e278707753d7d1ba91ffc2471db0">8.0.1295</a> comes with an interesting addition – the compilation flag <code class="language-plaintext highlighter-rouge">--enable-autoservername</code>.</p> <p>Running the editor with a <code class="language-plaintext highlighter-rouge">--servername</code> parameter allows remote connections from a different instance using command-line flags or function calls. I’ve been using this functionality to build a <a href="https://github.com/AndrewRadev/vimrunner">custom testing framework</a> in Ruby, but you have to knowingly launch it with that flag. Now, each instance can be available to connect with by default.</p> <p>It’s not <em>necessary</em>, exactly – you could always make yourself a shell alias like <code class="language-plaintext highlighter-rouge">alias vim="vim --servername VIM-$RANDOM"</code>. But the discovery gave me an idea of how to remove a tiny bit of friction from my Rust workflow, and I might end up using the same technique in the future. Read on to learn how to send your build output to a Vim instance for easier processing.</p> <!-- more --> <h2 id="but-why"><a class="anchor" aria-hidden="true" href="#but-why">🔗</a>But why?</h2> <p>When using a large IDE, or even something smaller like VSCode, I think there’s a tendency to keep one instance running and open things in it. In fact, on my machine, running <code class="language-plaintext highlighter-rouge">code &lt;some-file&gt;</code> in the terminal opens the code in an existing instance, even though you <em>can</em> launch multiple ones.</p> <p>Vim is a lot more decoupled than that. You <em>can</em> have one big Vim open holding tabs, terminal windows, git commands and a lot more. But it’s just one possible workflow and somewhat counter to what you get from the defaults. Personally, I tend to have one Vim focused on the code I’m currently working on, but I might also open other ones with notes or other projects to <del>copy-paste from</del> use as inspiration.</p> <p>I also use separate terminals for running commands – tests, git, etc. I respect the flexibility of <code class="language-plaintext highlighter-rouge">:terminal</code>, but I guess I’m just too used to jumping between workspaces and I like keeping things separate. But this does make it slightly harder to debug builds. Usually, when Rust shows me compile errors or test failures, I’d double-click their locations and paste them into Vim. A plugin like <a href="https://github.com/wsdjeg/vim-fetch">vim-fetch</a> helps a lot with this kind of thing.</p> <p>It works, it’s fine, I’m used to it. But if I <em>could</em> directly send the errors over to my editor…</p> <h2 id="finding-the-right-instance"><a class="anchor" aria-hidden="true" href="#finding-the-right-instance">🔗</a>Finding the right instance</h2> <p>First, let’s decide where to send the output. There might be multiple instances, but I’m looking for the one in the same directory I’m running the command in:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Let's get a list of all available instances:</span> <span class="n">vim_instances</span> <span class="o">=</span> <span class="sb">`vim --serverlist`</span><span class="p">.</span><span class="nf">lines</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:strip</span><span class="p">)</span> <span class="n">cwd</span> <span class="o">=</span> <span class="no">FileUtils</span><span class="p">.</span><span class="nf">pwd</span> <span class="n">servername</span> <span class="o">=</span> <span class="n">vim_instances</span><span class="p">.</span><span class="nf">find</span> <span class="k">do</span> <span class="o">|</span><span class="n">candidate</span><span class="o">|</span> <span class="c1"># Use --remote-expr to get the results of `getcwd()` from each of the Vims:</span> <span class="n">candidate_wd</span> <span class="o">=</span> <span class="sb">`vim --servername </span><span class="si">#{</span><span class="n">candidate</span><span class="si">}</span><span class="sb"> --remote-expr 'getcwd()'`</span><span class="p">.</span><span class="nf">strip</span> <span class="c1"># The one we're looking for should have a current working directory that's</span> <span class="c1"># somewhere under the command's working directory:</span> <span class="k">if</span> <span class="n">candidate_wd</span><span class="p">.</span><span class="nf">start_with?</span><span class="p">(</span><span class="n">cwd</span><span class="p">)</span> <span class="n">candidate</span> <span class="k">else</span> <span class="kp">nil</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>The invocation to send commands is extractable, although I don’t usually need the output. Here’s what it might look like if we pulled it out to a function without capturing the output:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">send_to_vim</span><span class="p">(</span><span class="n">servername</span><span class="p">,</span> <span class="n">command</span><span class="p">)</span> <span class="nb">system</span> <span class="s1">'vim'</span><span class="p">,</span> <span class="s1">'--servername'</span><span class="p">,</span> <span class="n">servername</span><span class="p">,</span> <span class="s1">'--remote-expr'</span><span class="p">,</span> <span class="n">command</span><span class="p">,</span> <span class="ss">out: </span><span class="s1">'/dev/null'</span> <span class="k">end</span> </code></pre></div></div> <p>This one is easier to manage in terms of escaping and quotes, too, since the <code class="language-plaintext highlighter-rouge">system</code> call takes care of quoting the individual arguments properly.</p> <h2 id="running-the-cargo-command"><a class="anchor" aria-hidden="true" href="#running-the-cargo-command">🔗</a>Running the cargo command</h2> <p>The <code class="language-plaintext highlighter-rouge">cargo</code> tool is extensible in the same way <code class="language-plaintext highlighter-rouge">git</code> is: If you create a command in <code class="language-plaintext highlighter-rouge">$PATH</code> named <code class="language-plaintext highlighter-rouge">cargo-something</code>, it becomes available as <code class="language-plaintext highlighter-rouge">cargo something</code>. This means it’s a good idea to handle both invocations at the start of the program:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="no">ARGV</span><span class="p">.</span><span class="nf">first</span> <span class="o">==</span> <span class="s1">'vim'</span> <span class="c1"># then it was called as `cargo vim &lt;command&gt;` and not `cargo-vim &lt;command&gt;`,</span> <span class="c1"># so remove the "vim" part:</span> <span class="no">ARGV</span><span class="p">.</span><span class="nf">shift</span> <span class="k">end</span> <span class="k">if</span> <span class="no">ARGV</span><span class="p">.</span><span class="nf">count</span> <span class="o">&lt;</span> <span class="mi">1</span> <span class="no">STDERR</span><span class="p">.</span><span class="nf">puts</span> <span class="s2">"USAGE: cargo vim &lt;build|run|test&gt; [args...]"</span> <span class="nb">exit</span> <span class="mi">1</span> <span class="k">end</span> </code></pre></div></div> <p>But once we have that, <code class="language-plaintext highlighter-rouge">ARGV</code> contains everything we need to run the <em>real</em> cargo command and get its output. It would be nice if I could just get the output of the previous command. I imagine myself running <code class="language-plaintext highlighter-rouge">cargo build</code> once, having it fail, and then just go “oh, let’s just take this elsewhere”, but I couldn’t find a good way of doing this. Instead, re-running should be fast enough the second time around, this time into a file:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Tempfile</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="s1">'cargo-vim'</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="nb">print</span> <span class="s2">"Running cargo </span><span class="si">#{</span><span class="no">ARGV</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s1">' '</span><span class="p">)</span><span class="si">}</span><span class="s2"> ... "</span> <span class="c1"># The output from `cargo` goes in the standard error stream, so we want to</span> <span class="c1"># redirect that one to the temporary file. Stdout we don't care about much.</span> <span class="n">result</span> <span class="o">=</span> <span class="nb">system</span> <span class="s1">'cargo'</span><span class="p">,</span> <span class="o">*</span><span class="no">ARGV</span><span class="p">,</span> <span class="ss">err: </span><span class="n">f</span><span class="p">,</span> <span class="ss">out: </span><span class="s1">'/dev/null'</span> <span class="nb">puts</span> <span class="s2">"DONE"</span> <span class="c1"># ...</span> <span class="k">end</span> </code></pre></div></div> <p>In the <a href="https://github.com/AndrewRadev/scripts/blob/c352c9e5bb42adb4435282e851ae4e98e319bfd0/bin/cargo-vim">real code</a> I’ve also added some measurements and visual cues to get something slightly fancier:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% cargo vim build Running cargo build ... DONE 123.23ms 🗸 % cargo vim build Running cargo build ... DONE 348.68ms ❌ </code></pre></div></div> <p>But the core of the code is rerunning the same command into a tempfile. That file’s path can then be used to populate the quickfix list if the result is falsey:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Tempfile</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="s1">'cargo-vim'</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="c1"># ...</span> <span class="k">if</span> <span class="n">result</span> <span class="c1"># No errors, clear and close the quickfix list</span> <span class="n">send_to_vim</span><span class="p">(</span><span class="n">servername</span><span class="p">,</span> <span class="s2">"setqflist([])"</span><span class="p">)</span> <span class="n">send_to_vim</span><span class="p">(</span><span class="n">servername</span><span class="p">,</span> <span class="s2">"execute('cclose')"</span><span class="p">)</span> <span class="k">else</span> <span class="c1"># There's errors, populate the quickfix list</span> <span class="n">send_to_vim</span><span class="p">(</span><span class="n">servername</span><span class="p">,</span> <span class="s2">"execute('silent compiler cargo')"</span><span class="p">)</span> <span class="n">send_to_vim</span><span class="p">(</span><span class="n">servername</span><span class="p">,</span> <span class="s2">"execute('silent cfile </span><span class="si">#{</span><span class="n">f</span><span class="p">.</span><span class="nf">path</span><span class="si">}</span><span class="s2">')"</span><span class="p">)</span> <span class="n">send_to_vim</span><span class="p">(</span><span class="n">servername</span><span class="p">,</span> <span class="s2">"execute('silent copen')"</span><span class="p">)</span> <span class="nb">exit</span> <span class="mi">1</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>Vim can handle cargo build output, because the <a href="https://github.com/rust-lang/rust.vim">basic Rust configuration</a> has a “compiler” definition for it. Running <code class="language-plaintext highlighter-rouge">compiler cargo</code> is enough to tell Vim how to parse the output of the build process in a useful way. The built-in <code class="language-plaintext highlighter-rouge">cfile</code> command loads up the given file path into the quickfix window as an “error report”.</p> <p>The end result is a <code class="language-plaintext highlighter-rouge">cargo vim build</code> that produces something like this:</p> <p><img src="/images/cargo-quickfix.png" alt="Build output in the quickfix window" /></p> <h2 id="was-this-really-necessary"><a class="anchor" aria-hidden="true" href="#was-this-really-necessary">🔗</a>Was this really necessary?</h2> <p>You don’t need any of this if you’re fine with changing your mental patterns a little bit. You can easily have the “compiler” setup in <code class="language-plaintext highlighter-rouge">~/.vim/ftplugin/rust.vim</code>:</p> <div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">compiler</span> cargo <span class="k">set</span> <span class="nb">makeprg</span><span class="p">=</span>cargo\ test </code></pre></div></div> <p>That way, if you get a build error, you could just switch to Vim and run the built-in <code class="language-plaintext highlighter-rouge">:make</code> command. It’ll pretty much do the same thing.</p> <p>It could be argued my <code class="language-plaintext highlighter-rouge">cargo vim</code> command is slightly more convenient in case you build up a more complicated command line, like if you invoke <code class="language-plaintext highlighter-rouge">cargo test --test test_markdown</code>, you can just tweak that invocation to <code class="language-plaintext highlighter-rouge">cargo vim test etc etc</code> rather than copy the extra bits to append to <code class="language-plaintext highlighter-rouge">:make</code> in Vim. Or there might be ENV vars that change the build process. Honestly, though, I just don’t like having the “running” part in Vim due to some form of mental compartmentalization. You do you.</p> <p>What this kind of separate script <em>would</em> be nice for for is pre-processing of the error output before sending it over to Vim’s <code class="language-plaintext highlighter-rouge">compiler</code> setup. I might end up doing the same thing for RSpec, where importing test failures is trickier due to some tests being printed as <code class="language-plaintext highlighter-rouge">spec/test_name_spec.rb[1:2:3:4]</code>. That part at the end is an identifier for the test, based on where it is in the example hierarchy, and it’s not possible to translate it <em>directly</em> into a file:line location. I’ve got a <a href="https://github.com/AndrewRadev/scripts/blob/c352c9e5bb42adb4435282e851ae4e98e319bfd0/bin/rspec-translate">proof-of-concept script</a> that might give me that info to plug into a similar “send to Vim” tool. Plus, RSpec can record the last set of test failures in a file, so it might not even be necessary to re-run the suite.</p> <p>Either way, no, none of this is “necessary”, but it was a fun little exploration for me. And maybe you’ve learned a thing or two about Vim’s quickfix list and its client-server interface.</p> Mon, 01 Feb 2021 16:40:00 +0100 https://andrewra.dev/2021/02/01/sending-build-output-to-vim/ https://andrewra.dev/2021/02/01/sending-build-output-to-vim/ vim rust ruby From Vim to Presentation Slides <p>There’s a little-known Vim command called <a href="https://vimhelp.org/syntax.txt.html#convert-to-HTML"><code class="language-plaintext highlighter-rouge">:TOhtml</code></a> that does something quite surprising for a built-in – it converts the current Vim window into raw HTML. This includes syntax highlighting for code, and even line numbers. It can convert the full buffer or a range of selected lines.</p> <p>While I’ve always considered it a fun trick, it was somewhat recently that I realized it has a very practical use – to paste syntax-highlighted code into presentation slides.</p> <!-- more --> <h2 id="the-process"><a class="anchor" aria-hidden="true" href="#the-process">🔗</a>The Process</h2> <p>First off, HTML is a good start, but it needs to be parsed as Rich Text Format:</p> <div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">" Main entry point, the :Tortf command, translates the given range as (start,</span> <span class="c">" end) parameters for the function that does the real work. Defaults to the</span> <span class="c">" whole buffer.</span> command<span class="p">!</span> <span class="p">-</span><span class="nb">range</span><span class="p">=</span>% Tortf <span class="k">call</span> <span class="nv">s:Tortf</span><span class="p">(&lt;</span>line1<span class="p">&gt;,</span> <span class="p">&lt;</span>line2<span class="p">&gt;)</span> <span class="k">function</span><span class="p">!</span> <span class="nv">s:Tortf</span><span class="p">(</span>start<span class="p">,</span> end<span class="p">)</span> <span class="c">" We need a temporary file to actually write the HTML to. The :TOhtml</span> <span class="c">" command opens a buffer, but doesn't save it:</span> <span class="c">"</span> <span class="k">let</span> filename <span class="p">=</span> <span class="nb">tempname</span><span class="p">().</span><span class="s1">'.html'</span> <span class="c">" We run the command with the given range and save it to the temporary file:</span> <span class="c">"</span> exe <span class="nv">a:start</span><span class="p">.</span><span class="s1">','</span><span class="p">.</span><span class="nv">a:end</span><span class="p">.</span><span class="s1">'TOhtml'</span> exe <span class="s1">'write '</span><span class="p">.</span>filename <span class="c">" The `libreoffice` command will figure out how to open an HTML file -- with</span> <span class="c">" the "writer" tool:</span> <span class="c">"</span> <span class="k">call</span> <span class="nb">system</span><span class="p">(</span><span class="s1">'libreoffice '</span><span class="p">.</span>filename<span class="p">.</span><span class="s1">' &amp;'</span><span class="p">)</span> <span class="c">" No need to stay in this buffer -- it's done its job, so we can safely get</span> <span class="c">" rid of it:</span> <span class="k">quit</span><span class="p">!</span> <span class="k">endfunction</span> </code></pre></div></div> <p>This command-function pair can live in your <code class="language-plaintext highlighter-rouge">.vimrc</code> or in a file in the <code class="language-plaintext highlighter-rouge">~/.vim/plugin</code> directory. Once I’ve set an appropriate light scheme, ran the <code class="language-plaintext highlighter-rouge">:gui</code> command to ensure I’ve got the right colors, I can execute the <code class="language-plaintext highlighter-rouge">:Tortf</code> command and get something like this:</p> <p><img src="/images/to_rtf.png" alt="Code in libreoffice" /></p> <p>Now I can copy that code and try to paste it in a LibreOffice Impress presentation. Instead of just using <code class="language-plaintext highlighter-rouge">Ctrl+V</code>, though, it’s better to “Paste special”. On the current LibreOffice version, that’s located under <strong>Edit</strong> &gt; <strong>Paste Special</strong> &gt; <strong>Paste Special…</strong>, or it can be triggered with <code class="language-plaintext highlighter-rouge">Shift+Ctrl+V</code>:</p> <p><img src="/images/paste_special.png" alt="Paste Special" /></p> <p>The normal paste tends to change fonts depending on what you’re pasting over. Random things get bolded, or colors change. This way tends to produce less surprises.</p> <p>And that’s pretty much it.</p> <h2 id="some-sensible-questions-to-ask"><a class="anchor" aria-hidden="true" href="#some-sensible-questions-to-ask">🔗</a>Some Sensible Questions to Ask</h2> <h3 id="any-problems-with-this-method">Any problems with this method?</h3> <p>Oh, tons. When I copy a slide, its code ends up centered for some reason, so I need to select it and left-align. Sometimes, colors still get messed up. Editing the code works, but it requires some work to maintain the right syntax highlighting.</p> <p>Some of this might be LibreOffice bugs, or it might be me not using it correctly. Let me know if you have any solutions I can try!</p> <h3 id="why-use-libreoffice-at-all-then">Why use LibreOffice at all then?</h3> <p>Frameworks like impress.js are pretty good for code, but I find them less flexible in terms of just positioning graphics and such around. Even with code, I’d occasionally like to position it weirdly or appearify chunks of code one by one, and the PowerPoint model of making presentations gives me that flexibility.</p> <p>Keynote might be much better, but I’m not a Mac user, so I couldn’t say.</p> <h3 id="would-this-method-work-with-keynote">Would this method work with Keynote?</h3> <p>Probably yes, but I can’t be sure. You’d need to tweak some of the commands to launch Keynote and some other word processor instead of LibreOffice.</p> <h3 id="what-about-using-screenshots-instead">What about using screenshots instead?</h3> <p>It’s an option, but code-as-text means you can make small edits later. It also allows people to select the code from the slides afterwards, even if you export them to PDF.</p> <p>Mind you, with the existence of tools like <a href="https://github.com/segeljakt/vim-silicon">vim-silicon</a>, screenshots are certainly a more interesting option nowadays.</p> Sat, 05 Oct 2019 08:00:00 +0200 https://andrewra.dev/2019/10/05/from-vim-to-presentation-slides/ https://andrewra.dev/2019/10/05/from-vim-to-presentation-slides/ vim talks Testing in Rust: Writing to Stdout <p>For the last two years, I’ve been one of the organizers of <a href="https://fmi.rust-lang.bg/">an elective Rust</a> course in Sofia University. Last semester, one of the homework assignments we came up with was to build several kinds of “logger” structs with buffering, multiple outputs, and with tagged logging. (The full assignment is <a href="https://2018.fmi.rust-lang.bg/tasks/3">here</a>, but all the instructions are in Bulgarian.)</p> <p>It was a pretty good exercise on using <code class="language-plaintext highlighter-rouge">Rc</code> and <code class="language-plaintext highlighter-rouge">RefCell</code> that wasn’t a linked list or a similar data structure. The goal was to ensure a logger can be cloned, with the copies sharing both the same buffer and output.</p> <p>Testing the code seemed easy at first glance, but I did run into a bit of a problem when simulating loggers printing to “the screen”. The solution is not exactly complicated, but I think it’s a good pattern to share.</p> <!-- more --> <h2 id="the-write-trait-and-simple-stubs"><a class="anchor" aria-hidden="true" href="#the-write-trait-and-simple-stubs">🔗</a>The <code class="language-plaintext highlighter-rouge">Write</code> Trait and Simple Stubs</h2> <p>The common trait for output in Rust is <a href="https://doc.rust-lang.org/std/io/trait.Write.html"><code class="language-plaintext highlighter-rouge">std::io::Write</code></a>. This is implemented for files, sockets, and, most importantly for testing purposes, for <code class="language-plaintext highlighter-rouge">Vec&lt;u8&gt;</code> and <code class="language-plaintext highlighter-rouge">&amp;mut Vec&lt;u8&gt;</code>. This makes it very easy to write a basic test:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[test]</span> <span class="k">fn</span> <span class="nf">test_mutable_reference</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// Prepare our output stub:</span> <span class="k">let</span> <span class="k">mut</span> <span class="n">out</span> <span class="o">=</span> <span class="nn">Vec</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span> <span class="c1">// Don't take ownership, so we can access it for the assertion:</span> <span class="k">let</span> <span class="k">mut</span> <span class="n">logger</span> <span class="o">=</span> <span class="nn">Logger</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">out</span><span class="p">);</span> <span class="n">logger</span><span class="nf">.log</span><span class="p">(</span><span class="s">"Some warning"</span><span class="p">);</span> <span class="n">logger</span><span class="nf">.flush</span><span class="p">();</span> <span class="c1">// Easier to compare if we just convert the bytes to a string first:</span> <span class="k">let</span> <span class="n">string_output</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">from_utf8</span><span class="p">(</span><span class="n">out</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span> <span class="nd">assert_eq!</span><span class="p">(</span><span class="n">string_output</span><span class="p">,</span> <span class="s">"Some warning</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span> <span class="p">}</span> </code></pre></div></div> <p>The logger implementation is a simplified version of the one in the task:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">io</span><span class="p">::{</span><span class="n">Write</span><span class="p">};</span> <span class="k">pub</span> <span class="k">struct</span> <span class="n">Logger</span><span class="o">&lt;</span><span class="n">W</span><span class="p">:</span> <span class="n">Write</span><span class="o">&gt;</span> <span class="p">{</span> <span class="n">out</span><span class="p">:</span> <span class="n">W</span><span class="p">,</span> <span class="p">}</span> <span class="k">impl</span><span class="o">&lt;</span><span class="n">W</span><span class="p">:</span> <span class="n">Write</span><span class="o">&gt;</span> <span class="n">Logger</span><span class="o">&lt;</span><span class="n">W</span><span class="o">&gt;</span> <span class="p">{</span> <span class="k">pub</span> <span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">out</span><span class="p">:</span> <span class="n">W</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span> <span class="n">Logger</span> <span class="p">{</span> <span class="n">out</span> <span class="p">}</span> <span class="p">}</span> <span class="c1">// Just write the message directly to the given output with a newline.</span> <span class="k">pub</span> <span class="k">fn</span> <span class="k">log</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">message</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="py">.out</span><span class="nf">.write</span><span class="p">(</span><span class="n">message</span><span class="nf">.as_bytes</span><span class="p">())</span><span class="nf">.unwrap</span><span class="p">();</span> <span class="k">self</span><span class="py">.out</span><span class="nf">.write</span><span class="p">(</span><span class="s">b"</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span> <span class="p">}</span> <span class="c1">// Not an interesting method, but could be if we added buffering.</span> <span class="k">pub</span> <span class="k">fn</span> <span class="nf">flush</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="py">.out</span><span class="nf">.flush</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>There’s no timestamps, no <code class="language-plaintext highlighter-rouge">Clone</code> implementation, and it’s a terrible idea to use <code class="language-plaintext highlighter-rouge">unwrap</code> in there (more on that later), but it’s enough to demonstrate the problem, even though the first test passes just fine:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>running 1 test test test_mutable_reference ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out </code></pre></div></div> <p>The issue shows up when we try to <em>share</em> the output vector somehow, so we can see how multiple loggers’ output interpolates:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[test]</span> <span class="k">fn</span> <span class="nf">test_mutable_reference_in_two_places</span><span class="p">()</span> <span class="p">{</span> <span class="k">let</span> <span class="k">mut</span> <span class="n">out</span> <span class="o">=</span> <span class="nn">Vec</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span> <span class="c1">// Does not compile:</span> <span class="k">let</span> <span class="k">mut</span> <span class="n">logger1</span> <span class="o">=</span> <span class="nn">Logger</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">out</span><span class="p">);</span> <span class="k">let</span> <span class="k">mut</span> <span class="n">logger2</span> <span class="o">=</span> <span class="nn">Logger</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">out</span><span class="p">);</span> <span class="n">logger1</span><span class="nf">.log</span><span class="p">(</span><span class="s">"One"</span><span class="p">);</span> <span class="n">logger2</span><span class="nf">.log</span><span class="p">(</span><span class="s">"Two"</span><span class="p">);</span> <span class="n">logger1</span><span class="nf">.log</span><span class="p">(</span><span class="s">"Three"</span><span class="p">);</span> <span class="n">logger1</span><span class="nf">.flush</span><span class="p">();</span> <span class="n">logger2</span><span class="nf">.flush</span><span class="p">();</span> <span class="nd">assert_eq!</span><span class="p">(</span><span class="nn">String</span><span class="p">::</span><span class="nf">from_utf8</span><span class="p">(</span><span class="n">out</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">(),</span> <span class="s">"One</span><span class="se">\n</span><span class="s">Two</span><span class="se">\n</span><span class="s">Three</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span> <span class="p">}</span> </code></pre></div></div> <p>You can’t really compile this code, because you’d be taking two mutable references to the same vector, in the same scope. You <em>could</em> create the loggers in two separate scopes, but that’s not what I’d like to test. If I initialized the two loggers with <code class="language-plaintext highlighter-rouge">std::io::stdout()</code> in a main function, they’d print “One”, “Two”, and “Three” just fine.</p> <p>Of course, the <code class="language-plaintext highlighter-rouge">stdout()</code> function returns separate <code class="language-plaintext highlighter-rouge">Stdout</code> structs that share a file descriptor, probably with some amount of locking. So, instead of using a simple <code class="language-plaintext highlighter-rouge">Vec</code>, we can do something fancier.</p> <h2 id="a-more-robust-stub"><a class="anchor" aria-hidden="true" href="#a-more-robust-stub">🔗</a>A More Robust Stub</h2> <p>The solution to sharing resources in Rust is usually <code class="language-plaintext highlighter-rouge">Rc</code>, with an added <code class="language-plaintext highlighter-rouge">RefCell</code> so we can allow mutability:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">io</span><span class="p">::{</span><span class="k">self</span><span class="p">,</span> <span class="n">Write</span><span class="p">};</span> <span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">rc</span><span class="p">::</span><span class="nb">Rc</span><span class="p">;</span> <span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">cell</span><span class="p">::</span><span class="n">RefCell</span><span class="p">;</span> <span class="nd">#[derive(Clone)]</span> <span class="k">struct</span> <span class="n">TestWriter</span> <span class="p">{</span> <span class="n">storage</span><span class="p">:</span> <span class="nb">Rc</span><span class="o">&lt;</span><span class="n">RefCell</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">u8</span><span class="o">&gt;&gt;&gt;</span><span class="p">,</span> <span class="p">}</span> <span class="k">impl</span> <span class="n">TestWriter</span> <span class="p">{</span> <span class="c1">// Creating a new `TestWriter` just means packaging an empty `Vec` in all</span> <span class="c1">// the wrappers.</span> <span class="c1">//</span> <span class="k">fn</span> <span class="nf">new</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span> <span class="n">TestWriter</span> <span class="p">{</span> <span class="n">storage</span><span class="p">:</span> <span class="nn">Rc</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nn">RefCell</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nn">Vec</span><span class="p">::</span><span class="nf">new</span><span class="p">()))</span> <span class="p">}</span> <span class="p">}</span> <span class="c1">// Once we're done writing to the buffer, we can pull it out of the `Rc` and</span> <span class="c1">// the `RefCell` and inspect its contents.</span> <span class="c1">//</span> <span class="k">fn</span> <span class="nf">into_inner</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">u8</span><span class="o">&gt;</span> <span class="p">{</span> <span class="nn">Rc</span><span class="p">::</span><span class="nf">try_unwrap</span><span class="p">(</span><span class="k">self</span><span class="py">.storage</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">()</span><span class="nf">.into_inner</span><span class="p">()</span> <span class="p">}</span> <span class="c1">// It's easier to compare strings than byte vectors.</span> <span class="c1">//</span> <span class="k">fn</span> <span class="nf">into_string</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">String</span> <span class="p">{</span> <span class="nn">String</span><span class="p">::</span><span class="nf">from_utf8</span><span class="p">(</span><span class="k">self</span><span class="nf">.into_inner</span><span class="p">())</span><span class="nf">.unwrap</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>This struct is perfectly cloneable and holds a simple mutable vector. All we need to do is implement the <code class="language-plaintext highlighter-rouge">Write</code> trait for it that delegates methods to the vector through a <code class="language-plaintext highlighter-rouge">borrow_mut()</code>:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="n">Write</span> <span class="k">for</span> <span class="n">TestWriter</span> <span class="p">{</span> <span class="k">fn</span> <span class="nf">write</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">buf</span><span class="p">:</span> <span class="o">&amp;</span><span class="p">[</span><span class="nb">u8</span><span class="p">])</span> <span class="k">-&gt;</span> <span class="nn">io</span><span class="p">::</span><span class="nb">Result</span><span class="o">&lt;</span><span class="nb">usize</span><span class="o">&gt;</span> <span class="p">{</span> <span class="k">self</span><span class="py">.storage</span><span class="nf">.borrow_mut</span><span class="p">()</span><span class="nf">.write</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="p">}</span> <span class="k">fn</span> <span class="nf">flush</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">io</span><span class="p">::</span><span class="nb">Result</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span> <span class="p">{</span> <span class="k">self</span><span class="py">.storage</span><span class="nf">.borrow_mut</span><span class="p">()</span><span class="nf">.flush</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>With this, our “fake stdout” can be cloned between two loggers just fine:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[test]</span> <span class="k">fn</span> <span class="nf">test_clonable_writer</span><span class="p">()</span> <span class="p">{</span> <span class="k">let</span> <span class="n">out</span> <span class="o">=</span> <span class="nn">TestWriter</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span> <span class="p">{</span> <span class="k">let</span> <span class="k">mut</span> <span class="n">logger1</span> <span class="o">=</span> <span class="nn">Logger</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">out</span><span class="nf">.clone</span><span class="p">());</span> <span class="k">let</span> <span class="k">mut</span> <span class="n">logger2</span> <span class="o">=</span> <span class="nn">Logger</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">out</span><span class="nf">.clone</span><span class="p">());</span> <span class="n">logger1</span><span class="nf">.log</span><span class="p">(</span><span class="s">"One"</span><span class="p">);</span> <span class="n">logger2</span><span class="nf">.log</span><span class="p">(</span><span class="s">"Two"</span><span class="p">);</span> <span class="n">logger1</span><span class="nf">.log</span><span class="p">(</span><span class="s">"Three"</span><span class="p">);</span> <span class="n">logger1</span><span class="nf">.flush</span><span class="p">();</span> <span class="n">logger2</span><span class="nf">.flush</span><span class="p">();</span> <span class="p">}</span> <span class="c1">// Ownership of `out` is never transferred, so we can access it:</span> <span class="nd">assert_eq!</span><span class="p">(</span><span class="n">out</span><span class="nf">.into_string</span><span class="p">(),</span> <span class="s">"One</span><span class="se">\n</span><span class="s">Two</span><span class="se">\n</span><span class="s">Three</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span> <span class="p">}</span> </code></pre></div></div> <p>If we wanted to test loggers in different threads, we could change the <code class="language-plaintext highlighter-rouge">Rc&lt;RefCell&lt;Vec&lt;u8&gt;&gt;&gt;</code> to <code class="language-plaintext highlighter-rouge">Arc&lt;Mutex&lt;Vec&lt;u8&gt;&gt;&gt;</code> and use <code class="language-plaintext highlighter-rouge">lock().unwrap()</code> instead of <code class="language-plaintext highlighter-rouge">borrow_mut()</code>.</p> <p>All the <code class="language-plaintext highlighter-rouge">unwrap</code> calls are not a problem, given that there’s no “user” to report errors to – if we’ve somehow messed up our test setup, it should probably blow up. We might make it a bit clearer what broke by using <code class="language-plaintext highlighter-rouge">expect</code> calls instead. For instance:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">into_inner</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">u8</span><span class="o">&gt;</span> <span class="p">{</span> <span class="nn">Rc</span><span class="p">::</span><span class="nf">try_unwrap</span><span class="p">(</span><span class="k">self</span><span class="py">.storage</span><span class="p">)</span><span class="nf">. expect</span><span class="p">(</span><span class="s">"TestWriter: More than one Rc refers to the inner Vec"</span><span class="p">)</span><span class="nf">. into_inner</span><span class="p">()</span> <span class="p">}</span> </code></pre></div></div> <p>That said, it’s not ideal to keep using <code class="language-plaintext highlighter-rouge">unwrap</code> in the logger code itself.</p> <h2 id="error-handling"><a class="anchor" aria-hidden="true" href="#error-handling">🔗</a>Error Handling</h2> <p>It’s not exactly obvious what the best way to handle errors is, for a logger. For one thing, we want the logger to be the thing printing the errors to files or to standard output. Returning <code class="language-plaintext highlighter-rouge">Result</code> from every single <code class="language-plaintext highlighter-rouge">log</code> statement would also be kind of annoying to deal with. The idea we came up with for the homework task was to split things up a bit and create pairs of methods – one that is allowed to fail and one that logs the error to stderr:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// A testable method that might error out:</span> <span class="k">pub</span> <span class="k">fn</span> <span class="nf">try_log</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">message</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">io</span><span class="p">::</span><span class="nb">Result</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span> <span class="p">{</span> <span class="k">self</span><span class="py">.out</span><span class="nf">.borrow_mut</span><span class="p">()</span><span class="nf">.write</span><span class="p">(</span><span class="n">message</span><span class="nf">.as_bytes</span><span class="p">())</span><span class="o">?</span><span class="p">;</span> <span class="k">self</span><span class="py">.out</span><span class="nf">.borrow_mut</span><span class="p">()</span><span class="nf">.write</span><span class="p">(</span><span class="s">b"</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span><span class="o">?</span><span class="p">;</span> <span class="nf">Ok</span><span class="p">(())</span> <span class="p">}</span> <span class="c1">// A simple wrapper that we expect just ignores stuff:</span> <span class="k">pub</span> <span class="k">fn</span> <span class="k">log</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">message</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="k">let</span> <span class="nf">Err</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="o">=</span> <span class="k">self</span><span class="nf">.try_log</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="p">{</span> <span class="nd">eprintln!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">e</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>In order to test it, we can just create another struct that implements <code class="language-plaintext highlighter-rouge">Write</code> by failing unconditionally:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">ErroringWriter</span> <span class="p">{}</span> <span class="k">impl</span> <span class="n">Write</span> <span class="k">for</span> <span class="n">ErroringWriter</span> <span class="p">{</span> <span class="k">fn</span> <span class="nf">write</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">_buf</span><span class="p">:</span> <span class="o">&amp;</span><span class="p">[</span><span class="nb">u8</span><span class="p">])</span> <span class="k">-&gt;</span> <span class="nn">io</span><span class="p">::</span><span class="nb">Result</span><span class="o">&lt;</span><span class="nb">usize</span><span class="o">&gt;</span> <span class="p">{</span> <span class="nf">Err</span><span class="p">(</span><span class="nn">io</span><span class="p">::</span><span class="nn">Error</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nn">io</span><span class="p">::</span><span class="nn">ErrorKind</span><span class="p">::</span><span class="n">Other</span><span class="p">,</span> <span class="s">"Write Error!"</span><span class="p">))</span> <span class="p">}</span> <span class="k">fn</span> <span class="nf">flush</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">io</span><span class="p">::</span><span class="nb">Result</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span> <span class="p">{</span> <span class="nf">Err</span><span class="p">(</span><span class="nn">io</span><span class="p">::</span><span class="nn">Error</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nn">io</span><span class="p">::</span><span class="nn">ErrorKind</span><span class="p">::</span><span class="n">Other</span><span class="p">,</span> <span class="s">"Flush Error!"</span><span class="p">))</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>It returns the right kind of error, and a specific message, if we really wanted to test details. I’d personally be fine with a simpler assertion:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[test]</span> <span class="k">fn</span> <span class="nf">test_erroring_io</span><span class="p">()</span> <span class="p">{</span> <span class="k">let</span> <span class="n">out</span> <span class="o">=</span> <span class="n">ErroringWriter</span> <span class="p">{};</span> <span class="k">let</span> <span class="k">mut</span> <span class="n">logger</span> <span class="o">=</span> <span class="nn">Logger</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">out</span><span class="p">);</span> <span class="k">if</span> <span class="k">let</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="o">=</span> <span class="n">logger</span><span class="nf">.try_log</span><span class="p">(</span><span class="s">"One"</span><span class="p">)</span> <span class="p">{</span> <span class="nd">assert!</span><span class="p">(</span><span class="kc">false</span><span class="p">,</span> <span class="s">"Expected try_log with an erroring writer to return an error"</span><span class="p">)</span> <span class="p">}</span> <span class="c1">// Should not panic:</span> <span class="n">logger</span><span class="nf">.log</span><span class="p">(</span><span class="s">"Two"</span><span class="p">);</span> <span class="n">logger</span><span class="nf">.flush</span><span class="p">();</span> <span class="p">}</span> </code></pre></div></div> <p>If it’s not an error, there’s something wrong, and that’s probably good enough guidance for something this simple. If you want to go in the other direction, a more sophisticated mock might even have methods to toggle its erroring or non-erroring state with more <code class="language-plaintext highlighter-rouge">Rc</code>/<code class="language-plaintext highlighter-rouge">RefCell</code> juggling.</p> <h2 id="extracting-to-a-separate-file"><a class="anchor" aria-hidden="true" href="#extracting-to-a-separate-file">🔗</a>Extracting to a Separate File</h2> <p><em>Update (2023-04-02): A better way to organize the shared code can be found in <a href="https://doc.rust-lang.org/book/ch11-03-test-organization.html#submodules-in-integration-tests">The Rust Book</a>. I don’t know if I missed it when I was writing this article, or if it’s new(ish), but it’s there.</em></p> <p>Having the custom structs in the actual test files can get in the way. What we could do is extract a separate <code class="language-plaintext highlighter-rouge">testing.rs</code> file to stick the code in:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>src ├── lib.rs ├── main.rs └── testing.rs tests └── test_logger.rs </code></pre></div></div> <p>We’ll need the module actually exported in <code class="language-plaintext highlighter-rouge">lib.rs</code>:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">mod</span> <span class="n">testing</span><span class="p">;</span> <span class="k">pub</span> <span class="k">struct</span> <span class="n">Logger</span><span class="o">&lt;</span><span class="n">W</span><span class="p">:</span> <span class="n">Write</span><span class="o">&gt;</span> <span class="p">{</span> <span class="c1">// ...</span> </code></pre></div></div> <p>And then we have to import it in the test code:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">logger</span><span class="p">::</span><span class="n">Logger</span><span class="p">;</span> <span class="k">use</span> <span class="nn">logger</span><span class="p">::</span><span class="nn">testing</span><span class="p">::</span><span class="o">*</span><span class="p">;</span> <span class="nd">#[test]</span> <span class="k">fn</span> <span class="nf">test_clonable_writer</span><span class="p">()</span> <span class="p">{</span> <span class="k">let</span> <span class="n">out</span> <span class="o">=</span> <span class="nn">TestWriter</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span> <span class="c1">// ...</span> </code></pre></div></div> <p>It’s a bit annoying, since the test module is included with the “real” code. You’d think it’d be possible to avoid this with a <code class="language-plaintext highlighter-rouge">#[cfg(test)]</code>, but at the time of writing, I couldn’t manage to do it – adding a <code class="language-plaintext highlighter-rouge">#[cfg(test)]</code> above the <code class="language-plaintext highlighter-rouge">pub mod testing</code> line doesn’t compile. We could, however, add <code class="language-plaintext highlighter-rouge">#[cfg(test)]</code> to the individual types and impls:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[cfg(test)]</span> <span class="nd">#[derive(Clone)]</span> <span class="k">pub</span> <span class="k">struct</span> <span class="n">TestWriter</span> <span class="p">{</span> <span class="n">storage</span><span class="p">:</span> <span class="nb">Arc</span><span class="o">&lt;</span><span class="n">Mutex</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">u8</span><span class="o">&gt;&gt;&gt;</span><span class="p">,</span> <span class="p">}</span> <span class="nd">#[cfg(test)]</span> <span class="k">impl</span> <span class="n">TestWriter</span> <span class="p">{</span> <span class="c1">// ...</span> </code></pre></div></div> <p>That way, the module itself is always compiled in, but the structs are only present in the binary that’s used for testing. Difficult to say if it’s worth the trouble – these mock types would only be imported in tests either way, so there’s little chance of naming conflicts or binary bloat.</p> <h2 id="closing-thoughts-and-further-reading"><a class="anchor" aria-hidden="true" href="#closing-thoughts-and-further-reading">🔗</a>Closing Thoughts and Further Reading</h2> <p>The standard library includes some powerful testing tools and a convenient test runner. Even so, it can be worth it to create custom tools to make tests simpler to write and easier to read. Extracting them to their own module means they can easily be shared between different test files. You might even decide to split them up additionally into, say, <code class="language-plaintext highlighter-rouge">src/testing/io.rs</code>, <code class="language-plaintext highlighter-rouge">src/testing/assertions.rs</code> and so on.</p> <p>In the end, tests are code too – reusing common logic works just as well, and you can apply all the same engineering patterns you use in “real” code.</p> <p>If you need more flexibility in testing I/O in particular, try <a href="https://doc.rust-lang.org/std/io/struct.Cursor.html"><code class="language-plaintext highlighter-rouge">std::io::Cursor</code></a>. It works just like a <code class="language-plaintext highlighter-rouge">Vec</code> for reading and writing, but allows you to rewind it and seek into it, like you could with a real file.</p> <p>For more ideas on mocking, you might also take a look at “<a href="https://klausi.github.io/rustnish/2019/03/31/mocking-in-rust-with-conditional-compilation.html">Mocking in Rust with conditional compilation</a>”.</p> Mon, 05 Aug 2019 08:00:00 +0200 https://andrewra.dev/2019/08/05/testing-in-rust-writing-to-stdout/ https://andrewra.dev/2019/08/05/testing-in-rust-writing-to-stdout/ rust testing Testing in Rust: Temporary Files <p>I recently wrote a tool to manipulate images embedded in mp3 tags: <a href="https://github.com/AndrewRadev/id3-image">id3-image</a>. To be able to make changes to the code with confidence, I needed tests. Rust comes with the very sensible <code class="language-plaintext highlighter-rouge">Write</code> trait that could have allowed me to mock any IO (a blog post for another day), but in this case, the project relied on the excellent <a href="https://crates.io/crates/id3">id3 crate</a>, so I wasn’t doing any file-writing myself.</p> <p>Instead, I wanted to have an actual mp3 file and an actual image, apply my code to them, and inspect the results. This generally isn’t difficult to do, but I ran into a few unexpected gotchas, so I’ll summarize how I ended up implementing it.</p> <!-- more --> <h2 id="first-attempt-a-tempdir"><a class="anchor" aria-hidden="true" href="#first-attempt-a-tempdir">🔗</a>First Attempt: a Tempdir</h2> <p>I’ve done this often in other projects: Create a temporary directory per-test, change into it, copy some test files, and then work on them as needed. When the test is done, the entire directory can be wiped out.</p> <p>In Rust, I’d do this with a macro like so:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">macro_rules!</span> <span class="n">in_temp_dir</span> <span class="p">{</span> <span class="p">(</span><span class="nv">$block:block</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span> <span class="k">let</span> <span class="n">tmpdir</span> <span class="o">=</span> <span class="nn">tempfile</span><span class="p">::</span><span class="nf">tempdir</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span> <span class="nn">env</span><span class="p">::</span><span class="nf">set_current_dir</span><span class="p">(</span><span class="o">&amp;</span><span class="n">tmpdir</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span> <span class="nv">$block</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>The <code class="language-plaintext highlighter-rouge">tempfile::tempdir()</code> function comes from the <a href="https://crates.io/crates/tempfile">tempfile</a> crate. After unwrapping, I get a <code class="language-plaintext highlighter-rouge">TempDir</code> that, when dropped, will remove the directory. Neat.</p> <p>The next step is copying a test file from a predefined place. In my case, I put some files in <code class="language-plaintext highlighter-rouge">tests/fixtures/</code>, so:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">macro_rules!</span> <span class="n">fixture</span> <span class="p">{</span> <span class="p">(</span><span class="nv">$filename:expr</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span> <span class="k">let</span> <span class="n">root_dir</span> <span class="o">=</span> <span class="o">&amp;</span><span class="nn">env</span><span class="p">::</span><span class="nf">var</span><span class="p">(</span><span class="s">"CARGO_MANIFEST_DIR"</span><span class="p">)</span><span class="nf">.expect</span><span class="p">(</span><span class="s">"$CARGO_MANIFEST_DIR"</span><span class="p">);</span> <span class="k">let</span> <span class="k">mut</span> <span class="n">source_path</span> <span class="o">=</span> <span class="nn">PathBuf</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="n">root_dir</span><span class="p">);</span> <span class="n">source_path</span><span class="nf">.push</span><span class="p">(</span><span class="s">"tests/fixtures"</span><span class="p">);</span> <span class="n">source_path</span><span class="nf">.push</span><span class="p">(</span><span class="nv">$filename</span><span class="p">);</span> <span class="nn">fs</span><span class="p">::</span><span class="nf">copy</span><span class="p">(</span><span class="n">source_path</span><span class="p">,</span> <span class="nv">$filename</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>It doesn’t <em>have</em> to be a macro, but it means I can be a bit loose about the type of <code class="language-plaintext highlighter-rouge">$filename</code>, at least to start. The env var <code class="language-plaintext highlighter-rouge">CARGO_MANIFEST_DIR</code> is something I’m just using to get the root of my project. Not sure if there’s a better way, but I always run tests via cargo, so I feel like it should be reasonably reliable.</p> <p>I could use these two tools to build a simple test that embeds an image and then checks if it worked:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[test]</span> <span class="k">fn</span> <span class="nf">test_successful_jpeg_image_embedding</span><span class="p">()</span> <span class="p">{</span> <span class="nd">in_temp_dir!</span><span class="p">({</span> <span class="nd">fixture!</span><span class="p">(</span><span class="s">"no_image.mp3"</span><span class="p">);</span> <span class="nd">fixture!</span><span class="p">(</span><span class="s">"test.jpg"</span><span class="p">);</span> <span class="c1">// No embedded pictures to begin with:</span> <span class="k">let</span> <span class="n">tag</span> <span class="o">=</span> <span class="nn">id3</span><span class="p">::</span><span class="nn">Tag</span><span class="p">::</span><span class="nf">read_from_path</span><span class="p">(</span><span class="s">"no_image.mp3"</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span> <span class="nd">assert!</span><span class="p">(</span><span class="n">tag</span><span class="nf">.pictures</span><span class="p">()</span><span class="nf">.count</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span> <span class="nf">embed_image</span><span class="p">(</span><span class="s">"no_image.mp3"</span><span class="p">,</span> <span class="s">"test.jpg"</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span> <span class="c1">// One embedded picture after we've run the function:</span> <span class="k">let</span> <span class="n">tag</span> <span class="o">=</span> <span class="nn">id3</span><span class="p">::</span><span class="nn">Tag</span><span class="p">::</span><span class="nf">read_from_path</span><span class="p">(</span><span class="s">"no_image.mp3"</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span> <span class="nd">assert!</span><span class="p">(</span><span class="n">tag</span><span class="nf">.pictures</span><span class="p">()</span><span class="nf">.count</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">);</span> <span class="p">});</span> <span class="p">}</span> </code></pre></div></div> <p>It’s a readable test, as long as you know what the helper macros do. And it works quite well. The problem shows up when I write more than one of these.</p> <h2 id="the-problem"><a class="anchor" aria-hidden="true" href="#the-problem">🔗</a>The problem</h2> <p>With several tests, this approach fails:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>failures: ---- test_successful_png_image_embedding stdout ---- thread 'test_successful_png_image_embedding' panicked at 'called `Result::unwrap()` on an `Err` value: entity not found', src/libcore/result.rs:997:5 note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace. ---- test_remove_and_add_image stdout ---- thread 'test_remove_and_add_image' panicked at 'called `Result::unwrap()` on an `Err` value: entity not found', src/libcore/result.rs:997:5 ---- test_successful_image_embedding_in_a_file_that_already_has_an_image stdout ---- thread 'test_successful_image_embedding_in_a_file_that_already_has_an_image' panicked at 'called `Result::unwrap()` on an `Err` value: StringError("Error writing image to music file test.mp3: entity not found")', src/libcore/result.rs:997:5 failures: test_remove_and_add_image test_successful_image_embedding_in_a_file_that_already_has_an_image test_successful_png_image_embedding test result: FAILED. 2 passed; 3 failed; 0 ignored; 0 measured; 0 filtered out </code></pre></div></div> <p>Even more annoying, the failures are random – different tests fail at different times. Usually, when I have problems like this, it’s a timing issue – could it be that filesystem changes are not being persisted? Does adding a few <code class="language-plaintext highlighter-rouge">sleep</code>s help?</p> <p>I’ll spare you the debugging session – the problem is more of a feature:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>If you want to control the number of simultaneous running test cases, pass the `--test-threads` option to the test binaries: cargo test -- --test-threads=1 </code></pre></div></div> <p>Changing the directory per test doesn’t work, because several tests are setting it basically at the same time. And the current working directory is process-local, not thread-local, so there’s no way to ensure each test thread is isolated.</p> <h2 id="files-and-paths"><a class="anchor" aria-hidden="true" href="#files-and-paths">🔗</a>Files and Paths</h2> <p>So what to do? An option is to run with <code class="language-plaintext highlighter-rouge">--test-threads=1</code>. This works just fine, but it seems like a hassle to require users of the tool to run tests in a special way.</p> <p>Instead of changing directories, we could allocate a temporary path for fixtures instead, and use that:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">song_file</span> <span class="o">=</span> <span class="nd">fixture!</span><span class="p">(</span><span class="s">"no_image.mp3"</span><span class="p">);</span> <span class="k">let</span> <span class="n">image_file</span> <span class="o">=</span> <span class="nd">fixture!</span><span class="p">(</span><span class="s">"test.jpg"</span><span class="p">);</span> </code></pre></div></div> <p>The tempfile crate does have a <code class="language-plaintext highlighter-rouge">tempfile()</code> function that’s automatically deleted when we have no reference to it anymore. Unlike with <code class="language-plaintext highlighter-rouge">tempdir()</code> which returns a custom structure, this gives us a <code class="language-plaintext highlighter-rouge">File</code> whose deletion is handled by the operating system.</p> <p>It doesn’t seem like a bad option. I’d expect I’d be able to do this:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Spoiler: doesn't compile</span> <span class="nf">embed_image</span><span class="p">(</span><span class="o">&amp;</span><span class="n">song_file</span><span class="py">.path</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">image_file</span><span class="py">.path</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span> </code></pre></div></div> <p>Unfortunately, no:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>error[E0609]: no field `path` on type `std::fs::File` </code></pre></div></div> <p>The built-in <code class="language-plaintext highlighter-rouge">File</code> type doesn’t give us a way to get its path. As it turns out, not every file needs to have a real path. From <a href="https://www.reddit.com/r/rust/comments/4sthxj/how_to_get_path_from_file/">a reddit thread</a>:</p> <blockquote> <p>With <code class="language-plaintext highlighter-rouge">from_raw_fd</code> you can have a unix pipe, a standard input/output stream or even a TCP/IP socket wrapped in a File.</p> </blockquote> <p>Leave it to Rust to illuminate my misconceptions, again. There <em>is</em> a crate that could help with this particular case: <a href="https://crates.io/crates/filepath">filepath</a>. Instead, I figured I’d use <code class="language-plaintext highlighter-rouge">TempDir</code> instead and package it up in a more reusable way.</p> <h2 id="a-reasonable-solution"><a class="anchor" aria-hidden="true" href="#a-reasonable-solution">🔗</a>A Reasonable Solution</h2> <p>Let’s start by making our own type, instead of passing around <code class="language-plaintext highlighter-rouge">File</code>s or <code class="language-plaintext highlighter-rouge">PathBuf</code>s:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">Fixture</span> <span class="p">{</span> <span class="n">path</span><span class="p">:</span> <span class="n">PathBuf</span><span class="p">,</span> <span class="n">source</span><span class="p">:</span> <span class="n">PathBuf</span><span class="p">,</span> <span class="n">_tempdir</span><span class="p">:</span> <span class="n">TempDir</span><span class="p">,</span> <span class="p">}</span> </code></pre></div></div> <p>The <code class="language-plaintext highlighter-rouge">_tempdir</code> field has an underscore, because its only purpose there is to keep ownership of it. That way it’s dropped when the <code class="language-plaintext highlighter-rouge">Fixture</code> is dropped. There’s separate <code class="language-plaintext highlighter-rouge">source</code> and <code class="language-plaintext highlighter-rouge">path</code>, because I’d like to separate creating a <code class="language-plaintext highlighter-rouge">Fixture</code> and copying the test file over (for purely pragmatic reasons that’ll be clear later).</p> <p>Creating a new fixture is a mixture of the previous <code class="language-plaintext highlighter-rouge">in_temp_dir!</code> and <code class="language-plaintext highlighter-rouge">fixture!</code> macros:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="n">Fixture</span> <span class="p">{</span> <span class="k">fn</span> <span class="nf">blank</span><span class="p">(</span><span class="n">fixture_filename</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span> <span class="c1">// First, figure out the right file in `tests/fixtures/`:</span> <span class="k">let</span> <span class="n">root_dir</span> <span class="o">=</span> <span class="o">&amp;</span><span class="nn">env</span><span class="p">::</span><span class="nf">var</span><span class="p">(</span><span class="s">"CARGO_MANIFEST_DIR"</span><span class="p">)</span><span class="nf">.expect</span><span class="p">(</span><span class="s">"$CARGO_MANIFEST_DIR"</span><span class="p">);</span> <span class="k">let</span> <span class="k">mut</span> <span class="n">source</span> <span class="o">=</span> <span class="nn">PathBuf</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="n">root_dir</span><span class="p">);</span> <span class="n">source</span><span class="nf">.push</span><span class="p">(</span><span class="s">"tests/fixtures"</span><span class="p">);</span> <span class="n">source</span><span class="nf">.push</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fixture_filename</span><span class="p">);</span> <span class="c1">// The "real" path of the file is going to be under a temporary directory:</span> <span class="k">let</span> <span class="n">tempdir</span> <span class="o">=</span> <span class="nn">tempfile</span><span class="p">::</span><span class="nf">tempdir</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span> <span class="k">let</span> <span class="k">mut</span> <span class="n">path</span> <span class="o">=</span> <span class="nn">PathBuf</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="o">&amp;</span><span class="n">tempdir</span><span class="nf">.path</span><span class="p">());</span> <span class="n">path</span><span class="nf">.push</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fixture_filename</span><span class="p">);</span> <span class="n">Fixture</span> <span class="p">{</span> <span class="n">_tempdir</span><span class="p">:</span> <span class="n">tempdir</span><span class="p">,</span> <span class="n">source</span><span class="p">,</span> <span class="n">path</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>So this creates a “blank” fixture – its directory exists and will be cleaned up, and it has a path, but there’s nothing there yet. In order to copy the file, I’ll add one more associated function:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="n">Fixture</span> <span class="p">{</span> <span class="k">fn</span> <span class="nf">copy</span><span class="p">(</span><span class="n">fixture_filename</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span> <span class="k">let</span> <span class="n">fixture</span> <span class="o">=</span> <span class="nn">Fixture</span><span class="p">::</span><span class="nf">blank</span><span class="p">(</span><span class="n">fixture_filename</span><span class="p">);</span> <span class="nn">fs</span><span class="p">::</span><span class="nf">copy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fixture</span><span class="py">.source</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">fixture</span><span class="py">.path</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span> <span class="n">fixture</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>This will actually copy the file from the source path into the temporary directory where we’ll be changing it. To make the tests a bit more terse, why not add a <code class="language-plaintext highlighter-rouge">Deref</code> implementation and a helper method:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="n">Deref</span> <span class="k">for</span> <span class="n">Fixture</span> <span class="p">{</span> <span class="k">type</span> <span class="n">Target</span> <span class="o">=</span> <span class="n">Path</span><span class="p">;</span> <span class="k">fn</span> <span class="nf">deref</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="o">&amp;</span><span class="k">Self</span><span class="p">::</span><span class="n">Target</span> <span class="p">{</span> <span class="k">self</span><span class="py">.path</span><span class="nf">.deref</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> <span class="k">fn</span> <span class="nf">read_tag</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Path</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">id3</span><span class="p">::</span><span class="n">Tag</span> <span class="p">{</span> <span class="nn">id3</span><span class="p">::</span><span class="nn">Tag</span><span class="p">::</span><span class="nf">read_from_path</span><span class="p">(</span><span class="n">path</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">()</span> <span class="p">}</span> </code></pre></div></div> <p>Going overboard with derefs is generally discouraged, but this is code that will only be used for testing – we can be a bit more loose with it, throw unwraps around, that sort of thing. Whether it’s actually a good idea depends on personal preference, I feel. Here’s what the test looks like now:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[test]</span> <span class="k">fn</span> <span class="nf">test_successful_jpeg_image_embedding</span><span class="p">()</span> <span class="p">{</span> <span class="k">let</span> <span class="n">song</span> <span class="o">=</span> <span class="nn">Fixture</span><span class="p">::</span><span class="nf">copy</span><span class="p">(</span><span class="s">"no_image.mp3"</span><span class="p">);</span> <span class="k">let</span> <span class="n">image</span> <span class="o">=</span> <span class="nn">Fixture</span><span class="p">::</span><span class="nf">copy</span><span class="p">(</span><span class="s">"test.jpg"</span><span class="p">);</span> <span class="k">let</span> <span class="n">tag</span> <span class="o">=</span> <span class="nf">read_tag</span><span class="p">(</span><span class="o">&amp;</span><span class="n">song</span><span class="p">);</span> <span class="nd">assert!</span><span class="p">(</span><span class="n">tag</span><span class="nf">.pictures</span><span class="p">()</span><span class="nf">.count</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span> <span class="nf">embed_image</span><span class="p">(</span><span class="o">&amp;</span><span class="n">song</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">image</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span> <span class="k">let</span> <span class="n">tag</span> <span class="o">=</span> <span class="nf">read_tag</span><span class="p">(</span><span class="o">&amp;</span><span class="n">song</span><span class="p">);</span> <span class="nd">assert!</span><span class="p">(</span><span class="n">tag</span><span class="nf">.pictures</span><span class="p">()</span><span class="nf">.count</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">);</span> <span class="p">}</span> </code></pre></div></div> <p>I like this quite a lot. It might be that <code class="language-plaintext highlighter-rouge">embed_image(&amp;song.path, &amp;image.path)</code> would have been clearer, but if we change <code class="language-plaintext highlighter-rouge">embed_image</code> to accept a slightly different argument (a <code class="language-plaintext highlighter-rouge">File</code>? A custom type?), we could just <code class="language-plaintext highlighter-rouge">Deref</code> to a different thing. So there’s some added flexibility in saying “our custom test-only structure is auto-converted to whatever the code expects”.</p> <p>As for <code class="language-plaintext highlighter-rouge">Fixture::blank</code>, here’s how I use it in a different test:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[test]</span> <span class="k">fn</span> <span class="nf">test_extracting_a_jpg_image</span><span class="p">()</span> <span class="p">{</span> <span class="k">let</span> <span class="n">song</span> <span class="o">=</span> <span class="nn">Fixture</span><span class="p">::</span><span class="nf">copy</span><span class="p">(</span><span class="s">"test.mp3"</span><span class="p">);</span> <span class="c1">// a copied fixture</span> <span class="k">let</span> <span class="n">image</span> <span class="o">=</span> <span class="nn">Fixture</span><span class="p">::</span><span class="nf">blank</span><span class="p">(</span><span class="s">"test.jpg"</span><span class="p">);</span> <span class="c1">// a blank fixture</span> <span class="c1">// To start:</span> <span class="c1">// - the song file has at least one embedded picture</span> <span class="c1">// - the image path doesn't correspond to an existing file</span> <span class="k">let</span> <span class="n">tag</span> <span class="o">=</span> <span class="nf">read_tag</span><span class="p">(</span><span class="o">&amp;</span><span class="n">song</span><span class="p">);</span> <span class="nd">assert!</span><span class="p">(</span><span class="n">tag</span><span class="nf">.pictures</span><span class="p">()</span><span class="nf">.count</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">);</span> <span class="nd">assert!</span><span class="p">(</span><span class="o">!</span><span class="n">image</span><span class="nf">.exists</span><span class="p">());</span> <span class="nf">extract_first_image</span><span class="p">(</span><span class="o">&amp;</span><span class="n">song</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">image</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span> <span class="c1">// After we've run the code, the image should exist on the filesystem</span> <span class="nd">assert!</span><span class="p">(</span><span class="n">image</span><span class="nf">.exists</span><span class="p">());</span> <span class="p">}</span> </code></pre></div></div> <p>So, using <code class="language-plaintext highlighter-rouge">TempDir</code> here has the nice benefit that we don’t <em>need</em> to have a real file – we can just keep a placeholder in the system that can be used as an output.</p> <h2 id="closing-thoughts"><a class="anchor" aria-hidden="true" href="#closing-thoughts">🔗</a>Closing Thoughts</h2> <p>I’m sure there’s other ways to deal with filesystem testing, but this is one that I’m quite happy with and a pattern I’ll likely use elsewhere. If you’d like to see the full test suite, you can check the project out <a href="https://github.com/AndrewRadev/id3-image">on github</a>.</p> <p>If you have ideas for improvements on the test suite (or on the code), feel free to open an issue on the repo to start a conversation.</p> Fri, 01 Mar 2019 14:30:00 +0100 https://andrewra.dev/2019/03/01/testing-in-rust-temporary-files/ https://andrewra.dev/2019/03/01/testing-in-rust-temporary-files/ rust testing Code Alignment <p>Okay, this is going to be a bit bikesheddy. It’s about a code style rule that some people love and some people hate, and I have an <em>✨opinion✨</em> about. It’s not a particularly strong opinion, as in, if the team I’m working with disagrees with me, I have no issue following the popular vote. It’s a small enough thing that it might not matter in the long term.</p> <p>Let’s start by examining some code:</p> <!-- more --> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">describe</span> <span class="no">Registration</span> <span class="k">do</span> <span class="n">it</span> <span class="s2">"disallows duplicates"</span> <span class="k">do</span> <span class="n">create</span><span class="p">(</span><span class="ss">:registration</span><span class="p">,</span> <span class="ss">uid: </span><span class="s1">'123'</span><span class="p">,</span> <span class="ss">provider: </span><span class="s1">'twitter'</span><span class="p">)</span> <span class="n">first</span> <span class="o">=</span> <span class="n">build</span><span class="p">(</span><span class="ss">:registration</span><span class="p">,</span> <span class="ss">provider: </span><span class="s1">'twitter'</span><span class="p">,</span> <span class="ss">uid: </span><span class="s1">'123'</span><span class="p">)</span> <span class="n">second</span> <span class="o">=</span> <span class="n">build</span><span class="p">(</span><span class="ss">:registration</span><span class="p">,</span> <span class="ss">provider: </span><span class="s1">'twitter'</span><span class="p">,</span> <span class="ss">uid: </span><span class="s1">'234'</span><span class="p">)</span> <span class="n">third</span> <span class="o">=</span> <span class="n">build</span><span class="p">(</span><span class="ss">:registration</span><span class="p">,</span> <span class="ss">provider: </span><span class="s1">'facebook'</span><span class="p">,</span> <span class="ss">uid: </span><span class="s1">'123'</span><span class="p">)</span> <span class="n">expect</span><span class="p">(</span><span class="n">first</span><span class="p">).</span><span class="nf">not_to</span> <span class="n">be_valid</span> <span class="n">expect</span><span class="p">(</span><span class="n">second</span><span class="p">).</span><span class="nf">to</span> <span class="n">be_valid</span> <span class="n">expect</span><span class="p">(</span><span class="n">third</span><span class="p">).</span><span class="nf">to</span> <span class="n">be_valid</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>The three variables, <code class="language-plaintext highlighter-rouge">first</code>, <code class="language-plaintext highlighter-rouge">second</code>, and <code class="language-plaintext highlighter-rouge">third</code>, are aligned by the <code class="language-plaintext highlighter-rouge">=</code> sign, and the method invocations on the right are aligned by commas. Here are the benefits I see with this:</p> <ul> <li> <p>On the left side, you can easily scan variable names. If the body of the code that uses these variables is complicated, you can index it visually – you look at the variable on the left side, you check what you have on the right. It’s <em>very</em> rare that I read variable names like normal text – left-to-right, top-to-bottom. I just scan them quickly and then consult the “index” as I’m reading the body of the code.</p> </li> <li> <p>On the right side, we have some very homogeneous invocations of the <code class="language-plaintext highlighter-rouge">build</code> method. The thing we’re interested in the most are the <em>differences</em> between them (what is the <code class="language-plaintext highlighter-rouge">provider</code>, what is the <code class="language-plaintext highlighter-rouge">uid</code>?) and when they’re ordered like this, it’s easy to see them at a glance.</p> </li> </ul> <p>This might not be too obvious in a short piece of code, but with larger chunks of text, it gets more and more useful. Arguably, you don’t want to end up with large chunks of code, but it depends. For instance:</p> <div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$icon-arrow-thick-right</span><span class="p">:</span> <span class="s2">"\e600"</span><span class="p">;</span> <span class="nv">$icon-arrow-thick-left</span><span class="p">:</span> <span class="s2">"\e601"</span><span class="p">;</span> <span class="nv">$icon-arrow-thick-up</span><span class="p">:</span> <span class="s2">"\e602"</span><span class="p">;</span> <span class="nv">$icon-arrow-thick-down</span><span class="p">:</span> <span class="s2">"\e603"</span><span class="p">;</span> <span class="nv">$icon-arrow-thick-b-right</span><span class="p">:</span> <span class="s2">"\e604"</span><span class="p">;</span> <span class="nv">$icon-arrow-thick-b-up</span><span class="p">:</span> <span class="s2">"\e605"</span><span class="p">;</span> <span class="nv">$icon-arrow-thick-b-left</span><span class="p">:</span> <span class="s2">"\e606"</span><span class="p">;</span> <span class="nv">$icon-arrow-thick-b-down</span><span class="p">:</span> <span class="s2">"\e607"</span><span class="p">;</span> <span class="nv">$icon-arrow-thin-right</span><span class="p">:</span> <span class="s2">"\e608"</span><span class="p">;</span> <span class="nv">$icon-arrow-thin-up</span><span class="p">:</span> <span class="s2">"\e609"</span><span class="p">;</span> <span class="nv">$icon-arrow-thin-left</span><span class="p">:</span> <span class="s2">"\e60a"</span><span class="p">;</span> </code></pre></div></div> <p>… and another 400 lines of the same. Compare to:</p> <div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$icon-arrow-thick-right</span><span class="p">:</span> <span class="s2">"\e600"</span><span class="p">;</span> <span class="nv">$icon-arrow-thick-left</span><span class="p">:</span> <span class="s2">"\e601"</span><span class="p">;</span> <span class="nv">$icon-arrow-thick-up</span><span class="p">:</span> <span class="s2">"\e602"</span><span class="p">;</span> <span class="nv">$icon-arrow-thick-down</span><span class="p">:</span> <span class="s2">"\e603"</span><span class="p">;</span> <span class="nv">$icon-arrow-thick-b-right</span><span class="p">:</span> <span class="s2">"\e604"</span><span class="p">;</span> <span class="nv">$icon-arrow-thick-b-up</span><span class="p">:</span> <span class="s2">"\e605"</span><span class="p">;</span> <span class="nv">$icon-arrow-thick-b-left</span><span class="p">:</span> <span class="s2">"\e606"</span><span class="p">;</span> <span class="nv">$icon-arrow-thick-b-down</span><span class="p">:</span> <span class="s2">"\e607"</span><span class="p">;</span> <span class="nv">$icon-arrow-thin-right</span><span class="p">:</span> <span class="s2">"\e608"</span><span class="p">;</span> <span class="nv">$icon-arrow-thin-up</span><span class="p">:</span> <span class="s2">"\e609"</span><span class="p">;</span> <span class="nv">$icon-arrow-thin-left</span><span class="p">:</span> <span class="s2">"\e60a"</span><span class="p">;</span> </code></pre></div></div> <p>This isn’t exactly “code” – it’s a table of contents of sorts. As such, it’s super useful to have it aligned. Just imagine a book where the index is described without alignment and how annoying it would be to orient yourself by pages/chapters. Again, on the left side you have an easily-scannable list, on the right, you have some very homogeneous content where you can easily notice the differences between lines (so you can filter the similarities out).</p> <p>CSS declarations are an interesting counter-example. You rarely want to scan “labels” on the left side. The fact that one CSS selector uses <code class="language-plaintext highlighter-rouge">margin</code> and <code class="language-plaintext highlighter-rouge">padding</code> isn’t as important as the values of those fields. And on the right side, you can have some very heterogenous content – pixels, pairs of numbers, quadruplets of numbers, text:</p> <div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.something</span> <span class="p">{</span> <span class="nl">margin-top</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span> <span class="nl">padding</span><span class="p">:</span> <span class="m">0</span> <span class="m">20px</span><span class="p">;</span> <span class="nl">text-align</span><span class="p">:</span> <span class="nb">right</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <p>Probably not much value in aligning it. There are some exceptions, like:</p> <div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">dl</span> <span class="p">{</span> <span class="nl">-webkit-margin-before</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span> <span class="nl">-webkit-margin-after</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span> <span class="py">margin-before</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span> <span class="py">margin-after</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <p>But this one can be hard to maintain automatically. I might do it if it’s isolated in a mixin, simply because it would be read far more often that it would be updated.</p> <h2 id="drawbacks"><a class="anchor" aria-hidden="true" href="#drawbacks">🔗</a>Drawbacks</h2> <p>Code like this can be more difficult to edit. You need to have an editor tool to help you (like <a href="https://github.com/vim-scripts/Align">this</a>, or <a href="https://github.com/godlygeek/tabular">this</a>, or <a href="https://github.com/junegunn/vim-easy-align">this</a>), and I think many people don’t, and align the code manually. Which doesn’t seem like a great idea. I don’t feel it’s worth enough to waste energy on it if your and your teammates’ editors don’t have the tooling to do it easily.</p> <p>Then there’s the issue with VCS logs. Every alignment change due to a new item means touching a lot of other items. Mind you, with <code class="language-plaintext highlighter-rouge">git diff</code>, there’s the <code class="language-plaintext highlighter-rouge">-b</code>/<code class="language-plaintext highlighter-rouge">--ignore-space-change</code> flag, so it might not be a huge problem in practice.</p> <p>Another potential problem is large differences in lengths. For instance:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">one</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">two</span> <span class="o">=</span> <span class="mi">2</span> <span class="n">three</span> <span class="o">=</span> <span class="mi">3</span> <span class="n">forty_two_hundred_and_twenty</span> <span class="o">=</span> <span class="mi">4220</span> </code></pre></div></div> <p>It’s now significantly harder to make the connection between <code class="language-plaintext highlighter-rouge">one</code> and <code class="language-plaintext highlighter-rouge">1</code>, because they’re quite far away from each other. My solution in situations like this is simple:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">one</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">two</span> <span class="o">=</span> <span class="mi">2</span> <span class="n">three</span> <span class="o">=</span> <span class="mi">3</span> <span class="n">forty_two_hundred_and_twenty</span> <span class="o">=</span> <span class="mi">4220</span> <span class="n">thirty_five_thousand</span> <span class="o">=</span> <span class="mi">35_000</span> </code></pre></div></div> <p>You might want to ask yourself why some variables are a single word, and others are an entire phrase. I find this kind of inconsistency to the amount of detail can hurt comprehension quite a lot. Maybe there’s a name you could introduce for a new concept? It’s not a “solution” to the alignment problem, all I’m saying is, it might be a minor smell to think about.</p> <p>I’ve also heard the argument that search-and-replace might end up being harder. I’m not convinced it’s a big issue, to be honest. Instead of looking for <code class="language-plaintext highlighter-rouge">foobar =</code>, you’d have to look for <code class="language-plaintext highlighter-rouge">foobar\s\+=</code>. It’s rare that only this one pattern will be enough to get things done anyway, and it’s very likely you’ll need manual intervention whatever set of patterns and replacements you come up with.</p> <h2 id="is-it-worth-it"><a class="anchor" aria-hidden="true" href="#is-it-worth-it">🔗</a>Is it worth it?</h2> <p>Depends. When you have a bunch of data with entries that have a very similar structure, I’d certainly want it to be aligned. Think the SCSS example above, or a database schema, or something like this:</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">payment_plans_for_select_box</span> <span class="o">=</span> <span class="p">[</span> <span class="p">{</span><span class="na">label</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Free</span><span class="dl">'</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="dl">'</span><span class="s1">free</span><span class="dl">'</span><span class="p">},</span> <span class="p">{</span><span class="na">label</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Business</span><span class="dl">'</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="dl">'</span><span class="s1">business</span><span class="dl">'</span><span class="p">},</span> <span class="p">{</span><span class="na">label</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Premium</span><span class="dl">'</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="dl">'</span><span class="s1">premium</span><span class="dl">'</span><span class="p">},</span> <span class="p">{</span><span class="na">label</span><span class="p">:</span> <span class="dl">'</span><span class="s1">SuperMega Plan</span><span class="dl">'</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="dl">'</span><span class="s1">super_mega</span><span class="dl">'</span><span class="p">},</span> <span class="p">]</span> </code></pre></div></div> <p>I’d also go for it in the testing case, where you often have a setup phase with several invocations of the same functions with different parameters. For anything else, it depends, and it seems like mostly personal preference. The “similar enough structure” idea is my key guiding point, in all cases.</p> <p>It’s tricky to apply, because it’s very contextual. How much is “similar enough”? Difficult to say. And it’s definitely not something that an automated tool can determine (at this time). It’s one of the reasons I dislike <code class="language-plaintext highlighter-rouge">gofmt</code>, it removes any alignment even when there would be a very noticeable benefit from it.</p> <p><strong>Update</strong>: Turns out, not exactly. Gofmt removes alignment from, say, multiple <code class="language-plaintext highlighter-rouge">var</code> lines, but if they’re grouped in a single <code class="language-plaintext highlighter-rouge">var ( )</code> statement, it actually aligns them. Which makes sense, I suppose – it provides a way for the developer to communicate “this is a group with similarities”, and the tool responds to it.</p> <p>Either way, whether you decide to apply this rule or not, it’s a good idea to know <em>why</em>, in more concrete terms than “it feels right/wrong”. I hope I’ve given you a possible answer to this question.</p> Sun, 13 Nov 2016 14:30:00 +0100 https://andrewra.dev/2016/11/13/code-alignment/ https://andrewra.dev/2016/11/13/code-alignment/ rules Building a Better gf Mapping <p>I recently started working with ember.js. It’s a nice framework, but, like most newish technologies, Vim support is minimal. So, as I usually do in such cases, I started working on some tools to help me out with navigation. Tim Pope’s <a href="https://github.com/tpope/vim-projectionist">projectionist</a> helped a lot, but I wanted more, so I started building it up in what would later be published as <a href="https://github.com/AndrewRadev/ember_tools.vim">ember_tools</a>.</p> <p>The biggest feature was the <code class="language-plaintext highlighter-rouge">gf</code> mapping (“go to file”), which was inspired by the one in vim-rails. Using <code class="language-plaintext highlighter-rouge">gf</code> in a rails project almost always does “the right thing” for whatever you can think of. So, I poked around in that one, tried to figure out how it was implemented. In the process, I learned a few things and had a bunch of pretty good ideas, which I’ll talk about in this blog post.</p> <!-- more --> <h2 id="some-basics"><a class="anchor" aria-hidden="true" href="#some-basics">🔗</a>Some basics</h2> <p>Before I start with the interesting stuff, let me explain a few basics you might need to know in order to understand it. Feel free to skip this section if you’re feeling confident in your Vimscript skills.</p> <p>First, the way you ordinarily customize the <code class="language-plaintext highlighter-rouge">gf</code> mapping is not by directly remapping it, but by modifying the “include expression”, or <code class="language-plaintext highlighter-rouge">includeexpr</code>. It’s an expression that gets evaluated every time you press <code class="language-plaintext highlighter-rouge">gf</code> or <code class="language-plaintext highlighter-rouge">&lt;c-w&gt;f</code> or anything in that family of mappings. In this particular scenario, things are implemented a bit differently, but it’s useful to know the concept.</p> <p>A different setting that modifies some core Vim behaviour is <code class="language-plaintext highlighter-rouge">iskeyword</code>. It contains a comma-separated list of all (groups of) keys that are considered part of a “word”. This affects what pressing <code class="language-plaintext highlighter-rouge">w</code> does, it affects <code class="language-plaintext highlighter-rouge">expand('&lt;cword&gt;')</code>, syntax highlighting, all sorts of things. It’s generally pretty risky to try changing it from its default. Recent versions of Vim allow some finer-grained control over some of this behaviour, but this one setting remains a powerful and dangerous tool that we’re going to use in a very careful way later.</p> <p>On a completely unrelated note, <em>autocommands</em> are sort of like Vim events. You can listen to an autocommand like <code class="language-plaintext highlighter-rouge">BufWrite</code> (when Vim writes the buffer to a file), or <code class="language-plaintext highlighter-rouge">InsertLeave</code> (when Vim exits insert mode) to run any old Vim command at the right time.</p> <p>And the last concept that you’ll need to know about, is the <code class="language-plaintext highlighter-rouge">execute</code> command, often shortened to <code class="language-plaintext highlighter-rouge">exe</code>. It’s sort of like eval in most languages, but it doesn’t allow you to evaluate a random expression, only a command. For instance, if you have some sequence of keypresses that you’ve been building up, like</p> <div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> keypress_sequence <span class="p">=</span> number_of_steps_to_the_right <span class="p">.</span> <span class="s2">"l"</span> </code></pre></div></div> <p>You can’t just run the sequence with <code class="language-plaintext highlighter-rouge">normal! keypress_sequence</code>. That would run the literal key sequence “keypress_sequence”. Instead, you can run <code class="language-plaintext highlighter-rouge">exe "normal! ".keypress_sequence</code> to evaluate the variable <code class="language-plaintext highlighter-rouge">keypress_sequence</code> to its string value and execute the <code class="language-plaintext highlighter-rouge">normal!</code> command with the right thing.</p> <p>And, if you didn’t figure it out from the last paragraph, in Vimscript, the dot is used for string concatenation, so:</p> <div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> a_hundred <span class="p">=</span> <span class="s2">"100"</span> <span class="k">let</span> a_hundred_plus_one <span class="p">=</span> a_hundred <span class="p">.</span> <span class="s2">" + 1"</span> echo a_hundred_plus_one <span class="c">" -&gt; 100 + 1</span> </code></pre></div></div> <p>I can’t guarantee you won’t have to reach for <code class="language-plaintext highlighter-rouge">:help</code> in the following paragraphs, but hopefully this should be enough. And with that out of the way…</p> <h2 id="how-it-works-in-vim-rails"><a class="anchor" aria-hidden="true" href="#how-it-works-in-vim-rails">🔗</a>How it works in vim-rails</h2> <p>If you poke around enough in vim-rails’ source code, looking for how <code class="language-plaintext highlighter-rouge">gf</code> works, you’ll likely end up in a place that has the following line:</p> <div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cmap <span class="p">&lt;</span><span class="k">buffer</span><span class="p">&gt;&lt;</span>script<span class="p">&gt;&lt;</span>expr<span class="p">&gt;</span> <span class="p">&lt;</span>Plug<span class="p">&gt;&lt;</span><span class="k">cfile</span><span class="p">&gt;</span> rails#<span class="k">cfile</span><span class="p">(</span><span class="s1">'delegate'</span><span class="p">)</span> </code></pre></div></div> <p>It maps the (fake) key sequence <code class="language-plaintext highlighter-rouge">&lt;Plug&gt;&lt;cfile&gt;</code> to the expression <code class="language-plaintext highlighter-rouge">rails#cfile('delegate')</code>. Yes, <code class="language-plaintext highlighter-rouge">&lt;Plug&gt;</code> is a “key” of sorts. If you look at <code class="language-plaintext highlighter-rouge">:help &lt;Plug&gt;</code>, you’ll discover that it “can be used for an internal mapping, which is not to be matched with any key sequence”. If you follow some links, you’ll also find it’s “a special code that a typed key will never produce”.</p> <p>In general, <code class="language-plaintext highlighter-rouge">&lt;cfile&gt;</code> is something that expands on the command line to the filename under the cursor, based on Vim’s built-in rules for recognizing a filename. What Tim Pope does here is create a <em>new</em> expression, <code class="language-plaintext highlighter-rouge">&lt;Plug&gt;&lt;cfile&gt;</code>, which expands to whatever <code class="language-plaintext highlighter-rouge">rails#cfile('delegate')</code> returns.</p> <p>That last function is the one that actually does the heavy lifting of figuring out how the stuff under the cursor maps to an actual file. So that, in vim-rails, calling <code class="language-plaintext highlighter-rouge">:echo rails#cfile('delegate')</code> with the cursor on the word “user”, would output “app/models/user.rb”.</p> <p>The expression <code class="language-plaintext highlighter-rouge">&lt;Plug&gt;&lt;cfile&gt;</code> is mapped to that value on the command-line with a <code class="language-plaintext highlighter-rouge">cmap</code>. The following two lines hook this up to the <code class="language-plaintext highlighter-rouge">gf</code> mapping:</p> <div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nmap <span class="p">&lt;</span><span class="k">buffer</span><span class="p">&gt;&lt;</span><span class="k">silent</span><span class="p">&gt;</span> <span class="p">&lt;</span>Plug<span class="p">&gt;</span>RailsFind <span class="p">&lt;</span>SID<span class="p">&gt;:</span><span class="k">find</span> <span class="p">&lt;</span>Plug<span class="p">&gt;&lt;</span><span class="k">cfile</span><span class="p">&gt;&lt;</span>CR<span class="p">&gt;</span> <span class="c">" ... snip ...</span> nmap <span class="p">&lt;</span><span class="k">buffer</span><span class="p">&gt;</span> gf <span class="p">&lt;</span>Plug<span class="p">&gt;</span>RailsFind </code></pre></div></div> <p>This creates a normal-mode mapping for <code class="language-plaintext highlighter-rouge">&lt;Plug&gt;RailsFind</code>, just in case somebody wants to use a different mapping for it. What it actually does is call the <code class="language-plaintext highlighter-rouge">:find</code> command with the right filename. Interestingly enough, the <code class="language-plaintext highlighter-rouge">:find</code> command can find all kinds of files in the <code class="language-plaintext highlighter-rouge">path</code> setting, so there might be some value in playing around with that one… But that’s not something I’ve experimented with yet.</p> <p>The actual <code class="language-plaintext highlighter-rouge">gf</code> mapping just delegates to the plug-mapping. There’s a few other mappings around that area that set things up for <code class="language-plaintext highlighter-rouge">&lt;c-w&gt;f</code> and similar other mappings, but they work roughly the same way.</p> <p>So why is this interesting for ember tools? Well, a tiny problem I happened to have is that my ember app was in a subdirectory of a rails app. This would activate vim-rails for my ember files, and that means activating rails’ gf. In order to avoid something like this happening, I added this little workaround to <code class="language-plaintext highlighter-rouge">ember_tools</code>:</p> <div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">" Override gf if rails sets it after us</span> autocmd <span class="nb">User</span> Rails cmap <span class="p">&lt;</span><span class="k">buffer</span><span class="p">&gt;&lt;</span>expr<span class="p">&gt;</span> <span class="p">&lt;</span>Plug<span class="p">&gt;&lt;</span><span class="k">cfile</span><span class="p">&gt;</span> ember_tools#Includeexpr<span class="p">()</span> </code></pre></div></div> <p>Which I find pretty clever! See, thanks to Tim Pope’s <code class="language-plaintext highlighter-rouge">&lt;Plug&gt;&lt;cfile&gt;</code> setup, I only need to override that one mapping to return the results of calling my own function. That function can, at any point, <code class="language-plaintext highlighter-rouge">return rails#cfile('delegate')</code> to fall back to the default vim-rails behaviour. All the other related mappings work just fine. It’s an interesting way to achieve this without a complicated combination of settings.</p> <p>This also opens up some possibilities to extend vim-rails’ <code class="language-plaintext highlighter-rouge">gf</code> mapping! As powerful as it is, there’s a few improvements I wanted to do there as well… I started work on this in a script I called <a href="https://github.com/AndrewRadev/Vimfiles/blob/9b17ea1be4ad1c12882aae5068699e43ea4f9310/personal/plugin/rails_extra.vim">rails_extra</a>. Currently, it’s a mess of functions from various of my plugins, and it’s not organized particularly well. It works wonders, though. I’ve made <code class="language-plaintext highlighter-rouge">gf</code> follow custom rspec matchers, look for factories, go to <code class="language-plaintext highlighter-rouge">require</code>d assets in the asset pipeline. My favorite extension, though, is <code class="language-plaintext highlighter-rouge">gf</code> on translation keys, which I’ll talk about next.</p> <h2 id="finding-translations"><a class="anchor" aria-hidden="true" href="#finding-translations">🔗</a>Finding translations</h2> <p>Internationalization has always been a pain for me to keep track of. It’s a necessity, but it does complicate things a lot and I often wish I had better tooling for it. For ember, I use the <a href="https://github.com/jamesarosen/ember-i18n">ember-i18n</a> project, and I hook up a helper to be able to do something like:</p> <div class="language-handlebars highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">{{</span><span class="nv">i18n</span> <span class="s1">'some.nested.translation_key'</span><span class="k">}}</span> </code></pre></div></div> <p>So how do I set things up so I can <code class="language-plaintext highlighter-rouge">gf</code> on the key and end up on the translation? The tricky bit here is not the actual file. If I can detect I’m on a translation key, I can easily say that the file to be returned is, in my case, <code class="language-plaintext highlighter-rouge">../config/en.yml</code>. (In this particular project, the translations are the same ones defined for the rails project, which happens to contain my ember one.)</p> <p>Detecting the key might look like this:</p> <div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">function</span><span class="p">!</span> EmberGfTranslations<span class="p">()</span> <span class="c">" ... snip ...</span> <span class="k">if</span> <span class="p">!</span>ember_tools#<span class="nb">search</span>#UnderCursor<span class="p">(</span><span class="s1">'i18n [''"]\zs\k\+[''"]'</span><span class="p">)</span> <span class="k">return</span> <span class="s1">''</span> <span class="k">endif</span> <span class="k">let</span> translation_key <span class="p">=</span> <span class="nb">expand</span><span class="p">(</span><span class="s1">'&lt;cword&gt;'</span><span class="p">)</span> <span class="c">" ... snip ...</span> <span class="k">endfunction</span> </code></pre></div></div> <p>I’m not going to explain <code class="language-plaintext highlighter-rouge">ember_tools#search#UnderCursor</code>, because it would take me another full blog post to go over all the weird logic in there. Suffice it to say, it works reasonably well for checking whether the cursor is somewhere on text that matches this pattern and moving said cursor to the beginning of it. In our case, due to the <code class="language-plaintext highlighter-rouge">\zs</code>, the beginning happens to be right on top of the translation key.</p> <p>Note, by the way, that the pattern matches <code class="language-plaintext highlighter-rouge">\k\+</code> (in quotes). The <code class="language-plaintext highlighter-rouge">\k</code> pattern matches “keyword” arguments, and, ordinarily, the <code class="language-plaintext highlighter-rouge">.</code> character is not one of them (it causes way more trouble than it’s worth). But in most ember identifiers I care for, <code class="language-plaintext highlighter-rouge">.</code>, <code class="language-plaintext highlighter-rouge">-</code>, and <code class="language-plaintext highlighter-rouge">/</code> are perfectly valid. Think of components, for instance – <code class="language-plaintext highlighter-rouge">{{user/profile-picture}}</code>. So I decided I can just do something like this:</p> <div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> saved_iskeyword <span class="p">=</span> &amp;<span class="nb">iskeyword</span> <span class="k">let</span> callbacks <span class="p">=</span> <span class="p">[</span> <span class="se"> \</span> <span class="p">...</span> <span class="se"> \</span> <span class="s1">'EmberGfImports'</span><span class="p">,</span> <span class="se"> \</span> <span class="s1">'EmberGfTranslations'</span><span class="p">,</span> <span class="se"> \</span> <span class="s1">'EmberGfRoutes'</span><span class="p">,</span> <span class="se"> \</span> <span class="p">...</span> <span class="se"> \</span> <span class="p">]</span> <span class="k">for</span> callback <span class="k">in</span> callbacks <span class="k">try</span> <span class="k">set</span> <span class="nb">iskeyword</span><span class="p">+=.,-,</span>/ <span class="c">" look for the right callback to invoke</span> <span class="k">finally</span> <span class="c">" whatever happens, just restore the old value of `iskeyword`</span> <span class="k">let</span> &amp;<span class="nb">iskeyword</span> <span class="p">=</span> saved_iskeyword <span class="k">endtry</span> <span class="k">endfor</span> </code></pre></div></div> <p>Basically, I just add everything I need to <code class="language-plaintext highlighter-rouge">iskeyword</code>, and restore it afterwards. No harm, no foul.</p> <p>Anyway, after storing the <code class="language-plaintext highlighter-rouge">translation_key</code> I found under ther cursor, I can split it by dots to get the list of sections in the rails yaml file. In the example, I’d have a list like <code class="language-plaintext highlighter-rouge">['some', 'nested', 'translation_key']</code>. So what now?</p> <p>The way the <code class="language-plaintext highlighter-rouge">gf</code> mapping works, we can only have our custom include expression return a file path. In our case, the file is easy – the big yaml file with translations, <code class="language-plaintext highlighter-rouge">config/locales/en.yml</code>. But how do we make Vim jump to the right place where the translation is found?</p> <p>Well, the solution I came up with… I’m honestly kind of surprised that it works. Let me start a new section for that.</p> <h2 id="when-you-have-a-problem-just-put-exe-until-its-fixed"><a class="anchor" aria-hidden="true" href="#when-you-have-a-problem-just-put-exe-until-its-fixed">🔗</a>When you have a problem, just put :exe until it’s fixed</h2> <p>Imagine you want to set things up so that the next time a particular file is opened, a particular command is run, only once. It might look something like this:</p> <div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code>autocmd <span class="nb">BufEnter</span> <span class="sr">/some/</span><span class="k">file</span>/<span class="nb">path</span> <span class="p">:</span>echo <span class="s2">"foo"</span> autocmd <span class="nb">BufEnter</span> <span class="sr">/some/</span><span class="k">file</span>/<span class="nb">path</span> <span class="p">:</span><span class="k">call</span> ClearFileOpenCallback<span class="p">()</span> " how <span class="k">to</span> <span class="k">do</span> this? </code></pre></div></div> <p>The first autocommand executes the thing we need, the second one clears that callback. How can we clear the callbacks? A simple way is to group them with a specific name. Here’s how that might look:</p> <div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">function</span> SetCallbacks<span class="p">()</span> augroup file_open_callback autocmd<span class="p">!</span> <span class="c">" set up a bunch of callbacks</span> augroup END <span class="k">endfunction</span> <span class="k">function</span> ClearCallbacks<span class="p">()</span> augroup file_open_callback autocmd<span class="p">!</span> augroup END <span class="k">endfunction</span> </code></pre></div></div> <p>By naming a group called <code class="language-plaintext highlighter-rouge">file_open_callback</code>, we can refer to this temporary autocommand group. The initial <code class="language-plaintext highlighter-rouge">autocmd!</code> in the beginning of the group clears everything that’s ever been defined in it. That’s why our “clear” function is just the group and the clearing command.</p> <p>So the setup makes sense – one function sets a callback, which, after it’s done with its work, triggers the other function. Said function clears the callback – and that’s how you get a one-shot autocommand.</p> <p>Now, the question is, how do we set up the autocommand so that it searches for the pattern we give it? Just put <code class="language-plaintext highlighter-rouge">:exe</code> until it works:</p> <div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">function</span><span class="p">!</span> ember_tools#SetFileOpenCallback<span class="p">(</span>filename<span class="p">,</span> <span class="p">...)</span> <span class="c">" all the words to search for, provided as variable arguments:</span> <span class="k">let</span> searches <span class="p">=</span> <span class="nv">a:000</span> <span class="c">" use absolute paths:</span> <span class="k">let</span> filename <span class="p">=</span> <span class="nb">fnamemodify</span><span class="p">(</span><span class="nv">a:filename</span><span class="p">,</span> <span class="s1">':p'</span><span class="p">)</span> augroup ember_tools_file_open_callback autocmd<span class="p">!</span> <span class="c">" start by going to the top of the file:</span> exe <span class="s1">'autocmd BufEnter '</span><span class="p">.</span>filename<span class="p">.</span><span class="s1">' normal! gg'</span> <span class="c">" search for every pattern, one after the other:</span> <span class="k">for</span> pattern <span class="k">in</span> searches exe <span class="s1">'autocmd BufEnter '</span><span class="p">.</span>filename<span class="p">.</span><span class="s1">' call search("'</span><span class="p">.</span><span class="nb">escape</span><span class="p">(</span>pattern<span class="p">,</span> <span class="s1">'"\').'</span>"<span class="p">)</span>' <span class="k">endfor</span> <span class="c">" clear all callbacks, so we leave it a one-shot thing:</span> exe <span class="s1">'autocmd BufEnter '</span><span class="p">.</span>filename<span class="p">.</span><span class="s1">' call ember_tools#ClearFileOpenCallback()'</span> augroup END <span class="k">endfunction</span> <span class="k">function</span><span class="p">!</span> ember_tools#ClearFileOpenCallback<span class="p">()</span> augroup ember_tools_file_open_callback autocmd<span class="p">!</span> augroup END <span class="k">endfunction</span> </code></pre></div></div> <p>The exe calls just evaluate down to autocommands keyed to that particular file. Each of the steps is triggered only on <code class="language-plaintext highlighter-rouge">BufEnter</code> of this one particular file, and they run in a sequence. Once they’re all called, we can clear the callbacks.</p> <p>After getting the translation keys in an array, we can split up the keys and search for them in sequence with (something similar to):</p> <div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">:</span><span class="k">call</span> ember_tools#SetFileOpenCallback<span class="p">(</span><span class="s1">'config/locales/en.yml'</span><span class="p">,</span> <span class="s1">'some'</span><span class="p">,</span> <span class="s1">'nested'</span><span class="p">,</span> <span class="s1">'translation_key'</span><span class="p">)</span> </code></pre></div></div> <p>This ends up creating a list of autocommands that looks like this:</p> <div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code>autocmd <span class="nb">BufEnter</span> <span class="sr">/path/</span><span class="k">to</span><span class="sr">/config/</span>locales/<span class="k">en</span><span class="p">.</span>yml normal<span class="p">!</span> gg autocmd <span class="nb">BufEnter</span> <span class="sr">/path/</span><span class="k">to</span><span class="sr">/config/</span>locales/<span class="k">en</span><span class="p">.</span>yml <span class="k">call</span> <span class="nb">search</span><span class="p">(</span><span class="s2">"some"</span><span class="p">)</span> autocmd <span class="nb">BufEnter</span> <span class="sr">/path/</span><span class="k">to</span><span class="sr">/config/</span>locales/<span class="k">en</span><span class="p">.</span>yml <span class="k">call</span> <span class="nb">search</span><span class="p">(</span><span class="s2">"nested"</span><span class="p">)</span> autocmd <span class="nb">BufEnter</span> <span class="sr">/path/</span><span class="k">to</span><span class="sr">/config/</span>locales/<span class="k">en</span><span class="p">.</span>yml <span class="k">call</span> <span class="nb">search</span><span class="p">(</span><span class="s2">"translation_key"</span><span class="p">)</span> autocmd <span class="nb">BufEnter</span> <span class="sr">/path/</span><span class="k">to</span><span class="sr">/config/</span>locales/<span class="k">en</span><span class="p">.</span>yml <span class="k">call</span> ember_tools#ClearFileOpenCallback<span class="p">()</span> </code></pre></div></div> <p>After that, we return <code class="language-plaintext highlighter-rouge">config/locales/en.yml</code> and the autocommands kick in, position the cursor in the right place, and then unset themselves. Not that complicated, even if it is kind of hacky. So the function that takes care of going to the translations ends up looking like this:</p> <div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">function</span><span class="p">!</span> EmberGfTranslations<span class="p">()</span> <span class="c">" ... snip ...</span> <span class="k">if</span> <span class="p">!</span>ember_tools#<span class="nb">search</span>#UnderCursor<span class="p">(</span><span class="s1">'i18n [''"]\zs\k\+[''"]'</span><span class="p">)</span> <span class="k">return</span> <span class="s1">''</span> <span class="k">endif</span> <span class="k">let</span> translation_key <span class="p">=</span> <span class="nb">expand</span><span class="p">(</span><span class="s1">'&lt;cword&gt;'</span><span class="p">)</span> <span class="k">let</span> translations_file <span class="p">=</span> <span class="nb">fnamemodify</span><span class="p">(</span><span class="s1">'config/locales/en.yml'</span><span class="p">,</span> <span class="s1">':p'</span><span class="p">)</span> <span class="c">" Prepare the arguments to the file open callback function:</span> <span class="c">" - The filename to fire on</span> <span class="c">" - The pieces of the key, searched one by one</span> <span class="c">"</span> <span class="k">let</span> callback_args <span class="p">=</span> <span class="p">[</span>translations_file<span class="p">]</span> <span class="k">call</span> <span class="nb">extend</span><span class="p">(</span>callback_args<span class="p">,</span> <span class="k">split</span><span class="p">(</span>translation_key<span class="p">,</span> <span class="s1">'\.'</span><span class="p">))</span> <span class="c">" The "call" function calls a function. Vimscript can be an odd language</span> <span class="c">" sometimes.</span> <span class="k">call</span> <span class="k">call</span><span class="p">(</span><span class="s1">'ember_tools#SetFileOpenCallback'</span><span class="p">,</span> callback_args<span class="p">)</span> <span class="c">" After we've set up the autocommands to fire when the file is opened, we</span> <span class="c">" return the file path and let Vim take care of the actually opening it (in</span> <span class="c">" a split, or tab, or whatever, depending on the mapping that was invoked)</span> <span class="k">return</span> translations_file <span class="k">endfunction</span> </code></pre></div></div> <p>There’s a few Vimscript-y weirdnesses in there, but hopefully, it should be readable enough as it is.</p> <h2 id="to-sum-up"><a class="anchor" aria-hidden="true" href="#to-sum-up">🔗</a>To Sum Up</h2> <p>The <code class="language-plaintext highlighter-rouge">gf</code> mapping is a useful abstraction to navigate your code, and extending it yourself is not super difficult to do. You take the text under the cursor, pick it apart with regexes and figure out what the path you’re looking for is. You do need some background knowledge, but the <code class="language-plaintext highlighter-rouge">:help</code> files are always nearby to help you out. If you’re working on a particular framework that doesn’t have special support for it, consider writing your own.</p> <p>If you’ve never written Vimscript so far, it could be a nice introduction that’ll give you a good productivity boost. Hope this article has given you some basic tools to get you started, and some clever ideas to inspire you to keep going.</p> Wed, 09 Mar 2016 11:44:00 +0100 https://andrewra.dev/2016/03/09/building-a-better-gf-mapping/ https://andrewra.dev/2016/03/09/building-a-better-gf-mapping/ vim ember The New(ish) Facebook Authentication Process <p>Facebook changed their authentication process <a href="https://developers.facebook.com/docs/apps/upgrading#upgrading_v2_0_user_ids">fairly recently</a>. Instead of using a global user id, users would now get an app-specific id instead. This took me by surprise, and I had to jump through some hoops to get stuff to work with our setup. This is a short post that outlines what we did to work around the problem.</p> <!-- more --> <h2 id="the-good-old-way"><a class="anchor" aria-hidden="true" href="#the-good-old-way">🔗</a>The Good Old Way</h2> <p>The way things worked in The Old Times, you had a facebook app, authenticated with it, got a user id and saved it to the database. Upon the next facebook authentication, you’d get the same id and find it in the database, logging the relevant user in. Usually, you’d maintain multiple different apps for production, development, staging, and whatever other domain you need, since apps were only limited to a single domain.</p> <p>For rails developers, <a href="https://github.com/mkdynamic/omniauth-facebook">Omniauth</a> helped a lot. With that, all you needed to do was hook up a link to the right url. Sending the user to it would redirect them to facebook, log them in there, and provide you the id in <code class="language-plaintext highlighter-rouge">request.env['omniauth.auth']</code> under the key <code class="language-plaintext highlighter-rouge">:uid</code>.</p> <h2 id="the-changes"><a class="anchor" aria-hidden="true" href="#the-changes">🔗</a>The Changes</h2> <p>Of course, with app-local ids, this gets a bit more complicated. Having three different apps no longer works well, since you have different ids for the same users. Putting them in the database means users can only log in with the facebook app they used to sign up. If your development database is a clone of production, it’s no longer easy to do tests on your local machine.</p> <p>The solution in this case is using <a href="https://developers.facebook.com/docs/apps/test-apps">test apps</a>. A test app is created from a normal app, has different access credentials, but the same user ids as its original. It’s not public, so you can’t cheat the system by using it in production, but you <em>can</em> use it for testing purposes on a domain other than your main one.</p> <h2 id="facebook-for-business"><a class="anchor" aria-hidden="true" href="#facebook-for-business">🔗</a>Facebook for Business</h2> <p>But what if you have a legitimate need for multiple domains in production? At Knowable, we had both “knowable.org” and “joinpolymer.com”, since we had two separately branded products backed by the same database. “Polymer” was our new project, and we wanted to introduce social logins to it. That’s when we noticed the app id changes. We couldn’t use test apps for the purpose, because they’re not public. We couldn’t use the same facebook app for both domains, either. So what now?</p> <p>The solution was “<a href="https://www.facebook.com/business">facebook for business</a>”. This was probably created for a completely different purpose, but it also happens to be the offical way to work around this issue. If you create a “facebook business” and you claim both apps as belonging to it (you just need to be an admin to do that), you can request an additional field from facebook called <code class="language-plaintext highlighter-rouge">token_for_business</code> (<a href="https://developers.facebook.com/docs/apps/for-business">documentation</a>). This is a string that’s the same across all apps of that business, unique per user. This means that storing that in the database lets you use it for authentication alongside the id they give you.</p> <p>As far as I know, there’s no way to pull this off easily in Omniauth. We needed to fetch it in a separate query to facebook using the <a href="https://github.com/arsduo/koala">koala</a> gem:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">get_facebook_business_token</span><span class="p">(</span><span class="n">omniauth</span><span class="p">)</span> <span class="n">graph</span> <span class="o">=</span> <span class="no">Koala</span><span class="o">::</span><span class="no">Facebook</span><span class="o">::</span><span class="no">API</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">omniauth</span><span class="p">[</span><span class="s1">'credentials'</span><span class="p">][</span><span class="s1">'token'</span><span class="p">])</span> <span class="n">profile</span> <span class="o">=</span> <span class="n">graph</span><span class="p">.</span><span class="nf">get_object</span><span class="p">(</span><span class="s1">'me'</span><span class="p">,</span> <span class="ss">fields: </span><span class="s1">'token_for_business'</span><span class="p">)</span> <span class="n">profile</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="s1">'token_for_business'</span><span class="p">)</span> <span class="k">end</span> </code></pre></div></div> <p>Usually, it’d be enough to look for the user/authentication with the recommended Omniauth oneliner:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">auth</span> <span class="o">=</span> <span class="n">find_by</span><span class="p">(</span><span class="ss">provider: </span><span class="n">omniauth</span><span class="p">[</span><span class="s1">'provider'</span><span class="p">],</span> <span class="ss">uid: </span><span class="n">omniauth</span><span class="p">[</span><span class="s1">'uid'</span><span class="p">])</span> </code></pre></div></div> <p>In case this didn’t work and the provider is “facebook”, you’d simply get the business token from facebook and try fetching the authentication with that as well:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">business_token</span> <span class="o">=</span> <span class="n">get_facebook_business_token</span><span class="p">(</span><span class="n">omniauth</span><span class="p">)</span> <span class="n">auth</span> <span class="o">=</span> <span class="n">find_by</span><span class="p">(</span><span class="ss">provider: </span><span class="s1">'facebook'</span><span class="p">,</span> <span class="ss">facebook_business_token: </span><span class="n">business_token</span><span class="p">)</span> </code></pre></div></div> <p>Not ideal, given that we need to put extra fields on the authentication record specifically for facebook, but it works fine for this one special case.</p> <h2 id="impact"><a class="anchor" aria-hidden="true" href="#impact">🔗</a>Impact</h2> <p>I’m slightly surprised that <code class="language-plaintext highlighter-rouge">omniauth-facebook</code> doesn’t provide something for this, but I’m assuming it’s a bit of an edge case to need two separate apps on two separate domains. And there’s no point attempting to fetch the business token for “normal” apps.</p> <p>Another interesting thing that we discovered is that facebook no longer provides a username, either. And, a friend working in a different company told me they may even not provide an email, if the user happens to not have confirmed their email to facebook. It gets harder and harder to create a fully functional user out of facebook data, but I suppose their changes are mostly geared towards protecting users’ privacy. Hard to say how to handle these cases, though.</p> Fri, 06 Mar 2015 15:18:00 +0100 https://andrewra.dev/2015/03/06/the-newish-facebook-authentication-process/ https://andrewra.dev/2015/03/06/the-newish-facebook-authentication-process/ ruby rails facebook Small Refactorings <p>A common piece of advice for dealing with bad rails code is to avoid making huge rewrites all at once. Instead, make small fixes whenever you see a problem. An often-cited variant of this is the “boyscout rule” – leave the code better than you found it.</p> <p>This is solid advice. Putting hacks and workarounds would only make things worse. Rewriting swaths of code for even small features would take a long amount of time and introduce risk. The best approach is to make small refactorings and build new features with new, better, code.</p> <p>As with most good advice, this can still backfire. Ever seen an app with five different caching mechanisms and seven different event tracking approaches, all slightly different? This may have been a result of multiple developers going “this is horrible, I’ll fix it”. In the end, they created an even larger mess, despite their good intentions. How did this happen?</p> <!-- more --> <p>Let’s start with a different question.</p> <h2 id="what-is-a-good-refactoring"><a class="anchor" aria-hidden="true" href="#what-is-a-good-refactoring">🔗</a>What is a “good” refactoring?</h2> <p>In the ruby community, we care a lot about good code. We have blog posts, conference talks, and many books on the topic. The community still gets a lot of newcomers and we try to educate them on the importance of structured, well-tested code.</p> <p>Unfortunately, even if you took two experienced rails developers and put them in front of bad code, they’d probably disagree on the solution.</p> <ul> <li>Some would follow The Rails Way, trusting themselves to the framework and its well-structured conventions, others would insist on extracting the logic in service objects, separate engines, or microservices.</li> <li>Some would partition the code into many extremely small classes, others would call this “overcomplicating” and opt for a more coarse approach.</li> <li>Some would aggressively monitor and improve the performance of the app, others would scoff at the idea, claiming it’s a problem to be solved at a later time.</li> </ul> <p>If you’ve identified with one half of the statements, I’m willing to bet there are rails developers out there that take the exact opposite stances and still create well-working apps. As much as we wish there was only one good way of doing things, that doesn’t seem to be the case. Much of our decisions stem not from reason, but from our backgrounds, previous languages, what worked for us and what didn’t. This becomes a particular problem in larger teams working on separate parts of the application.</p> <h2 id="good-developers-doing-bad-things"><a class="anchor" aria-hidden="true" href="#good-developers-doing-bad-things">🔗</a>Good developers doing bad things</h2> <p>Note that I’m still talking about “good” developers. By “good” in this case, I mean they genuinely believe in writing readable, maintainable code, they have good intentions and try to follow basic programming principles. Still, it’s not surprising for them to end up with a mess, especially when working with legacy code.</p> <h3 id="unfinished-business">Unfinished business</h3> <p>Let’s look at a more concrete use case. A developer needs to make a change to existing functionality in the big old legacy app. The logic ends up being in one 300-line method call that mutates global variables, accesses information from constants set in initializers and responds to three optional boolean flags that contain double negatives in their usage (<code class="language-plaintext highlighter-rouge">dont_add_extra_taxes: false</code>). The developer shudders in horror, and decides to get rid of this monstrosity, so they create a brand new class for the changed functionality, and use that one instead.</p> <p>This is the best solution at this point in time. Adding even more hacks to this large method is a terrible option. And rewriting it would require finding all the ways it’s being used and thinking of a good way to reimplement them. Which would take a long time and hold considerable risk of errors. It’s much better to get the immediate work out of the way and then find some time to take care of the rest of the refactoring.</p> <p>The problem is, there are now two implementations of logic that’s only slightly different. The old method still exists. Even if the developer says they’ll “fix it later”, that “later” might never arrive. Depending on the development processes and company culture, it could be difficult to allocate time for this kind of refactoring. Especially since it doesn’t seem to hold any value – the functionality is there and it works, right? There’s no immediate rush. As a result, that developer might even leave the company before the time they get around to it. I’ve seen quite a few cases of code that was clearly meant to be part of a larger refactoring… By people no longer working there. Heck, I’m guilty of being that person myself.</p> <h3 id="so-many-people">So many people</h3> <p>Still, I said we’re talking about a big team, right? There’s lots of good developers in this imaginary company we’re looking at. Wouldn’t somebody else pick up the torch and finish the cleanup? They will, if they know about it and agree with it. If another developer has to make a change that ends up in that same terrible method, it’s not clear whether they’ll find the previous one’s attempt to reimplement it. Even if they do, it’s unlikely they’ll adapt this solution to fit their different use case, since that may involve quite some work as well. Not to mention what I previously talked about: different developers have different ideas of “good code”. They may even find the rewrite attempt and decide it’s not really that good and they can do a better job.</p> <p>In any case, it’s easy to repeat the previous process: Just create a new class that handles this use case.</p> <p>In the end, everybody that touches that code ends up reimplementing a small part of it, but without a coherent vision of where it should end up, we’re left with a lot of subtle duplication and the original code keeps chugging along untouched.</p> <h3 id="limited-scope">Limited scope</h3> <p>Consistency is key here. When we work on a particular feature, it’s easy to get tunnel vision and see everything in light of this one particular use case. Looking at the original code and all of its quirks, it’s really easy to dismiss them as irrelevant. And, for the limited scope we need it for, that may be true. But the <code class="language-plaintext highlighter-rouge">dont_add_extra_taxes</code> option was created because of some particular feature. Understanding all those features is the only way towards really rewriting the code.</p> <p>This could, and should, happen gradually, since it would be practically impossible to figure it all out without trial and error. It often ends up, however, that “gradually” means “write this one use case and then drop it”. Continuing to reimplement everything may require re-rewriting the new code, or even deleting it and starting over. This is difficult to do, especially when it doesn’t feel like that work is needed at the time. When you repeat this process for multiple developers, all of them working on the code at different times and in different contexts, it’s no surprise that the end result is an incoherent mess.</p> <h3 id="company-culture">Company culture</h3> <p>As a result of all of the above, some teams even end up being paranoid of “refactoring” (in quotes). When too many developers have tried to refactor the code and either broke production or made things worse in the long run, the atmosphere deteriorates. It gets more and more difficult to refactor and it often has to be done quickly and sloppily between tasks. This itself leads to more problems, which closes the downward spiral.</p> <p>This is a difficult environment. A nice blog post that tackles the topic is <a href="http://andrzejonsoftware.blogspot.de/2014/01/refactoring-human-factor.html">“Refactoring: The Human Factor”</a> by Andrzej Krzywda.</p> <h2 id="solutions"><a class="anchor" aria-hidden="true" href="#solutions">🔗</a>Solutions</h2> <p>So if small, gradual refactorings end up causing all these problems, which path should we take instead? Keep hacking up the old method, making it larger and even more complex? Stop working on features and devote a week or two for a complete rewrite of this one method?</p> <p>Both of these sound ridiculous, and for good reason. Small refactorings are still our best tool to deal with these situations. I’d say we only need to be aware of the limitations of the approach and figure out how to mitigate them.</p> <h3 id="planning">Planning</h3> <p>One of the simplest things you can do, before jumping into refactoring, is <strong>think</strong>. Our first instinct can often be wrong and inconsistent with the rest of the app. Once we’ve started down some particular path, it’s difficult to turn around and make different choices, so we often push forward, making things somehow fit in a kludgy way.</p> <p>It would be best to avoid this from the very beginning. Think about the effect of the refactoring and how it’s going to fit with the system. Consider what new abstractions have to be created and how these will affect the architecture. Make drawings, random notes, mind maps, but get a good, clear picture of where you’re going before heading out.</p> <p>Even so, it’s almost certain you’ll make mistakes. Be prepared to adjust your vision and iterate on the refactoring multiple times. The fact that it’s new code doesn’t automatically make it good, especially if it’s introducing new structure that may or may not work well with the existing one.</p> <h3 id="communication">Communication</h3> <p>Another somewhat obvious way to avoid all these issues is to have a good, clear line of communication between developers. If you’re going deep into the bowels of bad code, it pays off to ask around for help. Maybe someone has already been there and started working on a replacement. If you can take that and extend it, or build something else with a consistent interface, you’re on the right track towards finally getting rid of the big bad method.</p> <p>This can be hard to do, but if your team has no shared understanding of features, you’ll end up having trouble sooner or later. Share knowledge in standups, pair with people on tricky problems, ask for advice. Do your best to spread the knowledge around and it’ll pay off pretty soon.</p> <h3 id="patience">Patience</h3> <p>Rash behaviour is especially common in newbie developers, possibly just out of university. They often attempt to apply their fresh knowledge to fix the codebase without taking the time to understand it well enough. Patience is key. It’s important not to go overboard with cleanup while you’re still unfamiliar with the code, unless it’s a simple fix or you’re pairing with another team member.</p> <p>Of course, it’s also important not to wait <strong>too long</strong>. When you stare into the heart of legacy code, the legacy code stares back. Even if the convoluted logic was written this way for a good reason, that doesn’t make it good code. Understand what it does, then figure out how to replicate the functionality in a better way.</p> <h3 id="persistence">Persistence</h3> <p>The final trick is to persist. With rails apps, it’s usually not that difficult to understand any given piece of functionality. You can use a pen and paper to chart the interactions, you can put debug statements in strategic places, but eventually, if you persist, you’ll figure out what the code does and how it does it (and feel very clever as a result). The trick is finding the time and energy to do so.</p> <p>Only several times in my career have I had the opportunity to really sit down and devote some undisturbed time to understand a large piece of code and rewrite it. These moments are rare and usually happen for odd reasons, but when they do, be sure to make good use of them. Otherwise, you could timebox yourself to, say, half an hour every day of trying to understand this one method. Put comments in it, describing what you know of it so far, or just write them down on paper. Build hypotheses, validate or reject them. With patience, you’ll eventually get to know every dark corner and you can then figure out an architecture that implements the same features in a better way.</p> <p>It’s not easy, but you have to pull the trigger at some point. You can skirt around on the edges of the method, you can wrap the old code in a new interface, but it will never really be gone until you dig deep into it and figure it out. By all means, finish your feature in a separate class first, get that out of the way and move the ticket to “Done”. But be sure to create a “Delete bad code” ticket and genuinely make the act of deletion a goal, in your head. Only when you meet that goal can you really pat yourself on the back for doing an excellent refactoring job.</p> <h2 id="conclusion"><a class="anchor" aria-hidden="true" href="#conclusion">🔗</a>Conclusion</h2> <p>One of the best ways to get rid of legacy code is to make small improvements. New features or changes should not be done in the old code, but should be created in new classes, with clean interfaces.</p> <p>However, this process relies on the assumption that you will eventually <strong>delete</strong> or <strong>fix</strong> the old code. Failing to do that is going to leave you with an ever-expanding app with no prospects of real improvement. It also doesn’t absolve you from thinking about the <strong>architecture</strong> of the new code and <strong>communicating</strong> with your teammates. Creating new components without a clear vision and without adapting them when needed only leads to more chaos.</p> <p>For a nice parable that tackles a similar topic, take a look at <a href="http://thecodelesscode.com/case/123">The Codeless Code</a>.</p> Tue, 29 Apr 2014 20:46:00 +0200 https://andrewra.dev/2014/04/29/small-refactorings/ https://andrewra.dev/2014/04/29/small-refactorings/ ruby rails The "Back to School" idea <p><a href="http://andrewradev.com/2013/08/18/ohm-2013/">OHM</a> was a place full of amazing ideas and clever hacks. In this atmosphere, <a href="http://shtrak.eu/vloo/who-s-that/">a friend</a> and I started talking about our education system and the age-old promise of “multimedia” in the classroom. It’s a popular topic these days, education. Startups like <a href="https://www.coursera.org/">coursera</a> are providing free online lessons, clever tools like <a href="http://letsgeddit.com/">geddit</a> are trying to improve teacher-student interaction. What Vloo and me discussed was using presentation slides in class in order to improve engagement.</p> <p>Something like this is already in use in my home country of Bulgaria. There are specialized classrooms equipped with computers and projectors, and every once in a while, the students have an “interactive class”, where they watch videos and perform quizzes. However, this is something that happens rarely and is considered a “special” lesson. I was more interested in turning any lesson into an actual lecture. Just as lecturers at tech conferences teach me about the latest, greatest web framework, it should be perfectly possible to use the same tools for a chemistry, or biology, or history lecture.</p> <p>As we were describing what a lesson would look like, we realized we could actually prepare one. We dubbed the event “Back to School” and I started working on a chemistry lecture to present at the local hackerspace, <a href="http://initlab.org/">initLab</a>.</p> <!-- more --> <h2 id="slides-make-everything-better"><a class="anchor" aria-hidden="true" href="#slides-make-everything-better">🔗</a>Slides Make Everything Better</h2> <p>The lecture itself was nothing amazing. I talked about elementary particles, covalent bonds, electron clouds (<a href="https://speakerdeck.com/andrewradev/back-to-school">slides in Bulgarian</a>). My lack of deep knowledge in the subject made me a bit uncomfortable and it showed, but it was alright as a whole. The more interesting part was the meta-lecture (<a href="https://speakerdeck.com/andrewradev/back-to-school-meta">slides in Bulgarian</a>), in which I talked about my motivations and prompted a discussion. As expected, people had much to say. Us techies have this tendency to apply technical solutions to all sorts of problems. While it may be misguided sometimes, I think it’s ultimately a good thing. Education is one area that we can throw <em>a lot</em> of tech at, and the amount of startups in the area shows.</p> <p>My simple idea is inspired greatly by lectures on programming. Go to any conference talk and you’ll see slides. Slides are the natural companion to words, the illustrations, the replacement for a chaotic whiteboard when we know what we’re talking about. Personally, I love this. Don’t get me wrong, I hate having to prepare slides, but I can see the value they bring to a lecture. In fact, I fully believe that, in the future, when everybody has a brain-installed computer, we’ll all have the ability to project ad-hoc holographic illustrations of whatever we’re talking about right now, making every interaction better.</p> <p>But even if you don’t subscribe to weird future speculations, it’s hard to deny that the occasional illustration helps when trying to figure out something complicated. I tried to distill what is it that helped me out in this particular talk:</p> <ul> <li> <p><strong>Summaries</strong>: A single word or phrase on a blank slide works wonders as a mental anchor. The lecturer may be going on and on about the details, which is why having the topic right in front of the audience helps in anchoring to the general idea.</p> </li> <li> <p><strong>Illustrations</strong>: Depending on what you’re learning, seeing a simple illustration of it could make all the difference. Imagination only goes so far when it comes to, say, the shapes of different electron orbitals.</p> </li> <li> <p><strong>Review</strong>: After every section, it’s easy to have a quick review of the main points with a set of bullets. It’s also easy to move back and forth: “Remember the Bohr model? Now that you know about electron orbitals, let’s talk about it again”. Not only is the repetition itself good for remembering, it’s also easier to make the connections between ideas that looked unrelated a moment ago.</p> </li> <li> <p><strong>Flow</strong>: Teachers follow some plan while talking, going through a particular string of ideas in a predetermined way. Having the plan set in the slides itself, it’s easy to stick to it and avoid distractions. And, with a good enough flow, it’s possible to suck the listener in, giving them a ride from idea to idea, building nice little bridges between them. I still remember fondly a series of algebra lessons in university, seemingly unrelated, culminating in the proof of a theorem that elegantly used every bit of knowledge we’d gained from the start of the semester. It was like watching the exciting reveal to a detective story.</p> </li> </ul> <p>The best thing about presentations is that it’s not even hard to make useful slides. Note that I don’t say “great” or even “good”, just “useful”. You don’t have to weave a complex tale with eye-catching illustrations and hilarious jokes to make a set of useful slides. There are a few simple rules I can think of that need to be adhered to:</p> <ul> <li> <p><strong>Little text and/or illustrations per slide</strong>: You don’t want to write a novel on a slide, and you don’t want to overload it with imagery. One phrase per slide is good. One title and 4-5 one-phrase bullets in a slide is good. One illustration in a slide is good.</p> </li> <li> <p><strong>Step-by-step reveals</strong>: Making all bullets in a slide visible as you’re talking about them is a big no-no. People will read ahead and will start thinking about what comes next. In the meantime, they will have a hard time listening to you. Showing things gradually keeps the flow going and anchors the screen to your words.</p> </li> <li> <p><strong>Little or no effects</strong>: This one should be a given. Fancy zooms and slides have been out of fashion pretty much since they were invented. The only animation you need is “Appear”.</p> </li> </ul> <p>Naturally, a good presenter may break the rules, as long as they have a good reason. But for a novice, just sticking to these constraints means making a very usable slideshow with not a whole lot of effort.</p> <h2 id="education"><a class="anchor" aria-hidden="true" href="#education">🔗</a>Education</h2> <p>Globally, there’s a ton of complaints about the current education system and a lot of drive to innovate it. Above, I mentioned <a href="https://www.coursera.org/">coursera</a> and <a href="http://letsgeddit.com/">geddit</a>. What they do is provide children and adults with better ways to actively pursue better education. Remember the “actively” bit there, I’ll be back to it in soon.</p> <p>These two startups (most startups, I beleive) are somewhat oriented towards places that can afford this. For instance, geddit assumes that everyone in the classroom will have a smartphone or tablet. While there have been campaigns like <a href="http://one.laptop.org/">One Laptop Per Child</a> that aim to equip poorer classrooms with technology, we can’t forget that there are many schools that still don’t have the possibility of providing for every student. There is still hope, though.</p> <p>Sugata Mitra’s “<a href="http://www.ted.com/talks/sugata_mitra_the_child_driven_education.html">The Child-driven Education</a>” talk demonstrates an experiment that tries to bring quality education to even the most remote regions by leveraging children’s natural curiosity. It uses very little technology (a single computer in a wall, one computer per 4 children), but due to the format of the study groups, this works out quite nicely.</p> <h2 id="motivation"><a class="anchor" aria-hidden="true" href="#motivation">🔗</a>Motivation</h2> <p>One problem that doesn’t seem to be addressed enough is the lack of motivation. Most startups provide amazing ways for people to learn new things, provided they actively allocate time and effort for it. Even Sugata Mitra’s experiments rely on children being motivated to learn, though it does attempt to “trick” them into learning by communicating with each other.</p> <p>However, if children have no desire to learn, there’s not much that can be done. In my experience, school has always had a culture of “learning is uncool”. The cool kids skip class and smoke behind school, they mess with the teachers, they play pranks. Students that actually get good grades are stigmatized as “nerds” and are pushed to underperform in order to avoid being ostracized. Adding to that, teachers lose motivation when they’re constantly heckled and ignored. They may become bitter and resentful which serves even more to damage the learning environment. This is an especially big problem with young teachers who don’t have the inherent authority that comes with age.</p> <p>In Bulgaria, student protection laws tie up teachers’ hands. A teacher may never order a student to leave the classroom, regardless of how they behave. A poor grade may only be written upon examination, but if the teacher tries to examine a student, they may play dumb and then dispute the grade, wasting half of the class’ time in the process. Aside from that, schools are stimulated to accept students regardless of their grades, since a lack of students will mean being financed with less money from the government. In the end, this means that it’s easy to just stroll through school without learning anything, provided you’re arrogant enough to “game the system”.</p> <h2 id="solutions"><a class="anchor" aria-hidden="true" href="#solutions">🔗</a>Solutions?</h2> <p>It’s really hard to motivate people. The only solution I can think of is finding ways to stimulate learning from an early age and fight the “too cool for school” culture. More and more people are becoming self-learners as adults, exploring online courses and youtube videos, and this is something that should be pushed to children as well. Educational shows like the old “Bill Nye the Science Guy” have potential for this, but if lessons at school are not engaging, only these shows on the side will always interest only a minority of kids.</p> <p>The idea of using presentation slides is somewhat targeted to improve motivation. I don’t think that the classroom should turn to using gimmicks and games every single day in order to maintain interest. This would be difficult to maintain in the long term, and it’s unclear whether actual knowledge will be kept afterwards. I’ve had bad experiences in university with a lecturer trying to show mostly pretty pictures and fun videos during class, only to end up with a full class of people that lack understanding of even the simplest theorems, due to focusing only on the fun bits. But incrementally improving the classes themselves, making even the boring parts of the subject a bit more memorable with visual cues and repetition, could actually work, I believe. Especially if it’s done every single class in a consistent, unobtrusive way. We are creatures of habit. Having a single “special” class once a week is not going to turn bad habits around. Having every class be just a bit more engaging might.</p> <h2 id="something-actionable"><a class="anchor" aria-hidden="true" href="#something-actionable">🔗</a>Something Actionable</h2> <p>Whether this can actually be pulled off is another topic entirely. I’m not a teacher and all this is borne out of my experience as a student and assumptions as a developer. One way I can see this working out is if some techie decides to “pair program” with a teacher for a while. As in, the techie can discuss the lesson with the actual, professional teacher, build up some slides in collaboration with them, and then present them as the teacher is speaking. I can imagine this happening with young teachers, both because they still haven’t built up their methodology in dealing with children, but also because they’d be more inclined to trust a technical solution.</p> <p>On my side, a lot of people seemed to be interested in the initLab lecture and there was a fair amount of discussion afterwards, which at least makes me positive that many people take these ideas at heart. One friend in particular is going to be leading HTML &amp; web-dev lectures to high school kids, and I’ll follow up with him to see what his experience is in terms of engagement. I’d still really like it if we found a way to do “pair teaching” with some actual, real teachers and see how it goes, but I’m not sure if there would be enough support from schools and enough technical possibility to pull this off (a projector, a canvas).</p> <p>In the end, I feel that my own part in this will be all talk. I’m far too easily distracted by a new idea for a vim plugin or a ruby gem to keep up a serious education-related campaign. But if you happen to be reading this and you happen to have a friend who’s a teacher, consider talking them about this, asking them what they think of it all. And let me know how it goes.</p> Sun, 20 Oct 2013 12:10:00 +0200 https://andrewra.dev/2013/10/20/the-back-to-school-idea/ https://andrewra.dev/2013/10/20/the-back-to-school-idea/ education talks OHM 2013 <p>I went to <a href="https://ohm2013.org/site/">OHM</a> (Observe, Hack, Make) this year, a hacker festival in the Netherlands. It was a fun week of camping, freedom talks, technology, retro-gaming and all sorts of other cool stuff. Since I was going there anyway, I decided to make a Vim event to spread word of the One True Editor.</p> <!-- more --> <p>It was a three-day Vim workshop and I’m pretty happy with how it went. The first day I talked about the very basics, things like moving around in a buffer and opening files. It was only an hour, which is not nearly enough to learn anything practical, but I tried to at least show the basic philosophy of Vim as I see it. My final summary was something along the lines of:</p> <ul> <li><strong>It uses semantic movements</strong>. Instead of pointing where the cursor should go, Vim can simply be told what to do, like “select a paragraph” or “delete the next two lines”.</li> <li><strong>It’s all text</strong>. The interface is completely textual, no animations, no fancy icons. This limitation, however, ends up for the better – there’s a lot of amazing UI-related plugins that build directory trees and fuzzy-finder dialogs with pure text.</li> <li><strong>It’s only text</strong>. Vim is at its best when it’s editing text, and for everything else, it works great with external programs.</li> <li><strong>It’s extensible</strong>. There’s an amazing amount of plugins out there, and writing some simple tools of your own is not difficult at all.</li> </ul> <p>I also pointed the attendees to my usual sources of beginner knowledge:</p> <ul> <li><a href="http://www.reddit.com/r/vim">The Vim subreddit</a>. Filled with discussions on new plugins, workflows and Vim features.</li> <li><a href="http://stackoverflow.com/questions/tagged/vim">Stack Overflow</a>. Excellent place to ask Vim-related newbie questions.</li> <li>And, of course, the good ol’ <a href="http://vimdoc.sourceforge.net/htmldoc/usr_toc.html">:help</a>. Some of the best documentation on a piece of software I’ve seen so far.</li> </ul> <p>The second day was something like a workflow show-and-tell. I went through some of my personal tricks, like how I navigate through a project, how I arrange windows as I’m working, how I deal with language-specific tools. I prompted everyone to share their own workflows on these topics and they didn’t disappoint. Before you know it, we were talking about registers, <code class="language-plaintext highlighter-rouge">relativenumber</code> and various plugins. Regrettably, I didn’t keep notes after the workshop, but I’m going to present the second day’s content to a few folks here in Berlin, so maybe I’ll make it a bit more organized and publish it afterwards.</p> <p>The third day was the Vimscript day. I skimmed through some scripting basics, like if-clauses, for-loops and the likes. This part was mostly glossed over as “it works pretty much as you’d expect for simple cases”. I tried to focus on the more interesting parts like variable scopes, functions and commands. For a more workflow-related angle, I talked about how to think about separating the user interface part from the actual scripting part, reiterating some points from an old talk/blog post, “<a href="http://andrewradev.com/2012/11/27/vimberlin-lessons-learned-from-building-splitjoin/#comment-773330766">Lessons Learned From Building Splitjoin</a>”. To get a feel of how it all fits together I wrote a simplified version of my simple <code class="language-plaintext highlighter-rouge">:Grep</code> command from this <a href="https://gist.github.com/AndrewRadev/2979701">gist</a>. I wanted to talk more about debugging and testing, but there wasn’t enough time. I still managed to squeeze in a demo of the <a href="http://www.vim.org/scripts/script.php?script_id=120">Decho</a> plugin, one of my favourite debugging tools.</p> <p>All in all, it was a fun couple of days, and I hope everyone that came learned something useful. Next time, I should definitely try to keep some more detailed notes for sharing purposes.</p> Sun, 18 Aug 2013 11:02:00 +0200 https://andrewra.dev/2013/08/18/ohm-2013/ https://andrewra.dev/2013/08/18/ohm-2013/ vim conferences