You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

编译时如何将外部.ex文件内容合并到当前Elixir文件中?

Elixir编译时动态加载外部.ex文件并合并代码

核心思路

利用Elixir的编译时宏读取外部文件,解析其代码AST,提取函数定义并注入到当前模块中,实现编译阶段的代码合并,而非运行时加载。

步骤1:实现编译时宏

创建一个工具模块,定义处理外部文件注入的宏:

defmodule MyApp.CompilerHelpers do
  defmacro inject_external_file_if_exists(file_path) do
    # 获取当前调用模块的文件路径,拼接外部文件的完整路径
    caller_file = __CALLER__.file
    base_dir = Path.dirname(caller_file)
    full_path = Path.join(base_dir, file_path)

    if File.exists?(full_path) do
      # 读取文件内容并解析为AST
      {:ok, content} = File.read(full_path)
      {ast, _} = Code.string_to_quoted(content)

      # 提取外部文件中的函数定义(def/defp)
      functions =
        case ast do
          # 如果外部文件包含完整模块定义,提取模块内的函数
          {:defmodule, _, [_module, [do: {:__block__, _, body}]]} ->
            Enum.filter(body, fn
              {:def, _, _} -> true
              {:defp, _, _} -> true
              _ -> false
            end)
          # 如果外部文件直接写函数块,提取其中的函数
          {:__block__, _, body} ->
            Enum.filter(body, fn
              {:def, _, _} -> true
              {:defp, _, _} -> true
              _ -> false
            end)
          # 单个函数的情况
          {:def, _, _} = func -> [func]
          {:defp, _, _} = func -> [func]
          _ -> []
        end

      # 将提取的函数注入当前模块
      {:__block__, [], functions}
    else
      # 文件不存在时返回空代码块,不影响编译
      {:__block__, [], []}
    end
  end
end

步骤2:在目标模块中使用宏

修改你的控制器文件,导入宏并调用:

defmodule MyAppWeb.MyController do
  use MyAppWeb, :controller
  import MyApp.CompilerHelpers

  def page1(conn, params) do
    # 原有业务逻辑
  end

  # 编译时注入外部文件的函数
  inject_external_file_if_exists("my_controller.personal.ex")
end

步骤3:编写外部专属代码

外部文件可以选择两种格式:

  1. 直接写函数(推荐,更简洁):
# my_controller.personal.ex
def specific_page1(conn, params) do
  # 子项目专属逻辑
end

def specific_page2(conn, params) do
  # 子项目专属逻辑
end
  1. 保留完整模块定义(兼容你原有的写法):
# my_controller.personal.ex
defmodule MyAppWeb.MyController do
  use MyAppWeb, :controller

  def specific_page1(conn, params) do
    # 子项目专属逻辑
  end

  def specific_page2(conn, params) do
    # 子项目专属逻辑
  end
end

关键说明

  • 编译时合并:宏在编译阶段执行,最终生成的模块会包含原有的函数和外部文件的函数,不会出现覆盖问题。
  • 路径兼容性:通过__CALLER__.file获取当前模块的文件路径,确保相对路径始终正确,不受模块所在目录影响。
  • 安全过滤:只提取def/defp函数定义,避免外部文件中的模块属性、use语句等干扰当前模块的编译。
  • Git忽略:将*.personal.ex加入项目的.gitignore文件,确保子项目专属代码不会被提交到模板仓库。

内容的提问来源于stack exchange,提问作者Duminamonri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 00:07:42