Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

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

概要

原著者の許諾を得て翻訳・公開いたします。

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を既存のソリューション(CarrierWavePaperclipShrineなど)と比較することはせず、できるだけ初心者にわかりやすくActive Storageフレームワークを紹介するよう努めます。

Active Storageをアプリで有効にするには、最初にrakeタスクを実行します。コマンドラインでrails active_storage:installを実行してrails db/migrateで新規マイグレーションを追加することで、Active Storageで必要となる2つのテーブル(active_storage_attachmentsactive_storage_blobs)を追加します。Active StorageのREADMEによると、これらのテーブルでは以下を行います。

Active Storageでは、Attachment joinモデルを経由するポリモーフィック関連付けを使い、実際のBlobに接続します1Blobモデルは添付ファイル(attachment)のメタデータ(ファイル名やコンテンツの種類など)と識別子のキーをストレージサービスに保存します。

Active Storageのこのアプローチは、他の有名なソリューションと異なっています。Paperclip、Carrierwave、Shrineは、いずれも既存のモデルにコラムを追加する必要があります。添付ファイルを扱うgemで唯一広く使われているのは、仮想属性に依存するAttachinary gemです。これは実に使いやすいプロプライエタリなソリューションで、Cloudinaryのストレージ専用です。

Active Storageも同じような方向性ですが、ファイルの保存場所をハードウェアや著名なクラウドプロバイダなどから選べるようになっています2。Amazon S3、Google Cloud Storage、Microsoft Azureについては即座に利用できます。

scaffoldでもできる

ここでActive Storageが動くところを見てみましょう。作成済みの新規アプリがあることが前提です。Gemfilejbuilder 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])

メモ: コントローラのアクションでリソースに対してcreateupdateを使うときに、添付ファイルを「許可されたパラメータ」として渡すのであれば、上のコードは不要です(正常に動きません)。初期のチュートリアルによってはファイルを明示的に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をブラウザで開き、適当な画像を選んで投稿を作成します。

First post with Active Storage

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に設定してください。

Log for a POST request

添付ファイルをPOSTしたときのログ

このログから、Railsがフォームを処理し、受け取ったファイルをディスクに保存し、保存場所をエンコードしてキーを生成し、そのキーをactive_storage_blobsテーブルで参照し、postsテーブルでレコードを1件作成し、BlobPostactive_storage_attachments経由で関連付けている様子がわかります。

以下は、PostsControllershowアクションがGETで呼び出されたときの挙動です。

Log for a GET request

アップロードの結果をGETしたときのログ

1件のリクエストは3箇所で処理されます。ActiveStorage::BlobsControllerActiveStorage::DiskControllerはファイルを扱います。このようにして、画像のパブリックなURLは常に実際の場所から切り離されます。クラウドサービスを使っている場合は、BlobsControllerによってクラウド内の正しい署名済みURLにリダイレクトされます。

Postインスタンスで何ができるようになったかをrails consoleで見てみましょう。

Rails console

rails consoleでレコードをいじる

添付ファイルのURLを生成するには、urlではなくservice_urlを呼ぶ必要がありますのでご注意ください。url_forimage_tagなどのビューヘルパーはこれを認識して自動でやってくれるので、こうしたメソッドを明示的に呼ぶ必要はめったにありません。

N+1を解決する

ビューで添付ファイルが出力されるときには、少なくとも3件のデータベースクエリが発生します(親モデルで1件、active_storage_attachmentsで1件、active_storage_blobsで1件)。多数の添付ファイルをすべて含むActive Recordオブジェクトのコレクションについて出力を繰り返す場合には、何か注意が必要でしょうか?調べてみましょう。index.html.erbを変更して、投稿ごとの画像(または少なくとも画像ファイル名)を表示するようにし(post.image.filenameもそうしたクエリをすべてトリガします)、/postsをブラウザで更新してログを見てみましょう。

Active StorageのN+1クエリ

見事なまでのN+1クエリ

問題が起きているのがおわかりでしょうか?「N+1」という名のヒドラがおぞましき頭をもたげています。ありがたいことに、Active Storageにはちゃんと解決法が用意されています。この方法では、関連付けられたblobをincludesするwith_attached_imageスコープ(またはwith_attached_your_attachment_nameスコープ)を生成します。必要な作業は、PostsController#index@posts = Post.all@posts = Post.with_attached_imageに変更することだけです。結果を見てみましょう。

N+1問題の解決

問題が解決しました!

素晴らしい!しかし、Active Recordが常に正しい選択を行えることを当てにしたくないという理由で、includesではなくeager_loadpreloadを使いたい場合はどうすればよいのでしょうか?そんなときは次のようにします。

class Post < ApplicationRecord
  scope :with_eager_loaded_image, -> { eager_load(image_attachment: :blob) }
  scope :with_preloaded_image, -> { preload(image_attachment: :blob) }
end

ここで注意しておきたいのは、プリロードをネストしている点です。blobsの読み込みはattachmentsを経由します。image_attachmentは、Active Storageによって追加された関連付けの名前です。特にこの場合attachmentの名前を変更すると、関連付けの名前も変更されます。

複数の添付ファイル

CRUDで複数の添付ファイルを扱えるようにするのも朝飯前です。

  • モデル:
# app/models/post.rb
class Post < ApplicationRecord
  has_many_attached :images
  # ここで暗黙の関連付けを複数形にしていることに注意
  scope :with_eager_loaded_images, -> { eager_load(images_attachments: :blob) }
end
  • コントローラ:
# app/controllers/posts_controller.rb
def post_params
  params.require(:post).permit(:title, :content, images: [])
end
  • ビュー:
<!-- app/views/posts/_form.html.erb -->
...
<div class="field">
  <%= form.label :images %>
  <%= form.file_field :images, multiple: true %>
</div>
...
<!-- app/views/posts/show.html.erb -->
...
<% if @post.images.attached? %>
<p>
  <strong>Images:</strong>
  <br>
  <% @post.images.each do |image| %>
    <%= image_tag(image) %>
  <% end %>
</p>
<% end %>

添付ファイルの1つ(またはすべて)を削除したい場合、purgeメソッドかpurge_laterメソッドを使えます。後者は組み込みのActiveStorage::PurgeJobを使ってバックグラウンドでファイルを削除します。親モデルを削除すると、デフォルトで非同期削除も呼び出されます。

ImageMagickでさまざまなサイズの画像を扱う

ここまでの時点では、ユーザーがアップロードした画像はそのまま表示されますが、多くの場合これは望ましくない動作です。Active Storageでは、ImageMagickによる画像変換をサポートしています。必要な準備は、Gemfileに(コメントアウトで)追加済みのmini_magick gemを有効にするだけです。これで、たとえば次のようにImageMagickの変換を使えるようになります。

<%= image_tag image.variant(resize: "500x500", monochrome: true) %>

上のコードは、指定したblobの指定したvariantを指すURLを作成しますが、変換そのものは、その画像がブラウザから最初にリクエストされるまではActiveStorage::VariantsControllerで扱われません。Active Storageでは、元のblobがActive Supportからダウンロードされ、サーバーのメモリ上で変換され、再度サーバーにアップロードされる必要があるため、コストの高い操作の遅延を試みます。

ファイルを(遅延せずに)最初に処理し、その後でURLを取得したい場合は、image.variant(resize: "100x100").processed.service_urlを呼び出します。これは、その特定のvariantが既に実行済みかどうかをチェックし、実行済みの場合は同じ処理を繰り返しません。

動画のプレビュー画像の生成(ffmpegを使用)やPDFの生成(mutoolを使用)も行えます。ただしこれらのライブラリはRailsでは標準で提供されていないので、ツールの準備は自分で行うことになります。

ImageMagickが有効になれば、アップロードされたファイルは自動的に(かつ非同期で)分析されメタデータを生成します。post.image.metadataを呼ぶことで、{"width"=>1200, "height"=>700, "analyzed"=>true}のようなハッシュを取得できます。

画像をImageMagick以外のライブラリで扱いたい方に残念なお知らせです。現時点のActive Storageにはimgproxyなどの画像処理ライブラリを使う公式の手段がまだありません。

秘密情報の扱いの改良

ここまではスイスイ進められましたが、他に何か必須の設定はあるのでしょうか?なくても大丈夫。Railsでお馴染みのomakase方式では、基本的な設定が既にconfig/storage.ymlに含まれています。このファイルは次のようになっています。

test:
  service: Disk
  root: <%= Rails.root.join("tmp/storage") %>

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
# amazon:
#   service: S3
#   access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
#   secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
#   region: us-east-1
#   bucket: your_own_bucket

development.rbにも既にconfig.active_storage.service = :localが設定されているので、Active Storageをlocalhostで動かす場合にローカルディスクを使うことをRailsが認識します。yamlファイルにはAWS S3、Google GCS、マイクロソフトのAzureStorageのクラウドサービス向け設定もコメントアウト状態で含まれています。

storage.ymlは(gitなどで)ソース管理したいので、そこに秘密のキーを直接書き込むのはよくありません。Railsでは、そうしたキーをRails.application.credentialsに保存することが前提になっています。おや、そこは以前にはsecretsと呼ばれていたのではありませんか?はい、そのとおりです。しかしDHHは#30067で次のように説明しています。

config/secrets.ymlconfig/secrets.yml.encSECRET_BASE_KEYの組み合わせは混乱の元になります。これらの秘密情報に何を保存するべきなのかも、SECRET_BASE_KEYが一般的なセットアップと関連するものなのかどうかもはっきりしません。

この混乱に終止符を打つため、Railsでは「credential」という概念が新しく導入されました。

credentialは、config/credentials.yml.encファイルに暗号化された形で保存されるので、このファイルは安全にソース管理できます。もう環境変数の問題や、キー変更をチーム全員で同期する問題にわずらわされることはありません。

config/master.keyファイルは、いかなる場合であってもGitに登録してはいけません(このファイルはRails 5.2プロジェクトの.gitignoreに既に含まれています)。このファイルには、credentialを復号できる自動生成されたキーが含まれます。ドキュメントでも次のように警告されています。

このマスターキーを絶対に紛失しないこと!マスターキーは、チームがアクセス可能なパスワード管理ソフトウェアに保存すること。万一紛失すれば、あなたを含むいかなる人物も、暗号化されたcredentialに一切アクセスできなくなります。

では、内容が常に暗号化されているcredentialをどうやって編集すればよいのでしょうか? Rails 5.2から、そのための新しいタスクrails credentials:editが用意されています。このコマンドを実行すると、デフォルトのエディタで平文のテキストファイルが開くので、そこにkey_name: key_valueの形式でキーを書き込めます。Rails.application.credentials.key_nameでcredentialにアクセスできます。ネストしたキーを使っている場合はRails.application.credentials.dig(:section_name, :nested_key_name)でアクセスできます。一時ファイルを保存して閉じると、内容が暗号化されてconfig/credentials.yml.encに保存されます。ターミナルでrails credentials:showを実行するとキーを出力できます。

これで、同一のmaster.keyをチームのメンバー全員に渡せば、重要な情報が漏洩する心配をせずにGitで安全に共同作業できるようになります。production環境では、RAILS_MASTER_KEY環境変数のみ設定する必要があります。

メモ: Atomなどの外部エディタで編集する場合は、EDITOR="atom --wait" credentials:editのように実行する必要があります。修正量が少ない場合はEDITOR=vi credentials:editのようにシェルで動くエディタの方が手っ取り早いかもしれません。

Active Storageをクラウドで使う

Amazon S3を使って簡単なデモを行います。当然ながら、publicに読み出しアクセスできるS3バケット(bucket)が必要です。用意が整えば、後は以下の3つの手順を進めるだけで使えるようになります。

  1. storage.ymlamazon:セクションのコメントアウトを解除します。
  2. development.rbconfig.active_storage.service = :amazonを指定して、デフォルトのクラウドサービスをS3に切り替えます。
  3. コンソールでrails credentials:editと入力し、以下の要領でキーを入力します
aws:
  access_key_id: #idを入力
  secret_access_key: #アクセスキーを入力

これで完了です!アップロードしたファイルやvariantは自動的にAmazon S3経由で取り扱われます。post.image.service_urlは、バケットインスタンスを指す署名済みURLを生成します。

ダイレクトアップロード

Active Storageの開発が活発だった昨年夏、Fabio Akita氏は、Active Storageを有効にしたプロジェクトがHerokuなどのephemeralな(=短命な)ファイルシステムを用いるプラットフォーム上で動作する場合に、巨大なファイルを扱うことについて懸念を表明しました。Railsの作者DHHはTwitterのスレッドでブラウザからクラウドへの「ダイレクトアップロード(Direct Upload)」を実装することについて納得しました。ダイレクトアップロードは、Attachinaryが実装したCloudinaryストレージへのファイル送信と似たような方法で、アプリのバックエンドを完全にバイパスします。

ダイレクトアップロード機能については、Railsガイド(edge)にJavaScriptコードスニペットのよい説明が既に掲載されています3(最終的にCoffeeScriptではなくES6を使います!)。

ここではダイレクトアップロードを簡単に動かしてみましょう。以下の手順ではWebpackerを念頭に置いていますので、アプリでrails webpacker:installを実行しておいてください。

$ yarn add activestorage
// app/javascript/packs/application.js
import * as ActiveStorage from "activestorage";
import "../utils/direct_uploads.js"

ActiveStorage.start();
// app/javascript/utils/direct_uploads.js
// このフォルダとファイルを作成し、以下のコードをコピペする
// http://edgeguides.rubyonrails.org/active_storage_overview.html#example
<!-- app/views/posts/_form.html.erb -->
<div class="field">
  <%= form.label :images %>
  <%= form.file_field :images, multiple: true, direct_upload: true %>
</div>

上のdirect_upload: trueオプションによって、ファイルのフィールドのHTMLが以下のように生成されます。

<input multiple="multiple" data-direct-upload-url="http://localhost:3000/rails/active_storage/direct_uploads" name="post[images][]" id="post_images" type="file">

JavaScriptサンプルコードでは、Direct Upload用JSイベントを用いて、アップロードサイクルに従ってUIに応答を表示する(プログレスバーを更新するなど)方法が示されています。

EdgeガイドのDirect Uproad installationのサンプルCSSコードをコピーして、direct_uploads.cssファイルに貼り付けます(このファイルはapp/assets/stylesheetsの下に作成できます)。これで、サーバーを起動してpostページを開けば、巨大なファイルを選択できるようになり、長時間のタスクによってサーバーがブロックされていない様子を見ることができます。このアップロードは完全にXHRで行われ、プログレスバーが動的に更新されます。

Chromeでダイレクトアップロードする

ブラウザでファイルをダイレクトアップロードする

: 本記事執筆時点では、S3を使うダイレクトアップデートがFirefoxでXML Parsing Errorエラーになります。

S3を使う場合は、S3バケットの[Permissions]タブでCORS設定をオープンにする必要があります。以下はdevelopment環境で雑に動かすためだけの設定なので、production環境では設定を全開のままにしないでください!

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

「鏡よ鏡」

Active Storageのもうひとつの大きな機能はミラーリングです。ミラーリングによって、複数のクラウドストレージプロバイダの間でファイルを同期して冗長性を高めたり、クラウドの統合に役立てたりできます。たとえば、storage.ymlに以下を記述するとします。

production:
  service: Mirror
  primary: local
  mirrors:
    - amazon
    - google

続いて、使うクラウドサービスをproduction.rb:productionで設定します。これにより、アップロードされたファイルがローカルに保存されると同時にAmazon S3とGoogle Cloud Storageにもバックアップされます。ファイルを削除すると、クラウドからも削除されます。

Active Storageのクラウド関連について今のところ説明できるのは以上です。productionで通用することを示さなければならないので、これについてさらに洞察を提供できればと思います。

この辺で、Rails 5.2のその他の目玉機能についても見てみましょう。

Content-Security-Policyヘッダー設定用DSL

セキュリティはやはり重要です。ここでアプリのセキュリティをさらに強化することにしましょう4CSP(Content Security Policy)は、ブラウザが受信するネットワークリクエストを制限するために設計されています(ブラウザページで読み込んでよいものを指定するなど)が、副次的効果として、ブラウザから送信されるリクエストにも制限をかけることでたちの悪いハッキングを防止します。

Railsアプリで簡単にCSPを設定できるようになりました。グローバルに設定する場合は以下のようにします。

# config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy do |p|
  # 現在のhostnameからの送信(セキュア通信のみ)をデフォルトですべて許可
  p.default_src :self, :https
  # data-urlからのフォントや画像の読み込みを許可
  p.font_src    :self, :https, :data
  # ActiveStorageアセットで使う可能性のあるhostnameは必ずここに追加すること
  p.img_src     :self, :https, :data, "cloudfront.example.com"
  # <object>タグの禁止(Flashさよなら)
  p.object_src  :none
  # インライン<style>を許可(オフにするには`:unsafe_inline`を削除)
  p.style_src   :self, :https, :unsafe_inline
end

コントローラごとに設定する場合は以下を使います(さらに動的になります)。

class PostsController < ApplicationController
  # グローバルポリシーのextend/オーバーライド
  content_security_policy do |p|
    # ユーザー固有のドメイン名をベースにするポリシーを設定
    p.base_uri :self, -> { "https://#{current_user.domain}.example.com" }
  end
end

ただし、(本記事執筆時点では)ドキュメントに記載されていない注意事項が1つあります。Webpackerとwebpack-dev-serverを使っている場合は、development環境でCSP設定を更新してhttp://localhost:3035ws://localhost:3035への接続を許可しなければなりません。これを行わないと、Webpackのホットリロード機能で必要なweb socket接続がRailsにブロックされてしまいます。詳しくはissue #31754をご覧ください。問題を発見したNick Savrovに感謝します。

# Webpackerを使う場合は以下のような感じでCSPを設定する必要がある
Rails.application.config.content_security_policy do |p|
  p.font_src    :self, :https, :data
  p.img_src     :self, :https, :data
  p.object_src  :none
  p.style_src   :self, :https, :unsafe_inline

  if Rails.env.development?
    p.script_src :self, :https, :unsafe_eval
    p.default_src :self, :https, :unsafe_eval
    p.connect_src :self, :https, 'http://localhost:3035', 'ws://localhost:3035'
  else
    p.script_src :self, :https
    p.default_src :self, :https
  end
end

これは、RailsチームがCSP設定をデフォルトで無効にすることを決定した理由のひとつでもあります。

どこからでもアクセスできるCurrentシングルトン

モデルの中からcurrent_userにアクセスする方法を知りたいと思ったことはありますか?StackOverflowの"current_user"に関するすべての質問第3位は、このメソッドをモデルで使う方法についての質問です。

この状況はRails 5.2から変更される予定です。今回追加されるCurrentシングルトンのマジックは、アプリのあらゆる場所からアクセス可能なグローバルストアであるかのように振る舞います5

# app/models/current.rb
class Current < ActiveSupport::CurrentAttributes
  attribute :user
end

後はコントローラのどこかでuserを設定するだけで、モデル/ジョブ/メイラーなどあらゆる場所でアクセス可能になります。

class ApplicationController < ActionController::Base
  before_action :set_current_user

  private

  def set_current_user
    Current.user = current_user
  end
end

# モデルで以下を行えるようになる
class Post < ApplicationRecord
  # post作成時のユーザー指定は不要
  # デフォルトでカレントユーザーが使われる
  belongs_to :user, default: -> { Current.user }
end

このアイデアは新しいものではありません。Steve Klabnik作のrequest_store gemを使えば、あらゆるRackアプリで同じことができます。

: 「この機能は『関心分離の法則』に違反している」と思われるかもしれません。おっしゃるとおりです。何かおかしいと思ったときは使わないでください。

訳注

以下の記事も参考にどうぞ。

Railsの`CurrentAttributes`は有害である(翻訳)

HTTP/2 Early Hints

HTTP/2の「Early Hints」は、ブラウザのページ内でアセットを使う前にアセットを事前にダウンロードできるようにする機能です。これによってHTTP/2のパイプラインリクエストが効き、ページの読み込み時間が短縮されます。

Pumaサーバーに--early_hintsフラグを追加して起動し、HTTP/2互換プロキシ(h2oなど)をサーバーの前に配置するだけで使えるようになります。

Early Hintsについて詳しくは、この機能の作者であるEileen Uchitelleの記事『HTTP2 Early Hints』をお読みください。

訳注: 『HTTP の新しいステータスコード 103 Early Hints』も参考にどうぞ。

Bootsnap

Railsのような多機能なフレームワークで作業する場合のトレードオフのひとつが「起動時間」です。巨大なモノリシックアプリの起動やタスクの実行に1〜2分を要することがあります。

Railsアプリを事前に起動しておくSpring gemに加えて、RubyやYAMLのファイル読み込みを高速化するBootsnap(Shopifyのツール)という一片のマジックがデフォルトでGemfileに追加されたことで、コールドスタートが2〜4倍も速くなります6

Bootsnapは、Springの設定が面倒なDockerでの開発や、CIサーバーで利用する場合に特に便利です。


Rails 5.2はまだ新しいので、私たちがproduction環境で試す機会はこれまでありませんでした。皆さまの情報をTwitterで共有するときには、お気軽に私たちのアカウントにもメンションを下さい。フォームで直接お問い合わせいただくこともできます。本記事の作成で大変参考になったActive Storage記事の著者である、Mike Gunderloy氏に感謝いたします。


スタートアップをワープ速度で成長させられる地球外エンジニアよ!Evil Martiansのフォームにて待つ。

関連記事

Rails 5.2を待たずに今すぐActiveStorageを使ってみた(翻訳)

Rails: システムテストをRSpecで実行する(翻訳)

Rails: パーシャルと`collection:`でN+1クエリを回避してビューを高速化(翻訳)


  1. 添付ファイルのblob(binary large object)とは、本質的に文字どおり巨大なバイナリファイルです。active_storage_blobsではバイナリファイルをデータベースに保存せず、情報(ファイルサイズ、コンテンツの種類、メタデータ)を元にファイルの置き場所だけをトラックします。 
  2. ActiveStorage::Serviceを継承することで、他のクラウドサービスのサポートも実装できます。 
  3. RailsプロジェクトでWebpackerを使う場合の詳しい設定については、以下の「新しいRailsフロントエンド開発」シリーズ記事のpart 1part 2part 3をお読みください。 
  4. 5.2より前のRailsについては、secureheaders gemをご覧ください。 
  5. これは技術的にはスレッドごとのストアであり、リクエストが終了するとクリアされるので、マルチスレッドアプリでも安全に使えます。 
  6. このマジックが好きでない場合は、rails new --skip-bootsnapを実行します。 

CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。