简体   繁体   English

带有状态代理的 Elixir 宏

[英]Elixir macro with Agent for state

So I am trying to get my head around how migrations work in Ecto.所以我试图了解迁移在 Ecto 中是如何工作的。 By this I dont mean how to use them but what is actually happening in the code.我的意思不是如何使用它们,而是在代码中实际发生了什么。 What I am seeing is that when various macros are run then state is been pumped into an Agent.我看到的是,当运行各种宏时,状态会被泵入代理。

When the mix task is run to migrate the data then the Agents data is retrieved and the appropriate SQL commands are ran.当运行混合任务以迁移数据时,将检索代理数据并运行适当的 SQL 命令。

This blows my mind, I dont understand how an Agent process is alive during compilation.这让我大吃一惊,我不明白在编译期间代理进程是如何活动的。 Ok, so I kind of understand that during macro expansion then any code outside of quoted blocks can be ran, including spawning processes but why is it still alive after compilation/expansion and during actual execution?好的,所以我有点理解在宏扩展期间,可以运行引用块之外的任何代码,包括生成过程,但为什么在编译/扩展后和实际执行期间它仍然存在?

Chris克里斯

I dont understand how an Agent process is alive during compilation.我不明白在编译期间代理进程是如何活动的。

It isn't.不是。 The macros turn the DSL into code that calls functions in Ecto.Migration and Ecto.Migration.Runner .宏将 DSL 转换为调用Ecto.MigrationEcto.Migration.Runner函数的代码。 Here's an example.这是一个例子。 The following migration:以下迁移:

defmodule M.Repo.Migrations.CreatePosts do
  use Ecto.Migration

  def change do
    create(:posts) do
      add :title, :string
      add :content, :text
    end
  end
end

compiles into this Erlang:编译成这个 Erlang:

-module('Elixir.M.Repo.Migrations.CreatePosts').

...

change() ->
    _@1 = #{'__struct__' := 'Elixir.Ecto.Migration.Table'} =
          posts,
    'Elixir.Ecto.Migration.Runner':start_command({create,
                          'Elixir.Ecto.Migration':'__prefix__'(_@1)}),
    case case _@1 of
       #{primary_key := _@2} -> _@2;
       _@2 when erlang:is_map(_@2) ->
           erlang:error({badkey, primary_key, _@2});
       _@2 -> _@2:primary_key()
     end
    of
      _@3 when (_@3 =:= nil) or (_@3 =:= false) ->
      _@4 = nil, nil;
      _ ->
      _@4 =
          'Elixir.Ecto.Migration.Runner':repo_config(migration_primary_key,
                             []),
      'Elixir.Ecto.Migration':add(case
                    'Elixir.Access':get(_@4, name, nil)
                      of
                    _@5
                        when (_@5 =:= nil) or
                           (_@5 =:= false) ->
                        id;
                    _@6 -> _@6
                      end,
                      case 'Elixir.Access':get(_@4, type, nil)
                      of
                    _@7
                        when (_@7 =:= nil) or
                           (_@7 =:= false) ->
                        bigserial;
                    _@8 -> _@8
                      end,
                      [{primary_key, true}])
    end,
    'Elixir.Ecto.Migration':add(title, string),
    'Elixir.Ecto.Migration':add(content, text),
    'Elixir.Ecto.Migration.Runner':end_command(),
    _@1.

Even if you don't read Erlang, you can figure out that it's calling Ecto.Migration.Runner.start_command , and then calls functions that add data to the agent, and then finally calls .end_command .就算不看Erlang,也能看出来是调用了Ecto.Migration.Runner.start_command ,然后调用了给agent添加数据的函数,最后调用了.end_command All this happens at runtime when the migration is executed, not compile time.所有这些都发生在执行迁移时的运行时,而不是编译时。 At compile time, only the DSL is expanded to this code.在编译时,仅将 DSL 扩展为此代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM