# module ECR

##
Overview

Embedded Crystal (ECR) is a template language for embedding Crystal code into other text, that includes but is not limited to HTML. The template is read and transformed at compile time and then embedded into the binary.

There are `<%= %>` and `<% %>` syntax. The former will render returned values. The latter will not, but instead serve to control the structure as we do in Crystal.

Using a dash inside `<...>` either eliminates previous indentation or removes the next newline:

- `<%- ... %>`: removes previous indentation
- `<% ... -%>`: removes next newline
- `<%-= ... %>`: removes previous indentation
- `<%= ... -%>`: removes next newline

A comment can be created the same as normal code: `<% # hello %>` or by the special tag: `<%# hello %>`. An ECR tag can be inserted directly (i.e. the tag itself may be escaped) by using a second `%` like so: `<%% a = b %>` or `<%%= foo %>`. Dashes may also be present in those escaped tags and have no effect on the surrounding text.

NOTE To use `ECR`, you must explicitly import it with `require "ecr"`

Quick Example:

Create a simple ECR file named `greeter.ecr`:

    Greetings, <%= @name %>!

and then use it like so with the `#def_to_s` macro:

    require "ecr"

    class Greeter
      def initialize(@name : String)
      end

      ECR.def_to_s "greeter.ecr"
    end

    Greeter.new("John").to_s # => "Greetings, John!\n"

Using logical statements:

    # greeter.ecr
    <%- if @name -%>
    Greetings, <%= @name %>!
    <%- else -%>
    Greetings!
    <%- end -%>

    require "ecr"

    class Greeter
      def initialize(@name : String | Nil)
      end

      ECR.def_to_s "greeter.ecr"
    end

    Greeter.new(nil).to_s    # => "Greetings!\n"
    Greeter.new("Jill").to_s # => "Greetings, Jill!\n"

Using loops:

    # greeter.ecr
    <%- @names.each do |name| -%>
    Hi, <%= name %>!
    <%- end -%>

    require "ecr"

    class Greeter
      @names : Array(String)

      def initialize(*names)
        @names = names.to_a
      end

      ECR.def_to_s "greeter.ecr"
    end

    Greeter.new("John", "Zoe", "Ben").to_s # => "Hi, John!\nHi, Zoe!\nHi, Ben!\n"

Comments and Escapes:

    # demo.ecr
    <%# Demonstrate use of ECR tag -%>
    A valid ECR tag looks like this: <%%= foo %>

    require "ecr"
    foo = 2
    ECR.render("demo.ecr") # => "A valid ECR tag looks like this: <%= foo %>\n"

Likewise, other Crystal logic can be implemented in ECR text.

##
Defined in:

ecr.cr
ecr/macros.cr

##
Macro Summary

- def_to_s(filename)

  Defines a `to_s(io)` method whose body is the ECR contained in filename, translated to Crystal code.

- embed(filename, io_name)

  Embeds an ECR file filename into the program and appends the content to an IO in the variable io_name.

- render(filename)

  Embeds an ECR file filename into the program and renders it to a string.

##
Macro Detail

macro def_to_s(filename) #

Defines a `to_s(io)` method whose body is the ECR contained in filename, translated to Crystal code.

    # greeting.ecr
    Hello <%= @name %>!

    require "ecr/macros"

    class Greeting
      def initialize(@name : String)
      end

      ECR.def_to_s "greeting.ecr"
    end

    Greeting.new("World").to_s # => "Hello World!"

The macro basically translates the text inside the given file to Crystal code that appends to the IO:

    class Greeting
      def to_s(io)
        io << "Hello "
        io << @name
        io << '!'
      end
    end

macro embed(filename, io_name) #

Embeds an ECR file filename into the program and appends the content to an IO in the variable io_name.

The generated code is the result of translating the contents of the ECR file to Crystal, a program that appends to an IO.

    # greeting.ecr
    Hello <%= name %>!

    require "ecr/macros"

    name = "World"

    io = IO::Memory.new
    ECR.embed "greeting.ecr", io
    io.to_s # => "Hello World!"

The `ECR.embed` line basically generates this Crystal code:

    io << "Hello "
    io << name
    io << '!'

macro render(filename) #

Embeds an ECR file filename into the program and renders it to a string.

The generated code is the result of translating the contents of the ECR file to Crystal, a program that appends to an IO and returns a string.

    # greeting.ecr
    Hello <%= name %>!

    require "ecr/macros"

    name = "World"

    rendered = ECR.render "greeting.ecr"
    rendered # => "Hello World!"

The `ECR.render` basically generates this Crystal code:

    String.build do |io|
      io << "Hello "
      io << name
      io << '!'
    end