Textのファイルアップロードを分解調査する(翻訳)|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社エンジニアの「?」を「!」に。原著者の許諾を得て翻訳・公開いたします。Goby言語の作者でありRails開発者/コントリビューターです。6.0.0.rc2を元にしています。Textコンポーネントに対してこれといった興味はありませんでした。Textを使うことは業務でもサブプロジェクトでもまずありそうになかったからです。Textを調べました。Textはある種の「沼」なのですが、現時点ではどうもこれといったオンライン資料が見当たらないので、自分が調べてわかった範囲で本記事にまとめました。Textに取り組み始めた人にもお役に立てばと願っています。Storageコンポーネント(主にダイレクトアップロード機能)についても多少言及します。全体の手順は以下の3つに分かれています。Textとはあまり関連していませんが、ブラウザのテキスト入力エリアに画像をドラッグアンドドロップする仕組みもめちゃめちゃ面白いと思います(さてどうでしょう?)?。RailsのJavaScriptライブラリは、最初に以下のようなファイルメタデータを持つPOSTリクエストを/rails/active_storage/direct_uploadsに送信します(このエンドポイントは設定変更可能です)。RailsはActiveStorage::Blobレコードを1件作成し、ダイレクトアップロード用のURLとそのblobの署名済みidを返します。JSライブラリは、ダイレクトアップロードのURLを受け取るとそのファイルアップロードを実行します。それと並行してTrixエディタのテキストエリアに画像が挿入され、画像がfigure要素でラップされます。アップロードされたファイルのメタデータは、figure要素のdata-trix-attachment属性の中に配置されます。この時点までに、アップロードした画像はページで表示され、アップロードしたファイルはストレージに、画像のblobレコードはデータベースにそれぞれ保存されます?。Railsは、次の手順でテキストコンテンツを保存するまでは、blobレコードをアタッチする対象をまだ認識していません。パース、変換、アタッチテキストコンテンツを無事に受け取っても、まだデータベースには保存できません。以下のような作業がまだ残っています。その画像のblobレコードは現時点では孤立しているので、ActionText::RichTextレコードなどにアタッチする方法を見つける必要があります。テキストコンテンツを必ずしもTrixエディタに表示するとは限らないので、Trixの要素を直接保存するのはよい考えとは言えません。Textで何らかの変換をかけておく必要があります。以上を実行するために、まずコンテンツをパースする必要があります。パースActionText::RichTextオブジェクトが初期化されるときに、テキストコンテンツはActionText::Contentオブジェクトの初期化に使われます(このオブジェクトはこの後のセクションでcontentオブジェクトと呼ぶことにします)。このcontentオブジェクトは、コンテンツをツリー構造で保存するので、内部の要素を検索/置換するのは簡単です。Textはcontentのツリーを探索して、[data-trix-attachment]セレクタにマッチする要素があるかどうかを調べます。該当のノードが見つかると、data-trix-attachmentの値をパースして一連の属性に変換します。Textはこれらの属性を用いてaction-text-attachmentノードを初期化します。このノードをシリアライズしてhtmlにすると以下のような感じになります。アタッチ次はリッチテキストレコードに画像blobをアタッチします。すべてのアタッチメントをどのように検索すればよいのでしょうか?ここで、contentオブジェクトにはattachablesというメソッド呼び出しがあることに気が付きました。このメソッドは、(ツリーを探索してコレクションすることで)その中に含まれるaction-text-attachment要素をすべて返します。Textの添付ファイル(attachment)は、実際には以下の3種類です。ActiveStorage経由でアップロードしたアセットTrixパーシャルリモート画像ActionTextはこれらの種類のattachableをすべて検索し、ActiveStorage::BlobをgrepしてActionText::RichTextレコードにアタッチしてから、レコードを保存します。署名済みグローバルidここまでで何か重要な手順が1つ抜けていることに皆さんお気づきでしょうか?「attachableはいつの間にblobレコードになったのか?」と首をかしげる方もいるでしょう。この点を説明するには、先ほどのaction-text-attachmentノード内に表示されるsgidという属性をまず理解しておく必要があります。何のこっちゃとお思いでしょうが、意味は次のとおりです。sgidはアプリ全体のグローバル識別子である。sgidはblobオブジェクトから直接生成される。sgidには、このblobオブジェクトを検索するのに必要な必要なものがすべて含まれている(モデル名やレコードidも)sgidは他人がこのデータを書き換えられないように署名されている(署名済みグローバルidは以下のようにコンソールで調べられます)。gemをチェックすべきです。ActionTextはこれのおかげで、idもモデル名も持たない添付ファイルblobを見つけられるのです。レンダリングテキストコンテンツの処理方法と保存方法を理解してしまえば、ActionTextでコンテンツをレンダリングする方法を推測するのは難しくありません。コンテンツをパースしてツリーにするすべての添付ファイルノードを検索する添付ファイルのsgidで画像blobをグローバルに検索するさてそれでは、本記事の冒頭で予告した「あっと驚くもの」で締めくくりましょう。loadするために、以下のようなスコープをいくつか提供しています。かかわらず、テキストコンテンツをレンダリングするときに、アプリケーションでn+1クエリが生み出されていることがわかります。なっているのでしょうか?原因は、blobレコードをsgidで検索しているからです!上は本質的に以下のようなものです。loadingのスコープの恩恵をまったく得られません。loadするクエリが余分に作成されるため、むしろ悪化することすらあります(#36177で報告されているissueがそれです)。#36177は記事公開時点で引き続きopenになっています。Textというコンポーネントの設計が実によくできていて、リッチテキストエディタが必要になった場合のベストチョイスだと思うようになりました。Textにはまだ改良の余地が残されています。前述のn+1クエリなどがそうですね。Textを使い始めている方から本記事にフィードバックをいただけるとうれしく思います。Textを使いやすいコンポーネントとして成熟させましょう?。↓DHHが以下をリツイートしました?。コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。チュートリアル第2版の監修および半分程度を翻訳、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れて更新翻訳中。enno.jpを運営。実は最近Go言語が好きで、Goで書かれたRubyライクなGoby言語のメンテナーでもある。仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。Storageを使ってはいけない(翻訳)#Tags#会社紹介#自社サービス#勉強会#Rails#PHP#デザイン#実績#週刊RailsウオッチCONTACTお気軽にご相談ください。(翻訳)CONTACTTechRachoでは、パートナーシップをご検討いただける方からのご連絡をお待ちしております。お気軽にご意見・ご相談ください。