Rails7 (zeitwerk)でgRPC (Protocol Buffers)を使ったらハマった
概要
Amazon.co.jp: スターティングgRPC 技術の泉シリーズ (技術の泉シリーズ(NextPublishing)) eBook : 武上 将樹: Kindleストア
こちらの書籍にあるGo(サーバ) × Rails(クライアント)のサンプルを写経していた際、zeitwerkの仕様にはまってエラーが出たので、回避方法をメモしておきます。
zeitwerkはRails6から採用されたオードロード機構ですが、Rails6ではActive Supportで実装されたものがclassicモードとして利用可能で、それであればエラーは発生しないとのことでした。
Rails7からはclassicモードが使えずzeitwerkで動かす必要があるため、下記サイトを参考にさせていただき対応しました。
アプリケーション外観
最終的なディレクトリ構成は下記の通り。
. ├── api # サーバ(Golang) │ ├── gen │ │ └── pb │ │ ├── filemeta.pb.go │ │ ├── image_upload_request.pb.go │ │ └── image_upload_response.pb.go │ ├── go.mod │ ├── go.sum │ ├── handler │ │ └── image_upload_handler.go │ └── server │ └── server.go ├── client # クライアント(Rails) │ ├── Gemfile │ ├── app │ │ ├── gen │ │ │ └── pb │ │ │ └── image │ │ │ └── upload │ │ │ ├── image_upload_request │ │ │ │ └── filemeta_pb.rb │ │ │ ├── image_upload_request_pb.rb │ │ │ ├── image_upload_request_services_pb.rb │ │ │ └── image_upload_response_pb.rb │ │ ├── models │ │ │ └── image_uploader.rb │ ├── config │ │ ├── application.rb ├── proto │ └── image │ └── upload │ ├── image_upload_request │ │ └── filemeta.proto │ ├── image_upload_request.proto │ └── image_upload_response.proto
ポイント1)1つのmessageにつき1つのprotoファイル
まずprotoファイルでスキーマを定義します。
最初は一つのprotoファイル内に3つのmessage
を記載していましたが、参考先にも記載がある通り、1protoファイルにつき一つのmessageとしました。zeitwerkによる定数読み込みでエラーになるためです。結果的に3つのprotoファイルを書き、大元のimage_upload_request.proto
で他の2つをimportして使うようにしました。
定義できたらprotoc
およびgrpc_tools_ruby_protoc
コマンドで各々GoとRubyのコードを自動生成します。
ポイント2)Rails側で読み込みパスを追加
config/application.rb
に下記の通り追加します。
config.paths.add Rails.root.join('app', 'gen', 'pb').to_s, eager_load: true
protoで定義して自動生成した定数の名前空間を正しく認識させるために必要でした。
ポイント3)カスタムInflectorを定義
Qiitaの記事にある通り、自前のInflectorを定義して利用します(記事の通りなので割愛)
なお自動生成したrubyのファイルimage_upload_request_services_pb.rb
について、ファイル名はservicers
となっているが中身のmodule名はImageUploadRequestService
となり、このままだとzeitwerkの読み込みエラーになったため、取り急ぎここのカスタムInflectorで対応しました(ファイル名末尾にpbがつく場合、かつ指定のファイル名の場合は、当該ファイル名を単数系に変換)。
以上の通り修正し、下記コマンドを実行してexpected file XXX to define constant XXX
のようなエラーが出なければOKです。
% bin/rails zeitwerk:check Hold on, I am eager loading the application. All is good!