3 min read

Starting a Ruby on Rails project

Starting a Ruby on Rails project
Photo by Paul Becker / Unsplash

I've recently started training a few people on RSpec. Since we are in a Ruby on Rails ecosystem, one of the questions that came up was about how to get a simple Ruby on Rails project started and configured to use a PostgreSQL database.

So, here follows the gist: the command I usually run to get the project generated, the ones I use afterward to add essential gems, and finally, how I rely on docker to get the rest of the services ready too.

Pre-requisites

I migrated years ago to Asdf to manage versions of all my languages on every machine I work on. So I make sure I have the version of Ruby I need with it in the chosen folder.

$> cd example-rails
$> asdf local ruby 3.2.2

We also need to install Docker and docker-compose.

The initial skeleton

Then I generate the Ruby on Rails application.

$> rails new -T -d postgresql -c tailwind -B .

This gets us:

  • no tests framework (I don't want mini-test)
  • PostgreSQL as database
  • TailwindCSS for the CSS part
  • no bundle install run after the generation of the application

Now, we add a bit more: rspec, a docker-compose.yml file and the related database configuration.

RSpec

$> bundle add rspec-rails --group development,test
$> rails generate rspec:install

This will get us started nicely.

We should also include Faker and FactoryBot:

$> bundle add faker --group test
$> bundle add factory_bot --group test

Goodjob (optional)

Background jobs are often included early in a project. While most will go with Sidekiq or Resque I have moved to Goodjob recently. As it relies on PostgreSQL to store and handle the queues, it's a bit safer (in my opinion) than Sidekiq. My main interest is that you can rely on your existing PostgreSQL server for jobs, at least for a start.

Yet, as soon as growth happens you might want to consider other options:

  • a separate PostgreSQL server to handle jobs and keep Goodjob
  • move to RabbitMQ or AWS SQS with Sneakers or Shoryuken.
$> bundle add good_job
$> bin/rails g good_job:install
$> bin/rails db:migrate

Then configure good_job as default queue adapter for ActiveJob by editing config/application.rb or config/environments/{RAILS_ENV}.env:

# config/application.rb or config/environments/{RAILS_ENV}.rb
config.active_job.queue_adapter = :good_job

The worker can be started with bundle exec good_job start. See GoodJob github's page for more details.

Docker time

I rely on Docker for running all kind of services a Ruby on Rails project depends on. Specifically I use docker-compose to have my fleet of services ready to start and stop at will.

This is very handy as I work on multiple projects at any given time. Each project has its own docker-compose.yml file thus ensuring isolation of versions, data, etc ...

Here is the basic docker-compose.yml file:

# docker-compose.yml
version: "3.7"
services:
  postgres:
    image: postgres:13.2
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_USER=postgres
    volumes:
      - data-postgres:/var/lib/postgresql/data
volumes:
  data-postgres:
    driver: local

To use this container with the Ruby on Rails project we need to have the following in the config/database.yml:

default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 10000
  host: localhost
  port: 5432
  username: postgres
  password: postgres
 
 development:
    <<: *default
    database: blob-development

test:
    <<: *default
    database: blob-test

Using docker-compose up -d in the terminal will start the container and make it ready to be used.

Complimentary

I also prefer to use UUIDs for ids. To do so I quickly generate use a migration with the following:

class EnableUUID < ActiveRecord::Migration
  def change
    enable_extension 'pgcrypto'
  end
end

And then, add the following initializer to ensure any new migration introducing a new table will use UUIDs as id by default.

# config/initializers/generators.rb
Rails.application.config.generators do |g|
  g.orm :active_record, primary_key_type: :uuid
end

See this great post from Pawel Urbanek on the topic for more details.

Wrapping up

Once all that is done, we are quite ready to work:

  • a working Ruby on Rails base application
  • a PostgreSQL instance running isolated
  • RSpec, FactoryBot, and Faker ready to write tests
  • UUIDs for ids
  • TailwindCSS for all the CSS and UI

From here, we can start to work and add bricks related to the specific use we will do with that application (authentication, authorization, payments processing, mailer ...).

So I'll let those parts to you.