Elixir Dev

Polar Sponsor
爱发电 赞助
.NET 9.0

Elixir/Phoenix 开发助手。运行并解读 mix test、credo、dialyzer、format。生成符合 OTP 规范的模块(Contexts、Schemas、GenServers 等)。调试编译错误,协助 Ecto 迁移与查询。适用于编写模块、修复测试、重构代码及理解 OTP 模式。

Elixir Dev. 运行混合命令

功能概述

Elixir Dev. 运行混合命令是一项面向实际任务的技能,主要用于请参看参考文献/ mix- commands. md 以获取完整命令的参考文献;Test. 解释失败 : 1. (MatchError) - 模式匹配失败;

核心要点

  • . 请检查返回值形状;
  • . (Ecto.;
  • NoResults)。

使用与执行

它将相关步骤、工具调用和结果整理方式集中到统一流程中,帮助使用者更快完成目标并减少重复操作。使用时应结合输入条件选择合适的执行方式,核对必要参数、依赖环境与输出内容,并按原始要求处理异常情况。从功能定位来看,该技能强调把分散的操作要求整理成清晰、可复用的处理流程,使用户能够围绕既定目标快速准备输入、选择执行方式并获得结构化结果。实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;

结果检查与注意事项

若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;涉及批量任务时,还应保存进度,避免中断后重复操作。该技能适合用于一次性任务,也可以接入自动化工作流,与其他技能或上层代理配合完成更完整的业务链路;在组合使用时,应明确每一步的输入输出关系,并避免不同步骤之间出现参数冲突。

Elixir 开发

运行 Mix 命令

完整命令参考请见 references/mix-commands.md。

测试

# 运行全部测试
mix test

# 指定文件或行号
mix test test/my_app/accounts_test.exs:42

# 按标签筛选
mix test --only integration

# 仅重跑失败用例(需在前一次运行中启用 --failed 标志)
mix test --failed

# 启用覆盖率统计
mix test --cover

解读测试失败信息:

  • ** (MatchError) —— 模式匹配失败;检查返回值结构是否符合预期。
  • ** (Ecto.NoResultsError) —— 对不存在的 ID 调用了 Repo.get!;改用 Repo.get 或补充种子数据(seed data)。
  • ** (DBConnection.OwnershipError) —— 缺少 async: true 配置或未正确设置 sandbox。
  • no function clause matching —— 函数参数元数(arity)错误,或传入了意外类型的参数。

Credo

mix credo --strict
mix credo suggest --format json
mix credo explain MyApp.Module  # 解释指定模块中的问题

常见 Credo 修复方式:

  • Credo.Check.Readability.ModuleDoc —— 添加 @moduledoc 注释。
  • Credo.Check.Refactor.CyclomaticComplexity —— 提取辅助函数以降低圈复杂度。
  • Credo.Check.Design.TagTODO —— 处理或移除 TODO 注释。

Dialyzer

mix dialyzer
mix dialyzer --format short

常见 Dialyzer 警告含义:

  • The pattern can never match —— 不可达代码(dead code),或模式中类型不匹配。
  • Function has no local return —— 所有执行路径均会崩溃;检查内部调用逻辑。
  • The call will never return —— 调用了总会抛出异常的函数。
  • 修复建议:添加 @spec 类型签名;万不得已时可使用 @dialyzer {:nowarn_function, func: arity} 抑制警告。

代码格式化

mix format
mix format --check-formatted  # CI 模式 —— 若存在未格式化代码则退出码为 1

模块生成规范

所有公开函数必须包含 @moduledoc、@doc 和 @spec。

Context 模块

defmodule MyApp.Notifications do
  @moduledoc """
  管理通知投递与用户偏好设置。
  """
  import Ecto.Query
  alias MyApp.Repo
  alias MyApp.Notifications.Notification

  @doc "按时间倒序列出指定用户的全部通知。"
  @spec list_notifications(String.t(), keyword()) :: [Notification.t()]
  def list_notifications(user_id, opts \\ []) do
    limit = Keyword.get(opts, :limit, 50)

    Notification
    |> where(user_id: ^user_id)
    |> order_by(desc: :inserted_at)
    |> limit(^limit)
    |> Repo.all()
  end
end

Schema 模块

defmodule MyApp.Notifications.Notification do
  @moduledoc """
  推送/邮件/SMS 通知的数据结构定义。
  """
  use Ecto.Schema
  import Ecto.Changeset

  @type t :: %__MODULE__{}

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id
  @timestamps_opts [type: :utc_datetime_usec]

  schema "notifications" do
    field :channel, Ecto.Enum, values: [:push, :email, :sms]
    field :title, :string
    field :body, :string
    field :delivered_at, :utc_datetime_usec
    field :user_id, :binary_id

    timestamps()
  end

  @required ~w(channel title body user_id)a

  @doc false
  def changeset(notification, attrs) do
    notification
    |> cast(attrs, @required ++ [:delivered_at])
    |> validate_required(@required)
    |> validate_length(:title, max: 255)
  end
end

OTP 模式

GenServer、Supervisor、Agent、Task 等典型 OTP 模式的详细说明请参阅 references/otp-patterns.md。

何时选用何种模式

模式 适用场景
GenServer 需要维护状态并支持同步/异步调用的进程(如缓存、限流器、连接池)
Agent 仅需简单封装状态,无复杂业务逻辑
Task 一次性异步任务,可选择“即发即弃”或等待结果
Task.Supervisor 受监督的“即发即弃”任务
Supervisor 管理子进程生命周期(启动、重启策略等)
Registry 基于名称或键进行进程查找
DynamicSupervisor 运行时动态启动子进程

GenServer 模板

defmodule MyApp.RateLimiter do
  @moduledoc "令牌桶限流器。"
  use GenServer

  # 客户端 API
  def start_link(opts) do
    name = Keyword.get(opts, :name, __MODULE__)
    GenServer.start_link(__MODULE__, opts, name: name)
  end

  @spec check_rate(String.t()) :: :ok | {:error, :rate_limited}
  def check_rate(key), do: GenServer.call(__MODULE__, {:check, key})

  # 服务端回调
  @impl true
  def init(opts) do
    {:ok, %{limit: Keyword.get(opts, :limit, 100), window_ms: 60_000, buckets: %{}}}
  end

  @impl true
  def handle_call({:check, key}, _from, state) do
    now = System.monotonic_time(:millisecond)
    {count, state} = increment(state, key, now)
    if count <= state.limit, do: {:reply, :ok, state}, else: {:reply, {:error, :rate_limited}, state}
  end

  defp increment(state, key, now) do
    # 实现细节
  end
end

常见编译错误

错误信息 原因 修复方法
module X is not available 依赖缺失或模块名拼写错误 检查 mix.exs 中的 deps 配置,并核对模块名
undefined function X/N 未导入(import)或未别名引用(alias)该函数所在模块 添加 import、alias,或使用完整模块路径调用
(CompileError) redefining module 模块名重复定义 重命名其中一个模块
protocol not implemented 缺少对应协议(protocol)的实现 为你的 struct 添加 defimpl
cannot use ^x outside of match Pin 操作符(^)使用位置错误 将其移至模式匹配上下文中(如 case、函数头、receive 等)

Ecto 查询模式

动态过滤条件

def list(filters) do
  Enum.reduce(filters, base_query(), fn
    {:status, val}, q -> where(q, [r], r.status == ^val)
    {:since, dt}, q -> where(q, [r], r.inserted_at >= ^dt)
    {:search, term}, q -> where(q, [r], ilike(r.name, ^"%#{term}%"))
    _, q -> q
  end)
  |> Repo.all()
end

预加载(Preloading)

# 查询时预加载(单次查询 + JOIN)
from(p in Post, join: a in assoc(p, :author), preload: [author: a])

# 分离式预加载(两次查询)
Post |> Repo.all() |> Repo.preload(:author)

# 嵌套预加载
Repo.preload(posts, [comments: :author])

聚合查询(Aggregates)

from(o in Order,
  where: o.tenant_id == ^tenant_id,
  group_by: o.status,
  select: {o.status, count(o.id), sum(o.amount)}
)
|> Repo.all()

Phoenix LiveView 基础

挂载(Mount)与事件处理(Handle Events)

defmodule MyAppWeb.DashboardLive do
  use MyAppWeb, :live_view

  @impl true
  def mount(_params, _session, socket) do
    {:ok, assign(socket, items: [], loading: true)}
  end

  @impl true
  def handle_event("delete", %{"id" => id}, socket) do
    MyApp.Items.delete_item!(id)
    {:noreply, assign(socket, items: MyApp.Items.list_items())}
  end

  @impl true
  def render(assigns) do
    ~H"""
    
<%= item.name %>
""" end end

通过 PubSub 实现实时通信

# 在 mount 中订阅
def mount(_, _, socket) do
  if connected?(socket), do: Phoenix.PubSub.subscribe(MyApp.PubSub, "items")
  {:ok, assign(socket, items: list_items())}
end

# 在 Context 中广播事件
def create_item(attrs) do
  with {:ok, item} <- %Item{} |> Item.changeset(attrs) |> Repo.insert() do
    Phoenix.PubSub.broadcast(MyApp.PubSub, "items", {:item_created, item})
    {:ok, item}
  end
end

# 在 LiveView 中处理消息
def handle_info({:item_created, item}, socket) do
  {:noreply, update(socket, :items, &[item | &1])}
end

相关专题

更多
Vite 项目如何打包和部署
Vite 项目如何打包和部署

Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

2026.10.28

0

16

Vite 如何搭配 Vue.js
Vite 如何搭配 Vue.js

Vite搭配Vue.js专题汇集创建Vue 3项目、配置开发环境、路径别名、Vue插件、代理、多环境构建与调试的已审核文章,帮助读者完成从初始化到日常开发的配置。

2026.10.28

0

15

Vite 项目目录怎么看
Vite 项目目录怎么看

Vite项目目录专题汇集入口文件、src源码、public静态资源、路径别名、项目根目录和构建输出目录相关的已审核文章,帮助读者理解各文件夹的用途与引用方式。

2026.10.28

0

10

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

2026.10.22

0

11

Gradle Wrapper配置教程
Gradle Wrapper配置教程

本专题介绍Gradle Wrapper的基本作用,涵盖文件组成、生成方法、版本配置和任务调用,帮助你理解项目如何使用指定的Gradle版本,减少不同开发环境带来的构建差异。

2026.10.22

0

10

Golangci-lint检查器使用教程
Golangci-lint检查器使用教程

本专题整理Golangci-lint常用Linters的用途与配置方法,覆盖错误处理、无效代码、安全、复杂度、命名和依赖检查,帮助项目合理选规则。

2026.10.22

0

15

Gradle怎么构建Java项目
Gradle怎么构建Java项目

本专题介绍Gradle Java项目构建方法,涵盖Java插件配置、依赖声明、源码编译、测试执行和打包检查,帮助你理解各阶段的任务关系,逐步完成项目构建。

2026.10.22

0

10

PHP时间戳漏洞修复教程
PHP时间戳漏洞修复教程

本专题汇总PHP常见时间戳漏洞,包括2038年溢出、时区漂移、时间戳伪造、重放攻击、时间盲注与日志审计失效等,并给出升级64位环境、使用 DateTimeImmutable、统一UTC、严格超时校验、预处理绑定、hash_equals 比对等修复方案,帮助开发者快速排查和加固时间相关代码。

2026.10.22

0

25

Golangci-lint配置文件与规则设置
Golangci-lint配置文件与规则设置

本专题聚焦Golangci-lint YAML配置文件,讲清运行参数、规则启停、目录排除、问题过滤及严重级别设置,便于团队统一检查标准。

2026.10.22

0

15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.7万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 134.1万人学习