TerminalSkills/skills

phoenix

Phoenix is an Elixir web framework built on the BEAM VM for fault-tolerant, real-time applications.

ソースを見る

使い方

  1. インストールコマンドをコピーし、自分の環境へ導入する前にソースを確認します。
  2. 必要なツール、認証情報、対応環境を原文で確認します。
  3. 入力と成果物を指定し、出力を資料と照合します。
npx skills add https://github.com/TerminalSkills/skills --skill phoenix

開始前の確認

  • 導入にはモデル契約、API枠、ファイルへのアクセス許可は含まれません。
  • スター数や導入数は活動の指標であり、独立した品質・安全性試験ではありません。
リポジトリの原文

見出し、例、コード、表、リンク、参照画像を含む原文を表示しています。

Phoenix

Overview

Phoenix leverages the Erlang VM (BEAM) for massive concurrency and fault tolerance. LiveView enables rich, real-time UIs with server-rendered HTML, with little custom JavaScript. This skill targets Phoenix 1.8 (1.8.16 checked) with LiveView 1.x and Ecto 3.

Instructions

Installation

bash
mix archive.install hex phx_new      # asks to confirm; add --force to skip the prompt
mix phx.new blog --database postgres # PostgreSQL is the default
cd blog
mix setup                            # deps.get, ecto.setup, assets.setup, assets.build
mix phx.server                       # http://localhost:4000 (dev server binds to localhost)

On Linux install inotify-tools for live reload. The examples below use the app name my_app; substitute your own.

Ecto Schema and Migration

elixir
# lib/my_app/articles/article.ex — Ecto schema
defmodule MyApp.Articles.Article do
  use Ecto.Schema
  import Ecto.Changeset

  schema "articles" do
    field :title, :string
    field :slug, :string
    field :body, :string
    field :published, :boolean, default: false
    belongs_to :author, MyApp.Accounts.User
    timestamps()
  end

  def changeset(article, attrs) do
    article
    |> cast(attrs, [:title, :body, :published, :author_id])
    |> validate_required([:title, :body, :author_id])
    |> validate_length(:title, max: 200)
    |> unique_constraint(:slug)
    |> generate_slug()
  end

  defp generate_slug(changeset) do
    case get_change(changeset, :title) do
      nil -> changeset
      title -> put_change(changeset, :slug, slugify(title))
    end
  end

  defp slugify(title) do
    title |> String.downcase() |> String.replace(~r/[^a-z0-9]+/u, "-") |> String.trim("-")
  end
end
elixir
# priv/repo/migrations/20261024120000_create_articles.exs — migration
defmodule MyApp.Repo.Migrations.CreateArticles do
  use Ecto.Migration

  def change do
    create table(:articles) do
      add :title, :string, null: false, size: 200
      add :slug, :string, null: false
      add :body, :text, null: false
      add :published, :boolean, default: false
      add :author_id, references(:users, on_delete: :delete_all), null: false
      timestamps()
    end

    create unique_index(:articles, [:slug])
  end
end

Context Module

elixir
# lib/my_app/articles.ex
defmodule MyApp.Articles do
  import Ecto.Query
  alias MyApp.Repo
  alias MyApp.Articles.Article

  def list_published do
    Article
    |> where(published: true)
    |> order_by(desc: :inserted_at)
    |> preload(:author)
    |> Repo.all()
  end

  def get_published_by_slug!(slug) do
    Article |> where(slug: ^slug, published: true) |> preload(:author) |> Repo.one!()
  end

  def search(term) do
    Article
    |> where([a], a.published and ilike(a.title, ^"%#{term}%"))
    |> order_by(desc: :inserted_at)
    |> preload(:author)
    |> Repo.all()
  end

  def create_article(attrs) do
    %Article{}
    |> Article.changeset(attrs)
    |> Repo.insert()
  end
end

Router

elixir
# lib/my_app_web/router.ex — routing
defmodule MyAppWeb.Router do
  use MyAppWeb, :router

  # pipeline :browser (session, flash, root layout, CSRF) is generated by mix phx.new
  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", MyAppWeb do
    pipe_through :browser
    live "/articles", ArticleLive.Index, :index
    live "/articles/:slug", ArticleLive.Show, :show   # Show: see mix phx.gen.live
  end

  scope "/api", MyAppWeb.API do
    pipe_through :api
    resources "/articles", ArticleController, only: [:index, :create]
  end
end

LiveView

elixir
# lib/my_app_web/live/article_live/index.ex
defmodule MyAppWeb.ArticleLive.Index do
  use MyAppWeb, :live_view
  alias MyApp.Articles
  @impl true
  def mount(_params, _session, socket) do
    {:ok, assign(socket, articles: Articles.list_published(), search: "")}
  end

  @impl true
  def handle_event("search", %{"query" => query}, socket) do
    {:noreply, assign(socket, articles: Articles.search(query), search: query)}
  end
end
heex
<div>
  <h1>Articles</h1>
  <form phx-change="search">
    <input type="text" name="query" value={@search} phx-debounce="300" placeholder="Search..." />
  </form>
  <div :for={article <- @articles}>
    <h2><.link navigate={~p"/articles/#{article.slug}"}>{article.title}</.link></h2>
    <p>By {article.author.email}</p>
  </div>
</div>

JSON Controller

elixir
# lib/my_app_web/controllers/api/article_controller.ex — API controller
defmodule MyAppWeb.API.ArticleController do
  use MyAppWeb, :controller
  alias MyApp.Articles

  def index(conn, _params) do
    json(conn, %{data: Enum.map(Articles.list_published(), &article_json/1)})
  end

  def create(conn, %{"article" => params}) do
    case Articles.create_article(params) do
      {:ok, article} -> conn |> put_status(:created) |> json(%{data: article_json(article)})
      {:error, changeset} -> conn |> put_status(422) |> json(%{errors: format_errors(changeset)})
    end
  end

  defp article_json(a), do: %{id: a.id, title: a.title, slug: a.slug}

  defp format_errors(changeset) do
    Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end)
  end
end

Channels (Real-time)

elixir
# lib/my_app_web/channels/user_socket.ex — authenticate before any channel joins
defmodule MyAppWeb.UserSocket do
  use Phoenix.Socket
  channel "room:*", MyAppWeb.RoomChannel

  @impl true
  def connect(%{"token" => token}, socket, _connect_info) do
    case Phoenix.Token.verify(MyAppWeb.Endpoint, "user socket", token, max_age: 86_400) do
      {:ok, user_id} -> {:ok, assign(socket, :user_id, user_id)}
      {:error, _} -> :error
    end
  end

  def connect(_params, _socket, _connect_info), do: :error

  @impl true
  def id(socket), do: "user_socket:#{socket.assigns.user_id}"
end

# lib/my_app_web/channels/room_channel.ex
defmodule MyAppWeb.RoomChannel do
  use MyAppWeb, :channel

  def join("room:" <> _room_id, _params, socket), do: {:ok, socket}

  def handle_in("message", %{"body" => body}, socket) do
    broadcast!(socket, "message", %{body: body, user: socket.assigns.user_id})
    {:noreply, socket}
  end
end

Register the socket in endpoint.ex: socket "/socket", MyAppWeb.UserSocket, websocket: true, longpoll: false.

Testing

elixir
defmodule MyApp.ArticlesTest do
  use MyApp.DataCase
  import MyApp.AccountsFixtures   # created by `mix phx.gen.auth Accounts User users`

  test "create_article/1 builds a slug" do
    attrs = %{title: "Launch notes", body: "Content", author_id: user_fixture().id}
    assert {:ok, article} = MyApp.Articles.create_article(attrs)
    assert article.slug == "launch-notes"
  end
end

Key Commands

mix phx.gen.live Articles Article articles title:string body:text generates a LiveView CRUD with migration and tests; mix ecto.migrate, mix phx.routes, mix test, iex -S mix phx.server (dev server with REPL), mix phx.gen.secret (value for SECRET_KEY_BASE).

Examples

Example 1: "Add a blog section with search"

Run mix phx.gen.auth Accounts User users for authors, add the schema, migration and context above, run mix ecto.migrate, and add the live routes. Typing in the search box sends a search event (debounced 300 ms) and the list re-renders without a page load.

Example 2: "Give me a JSON endpoint for mobile"

Add the /api scope and ArticleController. curl -s -X POST localhost:4000/api/articles -H 'content-type: application/json' -d '{"article":{"title":"Launch notes","body":"Hello","author_id":1}}' returns 201 with {"data":{"id":1,"title":"Launch notes","slug":"launch-notes"}}; a missing title returns 422 with {"errors":{"title":["can't be blank"]}}.

Guidelines

  • Production config is read from config/runtime.exs: set SECRET_KEY_BASE, DATABASE_URL, PHX_HOST and PHX_SERVER=true as environment variables; build with MIX_ENV=prod mix release.
  • Authenticate sockets and check authorization in every handle_event / handle_in; clients can send any event name and payload.
  • Pass only whitelisted fields to cast/3 (never cast(attrs, Map.keys(attrs))) and never cast privileged fields such as is_admin.
  • Keep the :browser pipeline's CSRF protection; skip it only in :api pipelines that authenticate with tokens.
  • Use phx-debounce on inputs that fire on every keystroke, and stream/3 instead of assigning large lists to the socket.

Key Patterns

  • Business logic lives in context modules; controllers and LiveViews only delegate to them.
  • Validate through changesets; Ecto never lazy-loads, so preload explicitly.
  • Use Ecto.Multi for multi-step transactions and Phoenix.PubSub.broadcast to push updates across LiveView processes.
同じリポジトリから

関連する Skills

すべての Skills
TerminalSkills
コミュニティ

3dsmax-rendering

Scripts and configures production rendering in Autodesk 3ds Max with the V-Ray and Corona renderers: output size and files, render elements, denoising, light mix, batch and command-line rendering, and network rendering. Use when a user asks to set up a production render, render several cameras in one batch, render from the command line or on a render farm, add render passes for compositing, or cut render time for archviz and product shots.

導入数
0
GitHub Stars
180
更新日
10月25日
TerminalSkills
コミュニティ

3dsmax-scripting

Covers scripting Autodesk 3ds Max, the 3D modeling and rendering application, with MAXScript and Python (pymxs): scene manipulation, object creation, material assignment, camera and light setup, batch operations, and file I/O. Use when tasks involve automating repetitive 3ds Max workflows, batch processing scenes, running scripts headless with 3dsmaxbatch, creating custom tools, or scripting scene setup for archviz, product visualization, or VFX.

導入数
0
GitHub Stars
180
更新日
10月25日
TerminalSkills
コミュニティ

3proxy

3proxy is a small open-source proxy server that runs HTTP/HTTPS, SOCKS4/5, SNI and TCP/UDP port-mapping proxies from one config file. Use when a user asks to set up an HTTP or SOCKS5 proxy, add proxy users and passwords, write 3proxy access rules, chain or rotate upstream (parent) proxies, limit bandwidth, connections or monthly traffic per user, run 3proxy in Docker, or fix a 3proxy.cfg that will not start.

導入数
0
GitHub Stars
180
更新日
10月25日
TerminalSkills
コミュニティ

a2a-protocol

Builds Agent2Agent (A2A) servers and clients, the open protocol (originally from Google, now under the Linux Foundation) that lets AI agents from different frameworks call each other. Use when the user wants to create an A2A-compliant agent, build an Agent Card, implement task management, connect agents across frameworks, set up agent discovery, handle streaming responses, implement push notifications, or orchestrate multi-agent workflows. Trigger words: a2a, agent to agent, agent2agent, a2a pro

導入数
0
GitHub Stars
180
更新日
10月25日