# Install

> Sets up a new Rails 8 application with recommended defaults for TDD, authentication, multi-tenancy, and Hotwire. Use when creating a new Rails project, bootstrapping an app, or when user mentions new app, rails new, project setup, or initial setup.

- Skill: `dchuk/install` (Agent Skill)
- Install (CLI): `npx skillmds@latest add dchuk/install`
- Raw SKILL.md: https://api.skillmd.com/api/skills/dchuk/install/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: dchuk (https://skillmd.com/u/dchuk)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/dchuk/install

---


# Rails 8 Application Setup

## Overview

Bootstrap a new Rails 8 application with production-ready defaults:
- Rails 8 with Solid Queue, Solid Cache, Solid Cable
- Built-in authentication (has_secure_password)
- Multi-tenancy via Account scoping
- Hotwire (Turbo + Stimulus)
- Minitest + fixtures for testing
- Tailwind CSS for styling

## Quick Start

```bash
rails new myapp --css tailwind --database sqlite3
cd myapp
```

## Post-Generation Setup

### 1. Generate Authentication

```bash
bin/rails generate authentication
bin/rails db:migrate
```

This creates:
- `User` model with `email_address` and `password_digest`
- `Session` model with token-based sessions
- `Current` model (CurrentAttributes)
- `Authentication` concern
- `SessionsController`

### 2. Add Account Model (Multi-Tenancy)

```bash
bin/rails generate model Account name:string
bin/rails generate migration AddAccountToUsers account:references
bin/rails db:migrate
```

```ruby
# app/models/account.rb
class Account < ApplicationRecord
  has_many :users, dependent: :destroy

  validates :name, presence: true
end
```

```ruby
# app/models/user.rb
class User < ApplicationRecord
  has_secure_password
  has_many :sessions, dependent: :destroy
  belongs_to :account

  normalizes :email_address, with: ->(e) { e.strip.downcase }

  validates :email_address, presence: true,
    uniqueness: true,
    format: { with: URI::MailTo::EMAIL_REGEXP }
end
```

### 3. Add Current Account

```ruby
# app/models/current.rb
class Current < ActiveSupport::CurrentAttributes
  attribute :session
  attribute :user_agent, :ip_address

  delegate :user, to: :session, allow_nil: true

  def account
    user&.account
  end
end
```

### 4. Configure Test Helper

```ruby
# test/test_helper.rb
ENV["RAILS_ENV"] ||= "test"
require_relative "../config/environment"
require "rails/test_help"

module ActiveSupport
  class TestCase
    parallelize(workers: :number_of_processors)
    fixtures :all

    private

    def sign_in(user)
      session = user.sessions.create!
      cookies.signed.permanent[:session_token] = {
        value: session.token,
        httponly: true
      }
      Current.session = session
    end

    def sign_out
      Current.session&.destroy
      cookies.delete(:session_token)
    end
  end
end
```

### 5. Create Fixtures

```yaml
# test/fixtures/accounts.yml
one:
  name: Acme Corp

other:
  name: Other Corp
```

```yaml
# test/fixtures/users.yml
one:
  email_address: user@example.com
  password_digest: <%= BCrypt::Password.create("password123") %>
  account: one

admin:
  email_address: admin@example.com
  password_digest: <%= BCrypt::Password.create("password123") %>
  account: one

other_account:
  email_address: other@example.com
  password_digest: <%= BCrypt::Password.create("password123") %>
  account: other
```

```yaml
# test/fixtures/sessions.yml
one:
  user: one
  token: <%= SecureRandom.urlsafe_base64(32) %>
```

### 6. Configure Solid Queue

```yaml
# config/queue.yml (already generated by Rails 8)
default: &default
  dispatchers:
    - polling_interval: 1
      batch_size: 500
  workers:
    - queues: "*"
      threads: 3
      processes: 1
      polling_interval: 0.1
```

```ruby
# config/environments/production.rb
config.active_job.queue_adapter = :solid_queue
config.solid_queue.connects_to = { database: { writing: :queue } }
```

### 7. Configure Solid Cache

```ruby
# config/environments/production.rb
config.cache_store = :solid_cache_store
config.solid_cache.connects_to = { database: { writing: :cache } }
```

### 8. Add Development Gems

```ruby
# Gemfile
group :development, :test do
  gem "debug", platforms: %i[mri windows], require: "debug/prelude"
  gem "brakeman", require: false
  gem "rubocop-rails-omakase", require: false
  gem "bullet"
end
```

```bash
bundle install
```

### 9. Configure Bullet (N+1 Detection)

```ruby
# config/environments/development.rb
config.after_initialize do
  Bullet.enable = true
  Bullet.alert = true
  Bullet.bullet_logger = true
  Bullet.console = true
  Bullet.rails_logger = true
end

# config/environments/test.rb
config.after_initialize do
  Bullet.enable = true
  Bullet.raise = true
end
```

### 10. Add Application Layout Defaults

```erb
<%# app/views/layouts/application.html.erb %>
<!DOCTYPE html>
<html>
  <head>
    <title><%= content_for(:title) || "MyApp" %></title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>
  </head>
  <body class="min-h-screen bg-white">
    <div id="flash">
      <% flash.each do |type, message| %>
        <div class="flash flash-<%= type %>"><%= message %></div>
      <% end %>
    </div>

    <main class="container mx-auto px-4 py-8">
      <%= yield %>
    </main>
  </body>
</html>
```

## Verification

Run the test suite to confirm everything is wired up:

```bash
bin/rails test
```

### Smoke Test

```ruby
# test/models/user_test.rb
require "test_helper"

class UserTest < ActiveSupport::TestCase
  test "fixture user is valid" do
    user = users(:one)
    assert user.valid?
  end

  test "requires email_address" do
    user = User.new(password: "password123", account: accounts(:one))
    assert_not user.valid?
    assert user.errors[:email_address].any?
  end

  test "requires unique email_address" do
    existing = users(:one)
    user = User.new(
      email_address: existing.email_address,
      password: "password123",
      account: accounts(:one)
    )
    assert_not user.valid?
    assert user.errors[:email_address].any?
  end
end
```

```ruby
# test/models/account_test.rb
require "test_helper"

class AccountTest < ActiveSupport::TestCase
  test "fixture account is valid" do
    account = accounts(:one)
    assert account.valid?
  end

  test "requires name" do
    account = Account.new
    assert_not account.valid?
    assert account.errors[:name].any?
  end
end
```

## Directory Structure After Setup

```
app/
├── controllers/
│   ├── application_controller.rb
│   ├── concerns/
│   │   └── authentication.rb
│   └── sessions_controller.rb
├── models/
│   ├── account.rb
│   ├── current.rb
│   ├── session.rb
│   └── user.rb
├── views/
│   └── layouts/
│       └── application.html.erb
config/
├── database.yml
├── queue.yml
├── cable.yml
└── environments/
    ├── development.rb
    ├── production.rb
    └── test.rb
test/
├── test_helper.rb
├── fixtures/
│   ├── accounts.yml
│   ├── users.yml
│   └── sessions.yml
└── models/
    ├── user_test.rb
    └── account_test.rb
```

## Rails 8 Defaults

Rails 8 ships with these defaults already configured:
- **Solid Queue** - Database-backed Active Job backend (replaces Redis/Sidekiq)
- **Solid Cache** - Database-backed cache store (replaces Redis/Memcached)
- **Solid Cable** - Database-backed Action Cable adapter
- **Propshaft** - Asset pipeline (replaces Sprockets)
- **Import Maps** - JavaScript without bundling
- **Kamal** - Deployment
- **Thruster** - HTTP/2 proxy with asset caching

## Checklist

- [ ] Rails app generated
- [ ] Authentication generated and migrated
- [ ] Account model created (multi-tenancy)
- [ ] Current model configured with account
- [ ] Test helper with sign_in/sign_out
- [ ] Fixtures created (accounts, users, sessions)
- [ ] Solid Queue configured
- [ ] Solid Cache configured
- [ ] Bullet configured for N+1 detection
- [ ] Smoke tests pass
- [ ] All tests GREEN

