Rails 5.2新機能を先行チェック!Active Storage/ダイレクトアップロード/Early Hintsほか(翻訳)

概要 原著者の許諾を得て翻訳・公開いたします。 英語記事: Rails 5.2: Active Storage and beyond 原文公開日: 2018/01/31 著者: Andy Barnov、Vladimir Dementyev サイト: Evil Martians Rails 5.2新機能を先行チェック!Active Storage/ダイレクトアップロード/Early Hintsほか(翻訳) 本記事では、ファイルの新しいアップロード方法、チーム内でのCredentialの共有、Content Security Policyなど、主にActiveStorageを中心としてRails 5.1の新機能を、アプリを起動するまでの手順付きでご紹介いたします。 Rails 5.2は約束どおりの新年のプレゼントとはならず、正式リリーズまでもうひと月待つことになってしまいましたが、それでもRC1となり、今や安定してきたと考えられます。というわけで、一足早くプレゼントの包み紙を明けてしまいましょう。今ならgem install rails –prereleaseを実行することでRails 5.2をインストールできます。 訳注: Gemfileでインストールする場合はgem “rails”, ‘>=5.2.0.rc1’を指定します。 Rails 6登場前の最後のメジャーアップデートである5.2のきらびやかな新機能の中でも、Active Storageはひときわ輝きを放っています。プロジェクトでファイルアップロードを扱える機能がRailsに組み込まれたのはこれが初めてです。DHHによるプレスリリースでは、Active StorageはBasecamp 3から切り出された、production環境生まれのフレームワークであることを謳っています。 本記事ではまずActive Storageについて解説し、続いてRails 5.2の他の機能をご紹介いたします。お時間がありましたら、どうぞ最後までお付き合いください。 Active Storageで添付ファイルを扱う 免責事項: 本記事では、Active Storageを既存のソリューション(CarrierWaveやPaperclipやShrineなど)と比較することはせず、できるだけ初心者にわかりやすくActive Storageフレームワークを紹介するよう努めます。 Active Storageをアプリで有効にするには、最初にrakeタスクを実行します。コマンドラインでrails active_storage:installを実行してrails db/migrateで新規マイグレーションを追加することで、Active Storageで必要となる2つのテーブル(active_storage_attachmentsとactive_storage_blobs)を追加します。Active StorageのREADMEによると、これらのテーブルでは以下を行います。 Active Storageでは、Attachment joinモデルを経由するポリモーフィック関連付けを使い、実際のBlobに接続します1。Blobモデルは添付ファイル(attachment)のメタデータ(ファイル名やコンテンツの種類など)と識別子のキーをストレージサービスに保存します。 Active Storageのこのアプローチは、他の有名なソリューションと異なっています。Paperclip、Carrierwave、Shrineは、いずれも既存のモデルにコラムを追加する必要があります。添付ファイルを扱うgemで唯一広く使われているのは、仮想属性に依存するAttachinary gemです。これは実に使いやすいプロプライエタリなソリューションで、Cloudinaryのストレージ専用です。 Active Storageも同じような方向性ですが、ファイルの保存場所をハードウェアや著名なクラウドプロバイダなどから選べるようになっています2。Amazon S3、Google Cloud Storage、Microsoft Azureについては即座に利用できます。 scaffoldでもできる ここでActive Storageが動くところを見てみましょう。作成済みの新規アプリがあることが前提です。Gemfileのjbuilder gemをコメントアウトしてbundle installを実行しておくと、scaffoldで余分なファイルを生成せずに済みます。今はActive Storageをチェックしたいだけなので、私たちのオリジナリティを発揮するためにフル機能のCRUD構築に時間をかけないことにします。 $ rails g scaffold post title:string content:text $ rails db:migrate # 訳注: 補いました 投稿に画像を1つ添付できるようにしましょう。モデル定義に以下のコードを追加します。 # app/models/post.rb class Post < ApplicationRecord has_one_attached :image end Active Storageを使い始めるには、コードを3箇所だけ変更する必要があります。これはその最初の1つです。 次は、scaffoldジェネレータが作ってくれたposts_controller.rbです。ここで必要なのは、次のようにpost_paramsメソッドの中にimageパラメータを書いてホワイトリスト化することだけです(strong parameters)。 # app/controllers/posts_controller.rb def post_params params.require(:post).permit(:title, :content, :image) end 参考までに、コントローラのコードの他の場所で既存のモデルにファイルを添付するには、以下のようにします。 @post.image.attach(params[:image]) メモ: コントローラのアクションでリソースに対してcreateやupdateを使うときに、添付ファイルを「許可されたパラメータ」として渡すのであれば、上のコードは不要です(正常に動きません)。初期のチュートリアルによってはファイルを明示的にattachする必要があるとしていることもありますが、ここでは既に該当しません。 次はビューです。生成された_form.html.erbで、送信ボタンのすぐ上にfile_fieldを追加します。 <!– app/views/posts/_form.html.erb –> <div class=”field”> <%= form.label :image %> <%= form.file_field :image %> </div> 次は、画像を表示します。 <!– app/views/posts/show.html.erb –> <% if @post.image.attached? %> <!– @post.image.present? は常にtrueを返すので、attached?で存在チェックすること –> <p> <strong>Image:</strong> <br> <%= image_tag @post.image %> </p> <% end %> これでできあがりです。マルチパートフォームでアップロードするときの本質的な詳細部分は、Railsがすべて代わりにやってくれます。rails sでサーバーを起動してlocalhost:3000/posts/newをブラウザで開き、適当な画像を選んで投稿を作成します。 Active Storageのスモークテスト 投稿の編集や画像の変更ももちろんできますので、やってみてください。ご覧のとおりファイルアップロード機能がアプリで使えるようになりました! ここまでの作業をまとめます。 モデル: 定義でhas_one_attachedメソッドを呼び出し、モデルのインスタンスごとの仮想属性とするシンボルを1つ引数として渡しました。ここでは属性名を:imageとしましたが、どんな名前でも構いません。 コントローラ: imageを許可されたパラメータ(strong parameters)でホワイトリスト化しました。 ビュー: フォームにfile_fieldを追加し、アップロードした画像をimage_tagで表示しました。 今度は、フォーム送信時の舞台裏を見ていくことにしましょう。サーバーのログを見てみると、すべてのSQL文の下に、それが生成されたコードの位置が表示されていることがわかります。以前はQuery Trace gemの仕事でしたが、この機能がRailsに組み込まれました。この出力がうれしくない場合は(#31691で特定のrbenv設定で問題が生じることが報告されています)、development.rbファイルのconfig.active_record.verbose_query_logsオプションをfalseに設定してください。 添付ファイルをPOSTしたときのログ このログから、Railsがフォームを処理し、受け取ったファイルをディスクに保存し、保存場所をエンコードしてキーを生成し、そのキーをactive_storage_blobsテーブルで参照し、postsテーブルでレコードを1件作成し、BlobをPostとactive_storage_attachments経由で関連付けている様子がわかります。 以下は、PostsControllerのshowアクションがGETで呼び出されたときの挙動です。 アップロードの結果をGETしたときのログ 1件のリクエストは3箇所で処理されます。ActiveStorage::BlobsControllerとActiveStorage::DiskControllerはファイルを扱います。このようにして、画像のパブリックなURLは常に実際の場所から切り離されます。クラウドサービスを使っている場合は、BlobsControllerによってクラウド内の正しい署名済みURLにリダイレクトされます。 Postインスタンスで何ができるようになったかをrails consoleで見てみましょう。 rails consoleでレコードをいじる 添付ファイルのURLを生成するには、urlではなくservice_urlを呼ぶ必要がありますのでご注意ください。url_forやimage_tagなどのビューヘルパーはこれを認識して自動でやってくれるので、こうしたメソッドを明示的に呼ぶ必要はめったにありません。 N+1を解決する ビューで添付ファイルが出力されるときには、少なくとも3件のデータベースクエリが発生します(親モデルで1件、active_storage_attachmentsで1件、active_storage_blobsで1件)。多数の添付ファイルをすべて含むActive Recordオブジェクトのコレクションについて出力を繰り返す場合には、何か注意が必要でしょうか?調べてみましょう。index.html.erbを変更して、投稿ごとの画像(または少なくとも画像ファイル名)を表示するようにし(post.image.filenameもそうしたクエリをすべてトリガします)、/postsをブラウザで更新してログを見てみましょう。 … Continue reading Rails 5.2新機能を先行チェック!Active Storage/ダイレクトアップロード/Early Hintsほか(翻訳)