今回達成すること
今回は、バリデーションエラーメッセージの日本語化(i18n)の実装を行い、同時にapplication.rbセッティングも行います。
具体的に以下の手順で実装していきます。
- ユーザーモデルの「password」カラムにバリデーションを設定
- passwordハッシュ化の設定
- エラーメッセージの日本語化(i18n)の実装
- application.rbのセッティング
- TimeZoneの変更
- Zeitwerkの設定
最終的なapplication.rbとuser.rbはこのようになります。
config/application.rb
require_relative 'boot'
require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "active_storage/engine"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "action_cable/engine"
# require "sprockets/railtie"
require "rails/test_unit/railtie"
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module Myapp8
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 6.0
# 表示TimeZone
config.time_zone = 'Tokyo'
# DB保存時間をlocal(Tokyo)にする
config.active_record.default_timezone = :local
# i18n
config.i18n.default_locale = :ja
# Zeitwerk $LOAD_PATHにPathを追加しない(Zeitwerk有効時false推奨)
config.add_autoload_paths_to_load_path = false
# autoload path => ActiveSupport::Dependencies.autoload_paths
config.autoload_paths += %W(#{config.root}/lib/validator)
# Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers
# -- all .rb files in that directory are automatically loaded after loading
# the framework and any gems in your application.
# Only loads a smaller set of middleware suitable for API only apps.
# Middleware like session, flash, cookies can be added back manually.
# Skip views, helpers and assets when generating a new resource.
config.api_only = true
end
end
app/models/user.rb
class User < ApplicationRecord
before_validation :downcase_email
# password hashing
has_secure_password
# validates
validates :email, presence: true, email: { allow_blank: true }
VALID_PASSWORD_REGEX = /\A[\w\-]+\z/
validates :password, presence: true,
length: { minimum: 8 },
format: {
with: VALID_PASSWORD_REGEX,
message: :invalid_password
},
allow_blank: true
# methods
# 自分以外のactiveなユーザーがいればtrueを返す
def activated?(email)
users = new_record? ? User.all : User.where.not(id: self.id)
users.find_by(email: email, activated: true).present?
end
private
# validatesにemail小文字化
def downcase_email
self.email = email.downcase if self.email
end
end
それでは実装に移りましょう。
passwordカラムのバリデーション
「password」カラムには、このようなバリデーションを設定していきます。
- 入力必須
- 8文字以上
- 書式は英数字、ハイフン、アンダーバーのみ利用可能
app/models/user.rb
class User < ApplicationRecord
...
# validates
validates :email, presence: true, email: { allow_blank: true }
# 追記
VALID_PASSWORD_REGEX = /\A[\w\-]+\z/
validates :password, presence: true,
length: { minimum: 8 },
format: {
with: VALID_PASSWORD_REGEX,
message: :invalid_password
},
allow_blank: true
...
end
入力必須
-
presence: true
trueにすると入力必須となります。
8文字以上
-
length: { minimum: 8 }
文字列の長さの最小を指定するには、lengthにminimumのパラメータを渡します。
ちなみに、最大の長さを制限したいとき =>
{ maximum: 20 }
文字列の長さを完全一致で制限したいとき =>
{ is: 8 }
8文字以上、20文字以下はこのように指定します。 =>
{ minimum: 8, maximum: 20 }
今回は gem 'bcrypt' の設定により、最大72文字までの制限がついていますのでmaximumは指定しません。
書式は英数字、ハイフン、アンダーバーのみ利用可能
Rubyの正規表現を利用します。
-
/\A[\w\-]+\z/
これを分解すると。。。
\A
=> 文字列の先頭一致[ ]
=> [ ]内のいずれか1文字\w
=> 英数字とアンダーバー-
=> ハイフン+
=> 1回以上の繰り返し\z
=> 文字列の文末一致まとめると。。。
「先頭から文末まで英数字かアンダーバーかハイフンを1文字以上繰り返す」文字列に制限しています。
パスワードは制限をかけすぎると第三者に読み解かれるリスクがありますので、許容範囲を広めにしています。
-
format: {}
withのパラメーターには正規表現を渡します。
messageには、表示したいエラーメッセージを渡します。
invalid_passwordは下のja.ymlファイルで作成します。
-
allow_blank: true
nilもしくは空白時に検証をスキップします。
これはまさにRailsチュートリアルの手法を採用しています。
新規ユーザー登録時に空のパスワードが有効になってしまうのかと心配になるかもしれませんが、安心してください。6.3.3で説明したように、
has_secure_password
では (追加したバリデーションとは別に) オブジェクト生成時に存在性を検証するようになっているため、空のパスワード (nil
) が新規ユーザー登録時に有効になることはありません。
以上でユーザーモデルのバリデーションは完了です。
passwordハッシュ化の設定を行う
passwordハッシュ化とは、passwordをランダムな文字列に変換してデータベースに保存することを言います。
ちなみに、暗号化は復元可能、ハッシュ化は復元不可を意味します。
ハッシュ化されたpassword
Gem 'bcrypt'
パスワードを安全なハッシュに変換するには、Gem bcrypt
を使います。
インストールがまだの方は、gem 'bcrypt'
を追記し、$ bundle install
コマンドを実行してください。
Railsのhas_secure_passwordメソッド
ハッシュ化されたパスワードをデータベースに保存するには、Railsのhas_secure_passwordメソッドを使います。
使い方は、has_secure_password
を追記するだけです。
app/models/user.rb
class User < ApplicationRecord
before_validation :downcase_email
# 追記
has_secure_password
...
end
has_secure_password
メソッドは、次のような機能を持ちます。
-
ハッシュ化したパスワードを、データベース内のpassword_digestという属性に保存できるようになる。
-
passwordとpassword_confirmation(パスワード確認フォーム)が使えるようになる。
また、双方が一致するかどうかのバリデーションも追加される。
-
authenticateメソッドが使えるようになる。
引数の文字列がパスワードと一致するとUserオブジェクトを、間違っているとfalse返すメソッド
この機能が使える条件として、モデル内にpassword_digestというカラムが含まれている必要があります。
参考
passwordハッシュ化のまとめ
あれこれ言いましたが、passwordのハッシュ化は
- Gem
bcrypt
をインストールして、 - モデルに
has_secure_password
を追記する
だけで実現します。
エラーメッセージの日本語化(i18n)の実装
さあ、メイントピックのお時間です。
バリデーションエラーを日本語化していきましょう。
i18nとは?
翻訳や多言語化をサポートするためのRubyのGemです。
Rails2.2以降からデフォルトで組み込まれています。
現状の確認
現在の状況を整理します。
ターミナルからrails consoleにログインしユーザーを作成してみます。
myapp $ rails c
irb(main):001:0> user = User.new(email:"user@example.com", password:"pass")
irb(main):002:0> user.save
irb(main):003:0> user.errors.full_messages
=> ["Password is too short (minimum is 8 characters)"]
-
user.errors.full_messages
user変数に保存されているエラーメッセージを確認することができます。
結果、["Password is too short (minimum is 8 characters)"]
パスワードが短いよ!と英語でエラーメッセージが表示されました。
エラーメッセージは英語のままですね。このエラーメッセージを日本語化していきます。
rails consoleからはexit
で抜けておいてください。
1. 日本語化の基となるファイルを作成する
Railsの多言語化対応は、ymlファイルで管理します。
config/locales ディレクトリ直下に、
myapp $ touch config/locales/ja.yml
2. ja.ymlファイルをコピペする
下記のgithubからja.ymlの内容をコピペします。
https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/ja.yml
「Row」ボタンをクリックして全選択するとコピーが簡単に行えますよ。
config/locales/ja.yml
ja:
activerecord:
errors:
messages:
record_invalid: 'バリデーションに失敗しました: %{errors}'
restrict_dependent_destroy:
has_one: "%{record}が存在しているので削除できません"
has_many: "%{record}が存在しているので削除できません"
date:
...
3. application.rbに日本語ファイルを利用する設定を行う
Railsのデフォルトは、en.ymlファイルを参照しています。
myapp $ rails runner "puts I18n.default_locale"
en
$ rails runnerは、Railsの環境下で、引数に渡したRubyコードを実行するコマンドです。$ rails r と省略することができます。
これをja.ymlファイルを参照するように変更します。
config/application.rb
module Myapp8
class Application < Rails::Application
...
# 追記
# i18n
config.i18n.default_locale = :ja
もう一度 $ rails r
で確認してみましょう。
myapp $ rails r "puts I18n.default_locale"
ja
OK!!これでi18nの初期設定は完了です。
rails consoleで確認してみる
もう一度エラーを吐き出すユーザーを作成してみます。
myapp $ rails c
irb(main):001:0> user = User.new(email:"user@example.com", password:"pass")
irb(main):002:0> user.save
irb(main):003:0> user.errors.full_messages
=> ["Passwordは8文字以上で入力してください"]
OK。エラーが日本語になりました!ただ、ここの「password」は「パスワード」と表示したい。。。
これはja.ymlファイルを編集することで実現します。
ja.ymlの編集方法
ja.ymlファイルは自分のアプリに合わせて編集していく必要があります。
activerecord:
以下にモデル名、カラム名の日本語読みを追加します。
config/locales/ja.yml
ja:
activerecord:
models:
user: ユーザー
attributes:
user:
email: メールアドレス
password: パスワード
activated: アクティブプラグ
admin: 管理者プラグ
errors:
...
ja.ymlの注意点
Railsはymlファイルの改行とインデントで日本語化のパスを参照しています。
例えば、カラム名の場合。
attributes: => モデル名 => カラム名 の順に改行とインデントを入れる必要があります。
attributes: # attributes:の直下に
user: # モデル名を指定し
email: メールアドレス # カラム名を指定する
日本語化の確認
ちゃんと設定できたか確認してみましょう。
rails consoleにログインしましょう。
既にconsoleにいる場合は、再読み込みコマンドreload!
を実行してください。
irb(main):028:0> User.model_name.human # モデル名日本語化の確認
=> "ユーザー"
irb(main):029:0> User.human_attribute_name :email # カラム名日本語化の確認
=> "メールアドレス"
irb(main):029:0> User.human_attribute_name :password
=> "パスワード"
irb(main):031:0> User.human_attribute_name :activated
=> "アクティブプラグ"
irb(main):032:0> User.human_attribute_name :admin
=> "管理者プラグ"
上のように日本語化できていれば成功です。
エラーの出るユーザーを保存しようとすると、 全てのエラーメッセージが日本語表示となりました。
irb(main):007:0> user.errors.full_messages
=> ["パスワードは8文字以上で入力してください"]
ja.ymlに独自のエラーメッセージを追加する
上で設定したpasswordの書式エラー「invalid_password」のメッセージを追加します。
app/models/user.rb
format: {
with: VALID_PASSWORD_REGEX,
message: :invalid_password # ← このエラーメッセージを追加する
},
config/locales/ja.yml(116行目以下)
errors:
format: "%{attribute}%{message}"
messages:
invalid_password: は半角英数字、ハイフン、アンダーバーが使えます # ← 追記
正しく設定てきたか確認してみます。
rails consoleにログインしてpassword書式エラーのユーザーを作成してみましょう。
myapp $ rails c
irb(main):001:0> user = User.new(email:"user@example.com", password:"pass~./word")
irb(main):002:0> user.save
irb(main):003:0> user.errors.full_messages
=> ["パスワードは半角英数字、ハイフン、アンダーバーが使えます"]
OK!ちゃんとja.ymlが読み込まれていますね。
invalidエラーメッセージを変えておく
config/locales/ja.yml(130行目あたり)
errors:
format: "%{attribute}%{message}"
messages:
...
invalid: は不正な値です
「は不正な値です」はユーザーからすれば、次にどういう行動を取れば正解か?が分かりにくいエラーメッセージです。
そこでよりエラーが理解できる「の書式が正しくありません」に変更します。
config/locales/ja.yml
errors:
format: "%{attribute}%{message}"
messages:
...
invalid: の書式が正しくありません # 変更
こいつも確認してみましょう。
emailの書式エラーメッセージとして設定していますので、正しくないemailで保存してみます。
myapp $ rails c
irb(main):001:0> user = User.new(email:"test.com", password:"password")
irb(main):002:0> user.save
irb(main):003:0> user.errors.full_messages
=> ["メールアドレスの書式が正しくありません"]
OK!エラーメッセージが更新されました。
あれ?読み込まれないとなった場合は、reload!を実行するか、consoleから抜けてもう一度試してみてください。
最後にapplication.rbのセッティング
最後は、TimeZoneとZeitwerkの設定を行いましょう。
TimeZoneを日本時間にする
RailsのTimeZoneには2種類あります。双方ともデフォルトでは世界標準時間のUTCが使用されています。
- 表示時間
- データベースを読み書きする時間
今回は、日本だけの利用を前提とするので1、2共に日本時間を採用します。
config/application.rb
module Myapp8
class Application < Rails::Application
...
# 追記
# 表示TimeZone
config.time_zone = 'Tokyo'
# DB読み書きをlocal(Tokyo)にする
config.active_record.default_timezone = :local
下記コマンドで確認できます。
myapp $ rails r "puts Time.zone.name"
Tokyo
myapp $ rails r "puts Rails.application.config.active_record.default_timezone"
local
Zeitwerkの設定
Zeitwerk(ツァイトベルク)はRails 6 から組み込まれたオートロードシステムです。
Zeitwerkを有効にしている場合、$LOAD_PATH(Rubyライブラリをロードするときの検索パスが入った配列)にオートロードパスを追加する必要がありません。
Rails 6 のデフォルトでは、$LOAD_PATHにオートロードパスを追加する設定になっていますが、Zeitwerk有効時は追加しない設定が推奨されています。
具体的には、config.add_autoload_paths_to_load_path
をfalseにすることで$LOAD_PATHにパスを追加しない設定となります。
config/application.rb
module Myapp8
class Application < Rails::Application
...
# 追記
# Zeitwerk $LOAD_PATHにPathを追加しない(Zeitwerk有効時false推奨)
config.add_autoload_paths_to_load_path = false
参考
Zeitwerkのコマンド集
Zeitwerkを有効にする
config/application.rb
module Myapp8
class Application < Rails::Application
config.load_defaults 6.0 # 6.0を指定すると自動的に有効になる
Zeitwerkが有効になっているか確認する
Rails console
$ rails c
> Rails.autoloaders.zeitwerk_enabled?
=> true
Zeitwerkを無効にする
config/application.rb
module Myapp8
class Application < Rails::Application
config.autoloader = :classic # クラシックモードを指定する
まとめ
今回は以下の設定を行っていきました。
- ユーザーモデルのpasswordカラムにバリデーションを設定
- passwordハッシュ化のセッティング
- i18nのデフォルト言語を日本語に変更
- ja.ymlファイルを新たに追加
- バリデーションエラーを日本語化
- passwordに独自のエラーメッセージを設定
- Rails TimeZoneの変更
- $LOAD_PATHにオートロードパスを追加しないよう設定
いやぁ〜長かったですね。
本当にお疲れ様でした。
コミットしとく
ここまでの変更を一旦コミットしておきましょう。
myapp $ git add -A
myapp $ git commit -m "add_user_validator"
さて、次回は?
さて次回は、ユーザーモデルにデモデータを入れ込むために、seedファイルを作成していきます。
お楽しみに!