Skip to content

Instantly share code, notes, and snippets.

@MyklClason
Last active April 26, 2024 18:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save MyklClason/791d6b14606bc56e72eba2995aab8e76 to your computer and use it in GitHub Desktop.
Save MyklClason/791d6b14606bc56e72eba2995aab8e76 to your computer and use it in GitHub Desktop.
AWS Cloud9 Rails Setup Guide (and migration from c9.io) for Rails 7.1

Overview

Warning: This guide intended for Rails 6.1. (It may not be complete)

This is direction for Migration from c9.io to AWS Cloud9 or to simply setup AWS Cloud9 for Rails Development, after you follow the cloud9 migration directions and created a workspace, keeping in mind the Workspace settings Section below.

Be sure you are using the right version of rails. You may need to remove rails and rail-ties and install rails 6.1. Check online for how (search how to reinstall rails with a specific version)

To create a new app in AWS Cloud9, the directions are basically the same, but you need to use rails new (see note below first) first and move all code out of the new folder and into the workspace (root) folder, so you don't need to CD into your project.

Note: When using Rails new it useful to review the rails new -h to see what flags you can use. Since this guide uses postgres, you'll want to use: rails new app_name --database postgresql

Cloud9 Setup

  • Click Environment
  • Add name + Description
  • Click Next
  • Select "No Ingress" EC2 instance
  • Select 2 GB of RAM (or higher)
  • Select Ubuntu Server
  • Click Next
  • Create Environment

Workspace settings

When creating a Cloud9 Environment, use Ubuntu not Amazon Linux

Cloud9 Editor Settings

  • Strip Whitespace
  • Soft tabs (2)

Bashrc + Git + Bash Aliases + Env Vars

Bash Alises: https://gist.github.com/MyklClason/d71a39ace28b9ec9f0ad

Be sure to update the name and email for git.

# Add to bottom of .bashrc file
if [ -f ~/.bash_aliases ]; then
  . ~/.bash_aliases
fi

# Handle issue with nano not being found during git merges and such via using vim.
# Has to be set here as it isn't being saved between terminal windows
git config --global core.editor "vim"

# General git config, be sure to update the name + email
git config --global user.name "<user name>"
git config --global user.email <user email>

# Add custom/modified aliases below here

# Then add profile env vars and project specific aliases and alias overrides below here
export HOST_URL="" # Add host URL 

Close and reopen any open terminals after the above is ran to use the aliases.

Increase disk space

Seems 10GB is never enough, increase to 20GB by following the below.

https://docs.aws.amazon.com/cloud9/latest/user-guide/move-environment.html

Postgres

First run

wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt-get update
sudo apt install postgresql postgresql-contrib libpq-dev

One Liner

wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -; sudo apt-get update; sudo apt install postgresql postgresql-contrib libpq-dev

(The above can cause odd issues with the Y/N if it isn't ran by itself)

Then

# ubuntu User setup
sudo service postgresql start
sudo sudo -u postgres psql
CREATE USER ubuntu SUPERUSER PASSWORD 'password'; # You may want to use something more secure
\q

Redis

sudo apt-get install redis-server

Yarn (Very important, webpacker install will fail if yarn not setup in advance)

npm install --global yarn

Heroku

Update Heroku

huf

If bash aliases are setup, else see bash aliases gist for what the alias is currently mapped to, as it has changed in the past depending on heroku CLI version).

Log into Heroku

heroku login -i

Heroku clone (for existing app)

heroku git:clone -a 

Ruby on Rails (if needed)

Ruby Version

First install the correct version of ruby. Remove the default version if a downgrade is needed.

Update RVM and reload:

rvm get head && rvm reload

Check currently installed rubies against required version. Keep only the required version (unless you have a specific long term need for multiple ruby versions)

rvm list

Remove versions via (unless good reason for it, best to only have a single ruby version installed and set as the default and current):

rvm remove <not required version>

To list installable versions of ruby

rvm list known

To install latest stable version and set as default, use this alias:

rvmlid

Standard Install (ruby 3.0+)

install and use default version

rvm use --default --install <required version>

Install with OpenSSL1.1 Fix (ruby < 3.0)

Newer versions of Ubuntu do not include OpenSSL 1.1 which breaks things in older versions of Ruby. So we need to install an older version of SSL then install the ruby version. Tested with Ruby 2.7.7 and Rails 5.

Sources: https://docs.openiam.com/docs-4.2.1.3/appendix/2-openssl (Dependencies install only) https://deanpcmad.com/2022/installing-older-ruby-versions-on-ubuntu-22-04/ (Compile SSL 1.1.1g, other versions of ruby may need a different version)

# Tested with
ruby '2.7.7'
gem 'rails', '~> 5.2.4', '>= 5.2.4.3'
gem 'pg', '>= 0.18', '< 2.0'
wget https://www.openssl.org/source/openssl-1.1.1g.tar.gz
tar zxvf openssl-1.1.1g.tar.gz

cd openssl-1.1.1g
./config --prefix=$HOME/.openssl/openssl-1.1.1g --openssldir=$HOME/.openssl/openssl-1.1.1g

make
make test

make install

rm -rf ~/.openssl/openssl-1.1.1g/certs
ln -s /etc/ssl/certs ~/.openssl/openssl-1.1.1g/certs

rvm install ruby-2.7.7 --with-openssl-dir=$HOME/.openssl/openssl-1.1.1g

ctw # Close terminal windows and get back to the rails folder

Rails Version

run rails -v to check your current version of rails. If that isn't the version you want:

Remove the current railties (you may need to add -v '5.0.0' at the end) or something if you need to uninstall a specific version.

gem uninstall railties

Install the correct railties, in this case, anything that is at least 6.1.X but less than Rails 7

gem install railties -v '~> 6.1'

Here is an example for an exact version:

gem install railties -v '6.1.4.1'

Once that installs, you should update the gemfile to use the version of rails installed by the railties

Update the gemfile to use the correct version (if needed)

gem rails, '~> 6.1'

Though you should replace the ~> 6.1.0 with the actual version it installs ie:

gem rails, '6.1.4.1'

Rails Setup

For New Rails App

Create a new rails app if needed Add Setup Rails if needed (if not migrating)

https://guides.rubyonrails.org/v6.1/getting_started.html

Use rails new -h to get the options for setup.

For simple rails apps, one can use these options:

  • --database=postgresql for postgres
  • --skip-test if you won't be following TDD or plan to use rspec instead of the built-in test lib.

After rails new has finished running:

  • Move everything from it (including hidden files) to the "environment" folder within Cloud9.
  • Delete the empty app folder
  • Add /.c9 to the gitignore
  • Run gic to perform the initial git commit on the repo.

For Existing Repo

Just make use of git clone. One the repo has been cloned, make sure to move all the files (be sure you have turned on viewing of hidden files in cloud9)

Note: It is possible to git clone from a heroku app, but it is not ideal to rely on this as it will only contain the branch that was pushed to heroku (typically master). This is especially true since GitHub now offers private as well as public repos for free.

Bundler Setup

Setup bundler

gem install bundler
bundle install

Importmaps

Bootstrap

https://dev.to/coorasse/rails-7-bootstrap-5-and-importmaps-without-nodejs-4g8

Todo:

Convert into a generator

Postgres Rails DB Setup

Add the pg gem and setup the database.yml (for new apps, the pg gem is already setup if --database postgresql was used)

https://gist.github.com/MyklClason/338536e05dcb09e093fca3f562d50bbc

Close and reopen any open terminals after the above is ran to use the aliases.

Be sure you have replaced the sqlite gem with the pg gem. Depending on your version of rails, you may need to use a specific version of the pg gem. Research online for which version of the gem for which version of rails.

Then you can setup the Rails database

# Do the postgres setup for cloud9
rake db:create
rake db:migrate
rake db:seed # Optional, may not always be needed or desired. Use caution. For new rails apps, this is safe to run as it does nothing.

You can run them all at once using:

rake db:create db:migrate db:seed

If not already done so, this is a good time to do a yarn install for existing web apps. Otherwise the things will have problems.

Rails Configuration

Add to config/application.rb to disable certain scaffold generation functionality and force SSL. Cloud9 already uses SSL and you'll want SSL reguardless of whether you use a custom domain name or not.

config.force_ssl = true # Force SSL
config.generators do |g|
  g.stylesheets false
  g.jbuilder = false
end

# Set host for each environment via config vars
config.action_mailer.default_url_options = { host: ENV["HOST_URL"] } 

Rails Console

Solves FATAL: Listen error: unable to monitor directories for changes. error if it comes up.

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p

Gitignore

For new rails apps, it is likely enough to just add /.c9 at the bottom of the file.

# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
#   git config --global core.excludesfile '~/.gitignore_global'

# Ignore bundler config.
/.bundle

# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep

# Ignore pidfiles, but keep the directory.
/tmp/pids/*
!/tmp/pids/
!/tmp/pids/.keep

# Ignore uploaded files in development
/storage/*
!/storage/.keep

/node_modules
/yarn-error.log

/public/assets
.byebug_history

# Ignore master key for decrypting credentials and more.
/config/master.key

/public/packs
/public/packs-test
/node_modules
/yarn-error.log
yarn-debug.log*
.yarn-integrity

/config/database.yml
.c9/

Heroku

To simply things, we add a Procfile that will tell heroku to run migrations whenever new code is pushed. Sometimes this may need to be disabled, but it is helpful more often than not.

Add Procfile to the root directory with just this:

release: rake db:migrate

Create app and push to heroku

heroku create <app_name> 
gphm # Push changes

Setup heroku backup (database must exist first)

heroku pg:backups:schedule DATABASE_URL --at '02:00 America/Los_Angeles' 

Devise

Add devise gem

Follow the setup instructions before continuing. Generate devise model

rails generate devise User first_name:string last_name:string role:string

Add application controller level before filters. Skip the before_action for public facing pages.

before_action :authenticate_user!

Commit changes and push to heroku (if using a single heroku enviornment)

gac "Add devise" && gphm

Bootstrap

Add bootstrap gem and follow instructions for stylesheet:

https://github.com/twbs/bootstrap-rubygem

Setup instructions based on: https://rubyyagi.com/how-to-use-bootstrap-and-jquery-in-rails-6-with-webpacker/#installing

Rails 7 Update:

Add bootstrap to your Gemfile:

gem 'bootstrap', '~> 5.1.3'

Import Bootstrap styles in app/assets/stylesheets/application.css.scss (will need to add .scss)

// Custom bootstrap variables must be set or imported before bootstrap.

@import "bootstrap";
yarn add bootstrap jquery popper.js @popperjs/core

Setup the webpack environment, config/webpack/environment.js:

const { environment } = require('@rails/webpacker')
const webpack = require("webpack");

// Add an additional plugin of your choosing : ProvidePlugin
environment.plugins.append(
  "Provide",
  new webpack.ProvidePlugin({
    $: "jquery",
    jQuery: "jquery",
    Popper: ["popper.js", "default"] // for Bootstrap 4
  })
);

module.exports = environment

Add to app/javascript/packs/application.js

import "bootstrap"

Then after the all the .start():

var jQuery = require('jquery')

// include jQuery in global and window scope (so you can access it globally)
// in your web browser, when you type $('.div'), it is actually refering to global.$('.div')
global.$ = global.jQuery = jQuery;
window.$ = window.jQuery = jQuery;

Bootstrap Generators and scaffolding:

Sadly these are bootstrap 3, but they are convient enough to still be useful even if they need adjustments for bootstrap 5.

https://github.com/seyhunak/twitter-bootstrap-rails (Just add the gem don't run the install task)

Add the gem:

gem "twitter-bootstrap-rails"
Add `.old` to `app/views/layout/application.html.erb` to avoid overriding.

Run the following to generate a new bootstrap themed application layout.

rails g bootstrap:layout application

Remove the favicon_link_tag entries as better to use a favicon generator anyway.

Copy anything needed from the original layout to the new layout, replacing old content as needed. (probably just all the ERB besides the "yield")

Move the Navigation to a partial and render it.

<%= render partial: "layouts/application/nav" %>

and replace it with the correct code form the correct bootstrap version documentation page for navbars.

https://getbootstrap.com/docs/5.1/components/navbar/

Sample nav partial:

<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <div class="container-fluid">
    <%= navbar_brand_link_to "Brand" %>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav me-auto mb-2 mb-lg-0">
      </ul>
      <ul class="navbar-nav me-auto mb-2 mb-lg-0">
      </ul>
      <ul class="navbar-nav ml-auto mb-2 mb-lg-0">
        <% if user_signed_in? %>
          <%= navbar_link_to "Log out", destroy_user_session_path, method: :delete %>
        <% else %>
          <%= navbar_link_to "Log in", new_user_session_path %>
        <% end %>
      </ul>
    </div>
  </div>
</nav>

Root Path

Generate pages controller home action

rails g controller pages home

Add root path to routes:

root "pages#home"

If a dedicated home page isn't needed just adjust so it redirects to a specific existing page. Can also add logic so certain users are redirected to specific pages (IE: Dashboard pages).

Business Logic Handling

Mutations (maintained, but no new features expected) or ActiveInteractions works well

Mutations Gem (see below for alternative)

Add the gem:

gem 'mutations'

Add the mutation template (for copy/pasting) at app/mutations/mutation_template.rb

class MutationTemplate < Mutations::Command

  required do
  end

  optional do
  end

  def execute
  end
end

# In a controller action (for instance), you can run it:
def create
end

ActiveInteraction

bundle add active_interaction

Folder Structure

app
  interactions
    domain
      list
      find
      create
      update
        # Add logic for individual fields if needed.
      destroy

Add to app/interactions

Templates:

class ApplicationInteraction < ActiveInteraction::Base
end


class TemplateInteraction < ApplicationInteraction
end

Testing your web app

Rails require hosts to be explictly added, heroku seems to do this automagically, but for cloud9, you'll need to add it manually. Seems better to add it under config/environments/development.rb.

Verify the server runs locally at this point and UI layout looks suitable.

View Component and Lookbook

ViewComponent

Setup gem according to the getting started.

Configuration

Within config/application.rb

config.view_component.generate.sidecar = true
config.view_component.generate.stimulus_controller = true
config.view_component.generate.locale = true
config.view_component.generate.preview = true
config.view_component.view_component_path = "app/views/components"
config.view_component.preview_paths << Rails.root.join("app/views/components")

ApplicationViewComponent

class ApplicationViewComponent < ViewComponentContrib::Base
  extend Dry::Initializer

  include ApplicationHelper
end

Improve Folder Structure

Improve the code organization for views. Lets have it setup like this. Auto-generated views will need to be moved into the appropriate folder.

views
  controllers/ # Views used by controllers
  mailers/ # Views used by Mailers
  layouts/

Add to ApplicationController

append_view_path Rails.root.join("app", "views", "controllers")

Add to ApplicationMailer

append_view_path Rails.root.join("app", "views", "mailers")

Dry Effects

Add to ApplicationController (comment out or exclude if devise not setup)

include Dry::Effects::Handler.Reader(:current_user)

around_action :set_current_user

private

def set_current_user
  # Assuming you have `#current_user` method defined:
  with_current_user(current_user) { yield }
end

Add to ApplicationViewComponent

include Dry::Effects.Reader(:current_user, default: nil)

Resources

https://github.com/allmarkedup/lookbook https://github.com/palkan/view_component-contrib https://evilmartians.com/chronicles/viewcomponent-in-the-wild-supercharging-your-components

Pagination and Filtering

  • See latest version setup instructions for kaminari and ransack

Rubocop, Rspec and Husky

For basic code quality and automated testing, we want to setup Rubocop, Rspec and Husky. Husky makes pre-commit hooks easy to verify things before they get commited. The pre-commit hook can be skipped if needed.

Add gems:

# Development/Test


group :development, :test do
  # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
  gem "debug", platforms: %i[mri mingw x64_mingw]
  
  gem "simplecov"
  gem "simplecov-html"
  gem "simplecov-lcov"

  gem "rubocop-rails"
  gem "rubocop-rspec"
  gem "factory_bot_rails" # https://github.com/thoughtbot/factory_bot
  gem "rails-controller-testing"
  gem "rspec-rails", "~> 6.0.0"

  gem "faker-bot" # Allows for querying faker data

  gem "i18n-tasks", "~> 1.0.12" # Better management of i18n, though doesn't play nice with ViewComponent

# Top Level Gems

gem "faker", "~> 3.0" # Useful for generating test/sample data, so kept at high level rather than dev/test

Setup Rspec

Setup Rubocop

Setup Husky

Potentially useful gists

Potentially useful heroku addons

  • New Relic

StimulusJS (if needed)

https://gist.github.com/MyklClason/09c6f58b9e6d21340582cbd6fbd3b8b8

Code Quality Config

https://gist.github.com/MyklClason/625c53576d8d0d584c2d19ce772542bc

Potentially useful gems

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