AGENT SKILL

python-anti-patterns

Common Python anti-patterns to avoid. Use as a checklist when reviewing code, before finalizing implementations, or when debugging issues that might stem from known bad practices.

License 许可
MIT
最近更新
2026-11-08
Stars
★ 1
Forks
0

01 前言 Front Matter

name
actiontext-richtext
description
Action Text (rich text) in Rails 8 — Trix editor integration, has_rich_text on models, Active Storage for embedded attachments, the safe-list of HTML tags, custom embeds via attachable mixin, server-side sanitization, XSS prevention, search indexing of plain text. Use when the user mentions Action Text, Trix, rich text editor, has_rich_text, WYSIWYG, formatted text content, embedded images, or asks how to add a rich text editor to Rails.

02 资源文件 1

03 SKILL.md 正文

ActionText (Rich Text)

Add rich text editing to Rails models. Trix editor + ActionText + ActiveStorage handle the editor, storage, and embedded attachments. Default Rails 8 has decent defaults — the gotchas are around sanitization, search indexing, and custom embeds.

The opinion

has_rich_text :body on the model. Trix is the default editor — fine for most cases. Sanitize on output (Rails does by default). Index the plain-text representation, not the HTML. For custom embeds (mentions, polls), use ActionText's attachable mixin. Don't migrate to TinyMCE/CKEditor unless you have a specific need — Trix's smaller surface area is a security win.

Counter-positions:

  • TinyMCE / CKEditor / Tiptap — more powerful editors. Larger XSS attack surface, separate licensing. Pick only if Trix is genuinely insufficient.

Setup

bin/rails action_text:install
bin/rails db:migrate

Creates action_text_rich_texts and active_storage_blobs/attachments tables.

Core patterns

Pattern 1: Model + form

class Post < ApplicationRecord
  has_rich_text :body
end
<%= form.label :body %>
<%= form.rich_text_area :body %>
<!-- View -->
<%= @post.body %>  <!-- Renders the rendered Trix HTML, sanitized -->

Pattern 2: Sanitization (the default + custom)

Rails sanitizes ActionText output by default. The allowed tags include common formatting (<strong>, <em>, <h1-6>, <a>, <img>, etc.) — see Rails::Html::SafeListSanitizer for the exact list.

To restrict further:

# config/initializers/action_text.rb
ActionText::ContentHelper.allowed_tags -= %w[h1 h2]

To allow more (carefully):

ActionText::ContentHelper.allowed_tags += %w[mark]

Anti-pattern: allowing <script> (even by mistake). The default safe-list excludes it; never add.

Pattern 3: Embedded attachments

Trix supports drag-and-drop image upload. ActionText wires it to ActiveStorage automatically.

# Variant configured on the attachment association (Rails 7.1+):
class Post < ApplicationRecord
  has_rich_text :body
end

# config/application.rb — variant pipeline (libvips by default in Rails 7+)
# Rails.application.config.active_storage.variant_processor = :vips

For the editor preview specifically, ActionText resizes embedded images automatically; you don't need to declare a variant unless you want a non-default size for <%= image_tag attachment.representation(resize_to_limit: [800, 800]) %> in your views.

The HTML stored includes <action-text-attachment> tags pointing at the blob.

Pattern 4: Custom attachables (mentions, polls)

Beyond images:

class User < ApplicationRecord
  include ActionText::Attachable

  # Define how the user renders inside rich text
  def to_trix_content_attachment_partial_path
    "users/mention"
  end
end
<!-- app/views/users/_mention.html.erb -->
<a href="<%= user_path(user) %>" class="mention">@<%= user.username %></a>
# In Trix client-side JS, you'd implement an @-completion that calls:
post.body.body.attachables.append(User.find(id))

This pattern is how Basecamp's hey.com handles inline mentions and pinned messages — see signal-v-noise blog posts.

Pattern 5: Plain-text indexing (search)

Rich-text HTML stored in body is hard to search. ActionText auto-stores a plain-text version:

post.body.to_plain_text  # "Hello, world!"

For search:

class Post < ApplicationRecord
  has_rich_text :body

  # If using pg_search:
  include PgSearch::Model
  multisearchable against: [:title, :body_plain]

  def body_plain
    body.to_plain_text
  end
end

See rails-search for full search patterns.

Pattern 6: Testing

RSpec.describe Post, type: :model do
  it "stores rich text body" do
    post = create(:post, body: "<strong>Hello</strong>")
    expect(post.body.to_plain_text).to eq("Hello")
    expect(post.body.to_s).to include("<strong>")
  end

  it "sanitizes script tags" do
    post = create(:post, body: "<script>alert(1)</script>Hello")
    expect(post.body.to_s).not_to include("<script>")
    expect(post.body.to_plain_text).to include("Hello")
  end
end
# System spec — Trix interaction
RSpec.describe "Editing a post", type: :system, js: true do
  it "allows rich text input" do
    sign_in user
    visit edit_post_path(post)
    fill_in_rich_text_area "post_body", with: "Hello world"
    click_button "Update"
    expect(page).to have_content("Hello world")
  end
end

fill_in_rich_text_area is ActionText's Capybara helper (loaded by action_text/system_test_helper).

Pattern 7: Migrating off serialize text columns

# Old: posts.body :text — plain or HTML stored raw
# New: ActionText

# Migration — assign the legacy column value to the has_rich_text `:body` setter.
# Read the legacy raw column via the table BEFORE has_rich_text was wired up.
class MigratePostBodyToActionText < ActiveRecord::Migration[8.0]
  disable_ddl_transaction!

  def up
    legacy_rows = ActiveRecord::Base.connection.select_all("SELECT id, legacy_body FROM posts WHERE legacy_body IS NOT NULL")
    legacy_rows.each do |row|
      post = Post.find(row["id"])
      post.update!(body: row["legacy_body"])  # routes to ActionText via has_rich_text
    end
  end
end

For non-trivial migrations, do this in a background job, not a migration. See safe-migrations.

Common mistakes to refuse

  • Don't allow <script> in the safe-list. XSS.
  • Don't accept user-provided CSS / HTML attributes (style, onclick) — Rails strips them by default; don't override.
  • Don't search against HTML — search against plain text via to_plain_text.
  • Don't store rich text in the same text column as your old plain text. Migrate.
  • Don't switch to TinyMCE / CKEditor without a justification — Trix's smaller surface is a security win.

When NOT to use this skill

  • The user needs Markdown (not WYSIWYG) — use a separate Markdown gem (kramdown, commonmarker).
  • The user needs collaborative editing — out of scope; use Liveblocks / Yjs.

See also

  • activestorage-uploads — for the embedded attachment storage
  • rails-search — indexing the plain-text body
  • rails-security-baseline — XSS / sanitization

Sources

04 同仓库更多 Skills

cercova-studios
cercova-studios/10x-swe

turnstile-spin

Set up Cloudflare Turnstile end-to-end in a project. Scan the codebase, create the widget via the Cloudflare API, embed it where user requests need bot verification (form submissions, SPA actions, API endpoints, download links, comment or vote submissions, etc.), wire canonical server-side siteverify in the customer's existing backend, validate, and persist the skill. Load this when a user asks to add Turnstile, set up CAPTCHA, protect a form or endpoint from bots, or fix a Turnstile integration. Uses Cloudflare's documented Turnstile contract.

后端与接口
★ 1
cercova-studios
cercova-studios/10x-swe

frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.

设计师前端开发视觉设计
★ 1
cercova-studios
cercova-studios/10x-swe

source-insights

Prepare inspected source evidence, preserve exact citations, and publish reviewed insights into an explicit local library.

★ 1
cercova-studios
cercova-studios/10x-swe

skill-graph

Discover skills and workflows through independent lexical search, relationship paths and a terminal graph.

Skill与智能体开发
★ 1
cercova-studios
cercova-studios/10x-swe

session-search

Recall public session turns with exact provenance, bounded excerpts and independent piped exports.

★ 1
cercova-studios
cercova-studios/10x-swe

newsletter

Produce a private local AI newsletter from bounded public research, with cited deep dives, HTML/Markdown output, Dagu scheduling and seven-day retention.

邮件与沟通资料检索与研究
★ 1