20200204のRailsに関する記事は11件です。

herokuのデータのバックアップとローカルのデータベースへのリストア

しょぼいメモを残します
データベースでpostgresqlを使っている時のみです。

バックアップ

curl -o latest.dump `heroku pg:backups public-url -a アプリ名`

リストア

pg_restore --verbose --clean --no-acl --no-owner -h localhost -U ローカルのDBユーザー名 -d ローカルDB名 latest.dump
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[rubocop] インデントを揃えるgem【初学者おすすめgem】

概要

今回は効率よくインデントを揃えるgemの紹介をします。

プログラミングを学習してはや6ヶ月経ちました。
少しづつ転職も始め、ある企業様から私のgitをみてインデントがオカシイと非常にありがたいフィードバックを頂きました。
そこで第三者からみてコードの読みやすさの大切さに改めて気づきました。

こんな人に読んでほしい!!

  • 現在、プログラミングを独学またはプログラミングスクールで学び、転職しよう!って思っている方。
  • インデントを効率よく揃える方法を知りたい!って思っている方。
  • 綺麗なコードを書きたい!って方。

綺麗なコードを書く重要性

エンジニアとして働くにあたり、
他の人が読みやすいコードを意識する必要性があると思いました。

理由

(1)プロダクトの共同開発
(2)万が一、担当から外る or 担当のメンバーが増えた際に、プロダクトのスムーズな引き継ぎ
(3)バクが起きた際に、可読性が高いと解決できるスピードが早まる

※あくまで私が思い浮かんだ理由をあげました。もしかしたら他にも理由があるかも知れません。

以上の理由から、現場に入った後を考えると、他の人が読みやすいコードが重要性を増します。
今回は他の人が読みやすいコードにするための1つとして「インデントを揃える」をピックアップしました。

rubocupとは?

RuboCopは、あなたのプロジェクトのrubyコードが「コーディング規約どおりに書かれているか」をチェックする静的コード解析ツールです。
その1つの機能として、インデントを揃えてくれます。

使い方

導入

gemfile.
group :development do
  gem 'rubocop', require: false #追記
end

gemファイルのdevelopmentにgem 'rubocop', require: falseを追記します。

その後にターミナルで
$ rails bundle installを忘れずにします。

インデントを揃えよう

ターミナルで
$ rubocop 修正したいファイル名をすると自動修正されます。
【例】 $ rubocop sample.html.erb
これで修正されたと思います。
実行するとこのような感じで出ると思います。
スクリーンショット 2020-02-04 22.28.59.png
出てきた内容を見ると、
[Corrected] Layout/IndentationWidth: Use 2 (not 4) spaces for indentation.
インデントを4つではなくて2つに修正しました。
こんな感じで実行後には、実行した内容を書いてくれてます。

rubocopは「ある程度」まで修正できます。どういう風に修正したらいいのかわからない場合はスキップされるみたいです。

参考URL

RuboCopをRailsオプションやLintオプションで使ってみよう
公式ドキュメント
【初心者向け】Railsの品質を上げるRuboCopのインストールと使い方!

最後に

まだまだ勉強不足なところがありますので、
アップデートできた知識は追加で記載していきます。

もし、何か修正点とかございましたらコメント等
大変恐縮ですが、宜しくおねがいします。

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

rails チュートリアル2週目~ヘルスケアwebサービスを自分で作る医者の日記~

2章最後でつまづく

We're sorry, but something went wrong.
If you are the application owner check the logs for more information.
heroku run rails db:migrate

するもだめ

ログ確認すると

2020-02-04T11:59:16.635000+00:00 heroku[router]: at=info method=GET path="/" host=hidden-ravine-49191.herokuapp.com request_id=309bfa62-5483-4e2d-84c9-2e306989f7d0 fwd="118.109.109.117" dyno=web.1 connect=0ms service=13ms status=500 bytes=1827 protocol=https
2020-02-04T11:59:16.622565+00:00 app[web.1]: I, [2020-02-04T11:59:16.622454 #4] INFO -- : [309bfa62-5483-4e2d-84c9-2e306989f7d0] Started GET "/" for 118.109.109.117 at 2020-02-04 11:59:16 +0000
2020-02-04T11:59:16.623211+00:00 app[web.1]: I, [2020-02-04T11:59:16.623135 #4] INFO -- : [309bfa62-5483-4e2d-84c9-2e306989f7d0] Processing by UsersController#index as HTML
2020-02-04T11:59:16.623867+00:00 app[web.1]: I, [2020-02-04T11:59:16.623790 #4] INFO -- : [309bfa62-5483-4e2d-84c9-2e306989f7d0] Rendering users/index.html.erb within layouts/application
2020-02-04T11:59:16.628739+00:00 app[web.1]: D, [2020-02-04T11:59:16.628649 #4] DEBUG -- : [309bfa62-5483-4e2d-84c9-2e306989f7d0] User Load (1.4ms) SELECT "users".* FROM "users"
2020-02-04T11:59:16.629812+00:00 app[web.1]: I, [2020-02-04T11:59:16.629733 #4] INFO -- : [309bfa62-5483-4e2d-84c9-2e306989f7d0] Rendered users/index.html.erb within layouts/application (5.8ms)
2020-02-04T11:59:16.630567+00:00 app[web.1]: I, [2020-02-04T11:59:16.630487 #4] INFO -- : [309bfa62-5483-4e2d-84c9-2e306989f7d0] Completed 500 Internal Server Error in 7ms (ActiveRecord: 1.4ms)
2020-02-04T11:59:16.631974+00:00 app[web.1]: F, [2020-02-04T11:59:16.631894 #4] FATAL -- : [309bfa62-5483-4e2d-84c9-2e306989f7d0]
2020-02-04T11:59:16.632530+00:00 app[web.1]: F, [2020-02-04T11:59:16.632446 #4] FATAL -- : [309bfa62-5483-4e2d-84c9-2e306989f7d0] ActionView::Template::Error (PG::UndefinedTable: ERROR: relation "users" does not exist
2020-02-04T11:59:16.632535+00:00 app[web.1]: LINE 1: SELECT "users".* FROM "users"
2020-02-04T11:59:16.632538+00:00 app[web.1]: ^
2020-02-04T11:59:16.632541+00:00 app[web.1]: : SELECT "users".* FROM "users"):
2020-02-04T11:59:16.632798+00:00 app[web.1]: F, [2020-02-04T11:59:16.632721 #4] FATAL -- : [309bfa62-5483-4e2d-84c9-2e306989f7d0] 12: </thead>
2020-02-04T11:59:16.632801+00:00 app[web.1]: [309bfa62-5483-4e2d-84c9-2e306989f7d0] 13:
2020-02-04T11:59:16.632804+00:00 app[web.1]: [309bfa62-5483-4e2d-84c9-2e306989f7d0] 14: <tbody>
2020-02-04T11:59:16.632807+00:00 app[web.1]: [309bfa62-5483-4e2d-84c9-2e306989f7d0] 15: <% @users.each do |user| %>
2020-02-04T11:59:16.632810+00:00 app[web.1]: [309bfa62-5483-4e2d-84c9-2e306989f7d0] 16: <tr>
2020-02-04T11:59:16.632812+00:00 app[web.1]: [309bfa62-5483-4e2d-84c9-2e306989f7d0] 17: <td><%= user.name %></td>
2020-02-04T11:59:16.632814+00:00 app[web.1]: [309bfa62-5483-4e2d-84c9-2e306989f7d0] 18: <td><%= user.email %></td>
2020-02-04T11:59:16.632921+00:00 app[web.1]: F, [2020-02-04T11:59:16.632847 #4] FATAL -- : [309bfa62-5483-4e2d-84c9-2e306989f7d0]
2020-02-04T11:59:16.633036+00:00 app[web.1]: F, [2020-02-04T11:59:16.632965 #4] FATAL -- : [309bfa62-5483-4e2d-84c9-2e306989f7d0] app/views/users/index.html.erb:15:in `_app_views_users_index_html_erb__912488948079837038_47098446139840'
^C

よくわからない。

とりあえず3章へいく

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

if current_user.admin? && !current_user?(user)で「!」が必要な理由

事象

railsチュートリアル「10.4.2 destroyアクション」において、
「現在のユーザーが管理者のときに限り」という条件を作るためにチュートリアルでは、下記のように記載されていました。

app/views/users/_user.html.erb
 <% if current_user.admin? && !current_user?(user) %>

2つの条件が成り立つ場合のif文になります。
右の条件において先頭に「!」が必要になる理由がわからなかったので記事にしました。
具体的には、現在のユーザーを「!」で否定すると条件が成り立たないのではと疑問に思っていました。

※左の条件は現在ログインしているユーザーが管理者権限を持つか判定しています。

!の意味

!はnot演算子です。
式の値が真である時偽を、偽である時真を返します。

!true=>false
!false=>true

!current_user?(user)が何を表すか

current_user?の判定を「!」で逆転しています。
ここでは、
current_user?がtrueならfalseで返し、
current_user?がfalseならtrueで返します。

忘れがちなeach文の存在

チュートリアルでややこしいことをやっているといま自分が何をやっているかわからなくなってしまうことが1周目だとちょくちょくありました。それゆえの失念だと思いますが、

そもそも、表題の条件文はeach文の中にあります。
下記がrender元のeach文です

app/views/users/index.html.erb
  <ul class="users">
     #省略型のeach文を表しています
   <%= render @users %>
  </ul>

下記がrender先になります。

app/views/users/_user.html.erb
<li>
  <%= gravatar_for user, size: 50 %>
  <%= link_to user.name, user %>
  <% if current_user.admin? && !current_user?(user) %>
    | <%= link_to "delete", user, method: :delete,
                                  data: { confirm: "You sure?" } %>
  <% end %>
</li>

このためuserの部分にユーザーの全レコードがそれぞれ入れられていく処理になります。
するとif分の結果として下記の4通りあるはずです。

①真&&真(=管理者 かつ userがcurrent_user)
②真&&偽(=管理者 かつ userがcurrent_userではない)
③偽&&真(=非管理者 かつ userがcurrent_user)
④偽&&偽(=非管理者 かつ userがcurrent_userではない)

そして!current_user?(user)
この条件はeach分によって割り当てられたuserが現在ログイン中のcurrent_user(=ここでは管理者)と同じかを判定しています。
管理者で無い限り!falseとなり、結果trueになります。

初見ではちょっとわかりづらい部分かと思います。
成り立つのは②の「管理者 かつ userがcurrent_userではない」時のみになります。

まとめ

if current_user.admin? && !current_user?(user)
上記の文は管理者かつeach文によって割り当てられたuserが管理者(current_user)で無い限り成り立つことになります。

よって、管理者がユーザー一覧ページを開くと管理者以外のユーザーに対して削除ボタンを追加できるようになっています。
つまり管理者が自分自身を消せないようにもなっています。

参考

https://docs.ruby-lang.org/ja/latest/doc/symref.html
https://teratail.com/questions/198416

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

【jQuery】リロードしないとプレビュー表示できない→turbolinksが原因

写真投稿アプリを作成中に、jQueryを用いてプレビュー表示機能を実装しようとした所、うまくプレビュー機能が機能せず、リロードを行うとプレビュー表示が成功するという問題に直面しました。
調べた所、初心者に多いミスらしく、私自身もJavaScriptにおいてturbolinksの概念が無かったため、解決にかなり時間を要しました。
ネット上でも同じような悩みを抱えている方が多かったため、参考になればと思い手順をまとめました。

環境

ruby 2.5.1
rails 5.2.4

やりたかった事

写真投稿フォーム

画像を選択

選択した画像がプレビュー表示される

発生した問題

写真投稿フォーム

画像を選択

$\color{red}{\rm 選択した画像がプレビュー表示されない}$

リロード

画像を選択

選択した画像がプレビュー表示される

問題のコード

jquery
  $(function(){
    fileField = $('#file')
    #選択された画像を取得し表示
    fileField.on('change', fileField, function(e) {
      var file = e.target.files[0]
      var reader = new FileReader(),
      $preview = $("#img-field");
      reader.onload = (function(file) {
        return function(e) {
          $preview.empty();
          $preview.append($('<img>').attr({
            src: e.target.result,
            width: "100%",
            class: "preview",
            title: file.name
          }));
        };
      })(file);
      reader.readAsDataURL(file);
    });
  });

解決方法

jquery
$(document).on('turbolinks:load', function() {
  #上記のコードをここに挟む
});

turbolinksによりJSが機能しない時があるとの事。
上記のように囲むことで、この問題は解決するようです。

turbolinksとは

turbolinksとは、一言で言うとWebアプリケーションをすばやく表示させることができるgemのこと。
rails4以降標準で用意されている。

初心者の方や、慣れている方でも忘れることが多いみたいなので、
JSが変だと思ったら、turbolinksを疑ってみましょう。

参考:https://github.com/turbolinks/turbolinks

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

【Rails】Action Mailerのメール送信に失敗する

症状

Action Mailerを利用したメール送信処理で、以下のエラーを吐いてしまう。
なお以前は正常に送信することができていた。

Net::SMTPAuthenticationError (530 Authentication required):

ArgumentError (SMTP To address may not be blank: []):

原因

環境変数を正常に読み込めていなかった。
SMTP認証情報やお問い合わせ宛先メールアドレスを環境変数に入れていたため、それらの情報を利用することができず、送信に失敗した。

対処

1.アプリケーションサーバー停止
pumactl stop

2.アプリケーションサーバー起動
rails s

根本原因は?

不明。
EC2のインスタンスを止めたり、立ち上げたりしていたので、それが契機っぽいが。。

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

config/initializeにカスタムバリデータを定義する

電話番号にバリデーションを使用する

telephone_numberというgemを使用してカスタムバリデーションを定義する。

まずはgemを定義

gem 'telephone_number'

そしてbundle install!

$ bundle

config/initializeに下記のように定義

config/initialize.rb
module ActiveModel
  module Validations
    class TelValidator < EachValidator
      def validate_each(record, attribute, value)
        return if value.blank?

        record.errors.add attribute, :invalid unless TelephoneNumber.valid?(value, :jp)
      end

      module HelperMethods
        def validates_tel_of(*attr_names)
          validates_with TelValidator, _merge_attributes(attr_names)
        end
      end
    end
  end
end

そしてあとは個別のモデルで下記のようにすれば電話番号のバリデーションがかかってくれる。

hoge.rb
validates :tel, tel: true

参考記事

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

config/initializersにカスタムバリデータを定義する

電話番号にバリデーションを使用する

telephone_numberというgemを使用してカスタムバリデーションを定義する。

まずはgemを定義

gem 'telephone_number'

そしてbundle install!

$ bundle

config/initializeに下記のように定義

config/initializers/tel_validator.rb
module ActiveModel
  module Validations
    class TelValidator < EachValidator
      def validate_each(record, attribute, value)
        return if value.blank?

        record.errors.add attribute, :invalid unless TelephoneNumber.valid?(value, :jp)
      end

      module HelperMethods
        def validates_tel_of(*attr_names)
          validates_with TelValidator, _merge_attributes(attr_names)
        end
      end
    end
  end
end

そしてあとは個別のモデルで下記のようにすれば電話番号のバリデーションがかかってくれる。

hoge.rb
validates :tel, tel: true

参考記事

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

コード書いたことないPdMやPOに捧ぐ、Rails on Dockerハンズオン vol.2 -Hello, Rails on Docker-

この記事はなにか?
この記事はが社内のプログラミング未経験者、ビギナー向けに開催しているRuby on Rails on Dockerハンズオンの内容をまとめたものです。ていうかこの記事を基にそのままハンズオンします。ハンズオンは
1回の内容は喋りながらやると大体40~50分くらいになっています。お昼休みに有志でやっているからです。
現在進行形なので週1ペースで記事投稿していけるように頑張ります。
ビギナーの方のお役にたったり、同じように有志のハンズオンをしようとしている人の参考になれば幸いです。

他のハンズオンへのリンク
Vol.1 - Introduction -
・ Vol.2 - Hello, Rails on Docker -

$, #, >について
$: ローカルでコマンドを実行するときは、頭に$をつけています。
#: コンテナの中でコマンドを実行するときは、頭に#をつけています。
>: Rails console内でコマンド(Rubyプログラム)を実行するときは、頭に>をつけています。

はじめに

第二回目の今回は、Ruby on RailsをDockerコンテナで起動させるHello worldをやっていきます。

今日のゴール

  • Ruby on Rails on Docker で Hello world する

では早速、Ruby on Rails on Docker で Hello world していきましょう!

Hello, Ruby on Rails on Docker

今回は↓の図のように、Docker 上に Rails アプリケーション用のコンテナと PostgreSQL (database) 用のコンテナを作っていきます。
image.png
まず、作業用のディレクトリを作っておきましょう。

$ mkdir Handson
$ cd Handson

今後、このHandsonディレクトリをホームディレクトリとして話を進めますので、特に指定がない場合、Handsonディレクトリでコマンドを叩いたり、Handsonディレクトリから見た相対パスでファイルを編集していると思ってください。

では早速、Ruby on Rails on Docker な環境を構築するために以下の4つのファイルを作成していきます。

  • Dockerfile: Rails アプリ用の Docker image の元となる設計図
  • Gemfile: Rails アプリに必要なgemを記載するファイル
  • Gemfile.lock: Gemfileによってインストールされたgemのバージョン情報などを管理するファイル
  • docker-compose.yml: 今回のアプリをコンテナ起動させるための Dcoker Compose ファイル

Dockerfile

Dockerfile
FROM ruby:2.7.0-alpine3.11

ENV HOME="/app"
ENV LANG=C.UTF-8
ENV TZ=Asia/Tokyo

WORKDIR $HOME

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 $HOME
COPY Gemfile.lock $HOME

RUN bundle install && \
    apk del build-packs

COPY . $HOME
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]

Dockerfile は初めて見る人にとっては「なんだこれ?」なものな気がしますが、読んでみると意外とシンプルです。
頭に大文字で書かれているのが命令と呼ばれるものでコマンドみたいなものです。
今回の Dockerfile では以下の命令を利用しています。

  • FROM: ベースイメージを定義する命令です。ruby イメージを指定しているので、元々 Ruby を使用できるイメージの上に Rails を動かす環境を作っていきます。タグの2.7.0-alpine3.11は2020.02.02時点で Ruby の最新バージョンを軽量ディストリビューションとしておなじみの alpine linux のこれまた最新バージョン3.11上で動かしているものを選んでいます。
  • ENV: 環境変数を定義する命令です。
  • WORKDIR: 作業ディレクトリを定義する命令です。ベースイメージ内に該当のディレクトリがない場合は、そのディレクトリを作成することもしてくれます。
  • COPY: ホストのファイルやディレクトリをイメージ内にコピーする命令です。
  • RUN: コマンドを実行する命令です。
  • EXPOSE: コンテナがリッスンするポートを宣言する命令です。 Rails ではデフォルトで 3000 番ポートを使用するので 3000 を指定してあげています。
  • CMD: ソフトウェアを実行するためのコマンドを定義する命令です。コンテナが起動する時に実行されるコマンドといった方がイメージ湧きやすいかもしれません。少し特別な書き方(["xxx", "xxx"]みたいな)をしますが、Rails アプリケーションを起動させるコマンドはrails server -b 0.0.0.0でして、それをCMDの記法で書いています。

あらかた命令と1行1行の内容について述べてしまいましたが、取りこぼしているところをキャッチアップ。

RUN宣言でapk ~と色々書いている9行目からの部分がありますが、apk addは Alpine linux でパッケージをインストールするコマンドです。
apk updateでパッケージリポジトリの最新のインデックス(インストールできる最新バージョンは何か)を取得してきて、apk upgradeですでにインストールしているパッケージで最新版にアップデートできるものをアップデートします。その後、apk addで Rails を起動するのに必要なパッケージをインストールしていきます。--no-cacheオプションはキャッシュを残さないようにするためのオプションです。不要なキャッシュを残さないことでコンテナ自体を軽量に保つことができます。(コンテナは軽量に保っておいた方がダウンロードに時間がかからなかったり、ホストのボリュームを圧迫しないのでよいとされています。)--virtualオプションはそのインストールしたパッケージ達を一つのグループとして名前づけしています。今回の例だとbuild-packsという名前をつけています。ここでインストールしたパッケージは Rails をビルドする上では必要なのですが起動させるためには不要なのであとでapk delで削除するために名前づけしています。

その後、COPY命令でGemfileGemfile.lockをイメージ内にコピーして、bundle installを実行してます。bundle installGemfileの内容に沿ってgemをインストールするコマンドです。Rails 自体もgemでインストールできるのでGemfilerailsを記入しておけばこのbundle installの際にインストールされます。Gemfile.lockはすでにインストール済のgemのバージョンなどを管理して無闇にバージョンアップさせないようにしてくれます。

最後にCOPY . $HOMEでローカルホストのファイルを一式イメージ内にコピーすることで Rails アプリケーションを起動させられる Docker image を作ることができます。

Gemfile

Gemfile
source 'https://rubygems.org'
gem 'rails', '~>6'

Gemfileはかなりシンプルで、インストールするgemのソースとrailsgem をインストールすることを定義しています。Rails は2020.02.02時点で最新バージョンが 6.0.2 なのでまぁメジャーバージョンとして 6 のものをインストールしてくださいというような指定の仕方をしています。
Rails アプリケーションでは最初rails newコマンドでアプリに必要なgemやファイルをインストール・生成するため、初期ではこれほどシンプルなGemfileがあるだけで構わないのです。

Gemfileでバージョンを指定する表現方法はいくつかあります。gemはGitHubなどで公開されていることが多くて大体 README でこう Gemfile に記載してくれと書かれていることが多いのであんまり気にすることはないかもしれませんが一応紹介。

  • gem 'rails', '6.0.0': 絶対 6.0.0 をインストール(バージョンを定義)
  • gem 'rails', >= 6.0.0': 6.0.0 より最新のものをインストール(最低バージョンを定義)
  • gem 'rails', >= 6.0.0', < 6.0.2: 6.0.0 以上 6.0.2 未満のバージョンをインストール(バージョンの範囲を定義)
  • gem 'rails', '~> 6.0.0': 6.0.X のバージョンをインストール(マイナーバージョンを定義)

Gemfile.lock

Gemfile.lockは最初にbundle installされるときに書き込まれるので、最初は空ファイルで問題ありません。

$ touch Gemfile.lock

touchコマンドはファイルの更新日時を現在時刻に更新するためのコマンドですが、ファイルが存在しない場合は空ファイルを生成してくれるのでGemfile.lockを生成するために使いました。

docker-compose.yml

docker-compose.yml
version: '3'

services:
  db:
    image: postgres:12.1-alpine
    environment:
      - TZ=Asia/Tokyo
    volumes:
      - ./tmp/db:/var/lib/postgresql/data

  web:
    build: .
    volumes:
      - .:/app
    ports:
      - 3000:3000
    depends_on:
      - db

まず、サービスとしてdbwebの二つがあることがわかるかと思います。dbは文字通りデータベース用のコンテナ(サービス)、webは Rails アプリケーションを動作させるコンテナ(サービス)です。dbコンテナではimagepostgres:12.1-alpineを指定しています。
docker-compose.ymlについては前回もお話したので、前回お話していない項目を中心にお話します。

  • environment: コンテナを起動する時に環境変数としてセットする。この場合、TZ(タイムゾーン)を東京にしてる。
  • volumes: コンテナからホストのディレクトリをマウントしている。左がホストのパス、右がコンテナ内のパス。ホストのパスはこのdocker-compose.ymlの場所からの相対パスで書いてます。こう書くことで簡単にいえば、ホストのパスとコンテナのパスを同期しているイメージになり、ホストでファイルを編集すればコンテナ内にも反映され、コンテナ内でファイルが編集されればホストのファイルにも反映されるという関係を気づけます。コンテナはステートレスなので一度コンテナを削除して新しくコンテナを起動させた場合、最初のコンテナ(削除したコンテナ)内で変更されたデータは全てなかったことになってしまうのですが、ホストのディレクトリに同期しておくことで次に起動するコンテナもそのディレクトリをマウントするのでデータが永続化されるようになります。
  • build: build: .docker-compose.ymlと同じディレクトリのDockerfileをbuildしたイメージを使ってコンテナを起動するようになります。
  • depends_on: コンテナの依存関係を定義します。今回の場合『webdbに依存している』と定義していることになりますが、これは次の2つが実現されます。
    • docker-compose upした時に、dbコンテナが起動してからwebコンテナを起動する
    • docker-compose up webwebコンテナを起動させようとした場合、dbコンテナも起動させる

Rails アプリケーションを新規作成

まずはrails newコマンドで新規に Rails アプリを作成します。

$ docker-compose run --rm --no-deps web rails new . -fGTd postgresql
...
Webpacker successfully installed ??

いろいろな要素のあるコマンドですね。ちょっと順を追って説明します。
まずこのコマンドの大枠はdocker-compose run [options] <service name> <command>です。今回の例では、

  • [options]: --rm --no-deps
  • <service name>: web
  • <command>: rails new . -fGTd postgresql

となっています。docker-compose rundocker-compose.ymlの定義にそって対象のコンテナを立ち上げてその中でコマンドを実行し、実行後にコンテナを停止するコマンドです。つまり、webコンテナを立ち上げてrails new . -fGTd postgresqlwebコンテナ内で実行してくれます。
--rmオプションはコマンド実行後にコンテナを停止した後に削除までしてくれるオプションです。基本停止したコンテナは不要だと思うのでこのオプションをつけるのがよきかと思います。
--no-depsオプションはdocker-compose.ymldepends_onが定義されていたとしてもそれを無視してdocker-compose runを実行することができます。

次にwebコンテナで実行されるrails new . -fGTd postgresqlを見ていきましょう。
まずrails newは Rails アプリケーションを新規作成するためのコマンドです。.はアプリケーションを作成する場所を指していてカレントディレクトリ(コマンドが実行されたディレクトリ)を示しています。-fGTdはオプションなので一つずつ紐解きます。

  • -f: ファイルの上書きを強制する。Gemfileなどに上書きが走りますがいちいち Yes or No を聞かれないようにするためにつけています。
  • -G: Gitの初期設定をスキップします。Rails 6 からなんかこのオプションをつけないとまともにrails newできなかったのでつけてます。
  • -T: minitestという Rails でデフォルトでインストールされるテストフレームワークのインストールをスキップします。僕はRSpecというテストフレームワークをよく使っているのでこのオプションをつけて無駄にminitestがインストールされないようにしています。
  • -d: -d <database name>で利用するデータベースを指定します。今回はpostgresqlを指定。

Rails アプリケーションの新規作成ができたら一度イメージをビルドしておきましょう。

$ docker-compose build
...
Successfully tagged handson_web:latest

docker-compose buildコマンドはdocker-compose.ymlDockerfileからのビルドが必要なサービスのイメージビルドをすべて実行してくれます。今回はdbは DockerHub のイメージを使っているのでwebのみがビルドが必要なサービスとしてビルドされます。

DBの接続設定

ビルドが終わったら、Rails アプリケーションの DB 接続設定をコーディングしていきます。 DB の接続設定はconfig/database.ymlに記載します。 Rails では設定系のファイルはconfigディレクトリに格納されています。

config/database.yml
...
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password:
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
...

hostusernamepasswordがデフォルトから新たに追加した項目です。
hostはDBのホスト名です。今 Rails アプリが稼働しているwebコンテナはdocker-compose.ymlが作る Docker ネットワークの中にいます。この中ではサービス名で名前解決してコンテナが相互に接続することができます。つまりhostとしてdbを設定することでdocker-compose.ymldbのサービス名で定義されたコンテナに接続できるようになるのです。
usernamepasswordは何の値なのでしょうか?これはdbコンテナのイメージで指定したpostgresのデフォルト値です。passwordは同じホストからのアクセスであれば省略が可能になっています。postgresイメージのusernamepasswordはそれぞれPOSTGRES_USERPOSTGRES_PASSWORDの環境変数で定義することもできます。

データベースの設定はここまでです。このデフォルトの設定値が開発環境(development)、テスト環境(test)の設定値として反映されるようになっています。

データベースを作成する

データベースの接続設定を定義したので、データベースを実際に作成していきます。
Rails ではデフォルトで本番環境(production)、開発環境(development)、テスト環境(test)の3つの環境(environment)が用意されています。特別に環境を指定しない場合、開発環境で挙動するようになっています。
データベースの作成はrails db:createコマンドを使いますが、このコマンドは開発環境とテスト環境用にデータベースを作成してくれます。本番環境用のデータベースを作成する場合は、RAILS_ENV=productionをオプションとしてつけます。
今回はまず開発環境向けにデータベースの作成を行いたいので、以下のコマンドを実行します。

$ docker-compose run --rm rails db:create

Hello Ruby on Rails on Docker!!

ここまでで Hello world に必要な作業は全て完了しました。
コンテナを立ち上げて Hello world ページが表示されることを確認しましょう。

$ docker-compose up -d

Rails アプリは少し起動に時間がかかります。すぐにhttp://localhost:3000にアクセスしてもまだアプリケーションが起動していないこともありますので、その場合はdocker-compose logsコマンドを使ってアプリケーションの起動状態を確認してみましょう。

$ docker-compose logs -f

-fオプションはログの変化をリアルタイムでコンソールに表示するためのオプションです。Rails アプリが起動した場合

web_1 | Use Ctrl+C to stop

というログが表示されます。この表示を確認したらCtrl+Cdocker-compose logsから抜け出しましょう。

では、http://localhost:3000にアクセスしてみましょう!
image.png
このようなページが表示されたでしょうか?このページが Rails アプリケーションの第一歩、つまり Hello world です。おめでとうございます!これでもう『Rails は Hello world までならやったことあります』と自慢することができます!

ここまでできたら後片付けをしておきましょう。
このままではコンテナが起動しっぱなしになってしまうので、最後にコンテナを停止させておきます。

$ docker-compose down

これでコンテナを停止させたので、http://localhost:3000にアクセスしても先ほどのページは表示されなくなっていることでしょう。

まとめ

今回は、Dockerfiledocker-compose.ymlなどのファイルを作成し、Rails アプリケーションを稼働させられる Docker イメージ、Docker コンテナを作成してみました。
さらに、Rails アプリを新規作成して Hello world に成功しました!
まだまだアプリケーション開発のほんの入り口ですが、Docker を使って Web アプリを起動させることができただけでもかなり感動があると思いますし、ここまでさほど大変ではないことも感じてもらえたかなと思います。これこそが Rails や Docker の偉大なところですね。

次回は、scaffoldという Rails の便利機能を使って、サンプル Web アプリケーションを作ってみようと思います。このscaffoldで作成できるアプリケーションが Rails の基本的なアプリケーションの形になりまして、その中には Rails アプリケーションを語る上では避けられないRESTfulMVCの要素が詰まっていますので、その辺りも合わせて学んでいけるようにしようと思います。

では、次回も乞うご期待!ここまでお読みいただきありがとうございました!

Reference

P.S. 間違っているところ、抜けているところ、説明の仕方を変えるとよりわかりやすくなるところなどありましたら、優しくアドバイスいただけると助かります。

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

コード書いたことないPdMやPOに捧ぐ、Ruby on Rails on Dockerハンズオン vol.2 -Hello, Ruby on Rails on Docker-

この記事はなにか?
この記事はが社内のプログラミング未経験者、ビギナー向けに開催しているRuby on Rails on Dockerハンズオンの内容をまとめたものです。ていうかこの記事を基にそのままハンズオンします。ハンズオンは
1回の内容は喋りながらやると大体40~50分くらいになっています。お昼休みに有志でやっているからです。
現在進行形なので週1ペースで記事投稿していけるように頑張ります。
ビギナーの方のお役にたったり、同じように有志のハンズオンをしようとしている人の参考になれば幸いです。

他のハンズオンへのリンク
- Ruby on Rails on Dockerハンズオン vol.1 -Introduction-

$, #, >について
$: ローカルでコマンドを実行するときは、頭に$をつけています。
#: コンテナの中でコマンドを実行するときは、頭に#をつけています。
>: Rails console内でコマンド(Rubyプログラム)を実行するときは、頭に>をつけています。

はじめに

第二回目の今回は、Ruby on RailsをDockerコンテナで起動させるHello worldをやっていきます。

今日のゴール

  • Ruby on Rails on Docker で Hello world する

では早速、Ruby on Rails on Docker で Hello world していきましょう!

Hello, Ruby on Rails on Docker

今回は↓の図のように、Docker 上に Rails アプリケーション用のコンテナと PostgreSQL (database) 用のコンテナを作っていきます。
image.png
まず、作業用のディレクトリを作っておきましょう。

$ mkdir Handson
$ cd Handson

今後、このHandsonディレクトリをホームディレクトリとして話を進めますので、特に指定がない場合、Handsonディレクトリでコマンドを叩いたり、Handsonディレクトリから見た相対パスでファイルを編集していると思ってください。

では早速、Ruby on Rails on Docker な環境を構築するために以下の4つのファイルを作成していきます。

  • Dockerfile: Rails アプリ用の Docker image の元となる設計図
  • Gemfile: Rails アプリに必要なgemを記載するファイル
  • Gemfile.lock: Gemfileによってインストールされたgemのバージョン情報などを管理するファイル
  • docker-compose.yml: 今回のアプリをコンテナ起動させるための Dcoker Compose ファイル

Dockerfile

Dockerfile
FROM ruby:2.7.0-alpine3.11

ENV HOME="/app"
ENV LANG=C.UTF-8
ENV TZ=Asia/Tokyo

WORKDIR $HOME

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 $HOME
COPY Gemfile.lock $HOME

RUN bundle install && \
    apk del build-packs

COPY . $HOME
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]

Dockerfile は初めて見る人にとっては「なんだこれ?」なものな気がしますが、読んでみると意外とシンプルです。
頭に大文字で書かれているのが命令と呼ばれるものでコマンドみたいなものです。
今回の Dockerfile では以下の命令を利用しています。

  • FROM: ベースイメージを定義する命令です。ruby イメージを指定しているので、元々 Ruby を使用できるイメージの上に Rails を動かす環境を作っていきます。タグの2.7.0-alpine3.11は2020.02.02時点で Ruby の最新バージョンを軽量ディストリビューションとしておなじみの alpine linux のこれまた最新バージョン3.11上で動かしているものを選んでいます。
  • ENV: 環境変数を定義する命令です。
  • WORKDIR: 作業ディレクトリを定義する命令です。ベースイメージ内に該当のディレクトリがない場合は、そのディレクトリを作成することもしてくれます。
  • COPY: ホストのファイルやディレクトリをイメージ内にコピーする命令です。
  • RUN: コマンドを実行する命令です。
  • EXPOSE: コンテナがリッスンするポートを宣言する命令です。 Rails ではデフォルトで 3000 番ポートを使用するので 3000 を指定してあげています。
  • CMD: ソフトウェアを実行するためのコマンドを定義する命令です。コンテナが起動する時に実行されるコマンドといった方がイメージ湧きやすいかもしれません。少し特別な書き方(["xxx", "xxx"]みたいな)をしますが、Rails アプリケーションを起動させるコマンドはrails server -b 0.0.0.0でして、それをCMDの記法で書いています。

あらかた命令と1行1行の内容について述べてしまいましたが、取りこぼしているところをキャッチアップ。

RUN宣言でapk ~と色々書いている9行目からの部分がありますが、apk addは Alpine linux でパッケージをインストールするコマンドです。
apk updateでパッケージリポジトリの最新のインデックス(インストールできる最新バージョンは何か)を取得してきて、apk upgradeですでにインストールしているパッケージで最新版にアップデートできるものをアップデートします。その後、apk addで Rails を起動するのに必要なパッケージをインストールしていきます。--no-cacheオプションはキャッシュを残さないようにするためのオプションです。不要なキャッシュを残さないことでコンテナ自体を軽量に保つことができます。(コンテナは軽量に保っておいた方がダウンロードに時間がかからなかったり、ホストのボリュームを圧迫しないのでよいとされています。)--virtualオプションはそのインストールしたパッケージ達を一つのグループとして名前づけしています。今回の例だとbuild-packsという名前をつけています。ここでインストールしたパッケージは Rails をビルドする上では必要なのですが起動させるためには不要なのであとでapk delで削除するために名前づけしています。

その後、COPY命令でGemfileGemfile.lockをイメージ内にコピーして、bundle installを実行してます。bundle installGemfileの内容に沿ってgemをインストールするコマンドです。Rails 自体もgemでインストールできるのでGemfilerailsを記入しておけばこのbundle installの際にインストールされます。Gemfile.lockはすでにインストール済のgemのバージョンなどを管理して無闇にバージョンアップさせないようにしてくれます。

最後にCOPY . $HOMEでローカルホストのファイルを一式イメージ内にコピーすることで Rails アプリケーションを起動させられる Docker image を作ることができます。

Gemfile

Gemfile
source 'https://rubygems.org'
gem 'rails', '~>6'

Gemfileはかなりシンプルで、インストールするgemのソースとrailsgem をインストールすることを定義しています。Rails は2020.02.02時点で最新バージョンが 6.0.2 なのでまぁメジャーバージョンとして 6 のものをインストールしてくださいというような指定の仕方をしています。
Rails アプリケーションでは最初rails newコマンドでアプリに必要なgemやファイルをインストール・生成するため、初期ではこれほどシンプルなGemfileがあるだけで構わないのです。

Gemfileでバージョンを指定する表現方法はいくつかあります。gemはGitHubなどで公開されていることが多くて大体 README でこう Gemfile に記載してくれと書かれていることが多いのであんまり気にすることはないかもしれませんが一応紹介。

  • gem 'rails', '6.0.0': 絶対 6.0.0 をインストール(バージョンを定義)
  • gem 'rails', >= 6.0.0': 6.0.0 より最新のものをインストール(最低バージョンを定義)
  • gem 'rails', >= 6.0.0', < 6.0.2: 6.0.0 以上 6.0.2 未満のバージョンをインストール(バージョンの範囲を定義)
  • gem 'rails', '~> 6.0.0': 6.0.X のバージョンをインストール(マイナーバージョンを定義)

Gemfile.lock

Gemfile.lockは最初にbundle installされるときに書き込まれるので、最初は空ファイルで問題ありません。

$ touch Gemfile.lock

touchコマンドはファイルの更新日時を現在時刻に更新するためのコマンドですが、ファイルが存在しない場合は空ファイルを生成してくれるのでGemfile.lockを生成するために使いました。

docker-compose.yml

docker-compose.yml
version: '3'

services:
  db:
    image: postgres:12.1-alpine
    environment:
      - TZ=Asia/Tokyo
    volumes:
      - ./tmp/db:/var/lib/postgresql/data

  web:
    build: .
    volumes:
      - .:/app
    ports:
      - 3000:3000
    depends_on:
      - db

まず、サービスとしてdbwebの二つがあることがわかるかと思います。dbは文字通りデータベース用のコンテナ(サービス)、webは Rails アプリケーションを動作させるコンテナ(サービス)です。dbコンテナではimagepostgres:12.1-alpineを指定しています。
docker-compose.ymlについては前回もお話したので、前回お話していない項目を中心にお話します。

  • environment: コンテナを起動する時に環境変数としてセットする。この場合、TZ(タイムゾーン)を東京にしてる。
  • volumes: コンテナからホストのディレクトリをマウントしている。左がホストのパス、右がコンテナ内のパス。ホストのパスはこのdocker-compose.ymlの場所からの相対パスで書いてます。こう書くことで簡単にいえば、ホストのパスとコンテナのパスを同期しているイメージになり、ホストでファイルを編集すればコンテナ内にも反映され、コンテナ内でファイルが編集されればホストのファイルにも反映されるという関係を気づけます。コンテナはステートレスなので一度コンテナを削除して新しくコンテナを起動させた場合、最初のコンテナ(削除したコンテナ)内で変更されたデータは全てなかったことになってしまうのですが、ホストのディレクトリに同期しておくことで次に起動するコンテナもそのディレクトリをマウントするのでデータが永続化されるようになります。
  • build: build: .docker-compose.ymlと同じディレクトリのDockerfileをbuildしたイメージを使ってコンテナを起動するようになります。
  • depends_on: コンテナの依存関係を定義します。今回の場合『webdbに依存している』と定義していることになりますが、これは次の2つが実現されます。
    • docker-compose upした時に、dbコンテナが起動してからwebコンテナを起動する
    • docker-compose up webwebコンテナを起動させようとした場合、dbコンテナも起動させる

Rails アプリケーションを新規作成

まずはrails newコマンドで新規に Rails アプリを作成します。

$ docker-compose run --rm --no-deps web rails new . -fGTd postgresql
...
Webpacker successfully installed ??

いろいろな要素のあるコマンドですね。ちょっと順を追って説明します。
まずこのコマンドの大枠はdocker-compose run [options] <service name> <command>です。今回の例では、

  • [options]: --rm --no-deps
  • <service name>: web
  • <command>: rails new . -fGTd postgresql

となっています。docker-compose rundocker-compose.ymlの定義にそって対象のコンテナを立ち上げてその中でコマンドを実行し、実行後にコンテナを停止するコマンドです。つまり、webコンテナを立ち上げてrails new . -fGTd postgresqlwebコンテナ内で実行してくれます。
--rmオプションはコマンド実行後にコンテナを停止した後に削除までしてくれるオプションです。基本停止したコンテナは不要だと思うのでこのオプションをつけるのがよきかと思います。
--no-depsオプションはdocker-compose.ymldepends_onが定義されていたとしてもそれを無視してdocker-compose runを実行することができます。

次にwebコンテナで実行されるrails new . -fGTd postgresqlを見ていきましょう。
まずrails newは Rails アプリケーションを新規作成するためのコマンドです。.はアプリケーションを作成する場所を指していてカレントディレクトリ(コマンドが実行されたディレクトリ)を示しています。-fGTdはオプションなので一つずつ紐解きます。

  • -f: ファイルの上書きを強制する。Gemfileなどに上書きが走りますがいちいち Yes or No を聞かれないようにするためにつけています。
  • -G: Gitの初期設定をスキップします。Rails 6 からなんかこのオプションをつけないとまともにrails newできなかったのでつけてます。
  • -T: minitestという Rails でデフォルトでインストールされるテストフレームワークのインストールをスキップします。僕はRSpecというテストフレームワークをよく使っているのでこのオプションをつけて無駄にminitestがインストールされないようにしています。
  • -d: -d <database name>で利用するデータベースを指定します。今回はpostgresqlを指定。

Rails アプリケーションの新規作成ができたら一度イメージをビルドしておきましょう。

$ docker-compose build
...
Successfully tagged handson_web:latest

docker-compose buildコマンドはdocker-compose.ymlDockerfileからのビルドが必要なサービスのイメージビルドをすべて実行してくれます。今回はdbは DockerHub のイメージを使っているのでwebのみがビルドが必要なサービスとしてビルドされます。

DBの接続設定

ビルドが終わったら、Rails アプリケーションの DB 接続設定をコーディングしていきます。 DB の接続設定はconfig/database.ymlに記載します。 Rails では設定系のファイルはconfigディレクトリに格納されています。

config/database.yml
...
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password:
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
...

hostusernamepasswordがデフォルトから新たに追加した項目です。
hostはDBのホスト名です。今 Rails アプリが稼働しているwebコンテナはdocker-compose.ymlが作る Docker ネットワークの中にいます。この中ではサービス名で名前解決してコンテナが相互に接続することができます。つまりhostとしてdbを設定することでdocker-compose.ymldbのサービス名で定義されたコンテナに接続できるようになるのです。
usernamepasswordは何の値なのでしょうか?これはdbコンテナのイメージで指定したpostgresのデフォルト値です。passwordは同じホストからのアクセスであれば省略が可能になっています。postgresイメージのusernamepasswordはそれぞれPOSTGRES_USERPOSTGRES_PASSWORDの環境変数で定義することもできます。

データベースの設定はここまでです。このデフォルトの設定値が開発環境(development)、テスト環境(test)の設定値として反映されるようになっています。

データベースを作成する

データベースの接続設定を定義したので、データベースを実際に作成していきます。
Rails ではデフォルトで本番環境(production)、開発環境(development)、テスト環境(test)の3つの環境(environment)が用意されています。特別に環境を指定しない場合、開発環境で挙動するようになっています。
データベースの作成はrails db:createコマンドを使いますが、このコマンドは開発環境とテスト環境用にデータベースを作成してくれます。本番環境用のデータベースを作成する場合は、RAILS_ENV=productionをオプションとしてつけます。
今回はまず開発環境向けにデータベースの作成を行いたいので、以下のコマンドを実行します。

$ docker-compose run --rm rails db:create

Hello Ruby on Rails on Docker!!

ここまでで Hello world に必要な作業は全て完了しました。
コンテナを立ち上げて Hello world ページが表示されることを確認しましょう。

$ docker-compose up -d

Rails アプリは少し起動に時間がかかります。すぐにhttp://localhost:3000にアクセスしてもまだアプリケーションが起動していないこともありますので、その場合はdocker-compose logsコマンドを使ってアプリケーションの起動状態を確認してみましょう。

$ docker-compose logs -f

-fオプションはログの変化をリアルタイムでコンソールに表示するためのオプションです。Rails アプリが起動した場合

web_1 | Use Ctrl+C to stop

というログが表示されます。この表示を確認したらCtrl+Cdocker-compose logsから抜け出しましょう。

では、http://localhost:3000にアクセスしてみましょう!
image.png
このようなページが表示されたでしょうか?このページが Rails アプリケーションの第一歩、つまり Hello world です。おめでとうございます!これでもう『Rails は Hello world までならやったことあります』と自慢することができます!

ここまでできたら後片付けをしておきましょう。
このままではコンテナが起動しっぱなしになってしまうので、最後にコンテナを停止させておきます。

$ docker-compose down

これでコンテナを停止させたので、http://localhost:3000にアクセスしても先ほどのページは表示されなくなっていることでしょう。

まとめ

今回は、Dockerfiledocker-compose.ymlなどのファイルを作成し、Rails アプリケーションを稼働させられる Docker イメージ、Docker コンテナを作成してみました。
さらに、Rails アプリを新規作成して Hello world に成功しました!
まだまだアプリケーション開発のほんの入り口ですが、Docker を使って Web アプリを起動させることができただけでもかなり感動があると思いますし、ここまでさほど大変ではないことも感じてもらえたかなと思います。これこそが Rails や Docker の偉大なところですね。

次回は、scaffoldという Rails の便利機能を使って、サンプル Web アプリケーションを作ってみようと思います。このscaffoldで作成できるアプリケーションが Rails の基本的なアプリケーションの形になりまして、その中には Rails アプリケーションを語る上では避けられないRESTfulMVCの要素が詰まっていますので、その辺りも合わせて学んでいけるようにしようと思います。

では、次回も乞うご期待!ここまでお読みいただきありがとうございました!

Reference

P.S. 間違っているところ、抜けているところ、説明の仕方を変えるとよりわかりやすくなるところなどありましたら、優しくアドバイスいただけると助かります。

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

Rails6 のちょい足しな新機能を試す 119(filter_paramters 編)

はじめに

Rails 6 に追加された新機能を試す第119段。 今回は、filter_parameters 編です。
Rails 6 では、 filter_parameters で proc を使って、フィルターしたときに、パラメータが配列になっているときに、フィルターされないというバグが修正されています。

Ruby 2.6.5, Rails 6.0.2.1, Rails 5.2.4.1 で確認しました。 (Rails 6.0.0 でこの修正が入っています。)

$ rails --version
Rails 6.0.2.1

今回は、User を2つ登録する画面を作って、その動作を確認します。
なお、 filter_parameters の動作を確認するための手抜き実装になってます。

Rails プロジェクトを作る

Rails プロジェクトを新たに作成します。

$ rails new rails_sandbox
$ cd rails_sandbox

User モデルを作る

name を持つ User モデルを作成します。

$ bin/rails g model User name

Controller と View を作る

User の controller と View を作ります。 View は手抜き実装で、 一覧画面 (index) と 登録画面 (new) だけです。

$ bin/rails g controller Users index new

routes を定義する

User 用にルーティングを定義します。 これまた、手抜き実装で、 index, new, create の3つだけにします。

config/routes.rb
Rails.application.routes.draw do
  resources :users, only: %i[index new create]
end

一覧画面 (index) を作成する

一覧画面を作成します。これまた、 table タグも使わない手抜き実装です。

Username を1件ずつ表示します。

画面の下に、登録画面へのリンクを表示します。

app/views/users/index.html.erb
<h1>Users#index</h1>

<% @users.each do |user| %>
  <p>
    <%= user.name %>
  </p>
<% end %>

<%= link_to 'New', new_user_path %>

登録画面 (new) を作成する

登録画面を作成します。ここで、今回の機能を試すためにパラメータの値が配列になるようにします。
(結果として2件のデータを登録することになります。)

パラメータが配列となるように、form.text_field の引数が name="names[]" としていることに注意してください。
これが、今回の機能を試すために必要なことの1つです。

app/views/users/new.html.erb
<h1>Users#new</h1>

<%= form_with url: users_path do |form| %>
  <p>
    <%= form.text_field name="names[]" %>
  </p>
  <p>
    <%= form.text_field name="names[]" %>
  </p>
  <%= form.submit %>
<% end %>

UsersController を完成させる

今回の機能を確認するために、完成させる必要は無いのですが、一応、動作するように実装します。
これまた、手抜き実装です。

index メソッドでは、全データを取得します。
create メソッドでは、パラメータを元にして User をデータベースに保存し、一覧画面にリダイレクトします。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    @users = User.all
  end

  def new
  end

  def create
    params[:names].each do |name|
      User.create(name: name)
    end
    redirect_to users_path
  end
end

filter_parameters を指定して、 names パラメータの値がフィルタリングさせる

いよいよ、今回の機能を確認するために、 filter_parametersnames パラメータの値をフィルタリングさせます。

proc を追加していることに注意してください。

config/application.rb
...
module App
  class Application < Rails::Application
    ...
    config.filter_parameters << lambda do |key, value|
      if key =~ /names/
        value.replace('[FILTERED]') if value.respond_to?(:replace)
      end
    end
  end
end

マイグレーションを実行する

$ bin/rails db:create db:migrate

rails server を実行して、登録画面から登録する

bin/rails s を実行し、 http://localhost:3000/users/new にアクセスし、2つのフィールドに適当に入力して、 Save ボタンを押します。

このとき、コンソールでは、 names パラメータが、 [FILTERED] となっていることに注意してください。

Started POST "/users" for 192.168.16.1 at 2020-01-25 02:48:55 +0000
Processing by UsersController#create as JS
  Parameters: {"authenticity_token"=>"..., "names"=>["[FILTERED]", "[FILTERED]"], "commit"=>"Save "}
     (0.2ms)  BEGIN

Rails5 では

Rails 5.2.4.1 では、 フィルターされず、そのまま画面で入力した値が表示されてしまいます。

Started POST "/users" for 192.168.0.1 at 2020-01-25 01:42:25 +0000
Processing by UsersController#create as JS
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"..., "names"=>["aaa", "bbb"], "commit"=>"Save "}

あくまで手抜きのコードです

実際のアプリでは、複数のデータを1つの画面で登録する場合には、 Form オブジェクトを使ったり、Validation を追加したり、Strong Paramters を使ったりすると思います。
今回は、あくまでも、機能の確認のための実装なので、そういった点は、すっとばして、手を抜いてます。
このままコードを再利用することはオススメしません。

試したソース

https://github.com/suketa/rails_sandbox/tree/try119_filter_parameters_proc

参考情報

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