- 投稿日:2020-08-06T22:49:50+09:00
GitHubでMiniMagickの脆弱性アラートが出た際の解決方法
現象
RailsアプリをGitHubにプッシュした所、mini_magickに関する脆弱性のアラームメールが届いた。
解決までの備忘録として残しておく。問題と原因
mini_magickのバージョンが古く、フェッチされたリモートイメージファイル名がリモートコマンドの実行を引き起こす可能性があるとのこと。
解決するには、バージョンをアップグレードすれば良さそう。GemFileを編集する
Gemfilegem 'mini_magick', '3.8.0'現在のMiniMagickのバージョンは、3.8だったので
アラートにて提示されているように4.9.4以降をインストールするように修正する。Gemfilegem 'mini_magick', '>= 4.9.4'上記のように編集することで、4.9.4以降にアップグレードされるはず。
bundle installする
bundle installこれでバージョンが変わっているはずなので、動作確認をして問題なければOK。
あとは、プッシュすればアラートが消えてました!
- 投稿日:2020-08-06T22:44:30+09:00
【Rails】アプリ開発の流れ③【検索機能】
前回は
CRUD機能の基本を作成しました。
https://qiita.com/ksyantaro/private/a76bef5f718d7e2c2e8b目次
- 検索機能
検索機能
- データベースに保存されている情報の中から、特定の条件に一致するデータを取得する。
- 参照機能(Read)と組み合わせることが多い。
- SQLにおけるWHERE指定のSELECT。
- 一般的に一覧画面に検索フォームを実装する。
- 検索条件をリセットするボタンを作るといい。
gem 'ransack'を使って実装をしていくこともできる
'ransack'は、少ないコードで複雑な検索機能を作成することができる。
【参考記事】
https://www.sejuku.net/blog/100824今回は手作りフォームで作成
【参考記事】
https://qiita.com/harashoo/items/6c880f8f99e13ed4878f
https://techacademy.jp/magazine/22330検索フォームの実装
index.html.haml= form_with url: blogs_path, method: :get, local: true do |f| = f.text_field :search = f.submit 'Search'コントローラの設定
blogs_controller.rbdef index @blogs = Blog.all @blogs = @blogs.where('title LIKE ?', "%#{params[:search]}%") if params[:search].present? endこの時点でタイトルでの検索自体はできる。
一覧表示ページの検索周りの調整
index.html.haml%h3 タイトル検索 = form_with url: blogs_path, method: :get, local: true do |f| = f.text_field :search = f.submit 'Search', name: nil = link_to "検索終了", blogs_path少し使いやすいように見た目を変えた。
まとめ
ransackを使わずとも単純な検索機能であれば作成できた。
もっと複雑な検索機能はransackに頼ることになるのでしょうか?
- 投稿日:2020-08-06T19:24:39+09:00
Docker 公式ドキュメントの Rails Quickstart 完全解説
Docker の公式ドキュメントに Rails の Quickstart があります:
Quickstart: Compose and Rails | Docker Documentationこちらのドキュメントを参考に Rails を始める方が増えており、
Tetatail で結構な件数の質問に回答しました。この Quickstartは説明が不十分だったり間違いがあったりで
Rails に慣れてないと行間を読んで正しい手順を踏むことは難しいでしょう。この記事で指摘している Quickstart の間違いなどは、すでに GitHub 上で PR を投稿しています。
Yukihiko shinoda/update quickstart rails by yukihiko-shinoda · Pull Request #11215 · docker/docker.github.ioここで Rails の Quickstart についてまとめて解説しておきます。
この Quickstart を実施するために必要なツール
この解説を理解するために必要な基礎知識
この Quickstart を理解するためには、次の基礎知識が必要です:
- エディターでテキストファイルを編集可能
- Docker のコマンドを使ってサービスを実行する程度の基礎知識
- Docker Compose の基礎知識
理解するために必要ないツール・知識
- Rails チュートリアル
- Rails チュートリアルで解説されていることと、この Quickstart で得られる知識は、大部分が別の内容です
- この Quickstart を理解する上で必要な Rails に関する知識はこの記事内で解説します
この Quickstart は何をするためのものなの?
この Quickstart ガイドでは、Docker Compose を使用して Rails / PostgreSQL アプリを設定および実行する方法を理解します。
この Quickstart はどんなことをしているの?
この Quickstart は次の手順を実施します:
- Define the project
- Docker イメージのビルドに必要なファイルを作成します
- Build the project
- 手順 1. で準備したファイルをもとに Rails の実行環境を起動して Rails を動かし新規 Rails アプリケーションを作成します
- このとき自動的に Docker イメージがビルドされます
- 新規作成した Rails アプリケーションを使い Docker イメージをビルドし直します
- Connect the database
- Rails の実行環境とデータベースのサービスを一度に起動します
- View the Rails welcome page!
- ブラウザで Rails の welcome page が表示されることを確認します
この Quickstart が理解しづらい原因
この Quickstart は手順 1. Define the project で準備するファイルやその内容が
相当な量であるにも関わらず、それらの目的についての解説が不十分であるためです。
- 上記の手順 1. Define the project で準備するファイルの内容を最初からすべて理解することは難しいです
- 準備するファイルの内容が次の目的を兼ねる内容になっており、どちらの目的のための内容なのか分かりづらいため
- 手順 2. Build the project で行う新規 Rails アプリケーション作成のための内容
- 最終的な Rails アプリケーション実行のための内容
- 手順 2. Build the project で Rails の実行環境のためのイメージを再度ビルドし直しています
- 手順 2. のコマンドが誤っています
Dockerfile
に不要な内容が含まれていますdocker-compose.yml
- 誤っています
- 不要な内容が含まれています
以降に、各手順で注意すべきポイントを解説します。
各手順の解説
Define the project
この手順では、Docker イメージのビルドに必要なファイルを作成します。
この手順を終えると、プロジェクトのディレクトリー内には次のようなファイルがあるはずです:
project/ +---docker-compose.yml +---Dockerfile +---entrypoint.sh +---Gemfile +---Gemfile.lockここで、初めてこの Quickstart を実施する人は
次の理由により混乱してしまうことでしょう:
- ファイルの内容が誤っていたり、不要な内容が含まれているため
- ここで準備するファイルやその内容が相当な量であるにも関わらず、それらの目的についての解説が不十分であるため
そこで、この記事では
Dockerfile
とdocker-compose.yml
の内容を次のように修正します:DockerfileFROM ruby:2.5 RUN apt-get update -qq && apt-get install -y nodejs RUN mkdir /myapp WORKDIR /myapp COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock RUN bundle install COPY . /myapp # 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"] # Start the main process. CMD ["rails", "server", "-b", "0.0.0.0"]docker-compose.ymlversion: '3.8' services: db: image: postgres environment: POSTGRES_PASSWORD: password web: build: . volumes: - .:/myapp ports: - "3000:3000" depends_on: - db各ファイルの目的の整理
ここで準備するファイルは次の 2 つの目的のためです:
- 手順 2. Build the project で行う新規 Rails アプリケーション作成のため
- 手順 3. Connect the database で行う最終的な Rails アプリケーション実行のため
どのファイルがどの目的のためのファイルなのかは、次の通りです:
ファイル 目的 docker-compose.yml 手順 2, 3. Dockerfile 手順 2, 3. entrypoint.sh 手順 3. Gemfile 手順 2. Gemfile.lock 手順 2. まず、手順 2. のために必要なファイルだけを準備できないの?
手順 2. のために必要なファイルだけを準備すると、次のようになります:
Dockerfile
手順 2. で必要な処理以外をコメントアウトします:
DockerfileFROM ruby:2.5 # RUN apt-get update -qq && apt-get install -y nodejs RUN mkdir /myapp WORKDIR /myapp COPY Gemfile /myapp/Gemfile # COPY Gemfile.lock /myapp/Gemfile.lock RUN bundle install # COPY . /myapp # 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"] # # Start the main process. # CMD ["rails", "server", "-b", "0.0.0.0"]
Gemfile
をCOPY
してbundle install
を実行する理由この後の手順 2. では
rails new
というコマンドを使い、
新規 Rails アプリケーションを作成します。
rails new
コマンドを使うためには、Rails をインストールする必要があります。
Rails は「Gem パッケージ」という形式で配布されています。
「Gem パッケージ」とは、Ruby のプログラムを配布するための形式です。参考: パッケージ - 意味・説明・解説 : ASCII.jpデジタル用語辞典
Gem パッケージをインストールするためには、
bundler
という、Gem パッケージを一度にインストールするためのツールの
bundle install
コマンドを使います。
bundle install
コマンドはGemfile
というファイルを参照してパッケージをインストールします。ここで出てきた命令一覧:
- RUN | Dockerfile reference | Docker Documentation
- COPY | Dockerfile reference | Docker Documentation
/myapp
を新規作成してWORKDIR
に指定する理由ここで、
/myapp
ディレクトリーを作成して作業ディレクトリーとして指定しています。
これは、ベースイメージとして指定されているruby:2.5
は、規定の作業ディレクトリーである/
には
大切なディレクトリーがたくさんあり、
Rails アプリケーションのコードを展開したり、後述する「bind
マウント」を行うと
様々な不具合が起きるためです。ベースイメージの作業ディレクトリーを調べる方法は次の通りです:
FROM ruby:2.5 RUN pwd$ docker-compose build db uses an image, skipping Building web Step 1/2 : FROM ruby:2.5 ---> a5c07a6f3bb6 Step 2/2 : RUN pwd ---> Running in 43f0381dd652 /参考: Answer: Is it possible to show the `WORKDIR` when building a docker image?
ここで出てきた命令一覧:
- WORKDIR | Dockerfile reference | Docker Documentation
- FROM | Dockerfile reference | Docker Documentation
Gemfile
Gemfile
は Quickstart で提示されている内容をそのまま準備します:Gemfilesource 'https://rubygems.org' gem 'rails', '~>5'
Gemfile
には、次のような情報を記述します:
- Gem パッケージをどこからダウンロードしてインストールするか
- インストールする Gem パッケージの一覧
この場合は標準の Gem パッケージ配信サーバーである https://rubygems.org から
Rails のバージョン 5 をインストールします。参考: Bundler: The best way to manage a Ruby application's gems
docker-compose.yml
続いて、
docker-compose.yml
です。手順 2. で必要な処理以外をコメントアウトします:
docker-compose.ymlversion: '3' services: # db: # image: postgres # environment: # POSTGRES_PASSWORD: password web: build: . volumes: - .:/myapp # ports: # - "3000:3000" # depends_on: # - dbbuild 命令の解説
db
サービスはここでは必要ありませんので、コメントアウトしました。
また、web
サービス内で手順 2. で必要な設定はbuild
とvolume
のみです。
build
は、先ほど作成したDockerfile
とGemfile
を使ってイメージをビルドするために必要です。
build: .
という指定は、
このweb
サービスのコンテナーのもととなるイメージとして、
docker-compose.yml
があるディレクトリーに存在するファイルを
コンテキストとしてビルドしたイメージを使うことになります。参考: build | Compose file version 3 reference | Docker Documentation
volumes 命令の解説
volumes
は、この設定がないと、
手順 2. でrails new
コマンドで作成する新規 Rails アプリケーションが、ホスト側のディレクトリーに残りません。
- .:/myapp
というvolumes
の設定は
ホスト側のdocker-compose.yml
があるディレクトリーをコンテナー内のディレクトリツリーの/myapp
に関連付けます。
こうすることで、手順 2. で実行するrails new
コマンドで作成された新規 Rails アプリケーションが
ホスト側のディレクトリーに出力されるようになります。このような volume の設定を
bind
といいます。参考:
Use bind mounts | Docker Documentation
volumes | Compose file version 3 reference | Docker Documentationまとめ
手順 2. のために必要なファイルだけを準備すると、次のようになります:
project/ +---docker-compose.yml +---Dockerfile +---Gemfile
Dockerfile
の中で、手順 2. に不要なコードをコメントアウトしたので
Gemfile.lock
は必要なくなりました。Build the project
新規 Rails アプリケーションの作成
冒頭で実行する次のコマンドについて解説します:
docker-compose run web rails new . --force --no-deps --database=postgresql
このコマンドは間違っており、正しくは次のコマンドです:
docker-compose run --no-deps web rails new . --force --database=postgresql
上記のコマンドは
docker-compose --no-deps run web
とrails new . --force --database=postgresql
に分けて考えます。
docker-compose --no-deps run web
上記のコマンドは
docker-compose.yml
で定義したweb
サービスのコンテナーを起動して実行します。
docker run
コマンドの書式は次の通りです:docker-compose run [options] [-v VOLUME...] [-p PORT...] [-e KEY=VAL...] [-l KEY=VALUE...] SERVICE [COMMAND] [ARGS...]
--no-deps
: Compose ファイル内のdepends_on
などで関係づけられたサービスを開始しませんつまり、
--no-deps
はdocker-compose.yml
内のdb
サービスのコンテナーを起動しないための設定です。
(この時点ではデータベースは必要ないため)
docker-compose run
コマンドは、
コンテナーを起動するためのイメージがまだビルドされていなければ、自動的にビルドを行います。
Dockerfile
内で、コンテナーを実行したときのCMD
が定義されていますが、
このトレーニングのようにdocker-compose run
コマンドでサービス名の後にコマンドを記述した場合は
CMD
の内容は無視され、代わりにコマンドラインから入力した内容が実行されます。
rails new . --force --database=postgresql
このコマンドは新規 Rails アプリケーションを作成します。
rails new
コマンドの書式は次の通りです:rails new APP_PATH [options]
-f
, [--force
]: すでに存在するファイルを上書きします
-d
, [--database=DATABASE
] 選択したデータベース向けに初期設定します
選択肢:mysql
/postgresql
/sqlite3
/oracle
/frontbase
/ibm_db
/sqlserver
/jdbcmysql
/jdbcsqlite3
/jdbcpostgresql
/jdbc
省略時:sqlite3
このコマンドによって
Gemfile
を新規 Rails アプリケーション用の内容に上書きするので
--force
オプションがない場合、Gemfile
を上書きするかどうかの確認が表示されます。イメージの再ビルド
手順 1. で、この解説記事に従って
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 "$@"また、
Dockerfile
の一部コメントアウトしていた部分を、次のようにコメントインします:DockerfileFROM ruby:2.5 RUN apt-get update -qq && apt-get install -y nodejs RUN mkdir /myapp WORKDIR /myapp COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock RUN bundle install # COPY . /myapp # 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"] # Start the main process. CMD ["rails", "server", "-b", "0.0.0.0"]まだコメントアウトしたままの命令がありますが、
これらは手順 4. を終えた後、Rails アプリケーションのイメージを作成するときに必要となる命令です。
手順 4. を終えた後で解説します。次のコマンドを実行すると、イメージが再ビルドされます:
docker-compose build
Dockerfile
の解説
ENTRYPOINT
に Shell Script を設定しCMD
でコマンドを指定する理由Rails Server の二重起動を防ぐための排他ファイルである
server.pid
が
何らかの原因で残ってしまった場合を想定して、
コンテナー起動時、Rails Server を起動する前に
entrypoint.sh
内でserver.pid
を削除する処理を実行しています。ここで重要なのは、
ENTRYPOINT
とCMD
命令を使った Dockerfile のデザインパターンで
イメージからコンテナーを起動したときの規定の動作を定義していることです。
ENTRYPOINT
とCMD
を両方とも配列形式で与えると、すべての配列の要素を並べたコマンドが実行されます。参考: Understand how CMD and ENTRYPOINT interact | Dockerfile reference | Docker Documentation
この例のように、
ENTRYPOINT
に Shell Script を設定し、
その Shell Script のコード内の最後でexec "$@"
を呼び出す設計は
Dockerfile のデザインパターンとして広く使われています。
憶えておいて損はありません。こうすることで、
CMD
で指定したコマンドを実行する前に何らかの初期化処理を実行することができるのです。参考:
- Docker - How to use an entrypoint script to initialize container data at runtime
- Exec form ENTRYPOINT example | Dockerfile reference | Docker Documentation
- Bourne Shell Builtins (Bash Reference Manual)
- Unix / Linux - Special Variables - Tutorialspoint
ここで出てきた命令一覧:
- CMD | Dockerfile reference | Docker Documentation
- ENTRYPOINT | Dockerfile reference | Docker Documentation
bundle install
の前にGemfile.lock
をCOPY
する理由このようにしないと、イメージを再ビルドしたときに、
Rails アプリケーションが開発時とは異なる動作や不具合を起こす可能性があります。
開発時とは異なるバージョンの Gem パッケージを使って動作することになる可能性があるためです。Gemfile と Gemfile.lock は、どちらもインストールする Gem パッケージを管理するためのファイルですが、
それぞれ次のように扱います:
Gemfile
- 開発者が目的に合わせて内容を編集します
- プロジェクトが直接呼び出す Gem パッケージを定義します
- 特別な理由がない限りバージョンを固定しません
Gemfile.lock
- 開発者が直接編集せず、
Gemfile
の内容をもとにコマンドでコンパイルして更新します- プロジェクトが直接呼び出さない Gem パッケージも含めた、すべての必要な Gem パッケージが定義されます
- すべてのパッケージがバージョンまで固定されています
bundle install
コマンドを実行したとき、Gemfile.lock
がなく、Gemfile
のみが存在すると、
Gemfile
で指定されている Gem パッケージの最新バージョンがインストールされます。
Gemfile.lock
が存在し、Gemfile
に特に更新がなければ
Gemfile.lock
の内容に従って、開発時と同じバージョンの Gem パッケージがインストールされます。参考:
- Summary | Bundler: The best way to manage a Ruby application's gems
- bundle install | Bundler: The best way to manage a Ruby application's gems
nodejs
をインストールする理由Rails は JavaScript 実行環境である Node.JS を利用しており、
nodejs
をインストールしていないと、Rails Server 実行時に次のようなエラーが発生します:web_1 | /usr/local/bundle/gems/execjs-2.7.0/lib/execjs/runtimes.rb:58:in `autodetect': Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)
どうして再ビルドが必要なの?
rails new
コマンドで上書きされたGemfile
には、
Rails 本体の他にも Rails アプリケーションを動作させるために必要な多くのパッケージが記載されています。しかし、先ほどの
docker-compose run
コマンドでビルドされたイメージには、
Rails 本体しかインストールされていません。
これは、docker-compose run
コマンドでビルドを行ったときのGemfile
には Rails しか記述されていなかったためです。なお、ここで次のような記述があります:
This, and changes to the Gemfile or the Dockerfile, should be the only times you’ll need to rebuild.
この意味についてはすべての手順を終えた後で解説しますので、
学習効率を考慮して、ひとまず次の手順に進むことをお奨めします。Connect the database
ここでは 2 つの設定を行っています:
- Rails アプリケーションのデータベース接続設定を
db
サービスに接続できるように変更db
サービスに Rails アプリケーションが利用するデータベースを新規作成Rails アプリケーションのデータベース接続設定を
db
サービスに接続できるように変更手順 2. Build the project で生成された
config/database.yml
のdefault: &default
に次の要素を追加します:config/database.ymlhost: db username: postgres password: passwordこの設定を行わずに
docker-compose up
を実行しても、ブラウザーでアクセスすると次のようなエラーが表示されます:PG::ConnectionBad could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?このエラーメッセージは、Rails アプリケーションが
ローカルに PostgreSQL が起動している前提で接続しようとして失敗していることを示しています。この場合、
config/database.yml
を更新してブラウザーを再読み込みするだけではエラーは解消しません。
これは、Rails がデータベース接続設定を読み込むのは Rails Server の起動時であるためです。
データベース接続設定を読み込み直すためには、
ターミナルでCtrl + C
を入力してコンテナーを停止し、docker-compose up
を再度実行します。
db
サービスに Rails アプリケーションが利用するデータベースを新規作成
docker-compose.yml
があるディレクトリーでもう 1 つターミナルを開き、次のコマンドを実行します:docker-compose run web rake db:create
この設定を行わずに
docker-compose up
を実行しても、ブラウザーでアクセスすると次のようなエラーが表示されます:ActiveRecord::NoDatabaseError FATAL: database "myapp_development" does not existView the Rails welcome page!
次のような画面が表示されましたでしょうか?:
この後の開発フローについて
Stop the application
この Quickstart では、アプリケーションを停止するときは、docker-compose down の利用が奨められています。
この記事に従ってdocker-compose.yml
を修正した場合、
docker-compose down
を実行すると、データベースの内容にアクセスできなくなります。データベースの内容を保持したいときは、
docker-compose up
を行ったターミナルでCtrl + C
を入力するか、
別のターミナルで docker-compose stop を実行すると、次に起動したときもデータベースの内容は保持されます。基本的には、開発のためのデータを保持する必要がある場合は PC 内に残すのではなく、
factory_bot をはじめとする
データベースフィクスチャー
を定義するための Gem パッケージを使ってコードに残します。
データの再利用の必要性を感じたら、コードに残す習慣をつけましょう。Rebuild the application
ここでは、イメージの再ビルドが必要な場合について述べられています。
ここで述べられている「イメージの再ビルドが必要な場合」とは、
「開発者が開発中に再ビルドを行うこと」を指しており、
「本番環境にデプロイするためのイメージをビルドすること」は指していません。このことを理解するために、まずは「本番環境にデプロイするためのイメージをビルドすること」を考えてみます。
本番環境にデプロイするためのイメージのビルド
手順 1. Define the project で
Dockerfile
コメントアウトしていた場合は、COPY . /myapp
をコメントインします:DockerfileFROM ruby:2.5 RUN apt-get update -qq && apt-get install -y nodejs RUN mkdir /myapp WORKDIR /myapp COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock RUN bundle install COPY . /myapp # 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"] # Start the main process. CMD ["rails", "server", "-b", "0.0.0.0"]
COPY . /myapp
は Rails アプリケーションのコードをビルドしたイメージに含めます。
本番環境では GitHub などのバージョン管理リポジトリーからコードをデプロイするのではなく、
Docker Hub などのDocker レジストリー
から事前にビルドしたイメージをデプロイすることを想定しています。参考: Docker Registry | Docker Documentation
Gemfile
,Gemfile.lock
と一緒に一度にCOPY
してはいけないの?
Gemfile
,Gemfile.lock
とCOPY
を分けているのは、bundle install
に時間がかかるためです。Docker イメージのビルドは、命令毎に結果がキャッシュされるので、
変更の可能性が低い命令、実行に時間がかかる命令は
なるべくDockerfile
の上に記述した方が再ビルド時の効率が良くなります。
COPY . /myapp
以降の命令はそれほどビルドに時間がかかりません。
もし、/myapp
をGemfile
,Gemfile.lock
と一緒に一度にCOPY
していた場合、
COPY . /myapp
以降の命令を再ビルドしたいときであっても、
Rails アプリケーションのコードを更新しているとbundle install
が実行されることになり
ビルドに時間がかかってしまいます。開発者が開発中に再ビルド
Quickstart では、手順 2. Build the project で
この Docker を使った Rails 開発のワークフローに関する重要なことが述べられていまました:This, and changes to the Gemfile or the Dockerfile, should be the only times you’ll need to rebuild.
これは、日本語訳すると、大体次のような意味です:
このときと、
Gemfile
もしくはDockerfile
を変更したときだけが、再ビルドが必要なタイミングとなるはずです。これは、
docker-compose.yml
のvolume
で/myapp
をホストからbind
マウントしているので
開発のためにコンテナーを起動したとき、イメージに含めた Rails アプリケーションのコードではなく
ホスト側の最新の Rails アプリケーションのコードが実行されるためです。Q & A
Docker 公式ドキュメントの Quickstart に関する質問
rails new
コマンドでファイルが生成されたログが出力されるのに、実際にファイルが生成されませんWindows 10 でバージョンが 1709 までだと、
Docker Compose で相対パスを指定したときに想定通りにマウントされない不具合があるようです。
Windows Update の適用をおすすめします。
Ruby on Rails - dockerでRuby on Railsコンテナ起動時、Could not find public_suffix-4.0.5 in any of the sources エラー|teratailディレクトリーの名前を
myapp
以外に変更するとrails new
コマンド実行時にエラーが発生します正確にすべてのディレクトリー名を変更すればエラーは発生しません。
どこかで書き換えを誤っていないか確認しましょう。
Ruby on Rails - ローカルディレクトリにrails newをしてもファイルが存在しない|teratailDocker と Rails による Web サービス実行に関する質問
エラーメッセージの意味が何を指しているのかわかりません
複数のサービスを起動するときはコンテキストとなるディレクトリーを分けて設計していますか?
コンテキストとなるディレクトリーを分けると、原因の範囲が狭まり調査しやすくなります。
Ruby on Rails - Docker + Railsの No such file or directory エラーの解決方法について|teratailパッケージが足りないエラーが表示されるので gem install しても直りません
Docker が動いているコンピューターに gem をインストールしていませんか?
gem
のインストールはコンテナ内で行う必要があります。
Ruby on Rails - docker-compose upがうまくいかない|terataildb サービス以外のデータベースに接続したいです
Rails の
database.yml
と環境変数でデータベース接続情報を変更して起動します。
MySQL - [Docker, Rails, Nginx, MySQL, EC2]docker-compose run app rails db:createするとエラーが出る。|teratailmulti-stage builds にしたら bundle: not found でイメージがビルドできなくなりました
ベースイメージが Ruby のイメージではないステージで
ruby
やbundler
を使おうとしていませんか?
Ruby on Rails - "docker-compose build"実行時のエラー "Service 'web' failed to build: The command '/bin/sh -c bundle instal"|teratailGemfile はあるのに bundle install すると Could not locate Gemfile というエラーメッセージが表示されます
コマンドは Docker が動いているコンピューターで実行しているか、
それともコンテナの中で実行しているのかを確認し、
そこにGemfile
があるかを確認しましょう
GitHub - Dockerを用いたローカル環境構築で、Could not locate Gemfileが表示される|teratailDockerで起動したサーバーにlocalhost:3000でアクセスすると「このページは動作していません。」と表示されます
Docker で起動した web サービスのログを確認して、ログが増えるかどうかを確認します。
増えなければ、ファイアウォールの問題などの可能性があります。
MySQL - dockerで起動したサーバーにlocalhost:3000でアクセスしようとするが「このページは動作していません。」と返ってくる|teratail
- 投稿日:2020-08-06T19:01:51+09:00
【webpacker】Rails6.0でのBootstrap, jQueryの導入方法まとめ
RailsへのBootstrapとjQuery導入方法は、
- 今までもあったGemを使った方法
- 6.0からのWebpackerを使った方法
と2つあるが、今回は後者の方を採用する。
参考対象者
- Rails6.0で、bootstrapやjQueryを使いたいなと思っている方
環境
$ rails -v Rails 6.0.3.1$ ruby -v ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-darwin19]Webpakerでパッケージを管理
$ yarn install jquery popper.js bootstrapRailsでは、BootstrapはjQueryとpopper.jsというパッケージに依存しているため、一緒にインストールする。
また、パッケージ類はnode_modulesディレクトリ以下にインストールされる。bootstrapの導入
Webpackの設定
config/webpack/environment.jsconst { environment } = require('@rails/webpacker') const webpack = require('webpack') environment.plugins.prepend('Provide', new webpack.ProvidePlugin({ $: 'jquery/src/jquery', jQuery: 'jquery/src/jquery', Popper: ['popper.js', 'default'] }) ) module.exports = environmentjQueryやpopperを事前に読み込むように設定している。
こうすることで、わざわざrequireや@importで読み込む必要がなくなる。bootstrap製のjavascriptを読み込む
app/javascripts/packs/application.jsrequire("@rails/ujs").start() require("turbolinks").start() require("@rails/activestorage").start() require("channels") require('bootstrap/dist/js/bootstrap.min.js')bootstrapのCSSを読み込む
app/assets/stylesheets/application.scss*= require bootstrap/dist/css/bootstrap.min.css *= require_tree . *= require_selfBootstrapの読み込みは、自分自身で書いたcss(*= require_self)よりも前に書き込む必要がある。
jQueryの導入
jQueryを読み込む
app/javascript/packs/application.jsrequire("@rails/ujs").start() require("turbolinks").start() require("@rails/activestorage").start() require("channels") require("jquery") require('bootstrap/dist/js/bootstrap.min.js')Usage
自身のJavaScriptファイルを読み込む
new.html.erb<div> //省略 <%= javascript_pack_tag 'users/new' %> </div>javascript/packsディレクトリ配下の、users/new.jsファイルを呼び出すことでjsファイルを使えるようにする。
- 投稿日:2020-08-06T16:03:24+09:00
AWS Copilotを使ってRailsコンテナの本番環境を爆速で構築する
やりたいこと
Amazon ECSで動くRails本番環境を手軽に作りたい。
やりたくないこと
- ECSを動かすためのネットワーク構築やロードバランサーの設定
- AWSコンソールを利用したコンテナのデプロイ
- 複雑なCI/CDフローの構築
→こういうときにはAWS Copilot!
AWS Copilot
AWSでコンテナ化されたアプリケーションの開発、リリースを容易に行うためのコマンドラインツールです。
コマンドを叩くとCloudFormationが動き、必要なリソースの作成やデプロイを行うことができる。CI/CDパイプラインもコマンド一つで作成できます。※Fargate起動タイプのみサポートしています
Copilotを支える概念
https://github.com/aws/copilot-cli/wiki/EnvironmentsService
ECS上で動くコンテナアプリケーションのことです。
主にECSのサービスやタスク定義と関連があります。Environment
本番環境やステージング環境といったステージのことです。
Environmentが異なるとVPCレベルで異なります。
主に以下のリソースと関連があります。
- VPC, Subnet
- ECS Cluster
- ALB, Security Group, Internet Gateway
- Route53Application
ServiceとEnvironmentを束ねたひとまとまりのことです。
Copilotを使ったRailsコンテナ環境の作り方
今回はNginxをかませず、直接ALBからRailsのコンテナにアクセスする仕組みを作ります。
手順概要
- ローカルで動くRailsコンテナアプリケーションを用意
- CopilotのインストールとAWS credentialsの設定
- Applicationの作成
- Environmentの作成
- RDSインスタンスの作成
- Serviceの作成
以下のコマンドでApplication、Environment、Serviceの作成を全て行ってくれますが、Serviceの作成の前にRails起動のためのデータベース(ここではRDSを利用)が必要なので、以下コマンドは使わずに一つ一つ手順を行っていきます。
$ copilot init1. ローカルで動くRailsコンテナアプリケーションを用意
ローカルで
docker-compose up
コマンドで起動するRailsアプリケーションを作成します。
Rails on DockerのQuickstartをalpine linuxでやってみるを参考にしつつ、APIモードで作成しました。なお、プロジェクトフォルダ名にはアンダースコアは利用しないほうが良いです。後々、copilotを利用してCI/CDパイプラインを作成する際にCloudFormationの命名規則に引っかかりエラーになります。
利用したDockerfileとdocker-compose.ymlは以下の通りです。
DockerfileFROM ruby:2.7.1-alpine3.12 ENV ROOT="/myapp" \ LANG=C.UTF-8 \ TZ=Asia/Tokyo WORKDIR ${ROOT} RUN apk update && \ apk upgrade && \ apk add --no-cache \ tzdata \ nodejs \ mysql-dev \ mysql-client \ vim && \ apk add --virtual build-packs --no-cache \ build-base \ curl-dev \ gcc \ g++ \ libc-dev \ libxml2-dev \ linux-headers \ make COPY Gemfile ${ROOT} COPY Gemfile.lock ${ROOT} RUN bundle install RUN apk del build-packs COPY . ${ROOT} COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 CMD ["rails", "server", "-b", "0.0.0.0"]docker-compose.ymlversion: '3' services: db: image: mysql:5.7 environment: MYSQL_DATABASE: myapp MYSQL_USER: root MYSQL_ALLOW_EMPTY_PASSWORD: 1 TZ: Asia/Tokyo command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci ports: - "3306:3306" volumes: - db_volume:/var/lib/mysql web: build: . command: ash -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 volumes: db_volume:取り急ぎ、コンテナ起動時にデータベースの作成とマイグレーションを行うために、entrypoint.shにコマンドを記述します。
マイグレーション用のECSタスクを作成して、適宜実行するのが良いかもしれません。entrypoint.sh#!/bin/sh set -e rm -f /myapp/tmp/pids/server.pid bundle exec rails db:create bundle exec rails db:migrate exec "$@"さらに
.dockerignore
でmaster.keyをビルド対象から除外している場合は、ビルド対象として含めるようにコメントアウトします。
本来は、AWS Systems Managerのパラメータストアにmaster.keyの文字列を登録して、copilotから参照できるようにmanifest.yml
(後述)のsecretsを登録すべきです。.dockerignore# Ignore master key for decrypting credentials and more. # /config/master.keyまたこの段階で、ALBのヘルスチェックに引っかかってコンテナの停止→起動を繰り返さないためにも、ヘルスチェック応答用のgemであるokcomputerを導入しました。
ルートアクセスに対して応答させるために以下のような記述をします。config/initializers/okcomputer.rbOkComputer.mount_at = '/'ルートアクセスして以下のような表示が返れば準備完了です。
2. CopilotのインストールとAWS credentialsの設定
Copilotのインストール
$ brew install aws/tap/copilot-cliAWS credentialsの設定(設定の仕方は設定の基本を参照)
リソースを作成したいAWSアカウント情報をdefaultのprofileに設定しておきます。~/.aws/credentials[default] aws_access_key_id=XXXXXXXXXXXXXXXXXXX aws_secret_access_key=YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY3. Applicationの作成
copilotコマンドの多くは対話型で操作が進みます。
Applicationが既に存在する場合は、既存のApplicationを利用するか確認されます。
今回は「copilot-demo」という名前のApplicationを新規に作成します。$ copilot app init Would you like to use one of your existing applications? (Y/n) > n What would you like to name your application? [? for help] > copilot-demoしばらく経過すると以下のログが表示され、CloudFormationでRoleが作成されたことがわかります。
ローカルではApplication名が書かれた「copilot/.workspace」というファイルが作成されます。Use existing application: No Application name: copilot-demo ✔ Created the infrastructure to manage services under application copilot-demo. ✔ The directory copilot will hold service manifests for application copilot-demo. Recommended follow-up actions: - Run `copilot init` to add a new service to your application.4. Environmentの作成
次にEnvironmentを作成します。
今回は「production」という名前で作成しました。$ copilot env init What is your environment's name? [? for help] > production Which named profile should we use to create production? [Use arrows to move, type to filter, ? for more help] > defaultしばらく経過すると以下のログが表示され、ネットワーク周りのリソース、ECSクラスタ、ALBが作成されたことがわかります。
もしApplication作成時にRoute53に指定しているドメインを指定すれば、Route53のレコードも作成されます。CloudFormationで「EnvironmentSecurityGroup」というセキュリティグループが作成されます、後ほどRDSセキュリティグループのインバウンドグループのソースとするためにIDを保存しておきます。
What is your environment's name? production Which named profile should we use to create production? default ✔ Created the infrastructure for the production environment. - Virtual private cloud on 2 availability zones to hold your services [Complete] - Virtual private cloud on 2 availability zones to hold your services [Complete] - Internet gateway to connect the network to the internet [Complete] - Public subnets for internet facing services [Complete] - Private subnets for services that can't be reached from the internet [Complete] - Routing tables for services to talk with each other [Complete] - ECS Cluster to hold your services [Complete] - Application load balancer to distribute traffic [Complete] ✔ Linked account XXXXXXXXXXX and region ap-northeast-1 to application copilot-demo. ✔ Created environment production in region ap-northeast-1 under application copilot-demo.5. RDSインスタンスの作成
Service作成時にデータベースに接続できないというエラーを回避するために、このタイミングでRDSインスタンスを作成します。
Aurora Serverlessを利用していますが、何を利用しても問題ありません。作成後は、Environment作成時に作成された「EnvironmentSecurityGroup」を、RDSセキュリティーグループのインバウンドルールのソースに設定します。
また、作成したRDSインスタンスの情報をdatabase.ymlに反映させます。
config/database.ymlproduction: <<: *default database: myapp_production host: copilot-demo-database.cluster-csfienv6hggj.ap-northeast-1.rds.amazonaws.com username: admin password: 5ohWzjXPr7w7u8OZgyBw6. Serviceの作成
最後にServiceを作成します。
Serviceの種類として以下の2種類があります。今回は「Load Balanced Web Service」を選択し、名前は「api」としました。
- Load Balanced Web Service: インターネットから接続がある場合に選択
- Backend Service: インターネットから接続がない場合に選択
※上記の選択によって作成されるリソースに差が出る(インターネットゲートウェイ等)
$ copilot svc init Which service type best represents your service's architecture? [Use arrows to move, type to filter, ? for more help] > Load Balanced Web Service Backend Service What do you want to name this Load Balanced Web Service? [? for help] > api Which Dockerfile would you like to use for api? [Use arrows to move, type to filter, ? for more help] > ./Dockerfileしばらく経過すると以下のログが表示され、CloudFormationでECSサービスやECSタスク定義が作成されます。
ローカルでは「copilot/api/manifest.yml」というファイルが作成されます。Service type: Load Balanced Web Service Service name: api Dockerfile: ./Dockerfile ✔ Wrote the manifest for service api at copilot/api/manifest.yml Your manifest contains configurations like your container size and port (:3000). ✔ Created ECR repositories for service api. Recommended follow-up actions: - Update your manifest copilot/api/manifest.yml to change the defaults. - Run `copilot svc deploy --name api --env test` to deploy your service to a test environment.manifest.yml
Serviceの定義をするファイルです。
Dockerイメージ、コンテナの接続ポート、アクセスを許すURLパス、ECSタスクのCPU/メモリ、ECSサービス内のタスク数、環境変数、シークレット(参照できるAWS Systems Managerのパラメータ)を指定できます。本番環境としてRailsコンテナを起動するためにRAILS_ENVを環境変数で渡します。
通常は「RAILS_ENV = development」ですが、CopilotのEnvironmentがproductionの時は「RAILS_ENV = production」という記述です。manifest.ymlname: api type: Load Balanced Web Service image: build: ./Dockerfile port: 3000 http: path: '/' cpu: 256 memory: 512 count: 1 variables: RAILS_ENV: development environments: production: variables: RAILS_ENV: production最後に、以下コマンドを叩き、定義したServiceをデプロイします。
以下が実行されます。
- ローカルでDockerイメージのビルド
- DockerイメージをECRへのプッシュ
- ECSタスク更新
- ECSサービス更新$ copilot deploy... Successfully tagged XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/copilot-demo/api:77673f1 Login Succeeded The push refers to repository [XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/copilot-demo/api] cee202204539: Pushed 2bf6e538dc23: Pushed 788fae7f9c70: Pushed 0ee2627f68ac: Pushed 246acc754b43: Pushed 68a27f30bbbc: Pushed bb97d43854fb: Pushed 9099c9ee41ff: Pushed 3d0400229c5c: Pushed 93a2bfafa84f: Pushed 54f362ba164c: Pushed c4b1ff92c516: Pushed 446d8e2016ac: Pushed 50644c29ef5a: Pushed 77673f1: digest: sha256:40d94e7257d0657eb2a69450caa3b9f81f33c258719f57a0cafcd857aae2e123 size: 3245 ✔ Deployed api, you can access it at http://copil-Publi-1R5AI7MW2C3Q3-974847059.ap-northeast-1.elb.amazonaws.com.実行ログ行末に記載のあるURLにアクセスすると正常に表示されており、ログを見ると本番環境として動作していることが確認できました。
画面
CloudWatchログ
Database 'myapp_production' already exists => Booting Puma => Rails 6.0.3.2 application starting in production => Run `rails server --help` for more startup options Puma starting in single mode... * Version 4.3.5 (ruby 2.7.1-p83), codename: Mysterious Traveller * Min threads: 5, max threads: 5 * Environment: production * Listening on tcp://0.0.0.0:3000 Use Ctrl-C to stopまとめ
今回作成したコードは以下です。
https://github.com/ssshun/rails-copilotAWS Copilotを利用して、AWSコンソールを利用した操作を極力減らし、手軽にRailsコンテナを本番稼働させることができました。
CI/CDパイプラインの作成はハマりどころが多かったので別途記載します。
- 投稿日:2020-08-06T15:43:08+09:00
Active Storage 導入環境下での単体テスト
まずActive Storageって何?
ファイルアップロードを行うための機能で、
これを使えばフォームで画像の投稿機能などが簡単に作れる。
また、クラウドストレージサービスへのファイルのアップロードも
簡単にできちゃいます。自分の場合は画像をアップロードできる機能があるアプリケーションを作成していたので、
Active Storageを導入していました。
特徴としては画像用のカラムを用意する必要がないこと。
今回はこの特徴で、エラーがでまくっていたのですが...本題のテストのこと
今回のエラーの原因は
①FactoryBotで、ダミーのimageデータを作ってしまっていた
②spec.rbで、fixture_file_uploadメソッドを使っていなかった
以上の2つが主な原因でエラーを吐きまくっていました。
特に②に関しては、見当違いのことを調べていたばかりに中々たどり着きませんでした..
それでは①から。
例えばproductというモデル/テーブルがあるとして
Active Storageを使っていない場合
imageカラム(画像用のカラム)が必要
Active Storageを使っている場合
imageカラム(画像用のカラム)が必要ないですよね。
上記を踏まえてFactoryBotの記述を見直すと
Active Storageを使っていない場合
products.rbFactoryBot.define do factory :product do image { 'test_image.jpg' } name { '化粧水' } explanation { '良い化粧水です' } price { '2000' } user end end
Active Storageを使ってる場合
products.rbFactoryBot.define do factory :product do name { '化粧水' } explanation { '良い化粧水です' } price { '2000' } user end endこうなるのは当然ですよね。
だってActive Storage環境下ではimageカラム必要ないですし。
(全然気付いていなかった..)これで①の部分は解決しました。
続いて②の方を。
fixture_file_uploadメソッド
fixture_file_uploadメソッド
fixture_file_uploadメソッド
大事なことなので3回言いました。
このメソッドはRSpecで用意されているメソッドです。全然テストが通らなかったときのspec.rb ↓↓↓↓
product_spec.rbrequire 'rails_helper' describe Product do describe '#create' do before do @product = build(:product) end # 1. imageが存在すれば登録できること it 'is valid with an image' do expect(@product).to be_valid end # 2. nameが空では登録できないこと it 'is invalid without a name' do @product.name = nil @product.valid? expect(@product.errors[:name]).to include('を入力してください') end . . . . . end end修正後のspec.rb ↓↓↓↓
product_spec.rbrequire 'rails_helper' describe Product do describe '#create' do before do @product = build(:item) @product.image = fixture_file_upload("/files/test_image.jpg") end # 1. imageが存在すれば登録できること it 'is valid with an image' do expect(@product).to be_valid end # 2. nameが空では登録できないこと it 'is invalid without a name' do @product.name = nil @product.valid? expect(@product.errors[:name]).to include('を入力してください') end . . . . . end end
fixture_file_upload("/files/test_image.jpg")
とはなんぞやということなんですけども、
下記のディレクトリの通りにテスト画像を配置しています。spec ├ factories ├ fixtures │ └ files │ ├ test_image.jpg │ └ .keep ├ models : :これを、before do ~ end の間でコールバック(予約)しておいて、
テストコードが実行される前にテスト用の画像を生成しているわけです。これで②も無事に解決です。
解決してしまえばなんてことない原因でしたが、
たどり着くまでに相当な時間と気力を消費してしまいました。もっと検索力上げないとな...
同じような原因でテストが通らない方の参考になれば幸いです。
- 投稿日:2020-08-06T15:20:11+09:00
【Rails】アプリ作成の流れ②【CRUD機能】
前回は
アプリ作成からデプロイまでしました。
https://qiita.com/ksyantaro/private/b2640573d1af18becece前回、scaffoldで作成しましたが、今回は1から作っていきます!
目次
- CRUD機能(サーバー)
- CRUD機能(ビュー)
- やっぱりnewもeditも作成したい
CRUD機能(サーバー)
- 登録機能(Create)
- 参照機能(Read)
- 変更機能(Update)
- 削除機能(Delete)
登録機能(Create)
- SQLにおけるINSERT。
- フォームに入力し、ボタンを押してデータベースに情報を保存。
- 特定のページにアクセスしたときのデータを保存はデータ表示の際に保存。
blogs_controller.rbdef create @blog = Blog.new(blog_params) if @blog.save redirect_to @blog, notice: 'ブログを投稿しました' else render :new end endblogs_controller.rbdef index @blog = Blog.new end※というように空のインスタンス変数を作成しないと
indexから直接createのときは機能しないから注意!参照機能(Read)
- SQLにおけるSELECT
- 情報を表示するページ
- 表示すると他の情報の登録や変更をするコードを書いたときは注意
blogs_controller.rbdef index @blogs = Blog.all end def show @blog = Blog.find(params[:id]) end変更機能(Update)
- SQLにおけるUPDATE
- 入力フォームと変更ボタンで更新する
blogs_controller.rbdef update @blog = Blog.find(params[:id]) if @blog.update(blog_params) redirect_to @blog, notice: 'ブログを更新しました' else render :show end end※index→createのときみたいに空のインスタンスを作る必要はない。すでにあるデータなので。
あまりこんなことはしないと思うけど笑削除機能(Delete)
- SQLにおけるDELETE
- データベースにすでにある情報を削除する。
- dependentオプションで関連する情報を消すことができる。
blogs_controller.rbdef destroy @blog = Blog.find(params[:id]) @blog.destroy redirect_to blogs_path, notice: 'ブログを削除しました' end最後にblog_paramsを忘れずに
blogs_controller.rbprivate def blog_params params.require(:blog).permit(:title, :text) endCRUD機能(ビュー)
gem 'haml-rails'を入れる。
rails haml:erb2hamlを打つ。C(indexを投稿画面とした場合)
index.html.haml= form_with model: @blog do |f| = f.text_field :title = f.text_area :text = f.submit "SEND"R(一覧表示と詳細表示)
index.html.haml- @blogs.each do |blog| = blog.title = blog.text = link_to '詳細ページ', blog_path(blog.id)show.html.haml= @blog.title = @blog.text = link_to '一覧ページ', blogs_pathU(showを更新画面とした場合)
ちなみにあまりこんなことはしない。
show.html.haml= form_with(model: @blog, local: true) do |f| = f.text_field :title = f.text_area :text = f.submit "SEND"上記のlocal: trueは
デフォルトでajax通信に設定されているのをキャンセルする記述。D(showページに削除ボタン)
show.html.haml= link_to '削除する', blog_path(@blog), method: :delete※実はパスの指定の仕方だけどここから学ぶことが2つ。
1つ目は、blog_pathは、methodがgetかdeleteかで、編集か削除かが変わってくる。2つ目は、index.html.hamlのときは、blog_pathの引数がblog.idになっていたのは、繰り返し処理で使われていたから。
そして、@blogが定義されていないから。newもeditもやっぱり作成。
blogs_controller.rbresources :blogs上記に変更する。
new
blogs_controller.rbdef index @blogs = Blog.all # @blog = Blog.new end def new @blog = Blog.new endindex.html.haml= link_to "新規作成ページ", new_blog_pathnew.html.haml= form_with model: @blog do |f| = f.text_field :title = f.text_area :text = f.submit "SEND"edit
blogs_controller.rbdef edit @blog = Blog.find(params[:id]) endshow.html.haml= link_to '編集する', edit_blog_path(@blog), method: :getedit.html.haml= form_with model: @blog do |f| = f.text_field :title = f.text_area :text = f.submit "SEND"まとめ
ということでCRUD機能を実装していきました。
見た目は実装していませんが、とりあえずこれで終了します。
blogs_controller.rbclass BlogsController < ApplicationController def index @blogs = Blog.all end def new @blog = Blog.new end def create @blog = Blog.new(blog_params) if @blog.save redirect_to blogs_path, notice: 'ブログを投稿しました' else render :index end end def show @blog = Blog.find(params[:id]) end def edit @blog = Blog.find(params[:id]) end def update @blog = Blog.find(params[:id]) if @blog.update(blog_params) redirect_to @blog, notice: 'ブログを更新しました' else render :show end end def destroy @blog = Blog.find(params[:id]) @blog.destroy redirect_to blogs_path, notice: 'ブログを削除しました' end private def blog_params params.require(:blog).permit(:title, :text) end end
- 投稿日:2020-08-06T14:53:22+09:00
Railsのxmlのフォーマット調整でハマったこと
Railsでxmlを表示させるときに
type="integer"
やnil="true
がつかないように対応したときのメモです。def index ...(処理)... render xml: data endのようにxml形式で出力するときに以下のデータ
カラム 値 id 1 name 太郎 name_kana タロウ age 20 notice nil を表示させると
<object> <id>1</id> <name>太郎</name> <name-kana>タロウ</name-kana> <age type="integer">20</age> <notice nil="true"/> </object>といった形で、
1. 文字列以外の要素にデータ型を示す属性が入る
2. キーのアンダーバーがハイフンになる
3. 値がない要素にnilという属性が入るのようになりました。
要件でフォーマットが決まっている場合などはこのままだとNGなので変更する必要があるのですが、
その際に色々とハマった点があったのでメモとして残します。文字列以外の要素にデータ型を示す属性が入る
以下で解消できました。
def index ...(処理)... - render xml: data + render xml: data, skip_types: true endRailsではxmlのフォーマットを作成するのにArrayやHashを渡しますが、
active_supportの中でArrayやHashに対してto_xmlメソッドを追加しています。
Arrayのto_xmlであればこここの中で、オプションとして
skip_types
をtrueにすればタイプが付与されないことがコメントにも記載されています。キーのアンダーバーがハイフンになる
以下で解消できました。
def index ...(処理)... - render xml: data + render xml: data, dasherize: false endこちらは
skip_types
とは異なり、Array.to_xmlメソッドの中ではなく、
to_xmlメソッドの中で呼ばれているActiveSupport::XmlMiniのrename_keyメソッド内に設定がありました。
メソッドはこここのメソッドはHash等のキーをxmlのタグに置き換える箇所ですが、
dasherize
がtrueの場合、アンダーバーと半角スペースをハイフンに置き換えています。
※正確には「アンダーバーと半角スペース以外の文字で囲まれているアンダーバーや半角スペースを置き換える」です。なので
dasherize
をfalseにすれば置換が発生しなくなります。値がない要素にnilという属性が入る
これだけ、オプションで対応できないため、以下の方法で対応しました。
nil="true"
を取り除くbuilderを作成してそれを使う具体的には以下です。
builderを作成
require 'builder/xmlmarkup' # 値がnilのものを表示させるのにnil=trueをつけないbuilder module Sample class XmlMarkup < ::Builder::XmlMarkup def tag!(sym, *args, &block) # nil=trueの属性を除去 args = args - [{ nil: true }] # あとは元々の処理に任せる super end end end上記のbuilderを呼び出す
def index ...(処理)... - render xml: data + render xml: data, builder: Sample::XmlMarkup.new() end
nil="true"
を付与する処理だけ、ActiveSupport::XmlMiniのto_tagメソッド内ので
attributes[:nil] = true if value.nil?のように問答無用で付与するようになっているのと、to_tagメソッドをオーバーライドするようなことが難しそうだったので、
ここで付与された属性を取り除くbuilderを作って対応しました。Builder::XmlBaseクラスのtag!メソッド内を見ると
builder/xmlbase.rbwhen nil attrs ||= {} attrs.merge!({:nil => true}) if explicit_nil_handling? elseと条件分岐が書かれていて処理をたどっていくと
Builder::XmlMarkupのコンストラクタでbuilder/xmlmarkup.rbdef initialize(options={}) indent = options[:indent] || 0 margin = options[:margin] || 0 @quote = (options[:quote] == :single) ? "'" : '"' @explicit_nil_handling = options[:explicit_nil_handling] super(indent, margin) @target = options[:target] || "" endと、
explicit_nil_handling
オプションで設定できるようなのですが、その前の
ActiveSupport::XmlMini側でnil="true"
の要素を付与してtag!メソッドを呼び出しているため、このオプションが意味をなさなくなっていました。ActiveSupport::XmlMiniのto_tagメソッドはオプションで渡されたxmlのbuilderのtag!メソッドを呼び出すので、
tag!メソッド内で{ nil: true }
を取り除く処理を入れました。注意点としてXmlMiniのto_tagメソッドでは
xml_mini.rb...(省略)... attributes[:nil] = true if value. ...(省略)... options[:builder].tag!(key, formatted_value, attributes)とtag!メソッドを呼び出しているんですが、
XmlMarkupのtag!メソッドは
xml_markup.rbdef tag!(sym, *args, &block)というように
*args
で受け取っているので値がnilのタグのargsは[nil, { nil: true }]という値になります。
なのでargs = args - [{ nil: true }]というやり方にしていますが、もう少しスマートな方法があるかもしれないです。
もしもっといいやり方ご存知でしたら教えて下さい。
- 投稿日:2020-08-06T14:30:18+09:00
【Rails】アプリ作成の流れ①【アプリ作成からHerokuデプロイまで】
今回の概要
Herokuにてデプロイをする
→データベースはpostgreSQLを使う目次
- アプリ作成
- scaffoldで土台作り
- ルーティングの設定
- デプロイの準備
- デプロイをする 参考記事https://qiita.com/kazukimatsumoto/items/a0daa7281a3948701c39
アプリ作成
ターミナル% rails _6.0.0_ new MyBlog -d postgresql % cd MyBlog % rails db:create
scaffoldで土台作り
ターミナル% rails g scaffold blog title:string body:text % rails db:migrate % rails s
ルーティングの設定
config/routes.rbRails.application.routes.draw do resources :blogs root 'blogs#index' endデプロイの準備
config/environments/production.rbconfig.assets.compile = trueデプロイする
GitHubに登録する
GitHubDesktopでリモートリポジトリを作成する
Herokuを利用する
Herokuにて新規登録する
herokuで検索してHPに行く。
Herokuをインストールする
ターミナル% brew install heroku/brew/heroku % heroku --version
Herokuにログインする
ターミナル% heroku login
Herokuにアプリを紐付ける
ターミナル% heroku create MyBlog 上記のようにすると以下のようにエラー文が出てきたので Creating ⬢ MyBlog... ! ▸ Name must start with a letter, end with a letter or digit and can only contain lowercase letters, digits, and ▸ dashes.
ターミナルkashiishintaro@shinMBA MyBlog % heroku create myblog 上記のようにすると以下のようにエラー文が出てきたので Creating ⬢ myblog... ! ▸ Name myblog is already taken
ターミナルkashiishintaro@shinMBA MyBlog % heroku create myblogfirst これでOK! Creating ⬢ myblogfirst... done https://myblogfirst.herokuapp.com/ | https://git.heroku.com/myblogfirst.git
Herokuにデプロイする
ターミナル% git push heroku master 上記のようにすると以下のようにURLが出てくる To https://git.heroku.com/myblogfirst.git ではなくこちら https://myblogfirst.herokuapp.com/
Herokuでのマイグレーションをする
ターミナル% heroku run rails db:migrate
まとめ
長くなりそうなので次の記事に続きます。
https://qiita.com/ksyantaro/private/a76bef5f718d7e2c2e8b
- 投稿日:2020-08-06T12:47:22+09:00
【Rails】必須gemと便利なgemのまとめ
- 投稿日:2020-08-06T11:56:56+09:00
【Rails】アプリ開発の事前準備
事前準備の目次
- アプリの雛形作成
- データベースの作成と編集
- GitHubで管理する
- コントローラの作成→ルーティングとビューの設定
- アプリを立ち上げる
- モデルの作成→マイグレーションファイルの編集
アプリの雛形作成
ターミナル$ rails _6.0.0_ new MySelf -d mysqlデータベースの作成
database.ymldefault: &default adapter: mysql2 # encoding: utf8mb4を消して、以下を記述 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: socket: /tmp/mysql.sockターミナル$ rails db:createGitHubで管理する
GitHubDesktopでローカルリポジトリを作成する。
その後、リモートリポジトリを作成する。その後、以下の記述を行う。
.gitignorepublic/uploads/*コントローラの作成→ルーティングやビューを編集する
ターミナル$ rails g controller home indexとすると
home_controller.rbclass homeController < ApplicationController def index end endconfig/ruotes.rbRails.application.routes.draw do # get 'home#index' を消して、以下を記述 root 'home#index' endviews/home/index.html.erb<h1>Home#index</h1> <p>Find me in app/views/home/index.html.erb</p>アプリを立ち上げる
ターミナル$ rails sモデルの作成→マイグレーションファイルの編集と作成
ターミナル$ rails g model homedb/migrate/2020XXXXXXXXXXXX_create_homes.rbclass CreateHomes < ActiveRecord::Migration[5.2] def change create_table :homes do |t| t.string :name t.text :text t.text :image t.timestamps null: true end end endターミナルrails db:migrate
まとめ
次は必須のgemをまとめていく。
- 投稿日:2020-08-06T11:29:35+09:00
TodoじゃなくてNoTodoアプリをリリースした。。(とridgepoleの注意点)
前書き
https://notodoo.herokuapp.com/
今回、NoTodoというwebアプリを作りました。Todoリストを作って「やることが多すぎる!」と感じたことはありませんか?
しかし逆にやらなくてもいいことも多くないですか
無駄なことを減らせば本当にやらなければいけないことに時間をさくことができます。
このwebアプリはそんな「しないことリスト」(NoTodo)をつくるためのものです。ツイッターでリストを公開することも出来ます。使った技術
rails6
heroku hobbyプラン(freeだとスリープするのがつらい)
ドメインは取得してません。使ったgem(一部省略してあります)
gem 'rspec-rails' gem 'factory_bot_rails' gem 'ridgepole' gem 'devise' gem 'omniauth-facebook' gem 'omniauth-google-oauth2' gem 'dotenv-rails' gem 'devise-i18n' gem 'devise-i18n-views' gem 'activeadmin' gem 'rails-i18n'今回初めて使ったものはrspecとfactory_bot_railsとridgepoleとomniauth-facebookです。
特に今回はridgepoleについて書こうと思います。紹介
今回始めてスマホ優先のサイトを作りました。いままではずっとパソコン優先のサイトでした。
↑ログイン前のTopページ TodoではなくNoTodoが目立つようにしました。
登録ページにはSNS認証が実装されています。(facebook認証が調子悪いときがあります。できないときはこのQiitaの記事のコメントにかいてください。)↑ログイン後のtopページ ずっと表示され続けるページと保存した翌日には非表示になるページがありますがデザインは一緒です。
ridgepole
使ってみた感想としてはすごく便利っです。migrateファイルをいちいち作らなくてもいいのはすごく楽でスマートです。ただ初めてなので結構つまずいたとこがありました。今回はそのうち2つを説明しようと思います。
①
migratecreate_table :posts do |t|ridgepolecreate_table "posts" do |t|ちがいです。笑うかもしれませんがマイグレーションからそのままコピーして移すと最初、結構ハマります。できれば文法同じにしてほしかったです(わけがあって変えてるのかもしれない)。
②
このサイト公開するにあたって最もハマった点です。ふつうherokuで公開するとき最後に
heroku run rails db:migrateを打つと思うのですがこれをやってしまうと大変です。
heroku run 'bundle exec ridgepole -c config/database.for.heroku.ridgepole.yml -E production -f db/Schemafile --apply'ホントはこれをやるべきでした。しかしもうDBをリセットしても治りません。他にもやり方があるかもですが僕の場合は一旦アプリを削除してheroku createからやりました。これがわかるまで丸1日かかりました(泣)ちなみにseedをやるときはheroku run rails db:seedでいいんです。
ridgepoleを使うメリット・デメリット
メリット
・いちいちmigrateファイルをつく作らなくてもいい。
・ファイル一つで済む。デメリット
・コマンドが覚えにくい
bundle exec ridgepole -c config/database.for.heroku.ridgepole.yml -E development -f db/Schemafile --applyこんな長いのなかなか覚えられません。いちいちコマンドを遡ってやらないといけません。
・エラーが不親切
これはただの僕の技術不足かもですがエラーが分かりづらいと思います。サンプル出せなくてすみません。
まとめ
いろいろ書きましたがとにかくNoTodoお願いします。
- 投稿日:2020-08-06T11:29:35+09:00
TodoじゃなくてNoTodoアプリ作ってみた。(とridgepoleの注意点)
前書き
https://notodoo.herokuapp.com/
今回、NoTodoというwebアプリを作りました。Todoリストを作って「やることが多すぎる!」と感じたことはありませんか?
しかし逆にやらなくてもいいことも多くないですか
無駄なことを減らせば本当にやらなければいけないことに時間をさくことができます。
このwebアプリはそんな「しないことリスト」(NoTodo)をつくるためのものです。ツイッターでリストを公開することも出来ます。使った技術
rails6
heroku hobbyプラン(freeだとスリープするのがつらい)
ドメインは取得してません。使ったgem(一部省略してあります)
gem 'rspec-rails' gem 'factory_bot_rails' gem 'ridgepole' gem 'devise' gem 'omniauth-facebook' gem 'omniauth-google-oauth2' gem 'dotenv-rails' gem 'devise-i18n' gem 'devise-i18n-views' gem 'activeadmin' gem 'rails-i18n'今回初めて使ったものはrspecとfactory_bot_railsとridgepoleとomniauth-facebookです。
特に今回はridgepoleについて書こうと思います。紹介
今回始めてスマホ優先のサイトを作りました。いままではずっとパソコン優先のサイトでした。
↑ログイン前のTopページ TodoではなくNoTodoが目立つようにしました。
登録ページにはSNS認証が実装されています。(facebook認証が調子悪いときがあります。できないときはこのQiitaの記事のコメントにかいてください。)↑ログイン後のtopページ ずっと表示され続けるページと保存した翌日には非表示になるページがありますがデザインは一緒です。
ridgepole
使ってみた感想としてはすごく便利っです。migrateファイルをいちいち作らなくてもいいのはすごく楽でスマートです。ただ初めてなので結構つまずいたとこがありました。今回はそのうち2つを説明しようと思います。
①
migratecreate_table :posts do |t|ridgepolecreate_table "posts" do |t|ちがいです。笑うかもしれませんがマイグレーションからそのままコピーして移すと最初、結構ハマります。できれば文法同じにしてほしかったです(わけがあって変えてるのかもしれない)。
②
このサイト公開するにあたって最もハマった点です。ふつうherokuで公開するとき最後に
heroku run rails db:migrateを打つと思うのですがこれをやってしまうと大変です。
heroku run 'bundle exec ridgepole -c config/database.for.heroku.ridgepole.yml -E production -f db/Schemafile --apply'ホントはこれをやるべきでした。しかしもうDBをリセットしても治りません。他にもやり方があるかもですが僕の場合は一旦アプリを削除してheroku createからやりました。これがわかるまで丸1日かかりました(泣)ちなみにseedをやるときはheroku run rails db:seedでいいんです。
ridgepoleを使うメリット・デメリット
メリット
・いちいちmigrateファイルをつく作らなくてもいい。
・ファイル一つで済む。デメリット
・コマンドが覚えにくい
bundle exec ridgepole -c config/database.for.heroku.ridgepole.yml -E development -f db/Schemafile --applyこんな長いのなかなか覚えられません。いちいちコマンドを遡ってやらないといけません。
・エラーが不親切
これはただの僕の技術不足かもですがエラーが分かりづらいと思います。サンプル出せなくてすみません。
まとめ
いろいろ書きましたがとにかくNoTodoお願いします。
- 投稿日:2020-08-06T10:28:14+09:00
googleアナリティクスを活用し、サイト分析を行う-1章
背景
現在デプロイしているサイトが自分以外のユーザに見ていただいているのか、経緯やアクセス数を調査したく、グーグルアナリティクスを導入ることにしました(ちょっと噛みそうになります…笑)
今まで、gitのトラフィックなどから、自分のポートフォリオサイトのアクセスを確認していたのですが、アナリティクスを使用していろいろと傾向を調査していきたいと考えています。※この記事は、アナリティクスを設定してから、約1時間ほど経過しました。まだ分析結果が表示されないのですが、もう少し、待ってから状況を報告したいと思います(長ければ24時間かかるとのことです。)
表示結果や、手順の修正情報等々はおって掲載します。環境
項目 内容 OS.Amazon Linux AMI release 2018.03 Ruby On Rails v5.2.4.3 MySQL v5.6 設定手順
作業時間:15分
(1)Googleアカウントの作成(割愛)
(2)Googleアナリティクスアカウントを作成
サイトに従って、主に以下を入力するだけです。
・アカウント名
・ウェブサイト名
・ウェブサイトのURL
・レポートのタイムゾーン(3)トラッキングコードを設置
上記のアカウントを作成すると、トラッキングコードが配布されます。
これをサイトの各ページの<ヘッダ>タグに組み込みます。
私の場合、railsで稼働しているので、application.html.erbに設定しました。以下のようになります。app/views/layouts/application.html.erb<html> <head> ... <!-- Global site tag (gtag.js) - Google Analytics --> <script async src="https://www.googletagmanager.com/gtag/js?id=UA-xxxxxx-1"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-xxxxxxxx-1'); </script> </head> <body> <%= yield %> </body> </html>以上です。
参考
Googleアナリティクス導入時の設定・設置方法【初心者向け】
【今さら聞けない】Googleアナリティクスとは?導入手順から使い方まで5分で理解!
- 投稿日:2020-08-06T10:28:14+09:00
Googleアナリティクスを活用し、サイト分析を行う-1章
背景
現在デプロイしているサイトが自分以外のユーザに見ていただいているのか、経緯やアクセス数を調査したく、グーグルアナリティクスを導入することにしました(ちょっと噛みそうになります…笑)
今まで、gitのトラフィックなどから、自分のポートフォリオサイトのアクセスを確認していたのですが、アナリティクスを使用していろいろと傾向を調査していきたいと考えています。※この記事は、アナリティクスを設定してから、約1時間ほど経過しました。まだ分析結果が表示されないのですが、もう少し、待ってから状況を報告したいと思います(長ければ24時間かかるとのことです。)
表示結果や、手順の修正情報等々はおって掲載します。環境
項目 内容 OS.Amazon Linux AMI release 2018.03 Ruby On Rails v5.2.4.3 MySQL v5.6 設定手順
作業時間:15分
(1)Googleアカウントの作成(割愛)
(2)Googleアナリティクスアカウントを作成
サイトに従って、主に以下を入力するだけです。
・アカウント名
・ウェブサイト名
・ウェブサイトのURL
・レポートのタイムゾーン(3)トラッキングコードを設置
上記のアカウントを作成すると、トラッキングコードが配布されます。
これをサイトの各ページの<ヘッダ>タグに組み込みます。
私の場合、railsで稼働しているので、application.html.erbに設定しました。以下のようになります。app/views/layouts/application.html.erb<html> <head> ... <!-- Global site tag (gtag.js) - Google Analytics --> <script async src="https://www.googletagmanager.com/gtag/js?id=UA-xxxxxx-1"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-xxxxxxxx-1'); </script> </head> <body> <%= yield %> </body> </html>以上です。
参考
Googleアナリティクス導入時の設定・設置方法【初心者向け】
【今さら聞けない】Googleアナリティクスとは?導入手順から使い方まで5分で理解!
- 投稿日:2020-08-06T10:23:57+09:00
Rails Webpacker configuration file not found
発生条件
rails6で新規アプリを作るとそうなる
rails webpacker:install
↪︎yarn not installed.brew install yarn
↪︎brew command not found.Homebrewをインストール
用語解説
webpacker -> railsでwebpackを使えるようにするためのgem。node.jsのモジュールの1つ。rails6からwebpackerが初期設定で搭載されるようになった。webpackはjs,css,画像といったモジュールをまとめてくれるツール。これにより管理しやすくなるだけでなく、webの読み込み速度が上がる。
yarn -> node.jsのパッケージマネージャ。webpackerを使うために必要。パッケージマネージャとは、多くのプログラムをまとめるパッケージを管理するツール。フレームワークやwebpackのようなモジュールバンドラーなどの多くのプログラムで構成されているモノを速くインストールすることができる。パッケージマネージャがない場合手動でパッケージを管理しなければいけないためとても大変。rails6からwebpackerが搭載されるようになったためyarnをインストールする必要性が出た。
Homebrew ->
関係性
yarn -> webpacker
参考
- 投稿日:2020-08-06T05:11:57+09:00
Rails6でPG::ConnectionBadというエラーが出た原因と対応
- 投稿日:2020-08-06T03:35:58+09:00
バリデーションのエラーメッセージの個別表示
自分用です!!
バリデーションでのエラーメッセージをviewで表示していきます。
例えば、userを登録するときに、passwordとnameにpresence: trueのバリデーションをかけた、状態でパスワードとnameを空欄で登録すると、バリデーションに引っかかります。
railsのオブジェクトは、デフォルトでエラーオブジェクトを保持しているので、画像のようにエラーメッセージを取得できます。
ここでエラーメッセージが日本語化されているのは、i18nの働きによるものです。
さらに、full_messagesというメソッドを使用することで、エラーメッセージだけを取り出すことができます。
エラーメッセージは、色々な入力フォームで使いたいので、パーシャルを使います。
_error_messages.html.erb<% if object.errors.any? %> <% object.errors.full_messages.each do |message| %> <div class="alert alert-danger"><%= message %></div> <% end %> <% end %>個別で表示したいので、each文を使用。
objectを指定して、そのobjectのエラーメッセージを表示されるようにします。
users/new.html.erb<%= form_with model: @user, local: true do |f| %> <%= render 'shared/error_messages', object: f.object %> <div class="form-group"> <%= f.label :first_name %> <%= f.text_field :first_name %> <% end %>このように、パーシャルを呼び出します。
object: f.objectでは、f.objectに@userが入っており、パーシャル側では、そのobjectのエラー文を表示してくれます。
- 投稿日:2020-08-06T01:23:53+09:00
2人でRailsアプリケーション開発をしてみて
プログラミングの勉強日記
2020年8月6日 Progate Lv.226
フロントエンドエンジニアを目指して独学で勉強を始め、アウトプットのためにポートフォリオなど個人開発をしていた。
チーム開発の経験を積むべきというアドバイスを聞き、2人でRailsでアプリケーションを作成した。アプリケーションを完成することができたので、簡単にまとめてみる。開発環境
Rails 5.1.7
VScode
バージョン: 1.46.1 (system setup)
コミット: cd9ea6488829f560dc949a8b2fb789f3cdc05f5d
日付: 2020-06-17T21:13:20.174Z
Electron: 7.3.1
Chrome: 78.0.3904.130
Node.js: 12.8.1
V8: 7.8.279.23-electron.0
OS: Windows_NT x64 10.0.18362開発の進め方
VscodeのRemote Development 拡張機能を使って、VSCode からインターネット経由で同じサーバーを使ってログインする。
対面で会うことはできないので、LINE電話を使って画面共有しながら開発を進める。課題とその解決方法
1.完成したアプリケーションのイメージを明確にできなかった。
→言葉だけで伝えるのが難しかったので、イラストや参考にするアプリケーションを見ながら話し合いを行った。2.役割分担を決められなかった
→自分の得意なことやできそうなことを相手に伝えて、お互いのやりたいことを進めるようにした。
アプリを1から作ったので、最初の作成やトップページなど大まかなところは一緒に作成した。3.同じファイルを編集してしまうなど、衝突してしまうことがあった。
→自分がどのファイルを操作しているのか相手に伝えるようにした。4.お互いのコードの理解が不十分だった
→わからないことがあれば、相手に聞くようにした。
私自身も「動いたからいいや」という考えではなく、コードを理解しながら書くように心がけた。5.プログラムにエラーが生じてしまい動かなくなったときにお互いブラウザで確認することができなく、作業が止まってしまった。
→2人で協力してエラーの原因を調べて、解決するようにした。
(それぞれが詰まってしまったら、一緒に問題を解決するようにした)感想
対面であって話すことができないので、コミュニケーションの取り方が難しかった。チーム開発にするにあたって、コミュニケーションをとることがいかに重要かを学ぶことができた。
エラーを解決できたり、できなかったことができるようになったりすることで喜びを共有することができ、チーム開発の楽しさを実感した。徐々に完成に近づき、形になっていくアプリ開発は楽しく、チーム開発をすることでよりアプリ開発を楽しんで行うことができた。個人開発の場合でも、チーム開発を想定してGitHubにあげていたが、実際にチーム開発をすることで個人開発では得られなかった経験をすることができた。サマーインターンでチーム開発をすることがあるので、この経験を活かしていきたいと思う。
- 投稿日:2020-08-06T01:13:48+09:00
DockerでRailsチュートリアルのローカル開発環境構築(Rails 6 + PostgreSQL + Webpack)
はじめに
最新(2020.8.5現在)のRailsチュートリアルではRails 6が使用されており、
これに対応した開発環境構築をDockerでやってみたいと思います。個人開発アプリの開発環境構築の際に、
私は新しいものが好きだから...とRails 6を導入しました。ところが...このRails 6からJavaScriptのモジュールバンドラーにWebpackが導入されたことにより、
BootstrapやFontawesomeといったツールの導入、管理方法が変わるだけでなく、
そもそも環境構築の際もRails 5では必要ない手順が必要だったりと、
Rails 6とWebpackの壁に盛大にぶつかることとなりました...せっかくなので、今回の試みを経て、知識、経験の整理定着を図りたいと思います。
また、実際にRailsチュートリアルを脱線しながら再走し、学びを深められたらと思っています。
個人開発アプリ
mdClip <オンラインmarkdownエディタ>以前Rails 5の環境構築をDockerでやってみた記事
はじめてのDockerでRails開発環境構築 [NGINX + Rails 5 (puma) + PostgreSQL] - Qiita環境
- Mac OS (Mojave)
- Docker for Mac
- Ruby 2.6.3-alpine
- Rails 6.0.3
- PostgreSQL 11.0-alpine
構成ファイル
以下の5つのファイルをワークスペースに用意します
Dockerfile
,docker-compose.yml
,Gemfile
,Gemfile.lock
,entrypoint.sh
https://github.com/naokit1030/sample_app_on_docker.git
git clone -b create-docker-files https://github.com/dev-naokit/sample_app_on_docker.git
Dockerfile
イメージの軽量化のため"alpine"を使用
nodejs, yarnはWebpack導入に必要
postgresql
だけでは駄目で、postgresql-dev
も必要bundle installの-j4オプションで
bundle install
が高速化されるbundle install後、不要ファイル削除はイメージサイズの削減に貢献
FROM ruby:2.6.3-alpine ENV LANG=ja_JP.UTF-8 ENV TZ=Asia/Tokyo ENV ROOT=/myapp \ GEM_HOME=/bundle \ BUNDLE_PATH=$GEM_HOME ENV BUNDLE_BIN=$BUNDLE_PATH/bin ENV PATH /app/bin:$BUNDLE_BIN:$PATH WORKDIR $ROOT RUN apk update && \ apk upgrade && \ apk add --no-cache \ gcc \ g++ \ libc-dev \ libxml2-dev \ linux-headers \ make \ nodejs \ postgresql \ postgresql-dev \ tzdata \ yarn && \ apk add --virtual build-packs --no-cache \ build-base \ curl-dev COPY Gemfile $ROOT COPY Gemfile.lock $ROOT RUN bundle install -j4 # 不要ファイル削除 RUN rm -rf /usr/local/bundle/cache/* /usr/local/share/.cache/* /var/cache/* /tmp/* && \ apk del build-packs COPY . $ROOT # Add a script to be executed every time the container starts. COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["sh", "/usr/bin/entrypoint.sh"] EXPOSE 3000
docker-compose.yml
- macでストレージのアクセスが遅い問題に対応するためvolumeに
:cahed
オプションを追加- webpack-dev-serverを別コンテナで起動させて、hot reloadに対応させています. (ファイル構成に変更があった場合にブラウザのリロード、必要に応じてjavascript周りのコンパイルをしてくれる)
- dbのhost, user, passwordは後で再利用
version: '3' services: db: image: postgres:11.0-alpine volumes: - postgres:/var/lib/postgresql/data:cached environment: - TZ=Asia/Tokyo ports: - '5432:5432' environment: PGDATA: /var/lib/postgresql/data/pgdata POSTGRES_USER: postgres POSTGRES_PASSWORD: password POSTGRES_INITDB_ARGS: '--encoding=UTF-8 --locale=ja_JP.UTF-8' TZ: Asia/Tokyo app: build: . command: ash -c "rm -f tmp/pids/server.pid && ./bin/rails s -p 3000 -b '0.0.0.0'" volumes: - .:/myapp:cached - rails_cache:/myapp/tmp/cache - bundle:/bundle:cached tmpfs: - /tmp tty: true stdin_open: true ports: - "3000:3000" environment: RAILS_ENV: development NODE_ENV: development DATABASE_HOST: db DATABASE_PORT: 5432 DATABASE_USER: postgres DATABASE_PASSWORD: password WEBPACKER_DEV_SERVER_HOST: webpacker depends_on: - db - webpacker links: - db - webpacker webpacker: build: . command: ./bin/webpack-dev-server volumes: - .:/myapp:cached environment: RAILS_ENV: development NODE_ENV: development WEBPACKER_DEV_SERVER_HOST: 0.0.0.0 tty: false stdin_open: false ports: - '3035:3035' volumes: rails_cache: postgres: bundle:
Gemfile
source 'https://rubygems.org' gem 'rails', '6.0.3'
Gemfile.lock
空のファイルで良いので
touch
コマンドのみtouch Gemfile.lock
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 "$@"手順
Rails 6.0.3をインストールします
docker-compose run app rails new . --force --no-deps --database=postgresql --skip-bundle
Gemfile
Railsチュートリアルに準じて以下のように変更
(Development, test環境でもPostgreSQLを使用するように変更してあります。)source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } gem 'rails', '6.0.3' gem 'puma', '4.3.4' gem 'pg', '1.1.4' gem 'sass-rails', '5.1.0' gem 'webpacker', '4.0.7' gem 'turbolinks', '5.2.0' gem 'jbuilder', '2.9.1' gem 'bootsnap', '1.4.5', require: false group :development, :test do gem 'byebug', '11.0.1', platforms: [:mri, :mingw, :x64_mingw] end group :development do gem 'web-console', '4.0.1' gem 'listen', '3.1.5' gem 'spring', '2.1.0' gem 'spring-watcher-listen', '2.0.1' end group :test do gem 'capybara', '3.28.0' gem 'selenium-webdriver', '3.142.4' gem 'webdrivers', '4.1.2' gem 'rails-controller-testing', '1.0.4' gem 'minitest', '5.11.3' gem 'minitest-reporters', '1.3.8' gem 'guard', '2.16.2' gem 'guard-minitest', '2.4.6' end group :production do end # Windows ではタイムゾーン情報用の tzinfo-data gem を含める必要があります #gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]Gemfile.lockをアップデート(必要ないかもしれません)
docker-compose run app bundle updatewebpackerのインストール
Rails 6ではwebpackerが必要ですが、
このままではインストールされていないためインストールしますdocker-compose run app rails webpacker:installデータベースの設定
config/database.yml
host, username, passwordをdocker-compose.yml
と一致させるdefault: &default adapter: postgresql encoding: unicode host: db username: postgres password: password pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> #省略...
docker-compose build
DockerfileやGemfileを変更する度に'build'が必要です
bundle installも実施されるため少し時間がかかりますdocker-compose buildいよいよコンテナを起動
docker-compose upデータベース作成
このままだとDBがないよと言われるので、development環境のDBを作成
docker-compose run app rake db:create動作確認
docker ps
で起動しているコンテナ確認
DB, Rails, webpack-dev-serverの3つのコンテナが起動しています。$ docker ps CONTAINER ID IMAGE COMMAND ... PORTS NAMES 1fb4f53d5652 sample_app_on_docker_app "sh /usr/bin/entrypo…" ... 0.0.0.0:3000->3000/tcp sample_app_on_docker_app_1 ccd40c018d53 sample_app_on_docker_webpacker "sh /usr/bin/entrypo…" ... 3000/tcp, 0.0.0.0:3035->3035/tcp sample_app_on_docker_webpacker_1 74392532098a postgres:11.0-alpine "docker-entrypoint.s…" ... 0.0.0.0:5432->5432/tcp sample_app_on_docker_db_1ブラウザで
localhost:3000
にアクセスするとお疲れさまでした"Yay! You’re on Rails!"
Trouble shoot
check_yarn_integrity
関連コンテナを
docker-compose up
で起動したときに出るエラーapp_1 | ======================================== app_1 | Your Yarn packages are out of date! app_1 | Please run `yarn install --check-files` to update. app_1 | ======================================== app_1 | app_1 | app_1 | To disable this check, please change `check_yarn_integrity` app_1 | to `false` in your webpacker config file (config/webpacker.yml). app_1 | app_1 | app_1 | yarn check v1.16.0 app_1 | info Visit https://yarnpkg.com/en/docs/cli/check for documentation about this command. app_1 | app_1 | app_1 | Exiting sample_app_on_docker_app_1 exited with code 1
config/webpacker.yml
以下のように変更します#...省略 development: <<: *default compile: false # true -> falseに変更 # Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules check_yarn_integrity: true #省略..."webpack-dev-server" not found
同じくコンテナを
docker-compose up
で起動したときに出るエラーwebpacker_1 | yarn run v1.16.0 webpacker_1 | error Command "webpack-dev-server" not found. webpacker_1 | info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command. sample_app_on_docker_webpacker_1 exited with code 1
package.json
にwebpack-dev-serverが記述されていないはずなので
webpack-dev-serverをインストールdocker-compose run app yarn add webpack-dev-serverおまけ
イメージサイズ 477MB
先人の知恵を多々お借りして軽量化したつもりです。
Dockerを学び始めた頃に作ったのイメージは1.5GBほどありました...
$docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
sample_app_on_docker_app latest 84aed607a3d2 31 minutes ago 477MB
sample_app_on_docker_webpacker latest 84aed607a3d2 31 minutes ago 477MB