@jeremysmithco

Email Digest Background Process
This set of Sidekiq workers queries a set of email digest subscribers and performs a fan-out to send email to each subscriber as a separate job.
class DigestEnqueuer
  include Sidekiq::Worker

  def perform(digest_id)
    digest = Digest.find(digest_id)
Curated ago by @jeremysmithco
File Tree Component
This is a view component that renders out the file tree in the editor menu for RailsInspire.
<%= tag.ul(class: "ml-4") do %>
  <% @tree.map do |node| %>
    <%= tag.li do %>
      <% if node.last.is_a?(SampleFile) %>
        <%= link_to [node.last.sample, node.last], class: "flex items-center space-x-1 ml-5 mb-2 hover:text-blue-300" do %>
Curated ago by @jeremysmithco
Open Delivered Emails in Dev Email Client (Mac-only)
Rails bin script to listen for emails delivered to filesystem and open with Mail (on a Mac).
#!/usr/bin/env ruby
require 'listen'

# This script watches for ActionMailer mail delivered to tmp/mails in dev,
# adds a timestamp and proper extension to the filename, and opens in the
Curated ago by @jeremysmithco
Multiple Active Storage Services with Override for Test
Active Storage services default to private access, unless you specifically make them public. By default, Active Storage assumes private access to services. This means generating signed, single-use URLs for blobs. If you'd rather make blobs publicly accessible, specify public: true in your app's config/storage.yml But sometimes apps need both private...
class Account < ApplicationRecord
  has_one_attached :logo, service: storage_service(:amazon_public)
  has_one_attached :report, service: storage_service(:amazon_private)
end
Curated ago by @jeremysmithco
Roll Your Own Audit Logging
Here's a simple audit logging solution using an AuditLog ActiveRecord model with a polymorphic association, a Sidekiq worker, and an audit_log controller method. I wrote about the approach in Audit Logging in Rails.
class ApplicationController < ActionController::Base
  def audit_log(auditable, account, event)
    AuditLogWorker.perform_async(auditable.to_global_id, account.id, current_user.id, event)
  end
end
Curated ago by @jeremysmithco
User Live Search with Stimulus, Turbo Frames and PgSearch
Here's a solution I've used on a project to add a user switcher dropdown with typeahead search to a navbar. It uses the pg_search gem for Postgres full-text search. There are a lot of blog posts out there with similar solutions, with terms like: typeahead, autocomplete, autosuggest, instant search.
class UsersSearchesController < ApplicationController
  def index
    @users = users_query(params[:query])

    respond_to do |format|
Curated ago by @jeremysmithco
Minification with Both Uglifier and esbuild
If you are working on legacy Rails projects with old ES5 code, but have also switched to jsbundling-rails, esbuild for new ES6 code, you may have struggled to find a good approach to minifying all your assets. Uglifier only works with ES5 and may cause issues in Harmony mode. Inspiration...
//= link_tree ../builds
//= link_tree ../images
//= link legacy.js
Curated ago by @jeremysmithco
Generating Open Graph Images with SVG partial and Vips
I found a way to generate custom Open Graph images for Sample files on this site using an SVG partial that is rendered and converted to PNG by ImageProcessing::Vips.
class SampleFile < ApplicationRecord
  has_one_attached :open_graph_image

  def update_open_graph_image
    self.open_graph_image.attach(io: CreateSampleFileImage.new(self).create, filename: "sample_file_#{id}.png")
Curated ago by @jeremysmithco
Alternative approach to drag and drop sorting with acts_as_list
Here's an somewhat unconventional approach to drag and drop sorting using acts_as_list I came up with this week. In the old days, I would have implemented something similar to this RailsCasts episode. But I think I might like this better. I made a YouTube video explaining my thinking. As Chris...
class ListInsertionsController < ApplicationController
  def create
    item = GlobalID::Locator.locate(params[:item])
    precursor = GlobalID::Locator.locate(params[:precursor])
    authorize item, :update?
Curated ago by @jeremysmithco
Rendering Markdown with Multiple Modes
Sometimes when I'm working with Markdown in a Rails app, I'll be using different sets of redcarpet flags for different types of content. For example, I have images and links turned on for blog posts, but turned off for comments. Keeping track of all the variations can be difficult. I...
module MarkdownHelper
  def render_markdown(content, mode)
    sanitize(MarkdownRenderer.new(content, mode).render)
  end
end
Curated ago by @jeremysmithco
Uploading Honeybadger source maps with esbuild and Sprockets
If you are using Honeybadger for JS error monitoring on your Rails app, you may want to upload source maps so they can be applied to JS stack traces. Here's how you can do that if you are using esbuild, Sprockets, and deploying to Heroku. The source map will be...
<script src="//js.honeybadger.io/v6.9/honeybadger.min.js" type="text/javascript"></script>
<script type="text/javascript">
  Honeybadger.configure({
    apiKey: "<%= ENV["HONEYBADGER_API_KEY_JS"] %>",
    environment: "<%= ENV["HONEYBADGER_ENV"] %>",
Curated ago by @jeremysmithco
Adding custom Active Storage transformations
Out of the box, Active Storage supports a number of transformations, through the image_processing gem. This gem provides a common interface to both MiniMagick (the old standard) and ruby-vips (the new default). I recently needed the ability to lighten images uploaded via Active Storage (for use as watermarks), but there...
<% if @document.watermark.attached? %>
  <%= image_tag @document.watermark.variant({ resize_to_limit: [1_000, 1_000], lighten: 0.5 }) %>
<% end %>
Curated ago by @jeremysmithco