<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Rebecca Le on sevenseacat.net</title><link>https://sevenseacat.net/</link><description>Recent content in Rebecca Le on sevenseacat.net</description><generator>Hugo</generator><language>en-au</language><lastBuildDate>Fri, 08 Nov 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://sevenseacat.net/rss.xml" rel="self" type="application/rss+xml"/><item><title>Modelling `through` relationships with Ash</title><link>https://sevenseacat.net/posts/2024/through-relationships-with-ash/</link><pubDate>Fri, 08 Nov 2024 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2024/through-relationships-with-ash/</guid><description>&lt;p&gt;One thing I&amp;rsquo;ve long thought missing from &lt;a href="http://ash-hq.org"&gt;Ash framework&lt;/a&gt; is the ability to create a type of &amp;ldquo;has many through&amp;rdquo; relationships. These are slightly different from &lt;a href="https://hexdocs.pm/ash/dsl-ash-resource.html#relationships-many_to_many"&gt;many-to-many relationships&lt;/a&gt;, which &lt;em&gt;do&lt;/em&gt; exist in Ash already - these are one way relationships, one -&amp;gt; many -&amp;gt; many. The other day I realized that these types of relationships &lt;em&gt;can&lt;/em&gt; still be created, using custom relationships - but it&amp;rsquo;s a little bit obscure.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; All of the code in this blog post is in a &lt;a href="https://github.com/sevenseacat/ash_through_relationship_example"&gt;repo on GitHub&lt;/a&gt;. Download a copy of the repo and follow along!&lt;/p&gt;</description></item><item><title>Dynamic HTML progress bars with TailwindCSS</title><link>https://sevenseacat.net/posts/2024/dynamic-html-progress-bars-with-tailwindcss/</link><pubDate>Sat, 31 Aug 2024 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2024/dynamic-html-progress-bars-with-tailwindcss/</guid><description>&lt;p&gt;Recently I was tasked with finding out why a timeline component wasn&amp;rsquo;t working in our Phoenix application, that shows the current progress of an operation as users mouse over different stages of it. It didn&amp;rsquo;t show any progress at all - not on first page load, or not when stages were hovered over.&lt;/p&gt;&#10;&lt;p&gt;There&amp;rsquo;s a little bit of Phoenix LiveView magic involved, but the HTML for the component boiled down to this:&lt;/p&gt;</description></item><item><title>Updating flash messages from Phoenix LiveView components</title><link>https://sevenseacat.net/posts/2023/flash-messages-in-phoenix-liveview-components/</link><pubDate>Wed, 22 Mar 2023 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2023/flash-messages-in-phoenix-liveview-components/</guid><description>&lt;p&gt;&lt;strong&gt;Written using Phoenix 1.7.2 and Phoenix LiveView 0.18.18&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;Every now and then in my daily development work, I find something within Phoenix and LiveView that surprises me a little bit, that requires a bit of thought and tinkering to make it work the way I would expect.&lt;/p&gt;&#10;&lt;p&gt;Today&amp;rsquo;s topic: flash messages within LiveView components.&lt;/p&gt;&#10;&lt;p&gt;Most of us are probably familiar with flash messages - the little (usually coloured) boxes that pop up to let us know pertinent information that we might find interesting, usually related to what we&amp;rsquo;re doing right now.&lt;/p&gt;</description></item><item><title>How does Ecto know the current UTC date/time?</title><link>https://sevenseacat.net/posts/2023/current-utc-datetime-in-ecto/</link><pubDate>Thu, 16 Feb 2023 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2023/current-utc-datetime-in-ecto/</guid><description>&lt;p&gt;&lt;strong&gt;Written using Ecto 3.9.4, Elixir 1.14.3 and Erlang 25.2.2&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;I recently spent some time looking into a timezone-related issue in an app I was working on, and one of the things I noticed was that Ecto was correctly saving timestamps for &lt;code&gt;inserted_at&lt;/code&gt;/&lt;code&gt;updated_at&lt;/code&gt; in &lt;abbr title="Coordinated Universal Time"&gt;UTC&lt;/abbr&gt;, despite my database being on my local machine (and not configured to be in UTC, hence issues).&lt;/p&gt;&#10;&lt;p&gt;This is the expected behaviour, so at the time I thought nothing of it. But I never told my app or Ecto what time zone I/my database are in - how exactly does Ecto know how to convert from my local time to UTC? Is it doing any conversion at all? Are timezones a figment of my imagination? And thus I started investigating.&lt;/p&gt;</description></item><item><title>Got errors with file uploads in Phoenix LiveView 0.18?</title><link>https://sevenseacat.net/posts/2022/errors-with-file-uploads-in-liveview-0.18/</link><pubDate>Thu, 29 Sep 2022 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2022/errors-with-file-uploads-in-liveview-0.18/</guid><description>&lt;p&gt;&lt;a href="https://www.phoenixframework.org/blog/phoenix-liveview-0.18-released"&gt;Phoenix LiveView 0.18 is out&lt;/a&gt;, and it comes with a bunch of cool new features that I&amp;rsquo;m eager to take advantage of. However, it comes with a few hidden gotchas for upgraders from previous versions&amp;hellip;&lt;/p&gt;&#10;&lt;p&gt;A couple of them I noticed were around file uploads, and previews of said file uploads.&lt;/p&gt;&#10;&lt;h2 id="my-form-doesnt-do-anything-when-i-submit-it-anymore"&gt;My form doesn&amp;rsquo;t do anything when I submit it, anymore&amp;hellip;&lt;a class="heading-anchor" href="#my-form-doesnt-do-anything-when-i-submit-it-anymore" aria-label="Link to this section"&gt;#&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;tl;dr - upgrade to LiveView 0.18.1.&lt;/p&gt;</description></item><item><title>One neat trick to getting Tailwind's JIT compiler working in Phoenix</title><link>https://sevenseacat.net/posts/2021/tailwind-jit-in-phoenix/</link><pubDate>Fri, 21 May 2021 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2021/tailwind-jit-in-phoenix/</guid><description>&lt;p&gt;&lt;strong&gt;Written using Phoenix 1.5 and Tailwind 2.1&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Note: This does not work on Windows machines. I&amp;rsquo;m sorry, Windows users!&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m a big fan of &lt;a href="http://tailwindcss.com"&gt;Tailwind CSS&lt;/a&gt; for styling the user interfaces of my web applications. (This site is styled with Tailwind!) Recently it received an awesome update that added &lt;abbr title="Just-In-Time"&gt;JIT&lt;/abbr&gt; compilation support. This has a couple of amazing benefits:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Minimal stylesheets will be compiled on demand in development. No more massive 5MB CSS files that kill your developer tools whenever you try to use them!&lt;/li&gt;&#10;&lt;li&gt;What you see in development is what you&amp;rsquo;ll see in production - the purge rules are now used for both environments. No more deploying changes, realizing you had some Tailwind classes in a file you hadn&amp;rsquo;t whitelisted, that are now unstyled only in production! Ooooops&amp;hellip;&lt;/li&gt;&#10;&lt;li&gt;You get access to more classes that were never even possible before - if you want to chain modifiers together like &lt;code&gt;dark:hover:lg:bg-blue-900&lt;/code&gt;, you can! Before these all had to be precomputed, and for sanity&amp;rsquo;s sake (the sheer number of possible options), they weren&amp;rsquo;t.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;This is all great! There&amp;rsquo;s one trick to getting it working in your Phoenix applications though.&lt;/p&gt;</description></item><item><title>Efficiently querying data in batches using Absinthe</title><link>https://sevenseacat.net/posts/2021/querying-batches-with-absinthe/</link><pubDate>Fri, 14 May 2021 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2021/querying-batches-with-absinthe/</guid><description>&lt;p&gt;Building APIs with Absinthe and GraphQL gives you great power, but with great power comes great responsibility. (Or something like that.) It can be easy to make a bit of a mess and go about fetching data in very slow and inefficient ways, and it&amp;rsquo;s up to us as developers to notice it happening and nip it in the bud.&lt;/p&gt;&#10;&lt;p&gt;Consider the following scenario:&lt;/p&gt;&#10;&lt;p&gt;Our app contains records for Courses, such as courses you might take at a college or university, and each Course has many associated Enrolments. Each Enrolment has a &lt;code&gt;type&lt;/code&gt; field, holding either the value &amp;ldquo;auto&amp;rdquo; or the value &amp;ldquo;manual&amp;rdquo;.&lt;/p&gt;</description></item><item><title>Adventures With Base-64</title><link>https://sevenseacat.net/posts/2020/adventures-with-base64/</link><pubDate>Thu, 20 Feb 2020 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2020/adventures-with-base64/</guid><description>&lt;p&gt;This&amp;rsquo;ll be a quick one, I promise.&lt;/p&gt;&#10;&lt;p&gt;Recently a friend was looking at generating random-but-unique URL identifiers for a web project, and I immediately thought about &lt;a href="https://www.youtube.com/watch?v=gocwRvLhDf8"&gt;this awesome video by Tom Scott&lt;/a&gt; about how YouTube encodes its IDs for URLs. (If you haven&amp;rsquo;t seen the video, or any of Tom Scott&amp;rsquo;s videos, I highly recommend them.) Rewatching the video, I was like ohhhh, base-64 encoding, I know how that works&amp;hellip;. wait a minute. This is not the base-64 encoding I am used to. Are there &lt;em&gt;two&lt;/em&gt; types of base-64 encoding? Am I missing something?&lt;/p&gt;</description></item><item><title>One-keystroke Unicode characters in QMK on macOS</title><link>https://sevenseacat.net/posts/2018/unicode-in-qmk-on-osx/</link><pubDate>Fri, 30 Mar 2018 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2018/unicode-in-qmk-on-osx/</guid><description>&lt;p&gt;&lt;strong&gt;Written using QMK 0.6.0 on macOS High Sierra 10.13.3&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;I recently picked up an &lt;a href="http://ergodox-ez.com"&gt;ErgoDox EZ&lt;/a&gt; keyboard, and was excited to add some Unicode characters to it. Some may know that I dabble with Esperanto, so I wanted the ability to type characters like ŝ and ŭ with a single shortcut. Plus, ✨ emoji ✨! I know macOS has an emoji picker with &lt;kbd&gt;Cmd + Ctrl + Space&lt;/kbd&gt;, but that&amp;rsquo;s kinda painful to do repeatedly.&lt;/p&gt;&#10;&lt;p&gt;So, I decided to dig into the &lt;a href="https://docs.qmk.fm/"&gt;QMK documentation&lt;/a&gt; and build my own keymap, including the nifty Unicode features I wanted.&lt;/p&gt;</description></item><item><title>How I Work: Tackling Advent of Code, Day 21</title><link>https://sevenseacat.net/posts/2018/advent-of-code-day-21/</link><pubDate>Sun, 28 Jan 2018 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2018/advent-of-code-day-21/</guid><description>&lt;p&gt;For the past three years, the amazing &lt;a href="https://twitter.com/ericwastl/"&gt;Eric Wastl&lt;/a&gt; has been running the &lt;a href="http://adventofcode.com/"&gt;Advent of Code&lt;/a&gt; - programming puzzles in the format of an advent calendar, a new puzzle every day for the month of December. They&amp;rsquo;re mindbending but great fun!&lt;/p&gt;&#10;&lt;p&gt;I wondered if people might be curious how I attack these kinds of puzzles - and by &lt;a href="https://twitter.com/sevenseacat/status/946658974442569728"&gt;the reaction I got on Twitter&lt;/a&gt;, I guess the answer is yes! So here goes - this is day 21, the most recent puzzle I&amp;rsquo;ve completed in the 2017 edition.&lt;/p&gt;</description></item><item><title>The Noobs Guide to Programming a Vortex Core</title><link>https://sevenseacat.net/posts/2017/the-noobs-guide-to-programming-a-vortex-core/</link><pubDate>Wed, 30 Aug 2017 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2017/the-noobs-guide-to-programming-a-vortex-core/</guid><description>&lt;p&gt;Recently I&amp;rsquo;ve formed a bit of an unhealthy obsession with mechanical keyboards, and one of the first boards I&amp;rsquo;ve picked up is the infamous Vortex Core.&lt;/p&gt;&#10;&lt;p&gt;For those not in the know, the Core is a 40% keyboard - a small keyboard with just 47 keys. It looks like this:&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://sevenseacat.net/images/vortex-core/photo.png" alt="The Vortex Core."&gt;&lt;/p&gt;&#10;&lt;p&gt;(image taken from vortexgear.tw)&lt;/p&gt;&#10;&lt;p&gt;No numbers! No function keys! No arrows! How does one even use it????&lt;/p&gt;</description></item><item><title>When Rubygems gem versioning goes bad</title><link>https://sevenseacat.net/posts/2017/rubygems-gem-versioning-bugs/</link><pubDate>Tue, 04 Apr 2017 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2017/rubygems-gem-versioning-bugs/</guid><description>&lt;p&gt;&lt;a href="http://rubygems.org/"&gt;Rubygems&lt;/a&gt;, the package manager for Ruby, has long had this neat little functionality to let you specify the version of a gem you want to use, when running a gem-related command. If you have two versions of Rails installed, say versions 4.2.8 and 5.0.2, you can specify which to use by prefixing the command with a specially-formatted version number.&lt;/p&gt;&#10;&lt;p&gt;By default Rubygems grabs the latest version installed, so if you ran &lt;code&gt;rails new myapp&lt;/code&gt; you would get a Rails 5.0.2 app, but you can override this by saying &lt;code&gt;rails _4.2.8_ new myapp&lt;/code&gt; to get a Rails 4.2.8 app instead. (The underscores aren&amp;rsquo;t a typo, that&amp;rsquo;s the actual command.) It&amp;rsquo;s not a feature you&amp;rsquo;d use every day, but when you need it it&amp;rsquo;s immensely useful. But someone came into the #rubyonrails channel on IRC today, reporting that this wasn&amp;rsquo;t working for them. They were trying to create a Rails 5.0.2 app with the proper &lt;code&gt;rails _5.0.2_ new appname&lt;/code&gt; command, and were getting a Rails 5.1 app instead. What gives?&lt;/p&gt;</description></item><item><title>The awesomeness of sass-autocompile</title><link>https://sevenseacat.net/posts/2017/sass-autocompile/</link><pubDate>Sun, 02 Apr 2017 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2017/sass-autocompile/</guid><description>&lt;p&gt;Sometimes there&amp;rsquo;s a real urge to overcomplicate tech situations. When looking at converting my blog from &lt;a href="http://jekyllrb.com"&gt;Jekyll&lt;/a&gt; to &lt;a href="http://gohugo.io"&gt;Hugo&lt;/a&gt; (which I&amp;rsquo;ll write a whole separate post about, soon!), one of the big listed &lt;a href="https://www.smashingmagazine.com/2015/11/static-website-generators-jekyll-middleman-roots-hugo-review/#hugo"&gt;weaknesses&lt;/a&gt; of Hugo is no concept of an asset pipeline - nothing to preprocess and compile stylesheets, JavaScript files, etc.&lt;/p&gt;&#10;&lt;p&gt;For larger websites, this might be a real concern. My blog, however, is pretty simple - just one or two Sass files, no JavaScript to speak of.&lt;/p&gt;</description></item><item><title>TIL: Ecto reading after writes</title><link>https://sevenseacat.net/posts/2017/til-ecto-reading-after-writes/</link><pubDate>Sat, 01 Apr 2017 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2017/til-ecto-reading-after-writes/</guid><description>&lt;p&gt;Today I learned that by default, Ecto doesn&amp;rsquo;t read data back from the database, after writing new or updated data.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;The scenario:&lt;/strong&gt; A trigger in the database, that calculates the new value of a field before insert or update. The person who presented this problem was quite convinced that the trigger wasn&amp;rsquo;t running in their tests, based on code like the following:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-elixir" data-lang="elixir"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;defmodule&lt;/span&gt; &lt;span class="nc"&gt;Folder&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Ecto.Schema&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;folders&amp;#34;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:path&lt;/span&gt; &lt;span class="c1"&gt;# Autocalculated via a PostgreSQL trigger.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-elixir" data-lang="elixir"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;folder paths are autogenerated&amp;#34;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;folder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="nc"&gt;Folder&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;folder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="c1"&gt;# The folder was inserted successfully&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;folder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="c1"&gt;# But this line was failing.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because they could see paths being generated for folders in their development database, they assumed it was a problem with their test environment setup.&lt;/p&gt;</description></item><item><title>A Whirlwind Guide to Esperanto</title><link>https://sevenseacat.net/posts/2016/guide-to-esperanto/</link><pubDate>Tue, 05 Apr 2016 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2016/guide-to-esperanto/</guid><description>&lt;p&gt;So after I wrote my last post, I heard from several people that they&amp;rsquo;d vaguely heard of this Esperanto thing, didn&amp;rsquo;t know anything about it, could I please tell them a little more? Absolutely! Enjoy!&lt;/p&gt;&#10;&lt;p&gt;&lt;small&gt;Note: Some of this may be somewhat factually inaccurate, as I am only a komencanto (beginner) and working off about six months casual learning.&lt;/small&gt;&lt;/p&gt;&#10;&lt;h2 id="the-quest-for-a-universal-language"&gt;The quest for a universal language&lt;a class="heading-anchor" href="#the-quest-for-a-universal-language" aria-label="Link to this section"&gt;#&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;The history of human language is long and fascinating. There are over 6000 spoken languages in the world currently, and there have been many thousands more lost in the sands of time. It&amp;rsquo;s no surprise that we seek ways to improve our communication, make ourselves more heard, what better way than if we could all speak the same language? And because all of our current languages are so imperfect and flawed, wouldn&amp;rsquo;t it be great if someone (or a group of people) &lt;em&gt;designed&lt;/em&gt; this one perfect language we could all share? They could make it simple, straightforward, easy to learn, easy to use, and the world would be a much better place. Or so the theory goes.&lt;/p&gt;</description></item><item><title>Internationalization using Gettext in the Phoenix framework</title><link>https://sevenseacat.net/posts/2015/i18n-in-phoenix-apps/</link><pubDate>Sun, 20 Dec 2015 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2015/i18n-in-phoenix-apps/</guid><description>&lt;p&gt;&lt;strong&gt;Written using Phoenix 1.1&lt;/strong&gt;&lt;/p&gt;&#10;&lt;h2 id="internationalization---what-is-it"&gt;Internationalization - what is it?&lt;a class="heading-anchor" href="#internationalization---what-is-it" aria-label="Link to this section"&gt;#&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;&lt;em&gt;Internationalization&lt;/em&gt; (henceforth called &lt;abbr title="Internationalization"&gt;i18n&lt;/abbr&gt;, because programmers are lazy and hate typing long words) is the process of adapting a web application for use in different languages or cultures. Any application that allows you to change your language has implemented i18n. It&amp;rsquo;s a close sibling of &lt;em&gt;localization&lt;/em&gt; (&lt;abbr title="localization"&gt;L10n&lt;/abbr&gt;) - the process of actually implementing a different language/culture in a web app. Once support for i18n has been included in an app, it can be localized. This may involve adding new text strings in a different language, or configuring different date formats.&lt;/p&gt;</description></item><item><title>Books, books, books!</title><link>https://sevenseacat.net/posts/2015/books-books-books/</link><pubDate>Fri, 10 Jul 2015 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2015/books-books-books/</guid><description>&lt;p&gt;I&amp;rsquo;m an avid reader. A really, really avid reader. I grew up reading everything I could get my hands on (including a lot of things that weren&amp;rsquo;t, ahem, age-appropriate) and as an adult I still love reading so many different things. I picked up a Kindle in about 2008, and never looked back.&lt;/p&gt;&#10;&lt;p&gt;Over the years, I&amp;rsquo;ve built up quite a backlog of technical books. Ooh, that looks good, one-click buy, and then forget about it. And there are a lot of great writers out there writing a lot of really great things, on topics I wish I knew more about but never got the time to learn. So now I&amp;rsquo;m making a pledge - to read and work through a whole pile of the e-books I already own, and expand my horizons.&lt;/p&gt;</description></item><item><title>Using page-specific page titles in the Phoenix framework</title><link>https://sevenseacat.net/posts/2015/page-specific-titles-in-phoenix/</link><pubDate>Mon, 01 Jun 2015 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2015/page-specific-titles-in-phoenix/</guid><description>&lt;p&gt;&lt;strong&gt;Written using Phoenix 0.13.1&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;One common task in web apps is setting a custom page title for each URL - your homepage should have a different title than a page listing out all of your projects (for example), which should have a different title than a page showing details of a single project.&lt;/p&gt;&#10;&lt;p&gt;There isn&amp;rsquo;t a built-in way to do this in Phoenix, but after much reading around forums, GitHub issues, etc. I hit on two potential ways to do it.&lt;/p&gt;</description></item><item><title>`add_foreign_key` gotchas in Rails 4.2</title><link>https://sevenseacat.net/posts/2015/add_foreign_key-gotchas-in-rails-4-2/</link><pubDate>Tue, 24 Feb 2015 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2015/add_foreign_key-gotchas-in-rails-4-2/</guid><description>&lt;p&gt;Rails 4.2 finally added native support for database-level foreign keys, which is great. You can write the following code in a migration (assuming the presence of a &lt;code&gt;users&lt;/code&gt; table):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:posts&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;references&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;add_foreign_key&lt;/span&gt; &lt;span class="ss"&gt;:posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And Rails will apply its conventions and infer what you expect:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Column | Type | Modifiers&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---------+---------+----------------------------------------------------&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; id | integer | not null default nextval(&amp;#39;posts_id_seq&amp;#39;::regclass)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; user_id | integer |&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Indexes:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;posts_pkey&amp;#34; PRIMARY KEY, btree (id)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;index_posts_on_user_id&amp;#34; btree (user_id)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Foreign-key constraints:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;fk_rails_cbe63c1bd4&amp;#34; FOREIGN KEY (user_id) REFERENCES users(id)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We have a database-level foreign key pointing from the &lt;code&gt;user_id&lt;/code&gt; column of the &lt;code&gt;posts&lt;/code&gt; table, to the &lt;code&gt;id&lt;/code&gt; column of the &lt;code&gt;posts&lt;/code&gt; table. You can specify all of the expected options for the foreign key - &lt;code&gt;on_delete&lt;/code&gt;, &lt;code&gt;on_cascade&lt;/code&gt;, etc. This will work just fine in both PostgreSQL and MySQL - it&amp;rsquo;s a no-op in every other ActiveRecord adapter (including SQLite!)&lt;/p&gt;</description></item><item><title>Slaying Dragons</title><link>https://sevenseacat.net/posts/2015/slaying-dragons/</link><pubDate>Sat, 07 Feb 2015 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2015/slaying-dragons/</guid><description>&lt;p&gt;The following is a complete piece of code from a real Rails app that is currently in production.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;supplied_within_states&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;states&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# So you can pass either an array or a list of arguments to the method&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;states&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;states&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flatten&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;states&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;include?&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;all&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scoped&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Here be dragons! - RS 2013-05-20&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Ideally this query would be much simpler - something like:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# scoped.joins(:product_variants =&amp;gt; {:supplier_product_variants =&amp;gt; :supplier})&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# .merge(Supplier.in_states(states))&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# to join all the tables and then filter on the final supplier table... however!&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# between Rails 3.0 not supporting nested has_many through associations (which is where the&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# supplier table comes from) and `merge` behaving a bit strangely with said nested HMT&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# (support for this got added by a gem).. this is the final result&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# It takes a couple of queries (two here and several in the Supplier.in_states class&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# method) but it does the job&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# When this app is upgraded to Rails 3.1+ this can be revisited (as it supports nested HMT&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# natively and probably has a bunch of other fixes too)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scoped&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;joins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:product_variants&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:supplier_product_variants&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:supplier&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:suppliers&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Supplier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in_states&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;states&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;)}))&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As the comment states, I wrote it, when the app was using Rails 3.0 - and there are many dragons present. It&amp;rsquo;s a horribly nested query that maps through a couple of associations and then does a second lookup, that also uses a couple of associations&amp;hellip; it does a lot of queries to get a simple result - a list of all products in the system that can be supplied within certain states of Australia.&lt;/p&gt;</description></item><item><title>Why you should be using Database Cleaner's `deletion` strategy</title><link>https://sevenseacat.net/posts/2015/use-database-cleaners-deletion-strategy/</link><pubDate>Sun, 01 Feb 2015 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2015/use-database-cleaners-deletion-strategy/</guid><description>&lt;blockquote&gt;&#10;&lt;p&gt;&lt;strong&gt;me&lt;/strong&gt;: Your regularly scheduled reminder that Database Cleaner&amp;rsquo;s &lt;code&gt;deletion&lt;/code&gt; strategy is so much faster than the &lt;code&gt;truncation&lt;/code&gt; strategy.&lt;br /&gt;&#10;&lt;strong&gt;my coworker&lt;/strong&gt;: You should write a blog post about it!&lt;br /&gt;&#10;&lt;cite&gt;— from &lt;a href="https://twitter.com/sevenseacat/status/560807525151301634"&gt;Twitter&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;&#10;&lt;/blockquote&gt;</description></item><item><title>New Year's Resolutions</title><link>https://sevenseacat.net/posts/2015/new-years-resolutions/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/posts/2015/new-years-resolutions/</guid><description>&lt;ol&gt;&#10;&lt;li&gt;&lt;s&gt;Start a blog.&lt;/s&gt; (Hello. This is my blog.)&lt;/li&gt;&#10;&lt;li&gt;Maintain said blog. This could be interesting - what will I write about?&lt;/li&gt;&#10;&lt;li&gt;Get my tenpin bowling average up to 175. (I currently hover between 155 and 160.)&lt;/li&gt;&#10;&lt;li&gt;Lose 10kg from whatever my post-holiday weight will be.&lt;/li&gt;&#10;&lt;li&gt;Finish reviewing, and see a successful publishing of, &lt;a href="http://manning.com/bigg2"&gt;Rails 4 in Action&lt;/a&gt;.&lt;/li&gt;&#10;&lt;li&gt;Learn enough AngularJS to build and deploy one (or more) apps.&lt;/li&gt;&#10;&lt;li&gt;Get my driver&amp;rsquo;s license!!!!&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Let&amp;rsquo;s see how I go!&lt;/p&gt;</description></item><item><title>Books</title><link>https://sevenseacat.net/books/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/books/</guid><description/></item><item><title>Projects</title><link>https://sevenseacat.net/projects/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sevenseacat.net/projects/</guid><description>&lt;p&gt;I&amp;rsquo;m a member of the &lt;a href="https://ash-hq.org"&gt;Ash Framework&lt;/a&gt; core team, and most of my open source work these days&#10;lives somewhere in the Ash ecosystem. Here are some of the things I&amp;rsquo;ve built.&lt;/p&gt;</description></item></channel></rss>