ども、@kimihom です。
前回に引き続き Action Text に関して調査を続けている。今回は Amazon S3 へアップロードしたものをテキストエリアの中に表示させてみよう。
Active Storage の設定
Action Text のファイルアップロードは、Active Storage の設定に完全に依存している。てことで、これから設定するのは全て Active Storage の設定となる。
まず、config/storage.yml
を編集してみよう。デフォルトでは local の向き先はディスクに保存するようになっているが、S3 に直接あげるように変えてみる。
amazon: service: S3 access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) % secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_acces region: ap-northeast-1 bucket: mybucket
Rails.application.credentials.dig
は、bundle exec rails credentials:edit
で暗号化されたパスワード関連をセット、閲覧することができる。この場所に AWS の キーを保存してより安全に管理しようという形だ。Heroku を使っていれば 環境変数に保存することが多いかとは思うが、こちらは環境共通で、重要性の高いものを保存するという使い方で良さそう。
そして、config/environments/development.rb
での Active Storage の向き先を :amazon
に変えておく。
# Store uploaded files on the local file system (see config/storage.yml for options). config.active_storage.service = :amazon
Gemfile に AWS SDK を取り込むことも忘れずに。
gem "aws-sdk-s3", require: false
S3 へのダイレクトアップロードになるため、S3 側の CORS の設定もしておく必要がある。
<?xml version="1.0" encoding="UTF-8"?> <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <CORSRule> <AllowedOrigin>your-domain.com</AllowedOrigin> <AllowedMethod>PUT</AllowedMethod> <AllowedHeader>Origin</AllowedHeader> <AllowedHeader>Content-Type</AllowedHeader> <AllowedHeader>Content-MD5</AllowedHeader> <AllowedHeader>Content-Disposition</AllowedHeader> <MaxAgeSeconds>3600</MaxAgeSeconds> </CORSRule> </CORSConfiguration>
改めて app/javascript/packs/application.js
で正しく JavaScript ライブラリがロードされているかを確認しておこう。require("@rails/activestorage").start()
を呼び出すことで、S3 へのダイレクトアップロードを実現することが可能だ。
require("@rails/activestorage").start() require("trix") require("@rails/actiontext")
じゃあ、require("@rails/actiontext")
では何をやっているの? という疑問が湧いたので調べてみると、意外とシンプルだった。trix-attachment-add
でテキストエリアへのファイルドラッグ&ドロップを検知したら、Active Storage の Direct Upload を使って直接 S3 へアップロードし、結果を Action Text 内に入れるという処理である。
Action Text で作られるテーブルを改めてチェック
Action Text を使うと、3つのテーブルが自動で作られる。これらの違いを改めて確認しておく。
action_text_rich_texts
1つの記事に作成される action_text_rich_texts は 1つのみだ。1つの記事に画像が複数あっても、action_text_rich_texts は一つのレコードとしてまとめられる。record_id に対象の記事ID が保存される。Action Text
は一つのテーブルで、他の複数の name
(Article#body や Profile#introduction など) を保存できるように設計されている様子。
CREATE TABLE IF NOT EXISTS "action_text_rich_texts" ( "id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar NOT NULL, "body" text, "record_type" varchar NOT NULL, "record_id" integer NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);
active_storage_blobs
記事を書いている途中でアップロードした時点で保存される。ファイルをアップロードしたけど、後で記事を保存しなかった、というケースでも1レコードとして保存される。1つの記事に複数のactive_storage_blobsが存在することはもちろんある。
CREATE TABLE IF NOT EXISTS "active_storage_blobs" ( "id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "key" varchar NOT NULL, "filename" varchar NOT NULL, "content_type" varchar, "metadata" text, "byte_size" bigint NOT NULL, "checksum" varchar NOT NULL, "created_at" datetime NOT NULL);
active_storage_attachments
記事が保存された時点で、画像と記事の紐付けが行われる。こちらも1つの記事に複数の active_storage_attachments が存在することはもちろんある。record_id
に記事ID 、blob_id にactive_storage_blobs
のレコード ID が保存される。
CREATE TABLE IF NOT EXISTS "active_storage_attachments" ( "id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar NOT NULL, "record_type" varchar NOT NULL, "record_id" integer NOT NULL, "blob_id" integer NOT NULL, "created_at" datetime NOT NULL)"
Trix による ContentEditable 制御
Action Text は、Active Storage と Trix をうまく繋げるためのものだと考えるのが良いだろう。Trix は、ContentEditable を華麗に制御する JavaScript ライブラリだ。シンプルな JavaScript で、テキストエリアを操作できる。 地味に ローカルストレージの保存は大事だ。デフォルトの Action Text だと編集中にリロードとか戻るとか押されると、今までの編集が消えてしまう。よくあるブログ執筆ツールのように、「編集時の状態に戻す」ボタンで書いてきたものを取ってこれるようにしておこう。
var element = document.querySelector("trix-editor") element.editor // 範囲選択 element.editor.setSelectedRange([0, 1]) // カーソル移動 element.editor.moveCursorInDirection("backward") element.editor.expandSelectionInDirection("forward") // テキスト挿入 element.editor.insertString("Hello") element.editor.insertHTML("<strong>Hello</strong>") element.editor.insertLineBreak() element.editor.deleteInDirection("backward") // undo / redo element.editor.undo() element.editor.redo() // ローカルストレージへ保存 // Save editor state to local storage localStorage["editorState"] = JSON.stringify(element.editor) // Restore editor state from local storage element.editor.loadJSON(JSON.parse(localStorage["editorState"]))
終わりに
今回までの調査で、Action Text 内にドラッグ&ドロップしたファイルを S3 へアップロードすることが可能となった。 実際に S3 に保存されていることを確認しよう。なお今回の S3 意外にも、Active Storage は Microsoft Azure Storage Service, Google Cloud Storage Service もサポートしている。
引き続き Trix の調査を続けていこうと思う。Action Text の根幹技術は Trix だ。また ContentEditable の世界へ足を踏み入れると思うとワクワクする!