Skip to content

Instantly share code, notes, and snippets.

@mediafinger
Last active May 15, 2019 15:45
Show Gist options
  • Save mediafinger/d81136ae6200e19f3aed20c67bdfd29b to your computer and use it in GitHub Desktop.
Save mediafinger/d81136ae6200e19f3aed20c67bdfd29b to your computer and use it in GitHub Desktop.
Install Ruby and create a new Rails app with a few custom configurations

Setup Ruby and configure a new Rails app

This is a step by step guide for everyone new to Ruby and Ruby on Rails - or for people who want to create a new Rails app on a new computer. And it is also a reminder for myself to check which gems and settings I like in Rails apps.

Table of contents

Setup Ruby

Install Ruby

Install your Ruby versions with:

Managing Ruby versions

Change you Ruby version with:

It can be setup to change the Ruby version automatically when changing into a directory. Consult the README for the setup steps.

Installing essential gems

Next install the gems bundler and rails:

  • gem install bundler (version 2.x will be installed)
  • gem install rails (the latest version is 5.2.x and will be installed, you might consider starting on 6.0.0.beta3 though)

Both gems are well documented.

Install a database

On macOS I use the PostgresApp to install Postgres / 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. The installation of Postgres is infamous for causing some configuration trouble on macOS

Setup a new Rails app

Learn more about Rails

Rails Guides: https://guides.rubyonrails.org/getting_started.html (don't overlook the 'Guides Index' dropdown in the top right)

Setting up a skeleton for a new Rails app

Example: an app named "lupus" 🐺

This guide assumes you've already installed a current ruby (version 2.6.3 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.

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 lupus
  --database=postgresql \
  --webpack \
  --skip-bundle \
  --skip-spring \
  --skip-test \
  --skip-action-cable \
  --skip-coffee
  • lupus 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 lupus 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.3' > .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 configuration file to the project:

.rubocop.yml

require: rubocop-rspec

AllCops:
  TargetRubyVersion: 2.6
  DisplayCopNames: true
  Exclude:
    - config.ru
    - bin/**
    - db/schema.rb
    - node_modules/**/*
    - vendor/**/*

Capybara/FeatureMethods:
  Enabled: false

Layout/AlignHash:
  Enabled: false

Lint/AmbiguousBlockAssociation:
  Exclude:
    - 'spec/**/*.rb'

Layout/EmptyLineAfterMagicComment:
  Enabled: true

Layout/IndentHash:
  EnforcedStyle: consistent

Layout/LeadingCommentSpace:
  Enabled: false

Layout/MultilineMethodCallIndentation:
  EnforcedStyle: indented
  IndentationWidth: 2

Metrics/AbcSize:
  Max: 25
  Exclude:
    - spec/system/**/*

Metrics/BlockLength:
  Exclude:
    - config.rb
    - config/**/*
    - spec/**/*

Metrics/ClassLength:
  Max: 120

Metrics/CyclomaticComplexity:
  Max: 16

Metrics/LineLength:
  Max: 125

Metrics/MethodLength:
  Max: 20

Metrics/PerceivedComplexity:
  Max: 10

RSpec/AlignLeftLetBrace:
  Enabled: true

RSpec/ContextWording:
  Enabled: false

RSpec/DescribeClass:
  Exclude:
    - 'spec/integration/**/*_spec.rb'
    - 'spec/system/**/*_spec.rb'

RSpec/EmptyExampleGroup:
  Enabled: false

RSpec/ExampleLength:
  Max: 25

RSpec/ExpectChange:
  EnforcedStyle: block

RSpec/EmptyLineAfterFinalLet:
  Enabled: false

RSpec/MultipleExpectations:
  Max: 6

RSpec/NamedSubject:
  Enabled: true

RSpec/NestedGroups:
  Max: 4

RSpec/VerifiedDoubles:
  Enabled: false

Style/BracesAroundHashParameters:
  Enabled: false

Style/Documentation:
  Enabled: false

Style/DoubleNegation:
  Enabled: false

Style/EmptyMethod:
  Enabled: false

Style/FormatString:
  EnforcedStyle: sprintf

Style/ModuleFunction:
  Enabled: false

Style/NumericLiterals:
  MinDigits: 15

Style/PercentLiteralDelimiters:
  PreferredDelimiters:
    default:  ()
    '%':  ()
    '%i': ()
    '%q': ()
    '%Q': ()
    '%r': '{}'
    '%s': ()
    '%w': ()
    '%W': ()
    '%x': ()

Style/PerlBackrefs:
  Enabled: false

Style/RegexpLiteral:
  Enabled: false

Style/Semicolon:
  AllowAsExpressionSeparator: true

Style/StringLiterals:
  EnforcedStyle: double_quotes

Style/SymbolArray:
  Enabled: false

Style/SymbolProc:
  Enabled: false

Style/TrailingCommaInArguments:
  EnforcedStyleForMultiline: comma
  Enabled: false

Style/TrailingCommaInHashLiteral:
  EnforcedStyleForMultiline: comma

Style/TrailingCommaInArrayLiteral:
  EnforcedStyleForMultiline: comma

Style/WordArray:
  EnforcedStyle: percent

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

Abstract HTML markup with HAML

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

Settings / Custom Configuration

One thing I do, is to add my own Settings mechanism to read ENV variables. It also supports default values and local overrides. That would be too much for this guide, but be welcome to keep reading on:

https://github.com/mediafinger/settings

Setup GitLab CI

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: lupus_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=lupus-production --api-key=$HEROKU_PRODUCTION_API_KEY
    - heroku run bundle exec rails db:migrate --exit-code --app lupus-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: lupus_test

Next steps

Whatever your project needs, or you like :-)

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