Ruby on Rails 7 with Bootstrap on Docker Compose 開発環境を簡単に構築する方法

## 対象読者

この記事は「 そろそろ Rails 7 触ってみようか 」と思っていて、普段から Docker を使っている（ あるいは、使ってみたいと思っている ）人などを、主な対象読者としています。

これから開発を始めていく「 スタートライン 」として、参考にしていただければ幸いです。

## 動作環境

適当に「 MacBook Pro (13-inch, M1, 2020) 」に Docker をインストールしただけなので、新しめの Docker 環境さえ整っていれば、基本的にはどこでも起動できるはず。

```
% brew list --cask --versions | grep docker
docker 4.12.0,85629
```

```
% docker -v
Docker version 20.10.17, build 100c701
```

```
% docker-compose -v
Docker Compose version v2.10.2
```

## まず、5つのファイルを作ろう。

実は、Docker の公式ドキュメントに「 クイックスタート 」が（ 現在のところ Rails 5 向けに ）書かれているので、これをベースとして、いい感じに改変していきましょう。

https://docs.docker.com/samples/rails/

ゼロから作成する必要があるのは、作業フォルダと、以下の「 5つのファイル 」だけです。

```
% tree .
.
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── compose.yaml
└── entrypoint.sh
```

### Dockerfile

昔の要件だと Yarn のインストールが足りないようだったので、追加しておきました。

```Dockerfile:Dockerfile
# syntax=docker/dockerfile:1
FROM ruby:3.1.2
RUN apt-get update -qq \
 && apt-get install -y nodejs postgresql-client npm \
 && rm -rf /var/lib/apt/lists/* \
 && npm install --global yarn
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

# Configure the main process to run when running the image
CMD ["rails", "server", "-b", "0.0.0.0"]
```

Ruby のバージョンについては、公式サイトで「 現在の安定版 」などを調べて、適宜、更新しておいてください。

https://www.ruby-lang.org/ja/downloads/

https://hub.docker.com/_/ruby

### entrypoint.sh

Dockerfile の中で出てくるスクリプトですが、この辺は、そのままでも大丈夫そうですね。

```shell:entrypoint.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
```

### Gemfile

```ruby:Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 7.0', '>= 7.0.4'
```

Rails のバージョンについても、公式サイトなどを確認して、最新版から試してみるのがオススメです。

https://guides.rubyonrails.org/

https://rubygems.org/gems/rails

### Gemfile.lock

これは「 空ファイル 」なので、例えば、次のようなコマンドで生成しておいてください。

```
touch Gemfile.lock
```

### compose.yaml

```yaml:compose.yaml
services:
  db:
    image: postgres:14.5-alpine
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: password
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
```

一応、PostgreSQL のバージョンも、固定しておいた方が無難でしょうか。

https://hub.docker.com/_/postgres

長らく `latest` を使っていると、ふいにバージョンを上げてしまって、マウントしているデータとの整合性に問題が生じ、起動できなくなってしまう可能性があります。

## その他のファイルは、自動生成しよう。

さて、上の「 5つのファイル 」の準備ができたら、次のようなコマンドを実行するだけで、その他に必要なファイル群は、自動生成されるはずです。

```
docker-compose run --rm --no-deps web rails new . --force --database=postgresql --css=bootstrap
```

ここで `--css=bootstrap` と付けておくと、いきなり（ おそらく最新版の ）Bootstrap が使える状態で、開発が始められるんだとか。

そして、生成された大量のファイルのうち、コンテナを立ち上げる前に編集しておかなければならないのは、データベースの設定ファイル `config/database.yml` だけ。

```diff_yaml:config/database.yml
...

default: &default
  adapter: postgresql
  encoding: unicode
+ host: db
+ username: postgres
+ password: password
  # For details on connection pooling, see Rails configuration guide
  # https://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

...
```

以上のように、三行ほど追記しておきます。

## あとは、起動するだけ？

それでは、次のコマンドを実行して（ ビルドしつつ ）コンテナを起動しましょう。

```
docker-compose up -d --build
```

ここまでで、起動自体はできているはずですが、接続するデータベースが存在しないとエラーになってしまうので、次のコマンドで作成しておきます。

```
docker-compose run --rm web rails db:create
```

## ブラウザで、ページを表示してみよう！

それでは、`http://localhost:3000/` にアクセスして、ちゃんと Rails が動いているかどうか、確認してみましょう。

次のような「 ウェルカムページ 」が表示されれば、とりあえずは、成功です。
![welcome.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109175/4a5e5e6f-8ddf-a72f-5600-ac962b4fdb93.png)
ただ、これだけだと「 本当に Bootstrap が使えるようになっているのか？ 」までは分からないので、次のコマンドを実行するなりして、まず、適当なページを作成します。

```
docker-compose run --rm web rails generate controller Articles index
```

次に、生成された View ファイル **ではなく**、折角なので、ここでは、あえて `app/views/layouts/application.html.erb` に、それらしい編集を加えておきましょう。

```erb:app/views/layouts/application.html.erb
...

  <body>
    <nav class="navbar navbar-expand-lg bg-light">
      <div class="container-fluid">
        <a class="navbar-brand" href="#">Navbar</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle nav>
          <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">
            <li class="nav-item">
              <a class="nav-link active" aria-current="page" href="#">Home</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">Link</a>
            </li>
            <li class="nav-item dropdown">
              <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
                Dropdown
              </a>
              <ul class="dropdown-menu">
                <li><a class="dropdown-item" href="#">Action</a></li>
                <li><a class="dropdown-item" href="#">Another action</a></li>
                <li><hr class="dropdown-divider"></li>
                <li><a class="dropdown-item" href="#">Something else here</a></li>
              </ul>
            </li>
            <li class="nav-item">
              <a class="nav-link disabled">Disabled</a>
            </li>
          </ul>
          <form class="d-flex" role="search">
            <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
            <button class="btn btn-outline-success" type="submit">Search</button>
          </form>
        </div>
      </div>
    </nav>
    <div class="container-fluid mt-2">
      <%= yield %>
    </div>
  </body>

...
```

例として、適当に「 Navbar 」のサンプルコードをコピペしてみました。

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

これで、`http://localhost:3000/articles/index` にアクセスすると、次のように表示されるでしょうか？
![index.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109175/98753019-78de-c688-5cb7-f45ca37b8821.png)
ちなみに、Bootstrap 等のバージョンについては、`package.json` を確認しておいてください。

すでに「 Bootstrap Icons 」も使えるようになっていると思うので、動作確認も兼ねて、色々と試してみましょう。

https://icons.getbootstrap.com/

## まだ、やり残していることがある。

しかし、実を言うと、上のページの動作環境には、多少なりとも問題が残っています。

例えば、Navbar には「 ドロップダウンメニュー 」が含まれていますが、これ、動きますかね？

反応しないとしたら「 必要な JavaScript が、ちゃんと読み込まれていない 」と思われますが、そもそも、まだコンパイルされていない可能性が高いので、次のコマンドを実行してみてください。

```
docker-compose run --rm web rails assets:precompile
```

これで正常に動作すれば、ひとまず、開発環境の構築は終了です。

本当は、これでもまだ Bootstrap の全ての機能が使えるわけではなかったりもするのですが、まぁ、細かいことは、また別の記事に書いていくことにします。

https://qiita.com/normalsalt/items/0df76cd01847dcc3217e