Ruby + Rails : Distilled

@distilled.skillstopractice.com

A collection of posts of interest to Ruby and Rails developers. Hand-picked, high signal, always on topic. Maintained by @skillstopractice.com Also available as a feed: https://bsky.app/profile/did:plc:ip3trmvdbnlm4g7cdc5xs7ub/feed/aaaf5jle4pb7e

How do you manage error classes in your app? - Granular — Domain::Specific::NotFoundError - Generic/Reusable — Error::NotFound - Not — StandardError/raise 'x' Personally, I create my own error classes so I can see what's wrong at a glance, even if it means catching existing ones. #RubyLang

Don't want to expose a class' initializer in Ruby? Making it private is easy! Just use `private_class_method :new`. This conveys intent that your class is not designed to be initialized from outside — perfect when you want to use the factory pattern.

Screenshot of Ruby code:

class Pizza
  def self.margherita
    new(sauce: 'tomato', cheese: 'mozzarella', toppings: %w[basil])
  end

  def self.vegetariana
    new(sauce: 'tomato', cheese: 'mozzarella',
        toppings: %w[aubergine mushrooms peppers onions olives])
  end

  private_class_method :new

  def initialize(sauce:, cheese:, toppings:)
    @sauce = sauce
    @cheese = cheese
    @toppings = toppings
  end
end
Screenshot of Ruby code:

Pizza.margherita
# => #<Pizza:0x0000000121c17f10
#      @cheese="mozzarella",
#      @sauce="tomato",
#      @toppings=["basil"]>

Pizza.vegetariana
# => <Pizza:0x0000000120f34988
#      @cheese="mozzarella",
#      @sauce="tomato",
#      @toppings=["aubergine", "mushrooms", "peppers", "onions", "olives"]>

Pizza.new(sauce: 'white', cheese: 'none', toppings: %w[pineapple])
# => private method 'new' called for class Pizza (NoMethodError)

How to automatically trigger a file download with Turbo? The user triggers the creation of a file. That file is created using a background job. I could update the view using Turbo Streams (e.g. render a 'Download Now' button). But I'd like download to automatically start 🤔 #rubyonrails

Incredibly common Rails perf anti-pattern: Rails.cache.fetch([complicated,cache,key], expires_in: random_interval) do dog_of_a_SQL_query end Cache hitrate: 10% (if anyone even knows what it is)

What's the best host for running a Ruby application, that stores files on the server, and runs sqlite as the database, on the server?

G

Having setters that appear to be at the instance level apply at the class level is something that (for me) definitely would be against the Principle of Least Surprise. An easy way to define class level accessors is nice but hard to see the use case for the instance level behavior.

Consider using pattern matching in Ruby. I wanted to update a user's location (RGeo Point) from an endpoint which receives a JSON object: { lat: ..., lng: ... }. Using pattern matching, we can detect such an object's shape, falling back to a catch-all (_) with an if statement for regular Points.

A screenshot of ruby code. The contents are:

class User < ApplicationRecord
  def location=(value)
    case value
    in { lng:, lat: }
      super(self.class.point_factory.point(lng, lat))
    in _ if value.respond_to?(:latitude) && value.respond_to?(:longitude)
      super
    else
      raise ArgumentError, 'Invalid location format'
    end
  end
end

user = User.last
user.update!(location: { lng: 4.8922, lat: 52.3731 })
user.location
# => #<RGeo::Geographic::SphericalPointImpl:0x4610 "POINT (4.8922 52.3731)">

What's the "Rails" way of designing a multi-step workflow with background jobs? Each job triggering the next, active record callbacks, state machines or something else? Help!

TinyBits is getting a new interesting feature, you can now (using the repo's head) supply the pack/unpack method with an external dictionary. For shorter messages with no duplicate strings this can lead to a dramatic size reduction, from 96 to 34 bytes in the example below

Bild