Skip to content

Instantly share code, notes, and snippets.

@mediafinger
Last active May 17, 2021 08:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mediafinger/4f4d8ca7c783c985e36b3bf2317a43e4 to your computer and use it in GitHub Desktop.
Save mediafinger/4f4d8ca7c783c985e36b3bf2317a43e4 to your computer and use it in GitHub Desktop.
First steps after creating a new Rails app

Setting up a skeleton for a new Rails app

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.

tl;dr

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.

Installing Postgres

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)

Creating the Rails app itself

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 run bundle install myself
  • I'll skip spring because it can waste more time than it saves.
  • I'll skip test because I (like most others) use rspec
  • 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

Initialize a git repo

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.

Fix the ruby-version

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

Cleaning up the Gemfile

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.

Tests with rspec

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

Linting with rubocop

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.

Add rake tasks

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

Rails generators

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

GitLab CI config

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

Abstract HTML markup away

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

Next steps

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment