[action_push_native] enumのバリデーション方式変更でコントローラーのエラーハンドリングを改善
変更の背景
Rails 7.1で導入されたenumのvalidate: trueオプションは、不正な値が設定された際の挙動を大きく変更します。従来、enumに不正な値を代入するとArgumentErrorが発生していましたが、このオプションを有効にすることで、代わりにバリデーションエラーとして扱われるようになります。
この変更により、コントローラー層でのエラーハンドリングが統一され、RESTful APIとしてより適切なHTTPステータスコード(422 Unprocessable Entity)を返せるようになります。従来はArgumentErrorをrescueするか、500エラーとして処理する必要がありましたが、これは設計上望ましくありませんでした。
技術的な詳細
変更内容
変更前:
enum :platform, { apple: "apple", google: "google" }
変更後:
enum :platform, { apple: "apple", google: "google" }, validate: true
挙動の違い
従来の挙動(validate: false、デフォルト):
device = Device.new(platform: "invalid_platform")
# => ArgumentError: 'invalid_platform' is not a valid platform
新しい挙動(validate: true):
device = Device.new(platform: "invalid_platform")
device.valid?
# => false
device.errors[:platform]
# => ["is not included in the list"]
コントローラーでの影響
この変更により、コントローラーでのエラーハンドリングが以下のように統一されます。
変更前:
def create
@device = Device.new(device_params)
@device.save!
render json: @device, status: :created
rescue ArgumentError => e
render json: { error: e.message }, status: :unprocessable_entity
rescue ActiveRecord::RecordInvalid => e
render json: { errors: e.record.errors }, status: :unprocessable_entity
end
変更後:
def create
@device = Device.new(device_params)
@device.save!
render json: @device, status: :created
rescue ActiveRecord::RecordInvalid => e
render json: { errors: e.record.errors }, status: :unprocessable_entity
end
技術的な利点
-
一貫性のあるエラーハンドリング: すべてのバリデーションエラーが
ActiveRecord::RecordInvalidとして統一される - 適切なHTTPステータスコード: 422(Unprocessable Entity)を返すことで、クライアント側のエラーハンドリングが容易になる
-
エラーメッセージの統一:
errorsオブジェクトを通じて、他のバリデーションエラーと同じ形式でエラー情報を取得できる - フォームでの扱いやすさ: Railsのフォームヘルパーが自動的にエラーメッセージを表示できる
注意点
この変更は、Rails 7.1以降で利用可能な機能です。既存のコードベースに適用する際は、ArgumentErrorをrescueしている箇所がないか確認し、必要に応じてリファクタリングする必要があります。