AttendList

Chrome ExtensionLoginStart tracking →

Home // Published 2nd June, 2025

Our Production Ruby on Rails Stack

AttendList is an attendance tracker for Google Meet. It's built on a Ruby on Rails backend that talks to a Chrome Extension.

This article explores the infrastructure and gem choices I've made while building AttendList, from the basics like background jobs, auth and blogging, to deployment and some fun extras.

Table of contents:

The basics (PostgreSQL, Redis, Sidekiq, RSpec)

gem "pg"                # database
gem "redis"             # cache
gem "sidekiq"           # background jobs
gem "rspec-rails"       # testing
gem "factory_bot_rails" # test data

AttendList's core stack is pretty vanilla — it uses PostgreSQL for the database, Redis for caching and job queues, and Sidekiq for background jobs.

This is the stack I use for all my side-projects. It's rock-solid, reliable and cheap to host.

I veer slightly off the "Rails way" with RSpec & factories for tests, but that's just a preference thing.

Authentication (Devise, OAuth)

gem "devise"                            # Auth
gem "activerecord-session_store"        # Session storage
gem "omniauth-rails_csrf_protection"    # Make Oauth and Devise play together nicely
gem "omniauth-google-oauth2"            # Support "login with Google"
gem "pretender"                         # Devise impersonation

An AttendList user logs in through a Devise login flow via Google OAuth.

AttendList then asks users to re-auth with Google to connect Google Meet (ie: grant necessary scopes), via an incremental authorization flow. It's an auth style that Google encourages where you initially only request the minimal scopes from a user (ie: name & email), then re-request more scopes as needed.

An incremental auth flow with Devise looks a bit like this:

button_to(user_google_oauth2_omniauth_authorize_path(
            scope: "...",
            prompt: "consent",
            include_granted_scopes: "true",
            login_hint: current_user.email,
        ), data: { turbo: false }) do
    >>> Connect Google Meet <<<
end

This prompts a user to OAuth again via Google — thereby granting additional scopes — after they've initially logged in.

Finally, I use the Pretender gem to login as different users within AttendList, for support & debugging.

View layer (Tailwind CSS and ViewComponents)

gem "tailwindcss-rails" # use TailwindCSS
gem "view_component"    # better version of partials
gem "rails_icons"       # easily use various icon libraries

I've been a big fan of ViewComponent for years now, and I use them heavily in all my apps. I've written about ViewComponents before; they plug in very neatly to the standard Rails stack, feel excellent to work with and are a huge step up from regular partials.

For styling, I use Tailwind CSS. It helps me style UIs extremely quickly, and it plays nicely with Rails and ViewComponent.

I lean heavily on TailwindUI for UI components, and pair it with the Phosphor icon library (via the rails_icons gem gem). I previously defaulted to Heroicons for icons, but Phosphor has vastly more icons available.

Performance and Profiling (Bullet, Rack Mini Profiler)

gem "rack-mini-profiler"    # Page rendering insights, SQL counts etc
gem "bullet"                # Automatically alerts for N+1 queries

I use the Bullet gem to catch N+1 queries, and combine it with rack-mini-profiler to keep an eye on performance. Both are handy and painless to setup.

I only run these gems in the development environment, but I saw recently that Campsite used to run rack-mini-profiler in production, which I might look into.

Google APIs

gem "googleauth"
gem "google-apps-meet-v2"
gem "google-apis-people_v1"

Google has a bunch of Ruby client libraries, and I use these three for AttendList.

There's not much to say here — they work fine; they're auto-generated, so they can feel a bit clunky to use, but it's not too bad.

Blogging (Custom)

gem "redcarpet"             # markdown parsing
gem "rouge"                 # code highlighting
gem "sitemap_generator"     # sitemap builder
gem "front_matter_parser"   # parse frontmatter from .md files

I've rolled my own blogging system for AttendList.

Its cornerstone is the Redcarpet gem. Redcarpet powers all the markdown rendering, which I pipe through a Tailwind CSS prose class to neatly format each blog post.

I pair Redcarpet with Rouge for code highlighting (like above), using the included plugin for Redcarpet and the base16.solarized.light theme (preview other themes).

I also use the Sitemap Generator gem to create an XML sitemap, and front-matter-parser to extract data from each post, like the title & published_at date.

Hosting and deployment (Hatchbox)

I use Hatchbox to deploy AttendList and host on servers from Hetzner.

Hetzner is absurdly good value — I'm using a single CAX31 instance, with 8 vCPU cores, 16 GB of RAM, 160 GB of storage and 20 TB of bandwidth... for US $15.50/mo 🤯.

Hatchbox costs $10/mo on top, but it's worth it. It eliminates deployment headaches and automatically configures the server, updates it, deploys new builds from GitHub, handles my domain names, SSL and much more.

Extras (Dashboard, Charts, DevX etc)

gem "blazer"            # Dashboards & queries
gem "ahoy_matey"        # Product analytics
gem "rails_charts"      # Charts (bar, line, pie etc.)
gem "annotate"          # Annotate models, specs etc
gem "honeybadger"       # Monitoring
gem "loops_sdk"         # Email marketing

Here are a couple of handy extra gems I use in AttendList:

Conclusion

I hope you found this short article interesting!

Despite being an early-stage app, AttendList still has a lot of moving parts; I hope this look under the hood was insightful.