- Ruby / Rails関連
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
をブラウザで開き、適当な画像を選んで投稿を作成します。
投稿の編集や画像の変更ももちろんできますので、やってみてください。ご覧のとおりファイルアップロード機能がアプリで使えるようになりました!
ここまでの作業をまとめます。
- モデル: 定義で
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
に設定してください。
このログから、Railsがフォームを処理し、受け取ったファイルをディスクに保存し、保存場所をエンコードしてキーを生成し、そのキーをactive_storage_blobs
テーブルで参照し、posts
テーブルでレコードを1件作成し、Blob
をPost
とactive_storage_attachments
経由で関連付けている様子がわかります。
以下は、PostsController
のshow
アクションがGET
で呼び出されたときの挙動です。
1件のリクエストは3箇所で処理されます。ActiveStorage::BlobsController
とActiveStorage::DiskController
はファイルを扱います。このようにして、画像のパブリックなURLは常に実際の場所から切り離されます。クラウドサービスを使っている場合は、BlobsController
によってクラウド内の正しい署名済みURLにリダイレクトされます。
Post
インスタンスで何ができるようになったかを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
をブラウザで更新してログを見てみましょう。
問題が起きているのがおわかりでしょうか?「N+1」という名のヒドラがおぞましき頭をもたげています。ありがたいことに、Active Storageにはちゃんと解決法が用意されています。この方法では、関連付けられたblobをincludes
するwith_attached_image
スコープ(またはwith_attached_your_attachment_name
スコープ)を生成します。必要な作業は、PostsController#index
の@posts = Post.all
を@posts = Post.with_attached_image
に変更することだけです。結果を見てみましょう。
素晴らしい!しかし、Active Recordが常に正しい選択を行えることを当てにしたくないという理由で、includes
ではなくeager_load
やpreload
を使いたい場合はどうすればよいのでしょうか?そんなときは次のようにします。
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.yml
やconfig/secrets.yml.enc
やSECRET_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つの手順を進めるだけで使えるようになります。
storage.yml
のamazon:
セクションのコメントアウトを解除します。development.rb
でconfig.active_storage.service = :amazon
を指定して、デフォルトのクラウドサービスをS3に切り替えます。- コンソールで
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で行われ、プログレスバーが動的に更新されます。
注: 本記事執筆時点では、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
セキュリティはやはり重要です。ここでアプリのセキュリティをさらに強化することにしましょう4。CSP(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:3035
とws://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アプリで同じことができます。
注: 「この機能は『関心分離の法則』に違反している」と思われるかもしれません。おっしゃるとおりです。何かおかしいと思ったときは使わないでください。
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のフォームにて待つ。
関連記事
-
添付ファイルのblob(binary large object)とは、本質的に文字どおり巨大なバイナリファイルです。
active_storage_blobs
ではバイナリファイルをデータベースに保存せず、情報(ファイルサイズ、コンテンツの種類、メタデータ)を元にファイルの置き場所だけをトラックします。 ↩ -
ActiveStorage::Service
を継承することで、他のクラウドサービスのサポートも実装できます。 ↩ - RailsプロジェクトでWebpackerを使う場合の詳しい設定については、以下の「新しいRailsフロントエンド開発」シリーズ記事のpart 1、part 2、part 3をお読みください。 ↩
- 5.2より前のRailsについては、secureheaders gemをご覧ください。 ↩
- これは技術的にはスレッドごとのストアであり、リクエストが終了するとクリアされるので、マルチスレッドアプリでも安全に使えます。 ↩
-
このマジックが好きでない場合は、
rails new --skip-bootsnap
を実行します。 ↩
訳注
以下の記事も参考にどうぞ。