whysthatso

This is the site's publishing timeline, everything is here for you to browse. I've separated out a couple of things that i would like to browse quickly myself, or where i think people might want to follow these separately.

today-i-learned

Differentiate Between Submit Buttons Rails

Posted on July 5, 2025

insted of the form.submit, use button_tag, which will POST and also <%= button_tag “Pay now”, {class: “primary-button”, name: “submit”, value: “foo”} %>

which will result in a params hash { “submit” => “foo”} making params more easily handleable and keeps the button label uncommitted to the value.

today-i-learned

Problems With Search_path In Postgres 16

Posted on July 3, 2025

I discovered that for some reason default search paths in some of my postgres 16 schemas were way off.

SELECT r.rolname, d.datname, rs.setconfig
FROM   pg_db_role_setting rs
LEFT   JOIN pg_roles      r ON r.oid = rs.setrole
LEFT   JOIN pg_database   d ON d.oid = rs.setdatabase
WHERE  r.rolname = 'immich' OR d.datname = 'immich';

this shows the defaults in the current schema, here it was immich db and user, but for some reason it had mattermost as a result.

alter database immich set search_path = "$user", public ;

fixed this

today-i-learned

Postgresql Performing Cache Lookup Failed For Relation

Posted on July 2, 2025

I had a weird state of a table where dropping it would return the error ‘performing cache lookup failed for relation '

what fixed it was looking up the oid and then just removing all its entries.

select from pg_depend where objid = MY_OID;
delete from pg_class where oid = MY_OID;
delete from pg_depend where objid = MY_OID;
delete from pg_constraint where conrelid = MY_OID;

might be very invasive, the state of that dataset was not too important.

today-i-learned

Running JavaScript after a Turbo Stream renders

Posted on May 11, 2025  //  hotwire turbo stimulus rails About

Turbo comes with turbo:before-stream-render but unfortunately doesn’t ship with the equivalent turbo:after-stream-render. Here’s how to run JavaScript after the stream renders.

Why we need this

If you are building your application with Hotwire, your Turbo streams will likely add, remove, and replace some HTML nodes. This mostly works except when you want to add HTML that comes with some JavaScript. Like a file picker, Trix editor, and the like.

Turbo itself won’t do anything about this. It’s a rather simple tool with simple purpose. JavaScript initialization should come with the HTML Turbo is about to add. Hotwire solves this with Stimulus.

The Hotwire way

The Hotwire answer to the problem is Stimulus controllers. Stimulus is build on top of MutationObserver which is a browser API providing the ability to watch for changes being made to the DOM.

When a Stimulus controller appears on the page, its connect method is called automatically. If our HTML doesn’t come with a Stimulus controller, we should create a new controller and put our initialization code inside its connect method:

// app/javascript/controllers/my_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  connect() {
    // Code to initialize something
  }
}

Then we simply add the controller to an element inside our Turbo Stream:

<turbo-stream>
  <div data-controller="my-controller">
  	Something that needs to be initialize with JavaScript.
  </div>
</turbo-stream>

When Stimulus is not an option

Sometimes Stimulus might not be what we want and we would love to have a Turbo Stream event to hook into anyways.

Luckily, Steve shared his little implementation of turbo:after-stream-render in this thread:

// application.js
const afterRenderEvent = new Event("turbo:after-stream-render");
addEventListener("turbo:before-stream-render", (event) => {
  const originalRender = event.detail.render

  event.detail.render = function (streamElement) {
    originalRender(streamElement)
    document.dispatchEvent(afterRenderEvent);
  }
})

As you can see, the idea is quite simple. We create a new custom Event and add an event listener for turbo:before-stream-render which already exist. We then run our event after we are done with original rendering.

To use it we create an event listener and paste the JavaScript that needs to run after rendering:

document.addEventListener("turbo:after-stream-render", () => {
  console.log("I will run after stream render")
});
today-i-learned

Check For Custom Errors In Rails Error Objects

Posted on May 10, 2025  //  rails

In a view one can usually do something like this to check if there is an error after a validation and then conditionally change some design, for example:

<% if course_registration_form.errors[:email].any? %>
	<%= content_tag(:p, course_registration_form.errors[:email].first, class: "mt-2 text-sm text-red-600") %>
<% end %>

however, if you have a custom validation that groups errors on an arbitrary key that is not backed by an object’s attribute, you have to query differently:

<% if course_registration_form.errors.where(:person_info).any? %>
	<%= content_tag(:p, course_registration_form.where(:person_info).map(&:message), class: "mt-2 text-sm text-red-600") %>
<% end %>
today-i-learned

Setup Rails Staging Environment

Posted on May 9, 2025
  • copy prod env file
  • change email settings, set interceptor
  • create database.yml staging from production
  • check storage.yml
  • check secrets.yml
  • check if gems are special to staging

env settings RAILS_ENV=staging RACK_ENV=staging

today-i-learned

Turbo Prefetch

Posted on May 7, 2025  //  rails turbo

• Turbo v8 prefetches links on hover • works automatically with Rails link_to • disable globally with <meta name="turbo-prefetch" content="false"> • disable per-link or per-section with data: { turbo_prefetch: false } • you can re-enable selectively inside disabled areas • use turbo:before-prefetch to cancel based on network conditions

today-i-learned

Print Out Postgresql Table Sizes

Posted on April 29, 2025  //  postgresql
SELECT
    schemaname || '.' || tablename AS table_full_name,
    pg_size_pretty(pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(tablename))) AS total_size
FROM pg_tables
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(tablename)) DESC;

Count mysql table rows (approximate)

SELECT 
    TABLE_SCHEMA,
    TABLE_NAME,
    TABLE_ROWS
FROM 
    information_schema.TABLES
WHERE 
    TABLE_TYPE = 'BASE TABLE'
    AND TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
ORDER BY 
    TABLE_ROWS DESC;

show mysql table size

SELECT 
    TABLE_SCHEMA,
    TABLE_NAME,
    ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS size_mb
FROM 
    information_schema.TABLES
WHERE 
    TABLE_TYPE = 'BASE TABLE'
    AND TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys')
ORDER BY 
    size_mb DESC;

show all unique values in a column

SELECT DISTINCT your_column
FROM your_table;
today-i-learned

How To Set Default Color In Tailwindcss 4 For A Rails App

Posted on April 29, 2025  //  tailwindcss rails
Application.css looks like this:

```css
@import "tailwindcss";

@theme {
	--brand-color: oklch(0.35 0.2153 266.44);
}

@layer base {
	html {
		@apply text-(--brand-color);
	}
}

so the color gets defined in the @theme directive, and it then gets applied by the base layer.

There are other ways, but this seems to me the cleanest, and when universally needed, the right choice.

Maybe when dynamic application of themes is important, it might be easier to manipulate the body tag like so:

<body class="text-{color}">

wwwwwwwwwwwwwwwwwyou might still have to define the color somewhere if it is a custom choice.

today-i-learned

Linux Optimizations For Subnet Routers And Exit Nodes

Posted on April 22, 2025

https://tailscale.com/kb/1320/performance-best-practices#linux-optimizations-for-subnet-routers-and-exit-nodes

Tailscale version 1.54 or later used with a Linux 6.2 or later kernel enables UDP throughput improvements using transport layer offloads. If a Linux device is acting as an exit node or subnet router, ensure the following network device configuration is in place for the best results:

NETDEV=$(ip -o route get 8.8.8.8 | cut -f 5 -d " ")
sudo ethtool -K $NETDEV rx-udp-gro-forwarding on rx-gro-list off

By default, changes made using the ethtool don’t persistent after a reboot. On Linux distributions using networkd-dispatcher (which you can verify with systemctl is-enabled networkd-dispatcher), you can run the following commands to create a script that configures these settings on each boot.

printf '#!/bin/sh\n\nethtool -K %s rx-udp-gro-forwarding on rx-gro-list off \n' "$(ip -o route get 8.8.8.8 | cut -f 5 -d " ")" | sudo tee /etc/networkd-dispatcher/routable.d/50-tailscale
sudo chmod 755 /etc/networkd-dispatcher/routable.d/50-tailscale

Run the following commands to test the script to ensure it runs successfully on your devices:

sudo /etc/networkd-dispatcher/routable.d/50-tailscale
test $? -eq 0 || echo 'An error occurred.'
today-i-learned

Sql Queries Helpful

Posted on April 21, 2025

find distinct set of values in a column item_type

select  distinct on (item_type) item_type from papertrail_versions;
today-i-learned

Dump And Restore Databases From Dockerized Mysql

Posted on April 14, 2025

Quick way to dump and restore db’s in a dockerized mysql db

Dump

docker exec some-mysql-container sh -c 'exec mysqldump -D db-name -uroot -p"$MYSQL_ROOT_PASSWORD"' > /some/path/on/your/host/all-databases.sql

Restore

docker exec -i some-mysql-container sh -c 'exec mysql -D db-name -uroot -p"$MYSQL_ROOT_PASSWORD"' < /some/path/on/your/host/all-databases.sql
today-i-learned

Wordpress Upload Limits On Official Docker Wordpress Apache Image

Posted on April 13, 2025

To configure the php file upload size limits, put a .htaccess in the wordpress root directory, add values there.

php_value post_max_size 51M <-- limits to 51MB 
php_value upload_max_filesize 51M <-- limits to 51MB

This is the place to configure site-specific php values.

today-i-learned

Skip Wordpress Image Generation

Posted on March 10, 2025

there are multiple ways to repress resized image set generation, here are two:

add_action( 'init', 'remove_large_image_sizes' );  
function remove_large_image_sizes() {  
	remove_image_size( '1536x1536' );
	remove_image_size( '2048x2048' );
	remove_image_size( 'thumbnail' ); 
	remove_image_size( 'medium' );   
	remove_image_size( 'medium_large' ); 
	remove_image_size( 'large' );
} 
add_filter( 'intermediate_image_sizes_advanced', 'disable_default_image_sizes' );  
function disable_default_image_sizes( $sizes ) {  
	unset( $sizes['thumbnail'] );  
	unset( $sizes['medium'] );  
    unset( $sizes['medium_large'] );  
    unset( $sizes['large'] );  
    unset( $sizes['2048x2048'] );  
    unset( $sizes['1536x1536'] );  
    return $sizes;  
}  

This will prevent the generation of a scaled main image when the width exceeds a point beyond 2k px

add_filter( 'big_image_size_threshold', '__return_false' );  
today-i-learned

Conditional Redirect In Wordpress

Posted on March 9, 2025
function redirect_plain_cms_to_frontend() {
if ( isset( $_SERVER['HTTP_HOST'] ) && 
	$_SERVER['HTTP_HOST'] === 'cms.example.ee' || 'stage.cms.example.ee') {
	$request_uri = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '/';  
    if ( ( $request_uri === '/' || $request_uri === '/index.php' ) ) {  
       wp_redirect( 'https://example.ee', 301 );  
       exit();  
    }  
  }  
}  
add_action( 'template_redirect', 'redirect_plain_cms_to_frontend' );
today-i-learned

Use Backblaze B2 As Active Storage Backend

Posted on February 27, 2025

Active storage and backblaze b2

  • for now stick with aws-sdk-s3 gem version 1.177.0 because Data Integrity Protection Headers from aws are not yet supported on b2 (https://www.backblaze.com/docs/cloud-storage-use-the-aws-sdk-for-ruby-with-backblaze-b2)
  • this is how your storage.yml backend for b2 should approximately look like:
    backblaze:
      service: S3
      access_key_id: <%= Rails.application.credentials.dig(:b2, :key_id) %>
      secret_access_key: <%= Rails.application.credentials.dig(:b2, :access_key) %>
      endpoint: https://s3.eu-central-003.backblazeb2.com
      bucket: bucket-name-<%= Rails.env %>
      region: eu-central
      request_checksum_calculation: when_required
      response_checksum_validation: when_required
    
  • these are the cors rules for direct upload to work:
[
	{
		"allowedHeaders": [
			"authorization",
			"content-type",
			"content-md5",
			"content-disposition",
			"x-bz-file-name",
			"x-bz-content-sha1"
		],
		"allowedOperations": [
			"b2_download_file_by_id",
			"b2_upload_part",
			"b2_upload_file",
			"s3_put",
			"b2_download_file_by_name",
			"s3_get",
			"s3_head"
		],
		"allowedOrigins": [
			"*" // or stricter
		],
		"corsRuleName": "downloadFromApp", // random value
		"exposeHeaders": null,
		"maxAgeSeconds": 3600
	}
]
  • form example for direct upload activated
<%= form.hidden_field :receipt, value: @bill.receipt.signed_id if @bill.receipt.attached? %>
<%= form.file_field :receipt, direct_upload: true %>

# and how to conditionally check if something is already attached
<% if @bill.receipt.attached? %>
	<%= link_to 'Download current receipt/invoice', @bill.receipt %>
<% end %>
  • this is the javascript necessary for direct upload
import * as ActiveStorage from "@rails/activestorage";

ActiveStorage.start();
  • this needed to be in the object controller for some reason i forgot
    include ActiveStorage::SetCurrent
    
  • sources
    • https://github.com/aws/aws-sdk-ruby/issues/3166
  • https://guides.rubyonrails.org/active_storage_overview.html#s3-service-amazon-s3-and-s3-compatible-apis
  • https://github.com/rails/rails/issues/50234
  • https://edgeguides.rubyonrails.org/active_storage_overview.html#direct-uploads
today-i-learned

Set Static Ip On Raspberry Pi Os

Posted on February 7, 2025

show connections

sudo nmcli -p connection show

configure static ip

sudo nmcli c mod "Wired connection 1" ipv4.addresses 10.0.0.220/24 ipv4.method manual

sudo nmcli con mod "Wired connection 1" ipv4.gateway 10.0.0.1

sudo nmcli con mod "Wired connection 1" ipv4.dns 10.0.0.1
today-i-learned

Check Gzip Status Of Nginx With Curl

Posted on February 7, 2025
curl http://example.com/ --silent --write-out "%{size_download}\n" --output /dev/null
31032
curl http://example.com/ --silent -H "Accept-Encoding: gzip,deflate" --write-out "%{size_download}\n" --output /dev/null
2553
today-i-learned

Boot Linux Into Rescue Mode

Posted on January 22, 2025

Besides forcing an error, it’s easier just to do this:

systemctl rescue
today-i-learned

Resize Btrfs Root Partition

Posted on January 21, 2025
  1. change size of lv in proxmox
  2. fdisk /dev/sdX
    1. expand
    2. partition
    3. size
    4. write
  3. btrfs filesystem resize max /
today-i-learned

Tmpfs On Arch

Posted on January 19, 2025

if you don’t want to have /tmp as a mount of the memory via tmpfs, mask the systemd mount:

sudo systemctl mask tmp.mount