認証ジェネレーターがUserモデル存在時の`CreateUsers`マイグレーションをスキップするように改善
既存のUserモデルがある状態で認証ジェネレーターを実行すると、db:migrateが「table already exists」エラーで失敗する問題が修正されました。ジェネレーターがUserモデルファイルの有無を事前に確認し、テーブルが既に存在する場合はCreateUsersマイグレーションの生成を省略するようになります。
背景
認証ジェネレーター(rails generate authentication)は、CreateUsersマイグレーションを無条件に生成する実装になっていたため、既存のUserモデルを持つアプリケーションに認証機能を追加しようとすると問題が発生していました。
#57149 で報告された問題の手順は次のとおりです。まず既存のUserモデルを持つRailsアプリケーションで rails generate authentication を実行すると、app/models/user.rb の上書きを尋ねるプロンプトが表示されます。ここで上書きを拒否(n)しても、db/migrate/TIMESTAMP_create_users.rb は生成されてしまい、その後 db:migrate を実行すると ActiveRecord::StatementInvalid: SQLite3::Exception: table "users" already exists というエラーが発生します。
ジェネレーターのソースを見ると、add_migrations メソッドがモデルファイルの受け入れ状況とは無関係に実行されていたことが根本原因です。ユーザーがモデルの上書きを断った場合でも、対応するマイグレーションは常に生成されていました。回避策として手動でマイグレーションファイルを削除する必要があり、ユーザー体験を著しく損なっていました。
技術的な変更
AuthenticationGenerator に、Userモデルファイルの存在確認と、その結果に基づくマイグレーション生成の条件分岐が追加されました。
変更前:
def add_migrations
generate "migration", "CreateUsers", "email_address:string!:uniq password_digest:string!", "--force"
generate "migration", "CreateSessions", "user:references ip_address:string user_agent:string", "--force"
end
変更後:
def create_authentication_files
@user_model_exists = File.exist?(File.expand_path("app/models/user.rb", destination_root))
template "app/models/session.rb"
template "app/models/user.rb"
template "app/models/current.rb"
# ...
end
def add_migrations
unless @user_model_exists
generate "migration", "CreateUsers", "email_address:string!:uniq password_digest:string!", "--force"
end
generate "migration", "CreateSessions", "user:references ip_address:string user_agent:string", "--force"
end
存在確認のタイミングは create_authentication_files の冒頭、つまりテンプレートを展開する前に行われる点が重要です。@user_model_exists は ジェネレーター実行前のファイル有無 を記録し、add_migrations での分岐に使用されます。なお、CreateSessions マイグレーションはUserモデルの有無にかかわらず常に生成される点も、既存アプリへの認証追加を想定した設計です。
テストも合わせて追加されています。test_create_users_migration_is_skipped_when_user_model_already_exists では、事前に app/models/user.rb を作成した上でジェネレーターを実行し、@rails_commands に CreateUsers マイグレーション生成コマンドが含まれないことと、CreateSessions は含まれることをそれぞれ検証しています。
設計判断
ファイルの存在確認 という単純なアプローチが採用されました。
Issue #57149 では、スキーマ上の users テーブル存在確認という代替案も提案されていましたが、最終的にファイルシステムの存在確認というシンプルなアプローチが採用されました。destination_root を基準に File.expand_path でパスを解決することで、ジェネレーターのテスト環境でも同じロジックが正しく動作します。
確認対象を app/models/user.rb のファイル存在に限定しているため、モデルの上書きを受け入れたかどうかのインタラクション結果を追跡する必要がなく、実装がシンプルに保たれています。既存のUserモデルがある場合はそのテーブルも存在するという前提を置いた、実用的な判断といえます。
まとめ
この修正は、最小限のコード変更で既存アプリへの認証追加時の破壊的な失敗を防ぐものです。ジェネレーター実行前のファイル有無という単一の事実から CreateUsers マイグレーションの生成を制御することで、グリーンフィールドアプリと既存アプリの両方に対して認証ジェネレーターが安全に機能するようになります。