2017-01-31 4 views
1

У меня есть приложение phoenix, которое использует timestamps во всех моделях. Однако я хочу, чтобы расширить временные метки, так что всякий раз, когда я включаю timestamps в модели он добавляет следующие поля:Elixir Phoenix - Добавить столбцы в timestamp

created_at  (utc datetime) 
updated_at  (utc datetime) 
created_at_utc (utc timestamp of created_at field) 
updated_at_utc (utc timestamp of updated_at field) 

Как я могу продлить функцию временных меток (или создать новый), который автоматически генерирует эти поля?

UPDATE Как было предложено в ответах я попытался расширить Ecto.Schema и Ecto.Migrations модули в моем помощнике db_helpers.ex, как показано ниже:

defmodule Extension do 
    defmacro extends(module) do 
    module = Macro.expand(module, __CALLER__) 
    functions = module.__info__(:functions) 

    signatures = Enum.map functions, fn { name, arity } -> 
     args = Enum.map 0..arity, fn(i) -> 
     { String.to_atom(<< ?x, ?A + i >>), [], nil } 
     end 

     { name, [], tl(args) } 
    end 

    quote do 
     defdelegate unquote(signatures), to: unquote(module) 
     defoverridable unquote(functions) 
    end 
    end 
end 


defmodule Ecto.Schema do 
    import Extension 

    extends Ecto.Schema 

    @doc """ 
     Generates `:inserted_at` and `:updated_at` timestamp fields. 
     The fields generated by this macro will automatically be set to 
     the current time when inserting and updating values in a repository. 
     ## Options 
     * `:type` - the timestamps type, defaults to `:naive_datetime`. 
     * `:type_utc` - the utc timestamps type, defaults to `:utc_datetime`. 
     * `:usec` - sets whether microseconds are used in timestamps. 
      Microseconds will be 0 if false. Defaults to true. 
     * `:created_at` - the name of the column for insertion times or `false` 
     * `:updated_at` - the name of the column for update times or `false` 
     * `:created_at_utc` - the name of the column for utc insertion times or `false` 
     * `:updated_at_utc` - the name of the column for utc update times or `false` 
     * `:autogenerate` - a module-function-args tuple used for generating 
      both `inserted_at` and `updated_at` timestamps 
     All options can be pre-configured by setting `@timestamps_opts`. 
    """ 
    defmacro timestamps(opts \\ []) do 
    quote bind_quoted: binding() do 
     timestamps = 
     [created_at: :created_at, updated_at: :updated_at, 
     created_at_utc: :created_at_utc, inserted_at_utc: :inserted_at_utc, 
     type: :naive_datetime, type_utc: :utc_datetime, 
     usec: true] 
     |> Keyword.merge(@timestamps_opts) 
     |> Keyword.merge(opts) 

     type  = Keyword.fetch!(timestamps, :type) 
     type_utc  = Keyword.fetch!(timestamps, :type_utc) 
     precision = if Keyword.fetch!(timestamps, :usec), do: :microseconds, else: :seconds 
     autogen = timestamps[:autogenerate] || {Ecto.Schema, :__timestamps__, [type, precision]} 

     if created_at = Keyword.fetch!(timestamps, :created_at) do 
     Ecto.Schema.field(created_at, type, []) 
     Module.put_attribute(__MODULE__, :ecto_autogenerate, {created_at, autogen}) 
     end 

     if updated_at = Keyword.fetch!(timestamps, :updated_at) do 
     Ecto.Schema.field(updated_at, type, []) 
     Module.put_attribute(__MODULE__, :ecto_autogenerate, {updated_at, autogen}) 
     Module.put_attribute(__MODULE__, :ecto_autoupdate, {updated_at, autogen}) 
     end 

     if created_at_utc = Keyword.fetch!(timestamps, :created_at_utc) do 
     Ecto.Schema.field(created_at_utc, type, []) 
     Module.put_attribute(__MODULE__, :ecto_autogenerate, {created_at_utc, autogen}) 
     end 

     if updated_at_utc = Keyword.fetch!(timestamps, :updated_at_utc) do 
     Ecto.Schema.field(updated_at_utc, type, []) 
     Module.put_attribute(__MODULE__, :ecto_autogenerate, {updated_at_utc, autogen}) 
     Module.put_attribute(__MODULE__, :ecto_autoupdate, {updated_at_utc, autogen}) 
     end 
    end 
    end 
end 

defmodule Ecto.Migration do 
    import Extension 

    extends Ecto.Migration 

    @doc """ 
     Adds `:created_at` and `:updated_at` timestamps columns. 
     Those columns are of `:naive_datetime` type, and by default 
     cannot be null. `opts` can be given to customize the generated 
     fields. 
     ## Options 
     * `:created_at` - the name of the column for insertion times, providing `false` disables column 
     * `:updated_at` - the name of the column for update times, providing `false` disables column 
     * `:created_at_utc` - the name of the column for utc insertion times, providing `false` disables column 
     * `:updated_at_utc` - the name of the column for utc update times, providing `false` disables column 
     * `:type` - column type, defaults to `:naive_datetime` 
     * `:type_utc` - column type for utc, defaults to `:utc_datetime` 
    """ 
    def timestamps(opts \\ []) do 
    opts = Keyword.put_new(opts, :null, false) 

    {type, opts} = Keyword.pop(opts, :type, :naive_datetime) 
    {type_utc, opts} = Keyword.pop(opts, :type_utc, :utc_datetime) 
    {created_at, opts} = Keyword.pop(opts, :created_at, :created_at) 
    {updated_at, opts} = Keyword.pop(opts, :updated_at, :updated_at) 
    {created_at_utc, opts} = Keyword.pop(opts, :created_at_utc, :created_at_utc) 
    {updated_at_utc, opts} = Keyword.pop(opts, :updated_at_utc, :updated_at_utc) 

    if created_at != false, do: add(created_at, type, opts) 
    if updated_at != false, do: add(updated_at, type, opts) 
    if created_at_utc != false, do: add(created_at_utc, type_utc, opts) 
    if updated_at_utc != false, do: add(updated_at_utc, type_utc, opts) 
    end 
end 

Однако, когда я пытаюсь запустить mix ecto.migrate Я получаю следующее сообщение об ошибке:

== Compilation error on file web/models/accounts.ex == 
** (CompileError) web/models/accounts.ex:4: undefined function schema/2 
    (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6 
    (elixir) lib/kernel/parallel_compiler.ex:117: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1 

в случае, если не extends макрос сделать так, чтобы все методы доступны. Обратите внимание, что модуль Extension приходит отсюда: https://gist.github.com/pavlos/c86858290743b4d4fed9

ответ

0

У вас есть два варианта:

. Рассмотрим , распространяющийсяtimestamps от Ecto.Schema и функция timestamps Функция от Ecto.Migration в вашем помощнике, которую вы будете импортировать в свои модели и миграции. Попробуйте реализовать свои столбцы так же, как они реализованы в исходном коде.

Этот подход может:

  • занимает больше времени, из-за земляных работ и изучения исходного кода
  • быть более полезным из-за лучшего знания исходного кода
  • быть сложнее реализовать и гарантировать, что вы выиграли 't делать ошибки

. Используйте new Функция или макрос из помощников вместо расширения функциональности timestamps. Он:

  • занимает меньше времени и усилий
  • легче реализовать

Помните, что вы хотите, чтобы эти поля не только в моделях, но в базе данных, что означает, вам придется добавить их в свои миграции или подготовить сценарий, который будет добавлять их на основе текущих значений из базы данных.

+0

Я пробовал расширять схему и миграцию, как было предложено, но теперь я получаю некоторые другие проблемы, упомянутые в обновлении. Есть предположения? – user2694306

+0

Оставьте идею расширения :) Используйте 2. подход, вам будет быстрее и безопаснее, чтобы вы ничего не сломали. – PatNowak

 Смежные вопросы

  • Нет связанных вопросов^_^