Notes

A collection of notes that I jot down occasionally for personal use. I find that writing things down helps me remember, or so that's the idea. Thoughts are my own, and not fully formed 🧠


Rails devs vs Typescript devs

Aug 31, 2024

Rails devs - dislike using types, but use a SQL database.

Typescript devs - must use types everywhere, but then use firebase.

Ironic no?

Rails Delegated Types

Aug 22, 2024

Today I stumbled upon Rails delegated types, a feature added in Rails 7. The paradigm can be used as an alternative to single table inheritance and purely abstract ActiveRecord classes. Keeping this one in my back pocket to use next time I come across a problem where I want the superclass view to be separately "queryable" in its own table without having to store all of unrelated subclass attributes in that same table.

With this approach, the “superclass” is a concrete class that is represented by its own table, where all the superclass attributes that are shared amongst all the “subclasses” are stored. And then each of the subclasses have their own individual tables for additional attributes that are particular to their implementation.

Snood is still around?

Aug 12, 2024

Snood was awesome, was it not? And what the heck, it's still being sold and it's $19.99 for the original version!

Pasted image

Zellij

Jul 25, 2024
I've switched to using zellij from tmux as my terminal multiplexer. I'm digging it so far, though it took a little bit for my muscle memory to get used to the new key bindings. But the user experience feels so much more thought out than tmux, at least for development 🤓

Using Faraday

Jun 01, 2024

I've seen the faraday ruby gem used in a number of ruby projects over the years, but I hadn't used it directly myself until recently.

There are a LOT of HTTP clients in ruby land, but faraday is consistently on the top of the list as being the most popular. I decided to try it out for a project I was leading at work. I needed to build a generalized HTTP client that could be used to interact with a number of different websites that accepted and responded with different content types. I also wanted to add some defaults such as a proxy uri, basic error handling, and a timeout. I really liked faraday's approach to mixing in ad-hoc request/response middleware to handle making requests, so I decided to give it a go. I built a little utility that wraps a faraday connection and its response. Nothing too fancy, but it made working with the connection and response objects more flexible for my specific use case. The example below is simplified, but it looked something like the following:

require "faraday"
require "http-cookie"

# Wraps a Faraday::Connection
# 
# - Uses default configuration options for a proxy uri, timeout, and error handling
# - Accepts all configuration options for a faraday connection
# - Accepts a block to configure additional middleware
# - Returns a custom custom Response object
class HTTPConnection
  attr_reader :connection

  def initialize(url:, **kwargs, &block)
    @connection = Faraday.new(
      url:,
      proxy: { uri: "https://someproxy.com" },
      request: { timeout: 15 }, # seconds
      **kwargs
    ) do |builder|
      builder.response(:raise_error)
      block&.call(builder)
    end
  end

  %w[get post put delete patch].each do |request_method|
    define_method(request_method) do |path, *args|
      Response.new(connection.public_send(request_method, path, *args))
    end
  end
  
  # Wraps a Faraday::Response
  # 
  # - Adds helper to get the request_method
  # - Adds helper to get the url
  # - Adds helper to easily retrieve cookies from the response
  class Response
    extend Forwardable

    attr_reader :response, :env

    def_delegators :response, :status, :body, :headers
    def_delegators :env, :request_body, :request_headers

    def url
      env.url.to_s
    end

    def request_method
      env.method.to_s
    end

    def cookies
      Cookies.new(
        cookie_header: headers['Set-Cookie'],
        url:
      )
    end

    def initialize(response)
      @response = response
      @env = response.env
    end
  end

  class Cookies
    def initialize(cookie_header:, url:)
      @jar = HTTP::CookieJar.new
      @url = url
      @jar.parse(cookie_header.to_s, @url)
    end

    def to_a
      @jar.cookies(@url)
    end

    def to_header
      { 'Cookie' => HTTP::Cookie.cookie_value(to_a) }
    end
  end
end

conn = HTTPConnection.new(
  url: "https://example.com",
  headers: { "Content-Type" => "application/json" }
) do |builder|
  # Can add any additional middleware here, depending on the requirements of the endpoint
  builder.request(:url_encoded)
  builder.response(:json)
end

# Works just like a normal faraday request
response = conn.post('/endpoint', { 'name' => 'Jason' })

More Stack Overflow

Apr 25, 2024

My previous note was about stack overflow so I continued the trend by answering another couple questions:

  • This question. As much I don't prefer using jsonb columns in postgres if I don't have to, I've overall had a pretty good experience using the jsonb_accessor gem. Despite some of its quirks, it does make using jsonb columns in Rails more user friendly. And at the end of the day that's a good thing.
  • This question. This one was less interesting as I think the user asking misunderstood where the stated bug's origin, but I learned a little something about the addressable ruby gem.

  • Entering Stack Overflow

    Mar 26, 2024

    While working on this site, I encountered an issue where content displayed using the the flash (a special tool used in conjunction with rails sessions to display temporary content between requests) was lingering for a brief second when revisiting a page that had previously displayed something from the flash. After searching through some documentation, I found that the underlying cause was due to the way turbo caches content between page reloads. A few minutes later, I happened to stumble upon this stack overflow thread related to the issue. I thought that I could provide a useful answer, and so I responded 🎉

    In the age of LLMs and notably ChatGPT, stack overflow is becoming less relevant for day to day usage, but I am happy that fiddling around with this site inspired me to contribute a response 🖥️

    Remembering Gall’s Law

    Mar 24, 2024

    Gall’s law has greatly influenced the way I’ve operated as a software developer over the last decade. It is as follows:

    A complex system that works is invariably found to have evolved from a simple system that worked. A complex system designed from scratch never works and cannot be patched up to make it work. You have to start over with a working simple system

    HTTP Basic Auth and the Browser

    Mar 24, 2024

    Today I remembered that it's not possible to traditionally log out of a website if HTTP basic authentication is used to login.

    Basic auth credentials are transmitted with each and every request by the browser. Hence why you cannot traditionally log out without closing the browser
    As soon as the browser prompts for credentials and the user logs in, those credentials are cached sent with every subsequent request until the browser is closed or restarted. The more you know ✨

    On Execution

    Mar 22, 2024

    I reminded someone of a quote I've heard today, which I still take to heart

    Good strategy is nothing without good execution

    Rubocop LSP

    Mar 19, 2024

    Running rubocop in lsp mode is pretty sweet 🍬

    Before this, my neovim setup synchronously called the rubocop command to auto format the file upon every save. While this was sped up a bit by using the --server flag, it is nowhere near as fast as running rubcop in lsp mode using the --lspflag. Taking advantage of neovim’s support of the language server protocol, I was able to integrate this seamlessly into my setup. It’s been great for diagnostics too 🤓

    Note Interactivity

    Mar 04, 2024

    Added some touches that allow notes to come alive a little bit more, including the ability to easily highlight code using Highlight JS 💅

    # Let's test the syntax highlighting with a simple factorial function
    def fact(n)
      if n == 0
        1
      else
        n * fact(n-1)
      end
    end

    Side Projects

    Feb 20, 2024

    I’ve always been motivated and had a lot of fun writing software for a company, and I’ve managed to maintain a stable level of productivity at work throughout the last decade. But I’ve always wanted to work on a fun software related project on the side 🤔

    The trouble I’ve had with working on side projects is that it can be difficult for me to build momentum with any particular idea. This is especially true if I’m finding the software I’m writing at work to be fulfilling. Software companies already have customers using their product and the feedback loop of shipping is immediately satisfying.

    I’ve dabbled with contributing to open source and have landed some cool contributions over the years, but I have yet to build something for me. Something that let’s me express my own personality on the web. So this year I’ve decided to take the pressure off of building something unique and cool, and instead start with something simple that I’ve always wanted to do. Create and host my own website from scratch. I want this to be the catalyst for tinkering a little bit more in my free time 🚀