К основному содержанию
правило

phoenix — правило для Cursor: стандарты Phoenix и Elixir

Правило для Cursor задаёт, как писать Phoenix-приложения на Elixir: контексты, форматирование, тесты. Пригодится, если агент уже правит ваш код.

Установить в Cursor · Вручную: положить файлы Все способы и платформы ↓
.cursor/rules/phoenix.mdc
Открыть на GitHub

Что делает phoenix

Файл .cursor/rules/phoenix.mdc подключается к Cursor и работает на всех файлах проекта. Внутри — набор требований к структуре Phoenix-приложения: вся работа с данными и бизнес-логика живёт в отдельном контекстном модуле, а контроллеры и LiveView остаются тонкими и занимаются только HTTP и WebSocket. Один модуль на файл, включая структуры.

Дальше идут правила оформления кода. Имена модулей в PascalCase, функции, переменные, файлы и каталоги в snake_case. Форматирование через mix format, в .formatter.exs советует выставить line_length: 100, отступ в два пробела, перевод строки в конце файла. Функции, которые могут бросить исключение, получают суффикс !. Ветвление предпочитают паттерн-матчингу и guard-выражениям вместо if/else в теле функции, а unless с else не используют вовсе.

Есть раздел про производительность и типичные ошибки. Запросы Ecto советует оптимизировать через Ecto.Query.preload/3, чтобы не ловить N+1, длительные задачи выносить в отдельные процессы через Task или GenServer. Отдельно предупреждает про размазанную по контроллерам бизнес-логику и про избыток метапрограммирования. Для аутентификации предлагает mix phx.gen.auth, для валидации ввода — Ecto Changesets. Правило рассчитано на проект, где Phoenix и Ecto уже стоят: без них большинство требований просто не к чему применить.

Какие задачи решает

  • удержать бизнес-логику в контекстах, а не в контроллерах
  • добиться единого форматирования Elixir-кода в команде
  • убрать N+1 в запросах Ecto
  • вынести долгие задачи из цикла обработки запроса
  • поднять аутентификацию на готовом генераторе

Примеры запросов агенту

  • добавь контекст для работы с заказами и оставь контроллер тонким
  • перепиши эту функцию через паттерн-матчинг и guard вместо if/else
  • проверь модуль на соответствие правилам phoenix

Как установить phoenix

Вручную: положить файлы

.cursor/rules/phoenix.mdc

Во frontmatter: description, globs, alwaysApply

По шагам, с командами для каждой платформы: Cursor rules: файлы .mdc, четыре режима и замена .cursorrules

Из того же набора69

pyramid Правило для Cursor с набором практик по Python-фреймворку Pyramid: структура проекта, конфигурация, транзакции и предикаты представлений. ★ 3 579звёзд репо pygame Правило для Cursor задаёт каркас Pygame-проекта: один UIManager, фиксированный шаг времени, вёрстка на якорях и темы в JSON. ★ 3 579звёзд репо prisma Правило для Cursor, которое задаёт агенту порядок работы с Prisma ORM: один клиент на приложение, выборки без лишних полей, аккуратные миграции. ★ 3 579звёзд репо playwright Правило задаёт Cursor стандарты написания end-to-end тестов на Playwright: официальный раннер, устойчивые локаторы, отказ от ручных задержек. ★ 3 579звёзд репо pandas Правило для Cursor задаёт стиль работы с pandas: векторизация вместо циклов, явные .loc и .iloc, экономные типы данных. Пригодится, если агент часто пишет медленный код. ★ 3 579звёзд репо notion-api Правило для Cursor задаёт, как писать код интеграций с Notion API: модули, токены в переменных окружения, официальные SDK и пагинация. ★ 3 579звёзд репо nginx Правило для Cursor задаёт, как писать конфиги NGINX: модульные include-файлы, современный TLS, кэш и лимиты. Пригодится, если агент правит nginx.conf в вашем проекте. ★ 3 579звёзд репо nestjs Правило для Cursor задаёт структуру NestJS-проекта: модули по фичам, явные типы, DTO с валидацией и глобальные фильтры ошибок. ★ 3 579звёзд репо

Похожие

elixir-expert VoltAgent Community Разбирает архитектуру Elixir-проекта и помогает менять код с опорой на OTP, Phoenix и Ecto. Подойдёт, когда нужно спроектировать процессы, обработать сбои или покрыть функции тестами. 167установок elixir Mindrally Скилл задаёт агенту стиль работы с Elixir и Phoenix: функциональные паттерны, Ecto, LiveView, OTP. Пригодится, когда проект на BEAM, а не на чём-то ещё. CX Codex +1 836установок backend-development Mindrally Задаёт агенту правила бэкенд-разработки на C++ и Elixir/Phoenix: именование, управление памятью, контексты Phoenix, обработка ошибок. CX Codex +1 753установок phoenix-api-channels Claude MPM Team Скилл для Claude, который ведёт агента по Phoenix: JSON-контроллеры, контексты Ecto, Channels и Presence на BEAM. CX Codex +1 339установок ecto-patterns Claude MPM Team Скилл подсказывает агенту, как писать схемы, changesets, миграции и транзакции в Ecto для Phoenix-приложений на Elixir. CX Codex +1 336установок phoenix-evals официальный [email protected] Скилл помогает создавать и запускать оценщики для AI- и LLM-приложений с Phoenix. Пригодится для разбора ошибок, проверки RAG и оценки качества моделей. 1 000установок

Рядом в теме «Бэкенд и API»

Всё по теме «Бэкенд и API» →

Вопросы о phoenix

Как установить phoenix в Cursor?

Создайте файл .cursor/rules/phoenix.mdc в корне проекта и вставьте в него содержимое правила.

Что делает правило phoenix?

Описывает стандарты разработки на Phoenix: контекстный дизайн, именование, форматирование через mix format, работу с Ecto и LiveView.

На какие файлы распространяется правило?

В нём указан glob **/*, то есть оно применяется ко всем файлам проекта.

Исходный текст

phoenix.mdc · CC0-1.0
---
description: Provides definitive guidelines for building robust, maintainable, and performant Phoenix applications using modern Elixir best practices, focusing on context-driven design, strict formatting, and effective testing.
globs: **/*
---
# phoenix Best Practices

This guide outlines the definitive best practices for developing Phoenix applications. Adhere to these rules to ensure consistency, maintainability, and high performance across our codebase.

## Code Organization and Structure

### 1. Context-Driven Design
Encapsulate all data access and business logic for a feature within a dedicated context module. Controllers and LiveViews must remain thin, focusing solely on HTTP/WebSocket handling.

**✅ GOOD:**
```elixir
# lib/my_app/accounts.ex
defmodule MyApp.Accounts do
  alias MyApp.Repo
  alias MyApp.Accounts.User

  def create_user(attrs) do
    %User{}
    |> User.changeset(attrs)
    |> Repo.insert()
  end

  def get_user!(id), do: Repo.get!(User, id)
end

# lib/my_app_web/controllers/user_controller.ex
defmodule MyAppWeb.UserController do
  use MyAppWeb, :controller
  alias MyApp.Accounts

  def create(conn, %{"user" => user_params}) do
    case Accounts.create_user(user_params) do
      {:ok, user} ->
        conn
        |> put_flash(:info, "User created successfully.")
        |> redirect(to: ~p"/users/#{user}")
      {:error, %Ecto.Changeset{} = changeset} ->
        conn
        |> put_flash(:error, "Failed to create user.")
        |> render("new.html", changeset: changeset)
    end
  end
end
```

### 2. Single Module Per Source File
Each `.ex` file must contain a single module. This includes structs.

**❌ BAD:**
```elixir
# lib/payment/worker.ex
defmodule Payment.Worker do
  defmodule WorkerConfiguration do
    defstruct [:retry_period_ms, :error_retry_limit]
  end
  # ...
end
```

**✅ GOOD:**
```elixir
# lib/payment/worker.ex
defmodule Payment.Worker do
  # ...
end

# lib/payment/worker_configuration.ex
defmodule Payment.WorkerConfiguration do
  defstruct [:retry_period_ms, :error_retry_limit]
end
```

### 3. Naming Conventions
Adhere strictly to Elixir's naming conventions.

*   **Modules:** `PascalCase`
*   **Functions, Variables, Files, Directories:** `snake_case`

**❌ BAD:**
```elixir
defmodule Payment.jobs.Inquiry_scheduler do # Module, file, dir naming
  defp calculateBalance(%Entry{bookType: "debit", amount: amount}, balance) do # Function, variable naming
    balance - amount
  end
end
```

**✅ GOOD:**
```elixir
# lib/payment/jobs/inquiry_scheduler.ex
defmodule Payment.Jobs.InquiryScheduler do
  defp calculate_balance(%Entry{book_type: "debit", amount: amount}, balance) do
    balance - amount
  end
end
```

### 4. Code Formatting
Always run `mix format`. Configure `.formatter.exs` with `line_length: 100`. Use 2-space soft tabs. End every file with a newline.

## Common Patterns and Anti-patterns

### 1. Bang Functions (`!`) for Raising Errors
Suffix functions that are expected to raise an exception (e.g., on invalid input or missing record) with `!`. This clearly signals to callers that the function might fail.

**✅ GOOD:**
```elixir
def create_user!(registration_uid, customer_params) do
  # ... logic that might raise ArgumentError or Ecto.NoResultsError
end

defp build_attrs!(%{
  "alien_id" => alien_id,
  "citizen_id" => citizen_id,
}) do
  %{citizen_id: citizen_id, alien_id: alien_id}
end

defp build_attrs!(_params) do
  raise ArgumentError, message: "Unexpected customer profile params"
end
```

### 2. Prefer Pattern Matching and Guards
Use pattern matching and guards in function definitions over `if/else` statements within the function body for clearer, more declarative logic.

**❌ BAD:**
```elixir
defp handle_response({:ok, %Response{status: status, body: body}}) do
  if status in 200..299 do
    {:ok, body}
  else
    {:error, :internal_server_error, reason}
  end
end
```

**✅ GOOD:**
```elixir
defp handle_response({:ok, %Response{status: status, body: body}})
     when status in 200..299,
     do: {:ok, body}

defp handle_response({:error, reason}),
     do: {:error, :internal_server_error, reason}
```

### 3. Pipe Operator Usage
Avoid using the pipe operator for a single function call. Start a pipeline with the raw value. Use `Kernel.then/2` for anonymous functions in a pipeline.

**❌ BAD:**
```elixir
some_string |> String.downcase()
value |> (&(&1 * 10)).()
```

**✅ GOOD:**
```elixir
String.downcase(some_string)
some_string |> String.trim() |> String.downcase() # Raw value start
value |> then(&(&1 * 10)) # then/2 for anonymous functions
```

### 4. Alias Grouping
Use shorthand syntax to group aliases from the same submodule.

**❌ BAD:**
```elixir
alias Payment.Ledger.Balance
alias Payment.Ledger.EntriesGenerator
```

**✅ GOOD:**
```elixir
alias Payment.Ledger.{Balance, EntriesGenerator}
```

### 5. Conditional Statements
Use `do:` for single-line `if/unless`. Never use `unless` with `else`; rewrite with `if`.

**❌ BAD:**
```elixir
if some_condition do "some_stuff" end
unless success do IO.puts("failure") else IO.puts("success") end
```

**✅ GOOD:**
```elixir
if some_condition, do: "some_stuff"
if success, do: IO.puts("success"), else: IO.puts("failure")
```

### 6. Ignored Variables
Use `_variable_name` for ignored variables to improve readability.

**❌ BAD:**
```elixir
def index(conn, _), do: Plug.Conn.resp(conn, :ok, "success")
```

**✅ GOOD:**
```elixir
def index(conn, _params), do: Plug.Conn.resp(conn, :ok, "success")
```

### 7. Private Functions
Place all private functions at the bottom of the module.

## Performance Considerations

Phoenix leverages the Erlang VM for high concurrency and fault tolerance.
*   **Ecto Queries**: Optimize database queries. Use `Ecto.Query.preload/3` to avoid N+1 issues.
*   **LiveView**: Embrace LiveView for interactive UIs to minimize client-side JavaScript and network roundtrips.
*   **Concurrency**: Design long-running tasks to run in separate processes (e.g., using `Task` or `GenServer`) to avoid blocking the request cycle.

## Common Pitfalls and Gotchas

### 1. Scattering Business Logic
The most common pitfall is scattering business logic across controllers, LiveViews, and schemas. Always centralize it in contexts.

### 2. Over-reliance on Metaprogramming
While powerful, excessive `use` or `import` can obscure where functions originate. Rely on documentation and `h/1` in IEx to understand module APIs.

### 3. Deployment Complexity
Elixir Releases with Docker simplify deployment, but still require careful configuration of environment variables and build steps (e.g., `npm install` for assets). Use `mix release` and the generated `Dockerfile`.

## Security Best Practices

### 1. Authentication
Use `mix phx.gen.auth` for a robust, production-ready authentication system.

### 2. Input Validation
Always validate user input using Ecto Changesets. Never trust client-side data.

**✅ GOOD:**
```elixir
def changeset(user, attrs) do
  user
  |> cast(attrs, [:email, :password, :password_confirmation])
  |> validate_required([:email, :password])
  |> validate_length(:password, min: 8)
  |> unique_constraint(:email)
  |> put_assoc(:profile, profile_changeset(user.profile, attrs))
end
```

### 3. XSS and CSRF Protection
Phoenix provides built-in protection against XSS (via HTML escaping in HEEx templates) and CSRF (via `Plug.CSRFProtection`). Ensure these are active.

## Error Handling

### 1. Return Tuples for Expected Outcomes
For operations that can succeed or fail, consistently return `{:ok, result}` or `{:error, reason}` tuples.

**✅ GOOD:**
```elixir
def update_user(user, attrs) do
  user
  |> User.changeset(attrs)
  |> Repo.update()
end
```

### 2. Centralized Logging and Error Tracking
Integrate with a centralized logging solution (e.g., Graylog via `GelfLogger`) and an error tracking service (e.g., Sentry) for production monitoring.

## API Design

### 1. Contexts as API Boundaries
Design your contexts to expose a clear, stable API for interacting with your application's data and business logic.

### 2. RESTful JSON APIs
Use `mix phx.gen.json` to scaffold RESTful JSON APIs. Ensure consistent response formats and HTTP status codes.

## Testing Approaches

### 1. Comprehensive ExUnit Testing
Write tests for all layers: contexts, controllers, LiveViews, and channels. Run tests with `mix test`.

### 2. Leverage Generated Test Cases
Phoenix generators (`mix phx.gen.live`, `mix phx.gen.html`, etc.) provide excellent examples for testing each component.

### 3. `ConnCase` and `DataCase`
Use `HelloWeb.ConnCase` for controller/LiveView tests (provides `conn` and web helpers) and `Hello.DataCase` for context/Ecto tests (manages database sandbox).

**✅ GOOD:**
```elixir
# test/my_app_web/controllers/user_controller_test.exs
defmodule MyAppWeb.UserControllerTest do
  use MyAppWeb.ConnCase, async: true
  alias MyApp.Accounts

  setup [:create_user]

  defp create_user(_) do
    {:ok, user} = Accounts.create_user(%{email: "[email protected]", password: "password"})
    %{user: user}
  end

  test "shows existing user", %{conn: conn, user: user} do
    conn = get(conn, ~p"/users/#{user}")
    assert html_response(conn, 200) =~ user.email
  end
end
```

### 4. LiveView Testing Helpers
For LiveView tests, use `render_hook/2`, `render_component/2`, `form/2`, `assert_patch/2`, etc. When debugging HTML output, use specific element selectors rather than broad string matching.

**✅ GOOD (using custom helpers for clarity):**
```elixir
# test/support/element_helpers.ex
defmodule MyApp.Support.ElementHelpers do
  import Phoenix.LiveViewTest

  def get_by_role(view, :button, text_filter) do
    button = view |> element("button", text_filter)
    assert has_element?(button)
    button
  end

  def get_by_role(view, :textbox, text_filter) do
    label = view |> element("label", text_filter)
    textbox_id = Regex.run(~r/(?<=for=")\w+(?=")/, label |> render) |> Enum.at(0)
    input = view |> element("input##{textbox_id}")
    textbox = if has_element?(input), do: input, else: view |> element("textarea##{textbox_id}")
    assert has_element?(label)
    assert has_element?(textbox)
    textbox
  end
end

# test/my_app_web/live/user_live_test.exs
defmodule MyAppWeb.UserLiveTest do
  use MyAppWeb.ConnCase, async: true
  import Phoenix.LiveViewTest
  import MyApp.Support.ElementHelpers # Import your custom helpers

  test "creates a new user", %{conn: conn} do
    {:ok, lv, _html} = live(conn, ~p"/users/new")

    lv
    |> get_by_role(:textbox, "Email")
    |> render_change(value: "[email protected]")

    lv
    |> get_by_role(:textbox, "Password")
    |> render_change(value: "securepassword")

    lv
    |> get_by_role(:button, "Create User")
    |> render_click()

    assert_patch(lv, ~p"/users")
    assert has_element?(lv.html, "User created successfully.")
  end
end
```

© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc

Смотрите также

Спонсоры проекта

Компании, которые помогают держать аналитику открытой