ぼくのかんがえたrails new
ここ一年ほど、仕事で自分が新規プロダクトのrails newをすることが多く、そのたびに色々考えながら落ち着いてきた初期設定です。
範囲はrails newを実行してから実アプリケーションコードを書く直前までです。
※ベストプラクティスというよりは自分の好みの側面が強い備忘録的なものとなります。
なおリポジトリはこちら https://github.com/cumet04/my-rails-new
ディレクトリを切る
rails new
する前に、まずプロジェクトのディレクトリをざっくり切ります。
プロジェクトルートに以下のように切ります:
- backend
- frontend
- infra
- develop
Railsに限らずnodejs系などでもそうですが、近年の開発環境はいわゆるdotfilesが無数にあってわけがわからないため、トップレベルでディレクトリを分けます。
READMEなどを除いてできるだけプロジェクト直下にファイルを置かない想定です。
backend
Railsプロジェクトのルートになるディレクトリです。この下でrails new
を行います。
本記事の内容はこの節以外はほぼこのディレクトリ下の話です。
frontend
nodejsなどのいわゆるfrontend系のコードを置きます。package.json
がここにある、というようなイメージです。
Railsのコードにあまり深く関わってこないため、本記事内やサンプルリポジトリには登場しません。
infra
サーバ構築用のansibleのコードやインフラ用のcloudformationのコードなどを置きます。
デプロイ関連のスクリプトも置いたりします。
develop
開発環境で使うスクリプトやAPIモックなど、プロダクション環境での動作には関係ないものを置きます。
ちょい作業の便利スクリプトや種類の増えたdocker-compose.ymlなどです。
(docker-composeは1つしかなければプロジェクトルートにも置きますが)
rails new
ディレクトリを切ったらbackend
下にて適当にrbenvやbundle initなどを行いrails new
します。
ここではオプションを含めコマンドは以下になりました:
rails new . \
--skip-action-mailbox \
--skip-action-cable \
--skip-sprockets \
--skip-listen \
--skip-javascript \
--skip-turbolinks \
--skip-webpack-install
オプションはアプリケーションの要件やRailsのバージョンによってかなり違うので、都度rails new --help
とにらめっこして決めます。
js系はRailsには触らせない構成なのでjavascript
turbolinks
webpack-install
あたりを落としておきます。
Rails5系以前だとcoffee
あたりも落とす必要があったと思います。
rails newができたらひとまず生成された.git
の削除とconfig/application.rb
のapplication名を修正し、そのままgit add .
してcommitします。
あとから初期ファイルのコメント群などをガッツリ消すので、説明コメントなどを見たくなったときにgitの履歴から閲覧できるようにしておきます。
.git
を削除するなら最初から--skip-git
すればいいじゃないと思いますが、初期の.gitignore
が欲しいので作ってから消しています。
慣れれば最初から--skip-git
でよいと思います。
不要な初期コメント・ファイルを消す
各種ファイルをガッツリ掃除します。
コメント系
.gitignore
やGemfile
、config/initializers/*
など説明用コメントがたくさんありますが、邪魔なので潔く消します。git履歴に残ってることですし。
initializersにはコメントのみのファイルもいくつかあるため、それらはファイルごと消します。
欲しくなったら追加しましょう。
credentials.yml.enc / master.key
Rails5.2から追加されたcredentials系の管理機能ですが、個人的には環境変数で差し込む方法に対してメリットが見いだせないので使いません。
credentials.yml.enc
をリポジトリから削除・master.keyをストレージとgitignoreから削除しておけばokです。
Gemfile
コメントの他にいくつか使わないものを早めに掃除しておきます。
- byebug -> 自分は使わないのでgroupブロックごと削除(好みです)
-
group :test
ブロック -> この辺は少なくともRails下のrubyではやらないので消します - tzinfo-data -> まぁwindows上では動かさないですね...
environments設定
config/environments/*
に置いているパラメータ系です。
デフォルトではenvironments/
下にある個別のdevelopment.rb
やproduction.rb
に必要な全パラメータが記述されていますが、
これに倣った場合、全環境に全く同じ記述がたくさんあったり環境ごとの差異がわかりにくかったりします。
そこで、production環境相当の設定をconfig/application.rb
にすべて記述し、developmentなど他の環境についてはproductionとの差分のみ記述することとしました。
以下、rails new直後相当の設定を一部整理・移植したものです。
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 6.0
# rails environments configs
config.cache_classes = true
config.eager_load = true
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
config.public_file_server.enabled = false
config.active_storage.service = :local
config.cache_store = :null_store
config.action_mailer.perform_caching = false
config.log_level = :debug
config.log_tags = [ :request_id ]
config.logger = ActiveSupport::Logger.new(STDOUT)
config.log_formatter = ::Logger::Formatter.new
config.active_support.deprecation = :notify
config.i18n.fallbacks = true
config.active_record.dump_schema_after_migration = false
# app configs
end
Rails.application.configure do
config.cache_classes = false
config.eager_load = false
config.action_controller.perform_caching = false
config.cache_store = :null_store
config.public_file_server.enabled = true
config.consider_all_requests_local = true
config.action_mailer.raise_delivery_errors = false
config.action_mailer.perform_caching = false
config.active_support.deprecation = :log
config.active_record.migration_error = :page_load
config.active_record.verbose_query_logs = true
config.active_record.dump_schema_after_migration = true
end
Rails.application.configure do
end
外部メールサーバやredisのセッション/キャッシュストアなどを使う場合はそれらのホスト名などが環境によって異なってきますが、それらは環境変数で定義してapplication.rb
内でENV.fetch
などで読み込むようにしておくとスマートです。
以下、しれっとデフォルトから変更している部分です。
cache_store / public_file_server.enabled
デフォルトではファイルの存在や環境変数でトグルするようになっていますが、運用上は環境によって設定が確定しているはずなので決め打ちにしておきます。
cacheについてはあとでredisなど導入する想定で一旦:null_store
にしています。
ログ出力
アプリケーションログについては環境によらず標準出力に出すようにします。
開発環境ではrails server
やdockerのため標準出力が良いですし、productionやstaging環境などにおいてもpumaと別個でログが出る必要がないのでひとまとめにしておきます。
サーバ上のログファイルの管理をlogrotateで一括でやりたいということもありますし、dockerで運用する場合でもstdout/stderrの二種類のみになっているほうが都合が良いです。
その他ミドルウェアなど
要件によって必要・不要などありますが、ミドルウェア類とデバッグ系ツールを入れます。
routes format
いきなりミドルウェアではないのですが、ルーティングの設定です。
Railsのルーティングではデフォルトでformat
が有効になっており、設定したURLの末尾に.json
や.xml
など拡張子的なものを追加するとそのフォーマットで元のパスにリクエストされたものと見做されます(controllerでrespond_to do |format|
するアレです)。
経験上これが必要になったことがなく、むしろ意図しないURLに意図しない処理が走ると問題なので、まるっと無効化します。
(同じデータに対しhtmlとjsonを返す要件はありますが、閲覧ページとAPIデータでは必要な情報粒度(URL)が違いますし、/api/**
などプレフィックスして別のルーティングとして定義するのが自然かと思います)
Rails.application.routes.draw do
scope format: false do
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
# このブロック内に通常のルーティングを記述する
end
end
Rails.application.config
やRails.application.routes.draw
の引数などでformat: false
を指定できればよいですが、残念ながらできないようなのでパス無指定のscopeでお茶を濁します。ネストが一段深くなってしまいますが、ルーティングなのでそこまで問題にはならないでしょう。
デバッグ系
better_errorsを入れたいので、Gemfileのdevelopment groupに
gem "better_errors"
gem "binding_of_caller"
を入れます。
またconfig/environments/development.rb
に
BetterErrors::Middleware.allow_ip! "0.0.0.0/0"
を追加しておきます。これが無いとdocker環境などでbetter_errorsが表示されません。
また他にも好みでpry-rails
を入れたりします。
mysql
Railsプロジェクトだとだいたい何かしらのDBを使いますし、自分の場合はmysqlを使うことが多いです。
Gemfileにてsqlite3
を削除、mysql2
と(development groupに)annotate
を入れます。
development環境でもdockerなりローカルマシンなりにmariadbを用意すればいいのでsqlite3は消します。
またannotateを入れるのであればrails generate annotate:install
しておきます。
config/database.yml
は前述のenvironmentsの方針に則り以下のようになります:
default: &default
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
host: <%= ENV.fetch('RAILS_DB_HOST') %>
port: <%= ENV.fetch('RAILS_DB_PORT', 3306) %>
database: <%= ENV.fetch('RAILS_DB_DATABASE') %>
username: <%= ENV.fetch('RAILS_DB_USERNAME') %>
password: <%= ENV.fetch('RAILS_DB_PASSWORD') %>
development:
<<: *default
production:
<<: *default
スッキリです。実際にはconnection poolの数くらいは別個で指定します。
redis session store
セッションをredisで持ちます。
Rails5系以前はredis-railsを使っていましたが、Rails6以降はredis-actionpackをそのまま使えば良い[1]ようです。
Gemfileにredis-actionpack
を足し、config/application.rb
にsession_storeの設定を入れます:
config.session_store(
:redis_store,
servers: {
host: ENV.fetch("RAILS_REDIS_HOST"),
port: ENV.fetch("RAILS_REDIS_PORT", 6379),
namespace: "sessions",
}, expire_in: 3.days, secure: true,
)
また開発環境はhttpsではないので忘れずに以下も入れておきます:
config.session_options[:secure] = false
まとめ
rubyを用意してrails new
するだけかと思いきや、アプリケーションコードを書くまでに考えるべき共通事項は案外多いです。
これは自分のよくある環境での一つの解でしかなく、開発体制やアプリケーションの規模、全体アーキテクチャによってもかなり変わってくると思いますが、考え方などなにか参考になればと思います。
Discussion