晶体内的ecr范围:或我如何在ECR模板中传递变量和对象?

作为一个水晶语言的初学者,我仍然在努力理解其中的一些概念,并培养对水晶语言编码的感觉。

当我遇到困难的问题,我解决了或开始理解了,我就把它们写到博客上,这样别人就能受益--因为有时会严重感受到缺乏文件的问题(由我来)。

继续前进!

这里是ECR的文档(在v.0.27,目前Crystal的最新版本)。

https://crystal-lang.org/api/0.27.0/ECR.html

你可以在这里看一下ECR的源代码,即嵌入式晶体模板。

https://github.com/crystal-lang/crystal/tree/c9d1eef8fde5c7a03a029d64c8483ed7b4f2fe86/src/ecr

ECR是一种编译时的模板语言。你不能用ECR在运行时处理模板!

统治世界的宏程序

当你在Crystal中使用ECR模板时(如果你通过使用Kemal的方式来使用它们),你是在使用宏。

Crystal有一种宏语言,允许你抽象和重用代码;作为编译的第一步,宏被评估,结果代码被 "粘贴 "到宏调用过的地方。

然后Crystal继续编译代码。

这对ECR来说是怎样的?

例如,我们以 def_to_s(文件名) 例子中的代码中的宏。

要求 "ecr"

类别 问候语
  def initialize(@name : String)
  结束

  ECR.def_to_s "greeting.ecr" 
结束

# greeting.ecr
问候,!

问候语.new("John").to_s #=> 问候语, John!

Crystal将调用宏 辩护律师 在编译时,"greeting.ecr "被传递到它里面。
该宏在此定义。
https://github.com/crystal-lang/crystal/blob/c9d1eef8fde5c7a03a029d64c8483ed7b4f2fe86/src/ecr/macros.cr

宏def_to_s(文件名)

  def to_s(__io__).

     ECR.embed {{filename}}, "__io__"

  结束

结束

你的类在第一步将被重写成这样。

要求 "ecr"

类别 问候语
  def initialize(@name : String)
  结束

  def to_s(__io__)
    ECR.embed "greeting.ecr", "__io__"
结束

你看到刚才发生了什么吗?一个to_s方法被添加到你的类中,它本身包含一个宏。让我们来看看这个宏的作用:

宏嵌入(文件名,io_name)。

    \{{run("ecr/process", {{filename}, {{io_name.id.stringify}) }}。

结束

这是ECR的核心调用。它所做的,是编译(/执行)一个不同的应用程序 ecr/process 并将文件名和io_name作为参数传给它。

返回是该应用程序的输出结果。

反斜杠是什么意思?

"有可能定义一个 生成一个或多个宏定义的宏.你必须在内层宏的表达式前面加上反斜杠字符(backslash character)来转义,以防止它们被外层宏评估。"

它本质上是一个嵌套的宏!

这里定义了ecr/process。

https://github.com/crystal-lang/crystal/blob/c9d1eef8fde5c7a03a029d64c8483ed7b4f2fe86/src/ecr/process.cr

它基本上是ECR.process_file的一个包装器(记住,这不再是一个宏了--这是一个应用程序,其输出最终将被粘贴到你的Crystal代码中!)。

ecr/processor在此定义。

https://github.com/crystal-lang/crystal/blob/c9d1eef8fde5c7a03a029d64c8483ed7b4f2fe86/src/ecr/processor.cr

它对文件进行处理,并创建一个字符串,将其反馈给对方。

这里有一小段摘录。

str << buffer_name

str << " << "

string.inspection(str)

str << '\n'

buffer_name 是我们上面传给 Crystal 的东西 (__io__ - 由 io_name.id.stringify 中的 id 识别)。

输出和控制值也被处理。此外,调试输出也会为你粘贴进去(用#作为注释)。

append_loc(str, filename, line_number, column_number)

基本上,你放在ECR文件中的代码被直接粘贴到你的代码中--在你指定的地方。这都是通过宏和运行一个特殊的ECR分析器来完成的。

ECR代码并不知道你的变量--如果你得到范围故障、未定义变量故障等,这不是由于你没有 "传入ECR",而是由于没有在正确的范围内。

试试你在主代码中直接用ECR代码做的事情。

示范

这里展示了如何在Kemal的ECR代码中 "使用 "一个类。该类嵌套了一个额外的ECR片段,以便在类的背景下进行渲染。

文件debug.cr。

要求 "kemal"

要求"././macros.cr"

调试模块
   包括宏程序

      类DClass
           @测试:字符串
           def initialize()
               @test = "测试字符串"
           结束
           ECR.def_to_s "src/views/snippets/debug_snippet.ecr"
       结束

  get "/debug" do |env|

    loggedin = false

    mrender "debug"
   结束

结束

文件debug.ecr。


调试信息


纳入装饰的片段

结束 %

宏的相关代码mrender,定义在macros.cr中。

宏指令mrender(文件名)
   渲染 "src/views/#{{filename}}.ecr","src/views/layout.ecr"
结束   

它使用Kemal的宏渲染,允许你为你的视图指定一个布局。(你不一定需要在你的代码中这样做,这里只是为了完整地给出它)

文件 src/views/snippets/debug_snippet.ecr。

调试片段

产出。

形象

把这一切放在一起。

  1. 用户在他们的网页浏览器中调用/debug
  2. Kemal's macro get将与路由"/debug "匹配。它被定义在我的debug.cr中
    1. 本地变量 登录 将被设置为假
    2. debug.ecr将被ECR宏处理,并被粘贴到 "debug.cr"(AST表示法)中。(AST表示法),就像你直接输入的一样
      1. 本地变量 登录 将被评估为假(在运行时)。
      2. 我们称之为 DClass.new(),并要求它提供它的字符串表示法--这是由def to_s方法定义的。
        1. 我们可以调用DClass.new(),因为它是在同一个模块中定义的,就像我们正在执行的一样。再一次,简单地认为你的ECR代码就粘贴在那里,在类定义的下面。
        2. 我们要求它的字符串表示,因为我们使用语法。

好吧,让我们看看在DClass.new调用中发生了什么。

      类DClass
          @测试:字符串
           def initialize()
               @test = "测试字符串"
           结束
           ECR.def_to_s "src/views/snippets/debug_snippet.ecr"
       结束

在初始化时,一个字符串@test被设置。这是一个 实例 我们刚刚创建的DClass实例的变量。(你可以看到它是一个实例变量,因为它前面有一个"@"。两个"@@"则是一个类变量)

这个实例变量在debug_snippet.ecr中使用/显示。

我们之前讨论过ECR.def_to_s的工作方式。

有效地,在通过宏阶段后,这个类看起来会是这样的。

      类DClass
          @测试:字符串
           def initialize()
               @test = "测试字符串"
           结束
           def to_s(__io__).

                __io__ << "Debug snippet\n"

                __io__ << ""

                __io__ << @test

                __io__ << ""

           结束
       结束

使用这种技术,你可以定义类来渲染ECR代码的片段,而不是手动设置和传递每一个变量名。

我希望这篇文章能帮助你,希望你能找到它,而不是越来越沮丧--我希望我有这样的东西来指导我开始工作 笑一笑

参考文献

凯末尔参考。

关于宏的更多信息,请参考本页面。

NB:AST节点:抽象语法树节点

这给我指出了正确的方向,赞一个!。