From elixir-phoenix
Provides Ecto patterns for schemas, changesets, queries, migrations, Multi, associations, preloads, upserts. Activates when editing Repo calls, Ecto.Query, or schema fields; skips Ash.
npx claudepluginhub oliver-kriska/claude-elixir-phoenix --plugin elixir-phoenixThis skill uses the workspace's default tool permissions.
Reference for working with Ecto schemas, queries, and migrations.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Reference for working with Ecto schemas, queries, and migrations.
cast/4 for user/API input, change/2 or put_change/3 for internal trusted data:float FOR MONEY — Always use :decimal or :integer (cents)u.name == ^user_input is safe, string interpolation causes SQL injectionhas_many, JOIN FOR belongs_to — Avoids row multiplicationfrom(a in A, b in B) without on: creates Cartesian productcast_assoc WITH SHARED DATA — When multiple parents share child data, deduplicate child records BEFORE building changesets. Dedup only works within a single changesetdefmodule MyApp.Context.Entity do
use Ecto.Schema
import Ecto.Changeset
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
schema "entities" do
field :name, :string
field :status, Ecto.Enum, values: [:draft, :active, :archived]
field :amount_cents, :integer # Never :float for money!
belongs_to :user, MyApp.Accounts.User
timestamps(type: :utc_datetime_usec)
end
def changeset(entity, attrs) do
entity
|> cast(attrs, [:name, :status, :amount_cents])
|> validate_required([:name])
|> foreign_key_constraint(:user_id)
end
end
| Function | Use When |
|---|---|
cast/4 | External data (user input, API) |
put_change/3 | Internal trusted data (timestamps, computed) |
change/2 | Internal data from existing struct |
| Relationship | Strategy |
|---|---|
belongs_to | JOIN (single query) |
has_many | Separate queries (avoid row multiplication) |
| Wrong | Right |
|---|---|
field :amount, :float | field :amount_cents, :integer |
"SELECT * WHERE name = '#{name}'" | from(u in User, where: u.name == ^name) |
Repo.all(User) |> Enum.filter(& &1.active) | from(u in User, where: u.active) |
| Preloading in loops | Repo.preload(posts, :comments) |
Repo.get!(User, user_id) with user input | Repo.get(User, id) + handle nil |
For detailed patterns, see:
${CLAUDE_SKILL_DIR}/references/changesets.md - cast vs put_change, custom validations, prepare_changes${CLAUDE_SKILL_DIR}/references/queries.md - Composable queries, dynamic, subqueries, preloading${CLAUDE_SKILL_DIR}/references/migrations.md - Safe migrations, concurrent indexes, NOT NULL${CLAUDE_SKILL_DIR}/references/transactions.md - Repo.transact, Ecto.Multi, upserts