<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jon Staab</title>
    <description>The latest articles on DEV Community by Jon Staab (@staab).</description>
    <link>https://dev.to/staab</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F144781%2F10a4c101-b715-41f9-8c54-00ae6884f300.png</url>
      <title>DEV Community: Jon Staab</title>
      <link>https://dev.to/staab</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/staab"/>
    <language>en</language>
    <item>
      <title>Logaralgorithms</title>
      <dc:creator>Jon Staab</dc:creator>
      <pubDate>Wed, 13 Apr 2022 16:50:25 +0000</pubDate>
      <link>https://dev.to/staab/logaralgorithms-11e9</link>
      <guid>https://dev.to/staab/logaralgorithms-11e9</guid>
      <description>&lt;p&gt;Logarithms are an elegant mathematical function with a variety of applications from measuring earthquakes to calculating interest rates. But I think they might be under-appreciated as a tool for your average application developer.&lt;/p&gt;

&lt;p&gt;The main popular use of logarithms is in charting exponential functions in a way that is intelligible to humans.&lt;/p&gt;

&lt;p&gt;You can see this in the bitcoin stock-to-flow price model:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XnUY34el--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rei7jk6hm5otnyywy1ml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XnUY34el--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rei7jk6hm5otnyywy1ml.png" alt="Bitcoin S2F Price Model" width="880" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Covid cases:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HjlTHTwp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4o04e0u6rhcgs5332kb4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HjlTHTwp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4o04e0u6rhcgs5332kb4.png" alt="Covid cases on a logarithmic scale" width="880" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Decibel scale:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NJSZsHdK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/313q773jtcrbodr8esni.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NJSZsHdK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/313q773jtcrbodr8esni.png" alt="Decibel chart" width="666" height="1016"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why not use this powerful tool when relaying everyday concepts to our users?&lt;/p&gt;

&lt;p&gt;Here's an example: you might redesign Twitter to show a post's score out of ten rather than the absolute like count, since people with lots of followers have vastly oversized reach compared with nobodies. In other words, there are relatively &lt;a href="https://www.intmath.com/blog/mathematics/twitter-follower-semi-log-graphs-5496"&gt;few high-follower accounts&lt;/a&gt;, meaning tweets either get a like or two, or tens of thousands, depending on who you are. What if there was a way to level the playing field a bit?&lt;/p&gt;

&lt;p&gt;Let's just say the theoretical maximum number of likes for a tweet is 1 million — anything with 1MM+ likes would have a score of ten. On a linear scale, you would need 1/10th of 1MM likes to score a 1, which obviously won't do.&lt;/p&gt;

&lt;p&gt;But we can plot our like numbers on a logarithmic scale using the formula &lt;code&gt;log base likes of max score = exponent&lt;/code&gt;. Substituting our max likes and max score into the formula gives us an exponent of 1/6. So, to calculate a given user's score, we just have to raise the number of likes to the power of 1/6, or &lt;code&gt;const getScore = likes =&amp;gt; Math.min(10**6, likes) ** (1/6)&lt;/code&gt;. Here's how that distribution looks&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 like = score of 1
10 likes = score of 1.47
100 likes = score of 2.15
1000 likes = score of 3.16
10000 likes = score of 4.64
100000 likes = score of 6.81
1000000 likes = score of 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This still feels a little top-heavy to me — let's fine-tune it a bit. One way to do this is to pad lower values with an additional score:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const getScore = likes =&amp;gt;
  (Math.min(10**5, likes) ** (1/5) + Math.min(10**6, likes) ** (1/6)) / 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us a much nicer distribution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 like = score of 1
10 likes = score of 1.53
100 likes = score of 2.33
1000 likes = score of 3.57
10000 likes = score of 5.48
100000 likes = score of 8.41
1000000 likes = score of 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Applying it to Twitter's UI might turn this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5N90Gfgz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e1xst22eosc9t9bza4hh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5N90Gfgz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e1xst22eosc9t9bza4hh.png" alt="Twitter's current UI" width="880" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Into this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nkh44vll--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vifszwupv1v0t9lpxphc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nkh44vll--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vifszwupv1v0t9lpxphc.png" alt="Image description" width="600" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Small change (and not necessarily an improvement), but it illustrates the idea.&lt;/p&gt;

&lt;p&gt;Other applications for logarithms in user interface design might include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Any other instance where you want to provide an opaque score, especially when incorporating a weighted average of several different data points.&lt;/li&gt;
&lt;li&gt;When you want to weight small values more heavily than large values, for example to give proportionally more voting power to small shareholders in a DAO to reinforce decentralization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think this is a very interesting technique, and will be reaching for logarithmic and exponential functions more often in my design work.&lt;/p&gt;

</description>
      <category>logarithm</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>Building EmbassyOS 2.17 on a Raspberry PI</title>
      <dc:creator>Jon Staab</dc:creator>
      <pubDate>Tue, 28 Dec 2021 13:32:24 +0000</pubDate>
      <link>https://dev.to/staab/building-embassyos-217-on-a-raspberry-pi-526f</link>
      <guid>https://dev.to/staab/building-embassyos-217-on-a-raspberry-pi-526f</guid>
      <description>&lt;p&gt;Start9 Labs' EmbassyOS is an exciting project, and I couldn't wait to try it - but I wasn't about to spend over $179 on the software. For true self-sovereignty, it should be free and open source, so I went that route. Unfortunately, I ran into a number of issues, enough to justify a blog post detailing what the errors were, and how I got past them.&lt;/p&gt;

&lt;p&gt;For guidance, I am following the &lt;a href="https://github.com/Start9Labs/embassy-os/blob/master/BuildGuide.md"&gt;official build guide&lt;/a&gt;, supplemented by &lt;a href="https://bitcoinmechanic.medium.com/building-my-wife-an-embassy-from-scratch-19cb87193fb2"&gt;this unofficial guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Note that EmbassyOS 0.3.0 is coming out &lt;em&gt;real soon now&lt;/em&gt;, so these tips will likely be obsolete by the time I publish them.&lt;/p&gt;

&lt;h1&gt;
  
  
  Building Stack
&lt;/h1&gt;

&lt;p&gt;The bulk of my time was spent building version 2.5.1 of stack, since only version 2.1.3 is available for Raspbian.&lt;/p&gt;

&lt;p&gt;The guide says to use &lt;code&gt;stack build --stack-yaml=stack-ghc-84.yaml --system-ghc&lt;/code&gt; to build stack, but it appears ghc 8.4 is no longer available for raspbian. 8.8.4 is the default ghc as of this writing. When I try to compile stack, I get the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;No setup information found for ghc-8.4.4 on your platform.
This probably means a GHC bindist has not yet been added for OS key 'linux-armv7', 'linux-armv7-ncurses6', 'linux-armv7-tinfo6'.
Supported versions: ghc-7.10.2, ghc-7.10.3, ghc-8.0.1, ghc-8.0.2, ghc-8.2.1, ghc-8.2.2, ghc-8.6.3, ghc-8.10.1, ghc-8.10.2, ghc-8.10.3, ghc-8.10.4, ghc-8.10.5, ghc-8.10.6, ghc-8.10.7, ghc-9.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Luckily, stack has multiple configuration files! I was able to get stack to build by specifying the compiler thus: &lt;code&gt;stack build --stack-yaml=stack-ghc-88.yaml --compiler ghc-8.8.4&lt;/code&gt; to match my system version. Sady, the following &lt;code&gt;install&lt;/code&gt; command failed, possibly because 8.8.4 is not listed in compatible ghcs above?&lt;/p&gt;

&lt;p&gt;At any rate, I next attempted using 8.6.3, which is listed as compatible, using &lt;code&gt;stack build --stack-yaml=stack-ghc-86.yaml --compiler ghc-8.6.3&lt;/code&gt;. This gave me some new errors, namely &lt;code&gt;Error: selected processor does not support movt [...]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Following &lt;a href="https://gitlab.haskell.org/ghc/ghc/-/issues/17856"&gt;this issue&lt;/a&gt; I edited &lt;code&gt;~/.stack/programs/arm-linux/ghc-8.6.3/lib/ghc-8.6.3/settings&lt;/code&gt; as suggested to force compiling for armv7:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;("C compiler flags", "-marm -march=armv7-a")
("target arch", "ArchARM {armISA = ARMv7, armISAExt = [VFPv2], armABI = HARD}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This seemed to work, although I got a new error that llvm wasn't available, &lt;code&gt;Warning: Couldn't figure out LLVM version! Make sure you have installed LLVM ghc: could not execute: opt&lt;/code&gt;. Stack wants llvm 6, but apt didn't have it so I used 9. I added llvm to my path as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export PATH=/usr/lib/llvm-9/bin:$PATH
export CPLUS_INCLUDE_PATH=$(llvm-config --includedir):$CPLUS_INCLUDE_PAT
export LD_LIBRARY_PATH=$(llvm-config --libdir):$LD_LIBRARY_PATH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;stack build&lt;/code&gt; continued to yell at me about using a version of llvm other than 6, but it appears to have worked. Invoking &lt;code&gt;stack install --stack-yaml=stack-ghc-86.yaml --compiler ghc-8.6.3&lt;/code&gt; did the trick as well. Of course, you'll want to add &lt;code&gt;./local/bin&lt;/code&gt; to your path once this completes, per the guide.&lt;/p&gt;

&lt;h1&gt;
  
  
  Building EmbassyOS
&lt;/h1&gt;

&lt;p&gt;This step was much more straightforward, the one change that needed to be made to the build guide was to install nodejs 15 rather than the latest version due to engine compatibility. To do that, use &lt;code&gt;nvm install 15&lt;/code&gt; rather than &lt;code&gt;nvm install node&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Status
&lt;/h1&gt;

&lt;p&gt;After that, my .img was ready to flash! I did, and it's running, but unfortunately the Embassy service is crashing with an error &lt;code&gt;/usr/local/bin/agent: /lib/arm-linux/gnueabihf/libm.so.6: version GLIBC_2.29 not found&lt;/code&gt;. I am still troubleshooting this one on Embassy's chat channel. Let me know if you find a way through that!&lt;/p&gt;

&lt;p&gt;Update: the Embassy team told me 0.3.0 is nearly there, and much easier to build than 0.2.17. Onward!&lt;/p&gt;

&lt;h1&gt;
  
  
  0.3.0
&lt;/h1&gt;

&lt;p&gt;This indeed was much smoother than 0.2.17, however I did still run into some small issues.&lt;/p&gt;

&lt;p&gt;When running &lt;code&gt;npm --prefix ui run build-prod&lt;/code&gt;, I got an error when building angular: &lt;code&gt;An unhandled exception occurred: Cannot find module 'webpack'&lt;/code&gt;. Running &lt;code&gt;npm --prefix ui install webpack&lt;/code&gt; fixed this. &lt;code&gt;npm install -g webpack&lt;/code&gt; will also do the trick.&lt;/p&gt;

&lt;p&gt;I also had trouble installing the buildx plugin, getting &lt;code&gt;docker: 'buildx' is not a docker command.&lt;/code&gt; even after installing it to &lt;code&gt;~/.docker/cli-plugins/docker-buildx&lt;/code&gt; with the correct permissions. It seems this is because the version of docker installed by apt on Debian is &lt;code&gt;Docker version 18.09.1, build 4c52b90&lt;/code&gt;, while the buildx feature &lt;a href="https://medium.com/@artur.klauser/building-multi-architecture-docker-images-with-buildx-27d80f7e2408"&gt;arrived in v19&lt;/a&gt;. Switching to Ubuntu 21.10 fixed the problem.&lt;/p&gt;

&lt;p&gt;After clearing that up, everything built beautifully. The product key is in &lt;code&gt;embassy-os/product_key.txt&lt;/code&gt;. Don't delete the repository (or the VPS you used to compile it) until you put that and the eos.img somewhere safe.&lt;/p&gt;

&lt;p&gt;At the time of writing, EmbassyOS 0.3.0 is not yet released, so there are a number of rough edges with getting it set up initially. It required a number of restarts before the embassy user was set up and it was able to connect to my LAN. Pro tip: check that your ethernet cable is actually an ethernet cable.&lt;/p&gt;

&lt;p&gt;Now that I've gotten my embassy built and running, it's time for me to tear it down. Don't get me wrong — I'm &lt;em&gt;very&lt;/em&gt; excited about the project, but it needs to be more stable before I trust it with all my data. In the meantime, I'm going to give &lt;a href="https://ownyourbits.com/nextcloudpi/"&gt;NextCloudPi&lt;/a&gt; a shot, with an eye toward wrapping it for Embassy down the road if I like it.&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>embassyos</category>
    </item>
    <item>
      <title>How to Query Multiple Schemas With Postgresql</title>
      <dc:creator>Jon Staab</dc:creator>
      <pubDate>Wed, 07 Apr 2021 17:36:48 +0000</pubDate>
      <link>https://dev.to/staab/how-to-query-multiple-schemas-with-postgresql-28kj</link>
      <guid>https://dev.to/staab/how-to-query-multiple-schemas-with-postgresql-28kj</guid>
      <description>&lt;p&gt;At work, we have a multi-tenancy setup where for performance purposes we split tenants across multiple identically-structured schemas. While giving each tenant their own schema would help with isolation, having too many separate database objects would make migrations and using pg_dump too resource intensive. Instead, we use 100 numerically named schemas to randomly distribute tenants across the schema space.&lt;/p&gt;

&lt;p&gt;This works great as an easy way to kick the can of application performance down the road. We have a &lt;code&gt;master&lt;/code&gt; database which contains a record of all tenants and which schema their data is contained in. The app simply looks up that tenant and uses its corresponding schema.&lt;/p&gt;

&lt;h1&gt;
  
  
  Oh, you wanted ALL your data?
&lt;/h1&gt;

&lt;p&gt;The difficulty when performing queries across the entire database, either for analytics purposes, or to find a particular object without knowing which tenant it belongs to. &lt;a href="https://clarkdave.net/2015/06/aggregate-queries-across-postgresql-schemas/"&gt;This article&lt;/a&gt; by Dave Clark is a great starting point, but it wasn't quite doing it for me.&lt;/p&gt;

&lt;p&gt;I've been able to combine his "application generated union query" and "union view" approaches into something that works really well for my use case of ad-hoc database exploration of just a handful of tables at a time, and doesn't require anything except pure SQL. My approach doesn't rely on periodic view refreshing, excessive load on the database, or wasting space on materialized views.&lt;/p&gt;

&lt;h1&gt;
  
  
  Dynamic SQL in Postgres is weird
&lt;/h1&gt;

&lt;p&gt;I didn't figure this out on my own; because postgres is generally statically typed, it's tricky to get it to do things in a generic or dynamic way. If you want to see my creative process, check out &lt;a href="https://stackoverflow.com/questions/66959970/infer-row-type-from-table-in-postgresql?noredirect=1#comment118383292_66959970"&gt;this Stack Overflow question&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;app_union&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tbl&lt;/span&gt; &lt;span class="n"&gt;anyelement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;setof&lt;/span&gt; &lt;span class="n"&gt;anyelement&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="k"&gt;execute&lt;/span&gt; &lt;span class="n"&gt;string_agg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;distinct&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'select * from %I.%I'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pg_typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tbl&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="s1"&gt;' union all '&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tenants&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app_union&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;my_table&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple, right? Breaking it down, there are two Postgres features that make this sort of thing possible: polymorphic types, and &lt;code&gt;pg_typeof&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In Postgres, &lt;code&gt;record&lt;/code&gt; types are statically typed. This means that when returning a row or set of rows from a function, the &lt;em&gt;caller&lt;/em&gt; has to provide the full row type definition of the result set. This looks something like &lt;code&gt;select my_function('table_name') t(id uuid, name text...)&lt;/code&gt;. This is incredibly laborious to write out for ad-hoc queries.&lt;/p&gt;

&lt;p&gt;Turns out, you can simply tell postgres to chill by using &lt;a href="https://www.postgresql.org/docs/13/extend-type-system.html"&gt;polymorphic types&lt;/a&gt;. However, the type still needs to be something postgres can infer, which is why we pass a null row of the table we want to the function instead of the name of the table as text.&lt;/p&gt;

&lt;p&gt;We then build the query by using &lt;code&gt;pg_typeof&lt;/code&gt; to get the record type of the row we passed in, and casting that type to text.&lt;/p&gt;

&lt;h1&gt;
  
  
  This is not fast
&lt;/h1&gt;

&lt;p&gt;Yes, I did promise this wouldn't hammer your database at the beginning, didn't I? Well, it turns out with dynamic sql, adding a where clause to the function is trivial. Here's my final version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;app_union&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tbl&lt;/span&gt; &lt;span class="n"&gt;anyelement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extra&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;setof&lt;/span&gt; &lt;span class="n"&gt;anyelement&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="k"&gt;execute&lt;/span&gt; &lt;span class="n"&gt;string_agg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;distinct&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'select * from %I.%I %s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pg_typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tbl&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="s1"&gt;' union all '&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tenants&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To solve my performance issues, I simply added an &lt;code&gt;extra&lt;/code&gt; text parameter to my function. This lets me pass any arbitrary sql into the function to go after the &lt;code&gt;from&lt;/code&gt; in &lt;em&gt;every&lt;/em&gt; sub-query. So, for example &lt;code&gt;select * from app_union(null:my_table, 'where n &amp;gt; 1')&lt;/code&gt; lets me apply the filter at the original table level rather than pulling all 12 million records and applying the filter to the union query.&lt;/p&gt;

&lt;h1&gt;
  
  
  How about a real example?
&lt;/h1&gt;

&lt;p&gt;I wanted to get a list of all tenants active in the last 7 days. With &lt;code&gt;app_union&lt;/code&gt;, it's a breeze:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_activity&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tenant&lt;/span&gt;
&lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;last_activity&lt;/span&gt;
  &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app_union&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'where created &amp;gt; now() - interval &lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt;7&lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt; day'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;group&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;tenant&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;tenant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query took about 10 seconds on my production server with 2.5MM event records. This is fairly hefty, but not bad for a one-off analytics query!&lt;/p&gt;

&lt;p&gt;Thanks for reading — here's hoping this proves useful in your database wrangling adventures.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>sql</category>
      <category>multitenancy</category>
    </item>
    <item>
      <title>Some Postgres Performance Tips</title>
      <dc:creator>Jon Staab</dc:creator>
      <pubDate>Fri, 18 Dec 2020 00:42:49 +0000</pubDate>
      <link>https://dev.to/staab/some-postgres-performance-tips-5a0o</link>
      <guid>https://dev.to/staab/some-postgres-performance-tips-5a0o</guid>
      <description>&lt;p&gt;This last weekend the application I develop and support went down for a couple hours while many of my company's customers were trying to make holiday shopping sales. It was not fun, and the next three days were packed with stress as I tried to resolve the underlying problems. But it was also a fun learning experience.&lt;/p&gt;

&lt;p&gt;Below is a quick summary of what I learned, complete with links to sources, just in case you (or I) ever find yourself in the same situation.&lt;/p&gt;

&lt;h1&gt;
  
  
  Locking
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.citusdata.com/blog/2018/02/15/when-postgresql-blocks/"&gt;Types of Locks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pganalyze.com/blog/postgresql-log-monitoring-101-deadlocks-checkpoints-blocked-queries"&gt;Illustration of a Deadlock&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My first thought was that I had made some mistake in a recent update, and my application was creating deadlocks. When I looked for long-running queries that were blocking others, I found... a lot. (&lt;a href="https://jaketrent.com/post/find-kill-locks-postgres/"&gt;source&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;query_start&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pg_stat_activity&lt;/span&gt; 
&lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pg_locks&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; 
  &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;pg_class&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oid&lt;/span&gt; 
  &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relkind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'r'&lt;/span&gt; 
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pg_backend_pid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I first started diagnosing the issue, there were over 100 rows returned, several of which had been running for several minutes.&lt;/p&gt;

&lt;p&gt;Updates are one of the the greediest kind of statements for holding locks, so when updating data it's good to be careful not to use them too often. Long-running transactions can cause a lock to be held for longer than necessary, so instead of opening a transaction for every request, I turned on psycopg2's &lt;code&gt;autocommit&lt;/code&gt; feature, and modified my code to opt-in to controlling transactions using a context manager.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://big-elephants.com/2013-09/exploring-query-locks-in-postgres/"&gt;This article&lt;/a&gt; defines a &lt;code&gt;lock_monitor&lt;/code&gt; query (at the very bottom) to find what queries are doing the blocking, although I've not seen it in action, since my problem wasn't actually locks — that was only a symptom.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching
&lt;/h2&gt;

&lt;p&gt;Something was suspect about the long-running queries. Normally deadlocks get automatically cleaned up by postgres, but these were lingering, and not getting caught by &lt;a href="https://stackoverflow.com/a/43363536/1467342"&gt;&lt;code&gt;pg_blocking_pids()&lt;/code&gt;&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The reason behind this was a degraded cache hit rate, which was in turn causing high cache turnover, and lots of disk reads and table scans which are sloooow. You can read more about that &lt;a href="https://www.craigkerstiens.com/2012/10/01/understanding-postgres-performance/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're using heroku-postgres with the &lt;a href="https://github.com/heroku/heroku-pg-extras"&gt;pg-extras&lt;/a&gt; extension installed, the cache hit rate can be retrieved using &lt;code&gt;heroku pg:cache-hit&lt;/code&gt;. It'll give you something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      name      |         ratio
----------------+------------------------
 index hit rate | 0.98304548448697819350
 table hit rate | 0.92119613681591593477
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are also some equivalent queries in the article above if you're not using heroku. That article also states that "If you find yourself with a ratio significantly lower than 99% then you likely want to consider increasing the cache available to your database."&lt;/p&gt;

&lt;p&gt;On our standard-0 heroku-postgres plan, our cache hit rate was somewhere around 87%. After upgrading to standard-2, our rate is closer to 92%. Better, but not as good as I'd like. Our database size was around 12GB, which was mostly audit log events and related indexes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Indexing
&lt;/h2&gt;

&lt;p&gt;How big the cache needs to be to get a good hit rate depends on the size of the database and access patterns, so shrinking it and using better indexes can be a good alternative to increasing the amount of memory available to your database. Below are some links on the subject with useful tips. To find low-hanging fruit, table size can be retrieved with this query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select pg_size_pretty(pg_total_relation_size(tablename::text))
from pg_tables where schemaname != 'pg_catalog';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pawelurbanek.com/postgresql-fix-performance"&gt;Cleaning up Indexes&lt;/a&gt; - the PGExtras package they're using in ruby is the same as our heroku pg-extras extension.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://scalegrid.io/blog/using-jsonb-in-postgresql-how-to-effectively-store-index-json-data-in-postgresql/"&gt;Indexing jsonb&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cybertec-postgresql.com/en/get-rid-of-your-unused-indexes/"&gt;This article&lt;/a&gt; has a helpful query to find unused indexes. You can get that from pg-extras too, but it seems to be inaccurate, at least on a newly provisioned database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After taking a look at &lt;code&gt;heroku pg:unused-indexes&lt;/code&gt;, I was able to remove or combine enough indexes to save an entire gigabyte of space, improving my cache's performance, and reducing the amount of work the database has to do when writing to my over-indexed tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Load
&lt;/h2&gt;

&lt;p&gt;Finally, there's CPU load. Load wasn't crucial to the problem I was facing, but it can be a good indicator that something is wrong. High CPU load means the database is doing more work than it needs to do, which means it's either not using indexes well, or it's not hitting the cache.&lt;/p&gt;

&lt;p&gt;I had a hard time finding CPU load for my heroku database, and since many hosts share resources between multiple tenants, the number of CPUs on the machine may not match how much compute power is available to my database. &lt;/p&gt;

&lt;p&gt;But every 5 minutes or so, heroku emits a log line with some helpful database and server metrics, defined &lt;a href="https://devcenter.heroku.com/articles/heroku-postgres-metrics-logs"&gt;here&lt;/a&gt;. To get just those lines, run &lt;code&gt;heroku logs -d heroku-postgres&lt;/code&gt;. Adding &lt;code&gt;-t&lt;/code&gt; is a good idea, as heroku logs has a short memory, and might not contain one of the log lines you're looking for.&lt;/p&gt;

&lt;p&gt;The load-avg stats, at least on dynos with &lt;a href="https://devcenter.heroku.com/articles/heroku-postgres-production-tier-technical-characterization#burstable-vcpu-performance"&gt;burstable performance&lt;/a&gt;, should average out at 0.2, not close to 1. I'm not sure if that applies for higher-tier plans without burstable performance though. My database's load average was around 0.6. After fixing the cache miss issues, it's much closer to the target 0.2.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Crisis averted — for now. I've been meaning to learn more about postgres, and it unfortunately took a crisis for me to do my homework. I will be keeping a closer eye on this stuff from here on out. I hope these tips are useful to you!&lt;/p&gt;

&lt;p&gt;Below are some more articles I've found since writing this one, which could come in handy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hakibenita.com/postgresql-unused-index-size"&gt;Free up space by deleting unused indexes, bloat, and null-filled indexes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>postgres</category>
      <category>performance</category>
      <category>heroku</category>
    </item>
    <item>
      <title>Opening Command Output in a New Buffer</title>
      <dc:creator>Jon Staab</dc:creator>
      <pubDate>Mon, 05 Oct 2020 21:29:57 +0000</pubDate>
      <link>https://dev.to/staab/open-output-in-a-new-buffer-2dak</link>
      <guid>https://dev.to/staab/open-output-in-a-new-buffer-2dak</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/staab/running-queries-in-kakoune-with-a-custom-command-4741"&gt;Last time&lt;/a&gt;, we tweaked our &lt;a href="https://dev.to/staab/using-kakoune-as-a-query-browser-2j72"&gt;kakoune query browser&lt;/a&gt; to open the selected query in an info window. As I mentioned at the bottom of that post, there are a few problems with this over-simplistic approach, one of which is the limitations of the info window.&lt;/p&gt;

&lt;p&gt;Today I'll be tweaking the plugin by moving the query output into its own scratch buffer, which we'll open for the user when a query is run. As an added bonus, I'll be generalizing the plugin to work the same for any external command, not just databases.&lt;/p&gt;

&lt;p&gt;To start with, let's review our plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def eval-query \
  -override \
  -docstring "Evaluate current selection using given connection." \
  -params 1 \
  -shell-script-candidates %{
    cat .eval-query.json \
      | jq -r '.connections|keys|@sh' \
      | sed s/\'//g \
      | tr -s " " "\n"
  } \
%{
  info -title "Query output" %sh{
    psql `cat .eval-query.json | jq .connections.$1 | cut -d\" -f 2` \
      -c "${kak_selection}" 2&amp;gt;&amp;amp;1
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The salient information here is that &lt;code&gt;info&lt;/code&gt; command. Instead of popping up an info dialog, we're going to capture the output and put it in the &lt;code&gt;|&lt;/code&gt; register using &lt;code&gt;set-register&lt;/code&gt;. Below is the modified command body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;set-register | %sh{
  cmd=`cat .eval-query.json | jq .connections.$1 | cut -d\" -f 2`
  eval "$cmd "${kak_selection}" 2&amp;gt;&amp;amp;1"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We used the &lt;code&gt;|&lt;/code&gt; register, since &lt;code&gt;:doc registers&lt;/code&gt; states that it should be used by commands that spawn a subshell. I'm not sure if this is 100% kosher, so leave a comment if you know better!&lt;/p&gt;

&lt;p&gt;Next, we're going to open a new scratch buffer called &lt;em&gt;eval-query-output&lt;/em&gt; and stick the contents of the register in there. This is pretty straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;edit -scratch *eval-query-output*
exec 'geA&amp;lt;ret&amp;gt;&amp;lt;esc&amp;gt;"|p;'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;edit&lt;/code&gt; command is probably familiar; it just opens a new buffer. &lt;code&gt;-scratch&lt;/code&gt; tells kakoune we don't care about linking it with a file. Finally, we use &lt;code&gt;execute-keys&lt;/code&gt; (&lt;code&gt;exec&lt;/code&gt; for short) to go to the end of the file, add a new line, and paste the contents of the &lt;code&gt;|&lt;/code&gt; register.&lt;/p&gt;

&lt;p&gt;And that's it! See it in action below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zqFx-BDM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8fzh5uwqfs8n7js3yij3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zqFx-BDM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8fzh5uwqfs8n7js3yij3.gif" alt="Opening query results in a new buffer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because the mechanism for running a query is so generic, this plugin can be used just as easily to open anything in a new buffer, not limited to query results. By simply renaming &lt;code&gt;connections&lt;/code&gt; to &lt;code&gt;commands&lt;/code&gt; and updating our config file (I also renamed the plugin to &lt;code&gt;beval&lt;/code&gt; for "buffer eval"), the possibilities become endless!&lt;/p&gt;

&lt;p&gt;Let's add a fancy &lt;code&gt;ls&lt;/code&gt; command that lets us open a directory listing in a new buffer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "commands": {
    "ls": "ls -lah"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s0Opq7eK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/83rap7doa7rpkb3dx6tp.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s0Opq7eK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/83rap7doa7rpkb3dx6tp.gif" alt="Opening a directory listing in a new buffer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, we can cheat by adding an &lt;code&gt;eval&lt;/code&gt; key to run any command we want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "commands": {
    "eval": "eval"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bZNMjIDi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/79ss0o7960jixg70mzxe.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bZNMjIDi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/79ss0o7960jixg70mzxe.gif" alt="Evaluate an arbitrary command"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What we basically have now is &lt;code&gt;|&lt;/code&gt; on steroids (and less well-designed). Let's go ahead and refactor this to split out the "pipe to buffer" functionality as a standalone thing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def eval-to-buffer \
  -override \
  -docstring "Evaluate current selection into scratch buffer." \
  -params 1..1000 \
%{
  set-register | %sh{ eval "$@ 2&amp;gt;&amp;amp;1" }

  # Open our output buffer
  edit -scratch *command-output*

  # Put output into the buffer
  exec '%"|pd'
}

def beval \
  -override \
  -docstring "Evaluate current selection using named command into scratch buffer." \
  -params 1 \
  -shell-script-candidates %{ cat .beval.json | jq -r '.commands|keys|@sh' | sed s/\'//g | tr -s " " "\n" } \
%{
  eval-to-buffer %sh{
    cmd=`cat .beval.json | jq .commands.$1 | cut -d\" -f 2`
    echo "$cmd \"${kak_selection}\""
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our project-specific configuration is independent of our mechanism for piping output to a buffer, since both are useful in different circumstances.&lt;/p&gt;

&lt;p&gt;Further refinement is of course out there, but for now, I'll leave this here. Thanks for reading!&lt;/p&gt;

</description>
      <category>kakoune</category>
      <category>unix</category>
      <category>postgres</category>
    </item>
    <item>
      <title>If in doubt, list it out</title>
      <dc:creator>Jon Staab</dc:creator>
      <pubDate>Wed, 08 Jul 2020 18:19:20 +0000</pubDate>
      <link>https://dev.to/staab/if-in-doubt-list-it-out-3d1m</link>
      <guid>https://dev.to/staab/if-in-doubt-list-it-out-3d1m</guid>
      <description>&lt;p&gt;Today (and for the last few weeks), I've been dealing with a bad technical decision I made about three years ago, which has suddenly come back to bite me, as the hidden requirements have suddenly come to light. I want to walk through my situation, and how I'm learning the painful lesson that it's best to enumerate domain objects rather than mush them into a lossy aggregate.&lt;/p&gt;

&lt;h1&gt;
  
  
  Background
&lt;/h1&gt;

&lt;p&gt;I work on an inventory management platform for consignment stores — when I was hired to build the system from scratch with no prior domain experience, it sounded simple.&lt;/p&gt;

&lt;p&gt;It's not simple. Consignment normally works with unique inventory, except when it doesn't. In addition, you have two models of paying suppliers living side-by-side: traditional retail where you pay your suppliers when you acquire the inventory (or thereabouts), and consignment, where you pay your suppliers when the inventory sells.&lt;/p&gt;

&lt;p&gt;To make this worse, it's possible for both payout models to be applied to a single SKU, either for the same instance of an item, or for multiple instances over time. &lt;/p&gt;

&lt;h1&gt;
  
  
  Where I went wrong
&lt;/h1&gt;

&lt;p&gt;In the name of "saving space", I compressed every item in the system into a single row in the &lt;code&gt;items&lt;/code&gt; table. &lt;/p&gt;

&lt;p&gt;There's nothing &lt;em&gt;necessarily&lt;/em&gt; wrong with this, as we keep an event log that lists every update to an item, so we can get at the item's state as of a point in time (e.g., when it was taken into inventory, or sold). We also keep various special purpose logs, including balance accruals and item status changes to help us decouple supplier payout state from the current state of the item.&lt;/p&gt;

&lt;p&gt;But the code started to smell when we had to shoehorn the concept of "status" (a concept having to do with unique inventory) into our plural-cardinality model: is this item sold? lost? in stock? &lt;/p&gt;

&lt;p&gt;I accommodated this with a &lt;code&gt;jsonb&lt;/code&gt; field in our database that kept track of how many items for the SKU could be attributed to a given stock, e.g., &lt;code&gt;{"active": 3, "sold": 1, "lost": 1}&lt;/code&gt;. I knew it was a bad design, but I couldn't predict how it might cause problems, so I left it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Discovering my mistakes
&lt;/h1&gt;

&lt;p&gt;I'm now writing customer-facing inventory analytics code, which includes calculating Cost of Goods Sold. Because the state of any given enumeration of an item has been lost (consignor split, price, and cost) due to subsequent edits, I now have to add a new table called "item_sales", which lists the attributes of an item needed to calculate COGS as of the time of the sale, and fill it by replaying the relevant events. &lt;/p&gt;

&lt;p&gt;Also, because we need to support partial refunds, I can't compress these records into one per sale, with a record of quantity; I have to mark each enumeration of an item as refunded independently.&lt;/p&gt;

&lt;p&gt;So I'm left with the design I tried to avoid in the first place, except it's ad-hoc and very difficult to maintain and understand. If I had enumerated every item instance in the first place, I would have had all the COGS data as of the sale date. And once an item sold I could have "locked" those instances to avoid mutating the data even if its un-sold brethren were modified.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;We left designing analytics until we already had two years worth of customer data. Analytics is pretty much your worst-case scenario for understanding your data, and contrasts strongly with a CRUD-paradigm view of what's true now with no regard for history. Even an event log (though useful for migrating to a new schema) won't save you, since events often have to be procedurally re-played rather than simply aggregated.&lt;/p&gt;

&lt;p&gt;Learn from my mistakes — if in doubt, list it out. It's as Rich Hickey says when evangelizing for Datomic: storage space is cheap, developer time isn't. Keeping redundant copies of your data is redundant until it isn't. You have to understand what's &lt;em&gt;possible&lt;/em&gt; in your domain, and point your design in a direction that will handle that from every angle.&lt;/p&gt;

</description>
      <category>schema</category>
      <category>domain</category>
      <category>migrations</category>
      <category>analytics</category>
    </item>
    <item>
      <title>Building my own Stack Traces</title>
      <dc:creator>Jon Staab</dc:creator>
      <pubDate>Tue, 06 Aug 2019 21:01:46 +0000</pubDate>
      <link>https://dev.to/staab/building-my-own-stack-traces-lc2</link>
      <guid>https://dev.to/staab/building-my-own-stack-traces-lc2</guid>
      <description>&lt;p&gt;Have you ever had an error in a javascript project?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeError: Cannot read property 'subdomain' of undefined
      at /Users/jstaab/my-project/src/server/db/core.js:568:35
      at Array.filter (&amp;lt;anonymous&amp;gt;)
      at /Users/jstaab/my-project/src/server/db/core.js:147:1
      at log (src/common/logger.js:75:14)
      at _callee16$ (src/server/db/core.js:768:28)
      at tryCatch (node_modules/regenerator-runtime/runtime.js:45:40)
      at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:271:22)
      at Generator.prototype.(anonymous function) [as next] (node_modules/regenerator-runtime/runtime.js:97:21)
      at asyncGeneratorStep (src/server/db/core.js:22:103)
      at _next (src/server/db/core.js:24:194)
      at &amp;lt;anonymous&amp;gt;
      at process._tickCallback (internal/process/next_tick.js:189:7)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even with source maps, that stack would only point to where the error was thrown, but I have no idea where in the application that thing was called. &lt;/p&gt;

&lt;p&gt;Today I decided to do something about it, by implementing my own stack traces. For example, for the above error, I got this extra information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Intercepted in:
     server/db/core/getWhereColumns
     server/db/core/deleteRecords
     server/db/domain/MyModel.delete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This just gets appended to an error's built-in stack trace (which are just strings in the first place). This makes it orders of magnitude faster for me to isolate problems and fix them, whether I'm developing locally or diagnosing a problem in production.&lt;/p&gt;

&lt;p&gt;This approach has drawbacks: there's some nasty boilerplate, and definitely some performance downsides, which I'll go into at the end of this post. But is it worth it? I think so.&lt;/p&gt;

&lt;h1&gt;
  
  
  Implementation
&lt;/h1&gt;

&lt;p&gt;The basic concept is that you have a decorator that catches errors and rejected promises, annotates their stack, and re-throws them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;IS_INSTRUMENTED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;isInstrumented&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instrument&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;IS_INSTRUMENTED&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;modifyStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;modifyStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;IS_INSTRUMENTED&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason I'm not using an async function here is that I want to be able to instrument synchronous functions without converting them to promise-returning functions (async functions &lt;em&gt;always&lt;/em&gt; return promises). We're also using es5 &lt;code&gt;function&lt;/code&gt; syntax and &lt;code&gt;bind&lt;/code&gt; so we can pass &lt;code&gt;this&lt;/code&gt; along.&lt;/p&gt;

&lt;p&gt;This function takes a name and another function, and returns a function that passes the arguments along to the decorated function, catching any synchronous errors that it might throw and modifying their stack. It also checks if the return value is a promise, and modifies the stack of the return value. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that you should only ever throw/reject &lt;code&gt;Error&lt;/code&gt;s, since this will add wonky &lt;code&gt;stack&lt;/code&gt; properties to anything else.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's &lt;code&gt;modifyStack&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modifyStack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalStack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalStack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;
    &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;    Intercepted in:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`\n\t &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A Basic Example
&lt;/h2&gt;

&lt;p&gt;Here's how you'd use &lt;code&gt;instrument&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;concat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utils.concat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;append&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utils.append&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that I &lt;em&gt;don't&lt;/em&gt; recommend decorating utility functions, since they're likely to be leaf nodes in your call graph, and are usually called synchronously so you'll get a good stack trace for them. You also don't want to add the overhead of this decorator to something that's going to get called very often. This decorator is much better for controllers, hefty logic functions, etc, to give you a sense of what code path has gone wrong. With that in mind, what if you called the above function with weird arguments?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="cm"&gt;/*
Uncaught TypeError: a.concat is not a function
    at &amp;lt;anonymous&amp;gt;:1:55
    at wrapper (&amp;lt;anonymous&amp;gt;:7:18)
    at &amp;lt;anonymous&amp;gt;:2:53
    at wrapper (&amp;lt;anonymous&amp;gt;:7:18)
    at &amp;lt;anonymous&amp;gt;:1:1

    Intercepted in:
     utils.concat
     utils.append
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not &lt;em&gt;that&lt;/em&gt; helpful, but remember, this is just a small example. In complex application code, this sort of thing can be a lifesaver.&lt;/p&gt;

&lt;h1&gt;
  
  
  Class Methods
&lt;/h1&gt;

&lt;p&gt;Class syntax can make this approach a pain. If you don't want to add a syntax transformer, you have another option: instrument an entire class. Here's what that looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instrumentClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cls&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;
  &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getOwnPropertyNames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constructor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getPrototypeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Libation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;pourOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Tea&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Libation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;pourOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pourOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bland&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;instrumentClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Tea&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're basically following this &lt;a href="https://stackoverflow.com/a/31055217/1467342"&gt;StackOverflow answer&lt;/a&gt; to traverse the chain of inheritance so we can enumerate all methods attached to our instance. We then instrument those methods, marking them with the class name and method name, skipping the &lt;code&gt;constructor&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's how this would look in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tea&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Tea&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ground&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;

&lt;span class="nx"&gt;tea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pourOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ground&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="cm"&gt;/*
Uncaught (in promise) TypeError: Cannot set property 'wet' of null
    at Tea.pourOn (&amp;lt;anonymous&amp;gt;:57:16)
    at Tea.wrapper (&amp;lt;anonymous&amp;gt;:5:18)
    at target.bland (&amp;lt;anonymous&amp;gt;:63:35)
    at new Promise (&amp;lt;anonymous&amp;gt;)
    at Tea.pourOn (&amp;lt;anonymous&amp;gt;:63:11)
    at Tea.wrapper (&amp;lt;anonymous&amp;gt;:5:18)
    at &amp;lt;anonymous&amp;gt;:74:5

    Intercepted in:
     Libation.pourOn
     Tea.pourOn
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that the original stack trace is just plain wrong — and this is running in Chrome's devtools, with no build step. But now we have more information: the error happened in Libation.pourOn, which was called by Tea.pourOn! I don't know about you, but I find this very helpful.&lt;/p&gt;

&lt;h1&gt;
  
  
  Footguns and Performance
&lt;/h1&gt;

&lt;p&gt;Obviously, any time you're changing the semantics of your host language, you need to be careful. This code is not perfect yet, but I've tried to address some of the possible issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This code won't wrap synchronous functions in a Promise.&lt;/li&gt;
&lt;li&gt;This code will pass &lt;code&gt;this&lt;/code&gt; down along with the arguments to the wrapped function.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;instrumentClass&lt;/code&gt; &lt;em&gt;does&lt;/em&gt; create a new copy of each method, including a wrapping closure. What it &lt;em&gt;doesn't&lt;/em&gt; do is create a new copy for each instance, which means we still have the general memory-conserving characteristics of object-oriented method dispatch.&lt;/li&gt;
&lt;li&gt;We're not unnecessarily wrapping every method of &lt;code&gt;Object&lt;/code&gt;, and we can easily add code to whitelist which methods we wrap.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Performance is also not terrible. Here's a little bit of profiling code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;valueOf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;valueOf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hi there&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without the wrapper, this comes out to ~200ms. With the wrapper, it comes out to ~450ms. This is 150% slower, but it's really a worst case scenario since the wrapper is doing more work than the inner function (which is why it's a bad idea to wrap frequently-used utility functions). If the inner function did more work, the cost of the wrapper would be negligible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;valueOf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;valueOf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hi there&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This comes out to ~11200ms for both versions.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This code is available on &lt;a href="https://github.com/staab/succinct-async"&gt;github&lt;/a&gt; — please let me know in the comments what you think of this approach! Am I a generously mustached daredevil maverick, or am I going to end up like Rod Kimble, desperately shouting "Whiskey" on my way toward the side of a van?&lt;/p&gt;

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

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>Running Queries in Kakoune with a Custom Command</title>
      <dc:creator>Jon Staab</dc:creator>
      <pubDate>Wed, 29 May 2019 17:55:31 +0000</pubDate>
      <link>https://dev.to/staab/running-queries-in-kakoune-with-a-custom-command-4741</link>
      <guid>https://dev.to/staab/running-queries-in-kakoune-with-a-custom-command-4741</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/staab/using-kakoune-as-a-query-browser-2j72"&gt;Last Time&lt;/a&gt;, I wrote about building a minimal query browser using bash and Kakoune. In it, I set up a file watcher using &lt;a href="http://eradman.com/entrproject/" rel="noopener noreferrer"&gt;entr&lt;/a&gt;, and auto-ran my queries on file change.&lt;/p&gt;

&lt;p&gt;An arguably better approach would have been to register a custom command to do that, since my queries might not always be safe in a production environment. Kakoune's &lt;a href="https://github.com/mawww/kakoune/blob/master/doc/pages/commands.asciidoc#declaring-new-commands" rel="noopener noreferrer"&gt;documentation on the topic&lt;/a&gt; is dense to say the least, so let's put together an example to go with it!&lt;/p&gt;

&lt;h1&gt;
  
  
  Project Structure
&lt;/h1&gt;

&lt;p&gt;For this project, we're going to need two components: Kakoune commands, and connection configuration. Commands should be reusable between projects, while connections will be project-specific.&lt;/p&gt;

&lt;p&gt;Best practice when writing Kakoune plugins is to put them in a file on their own, somewhere in one of Kakoune's &lt;a href="https://github.com/mawww/kakoune/blob/17c5e7aa5fb020186378f7f738e617b5743839fc/README.asciidoc#231-configuration" rel="noopener noreferrer"&gt;autoload directories&lt;/a&gt;. I'll be putting my plugin into &lt;code&gt;~/.config/kak/autoload/jstaab/eval-query.kak&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Connection configuration, on the other hand, will go in my project root directory, in &lt;code&gt;.eval-query.json&lt;/code&gt;. For now, I'll just use a simple json object describing arguments to pass to psql:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"connections"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"local"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-d my_project"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-h 192.168.0.1 -d my_project"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Adding our Command
&lt;/h1&gt;

&lt;p&gt;For this plugin, we're only going to need one command, which I'll name after my plugin: &lt;code&gt;eval-query&lt;/code&gt;. &lt;a href="https://github.com/mawww/kakoune/blob/master/doc/pages/commands.asciidoc#declaring-new-commands" rel="noopener noreferrer"&gt;Commands&lt;/a&gt; are defined using the &lt;code&gt;def&lt;/code&gt; keyword with some optional switches. We of course want a docstring and we'll add the &lt;code&gt;-override&lt;/code&gt; switch so we can redefine this command as we work on it (we'll want to remove that when we're done).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def eval-query \
  -override \
  -docstring "Evaluate current selection using given connection." \
%{
    echo "I don't do anything yet"
}

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

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;%{}&lt;/code&gt; syntax is kakoune-speak for a multi-line string. The bodies of commands abide by the rules described in the &lt;a href="https://github.com/mawww/kakoune/blob/17c5e7aa5fb020186378f7f738e617b5743839fc/doc/pages/command-parsing.asciidoc#command-parsing" rel="noopener noreferrer"&gt;command parsing docs&lt;/a&gt;. When the command is invoked, the contents of this string are what will be evaluated.&lt;/p&gt;

&lt;p&gt;Let's go ahead and invoke our no-op command, by typing &lt;code&gt;:eval-query&lt;/code&gt; in normal mode. This will print our message, "I don't do anything yet" down on the bottom-left of the screen.&lt;/p&gt;

&lt;h1&gt;
  
  
  Asking for Input
&lt;/h1&gt;

&lt;p&gt;The first thing we'll need to do is add a way to ask the user which connection we want to use. There are a lot of ways to do this, but we'll be using &lt;a href="https://github.com/mawww/kakoune/blob/master/doc/pages/commands.asciidoc#declaring-new-commands" rel="noopener noreferrer"&gt;&lt;code&gt;-shell-script-candidates&lt;/code&gt;&lt;/a&gt;. This switch takes a shell script that supplies a newline-delimited list of possible completions, which Kakoune will then fuzzy-match over. We'll also have to tell Kakoune how many parameters are expected, using &lt;code&gt;-params&lt;/code&gt;. We'll also just have the command echo back at us what we provide:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def eval-query \
  -override \
  -docstring "Evaluate current selection using given connection." \
  -params 1 \
  -shell-script-candidates %{ echo something } \
%{
    echo %arg{@}
}

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

&lt;/div&gt;



&lt;p&gt;This gives us only one auto-complete option, "something". When we invoke this command with &lt;code&gt;:eval-query&lt;/code&gt;, it prompts us with "something", and echoes it back to us.&lt;/p&gt;

&lt;p&gt;Let's fill in the completion script by reading from our config file and grabbing the keys using &lt;a href="https://stedolan.github.io/jq/" rel="noopener noreferrer"&gt;jq&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def eval-query \
  -override \
  -docstring "Evaluate current selection using given connection." \
  -params 1 \
  -shell-script-candidates %{
    cat .eval-query.json \
      | jq -r '.connections|keys|@sh' \
      | sed s/\'//g \
      | tr -s " " "\n"
  } \
%{
    echo %arg{@}
}

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

&lt;/div&gt;



&lt;p&gt;This looks at the project's config file, pulls out the keys, and provides them as hints to the command's autocomplete. Now we know which connection to use!&lt;/p&gt;

&lt;h1&gt;
  
  
  Evaluating the Query
&lt;/h1&gt;

&lt;p&gt;The last piece of the puzzle is finding the query we want and passing it to our database for execution. Kakoune makes this simple, by providing a &lt;code&gt;$kak_selection&lt;/code&gt; variable to our shell expansions. Getting the string quoting correct on the other hand can be tricky.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def eval-query \
  -override \
  -docstring "Evaluate current selection using given connection." \
  -params 1 \
  -shell-script-candidates %{
    cat .eval-query.json \
      | jq -r '.connections|keys|@sh' \
      | sed s/\'//g \
      | tr -s " " "\n"
  } \
%{
  info -title "Query output" %sh{
    echo "${kak_selection}"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've changed the &lt;code&gt;echo&lt;/code&gt; command to an &lt;code&gt;info&lt;/code&gt; command, which opens up an info box filled with the stdout of the &lt;code&gt;%sh&lt;/code&gt; expansion. Inside the shell expansion, we're echoing the contents of the current selection. &lt;/p&gt;

&lt;p&gt;To pass it to postgres, we grab the options from the specified connection, and pass them, along with the current selection to postgres:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def eval-query \
  -override \
  -docstring "Evaluate current selection using given connection." \
  -params 1 \
  -shell-script-candidates %{
    cat .eval-query.json \
      | jq -r '.connections|keys|@sh' \
      | sed s/\'//g \
      | tr -s " " "\n"
  } \
%{
  info -title "Query output" %sh{
    psql `cat .eval-query.json | jq .connections.$1 | cut -d\" -f 2` \
      -c "${kak_selection}" 2&amp;gt;&amp;amp;1
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that I'm redirecting stderr to stdout so we can see any errors that might arise (like a nonexistent database, for example). Here's what this looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsnurbjhyu0echeals8zt.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsnurbjhyu0echeals8zt.gif" alt="Evaluating a query in Kakoune" width="504" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Binding to a Hotkey
&lt;/h1&gt;

&lt;p&gt;You'll notice that this isn't exactly ergonomic. Do I have to type out the full command, complete with my connection every time? The answer is no. We can bind our command — along with any useful variations — to keyboard shortcuts, using &lt;a href="https://github.com/mawww/kakoune/blob/master/doc/pages/mapping.asciidoc" rel="noopener noreferrer"&gt;&lt;code&gt;map&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;map global user \" \
  -docstring "Evaluate current selection in local database" \
  ':eval-query local&amp;lt;ret&amp;gt;'

map global user \' \
  -docstring "Evaluate current paragraph in local database" \
  '&amp;lt;a-i&amp;gt;p:eval-query local&amp;lt;ret&amp;gt;'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've created two bindings here; the first one just calls our command with the &lt;code&gt;local&lt;/code&gt; connection filled in. The second one does the same thing, but selects the current paragraph.&lt;/p&gt;

&lt;p&gt;This has the effect of letting us run a query with two keystrokes, even if we don't have it already selected. If we need more control, for example to run just part of a query, we can use the more basic double quote version. You can see that in action below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flk0i6p7h57hfi6e3phzv.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flk0i6p7h57hfi6e3phzv.gif" alt="Shortcuts!" width="504" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that the keys I chose to bind are completely arbitrary; change them to whatever you prefer!&lt;/p&gt;

&lt;h1&gt;
  
  
  Addenda
&lt;/h1&gt;

&lt;p&gt;This "plugin" is far from done. Like every software project, it has the potential to be a business/all-consuming obsession, so I'm going to stop here, for now. Partly as a disclaimer, and partly as an exercise for the reader, here's a non-exhaustive list of improvements that could be made:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large result sets will not be shown; I'm not sure what's going on internally, but Kakoune appears to have an upward limit on the amount of text that can fit in the &lt;code&gt;info&lt;/code&gt; block. One way to handle this would be to put the results in a &lt;a href="https://github.com/mawww/kakoune/blob/master/doc/pages/commands.asciidoc#files-and-buffers" rel="noopener noreferrer"&gt;scratch buffer&lt;/a&gt; instead.&lt;/li&gt;
&lt;li&gt;Plugins really shouldn't put keyboard shortcuts in the user scope. A better approach might be to create a &lt;a href="https://github.com/mawww/kakoune/blob/17c5e7aa5fb020186378f7f738e617b5743839fc/doc/pages/modes.asciidoc#user-modes" rel="noopener noreferrer"&gt;custom user mode&lt;/a&gt; to namespace the shortcuts defined by the plugin, then &lt;a href="https://github.com/mawww/kakoune/blob/17c5e7aa5fb020186378f7f738e617b5743839fc/doc/pages/options.asciidoc" rel="noopener noreferrer"&gt;declare an option&lt;/a&gt; that a user can override in her config file. To get that into the mappings, you'd likely have to listen for a &lt;code&gt;GlobalSetOption&lt;/code&gt; &lt;a href="https://github.com/mawww/kakoune/blob/17c5e7aa5fb020186378f7f738e617b5743839fc/doc/pages/hooks.asciidoc" rel="noopener noreferrer"&gt;hook&lt;/a&gt; to re-bind the mapping when the user sets the module option.&lt;/li&gt;
&lt;li&gt;Errors and edge cases could be handled much more reliably. I mostly ignored it for the purposes of this post.&lt;/li&gt;
&lt;li&gt;In order to share this code (and take on the accompanying burden of maintaining an open source project), we should wrap it up in an installable package. This might be as simple as making the file downloadable somewhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maybe I'll do further posts on how to accomplish these things 😉&lt;/p&gt;

&lt;p&gt;At any rate, my point here isn't primarily to demonstrate how to create a Kakoune plugin; it's to showcase the flexibility and ease with which you can customize Kakoune to suit &lt;em&gt;your&lt;/em&gt; workflow. An ad-hoc architecture that only concerns one person's use case can be much less robust than a plugin meant for general use. Don't be afraid to push JetBrains out of your way and make some of your own tools!&lt;/p&gt;

</description>
      <category>kakoune</category>
    </item>
    <item>
      <title>Using Kakoune as a Query Browser</title>
      <dc:creator>Jon Staab</dc:creator>
      <pubDate>Mon, 27 May 2019 20:58:25 +0000</pubDate>
      <link>https://dev.to/staab/using-kakoune-as-a-query-browser-2j72</link>
      <guid>https://dev.to/staab/using-kakoune-as-a-query-browser-2j72</guid>
      <description>&lt;p&gt;For my day-to-day work, I use &lt;a href="https://tableplus.io/"&gt;TablePlus&lt;/a&gt; to develop/maintain various postgres and mysql databases, which I can honestly recommend as a pretty great query browser. &lt;/p&gt;

&lt;p&gt;Like any other piece of software though, it has its warts, among which are sub-par query formatting and odd text-editing keybindings. This isn't helped by the fact that I use Kakoune (a text editor that's built around doing vim "the unix way") for most of my text-editing activities. I also use tmux for a window manager, since Kakoune doesn't come with one. &lt;/p&gt;

&lt;p&gt;So I asked myself: instead of wishing there were Kakoune-like editing abilities in TablePlus, what if I put sql into Kakoune? It turned out to be easier than I imagined because, well, unix.&lt;/p&gt;

&lt;h1&gt;
  
  
  Editing queries
&lt;/h1&gt;

&lt;p&gt;To start with, I created a directory to put all my scripts and sql-related projects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir query-browser
$ cd query-browser
$ mkdir scripts projects
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creating a place where I can edit queries was as simple as creating a &lt;code&gt;sql&lt;/code&gt; file, opening it in Kakoune, and copying my query-in-progress into it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir projects/my-project
$ kak projects/my-project/queries.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This, so far, looks like any other code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lb94By9f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/e1fkly2m5z71msr6wldb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lb94By9f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/e1fkly2m5z71msr6wldb.png" alt="Queries in Kakoune"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thinking ahead a bit, that looks problematic. If I'm developing that last query, I don't want to run the CREATE TABLE query every time! Easily solved:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6zmdSUbb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/a730k7m11ibfzrslyfyo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6zmdSUbb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/a730k7m11ibfzrslyfyo.png" alt="Queries split into multiple Kakoune buffers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now I can run the setup query once on the right, close that tmux pane, and get to work on the query I'm developing. &lt;/p&gt;

&lt;h1&gt;
  
  
  Running queries
&lt;/h1&gt;

&lt;p&gt;Now that I have some sql files, I can run them using the &lt;code&gt;psql&lt;/code&gt; command line tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ psql -d postgres -f projects/dogfish/setup.sql
INSERT 0 3
$ psql -d postgres -f projects/dogfish/queries.sql
  name   | length | edible
--------------+--------+--------
 dogfish |   22.3 | t
(1 row)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's generalize this by saving it to a script called &lt;code&gt;run.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;psql -d postgres -e -f "$1" ${@:2}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a quick &lt;code&gt;chmod +x ./scripts/run.sh&lt;/code&gt;, we can run our queries file with &lt;code&gt;./scripts/run.sh projects/dogfish/queries.sql&lt;/code&gt;. Notice that I'm passing the rest of the arguments directly to &lt;code&gt;psql&lt;/code&gt; so we still have access to all of its command line options.&lt;/p&gt;

&lt;h1&gt;
  
  
  Developing with a feedback loop
&lt;/h1&gt;

&lt;p&gt;I love quick feedback when I'm working. I almost always have some tests running in another window, or hot reload turned on for my app. We can do the same thing here, too. &lt;/p&gt;

&lt;p&gt;Let's make another script called &lt;code&gt;./scripts/watch.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;find "$1" | entr -p -r ./scripts/run.sh $@
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one uses &lt;code&gt;find&lt;/code&gt; along with &lt;a href="http://eradman.com/entrproject/"&gt;&lt;code&gt;entr&lt;/code&gt;&lt;/a&gt; to watch the given query file and run it when it changes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that I &lt;em&gt;don't&lt;/em&gt; recommend this if you're going to be using this technique in production. It's much better to create a &lt;a href="https://github.com/mawww/kakoune/blob/master/doc/pages/commands.asciidoc#declaring-new-commands"&gt;custom command&lt;/a&gt; that explicitly runs your query. For me, I want a fast feedback loop in development, so file watching fits the bill.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can start our watcher (don't forget to &lt;code&gt;chmod +x&lt;/code&gt; it) with &lt;code&gt;./scripts/watch.sh projections/dogfish/queries.sql&lt;/code&gt;. Now every time we make a change to the file, it runs it and shows us the results!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rIUu6Dlv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/4rv16g58u1x0aj6k6f98.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rIUu6Dlv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/4rv16g58u1x0aj6k6f98.png" alt="A working feedback loop"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Auto-formatting
&lt;/h1&gt;

&lt;p&gt;The possibilities with this technique are endless, but let's cap it off here with adding auto-formatting to our query browser. If you're following along, install &lt;a href="https://github.com/darold/pgFormatter"&gt;pgFormatter&lt;/a&gt; for your OS (&lt;code&gt;brew install pgformatter&lt;/code&gt; works on OSX). Now, save a new script to &lt;code&gt;./scripts/format.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;current="$(cat $1)"
formatted="$(pg_format $1 -s 2 ${@:2}"

# Only write to the file if it changed to avoid an infinite loop
if [[ "$current" != "$formatted" ]]; then
  echo "$formatted" &amp;gt; "$1"
fi

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

&lt;/div&gt;



&lt;p&gt;Chmod it and add it into our &lt;code&gt;watch&lt;/code&gt; script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;find "$1" | entr -p -r bash -c "./scripts/format.sh $1; ./scripts/run.sh $@"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now we have automatic query formatting:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q-S9NaNI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.makeagif.com/media/5-27-2019/nnn_0S.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q-S9NaNI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.makeagif.com/media/5-27-2019/nnn_0S.gif" alt="nnn_0S"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Final Words
&lt;/h1&gt;

&lt;p&gt;Obviously this isn't a full-featured query browser, but for me, the longer I develop software, the fewer features I &lt;em&gt;want&lt;/em&gt; in the software that I use. Kakoune is a great foundation for building custom, minimal tools for day-to-day use. And, of course, this technique isn't limited to Kakoune — it's just a matter of building small tools that do one thing well, and composing them together. &lt;/p&gt;

&lt;p&gt;I do encourage you to try Kakoune out if you haven't! Its learning curve is not beginner friendly, but it does pay off for the dedicated student.&lt;/p&gt;

</description>
      <category>kakoune</category>
      <category>postgres</category>
      <category>unix</category>
      <category>entr</category>
    </item>
  </channel>
</rss>
