Logo
Rachael Kalicun

"If God gives you something you can do, why in God’s name wouldn’t you do it?" - Stephen King

Why I Spent a Week Writing a Ruby Script to Track My Spending

It’s been a full year since I stopped trading my time for money. This is freedom but not the kind that erases fear or doubt. It’s the solitary kind. The kind that makes you confront yourself. It gives you space, but it also gives you yourself, unfiltered, 100 percent of the time.

What happened this year was quiet and hard to quantify, but it was a year of:

What I’ve done with this freedom hasn’t always looked productive from the outside (or even from the inside). I decided to build something I care about. It’s taking longer than I anticipated, but I’m not beating myself up over it or apologizing for it. I’m owning it. Even with the freedom to spend time how I want, there are always setbacks, both internal and external. I’m not a machine.

Last week, I paused that main effort and spent a full week writing code for something completely different: a tool to partially automate my financial tracking. Why? Well, because I wanted to and because I could. That, to me, is one of the definitions of wealth. I can follow what interests me and build something just because I care about it.

But let’s get serious, too. This is America, the land of no safety nets. I still have to worry about money. This kind of freedom depends on a more traditional kind of wealth: the kind with dollar signs.

That’s why I built the tool. I want to keep practicing this version of wealth, but I also want to do it for many decades to come. So until I make $10 million with my unshipped cooking app, I need to keep answering the question:

“Can I actually sustain this lifestyle indefinitely?”


Looking Back at the Last Time I Did This

I wrote about my financial tracking process last November. It was a very manual process I started before I quit my job as an attempt to understand how much I actually spend in a year, broken down by category.

I haven’t done it since December because it was a pain in the ass. I had to download CSVs from every bank, credit card, brokerage, and HSA account. I renamed columns, flipped signs, spent half a day deciphering Amazon orders, and manually categorized every transaction.

Now, it’s August. It’s officially been a year since my last paycheck, and that milestone has been weighing on me. I wanted to see my finances clearly and with honesty. I wanted to answer the real question with real data:

“Is this lifestyle actually viable?”


How the Tool Works

The script I wrote is built in Ruby and designed to handle a bunch of financial institution CSV formats. It:

Data

I drop all my CSVs into a folder, and it does the rest.

input_paths = Dir["data/*.csv"]
rows = NormalizeCsvs.normalize_csvs(input_paths, FORMATS)

Each institution has quirks so I map to a FORMATS hash:

FORMATS = {
  "amazon" => {
	type: :expense,
	date: "date",
	description: "items",
	debit: "total",
	credit: "refund",
	bank_account: false
	},
  "capital_one" => {
	type: :expense,
	date: "Transaction Date",
	description: "Description",
	debit: "Debit",
	credit: "Credit",
	bank_account: false
	},
  "fidelity" => {
	type: :income,
	date: "Run Date",
	description: "Action",
	debit: "Amount ($)",
	credit: "Amount ($)",
	bank_account: true
	}
}

The script parses and standardizes each row like this:

{
  "Date" => "2025-07-10",
  "Description" => "Trader Joe's",
  "Amount" => 49.99,
  "Category" => "Groceries",
  "Notes" => "",
  "Source" => "Citibank",
  "Type" => "Expense"
}

It handles:


Categorization: My Rules

Regex-based rules tag most transactions automatically:

CATEGORIZATION_RULES = {
  /safeway|trader joe|natural grocers/i => "Groceries",
  /jiffy lube|progressive|motor vehicle/i => "Car",
  /chatgpt|canva|taxact/i => "Subscriptions",
  /airways|united|hotel/i => "Travel"
}

Amazon has its own logic:

CATEGORIZATION_RULES_AMAZON = {
  /dog|pet/i => "Dog",
  /nuts|coffee|tea/i => "Groceries",
  /blanket|nonstick|curtain/i => "Home",
  /book|kindle/i => "Books",
  /.*/ => "Amazon - Uncategorized"
}

Multi-item Amazon orders like “Dog food; Paper towels” are flagged in a “Notes” column as “Amazon Multi Order” so I can pull them apart manually.


Output: Just Two Files

Once processed, rows are separated:

income_rows = rows.select { |r| r["Type"] == "Income" }
expense_rows = rows.select { |r| r["Type"] == "Expense" }

NormalizeCsvs.write_csv("output/income.csv", income_rows, columns)
NormalizeCsvs.write_csv("output/expenses.csv", expense_rows, columns)

Each file includes only what I care about:

Date, Description, Amount, Category, Notes, Source


I Trust It Through Tests

I wrote a full test suite using Minitest to catch edge cases and regression issues.


Amazon Data: Still a Work in Progress

I used the AZAD Chrome extension to export my Amazon orders as CSV. It’s been helpful, but I’m relying on a third-party tool that could break or disappear. I’ve considered:


Remaining Limitations


What I Learned After Running It

After running the tool and reviewing the results:

2025 spending categories 2025 grocery spending


Why It Mattered

Spending a week writing this script was something I wanted to do for fun, but it is also really useful and practical. Knowing one’s financial state is imperative so that every other desired pursuit can continue.

I want to make money from projects I care about, but that won’t happen right away. Maybe not for a long time. That doesn’t mean I don’t want to keep doing them, but I also don’t want to destroy the future I built and worked so hard for in the meantime.

This script helps me protect the long game.


Why I Didn’t Use AI (This Time)

I initially thought this might be the perfect place to try AI for the first time in a project to analyze and categorize transactions, but once I started implementing my regex rules, I realized I didn’t need it.

Regex gave me full control, predictable behavior, and fast results.

AI did write my readme, though.


View the code on GitHub