Example: an admin interface named "dtx_admin"
This guide assumes you've already installed a current ruby
(version 2.6.1 by time of writing this) and the gems rails
(version 5.2.x) and bundler
(version 2.0.1 or newer) on your machine. It was written on macOS on might assume you use homebrew. Nevertheless you should be able to follow it on Linux or the Linux DSL in Win10 or even under Windows itself.
If you don't want to read a guide, but just have a somewhat-setup minimal Rails app, you'll find a more current one (Ruby 3.0, Rails 6.1) here https://github.com/mediafinger/minimal which you can use as a start or template for your own app. The commits are described in its README.
On macOS I use the PostgresApp to install PostgreSQL. You can also use homebrew or any other method. When you are ready, try to manually install the pg
gem, before continuing.
gem install pg
(here may be dragons)
When creating a new Rails app, you can pass several parameters on the command line. For this project it could be those:
rails new dtx_admin
--database=postgresql \
--webpack \
--skip-bundle \
--skip-spring \
--skip-test \
--skip-action-cable \
--skip-coffee
- dtx_admin is the name if the app I am creating
- We want to use
PostgreSQL
as a database webpack
is the latest fad in the Rails world to bundle your assets- I'll skip
bundle
because I usually edit the Gemfile before I runbundle install
myself - I'll skip
spring
because it can waste more time than it saves. - I'll skip
test
because I (like most others) userspec
- I'll skip
action
-cable as we don't do anything with websockets (yet) - I'll skip
coffee
(script) as we won't add much Coffee- or Javascript just yet
Change to the newly created dtx_admin folder and run git init
(unless Rails does this automatically for you).
Create your first commit and create commits after every setup step.
Add a .ruby-version file to ensure Ruby version managers use the right version. For heroku you will have to add this information in the Gemfile.
echo '2.6.1' > .ruby-version
Usually I remove some of the gems in the Gemfile and the comments and add a few development and test dependencies. That depends on personal preferences. And I sort the gems alphabetically, to appease my linter.
Those are the gems I like to use in development and test environments:
group :development, :test do
gem "awesome_print", "~> 1.8" # nicer formatted console output
gem "bundler-audit", "~> 0.6" # to ensure no used dependencies have know security warnings
gem "byebug", platforms: [:mri, :mingw, :x64_mingw] # to debug
gem "factory_bot_rails", "~> 4.8" # to create factories for tests
gem "rspec-rails", "~> 3.8" # sets up rspec for tests
gem "rubocop", "0.65.0", require: false # fixed to one version
gem "rubocop-rspec" # sets up the linter
end
Run bundle install
when you are ready.
We bundled the rspec-rails gem, which includes rspec for us, but also has an helper to create a few files and folders. Just run:
rails generate rspec:install
You should add spec/examples.txt
to .gitignore
Rubocop's default settings are horribly strict. So I add a custom .rubocop.yml file to the project.
Afterwards run bundle exec rubocop -a
to auto-correct all found offenses.
In my projects I add a taks rake ci
to the Rakefile which automatically runs rubocop and the tests and any other task I run to ensure my project is in a good state.
Rakefile
if %w(development test).include? Rails.env
require "awesome_print"
require "bundler/audit/task"
require "rspec/core/rake_task"
require "rubocop"
Bundler::Audit::Task.new
RSpec::Core::RakeTask.new(:rspec) do |t|
# t.exclude_pattern = "**/{system}/*_spec.rb" # skip system specs
end
namespace :factory_bot do
desc "Verify that all FactoryBot factories are valid"
task lint: :environment do
puts "Building all factories and traits to ensure they are valid"
FactoryBot.lint traits: true, strategy: :build, verbose: true
end
end
desc "Run rubocop"
task rubocop: :environment do
sh "bundle exec rubocop -c .rubocop.yml"
end
desc "Run rubocop with autocorrect"
task rubocopa: :environment do
puts "Obey the autocorrection cop!"
sh "bundle exec rubocop -c .rubocop.yml --auto-correct"
end
desc "Run rubocop and the specs"
task ci: %w(rubocop bundle:audit factory_bot:lint rspec)
end
You use the rails generate ...
command to generate files or a scaffold. Usually this creates more files than I need (or want to have automatically created). Therefore I adapt the generator configuration in
config/application.rb
config.generators do |g|
g.orm :active_record, primary_key_type: :uuid
g.helper false
g.javascripts false
g.stylesheets = false
g.controller_specs false
g.factory_bot false
g.helper_specs false
g.view_specs false
g.system_tests = nil
end
This is an example of a CI configuration for gitlab. Like everything in this setup guide, you have to adapt it to your needs. This example expects the ENV variable HEROKU_PRODUCTION_API_KEY
to be set to push / deploy to production.
.gitlab-ci.yml
image: timbru31/ruby-node # Docker image with Ruby 2.6, Node.js 10, npm 6 and yarn
services:
- postgres:10.1
# Cache the bundled gems to speed up the CI runs by minutes
cache:
key: dtx_admin_ci_cache
paths:
- Gemfile.lock
- vendor/bundle/
untracked: true
variables:
DISABLE_SPRING: 1
RAILS_ENV: test
DB_HOST: postgres
before_script:
- ruby --version
- gem --version
- chmod 755 Gemfile*
- gem install bundler:2.0.1 --no-document
- bundle check || bundle install --without production --jobs $(nproc) --path vendor/bundle --binstubs vendor/bundle/bin
- cp config/database.yml.ci config/database.yml # copy gitlab-ci specific config
- bundle exec rails db:create RAILS_ENV=test
- bundle exec rails db:schema:load RAILS_ENV=test
stages:
- test
- deploy
# enable when JS / CSS or other assets need to be bundled
# - bundle exec webpack
# Optionally add more runners to GitLab-CI
# and extract the tasks to smaller jobs
# which can then be executed in parallel
# (drawback is that the setup will be done for each job)
testing:
stage: test
script:
# run rubocop, bundler:audit, factory_bot:lint and all specs
- bundle exec rake ci
# enable when using selenium_chrome_headless / chromedriver
#
# # install Chrome and chromedriver to run headless system specs
# - chmod +rx ./bin/setup_chrome
# - ./bin/setup_chrome
#
# # run only the system specs
# - bundle exec rspec spec/system
production:
stage: deploy
only:
- master
script:
- curl https://cli-assets.heroku.com/install.sh | sh
- heroku --version
# Use the gem dpl to push code to heroku
- gem install dpl
- dpl --provider=heroku --app=dtx-admin-production --api-key=$HEROKU_PRODUCTION_API_KEY
- heroku run bundle exec rails db:migrate --exit-code --app dtx-admin-production
The database config file that is being copied in the before script could look like this:
config/database.yml.ci
test: &test
adapter: postgresql
encoding: unicode
username: postgres
password: postgres
host: <%= ENV['DB_HOST'] %>
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
database: dtx_admin_test
I prefer to use HAML over Ruby's default ERB. That's why I install it now:
Gemfile
gem "haml-rails", "~> 2.0"
And convert either only the layout file or all existing files to HAML:
rails generate haml:application_layout convert
HAML_RAILS_DELETE_ERB=true rails haml:erb2haml
Whatever your project needs, or you like :-)
One thing I do, is to add my own Settings mechanism to read ENV variables. It also supports default values and local overrides. But that would be too much for this guide.