このチャプターで達成すること
この「モデル開発事前準備」のチャプターでは、ユーザーモデル開発前の準備を行います。
具体的にはRailsの初期設定やGemのインストール、使われていないDockerイメージの削除などを行います。
このチャプターを通してモデル開発の前にやるべき事を学んでいきましょう。
今回達成すること
今回は
ここの設定はアプリ全体に影響するので、最初に行うのが鉄則です。
apiディレクトリでブランチ切っとく
作業に入る前に「api」ディレクトリ上でブランチを作成しましょう。
root $ cd api
api $ git checkout -b 20200702_initial_settings
20200702_initial_settings
... ブランチ名に作業開始日を付けると、日付ごとに並ぶので見返しやすくなります。これは筆者の癖なので、皆さんはご自由に。
確認しておきましょう。
api $ git branch
* 20200702_initial_settings
master
ブランチに移動できていることを確認できたら「root」ディレクトリに戻っておきます。
api $ cd ..
application.rbの初期設定を行う
Railsの「config」ディレクトリ直下の
タイムゾーンの設定や、デフォルトで使用する言語、アプリ起動時に読み込むディレクトリなどを指定します。
Railsアプリのタイムゾーン設定
まずはタイムゾーンの設定を行いましょう。
初めからあるコメントは丸っと消しちゃって大丈夫です。
api/config/application.rb
...
module App
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 6.0
# 追加
# Railsアプリデフォルトのタイムゾーン(default 'UTC')
# TimeZoneList: http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html
config.time_zone = ENV["TZ"]
config.api_only = true
end
end
-
config.time_zone
... Railsアプリのタイムゾーンを設定します。デフォルトはUTC
。設定できる値は下記URLにあるオブジェクトの値で、key(Tokyo), value(Asia/Tokyo)どちらでも構いません。
-
ENV["TZ"]
... RailsのDockerfileで設定した環境変数のタイムゾーンを取得しています。ここに入っている値は
"Asia/Tokyo"
です。
ここで設定したタイムゾーンはRailsのTimeWithZone
クラスに影響します。
Railsコンソールで確認してみましょう。
root $ docker-compose run --rm api rails c
# 設定タイムゾーンの確認
> Time.zone.name
=> "Asia/Tokyo"
# 現在時間がJSTになっているか確認
> Time.current
=> Thu, 02 Jul 2020 21:25:05 JST +09:00
日本時間になっていますね。
コンソールはexit
で抜けましょう。(以後省略)
> exit
(コラム)Time.currentとTime.nowの違い
どちらも現在時刻を取得するメソッドですが違いがあります。
Time.current
Railsの独自のメソッドで、TimeWithZoneクラスを使用しています。
config.time_zone
で設定したタイムゾーンを元に現在時刻を取得します。
Time.now
Rubyのメソッドで、Timeクラスを使用しています。
環境変数TZ
の値、無ければシステム(OS)のタイムゾーンを元に現在時刻を取得します。
今回はDockerfileにTZ=Asia/Tokyo
を指定しているので、Time.now
は日本時間を取得します。
指定できるタイムゾーンは下記URLの「TZ database name」の値です。
(コラム終わり)
データベースの読み書きに使用するタイムゾーン設定
Railsにはアプリのタイムゾーンとは別に、データベースの読み書きに使用するタイムゾーンがあります。
api/config/application.rb
...
module App
class Application < Rails::Application
...
# 追加
# データベースの読み書きに使用するタイムゾーン(:local | :utc(default))
config.active_record.default_timezone = :utc
config.api_only = true
end
end
config.active_record.default_timezone
... データベースから日付と時刻を取り出した際のタイムゾーンを、世界時間と地方時間どちらのタイムゾーンで読み込むかを指定します。デフォルトは:utc
。
:utc
... 世界時間Time.utcで読み込みます。
> Time.utc(2020,4,1,12) => 2020-04-01 12:00:00 UTC
:local
... 地方時間Time.localで読み込みます。
> Time.local(2020,4,1,12) => 2020-04-01 12:00:00 +0900
Time.localはRubyのメソッドで、Rubyタイムゾーンを参照します。
RubyタイムゾーンはTime.now.zone
で確認できます。
> Time.now.zone
=> "JST"
config.active_record.default_timezone
の指定は下記にも影響があります。
1. データベースの保存時間に影響
:utc
で保存した場合、世界標準時間で保存されます。
> time = Time.new(2020,4,1,12)
=> 2020-04-01 12:00:00 +0900
> User.create(name: "utc", email: "utc@examle.com", password_digest: "password", created_at: time)
> User.last.created_at_before_type_cast
=> 2020-04-01 03:00:00 UTC
<カラム名>_before_type_cast
... 型変換前の値を取得する。
:local
で保存した場合、日本時間で保存されます。
> User.create(name: "local", email: "local@examle.com", password_digest: "password", created_at: time)
> User.last.created_at_before_type_cast
=> 2020-04-01 12:00:00 +0900
2. Railsのタイムスタンプに影響
Railsのcreated_at
、updated_at
の時間を決定するタイムスタンプには下記のコードが使われています。
:utc
と設定した場合はTime.now.utc
の時間で保存されます。
def current_time_from_proper_timezone #:nodoc:
self.class.default_timezone == :utc ? Time.now.utc : Time.now
end
3. 変更時は要注意
保存時と表示時のdefault_timezone
を合わせないと前後9時間の誤差が出るので注意が必要です。
PostgreSQL保存時間 | 保存表示共に local | 保存表示共にutc | 保存local/表示utc | 保存utc/表示local |
---|---|---|---|---|
2020-04-01 03:00:00 | 2020-04-01 3:00:00 | 2020-04-01 3:00:00 | 2020-04-01 12:00:00 | 2020-03-31 18:00:00 |
- | 一致 | 一致 | 9時後(+) | 9時前(-) |
今回の設定はデータベースPostgreSQLに合わせ、:utc
に設定しています。
config.active_record.default_timezone = :utc
読み込み時はconfig.time_zone
で変換されるため日本時間で表示され、保存時はUTCで保存されます。
日本語化ファイルの読み込み設定
Railsには、エラーメッセージやメールタイトルを他の言語に翻訳する「i18n」と言うモジュールが入っています。
こいつが読み込むファイルを指定します。デフォルトは:en
。
api/config/application.rb
module App
class Application < Rails::Application
...
# 追加
# i18nで使われるデフォルトのロケールファイルの指定(default :en)
config.i18n.default_locale = :ja
config.api_only = true
end
end
config.i18n.default_locale = :ja
... ここで指定した値のファイルをデフォルトの翻訳ファイルとして読み込みます。:en
... 読み込みファイルパス「/config/locales/en.yml」:ja
... 読み込みファイルパス「/config/locales/ja.yml」
Railsコンソールで設定を確認することができます。
> I18n.locale
=> :ja
Zeitwerk(ツァイトベルク)の設定
Railsの$LOAD_PATH
に自動読み込みパスを足すべきかどうかを指定します。デフォルトはtrue
。
api/config/application.rb
module App
class Application < Rails::Application
...
# 追加
# $LOAD_PATHにautoload pathを追加しない(Zeitwerk有効時false推奨)
config.add_autoload_paths_to_load_path = false
config.api_only = true
end
end
$LOAD_PATHとは
ファイルを読み込むRubyのメソッド「load」や「require(リィクゥァィア)」が、ファイルを参照するときに使われるディレクトリパス群です。
> pp $LOAD_PATH
["/app/lib",
"/app/vendor",
"/usr/local/bundle/gems/actioncable-6.0.3.1/lib",
...
require "rails"
などと言った記述がありますが、これだけでどうやってRailsのGemファイルを参照しているのか疑問になりますよね。
それは$LOAD_PATH
に「/usr/local/bundle/gems/rails-6.0.3.1/lib」というパスが指定されているので参照できているのです。
自動読み込みパスとは
Railsが自動で読み込むディレクトリパスのことを言います。
ここには「app」以下にある全てのサブディレクトリや、アプリが依存する可能性があるGemのパスが入っています。
これは、Rails6から組み込まれた「Zeitwerk(ツァイトベルク)」というオートロードシステムが読み込んでいます。
> pp ActiveSupport::Dependencies.autoload_paths
["/app/app/channels",
"/app/app/controllers",
"/app/app/controllers/concerns",
"/app/app/jobs",
"/app/app/mailers",
"/app/app/models",
...
今回の設定まとめ
つまりconfig.add_autoload_paths_to_load_path
は、Railsが自動で読み込んでいるディレクトリパスを$LOAD_PATH
に追加するかを決定します。
既に読み込んでいるのなら「require」を使わなくても読み込めるのでfalse
としています。
また、「Zeitwerk(ツァイトベルク)」を使用するアプリケーションではfalse
が推奨されています。
Zeitwerkが有効であるかは下記コマンドで確認できます。
> Rails.autoloaders.zeitwerk_enabled?
=> true
最終的なapplication.rb
最終的な
api/config/application.rb
...
module App
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 6.0
# Railsアプリデフォルトのタイムゾーン(default 'UTC')
# TimeZoneList: http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html
config.time_zone = ENV["TZ"]
# データベースの読み書きに使用するタイムゾーン(:local | :utc(default))
config.active_record.default_timezone = :utc
# i18nで使われるデフォルトのロケールファイルの指定(default :en)
config.i18n.default_locale = :ja
# $LOAD_PATHにautoload pathを追加しない(Zeitwerk有効時false推奨)
config.add_autoload_paths_to_load_path = false
config.api_only = true
end
end
ここまでの変更をコミットしときましょう。
root $ cd api && git commit -am "application.rb_initial_setting" && cd ..
[20200702_initial_settings 56ccbc8] application.rb_initial_setting
1 file changed, 13 insertions(+), 8 deletions(-)
まとめ
今回は
ちなみに、Zeitwerkに「lib」ディレクトリ以下のファイルを読み込ますには、このように記述します。
application.rb
config.autoload_paths += %W(#{config.root}/lib)
ただ、
- 公式であまり推奨されていないこと(完全な非推奨ではない)
- 「lib」ディレクトリ以下に置くファイルが少量であること
を踏まえ、今回はファイル読み込みにrequire
メソッドを使用します。
「lib」ディレクトリ以下にファイルが増えてきたらまた検討すれば良いでしょう。
さて次回は?
次回はモデル開発に必要なGemのインストール編!
それではまたここでお会いしましょう。
バイバイ。