Der Anwendungsbereich von ecr in crystal: oder wie übergebe ich Variablen und Objekte in ECR-Vorlagen?

Als Anfänger in der Crystal-Sprache fällt es mir immer noch schwer, einige der darin enthaltenen Konzepte zu verstehen und ein Gefühl für die Programmierung in Crystal zu entwickeln.

Wenn ich auf schwierige Probleme stoße, die ich löse oder zu verstehen beginne, blogge ich darüber, damit andere davon profitieren können - denn der Mangel an Dokumentation wird (von mir) manchmal stark empfunden.

Vorwärts!

Hier ist die Dokumentation für ECR (in v.0.27, der derzeit letzten Version von Crystal):

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

Sie können sich den Quellcode von ECR, den eingebetteten Kristallvorlagen, hier ansehen:

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

ECR ist eine Templatesprache zur Kompilierzeit. Sie können ECR nicht verwenden, um Vorlagen zur Laufzeit zu verarbeiten!

Makros, die die Welt regieren

Wenn Sie die ECR-Vorlagen in Crystal verwenden (auch wenn Sie sie mit Hilfe von Kemal verwenden), verwenden Sie Makros.

Crystal verfügt über eine Makrosprache, mit der Sie Code abstrahieren und wiederverwenden können. Im ersten Schritt der Kompilierung werden die Makros ausgewertet und der resultierende Code wird an der Stelle "eingefügt", an der der Makroaufruf stand.

Anschließend kompiliert Crystal den Code.

Wie funktioniert das bei ECR?

Nehmen wir zum Beispiel die def_to_s(Dateiname) Makro im Code des Beispiels:

erfordern "ecr"

class Begrüßung
  def initialize(@name : String)
  end

  ECR.def_to_s "greeting.ecr"
end

# gruss.ecr
Gruß, !

Gruß.new("John").to_s #=> Gruß, John!

Crystal ruft das Makro def_to_s zur Kompilierzeit mit der Übergabe von "greeting.ecr".
Das Makro wird hier definiert:
https://github.com/crystal-lang/crystal/blob/c9d1eef8fde5c7a03a029d64c8483ed7b4f2fe86/src/ecr/macros.cr

Makro def_to_s(Dateiname)

  def to_s(__io__)

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

  Ende

Ende

wird Ihre Klasse im ersten Schritt wie folgt umgeschrieben:

erfordern "ecr"

class Begrüßung
  def initialize(@name : String)
  end

  def to_s(__io__)
    ECR.embed "gruss.ecr", "__io__"
end

Siehst du, was gerade passiert ist? Deiner Klasse wurde eine to_s-Methode hinzugefügt, die ihrerseits ein Makro enthält. Schauen wir uns an, was dieses Makro tut:

Makro embed(Dateiname, io_name)

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

Ende

Dies ist der Hauptaufruf von ECR. Er kompiliert (? / führt aus) eine andere Anwendung ecr/Prozess und übergibt ihm Dateiname und io_name als Parameter.

Die Rückgabe ist das Ergebnis der Ausgabe dieser Anwendung.

Was bedeutet der Backslash?

"Es ist möglich, eine Makro, das eine oder mehrere Makrodefinitionen erzeugt. Sie müssen Makroausdrücke des inneren Makros mit einem vorangestellten Backslash-Zeichen \ entschlüsseln, um zu verhindern, dass sie von dem äußeren Makro ausgewertet werden."

Es handelt sich im Wesentlichen um ein verschachteltes Makro!

ecr/process ist hier definiert:

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

es ist im Wesentlichen ein Wrapper um ECR.process_file (denken Sie daran, dass dies kein Makro mehr ist - dies ist eine Anwendung, deren Ausgabe schließlich in Ihren Crystal-Code eingefügt wird!)

ecr/processor ist hier definiert:

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

Es verarbeitet die Datei und erstellt eine Zeichenkette, die es zurückgibt.

Hier ein kleiner Auszug:

str << Puffer_name

str << " << "

string.inspect(str)

str << '\n'

buffer_name ist das, was wir oben an Crystal übergeben haben (__io__ - identifiziert durch seine ID in io_name.id.stringify).

Ausgabe- und Kontrollwerte werden ebenfalls verarbeitet. Außerdem wird die Debug-Ausgabe für Sie eingefügt (mit # als Kommentar):

append_loc(str, dateiname, zeilen_nummer, spalten_nummer)

Im Grunde wird der Code, den Sie in die ECR-Dateien eingeben, direkt in Ihren Code eingefügt - an der von Ihnen angegebenen Stelle. Das alles geschieht durch Makros und einen speziellen ECR-Parser.

Der ECR-Code kennt Ihre Variablen nicht - wenn Sie Bereichsfehler, undefinierte Variablen usw. erhalten, liegt das nicht daran, dass Sie sie nicht an ECR übergeben haben, sondern daran, dass sie sich nicht im richtigen Bereich befinden.

Versuchen Sie das, was Sie mit dem ECR-Code machen wollten, direkt in Ihrem Hauptcode.

Demonstration

Hier ist eine Demonstration, wie Sie eine Klasse in Ihrem ECR-Code in Kemal "verwenden" können. Die Klasse verschachtelt ein zusätzliches ECR-Snippet, das mit dem Kontext der Klasse gerendert wird.

die Datei debug.cr:

erfordern "kemal"

require "./../macros.cr"

Modul Fehlersuche
   Makros einbeziehen

      Klasse DClass
           @test : Zeichenkette
           def initialize()
               @test = "Testzeichenfolge"
           Ende
           ECR.def_to_s "src/views/snippets/debug_snippet.ecr"
       Ende

  get "/debug" do |env|

    eingeloggt = false

    mrender "debug"
   Ende

Ende

die Datei debug.ecr:

<% content_for “main” do%>

Debug-Informationen

<%= loggedin %>

Eingebauter dekorierter Schnipsel

<%= DClass.new() %>

<% end %>

den entsprechenden Code für das Makro mrender, definiert in macros.cr:

makro mrender(filename)
   render "src/views/#{{Dateiname}}}.ecr", "src/views/layout.ecr"
Ende   

Es verwendet das Makro render von Kemal, mit dem Sie ein Layout für Ihre Ansicht angeben können. (Sie brauchen dies nicht unbedingt in Ihrem Code, es wird hier nur der Vollständigkeit halber angegeben)

die Datei src/views/snippets/debug_snippet.ecr:

Debug-Schnipsel

Das Ergebnis:

Bild

Alles zusammengenommen:

  1. Der Benutzer ruft /debug in seinem Webbrowser auf
  2. Kemal 's Makro get wird für die Route "/debug" passen. Es ist in meiner debug.cr definiert
    1. Die lokale Variable eingeloggt wird auf false gesetzt
    2. debug.ecr wird von den ECR-Makros verarbeitet und in "debug.cr" eingefügt (die AST-Darstellung) eingefügt, so als ob sie direkt von Ihnen eingegeben worden wäre
      1. die lokale Variable eingeloggt wird als false ausgewertet (zur Laufzeit)
      2. wir nennen DClass.new()und fragen Sie sie nach ihrer String-Repräsentation, die durch die Methode def to_s definiert ist.
        1. können wir DClass.new() aufrufen, da sie im selben Modul definiert ist, in dem wir gerade ausgeführt werden. Stellen Sie sich einfach vor, dass Ihr ECR-Code genau dort, unterhalb der Klassendefinition, eingefügt wird.
        2. wir fragen nach der Darstellung der Zeichenkette, da wir die Syntax verwenden

Schauen wir uns nun an, was beim Aufruf von DClass.new passiert:

      Klasse DClass
          @test : Zeichenkette
           def initialize()
               @test = "Testzeichenfolge"
           Ende
           ECR.def_to_s "src/views/snippets/debug_snippet.ecr"
       Ende

Bei der Initialisierung wird eine Zeichenkette @test gesetzt. Dies ist eine Instanz der DClass-Instanz, die wir gerade erstellt haben. (Dass es sich um eine Instanzvariable handelt, erkennen Sie daran, dass ihr ein "@" vorangestellt ist. Zwei "@@" würden eine Klassenvariable darstellen)

Diese Instanzvariable wird in debug_snippet.ecr verwendet / angezeigt

Wir haben bereits besprochen, wie ECR.def_to_s funktioniert.

Nach dem Durchlaufen der Makrophase würde diese Klasse etwa so aussehen:

      Klasse DClass
          @test : Zeichenkette
           def initialize()
               @test = "Testzeichenfolge"
           Ende
           def to_s(__io__)

                __io__ << "Debug-Snippet\n"

                __io__ << ""

                __io__ << @test

                __io__ << ""

           Ende
       Ende

Mit dieser Technik können Sie Klassen definieren, um Schnipsel von ECR-Code zu rendern, anstatt jeden Variablennamen manuell einzurichten und zu übergeben.

Ich hoffe, dass dieser Artikel Ihnen hilft und dass Sie ihn finden, anstatt frustriert zu werden - ich wünschte, ich hätte so etwas als Leitfaden für meine ersten Schritte gehabt Lächeln

Referenzen

Bezug auf Kemal:

Auf dieser Seite finden Sie weitere Informationen zu Makros:

NB: AST-Knoten: Knoten des abstrakten Syntaxbaums

Das hat mich auf den richtigen Weg gebracht, Hut ab!!