NAME

ember - eRuby template processor

SYNOPSIS

ember [OPTIONS] [FILE]

Command

Evaluates eRuby directives (see [SYNTAX] below) in the given FILE and writes the result to the standard output stream. If FILE is not given, then the standard input stream is read and evaluated instead.

Options

-s, --shorthand

Treat lines beginning with zero or more whitespace followed by the "%" character as eRuby directives.

-i, --infer_end

Add missing "<% end %>" directives based on indentation.

-u, --unindent

Unindent the bodies of directives that define a Ruby block (do … end) or scope (begin … end).

-c, --compile

Print underlying Ruby program compiled from the input eRuby template and exit.

-h, --help

Display this manual and exit.

-v, --version

Print version number and exit.

DESCRIPTION

Ember (EMBEdded Ruby) is an [eRuby] template processor that allows debugging, reduces markup, and improves composability of eRuby templates.

Features

  • Reports correct line numbers in error message stack traces.

  • Omits newlines trailing code-only <% ... %> directives.

  • Can infer missing <% end %> directives based on indentation.

  • Can unindent eRuby block bodies hierarchically.

  • Implemented in 361 lines of pure Ruby.

Resources

Project website

http://snk.tuxfamily.org/lib/ember/

Announcements feed

http://snk.tuxfamily.org/lib/ember/ann.xml

API documentation

http://snk.tuxfamily.org/lib/ember/api/

Source code (browse online, download, or checkout)

http://github.com/sunaku/ember

Issue tracker (report bugs, request features, get help)

http://github.com/sunaku/ember/issues

INSTALL

Prerequisites

Installing

Installing as a [Ruby] library:

gem install ember

Installing as a [Rails] plugin:

script/plugin install git://github.com/sunaku/ember.git

Upgrading

gem update ember

Removing

gem uninstall ember

SYNTAX

This section explains [eRuby] template syntax and Ember extensions thereof.

eRuby templates are plain-text documents that contain special processing instructions known as directives. These instructions are evaluated in place, meaning that they are replaced by the result of their evaluation.

Directives

Directives are expressed in either standard or shorthand notation:

Notation Directive Head Operation Body Tail

Standard

<%xy%>

<%

x

y

%>

Shorthand

%xy

%

x

y

In standard notation, the directive is composed of a head, an operation, a body, and a tail; and it may appear anywhere in the template.

In shorthand notation, the directive is composed of a head, an operation, and a body; and it may only appear in the template if it occupies an entire line (leading whitespace is permitted only in Ember; trailing whitespace is permitted in both Ember and eRuby).

Regardless of the notation used, directives are atomic constructs; they cannot be nested within one another.

Operations

An operation is the first character following the head of a directive. It specifies how the directive will be processed.

Ember supports the following operations, and here is what they do:

%

One "%" character is omitted from the head of the directive and the entire directive is inserted into the output.

#

The entire directive is omitted from the output.

=

The body of the directive is evaluated as Ruby code, and the result of this evaluation is inserted into the output.

~

(only in Ember) The body of the directive is evaluated as an eRuby template, and the result of this evaluation is inserted into the output.

+

(only in Ember) The body of the directive is evaluated as Ruby code, and the result of this evaluation is assumed to be a string that specifies the path (either absolute or relative to the eRuby template file in which this directive is found) to a file containing an eRuby template. This file is read and its contents are evaluated as an eRuby template, and the result of this evaluation is inserted into the output.

<

(only in Ember) The body of the directive is evaluated as Ruby code, and the result of this evaluation is assumed to be a string that specifies the path (either absolute or relative to the eRuby template file in which this directive is found) to a file. This file is read and its contents are inserted into the output.

|

(only in Ember) The body of the directive is treated as the beginning of a Ruby block. The do keyword is automatically appended to the body of the directive if missing.

None of the above

The body of the directive is evaluated as Ruby code, but the result of this evaluation is not inserted into the output.

USAGE

Begin by loading the Ember library into your program:

require 'rubygems' # might not be necessary; see HACKING
require 'ember'

Instantiate an Ember template processor:

source   = "your eRuby template here"
options  = {} # see API documentation
template = Ember::Template.new(source, options)

Inspect the Ruby program that was compiled (and is used) by the Ember template processor to evaluate the eRuby template given as input:

puts template.program

View the result of evaluating the eRuby template:

puts template.render

See the API documentation for more information.

An empty template

The above template compiles into the following Ruby program:

(e08afdfb_62c7_485f_87a0_80914e1b4703 = :_erbout; _erbout = []; ; _erbout.join)

Which then produces the following output when rendered:


Comment directives

<%# this is a comment %>
%# this is also a comment
<%# this
is
        a
multi-line comment %>

With {:shorthand=>true} options, the above template compiles into the following Ruby program:

(e08afdfb_62c7_485f_87a0_80914e1b4703 = :_erbout; _erbout = []; _erbout << "\n"
_erbout << "\n"




; _erbout.join)

Which then produces the following output when rendered:


Escaped directives

<%% this is an escaped directive %>
%% this is an escaped directive

With {:shorthand=>true} options, the above template compiles into the following Ruby program:

(e08afdfb_62c7_485f_87a0_80914e1b4703 = :_erbout; _erbout = []; _erbout << "<% this is an escaped directive %>\n"
_erbout << "% this is an escaped directive\n"
; _erbout.join)

Which then produces the following output when rendered:

<% this is an escaped directive %>
% this is an escaped directive

Vocal directives

<%= "hello" %>
%= "world"

With {:shorthand=>true} options, the above template compiles into the following Ruby program:

(e08afdfb_62c7_485f_87a0_80914e1b4703 = :_erbout; _erbout = []; _erbout << ("hello") << "\n"
_erbout << ("world") << "\n"
; _erbout.join)

Which then produces the following output when rendered:

hello
world

Silent directives

<% a = "hello" %>
% b = "world"

<%= a %>
%= b

With {:shorthand=>true} options, the above template compiles into the following Ruby program:

(e08afdfb_62c7_485f_87a0_80914e1b4703 = :_erbout; _erbout = [];  a = "hello"
 b = "world"
_erbout << "\n"
_erbout << (a) << "\n"
_erbout << (b) << "\n"
; _erbout.join)

Which then produces the following output when rendered:

hello
world

Block directives

% words = %w[hello world]

<% words.each do |w| %>
  <%= w %>
<% end %>

% words.each do |w|
  %= w
% end

%|words.each |w|
  %= w
% end

With {:shorthand=>true} options, the above template compiles into the following Ruby program:

(e08afdfb_62c7_485f_87a0_80914e1b4703 = :_erbout; _erbout = [];  words = %w[hello world]
_erbout << "\n"
 words.each do |w|
_erbout << "  " << (w) << "\n"
 end
_erbout << "\n"
 words.each do |w|
_erbout << "  " << (w) << "\n"
 end
_erbout << "\n"
words.each do |w|
_erbout << "  " << (w) << "\n"
 end
; _erbout.join)

Which then produces the following output when rendered:

  hello
  world

  hello
  world

  hello
  world

Unindent block content

<% [1].each do |i| %>
  <%= i %>
    % [2].each do |j|
      %= j
              %|[3].each |k|
                   %= k
              % end
    % end
<% end %>

With {:shorthand=>true, :unindent=>true} options, the above template compiles into the following Ruby program:

(e08afdfb_62c7_485f_87a0_80914e1b4703 = :_erbout; _erbout = [];  [1].each do |i|
_erbout << (i) << "\n"
 [2].each do |j|
_erbout << (j) << "\n"
[3].each do |k|
_erbout << (k) << "\n"
 end
 end
 end
; _erbout.join)

Which then produces the following output when rendered:

1
2
3

Wrap block content

<%
  def introducing(subject, &block)
    Ember::Template.wrap_content_block(block, rand(10)) do |content|
      "And now I would like to introduce #{subject}:\n\n#{content.join}"
    end
  end

  def coin_toss(pronoun, &block)
    Ember::Template.wrap_content_block(block) do |content|
      "#{pronoun} favorite side of a coin toss is #{content.join}."
    end
  end
 %>

% introducing "Matz" do |number|
  Father of the Ruby programming language,
  and also a jolly and well mannered fellow.

  % coin_toss("His") { number % 2 == 0 ? "heads" : "tails" }
% end

With {:shorthand=>true, :unindent=>true} options, the above template compiles into the following Ruby program:

(e08afdfb_62c7_485f_87a0_80914e1b4703 = :_erbout; _erbout = [];
  def introducing(subject, &block)
    Ember::Template.wrap_content_block(block, rand(10)) do |content|
      "And now I would like to introduce #{subject}:\n\n#{content.join}"
    end
  end

  def coin_toss(pronoun, &block)
    Ember::Template.wrap_content_block(block) do |content|
      "#{pronoun} favorite side of a coin toss is #{content.join}."
    end
  end

_erbout << "\n"
 introducing "Matz" do |number|
_erbout << "Father of the Ruby programming language,\n"
_erbout << "and also a jolly and well mannered fellow.\n"
_erbout << "\n"
 coin_toss("His") { number % 2 == 0 ? "heads" : "tails" }
 end
; _erbout.join)

Which then produces the following output when rendered:

And now I would like to introduce Matz:

Father of the Ruby programming language,
and also a jolly and well mannered fellow.

His favorite side of a coin toss is heads.

Capture block content

<%
  def introducing(subject, &block)
    content = Ember::Template.content_from_block(block, rand(2))

    buffer = Ember::Template.buffer_from_block(block)
    buffer << "introducing(#{subject.inspect}):\n\n#{content.join}"
  end

  def coin_toss(pronoun, &block)
    content = Ember::Template.content_from_block(block)

    buffer = Ember::Template.buffer_from_block(block)
    buffer << "coin_toss(#{pronoun.inspect}): #{content.join}"
  end
 %>

% introducing "Matz" do |number|
  Father of the Ruby programming language,
  and also a jolly and well mannered fellow.

  % coin_toss("His") { number % 2 == 0 ? "heads" : "tails" }
% end

With {:shorthand=>true, :unindent=>true} options, the above template compiles into the following Ruby program:

(e08afdfb_62c7_485f_87a0_80914e1b4703 = :_erbout; _erbout = [];
  def introducing(subject, &block)
    content = Ember::Template.content_from_block(block, rand(2))

    buffer = Ember::Template.buffer_from_block(block)
    buffer << "introducing(#{subject.inspect}):\n\n#{content.join}"
  end

  def coin_toss(pronoun, &block)
    content = Ember::Template.content_from_block(block)

    buffer = Ember::Template.buffer_from_block(block)
    buffer << "coin_toss(#{pronoun.inspect}): #{content.join}"
  end

_erbout << "\n"
 introducing "Matz" do |number|
_erbout << "Father of the Ruby programming language,\n"
_erbout << "and also a jolly and well mannered fellow.\n"
_erbout << "\n"
 coin_toss("His") { number % 2 == 0 ? "heads" : "tails" }
 end
; _erbout.join)

Which then produces the following output when rendered:

introducing("Matz"):

Father of the Ruby programming language,
and also a jolly and well mannered fellow.

coin_toss("His"): heads

Template evaluation result buffer

<%
  def introducing(subject, &block)
    buffer = Ember::Template.buffer_from_block(block)
    #
    # you can do whatever you want with buffer,
    # now that you have a reference to it! >:-)
    #
    buffer << "introducing(#{subject.inspect})"
  end
 %>

% introducing "Matz" do |number|
  Father of the Ruby programming language,
  and also a jolly and well mannered fellow.
% end

With {:shorthand=>true, :unindent=>true} options, the above template compiles into the following Ruby program:

(e08afdfb_62c7_485f_87a0_80914e1b4703 = :_erbout; _erbout = [];
  def introducing(subject, &block)
    buffer = Ember::Template.buffer_from_block(block)
    #
    # you can do whatever you want with buffer,
    # now that you have a reference to it! >:-)
    #
    buffer << "introducing(#{subject.inspect})"
  end

_erbout << "\n"
 introducing "Matz" do |number|
_erbout << "Father of the Ruby programming language,\n"
_erbout << "and also a jolly and well mannered fellow.\n"
 end
; _erbout.join)

Which then produces the following output when rendered:

introducing("Matz")

Infer block endings

Omit <% end %> directives from the template:

% words = %w[hello world]

<% words.each do |w| %>
  <%= w %>

% words.each do |w|
  %= w

%|words.each |w|
  %= w

With {:shorthand=>true, :infer_end=>true} options, the above template compiles into the following Ruby program:

(e08afdfb_62c7_485f_87a0_80914e1b4703 = :_erbout; _erbout = [];  words = %w[hello world]
_erbout << "\n"
 words.each do |w|
_erbout << "  " << (w) << "\n"
end; _erbout << "\n"
 words.each do |w|
_erbout << "  " << (w) << "\n"
end; _erbout << "\n"
words.each do |w|
_erbout << "  " << (w) << "\n"
end; _erbout.join)

Which then produces the following output when rendered:

  hello
  world

  hello
  world

  hello
  world

Raw file inclusion

When doc/example.txt contains:

This is a plain-text file.  Notice that <%=
"eRuby directives" %> have no effect here!

And the eRuby template is:

<%< "doc/example.txt" %>

%< "doc/example.txt"

With {:shorthand=>true, :source_file=>"./USAGE"} options, the above template compiles into the following Ruby program:

(e08afdfb_62c7_485f_87a0_80914e1b4703 = :_erbout; _erbout = []; _erbout << (::Ember::Template.read_file(("doc/example.txt"), {:shorthand=>true, :source_file=>"./USAGE"})) << "\n"
_erbout << "\n"
_erbout << (::Ember::Template.read_file(("doc/example.txt"), {:shorthand=>true, :source_file=>"./USAGE"})) << "\n"
; _erbout.join)

Which then produces the following output when rendered:

This is a plain-text file.  Notice that <%=
"eRuby directives" %> have no effect here!

This is a plain-text file.  Notice that <%=
"eRuby directives" %> have no effect here!

Template file inclusion

When doc/example.erb contains:

This is an eRuby template.  Notice that <%=
"eRuby directives" %> do take effect here!

And the eRuby template is:

<%+ "doc/example.erb" %>

%+ "doc/example.erb"

With {:shorthand=>true, :source_file=>"./USAGE"} options, the above template compiles into the following Ruby program:

(e08afdfb_62c7_485f_87a0_80914e1b4703 = :_erbout; _erbout = []; ::Ember::Template.load_file(("doc/example.erb"), {:shorthand=>true, :source_file=>"./USAGE"}.merge!(:continue_result => true)).render(::Kernel.binding); _erbout << "\n"
_erbout << "\n"
::Ember::Template.load_file(("doc/example.erb"), {:shorthand=>true, :source_file=>"./USAGE"}.merge!(:continue_result => true)).render(::Kernel.binding); _erbout << "\n"
; _erbout.join)

Which then produces the following output when rendered:

This is an eRuby template.  Notice that eRuby directives do take effect here!

This is an eRuby template.  Notice that eRuby directives do take effect here!

Dynamic template evaluation

<%~ "%= 2 + 2" %>

%~ "%= 2 + 2"

With {:shorthand=>true} options, the above template compiles into the following Ruby program:

(e08afdfb_62c7_485f_87a0_80914e1b4703 = :_erbout; _erbout = []; ::Ember::Template.new(("%= 2 + 2"), {:shorthand=>true}.merge!(:continue_result => true)).render(::Kernel.binding); _erbout << "\n"
_erbout << "\n"
::Ember::Template.new(("%= 2 + 2"), {:shorthand=>true}.merge!(:continue_result => true)).render(::Kernel.binding); _erbout << "\n"
; _erbout.join)

Which then produces the following output when rendered:

4

4

HACKING

Prerequisites

Install Ruby libraries necessary for development using [Bundler]:

bundle install

Infrastructure

[Inochi] serves as the project infrastructure for Ember. It handles tasks such as building this help manual and API documentation, and packaging, announcing, and publishing new releases. See its help manual and list of tasks to get started:

inochi --help     # display help manual
inochi --tasks    # list available tasks

$LOAD_PATH setup

Ensure that the lib/ directory is listed in Ruby’s $LOAD_PATH before you use any libraries therein or run any executables in the bin/ directory.

This can be achieved by passing an option to Ruby:

ruby -Ilib bin/ember
irb -Ilib -r ember

Or by setting the $RUBYLIB environment variable:

export RUBYLIB=lib   # bash, ksh, zsh
setenv RUBYLIB lib   # csh
set -x RUBYLIB lib   # fish

ruby bin/ember
irb -r ember

Or by running Ruby through the ruby-wrapper tool.

RubyGems setup

If you use Ruby 1.8 or older, then ensure that RubyGems is activated before you use any libraries in the lib/ directory or run any executables in the bin/ directory.

This can be achieved by passing an option to Ruby:

ruby -rubygems bin/ember
irb -rubygems -r ember

Or by setting the $RUBYOPT environment variable:

export RUBYOPT=-rubygems   # bash, ksh, zsh
setenv RUBYOPT -rubygems   # csh
set -x RUBYOPT -rubygems   # fish

Running tests

Simply execute the included test runner, which sets up Ruby’s $LOAD_PATH for testing, loads the included test/test_helper.rb file, and then evaluates all test/**/*_test.rb files:

ruby test/runner

Its exit status will indicate whether all tests have passed. It may also print additional pass/fail information depending on the testing library used in the test/test_helper.rb file.

Contributing

Fork this project on GitHub and send a pull request.

HISTORY

Version 0.3.1 (2011-04-22)

This release fixes a compatibility issue with Ruby 1.9.2-rc2.

Bug fixes
  • Compatibility fixes for Ruby 1.9.2-rc2.

Housekeeping
  • Upgrade to Inochi 6.0.2.

  • Upgrade from Dfect 2 to Detest 3.1.0.

  • Move SYNTAX section into separate source file.

Version 0.3.0 (2010-04-26)

This release adds class methods that let you (portably and more easily) create your own domain specific languages in eRuby; adds more usage examples in the help manual; and removes a binding inheritance hack.

New features
  • Ember::Template::wrap_content_block() wraps eRuby block content appending.

  • Ember::Template::content_from_block() extracts content from eRuby blocks.

  • Ember::Template::buffer_from_block() gives access to template evalutaion result buffer.

Bug fixes
  • Remove hack for inheriting parent template binding.

Housekeeping
  • Add example on unindenting node content and eRuby DSL examples that use the new content block methods.

  • Simplify code examples using the new wrap_content_block() method.

Version 0.2.0 (2010-04-25)

This release adds Ruby on Rails integration.

New features
  • Ember can now be used directly as a Ruby on Rails (2.3 or newer) plugin. The plugin registers Ember as the default Rails template handler for "erb" and "rhtml" file types. Ember processing options can be set in the Rails environment file:

    ActionView::TemplateHandlers::Ember.options = {
      :unindent => true,
      :shorthand => true,
      :infer_end => true
    }
    

    Thanks to Kamil Kukura for contributing this feature.

Version 0.1.1 (2010-04-20)

This release fixes a nested rendering bug, updates the manual, and further beautifies the Ruby code that results from eRuby template compilation.

Bug fixes
  • Ember::Template#render() now creates isolated contexts by default to prevent nested calls from clobbering each other’s output!

    For example, if template A calls method X which renders template B (thinking that template B’s rendering is isolated and will not affect the rendering of template A) then you’re in for a wild bug chase! >8-(

Housekeeping
  • Upgrade to Inochi 2.0.0rc5. Convert manual from ERBook to Ember + Ronn.

  • Remove spaces around value insertions in template compilation output.

  • Remove logo images from source repository because they’re no longer used.

Version 0.1.0 (2010-04-03)

This release improves the handling of eRuby comment directives, fixes a bug in the <% end %> inference logic, and performs some minor housekeeping.

New features
  • Single-line comment directives are now ignored (treated like no-ops) in input templates. This allows us to surround eRuby block directives with section separators made from single-line comment directives:

    %|some_block_directive
    
      Inside some_block_directive.
    
    %#---------------------------------------------------------------------------
    
      Still inside some_block_directive!
    
      %#-------------------------------------------------------------------------
      %| nested_block_directive
      %#-------------------------------------------------------------------------
    
        Inside nested_block_directive.
    
Bug fixes
  • <% end %> inference did not work for blocks beginning with def, class, and module keywords.

Housekeeping
  • Upgrade to Inochi 2.0.0-rc3. This project no longer depends on the "inochi" or "trollop" gems at runtime.

Version 0.0.1 (2009-10-03)

This release improves Ruby 1.9 support and revises the user manual.

Bug fixes
  • Nested templates could not access parent’s binding in Ruby 1.9

Housekeeping
  • Use simpler Copyright reminder at the top of every file.

  • Rename internal ‘Program` class’ methods to be self-documenting.

  • Open source is for fun, so speak of "related works", not "competitors".

Version 0.0.0 (2009-02-13)

This is the first public release of Ember. Enjoy!

AUTHORS

Suraj N. Kurapati

Credits

Kamil Kukura

License

(the ISC license)

Copyright 2009 Suraj N. Kurapati <sunaku@gmail.com>

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

SEE ALSO