在elixir中,如何为来自外部源(第三方,stdlib等)的模块添加功能?

时间:2016-09-11 04:50:53

标签: elixir

我是elixir的新手并且构建了一个命令行应用程序,以熟悉mix和elixir而没有太多的依赖。

当我构建命令行应用程序时,我倾向于使用ansi颜色代码来使输出更具可读性。

IO.ANSI似乎只支持8种颜色,可能会与brightunderline等内容结合使用。无法获得16种颜色中的其他8种颜色-color pallette。

当前来源包含以下代码:

colors = [:black, :red, :green, :yellow, :blue, :magenta, :cyan, :white]

for {color, code} <- Enum.with_index(colors) do
  @doc "Sets foreground color to #{color}."
  defsequence color, code + 30

  @doc "Sets background color to #{color}."
  defsequence :"#{color}_background", code + 40
end

看来,实现我之后所做的一项微不足道的修改就是将以下几行添加到现有函数中:

  @doc "Sets foreground color to light #{color}."
  defsequence :"light_#{color}", code + 90

  @doc "Sets background color to light #{color}."
  defsequence :"light_#{color}_background", code + 100

如何在elixir中实现这样的事情(不需要代码)?或者是不可能的?

1 个答案:

答案 0 :(得分:2)

您无法修改现有模块,这是设计使然。我不希望我团队中的任何人改变我已经知道的东西。

但应该可以创建满足您需求的新模块。 defsequence是一个可以从其他模块导入的宏,因此您可以编写:

defmodule IO.ExtendedANSI do
  import IO.ANSI.Sequence

  colors = [:black, :red, :green, :yellow, :blue, :magenta, :cyan, :white]

  for {color, code} <- Enum.with_index(colors) do
    @doc "Sets foreground color to light #{color}."
    defsequence :"light_#{color}", code + 90

    @doc "Sets background color to light #{color}."
    defsequence :"light_#{color}_background", code + 100
  end
end

但是现在,您必须记住哪个函数来自原始ANSI,哪个函数来自ExtendedANSI。您可以为ANSI中存在的所有函数创建委托:

defmodule IO.ExtendedANSI do
  import IO.ANSI.Sequence

  IO.ANSI.__info__(:functions)
  |> Enum.each(fn {name, arity} ->
    args = Enum.map(:lists.seq(1, arity), fn(i) -> {:"arg#{i}", [], Elixir} end)
    defdelegate unquote({name, [], args}), to: IO.ANSI
  end)

  colors = [...]
    ...
end

现在,您可以致电IO.ExtendedANSI.blue()IO.ExtendedANSI.light_blue()

虽然解决方案并不完美:

  • 我们需要重新定义colors,因此如果原始版本发生变化,我们也需要更改代码。我不确定是否有办法获得它,因为它是内部实现细节
  • 使用def format(chardata, emit? \\ enabled?)等参数的函数会在帮助中正确显示See IO.ANSI.format/2,但它们会有签名def fromat(arg1, arg2)。应该有办法以某种方式做到这一点。