20200806のRailsに関する記事は20件です。

GitHubでMiniMagickの脆弱性アラートが出た際の解決方法

現象

RailsアプリをGitHubにプッシュした所、mini_magickに関する脆弱性のアラームメールが届いた。
解決までの備忘録として残しておく。

問題と原因

image.png
mini_magickのバージョンが古く、フェッチされたリモートイメージファイル名がリモートコマンドの実行を引き起こす可能性があるとのこと。
解決するには、バージョンをアップグレードすれば良さそう。

GemFileを編集する

Gemfile
gem 'mini_magick',  '3.8.0'

現在のMiniMagickのバージョンは、3.8だったので
アラートにて提示されているように4.9.4以降をインストールするように修正する。

Gemfile
gem 'mini_magick', '>= 4.9.4'

上記のように編集することで、4.9.4以降にアップグレードされるはず。

bundle installする

bundle install

これでバージョンが変わっているはずなので、動作確認をして問題なければOK。
あとは、プッシュすればアラートが消えてました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】アプリ開発の流れ③【検索機能】

前回は

CRUD機能の基本を作成しました。
https://qiita.com/ksyantaro/private/a76bef5f718d7e2c2e8b

目次

  1. 検索機能

検索機能

  • データベースに保存されている情報の中から、特定の条件に一致するデータを取得する。
  • 参照機能(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.rb
  def 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に頼ることになるのでしょうか?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 を理解するためには、次の基礎知識が必要です:

理解するために必要ないツール・知識

  • Rails チュートリアル
    • Rails チュートリアルで解説されていることと、この Quickstart で得られる知識は、大部分が別の内容です
    • この Quickstart を理解する上で必要な Rails に関する知識はこの記事内で解説します

この Quickstart は何をするためのものなの?

この Quickstart ガイドでは、Docker Compose を使用して Rails / PostgreSQL アプリを設定および実行する方法を理解します。

この Quickstart はどんなことをしているの?

この Quickstart は次の手順を実施します:

  1. Define the project
    • Docker イメージのビルドに必要なファイルを作成します
  2. Build the project
    • 手順 1. で準備したファイルをもとに Rails の実行環境を起動して Rails を動かし新規 Rails アプリケーションを作成します
      • このとき自動的に Docker イメージがビルドされます
    • 新規作成した Rails アプリケーションを使い Docker イメージをビルドし直します
  3. Connect the database
    • Rails の実行環境とデータベースのサービスを一度に起動します
  4. 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 を実施する人は
次の理由により混乱してしまうことでしょう:

  • ファイルの内容が誤っていたり、不要な内容が含まれているため
  • ここで準備するファイルやその内容が相当な量であるにも関わらず、それらの目的についての解説が不十分であるため

そこで、この記事では Dockerfiledocker-compose.yml の内容を次のように修正します:

Dockerfile
FROM 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.yml
version: '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. で必要な処理以外をコメントアウトします:

Dockerfile
FROM 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"]
GemfileCOPY して bundle install を実行する理由

この後の手順 2. では rails new というコマンドを使い、
新規 Rails アプリケーションを作成します。

rails new コマンドを使うためには、Rails をインストールする必要があります。
Rails は「Gem パッケージ」という形式で配布されています。
「Gem パッケージ」とは、Ruby のプログラムを配布するための形式です。

参考: パッケージ - 意味・説明・解説 : ASCII.jpデジタル用語辞典

Gem パッケージをインストールするためには、
bundler という、Gem パッケージを一度にインストールするためのツールの
bundle install コマンドを使います。

bundle install コマンドは Gemfile というファイルを参照してパッケージをインストールします。

ここで出てきた命令一覧:

/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?

ここで出てきた命令一覧:

Gemfile

Gemfile は Quickstart で提示されている内容をそのまま準備します:

Gemfile
source '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.yml
version: '3'
services:
  # db:
  #   image: postgres
  #   environment:
  #     POSTGRES_PASSWORD: password
  web:
    build: .
    volumes:
      - .:/myapp
    # ports:
    #   - "3000:3000"
    # depends_on:
    #   - db
build 命令の解説

db サービスはここでは必要ありませんので、コメントアウトしました。
また、web サービス内で手順 2. で必要な設定は buildvolume のみです。

build は、先ほど作成した DockerfileGemfile を使ってイメージをビルドするために必要です。
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 webrails 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-depsdocker-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 の一部コメントアウトしていた部分を、次のようにコメントインします:

Dockerfile
FROM 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 を削除する処理を実行しています。

ここで重要なのは、ENTRYPOINTCMD 命令を使った Dockerfile のデザインパターンで
イメージからコンテナーを起動したときの規定の動作を定義していることです。

ENTRYPOINTCMD を両方とも配列形式で与えると、すべての配列の要素を並べたコマンドが実行されます。

参考: Understand how CMD and ENTRYPOINT interact | Dockerfile reference | Docker Documentation

この例のように、ENTRYPOINT に Shell Script を設定し、
その Shell Script のコード内の最後で exec "$@" を呼び出す設計は
Dockerfile のデザインパターンとして広く使われています。
憶えておいて損はありません。

こうすることで、CMD で指定したコマンドを実行する前に何らかの初期化処理を実行することができるのです。

参考:

ここで出てきた命令一覧:

bundle install の前に Gemfile.lockCOPY する理由

このようにしないと、イメージを再ビルドしたときに、
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 パッケージがインストールされます。

参考:

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.ymldefault: &default に次の要素を追加します:

config/database.yml
  host: 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"?

2020-08-06_13h03_31.png

このエラーメッセージは、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 exist

2020-08-06_13h06_15.png

View the Rails welcome page!

次のような画面が表示されましたでしょうか?:

2020-08-06_14h41_27.png

この後の開発フローについて

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 パッケージを使ってコードに残します。
データの再利用の必要性を感じたら、コードに残す習慣をつけましょう。

参考: テストフィクスチャとは何? Weblio辞書

Rebuild the application

ここでは、イメージの再ビルドが必要な場合について述べられています。
ここで述べられている「イメージの再ビルドが必要な場合」とは、
「開発者が開発中に再ビルドを行うこと」を指しており、
「本番環境にデプロイするためのイメージをビルドすること」は指していません。

このことを理解するために、まずは「本番環境にデプロイするためのイメージをビルドすること」を考えてみます。

本番環境にデプロイするためのイメージのビルド

手順 1. Define the projectDockerfile コメントアウトしていた場合は、COPY . /myapp をコメントインします:

Dockerfile
FROM 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.lockCOPY を分けているのは、bundle install に時間がかかるためです。

Docker イメージのビルドは、命令毎に結果がキャッシュされるので、
変更の可能性が低い命令、実行に時間がかかる命令は
なるべく Dockerfile の上に記述した方が再ビルド時の効率が良くなります。

COPY . /myapp 以降の命令はそれほどビルドに時間がかかりません。
もし、/myappGemfile, 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.ymlvolume/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をしてもファイルが存在しない|teratail

Docker と Rails による Web サービス実行に関する質問

エラーメッセージの意味が何を指しているのかわかりません

複数のサービスを起動するときはコンテキストとなるディレクトリーを分けて設計していますか?
コンテキストとなるディレクトリーを分けると、原因の範囲が狭まり調査しやすくなります。
Ruby on Rails - Docker + Railsの No such file or directory エラーの解決方法について|teratail

パッケージが足りないエラーが表示されるので gem install しても直りません

Docker が動いているコンピューターに gem をインストールしていませんか?
gem のインストールはコンテナ内で行う必要があります。
Ruby on Rails - docker-compose upがうまくいかない|teratail

db サービス以外のデータベースに接続したいです

Rails の database.yml と環境変数でデータベース接続情報を変更して起動します。
MySQL - [Docker, Rails, Nginx, MySQL, EC2]docker-compose run app rails db:createするとエラーが出る。|teratail

multi-stage builds にしたら bundle: not found でイメージがビルドできなくなりました

ベースイメージが Ruby のイメージではないステージで rubybundler を使おうとしていませんか?
Ruby on Rails - "docker-compose build"実行時のエラー "Service 'web' failed to build: The command '/bin/sh -c bundle instal"|teratail

Gemfile はあるのに bundle install すると Could not locate Gemfile というエラーメッセージが表示されます

コマンドは Docker が動いているコンピューターで実行しているか、
それともコンテナの中で実行しているのかを確認し、
そこに Gemfile があるかを確認しましょう
GitHub - Dockerを用いたローカル環境構築で、Could not locate Gemfileが表示される|teratail

Dockerで起動したサーバーにlocalhost:3000でアクセスすると「このページは動作していません。」と表示されます

Docker で起動した web サービスのログを確認して、ログが増えるかどうかを確認します。
増えなければ、ファイアウォールの問題などの可能性があります。
MySQL - dockerで起動したサーバーにlocalhost:3000でアクセスしようとするが「このページは動作していません。」と返ってくる|teratail

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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 bootstrap

Railsでは、BootstrapはjQueryとpopper.jsというパッケージに依存しているため、一緒にインストールする。
また、パッケージ類はnode_modulesディレクトリ以下にインストールされる。

bootstrapの導入

Webpackの設定

config/webpack/environment.js
const { 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 = environment

jQueryやpopperを事前に読み込むように設定している。
こうすることで、わざわざrequireや@importで読み込む必要がなくなる。

bootstrap製のjavascriptを読み込む

app/javascripts/packs/application.js
require("@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_self

Bootstrapの読み込みは、自分自身で書いたcss(*= require_self)よりも前に書き込む必要がある。

jQueryの導入

jQueryを読み込む

app/javascript/packs/application.js
require("@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ファイルを使えるようにする。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Copilotを使ってRailsコンテナの本番環境を爆速で構築する

やりたいこと

Amazon ECSで動くRails本番環境を手軽に作りたい。

やりたくないこと

→こういうときにはAWS Copilot!

AWS Copilot

AWSでコンテナ化されたアプリケーションの開発、リリースを容易に行うためのコマンドラインツールです。
コマンドを叩くとCloudFormationが動き、必要なリソースの作成やデプロイを行うことができる。CI/CDパイプラインもコマンド一つで作成できます。

※Fargate起動タイプのみサポートしています

Copilotを支える概念

スクリーンショット 2020-08-06 1.23.33.png
https://github.com/aws/copilot-cli/wiki/Environments

Service

ECS上で動くコンテナアプリケーションのことです。
主にECSのサービスやタスク定義と関連があります。

Environment

本番環境やステージング環境といったステージのことです。
Environmentが異なるとVPCレベルで異なります。
主に以下のリソースと関連があります。
- VPC, Subnet
- ECS Cluster
- ALB, Security Group, Internet Gateway
- Route53

Application

ServiceとEnvironmentを束ねたひとまとまりのことです。

Copilotを使ったRailsコンテナ環境の作り方

今回はNginxをかませず、直接ALBからRailsのコンテナにアクセスする仕組みを作ります。

手順概要

  1. ローカルで動くRailsコンテナアプリケーションを用意
  2. CopilotのインストールとAWS credentialsの設定
  3. Applicationの作成
  4. Environmentの作成
  5. RDSインスタンスの作成
  6. Serviceの作成

以下のコマンドでApplication、Environment、Serviceの作成を全て行ってくれますが、Serviceの作成の前にRails起動のためのデータベース(ここではRDSを利用)が必要なので、以下コマンドは使わずに一つ一つ手順を行っていきます。

$ copilot init

1. ローカルで動くRailsコンテナアプリケーションを用意

ローカルでdocker-compose upコマンドで起動するRailsアプリケーションを作成します。
Rails on DockerのQuickstartをalpine linuxでやってみるを参考にしつつ、APIモードで作成しました。

なお、プロジェクトフォルダ名にはアンダースコアは利用しないほうが良いです。後々、copilotを利用してCI/CDパイプラインを作成する際にCloudFormationの命名規則に引っかかりエラーになります。

利用したDockerfileとdocker-compose.ymlは以下の通りです。

Dockerfile
FROM 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.yml
version: '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.rb
OkComputer.mount_at = '/'

ルートアクセスして以下のような表示が返れば準備完了です。

スクリーンショット 2020-08-05 12.56.09.png

2. CopilotのインストールとAWS credentialsの設定

Copilotのインストール

$ brew install aws/tap/copilot-cli

AWS credentialsの設定(設定の仕方は設定の基本を参照)
リソースを作成したいAWSアカウント情報をdefaultのprofileに設定しておきます。

~/.aws/credentials
[default]
aws_access_key_id=XXXXXXXXXXXXXXXXXXX
aws_secret_access_key=YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY

3. 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.

スクリーンショット 2020-08-06 3.10.17.png

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を利用していますが、何を利用しても問題ありません。

スクリーンショット 2020-08-05 11.37.35.png
スクリーンショット 2020-08-05 11.39.03.png
スクリーンショット 2020-08-05 11.39.22.png

作成後は、Environment作成時に作成された「EnvironmentSecurityGroup」を、RDSセキュリティーグループのインバウンドルールのソースに設定します。
スクリーンショット 2020-08-05 13.01.28.png

また、作成したRDSインスタンスの情報をdatabase.ymlに反映させます。

config/database.yml
production:
  <<: *default
  database: myapp_production
  host: copilot-demo-database.cluster-csfienv6hggj.ap-northeast-1.rds.amazonaws.com
  username: admin
  password: 5ohWzjXPr7w7u8OZgyBw

6. 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.yml
name: 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にアクセスすると正常に表示されており、ログを見ると本番環境として動作していることが確認できました。

画面

スクリーンショット 2020-08-06 4.11.27.png

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-copilot

AWS Copilotを利用して、AWSコンソールを利用した操作を極力減らし、手軽にRailsコンテナを本番稼働させることができました。
CI/CDパイプラインの作成はハマりどころが多かったので別途記載します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.rb
FactoryBot.define do
  factory :product do
    image             { 'test_image.jpg' }
    name              { '化粧水' }
    explanation       { '良い化粧水です' }
    price             { '2000' }
    user
  end
end

Active Storageを使ってる場合

products.rb
FactoryBot.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.rb
require '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.rb
require '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 の間でコールバック(予約)しておいて、
テストコードが実行される前にテスト用の画像を生成しているわけです。

これで②も無事に解決です。


解決してしまえばなんてことない原因でしたが、
たどり着くまでに相当な時間と気力を消費してしまいました。

もっと検索力上げないとな...

同じような原因でテストが通らない方の参考になれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】アプリ作成の流れ②【CRUD機能】

前回は

アプリ作成からデプロイまでしました。
https://qiita.com/ksyantaro/private/b2640573d1af18becece

前回、scaffoldで作成しましたが、今回は1から作っていきます!

目次

  1. CRUD機能(サーバー)
  2. CRUD機能(ビュー)
  3. やっぱりnewもeditも作成したい

CRUD機能(サーバー)

  • 登録機能(Create)
  • 参照機能(Read)
  • 変更機能(Update)
  • 削除機能(Delete)

登録機能(Create)

  • SQLにおけるINSERT。
  • フォームに入力し、ボタンを押してデータベースに情報を保存。
  • 特定のページにアクセスしたときのデータを保存はデータ表示の際に保存。
blogs_controller.rb
  def create
    @blog = Blog.new(blog_params)
    if @blog.save
      redirect_to @blog, notice: 'ブログを投稿しました'
    else
      render :new
    end
  end
blogs_controller.rb
  def index
    @blog = Blog.new
  end

※というように空のインスタンス変数を作成しないと
indexから直接createのときは機能しないから注意!

参照機能(Read)

  • SQLにおけるSELECT
  • 情報を表示するページ
  • 表示すると他の情報の登録や変更をするコードを書いたときは注意
blogs_controller.rb
  def index
    @blogs = Blog.all
  end

  def show
    @blog = Blog.find(params[:id])
  end

変更機能(Update)

  • SQLにおけるUPDATE
  • 入力フォームと変更ボタンで更新する
blogs_controller.rb
  def 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.rb
def destroy
  @blog = Blog.find(params[:id])
  @blog.destroy
  redirect_to blogs_path, notice: 'ブログを削除しました'
end

最後にblog_paramsを忘れずに

blogs_controller.rb
  private

  def blog_params
    params.require(:blog).permit(:title, :text)
  end

CRUD機能(ビュー)

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_path

U(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.rb
resources :blogs

上記に変更する。

new

blogs_controller.rb
  def index
    @blogs = Blog.all
    # @blog = Blog.new
  end

  def new
    @blog = Blog.new
  end
index.html.haml
  = link_to "新規作成ページ", new_blog_path
new.html.haml
= form_with model: @blog do |f|
  = f.text_field :title
  = f.text_area :text
  = f.submit "SEND"

edit

blogs_controller.rb
  def edit
    @blog = Blog.find(params[:id])
  end
show.html.haml
  = link_to '編集する', edit_blog_path(@blog), method: :get
edit.html.haml
= form_with model: @blog do |f|
  = f.text_field :title
  = f.text_area :text
  = f.submit "SEND"

まとめ

ということでCRUD機能を実装していきました。

見た目は実装していませんが、とりあえずこれで終了します。

blogs_controller.rb
class 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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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
  end

Railsでは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.rb
        when nil
          attrs ||= {}
          attrs.merge!({:nil => true}) if explicit_nil_handling?
        else

と条件分岐が書かれていて処理をたどっていくと
Builder::XmlMarkupのコンストラクタで

builder/xmlmarkup.rb
    def 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.rb
def tag!(sym, *args, &block)

というように*argsで受け取っているので値がnilのタグのargsは

[nil, { nil: true }]

という値になります。
なので

args = args - [{ nil: true }]

というやり方にしていますが、もう少しスマートな方法があるかもしれないです。

もしもっといいやり方ご存知でしたら教えて下さい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】アプリ作成の流れ①【アプリ作成からHerokuデプロイまで】

今回の概要

Herokuにてデプロイをする
→データベースはpostgreSQLを使う

目次

  1. アプリ作成
  2. scaffoldで土台作り
  3. ルーティングの設定
  4. デプロイの準備
  5. デプロイをする 参考記事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.rb
  Rails.application.routes.draw do
    resources :blogs
    root 'blogs#index'
  end

デプロイの準備

config/environments/production.rb
  config.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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】必須gemと便利なgemのまとめ

必須のgem

haml-rails

hamlを使用するために必須なgem。

devise

ユーザー管理機能を作成することができる

便利なgem

carrierwave

画像アップロードができる

ancestry

カテゴリ機能をつけることができる

omniauth系

deviseと組み合わせることでSNS連携をすることができる

kaminari

ページネーション機能をつけることができる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】アプリ開発の事前準備

事前準備の目次

  1. アプリの雛形作成
  2. データベースの作成と編集
  3. GitHubで管理する
  4. コントローラの作成→ルーティングとビューの設定
  5. アプリを立ち上げる
  6. モデルの作成→マイグレーションファイルの編集

アプリの雛形作成

ターミナル
  $ rails _6.0.0_ new MySelf -d mysql

データベースの作成

database.yml
default: &default
  adapter: mysql2
  # encoding: utf8mb4を消して、以下を記述
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password:
  socket: /tmp/mysql.sock
ターミナル
  $ rails db:create

GitHubで管理する

GitHubDesktopでローカルリポジトリを作成する。
その後、リモートリポジトリを作成する。

その後、以下の記述を行う。

.gitignore
public/uploads/*

コントローラの作成→ルーティングやビューを編集する

ターミナル
  $ rails g controller home index

とすると

home_controller.rb
  class homeController < ApplicationController

    def index
    end

  end
config/ruotes.rb
  Rails.application.routes.draw do

    # get 'home#index' を消して、以下を記述
    root 'home#index'

  end
views/home/index.html.erb
  <h1>Home#index</h1>
  <p>Find me in app/views/home/index.html.erb</p>

アプリを立ち上げる

ターミナル
  $ rails s

モデルの作成→マイグレーションファイルの編集と作成

ターミナル
  $ rails g model home
db/migrate/2020XXXXXXXXXXXX_create_homes.rb
  class 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をまとめていく。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TodoじゃなくてNoTodoアプリをリリースした。。(とridgepoleの注意点)

前書き

https://notodoo.herokuapp.com/
今回、NoTodoというwebアプリを作りました。Todoリストを作って「やることが多すぎる!」と感じたことはありませんか?
しかし逆にやらなくてもいいことも多くないですか
無駄なことを減らせば本当にやらなければいけないことに時間をさくことができます。
このwebアプリはそんな「しないことリスト」(NoTodo)をつくるためのものです。ツイッターでリストを公開することも出来ます。

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について書こうと思います。

紹介

今回始めてスマホ優先のサイトを作りました。いままではずっとパソコン優先のサイトでした。

NOtodotop.png

↑ログイン前のTopページ TodoではなくNoTodoが目立つようにしました。
登録ページにはSNS認証が実装されています。(facebook認証が調子悪いときがあります。できないときはこのQiitaの記事のコメントにかいてください。)

notodo2.png

↑ログイン後のtopページ ずっと表示され続けるページと保存した翌日には非表示になるページがありますがデザインは一緒です。

ridgepole

使ってみた感想としてはすごく便利っです。migrateファイルをいちいち作らなくてもいいのはすごく楽でスマートです。ただ初めてなので結構つまずいたとこがありました。今回はそのうち2つを説明しようと思います。

migrate
create_table :posts do |t|
ridgepole
create_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お願いします。

https://notodoo.herokuapp.com/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TodoじゃなくてNoTodoアプリ作ってみた。(とridgepoleの注意点)

前書き

https://notodoo.herokuapp.com/
今回、NoTodoというwebアプリを作りました。Todoリストを作って「やることが多すぎる!」と感じたことはありませんか?
しかし逆にやらなくてもいいことも多くないですか
無駄なことを減らせば本当にやらなければいけないことに時間をさくことができます。
このwebアプリはそんな「しないことリスト」(NoTodo)をつくるためのものです。ツイッターでリストを公開することも出来ます。

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について書こうと思います。

紹介

今回始めてスマホ優先のサイトを作りました。いままではずっとパソコン優先のサイトでした。

NOtodotop.png

↑ログイン前のTopページ TodoではなくNoTodoが目立つようにしました。
登録ページにはSNS認証が実装されています。(facebook認証が調子悪いときがあります。できないときはこのQiitaの記事のコメントにかいてください。)

notodo2.png

↑ログイン後のtopページ ずっと表示され続けるページと保存した翌日には非表示になるページがありますがデザインは一緒です。

ridgepole

使ってみた感想としてはすごく便利っです。migrateファイルをいちいち作らなくてもいいのはすごく楽でスマートです。ただ初めてなので結構つまずいたとこがありました。今回はそのうち2つを説明しようと思います。

migrate
create_table :posts do |t|
ridgepole
create_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お願いします。

https://notodoo.herokuapp.com/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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分で理解!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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分で理解!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

参考

webpacker, yarn

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails6でPG::ConnectionBadというエラーが出た原因と対応

エラーが出た手順

rails sでWebサーバー起動

http://localhost:3000/ にアクセスすると
PG::ConnectionBadというエラーが出た

3C20CC71-D23D-4497-A685-5704DA0F704A.png

原因

db(PostgreSQL)のほうはdockerで立ち上げる構成だったのだけど
立ち上げ忘れていたので、dbに接続できない状態だった。

対応

docker-compose up -dでDBを起動する

確認

webページの再読み込み

正常に表示された OK!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

バリデーションのエラーメッセージの個別表示

自分用です!!

バリデーションでのエラーメッセージをviewで表示していきます。

例えば、userを登録するときに、passwordとnameにpresence: trueのバリデーションをかけた、状態でパスワードとnameを空欄で登録すると、バリデーションに引っかかります。

railsのオブジェクトは、デフォルトでエラーオブジェクトを保持しているので、画像のようにエラーメッセージを取得できます。

ここでエラーメッセージが日本語化されているのは、i18nの働きによるものです。

さらに、full_messagesというメソッドを使用することで、エラーメッセージだけを取り出すことができます。

スクリーンショット 2020-08-06 3.06.38.png

エラーメッセージは、色々な入力フォームで使いたいので、パーシャルを使います。

_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のエラー文を表示してくれます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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にあげていたが、実際にチーム開発をすることで個人開発では得られなかった経験をすることができた。サマーインターンでチーム開発をすることがあるので、この経験を活かしていきたいと思う。
 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 update

webpackerのインストール

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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む