Starting a Ruby on Rails project
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.