2017-01-02 156 views
1

(代码引用已被匿名)药剂+凤凰:__module__报价

内未定义在我的凤凰模型,我有一些方法,这是多余的,像这样基本的一个:

def build(params) do 
    changeset(%__MODULE__{}, params) 
    end 

由于我把它们放在我的模型模块中,它们工作正常,但我想避免代码重复,并且希望通过这样的帮助模块将它们提供给我的所有模型:

defmodule MyApp.Helpers.Model do 
    defmodule Changeset do 
    defmacro __using__(_opts) do 
     quote do 
     def build(params) do 
      changeset(%__MODULE__{}, params) 
     end 
     end 
    end 
    end 
end 

这样做,我得到一个错误:

== Compilation error on file lib/my_app/model/my_model.ex == 
** (CompileError) lib/my_app/model/my_model.ex:3: MyApp.Model.MyModel.__struct__/1 is undefined, cannot expand struct MyApp.Model.MyModel 
    (stdlib) lists.erl:1354: :lists.mapfoldl/3 

的相关模型基本上是这样的:

defmodule MyApp.Model.MyModel do 
    use MyApp.Helpers, :model 
    use MyApp.Helpers.Model.Changeset # here for comprehension, should be in MyApp.Helpers quoted :model method 

    schema "my_table" do 
    field :name, :string 

    timestamps() 
    end 

    @required_fields ~w(name)a 
    @optional_fields ~w() 
    @derive {Poison.Encoder, only: [:name]} 

    def changeset(model, params \\ %{}) do 
    model 
    |> cast(params, @required_fields) 
    |> cast(params, @optional_fields) 
    |> validate_required(@required_fields) 
    |> validate_format(:name, ~r/^[a-z]{3,}$/) 
    |> unique_constraint(:name) 
    end 
end 

我想那是因为模块尚未在编译时间内定义宏,但我不知道,也没有如何解决这个问题,并使其工作。

这里的一些灯将不胜感激,谢谢。

+0

你能也张贴你'在use'ing此模块的模块的代码? – Dogbert

+1

你或许应该换'使用MyApp.Helpers。在MyModel模块中模型和定义MyModel行是后者(结构声明)先行。 – mudasobwa

+0

好了,在@mudasobwa建议之后,我在'模式'调用之后放置了'使用MyApp.Helpers.Model.Changeset',它在'MyModel'中生成结构并且它可以工作。所以我想我们无法在凤凰通常的MyApp.Helpers,model中全局地使用这个'use',因为我们首先需要模式定义? – Sylver

回答

3

问题是通过调用defstruct宏来定义结构,并且不能在以前使用,因为编译器不知道如何扩展它。在ecto模式的情况下,该结构由下面的schema宏声明。

幸运的是,看的defstruct的文档,我们可以看到它创建了一个名为__struct__/0在结构声明的模块功能。而且函数甚至可以在定义之前调用其他本地函数!利用这些知识,我们可以改变你的宏:

defmodule MyApp.Helpers.Model do 
    defmodule Changeset do 
    defmacro __using__(_opts) do 
     quote do 
     def build(params) do 
      changeset(__struct__(), params) 
     end 
     end 
    end 
    end 
end 

也就是说,定义在__using__功能通常被认为是一种不好的做法,作为Kernel.use/1

最后的文件中指出,开发商也应避免除非这些函数是先前定义的@callback的默认实现,或者是功能 意味着被覆盖(见defoverridable/1即使在这些情况下,定义函数应该被看作是“最后的资源”。在__using__

定义函数有许多不足之处,包括:慢编译(编译函数的一遍又一遍的每个模块中它的注入),难以调试(“这是哪里来的?”)和可降解性差。

更好的方法可能会定义一个单一的,可重复使用的功能。例如:

defmodule MyApp.SchemaUtils do 
    def build(schema, params) do 
    schema.changeset(struct(schema), params) 
    end 
end 

PS。 @derive调用必须在struct之前声明。

+0

感谢您的详细解答! – Sylver