I built an interactive tool for visualizing task progress.

Screenshot of Hill Chart

A hill chart is a visual tool for tracking tasks and projects, using the metaphor of going up and down hill to communicate the different phases of work.

Every piece of work has two phases: an uphill phase where you figure out your approach, and a downhill phase focused on execution.1

I appreciate hill charts because they embrace the fuzziness of task-based work. Unlike rigid progress metrics like “done/not done,” percentage completion, or T-shirt sizes, hill charts offer a more nuanced way to describe progress. If you export the chart each time you update it, you can also detect trends over time.

This tool runs in the browser via Github Pages and is built with plain JavaScript, d3.js, and is styled with TailwindCSS. You can explore the code by right-clicking → View Source or checking out the GitHub repo.2

When you’re interested in testing the actual SQL queries generated by ActiveRecord, rather than just what’s returned from the database, you can capture them using ActiveSupport::Notifications.

module QueryCapturing
  IGNORED_QUERIES = ["SCHEMA", "TRANSACTION"]

  def capture_queries
    captured_queries = []
    subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |event|
      captured_queries << event.payload[:sql] unless IGNORED_QUERIES.include?(event.name)
    end
    yield captured_queries
  ensure ActiveSupport::Notifications.unsubscribe(subscriber)
  end
end
class ServiceMetadata::QueryCommentsTest < Minitest::Test
  include QueryCapturing

  def test_annotates_queries_with_metadata_comments
    capture_queries do |queries|
      ServiceMetadata::QueryComments.comment { Comment.all.load }

      assert queries.all? { |q| q.match?(/\/\*.*?origin_service: my_service.*?\*\//m) }
    end
  end
end
def print_progress(title, total, current_progress, bar_width: 50)
  progress_pct = (current_progress.to_f / total) * bar_width
  printf("\r#{title}: [%-#{bar_width}s ] -- %s", "▤" * progress_pct.round, "#{current_progress}/#{total} ")
end

# Usage
1.upto(10) do |i|
  print_progress("Here we go!", 10, i)
  sleep 0.2
end;print("\n")

# Output Example:
# Here we go!: [▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤                ] -- 7/10

I’ve been playing around with the idea of building a checklist app that’s geared towards completeing procedures.

Checklist procedure app prototype

I remember watching a livestream of a rocket launch and seeing the closeout crew go through a checklist using a tablet strapped to their thigh. I thought, “I’m no NASA engineer, but that looks useful!”

I built an interactive jq TUI using fzf!

I was searching for an interactive jq editor when I came across this repo, which had an intriguing suggestion:

echo '' | fzf --print-query --preview "cat *.json | jq {q}" – An fzf hack that turns it into an interactive jq explorer.

This sent me down a rabbit hole, and I discovered just how incredibly configurable fzf is, e.g.:

  • You can bind custom keys to execute non-default behaviors:
    --bind=ctrl-y:execute-silent(jq {q} $tempfile | pbcopy)
    
  • You can start fzf with an initial query:
    --query="."
    
  • You can configure fzf with different layouts:
    --preview-window=top:90%:wrap
    
  • You can add a multi-line header to provide instructions:
    --header=$'ctrl+y : copy JSON\nctrl+f : copy filter\nenter : output\nesc : exit'
    

I wonder how many different TUIs I can create with just fzf?

Checkout the code for ijq here.

Turn your ApplicationRecord models into a Mermaid ERD

Rails.application.eager_load!

# Instead of all ApplicationRecord descendants, you can
# make an Array of only the models you care about
data = ApplicationRecord.descendants.each_with_object({}) do |model, data|
  model_name = model.name.upcase
  data[model_name] = {
    columns: model.columns.map { |column| [column.name, column.sql_type] },
    associations: model.reflect_on_all_associations.each_with_object({}) do |reflection, assoc_data|
      assoc_data[reflection.name] = {
        klass: reflection.class_name.upcase,
        relationship: reflection.macro
      }
    end
  }
end

# Intermediate step to save the data as JSON
File.open("data.json", "w") { |file| file.write(data.to_json) }

mermaid_erd = ["erDiagram"]

data.each do |table, details|
  columns = details[:columns].map { |name, type| "#{name} #{type}" }.join("\n  ")
  mermaid_erd << "#{table} {\n  #{columns}\n}"
end

data.each do |table, details|
  details[:associations]&.each do |association_name, association_details|
    other_table = association_details[:klass]
    case association_details[:relationship]
    when :belongs_to
      mermaid_erd << "#{table} ||--o{ #{other_table} : \"#{association_name}\""
    when :has_one
      mermaid_erd << "#{table} ||--|| #{other_table} : \"#{association_name}\""
    when :has_many
      mermaid_erd << "#{table} ||--o{ #{other_table} : \"#{association_name}\""
    end
  end
end

File.open("out.mermaid", "w") { |file| file.puts mermaid_erd.join("\n") }

Adding Memos

#memo

The thing I love most about social media feeds is the immediacy of capturing and sharing thoughts and ideas. Writing on my blog, being built with Jekyll, creates just enough friction for me to overthink what I’m sharing.

Inspired by Julia Evans’s TIL blog implementation, I’m hoping a dedicated page and a rake task removes enough friction for me to share more often.