Rich text é um registro polimórfico
Todo model Rails que guarda texto simples o mantém numa coluna da própria tabela: uma coluna body, uma coluna description, uma por campo. O ActionText faz diferente. Ele guarda conteúdo rico numa única tabela compartilhada, chamada action_text_rich_texts, e liga cada linha ao seu dono por uma associação polimórfica.
A tabela por trás de todo has_rich_text
Rode bin/rails db e descreva a tabela com \d. As colunas, em resumo:
\d action_text_rich_texts
Coluna | Tipo | Notas
------------|----------|-------------------------------
id | bigint | chave primária
name | varchar | nome do atributo ("content")
body | text | o HTML de fato
record_type | varchar | "Community::Comment"
record_id | bigint | id do comentário
created_at | datetime |
updated_at | datetime |
Três colunas fazem o trabalho. name é o atributo declarado no model: quando você escreve has_rich_text :content, a string "content" cai aqui. record_type e record_id formam a chave estrangeira polimórfica, que aponta de volta para o model dono desse rich text.
Dois models, uma tabela
Esta aplicação tem dois models que usam ActionText. O primeiro é Enrollment::LessonNote:
# app/models/enrollment/lesson_note.rb (trimmed)
module Enrollment
class LessonNote < ApplicationRecord
has_rich_text :content
end
end
O segundo é Community::Comment:
# app/models/community/comment.rb (trimmed)
module Community
class Comment < ApplicationRecord
has_rich_text :content
end
end
Os dois chamam has_rich_text :content e guardam o rich text na mesma tabela action_text_rich_texts. A chave polimórfica os separa: o rich text de uma nota tem record_type: "Enrollment::LessonNote", e o de um comentário tem record_type: "Community::Comment".
O que o has_rich_text cria
Chamar has_rich_text :content num model faz três coisas por baixo dos panos:
- Define uma associação
has_one,has_one :rich_text_content, class_name: "ActionText::RichText", e o model ganha um acessor.contentque devolve um registroActionText::RichText, não uma string. - Define um leitor
content, que monta o registro quando ainda não existe, e um escritorcontent=, que define o body dele, entãonote.content = "Olá"se lê como um atributo comum. - Declara esse
has_onecomautosave: trueedependent: :destroy, então salvar o pai salva o rich text, e destruir o pai o destrói.
Você nunca mexe na tabela action_text_rich_texts diretamente. Você escreve note.content = "<p>Ideia em negrito</p>", salva a nota, e o ActionText cuida da ligação.
Leitura: o ActionText::Content embrulha o HTML
Ler comment.content não devolve uma string crua. Devolve um registro ActionText::RichText cujo atributo .body é um objeto ActionText::Content. Esse objeto embrulha o HTML bruto e oferece helpers:
comment = Community::Comment.first
comment.content # => registro ActionText::RichText
comment.content.body # => objeto ActionText::Content
comment.content.to_s # => o HTML, dentro de <div class="trix-content">
comment.content.to_plain_text # => "Ideia em negrito" (sem tags)
A diferença importa na validação. O Community::Comment desta aplicação valida o tamanho no texto puro:
def content_present_and_within_limit
text = content.present? ? content.to_plain_text.strip : ""
return errors.add(:content, :blank) if text.empty? && !content&.body&.attachments&.any?
errors.add(:content, :too_long, count: MAX_LENGTH) if text.length > MAX_LENGTH
end
Validar to_plain_text.length em vez do tamanho do HTML bruto é proposital. Um par de negrito, <strong></strong>, acrescenta 17 caracteres de markup que o estudante nunca digitou e que não deveriam contar contra o limite.
A migration que você já rodou
O ActionText traz um gerador de instalação (bin/rails action_text:install) que cria a migration. A versão desta aplicação fica em db/migrate/20260830180001_create_action_text_tables.action_text.rb. Ela cria uma tabela, action_text_rich_texts, com o índice único em [record_type, record_id, name] que garante um rich text por nome por registro.
Para onde este curso vai
Esta primeira aula tratou da camada de armazenamento, a tabela polimórfica que guarda todo rich text da aplicação. As seis aulas seguintes sobem a partir daqui:
- Aula 2: o editor Trix, o que ele renderiza e como o
rich_text_arealiga o formulário ao armazenamento. - Aula 3: sanitização, ou seja, quais tags HTML o ActionText mantém e quais descarta.
- Aula 4: anexos, e como o Trix e o ActiveStorage trabalham juntos para embutir arquivos.
- Aula 5: renderização e estilo, exibindo rich text com segurança e com boa aparência.
- Aula 6: a troca de editor, e como esta aplicação usa o Lexxy no lugar do Trix sobre a mesma camada de armazenamento.
- Aula 7: juntando tudo, o ciclo completo da requisição, da tecla digitada à página renderizada.
Continue com a aula 2
Trix: o editor padrão
O Trix renderiza um elemento customizado que escreve um conjunto fixo de tags HTML num input escondido.
Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.