Installing h3-pg extension for https://postgresapp.com

Repeat whenever postgres.app is updated.

(Setup the paths)[https://postgresapp.com/documentation/cli-tools.html]

Build from source and install:

gh repo clone zachasme/h3-pg
cd h3-pg
make
make install

Hat tip to https://github.com/PostgresApp/PostgresApp/issues/683#issuecomment-1274754377

I had a vague memory that some flies were like Matryoshka dolls. So here we go, more than you wanted to know about aphids: www.youtube.com/watch

I wonder why https://guides.rubyonrails.org/active_record_postgresql.html does not seem to be linked from the guides index?

Using a binary string returned from ActiveRecord

When a DB query returns a binary string, this needs unescaping before use. The case that brought this to light for me was a relatively complex PostGIS query which generates a MapBox vector tile lines geometry imported from OpenStreeMap data:

  WITH bounds AS (
    SELECT ST_TileEnvelope(#{@zoom}, #{@x}, #{@y}) AS envelope
  ),
  tile_geom AS (
    SELECT ST_AsMVTGeom(ST_Transform(l.geom, 3857), bounds.envelope)
    FROM planet_osm_line
  )
  SELECT ST_AsMVT(*) FROM tile_geom;

While all my carrier has dealt with integer, string or even JSON data in the DB, this binary response was new to me. Outputting the result from a Rails controller for consumption by a MapBox GL instance was frustrating, as the tile data was received, but not displayed.

The output from ST_AsMVT() is a protobuf, so not easy to confirm correctness, but pasting the response body into a tool like protobuf-decoder can help verify if the data is a valid protobuf.

The solution was to return the respone unescaped by unescape_bytea():

ActiveRecord::Base.connection.unescape_bytea(response.rows.first&.first)

Well, I’ve been coding in Rails for around 15 years, and I’m still learning…

I just finished reading: Slaughterhouse 5 by Kurt Vonnegut 📚 It occurs to me that we are all time travellers, just most of us are stuck at 1x in a forward direction, with no random access.

News & Podcasts

2024 has lots of potential, but I’ve stopped opening news apps or scrolling for my mood and state of mind.

The subset of things I can influence are much smaller than the set of inputs. So now I get a daily news summary email, and that’s it. I don’t follow links from those articles.

I’m still listening to political podcasts though, which give a deeper understanding of what’s going on and why. I can recommend pod save the UK and The Rest Is Politics for a UK perspective.

Reading this left me both upbeat and sad that there is no Manhattan Project effort that all political leanings can get behind.We Can Already Stop Climate Change If We Want To

However, the articles did lead me to make sunsets which looks to be an excellent start. For $9 you can sponsor the launch of 1g of sulphur dioxide into the upper atmosphere to reflect incoming sunlight to negate the equivalent heating effect of one ton of carbon dioxide.

For someone with an estimated personal carbon footprint of 7 tons, this is doable.

They also have a monthly subscription!

I love this take on Pac-Man. I expect copies to be all over the AppStore within days though 🤬 PAKU PAKU

Year in books for 2023

Here are the books I finished reading in 2023.

Red Rising (Red Rising Saga, #1) How to Stop Time Tomorrow, and Tomorrow, and Tomorrow Start Small, Stay Small Children of Time Ocean Sailing Heir to the Empire: Star Wars Legends The Lost Cause The Lost Cause

Having grown up on Sinclair spectrum and then MS-DOS and Windows, I was a late comer to the Apple ecosystem ( a MacBook Core 2 Duo being my first device ). I now know why the Apple logo has a bite (byte?) out of it.

twitter.com/JonErlich…

Cory Doctorow’s new book might just leap to the front of my “to read” list.

whatever.scalzi.com/2023/11/1…

This is a great summary of rules for generating APIs, including many points I have not considered before. github.com/stickfigu…

Migrating a Dokku application to a new server

Assuming a few things:

  1. the original server is still running, and that the new server cannot be bootstrapped from an image or backup of the old server (for example, they are with different hosts)
  2. Dokku has been installed on the new server and is accessible using the dokku user (See https://dokku.com)
  3. We have an SSH config entry for both old and new servers in ~/.ssh/config:
host new_server
  RequestTTY yes
  Hostname 55.190.140.231
  User dokku
  IdentityFile ~/.ssh/id_rsa

Host old_server
  RequestTTY yes
  Hostname 55.78.183.109
  User dokku
  IdentityFile ~/.ssh/id_rsa

Create new app

ssh new_server apps:create icepop_app

Fetch env vars for original

ssh old_server config:show icepop_app

Set important keys (not DOKKU_*)

ssh new_server config:set icepop_app RAILS_MASTER_KEY=VALUE EMAIL_API_KEY=VALUE MAP_API_KEY=VALUE

Add new git remote repo based on the current one

git remote -v

git remote add new_server dokku@new_server:icepop_app

Create new DB

ssh new_server postgres:create icepop_app_production

Link DB to the new app

ssh new_server postgres:link icepop_app_production icepop_app

Dump the data from the old DB You might want to do this at a slow time to minimise lost updates, or introduce the maintenance page.

ssh old_server postgres:export icepop_app-production > ~/Downloads/sql/icepop_app_production.dump

Import the dump

ssh new_server postgres:import icepop_app_production < ~/Downloads/sql/icepop_app_production.dump

Deploy the project

git push new_server deploy:main

Add domains to the app

ssh new_server domains:add icepop_app www.icepop_app.com icepop_app.com

Configure SSL certificate(s) via letsencrypt

ssh new_server letsencrypt:set icepop_app email james@icepop_app.com

Check where the DNS is configured and move the A records to point to the new server using the registrar’s UI whois will give you a clue which registrar you used if you’re anything like me and have three in use.

whois icepop_app.com

Request SSL certificates

ssh new_server letsencrypt:enable icepop_app

Tail the logs to ensure traffic is flowing as expected

ssh new_server logs -t icepop_app

Connect to the Rails console to check changes are reflected in the browser

ssh new_server run icepop_app "bundle exec rails console -e production"

All being well, your app is migrated (remember to check for any local file uploads etc) and the old server can be decomissioned.

I really enjoy reading Thomas’ work, it’s always deep and interesting. This is especially cool.

unchartedterritories.tomaspueyo.com/p/solar-e… 🌍💨

Today, I rediscovered a delightfully useful calculator app (macOS): numi.app

It took a little searching to solve a problem on my daughter’s windows PC. Every so often, the current window would flash with inverted colours (white to black, blue to orange). TLDR, it’s an accessibility setting (https://answers.microsoft.com/en-us/windows/forum/all/windows-10-screen-flashinginverting-colors/fbcbc715-8fd8-4476-9646-ff95739437e1)

It took a while to notice it was the window, as she runs word etc full-screen. We replaced the HDMI cable, rebooted, considered the graphics card.

Anyway, an over-active-but-unread discord channel was making notification sounds at frequent and random intervals. Both issues are now fixed, happily.

This looks nice at first glance. I wonder if framework agnostic means “works with plain JS”? github.com/toeveryth…

I think I knew some of these points, but wow, maps are weird/clever/deceitful!

unchartedterritories.tomaspueyo.com/p/maps-di…

Working with WeatherKit from Ruby

The Dark Sky API was recently shutdown by Apple, who acquired the iOS app and API a couple of years ago.

Working with the new API from Apple, WeatherKit, may be easy in Swift, but is more complex due to the introduction of JWT for authentication and the use of Apple developer credentials.

Here is how I was able to get the REST API working for my Rails app, using the Tenkit gem

bundle add tenkit

Credentials

in config/initializers/tenkit.rb:

Tenkit.configure do |c|
  c.team_id = Rails.application.credentials.tenkit[:apple_developer_team_id]
  c.service_id = Rails.application.credentials.tenkit[:apple_developer_service_id]
  c.key_id = Rails.application.credentials.tenkit[:apple_developer_key_id]
  c.key =  Rails.application.credentials.tenkit[:apple_developer_private_key]
end

The credentials needed to construct a JWT (done for us by the Tenkit gem) are gathered from your Apple Developer account. I’d recommend putting them in the Rails app credentials (as used above), or ENV variables if that is more convenient for your project.

Below, we’ll gather the values needed, then display them in the credentials file.

Gathering

apple_developer_team_id - This is your Apple Team ID, taken from the top-right of your developer account page:

apple_developer_service_id - This is a Service ID that you’ll need to create: https://developer.apple.com/account/resources/identifiers/list/serviceId It will be in the same style as an App ID, as a reverse domain name, e.g. “uk.co.cowlibob.fancypantsweather”

apple_developer_key_id - Generate a new key: https://developer.apple.com/account/resources/authkeys/add Give it a name (not important but descriptive to you) and make sure to check “WeatherKit”

Make sure you download the key and keep it safe for the next credential. Use the generated Key ID as the value for apple_developer_key_id.

apple_developer_private_key - This is the freshly downloaded private key, a .p8 file. P8 is a format that presents the private key in plain text (.p12 is a binary format). You may have seen these before, they look something like this:

-----BEGIN PRIVATE KEY-----
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA
-----END PRIVATE KEY-----

Configuring the credentials

You can now open the credentials file for edit and add these. I use vim to edit these, as specified by the env variable EDITOR. We can use the | symbol to start a block of text in the YAML file, much like a heredoc in Ruby.

> rails credentials:edit --environment development

# aws:
#   access_key_id: 123
#   secret_access_key: 345
tenkit:
  apple_developer_team_id: 'A11AA1AAA1'
  apple_developer_service_id: 'uk.co.cowlibob.fancypantsweather'
  apple_developer_private_key: |
    -----BEGIN PRIVATE KEY-----
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAA
    -----END PRIVATE KEY-----
  apple_developer_key_id: 'BBB11BBBBB'

Making a request

client = Tenkit::Client.new
lat = "53.4138684"
lon = "-1.5833708"

report = client.weather(
  lat,
  lon,
  data_sets: [:forecast_hourly]
)

ap report.weather.forecast_hourly.hours

That’s all there is to it once setup. You can also request multiple and different data sets. There are some undocumented features of the API such as date ranges that I’ll detail in a future post. For now, this is what the Tenkit gem supports.

Problems with SwiftUI Map View

The SwiftUI Map view is a great timesaver, but has some odd issues.

When two Map views are visible, (e.g. siblings in a vStack):

  • Both views will receive input applied to one, e.g. panning by sliding a finger across one of the views. Here, the views are in sync with each other.
  • Zoom by pinch gesture (in or out) does not work. In fact, coordinateRegion supplied to the Map() view has no effect either.

This extends to a scenario with a map on a presented view, where the parent view also displays a map.

Time to dig in and wrap MKMapView.

Using Screen to manage long-running server tasks

Occasionally I need to start a long running batch process on a server, such as a rake task to sync data from one system to another.

Leaving an SSH session open is not always possible, so here’s how to do it:

ssh server
screen -S peach
bundle exec rake long_running_task

Hitting ctrl-a then d will put the task in the background and you can then close the SSH session.

To reconnect to the long running task:

ssh server
screen -r peach

Note that “peach” is just a label for the screen session; it could be anything such as “long_running_task”, “elk123” or “404_cake_not_found”

I love how much fastlane.tools automates for you, but when you login with the incorrect credentials, it’s difficult to logout. github.com/fastlane/…

I really enjoyed the first episode of www.rooftopruby.com

On the theme of no magic, a colleague pointed out authentication-zero as an interesting alternative to devise for authentication. I’ve used and enjoyed clearance before, but am liking the simplicity of generated code.

Starting again

I’m starting a new rails project, and have been evaluating bullettrain.co to kick-start the project.

It looks really good, and has powerful generators and theming.

However, I’ve been bitten before by building an app using a 3rd party project (refinerycms anyone?) and limited by the dependencies, and lost in it’s magic.

As such, having built a lot with bullettrain in just one day, I’m jumping back to vanilla rails 7.0.4, with tailwindcss (another experiment for me).

Mastodon