Jekyll2021-01-25T12:29:21+00:00https://redgreen.no/feed.xmlredgreenRedgreen is a blog about programming, funny code and tech written by Norwegian developer and rubyist Lars O. Overskeid.
Extend ActiveStorage::Blob with callbacks2021-01-25T09:00:00+00:002021-01-25T09:00:00+00:00https://redgreen.no/2021/01/25/active-storage-callbacks<p>During my <a href="/2021/01/24/custom-analyzer-for-activestorage.html">experimenting with ActiveStorage</a> I came to learn that while it’s been in Rails since version 5.2, it still lacks both validations and callbacks for you attached blobs. Luckily the there’s a community maintained gem with some basic validations, called <a href="https://github.com/igorkasyanchuk/active_storage_validations">active_storage_validations</a>, the callbacks you have to add on your own.</p>
<p>To understand the challenge, let us first look at the flow of attaching an ActiveStorage blob to a new ActiveRecord model (let’s call it <code class="language-plaintext highlighter-rouge">User</code>):</p>
<ol>
<li>Blob gets uploaded</li>
<li>Blob is attached to a in-memory instance of the new <code class="language-plaintext highlighter-rouge">User</code></li>
<li>If the <code class="language-plaintext highlighter-rouge">User</code> object is valid, it, and the <code class="language-plaintext highlighter-rouge">Blob</code> gets persisted.</li>
<li>Asynchronously, the <code class="language-plaintext highlighter-rouge">Analyzer</code> completes it work, and updates the <code class="language-plaintext highlighter-rouge">Blob</code> with new metadata.</li>
</ol>
<p>I want my user to be notified after the blob gets updated with new metadata, so I can refresh the Elasticsearch index with the new information. And while the association model between my User and Blob, <code class="language-plaintext highlighter-rouge">ActiveStorage::Attachment</code> has a <code class="language-plaintext highlighter-rouge">belongs_to :record, touch: true</code>, nothing happens if a blob gets updated.</p>
<p>So let us utilize some of the magic in Ruby, and add some functionality to <code class="language-plaintext highlighter-rouge">ActiveStorage::Blob</code>:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Rails</span><span class="p">.</span><span class="nf">configuration</span><span class="p">.</span><span class="nf">to_prepare</span> <span class="k">do</span>
<span class="k">module</span> <span class="nn">ActiveStorageTouchRecordsAfterAnalyze</span>
<span class="c1"># Gives us some convenient shortcuts, like `prepended`</span>
<span class="kp">extend</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Concern</span>
<span class="c1"># When prepended into a class, define our callback</span>
<span class="n">prepended</span> <span class="k">do</span>
<span class="n">after_update_commit</span> <span class="ss">:touch_all_records</span>
<span class="k">end</span>
<span class="c1"># Iterate all attached records, and touch them</span>
<span class="k">def</span> <span class="nf">touch_all_records</span>
<span class="n">attachments</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">attachment</span><span class="o">|</span>
<span class="c1"># there's a theoretical chance that the record is missing,</span>
<span class="c1"># therefore I .try() the touch.</span>
<span class="n">attachment</span><span class="p">.</span><span class="nf">record</span><span class="p">.</span><span class="nf">try</span><span class="p">(</span><span class="ss">:touch</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1"># After defining the module, call on ActiveStorage::Blob to prepend it in.</span>
<span class="no">ActiveStorage</span><span class="o">::</span><span class="no">Blob</span><span class="p">.</span><span class="nf">prepend</span> <span class="no">ActiveStorageTouchRecordsAfterAnalyze</span>
<span class="k">end</span></code></pre></figure>
<p>The above code goes into <code class="language-plaintext highlighter-rouge">config/initializers/active_storage_touch_records_after_analyze.rb</code>. After a reload, you can verify that it’s working by calling <code class="language-plaintext highlighter-rouge">.analyze</code> on one of your blobs.</p>
<blockquote>
<p>to_prepare: Run after the initializers are run for all Railties (including the application itself), but before eager loading and the middleware stack is built. More importantly, will run upon every code reload in development, but only once (during boot-up) in production and test.</p>
</blockquote>
<p>Note that I’m using a <code class="language-plaintext highlighter-rouge">.to_prepare</code> block around this initializer. This ensures that every bit of Rails is loaded enough, so I can safely inject my code. It also gives the benefit of being reloaded in development, which is quite helpful when making changes to initializers. For more in-depth information on the Rails boot-up stages, check out <a href="https://guides.rubyonrails.org/configuring.html">the official documentation</a>.</p>Lars O. OverskeidActiveStorage is currently missing both validations and callbacks, but you can easily extend it with the callbacks you need.Writing a custom analyzer for ActiveStorage2021-01-24T09:00:00+00:002021-01-24T09:00:00+00:00https://redgreen.no/2021/01/24/custom-analyzer-for-activestorage<p>Every now and then, I sit down an try out some new features of Rails, or gems I’ve read about but never really had any usecase for in my daily work. This weekend I’ve played around with <a href="https://edgeguides.rubyonrails.org/active_storage_overview.html">ActiveStorage</a> and <a href="https://github.com/hotwired/hotwire-rails">Hotwire</a>, making a simple clone of a ETL project I like to rewrite.</p>
<p>The basic use-case for the app is to fetch EDI files from different locations, organise and parse them into a warehousing solution. The production version of this app uses S3 quite heavily, and has lots of custom code for managing the documents stored in S3.</p>
<p>ActiveStorage is quite easy to set up, and has built-in support for S3 storage (and many other cloud solutions). After a file has been attached, it’s queued for what is called analyzing. Basically it iterates an array of registered analyzers, and picks the one that responds to <code class="language-plaintext highlighter-rouge">accept?</code> first. There are a couple of built in analyzers:</p>
<ul>
<li><a href="https://github.com/rails/rails/blob/main/activestorage/lib/active_storage/analyzer/image_analyzer.rb">ImageAnalyzer</a></li>
<li><a href="https://github.com/rails/rails/blob/main/activestorage/lib/active_storage/analyzer/video_analyzer.rb">VideoAnalyzer</a></li>
<li><a href="https://github.com/rails/rails/blob/main/activestorage/lib/active_storage/analyzer/null_analyzer.rb">NullAnalyzer</a></li>
</ul>
<p>They are registered in that order, hooks on to the mimetype of the uploaded files, and gives back some metadata when ran.</p>
<p>For my particular usecase, I want to analyze mostly <code class="language-plaintext highlighter-rouge">text/csv</code> and <code class="language-plaintext highlighter-rouge">text/plaintext</code> files, at least for this basic testing. I’ll start by making a file called <code class="language-plaintext highlighter-rouge">lib/analyzer/edi_analyzer.rb</code> with the very <a href="http://localhost:4000/2021/01/24/custom-analyzer-for-activestorage.html">basic requirements of an ActiveStorage analyzer</a>:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Analyzer::EdiAnalyzer</span> <span class="o"><</span> <span class="no">ActiveStorage</span><span class="o">::</span><span class="no">Analyzer</span>
<span class="k">def</span> <span class="nf">metadata</span>
<span class="p">{</span> <span class="ss">ruby_is_awesome: </span><span class="kp">true</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>This should be enough to register my analyzer, which should be done in an initializer. In <code class="language-plaintext highlighter-rouge">config/initializers/active_storage_analyzers.rb</code> I add the following code:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"> <span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">config</span><span class="p">.</span><span class="nf">active_storage</span><span class="p">.</span><span class="nf">analyzers</span><span class="p">.</span><span class="nf">append</span> <span class="no">Analyzer</span><span class="o">::</span><span class="no">EdiAnalyzer</span></code></pre></figure>
<p>If I open a <code class="language-plaintext highlighter-rouge">rails console</code>, I can call on <code class="language-plaintext highlighter-rouge">ActiveStorage.analyzers</code> to see that my analyzer is correctly registered:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>irb(main):026:0> ActiveStorage.analyzers
=> [ActiveStorage::Analyzer::ImageAnalyzer, ActiveStorage::Analyzer::VideoAnalyzer, Analyzer::EdiAnalyzer]
</code></pre></div></div>
<p>As you can see, the analyzer is registered as the last one being called, if you’d like to add it as the first, change <code class="language-plaintext highlighter-rouge">.append</code> to <code class="language-plaintext highlighter-rouge">.prepend</code> in your initializer.</p>
<p>While the analyzer now is registered, it will never be called, as it has not yet implemented the class method <code class="language-plaintext highlighter-rouge">accept?</code>. Let’s do that.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Analyzer::EdiAnalyzer</span> <span class="o"><</span> <span class="no">ActiveStorage</span><span class="o">::</span><span class="no">Analyzer</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">accept?</span><span class="p">(</span><span class="n">blob</span><span class="p">)</span>
<span class="p">[</span><span class="s1">'text/csv'</span><span class="p">,</span><span class="s1">'text/plain'</span><span class="p">].</span><span class="nf">include?</span> <span class="n">blob</span><span class="p">.</span><span class="nf">content_type</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">metadata</span>
<span class="p">{</span> <span class="ss">ruby_is_awesome: </span><span class="kp">true</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>This returns true given that the <code class="language-plaintext highlighter-rouge">content_type</code> of the uploaded file is either <code class="language-plaintext highlighter-rouge">text/csv</code> or <code class="language-plaintext highlighter-rouge">text/plain</code>. In a later evolution of this analyzer you would probably do a more complex check, but this will do for now.</p>
<p>On to the actual result of the analyzer. When our analyzer is picked as the one to provide metadata, ActiveStorage will call <code class="language-plaintext highlighter-rouge">metadata</code> on an instance of our class. I have already implemented a simple <code class="language-plaintext highlighter-rouge">EdiFileParser</code> class that can parse the headers of these files, so let’s utilize that in our analyzer:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Analyzer::EdiAnalyzer</span> <span class="o"><</span> <span class="no">ActiveStorage</span><span class="o">::</span><span class="no">Analyzer</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">accept?</span><span class="p">(</span><span class="n">blob</span><span class="p">)</span>
<span class="p">[</span><span class="s1">'text/csv'</span><span class="p">,</span><span class="s1">'text/plain'</span><span class="p">].</span><span class="nf">include?</span> <span class="n">blob</span><span class="p">.</span><span class="nf">content_type</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">metadata</span>
<span class="n">parse_file</span> <span class="k">do</span> <span class="o">|</span><span class="n">edi_doc</span><span class="o">|</span>
<span class="p">{</span>
<span class="ss">sender_gln: </span><span class="n">edi_doc</span><span class="p">.</span><span class="nf">sender_gln</span><span class="p">,</span>
<span class="ss">recipient_gln: </span><span class="n">edi_doc</span><span class="p">.</span><span class="nf">recipient_gln</span><span class="p">,</span>
<span class="ss">edi_standard: </span><span class="n">edi_doc</span><span class="p">.</span><span class="nf">standard</span><span class="p">,</span>
<span class="ss">edi_type: </span><span class="n">edi_doc</span><span class="p">.</span><span class="nf">type</span>
<span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">parse_file</span>
<span class="n">download_blob_to_tempfile</span> <span class="k">do</span> <span class="o">|</span><span class="n">file</span><span class="o">|</span>
<span class="n">edi_doc</span> <span class="o">=</span> <span class="no">EdiParser</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">file</span><span class="p">)</span>
<span class="k">if</span> <span class="n">edi_doc</span><span class="p">.</span><span class="nf">valid?</span>
<span class="k">yield</span> <span class="n">edi_doc</span>
<span class="k">else</span>
<span class="n">logger</span><span class="p">.</span><span class="nf">info</span> <span class="s2">"Skipping EDI analysis because it's not a known EDI document format"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>Quite a bit of code added there. While it should be pretty straightforward, let’s break it down some. When <code class="language-plaintext highlighter-rouge">metadata</code> is called on our instance, we make a call for the private method <code class="language-plaintext highlighter-rouge">parse_file</code>. If the call is successfull, and we get an object for our block, we use the object to return a hash of useful metadata.</p>
<p>Inside the <code class="language-plaintext highlighter-rouge">parse_file</code> private method, I call on <code class="language-plaintext highlighter-rouge">download_blob_to_tempfile</code>. This is a private method provided by <code class="language-plaintext highlighter-rouge">ActiveStorage::Analyzer</code>, that returns a <code class="language-plaintext highlighter-rouge">Tempfile</code> with the blob contents.</p>
<p>I then instantiate a copy of my <code class="language-plaintext highlighter-rouge">EdiParser</code> with this <code class="language-plaintext highlighter-rouge">Tempfile</code>. If the parser thinks it’s <code class="language-plaintext highlighter-rouge">valid?</code> I yield the instance of the parser back to my original block, and if it’s not, I log the result and fail silently. The <code class="language-plaintext highlighter-rouge">logger</code> method is also provided by <code class="language-plaintext highlighter-rouge">ActiveStorage::Analyzer</code>, and is an alias for <code class="language-plaintext highlighter-rouge">ActiveStorage.logger</code>.</p>
<p>Given a known format, my ActiveStorage attachment will now have a metadata hash like this:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="p">{</span>
<span class="ss">sender_gln: </span><span class="s2">"1234567890"</span><span class="p">,</span>
<span class="ss">recipient_gln: </span><span class="s2">"0987654321"</span><span class="p">,</span>
<span class="ss">edi_standard: </span><span class="s2">"EFO/Nelfo 4.0"</span><span class="p">,</span>
<span class="ss">edi_type: </span><span class="s2">"Invoice"</span>
<span class="p">}</span></code></pre></figure>
<p>Happy hacking :)</p>Lars O. OverskeidWriting custom analyzers for your ActiveStorage blogs is not well documented, but quite easy. This is how I implemented a simple EDI file analyzer for my neverending hobby project.Manjaro/Arch: transfer packages to another computer2021-01-23T09:00:00+00:002021-01-23T09:00:00+00:00https://redgreen.no/2021/01/23/manjaro-arch-transfer-packages-to-another-computer<p>So you’ve finally set up your computer to your liking, and organized and backed up all your dotfiles. If you want to save a list of all the software you’ve installed, you can use this simple <code class="language-plaintext highlighter-rouge">pacman</code> trick.</p>
<blockquote>
<p><a href="https://archlinux.org/pacman/pacman.8.html">Pacman</a> is a package management utility that tracks installed packages on a Linux system. It features dependency support, package groups, install and uninstall scripts, and the ability to sync your local machine with a remote repository to automatically upgrade packages. Pacman packages are a zipped tar format.</p>
</blockquote>
<p>In order to save all installed packages, execute the following:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pacman -Qqe > .config/packages.txt
</code></pre></div></div>
<p>I’m putting the output into <code class="language-plaintext highlighter-rouge">packages.txt</code> in my <code class="language-plaintext highlighter-rouge">.config</code> folder, as this is a git repo that’s backed up to Github. The <code class="language-plaintext highlighter-rouge">-Qqe</code> options makes a neat list.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">-Q</code>: Query the package database. This operation allows you to view installed packages and their files, as well as meta-information about individual packages (dependencies, conflicts, install date, build date, size). This can be run against the local package database or can be used on individual package files. In the first case, if no package names are provided in the command line, all installed packages will be queried. Additionally, various filters can be applied on the package list.</li>
<li><code class="language-plaintext highlighter-rouge">-q</code>: Show less information for certain query operations. This is useful when pacman’s output is processed in a script. Search will only show package names and not version, group, and description information; owns will only show package names instead of “file is owned by pkg” messages; group will only show package names and omit group names; list will only show files and omit package names; check will only show pairs of package names and missing files; a bare query will only show package names rather than names and versions.</li>
<li><code class="language-plaintext highlighter-rouge">-e</code>: Restrict or filter output to explicitly installed packages. This option can be combined with -t to list explicitly installed packages that are not required by any other package.</li>
</ul>
<p>Now in order to install all these packages on your other machine, execute the following:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo pacman -S - < .config/packages.txt
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">-S</code> flag synchronizes (installs) all packages listed in the <code class="language-plaintext highlighter-rouge">packages.txt</code> file.</p>Lars O. OverskeidHow to make a backup of all installed packages on a Arch/Manjaro distro, and install them on a different machine.Getting Norwegian characters on a US keyboard in Linux2021-01-22T09:00:00+00:002021-01-22T09:00:00+00:00https://redgreen.no/2021/01/22/norwegian-characters-us-keyboard-linux<p>About a year ago, I switched to Linux from MacOS. After some tinkering, I got everything set up nicely for coding, and writing. Most of my writing is in Norwegian, and I had a <code class="language-plaintext highlighter-rouge">.Xmodmap</code> hack to get Norwegian characters like I was used to from MacOS.</p>
<p>This year, I decided to a fresh install after a year of finding out how I liked my setup, and decided to change the keybindings a bit. Previously, I used <code class="language-plaintext highlighter-rouge">Alt_L</code> as a modifier to access the Norwegian characters, but I like using that modifier for shifting through tabs in my editor and Firefox, so I’ve decided to use the right alt-key, <code class="language-plaintext highlighter-rouge">ISO_Level3_Shift</code> or <code class="language-plaintext highlighter-rouge">AltGr</code> for accessing the Norwegian characters.</p>
<p>In MacOS the default bindings are:</p>
<table>
<thead>
<tr>
<th>Keys</th>
<th>Result</th>
</tr>
</thead>
<tbody>
<tr>
<td>Option ‘</td>
<td>æ</td>
</tr>
<tr>
<td>Option o</td>
<td>ø</td>
</tr>
<tr>
<td>Option a</td>
<td>å</td>
</tr>
<tr>
<td>Shift Option ‘</td>
<td>Æ</td>
</tr>
<tr>
<td>Shift Option o</td>
<td>Ø</td>
</tr>
<tr>
<td>Shift Option a</td>
<td>Å</td>
</tr>
</tbody>
</table>
<p>So I created the following Xmodmap settings and saved to <code class="language-plaintext highlighter-rouge">~/.Xmodmap</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>keysym apostrophe = apostrophe quotedbl apostrophe quotedbl ae AE
keysym o = o O o O oslash Oslash
keysym a = a A a A aring Aring
</code></pre></div></div>
<p>Each keysym column in the table corresponds to a particular combination of modifier keys:</p>
<ol>
<li>Key</li>
<li>Shift+Key</li>
<li>Mode_switch+Key</li>
<li>Mode_switch+Shift+Key</li>
<li>ISO_Level3_Shift+Key</li>
<li>ISO_Level3_Shift+Shift+Key</li>
</ol>
<p>For more details of <code class="language-plaintext highlighter-rouge">Xmodmap</code>, check out the <a href="https://wiki.archlinux.org/index.php/xmodmap">Arch wiki on xmodmap</a>.</p>Lars O. OverskeidUsing a aluminum Apple keyboard in Linux, and getting accessible Norwegian characters like MacOS.xterm-kitty: unknown terminal type2020-05-10T06:00:00+00:002020-05-10T06:00:00+00:00https://redgreen.no/2020/05/10/kitty-unknown-terminal-type<p>Nowadays, I’m using <a href="https://sw.kovidgoyal.net/kitty/">kitty</a> as my terminal emulator. It’s a pretty lightweight emulator, which utilizes the GPU for rendering.</p>
<p>Upon opening my first ssh session to a remote host, I find that some keys (backspace), and keyboard shortcuts are not working, and I get the following error from my remote debian host:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'xterm-kitty': unknown terminal type.
</code></pre></div></div>
<p>This is because my remote host does not know how to handle kitty by default. Luckily, there’s a way to communicate what capabilities your terminal emulator have, using <a href="https://www.tldp.org/HOWTO/Text-Terminal-HOWTO-16.html">terminfo</a></p>
<p>In order to copy over a <code class="language-plaintext highlighter-rouge">terminfo</code> file to your remote host’s <code class="language-plaintext highlighter-rouge">~/.terminfo</code> folder, run the following:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kitty +kitten ssh your.remote.server
</code></pre></div></div>
<p>This will open a new session, and you should now have a file called <code class="language-plaintext highlighter-rouge">~/.terminfo/x/xterm-kitty</code>, which will be loaded on every session from <code class="language-plaintext highlighter-rouge">kitty</code> in the future. Also, all normal keyboard shortcuts like <code class="language-plaintext highlighter-rouge">ctrl+a</code> should be working as intended.</p>Lars O. OverskeidUsing kitty terminal emulator on remote sessionsTemperature module for Polybar2020-05-01T14:00:00+00:002020-05-01T14:00:00+00:00https://redgreen.no/2020/05/01/polybar-temperature-module<p>My system:</p>
<ul>
<li>OS: Manjaro Linux x86_64</li>
<li>Kernel: 5.6.7-1-MANJARO</li>
<li>CPU: AMD Ryzen 9 3950X (32) @ 3.500GHz</li>
<li>GPU1: AMD ATI Radeon RX 5600 OEM/5600 XT / 5700/5700 XT</li>
<li>GPU2: AMD ATI Radeon RX 5600 OEM/5600 XT / 5700/5700 XT</li>
</ul>
<p>First, we need to get a source for our temperatures. Simply invoke <code class="language-plaintext highlighter-rouge">sensors</code> after installing <a href="https://github.com/lm-sensors/lm-sensors">lm-sensors</a>.</p>
<p>Your output will look something like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>amdgpu-pci-0c00
Adapter: PCI adapter
vddgfx: 800.00 mV
fan1: 1154 RPM (min = 0 RPM, max = 4950 RPM)
edge: +48.0°C (crit = +118.0°C, hyst = -273.1°C)
(emerg = +99.0°C)
junction: +49.0°C (crit = +99.0°C, hyst = -273.1°C)
(emerg = +99.0°C)
mem: +54.0°C (crit = +99.0°C, hyst = -273.1°C)
(emerg = +99.0°C)
power1: 32.00 W (cap = 150.00 W)
Adapter: PCI adapter
Vcore: 1.48 V
Vsoc: 1.08 V
Tdie: +36.8°C
Tctl: +36.8°C
Tccd1: +35.5°C
Tccd2: +35.5°C
Icore: 20.00 A
Isoc: 10.75 A
nvme-pci-0100
Adapter: PCI adapter
Composite: +47.9°C (low = -0.1°C, high = +84.8°C)
(crit = +89.8°C)
amdgpu-pci-0f00
Adapter: PCI adapter
vddgfx: 775.00 mV
fan1: 1172 RPM (min = 0 RPM, max = 4950 RPM)
edge: +34.0°C (crit = +118.0°C, hyst = -273.1°C)
(emerg = +99.0°C)
junction: +34.0°C (crit = +99.0°C, hyst = -273.1°C)
(emerg = +99.0°C)
mem: +36.0°C (crit = +99.0°C, hyst = -273.1°C)
(emerg = +99.0°C)
power1: 10.00 W (cap = 150.00 W)
nvme-pci-0400
Adapter: PCI adapter
Composite: +42.9°C (low = -273.1°C, high = +84.8°C)
(crit = +84.8°C)
Sensor 1: +42.9°C (low = -273.1°C, high = +65261.8°C)
Sensor 2: +42.9°C (low = -273.1°C, high = +65261.8°C)
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">k10temp-pci-00c3</code> section is provided by the <code class="language-plaintext highlighter-rouge">k10temp</code> kernel module, which handles AMD CPUs. In order to find a file for our temperature model to get information from, execute the following:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>for i in /sys/class/hwmon/hwmon*/temp*_input; do echo "$(<$(dirname $i)/name): $(cat ${i%_*}_label 2>/dev/null || echo $(basename ${i%_*})) $(readlink -f $i)"; done | grep k10temp
</code></pre></div></div>
<p>which should result in something like:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>k10temp: Tdie /sys/devices/pci0000:00/0000:00:18.3/hwmon/hwmon2/temp1_input
k10temp: Tctl /sys/devices/pci0000:00/0000:00:18.3/hwmon/hwmon2/temp2_input
k10temp: Tccd1 /sys/devices/pci0000:00/0000:00:18.3/hwmon/hwmon2/temp3_input
k10temp: Tccd2 /sys/devices/pci0000:00/0000:00:18.3/hwmon/hwmon2/temp4_input
</code></pre></div></div>
<p>Normally, you’d be wanting the <code class="language-plaintext highlighter-rouge">Tdie</code> temperature, but feel free to include more if you’re interested.</p>
<p>Now include the following module in your <code class="language-plaintext highlighter-rouge">~/.config/polybar/config</code> file, and reload.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[module/temperature]
type = internal/temperature
interval = 2
hwmon-path = /sys/devices/pci0000:00/0000:00:18.3/hwmon/hwmon2/temp1_input
base-temperature = 20
warn-temperature = 90
</code></pre></div></div>Lars O. OverskeidSetting up a temperature module in Polybar for AMD Ryzen 3950XSwitching to i3 from XFCE2020-05-01T08:00:00+00:002020-05-01T08:00:00+00:00https://redgreen.no/2020/05/01/switching-to-i3<p>This article is mainly to keep track of what I’ve learned and done, configuring my own i3. If you’re new to i3, check out <a href="https://i3wm.org/docs/userguide.html">i3wm.org’s userguide</a></p>
<h2 id="configuration-and-keyboard-shortcuts">Configuration and keyboard shortcuts</h2>
<p>The config file for i3 resides in <code class="language-plaintext highlighter-rouge">~/.config/i3/config</code> on Manjaro/Arch. Use <code class="language-plaintext highlighter-rouge">mod+shift+r</code> to restart i3 after configuration changes. During first setup you choose a modifier key, normally this would be <code class="language-plaintext highlighter-rouge">alt</code> or <code class="language-plaintext highlighter-rouge">command</code>. If you configure yourself out of a usable system, deleting the config file will reset everything and run the configuration tool again.</p>
<p>The default keyboard shortcuts for getting started are:</p>
<table>
<thead>
<tr>
<th style="text-align: left">Shortcut</th>
<th style="text-align: left">Function</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">mod+enter</code></td>
<td style="text-align: left">Open new terminal</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">mod+j</code></td>
<td style="text-align: left">Focus left</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">mod+k</code></td>
<td style="text-align: left">Focus down</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">mod+l</code></td>
<td style="text-align: left">Focus up</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">mod+;</code></td>
<td style="text-align: left">Focus right</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">mod+a</code></td>
<td style="text-align: left">Focus parent</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">mod+space</code></td>
<td style="text-align: left">Toggle focus mode</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">mod+shift+j</code></td>
<td style="text-align: left">Move window left</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">mod+shift+k</code></td>
<td style="text-align: left">Move window down</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">mod+shift+l</code></td>
<td style="text-align: left">Move window up</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">mod+shift+;</code></td>
<td style="text-align: left">Move window right</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">mod+0..9</code></td>
<td style="text-align: left">Switch workspace</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">mod+shift+0..9</code></td>
<td style="text-align: left">Move window to workspace</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">mod+d</code></td>
<td style="text-align: left">Application launcher (rofi)</td>
</tr>
</tbody>
</table>
<p>For more keyboard shortcuts, check out <a href="https://i3wm.org/docs/refcard.html">i3 reference card</a> from i3wm.org</p>
<h2 id="i3-gaps-for-the-looks">i3-gaps for the looks</h2>
<p><a href="https://github.com/Airblader/i3">i3-gaps</a> is a fork of the original i3 window manager, which provides some whitespace between the windows. I find it aestethically pleasing. You’re free to configure the different features as you please, this is my current configuration, copied from user <a href="https://classicforum.manjaro.org/index.php?topic=27260.0">twodogsdave</a> on the Manjaro forums:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Disable window titlebars entirely
for_window [class="^.*"] border pixel 2
# Set inner/outer gaps
gaps inner 10
gaps outer 15
# Additionally, you can issue commands with the following syntax. This is useful to bind keys to changing the gap size.
# gaps inner|outer current|all set|plus|minus <px>
# gaps inner all set 10
# gaps outer all plus 5
# Smart gaps (gaps used if only more than one container on the workspace)
# smart_gaps on
# Smart borders (draw borders around container only if it is not the only container on this workspace)
# on|no_gaps (on=always activate and no_gaps=only activate if the gap size to the edge of the screen is 0)
smart_borders on
# Press $mod+Shift+g to enter the gap mode. Choose o or i for modifying outer/inner gaps. Press one of + / - (in-/decrement for current workspace) or 0 (remove gaps for current workspace). If you also press Shift with these keys, the change will be global for all workspaces.
set $mode_gaps Gaps: (o) outer, (i) inner
set $mode_gaps_outer Outer Gaps: +|-|0 (local), Shift + +|-|0 (global)
set $mode_gaps_inner Inner Gaps: +|-|0 (local), Shift + +|-|0 (global)
bindsym $mod+Shift+g mode "$mode_gaps"
mode "$mode_gaps" {
bindsym o mode "$mode_gaps_outer"
bindsym i mode "$mode_gaps_inner"
bindsym Return mode "default"
bindsym Escape mode "default"
}
mode "$mode_gaps_inner" {
bindsym plus gaps inner current plus 5
bindsym minus gaps inner current minus 5
bindsym 0 gaps inner current set 0
bindsym Shift+plus gaps inner all plus 5
bindsym Shift+minus gaps inner all minus 5
bindsym Shift+0 gaps inner all set 0
bindsym Return mode "default"
bindsym Escape mode "default"
}
mode "$mode_gaps_outer" {
bindsym plus gaps outer current plus 5
bindsym minus gaps outer current minus 5
bindsym 0 gaps outer current set 0
bindsym Shift+plus gaps outer all plus 5
bindsym Shift+minus gaps outer all minus 5
bindsym Shift+0 gaps outer all set 0
bindsym Return mode "default"
bindsym Escape mode "default"
}
</code></pre></div></div>
<h2 id="screen-tearing-in-firefox">Screen tearing in Firefox</h2>
<p>i3 does not come with a compositor, like XFCE has. This results in some tearing when scrolling in Firefox, and other applications. In order to fix this, you need <a href="https://github.com/yshui/picom">Picom</a>, which is a maintained fork of the old compton project. To enable it, add the following to your i3 config file.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>exec_always --no-startup-id picom -b
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">exec_always</code> ensures it also runs when reloading i3.</p>
<h2 id="better-application-launcher">Better application launcher</h2>
<p>Issuing <code class="language-plaintext highlighter-rouge">super+d</code> brings up <code class="language-plaintext highlighter-rouge">dmenu</code>, which gives you a fuzzy-searchable list of all executables on your system.</p>
<p>If you’re coming from MacOS, you’ll probably find <code class="language-plaintext highlighter-rouge">dmenu</code> a bit dull, compared to the awesomeness of Alfred and the likes. Check out <a href="https://github.com/davatorium/rofi">rofi</a> for a highly configurable drop-in replacement.</p>
<p>Here’s my <code class="language-plaintext highlighter-rouge">.config/rofi/config.rasi</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>configuration {
modi: "window,drun,ssh,combi";
theme: "solarized";
font: "hack 10";
combi-modi: "window,drun";
}
</code></pre></div></div>
<p>To launch <code class="language-plaintext highlighter-rouge">rofi</code> in combi-mode (all modes combined) instead of <code class="language-plaintext highlighter-rouge">dmenu</code>, trade your <code class="language-plaintext highlighter-rouge">dmenu</code> keybinding for:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bindsym $mod+d exec rofi -show combi
</code></pre></div></div>
<h2 id="kitty">Kitty</h2>
<p>The default terminal application shipping with i3 is not the pinnacle of terminals. Currently I’m using kitty, and I’m quite happy with it. After installing kitty, trade the following line:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bindsym $mod+Return exec i3-sensible-terminal
</code></pre></div></div>
<p>with</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bindsym $mod+Return exec kitty
</code></pre></div></div>
<h2 id="polybar">Polybar</h2>
<p>I like to have a simple, but customizable statusbar. Currently I’m using <a href="https://github.com/polybar/polybar">Polybar</a> instead of the default i3 statusbar.</p>
<h2 id="locking-your-screen">Locking your screen</h2>
<p>I use <code class="language-plaintext highlighter-rouge">i3lock</code> for this, and bind it to <code class="language-plaintext highlighter-rouge">super+shift+x</code> like this</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bindsym $mod+shift+x exec i3lock -c 000000
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">-c 000000</code> gives it a completely black background.</p>Lars O. OverskeidMy log of configuration changes, when switching from XFCE to i3Upgrade PostgreSQL from 9.4/9.5 to 9.62017-01-03T14:40:25+00:002017-01-03T14:40:25+00:00https://redgreen.no/2017/01/03/upgrade-homebrew-postgresql<p>After upgrading everything in Homebrew, I discovered that my PostgreSQL no longer
loaded, due to the database files being an older version than the PostgreSQL software.
These are the steps I followed to upgrade all databases, and get up and running again.</p>
<p>1: Make sure PostgreSQL is not running before starting the upgrade process.</p>
<p>2: Make a new PostgreSQL 9.6 database where your old data will be migrated to.</p>
<figure class="highlight"><pre><code class="language-zsh" data-lang="zsh">% initdb /usr/local/var/postgres_new <span class="nt">-E</span> utf8</code></pre></figure>
<p>3: Upgrade databases to 9.6 by using <code class="language-plaintext highlighter-rouge">pg_upgrade</code> tool. To find the version number of the
old binaries, you simply <code class="language-plaintext highlighter-rouge">ls /usr/local/Cellar/postgresql</code>.</p>
<figure class="highlight"><pre><code class="language-zsh" data-lang="zsh">% pg_upgrade <span class="se">\</span>
<span class="nt">-b</span> /usr/local/Cellar/postgresql/9.5.4_1/bin/ <span class="se">\</span>
<span class="nt">-B</span> /usr/local/Cellar/postgresql/9.6.1/bin/ <span class="se">\</span>
<span class="nt">-d</span> /usr/local/var/postgres <span class="se">\</span>
<span class="nt">-D</span> /usr/local/var/postgres_new</code></pre></figure>
<p>Change the version numbers needed for your setup, usage of the <a href="https://www.postgresql.org/docs/9.6/static/pgupgrade.html">pg_upgrade</a> tool
is as simple as:</p>
<figure class="highlight"><pre><code class="language-zsh" data-lang="zsh">pg_upgrade <span class="nt">-b</span> oldbindir <span class="nt">-B</span> newbindir <span class="nt">-d</span> olddatadir <span class="nt">-D</span> newdatadir</code></pre></figure>
<p>4: Move your new database to the PostgreSQL default location:</p>
<figure class="highlight"><pre><code class="language-zsh" data-lang="zsh">% <span class="nb">mv</span> /usr/local/var/postgres /usr/local/var/postgres9.5
% <span class="nb">mv</span> /usr/local/var/postgres_new /usr/local/var/postgres</code></pre></figure>
<p>5: That’s it :)</p>Lars O. OverskeidHow to upgrade your homebrew installed PostgreSQL to 9.6Split your Rails routes into smaller files2017-01-03T09:16:25+00:002017-01-03T09:16:25+00:00https://redgreen.no/2017/01/03/split-your-rails-routes<p>In most monolithic Rails projects, the routes.rb file gets huge. Here’s a little trick to split it into
smaller files for readability.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span>
<span class="c1"># A lot of routes</span>
<span class="k">end</span></code></pre></figure>
<p>This is what your <code class="language-plaintext highlighter-rouge">config/routes.rb</code> file looks like by default. Adding simple routes and namespaces is easy enough,
but when your project becomes big, the file becomes cluttered. There’s no significant performance impact of having a big
<code class="language-plaintext highlighter-rouge">routes.rb</code> file, and therefore no performance gain in splitting it up, but I like having a visually clean
file while developing.</p>
<p>In my application, I have a couple of namespaces, including a “admin” namespace, so lets extract that into
it’s own routes file <code class="language-plaintext highlighter-rouge">/config/routes/admin_routes.rb</code>. In order for Rails to load this file, we need to
add <code class="language-plaintext highlighter-rouge">/config/routes</code> to the auto load path, or require it manually. Add the following to your application.rb</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">module</span> <span class="nn">YourProject</span>
<span class="k">class</span> <span class="nc">Application</span> <span class="o"><</span> <span class="no">Rails</span><span class="o">::</span><span class="no">Application</span>
<span class="n">config</span><span class="p">.</span><span class="nf">autoload_paths</span> <span class="o">+=</span> <span class="sx">%W(</span><span class="si">#{</span><span class="n">config</span><span class="p">.</span><span class="nf">root</span><span class="si">}</span><span class="sx">/config/routes)</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>Now we can create the file containing the admin namespace:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">module</span> <span class="nn">AdminRoutes</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">extended</span><span class="p">(</span><span class="n">router</span><span class="p">)</span>
<span class="n">router</span><span class="p">.</span><span class="nf">instance_exec</span> <span class="k">do</span>
<span class="n">namespace</span> <span class="ss">:admin</span> <span class="k">do</span>
<span class="n">resources</span> <span class="ss">:articles</span>
<span class="n">root</span> <span class="ss">to: </span><span class="s2">"dashboard#index"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>Here we are hooking onto the the router when it’s extended by using the <a href="https://ruby-doc.org/core-2.4.0/Module.html#method-i-extended">Module#extended</a>
method, and we receive the router as an argument. By using <a href="https://ruby-doc.org/core-2.4.0/BasicObject.html#method-i-instance_exec">Object#instance_exec</a>
after being extended by routes.rb, we are in the context of <code class="language-plaintext highlighter-rouge">Rails.application.routes.draw</code> and are able to configure our routes as normal.</p>
<p>Going back to the first example, extending the basic routes.rb file is as easy as</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span>
<span class="kp">extend</span> <span class="no">AdminRoutes</span>
<span class="c1"># A lot of routes</span>
<span class="k">end</span></code></pre></figure>
<p>Hope you find this somewhat useful. I’ve only tested this on Rails 5.0.1, but it should work for Rails 4.x as well.</p>Lars O. OverskeidIn most monolithic Rails projects, the routes.rb gets huge. Here's a little trick to split it for readabilityFive simple guidelines for unit testing2016-12-30T07:16:25+00:002016-12-30T07:16:25+00:00https://redgreen.no/2016/12/30/simple-guidelines-for-unit-testing<p>In my opinion, writing unit tests is not about discovering bugs as much as it’s a tool for designing
your code in a robust way. Unit tests are in principle small tests for nimble isolated parts of your code, such
as a class or even just one method.</p>
<h2 id="writing-unit-tests-is-a-design-process">Writing unit tests is a design process</h2>
<p>The unit test defines a recipe of how the isolated code is supposed to respond
to the given input, and thereby it is a way of designing your application.</p>
<p>Defining the expected outcome of a class or method with a unit test is often a lot easier than actually implementing
the code that should produce the outcome, therefore unit tests are cheaper to edit or even throw away than implemented
code.</p>
<p>By following a Test-driven Development principle, you always write a failing test before you implement anything,
and if those tests are good, you will maintain a high level of test coverage on all the different components that
make up your awesome application. Bugs happen either because you got some input you didn’t expect, or because the
different units (with green tests) does not work together as intended. These kinds of bugs are better discovered
with automated integration tests, or manual testing.</p>
<h2 id="good-vs-bad-unit-tests">Good vs. Bad unit tests</h2>
<p>A little over a decade ago, <a href="https://twitter.com/mfeathers">Michael Feathers</a> made a good list of some features a
unit test should <em>not</em> include. He argued that a test is not a unit test if:</p>
<ol>
<li>It talks to the database</li>
<li>It communicates across the network</li>
<li>It touches the file system</li>
<li>It can’t run correctly at the same time as any of your other unit tests</li>
<li>You have to do special things to your environment (such as editing
config files) to run it.</li>
</ol>
<p>By following these, you get good unit tests, and you also benefit from having fast tests. Having a fast test suite
is essential when following TDD.</p>
<p>I found some examples of RSpec model specs for a Rails application, on which I think that the principles for unit testing should apply.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">describe</span> <span class="no">User</span><span class="p">,</span> <span class="s1">'.active'</span> <span class="k">do</span>
<span class="n">it</span> <span class="s1">'returns only active users'</span> <span class="k">do</span>
<span class="n">active_user</span> <span class="o">=</span> <span class="n">create</span><span class="p">(</span><span class="ss">:user</span><span class="p">,</span> <span class="ss">active: </span><span class="kp">true</span><span class="p">)</span>
<span class="n">non_active_user</span> <span class="o">=</span> <span class="n">create</span><span class="p">(</span><span class="ss">:user</span><span class="p">,</span> <span class="ss">active: </span><span class="kp">false</span><span class="p">)</span>
<span class="n">result</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">active</span>
<span class="n">expect</span><span class="p">(</span><span class="n">result</span><span class="p">).</span><span class="nf">to</span> <span class="n">eq</span> <span class="p">[</span><span class="n">active_user</span><span class="p">]</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>This first example tests a <code class="language-plaintext highlighter-rouge">scope</code> on the <code class="language-plaintext highlighter-rouge">User</code> class for finding only active users. It requires database access, and would fail in a
weird edgecase where some other unit test create a user at the same time. While some might argue that testing scopes this way is a
necessary evil, this is better tested with a integration test. We should assume that the <code class="language-plaintext highlighter-rouge">scope</code> functionality provided by ActiveRecord
is properly tested already.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">describe</span> <span class="no">User</span><span class="p">,</span> <span class="s1">'#name'</span> <span class="k">do</span>
<span class="n">it</span> <span class="s1">'returns the concatenated first and last name'</span> <span class="k">do</span>
<span class="n">user</span> <span class="o">=</span> <span class="n">build</span><span class="p">(</span><span class="ss">:user</span><span class="p">,</span> <span class="ss">first_name: </span><span class="s1">'Josh'</span><span class="p">,</span> <span class="ss">last_name: </span><span class="s1">'Steiner'</span><span class="p">)</span>
<span class="n">expect</span><span class="p">(</span><span class="n">user</span><span class="p">.</span><span class="nf">name</span><span class="p">).</span><span class="nf">to</span> <span class="n">eq</span> <span class="s1">'Josh Steiner'</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>This is a good example of a good unit test. Although the example is simple, it provides a clear description of how the <code class="language-plaintext highlighter-rouge">#name</code> method
on an instantiated <code class="language-plaintext highlighter-rouge">User</code> should behave, and does not violate any of the five rules. If you want to be explicit, you should also write
some test-code for how the method should behave if <code class="language-plaintext highlighter-rouge">first_name</code> and/or <code class="language-plaintext highlighter-rouge">last_name</code> is <code class="language-plaintext highlighter-rouge">nil</code>.</p>Lars O. OverskeidThese guidelines helps you minimize the effort taken to develop and maintain your unit tests