2017-01-02 118 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