Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Too much spare time

  • Loading branch information...
commit f0621b82d6616101d4ecdc4ef87e2d67e3427ee0 0 parents
@vti authored
8 .gitignore
@@ -0,0 +1,8 @@
+blib
+MANIFEST
+Makefile
+Makefile.old
+pm_to_blib
+*~
+*.bak
+*.swp
39 Makefile.PL
@@ -0,0 +1,39 @@
+#!/usr/bin/env perl
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use ExtUtils::MakeMaker;
+
+my $mm = $ExtUtils::MakeMaker::VERSION;
+
+WriteMakefile(
+ NAME => 'Text::Haml',
+ VERSION_FROM => 'lib/Text/Haml.pm',
+ ABSTRACT => 'Haml parser',
+ AUTHOR => 'Viacheslav Tykhanovskyi <vti@cpan.org>',
+
+ ($mm < 6.3002 ? () : ('LICENSE' => 'artistic_2')),
+
+ ( $mm < 6.46
+ ? ()
+ : ( META_MERGE => {
+ requires => {perl => '5.008001'},
+ resources => {
+ homepage => 'http://mojolicious.org',
+ license => 'http://dev.perl.org/licenses/',
+ repository => 'http://github.com/vti/text-haml/tree/master'
+ },
+ no_index => {directory => [qw/t/]}
+ },
+ META_ADD => {
+ build_requires => {},
+ configure_requires => {}
+ },
+ )
+ ),
+
+ test => {TESTS => 't/*.t t/*/*.t t/*/*/*.t t/*/*/*/*.t'}
+);
557 lib/Text/Haml.pm
@@ -0,0 +1,557 @@
+package Text::Haml;
+
+use strict;
+use warnings;
+
+our $VERSION = '0.010101';
+
+sub new {
+ my $class = shift;
+
+ # Default attributes
+ my $attrs = {};
+ $attrs->{format} = 'xhtml';
+ $attrs->{tape} = [];
+ $attrs->{encoding} = 'utf-8';
+ $attrs->{escape_html} = 1;
+ $attrs->{helpers} = {};
+ $attrs->{prepend} = '';
+ $attrs->{append} = '';
+ $attrs->{namespace} = '';
+ $attrs->{escape} = <<'EOF';
+ my $s = shift;
+ $s =~ s/&/&amp;/g;
+ $s =~ s/</&lt;/g;
+ $s =~ s/>/&gt;/g;
+ $s =~ s/"/&quot;/g;
+ $s =~ s/'/&apos;/g;
+ return $s;
+EOF
+
+ my $self = {%$attrs, @_};
+ bless $self, $class;
+
+ return $self;
+}
+
+# Yes, i know!
+sub format { @_ > 1 ? $_[0]->{format} = $_[1] : $_[0]->{format} }
+sub tape { @_ > 1 ? $_[0]->{tape} = $_[1] : $_[0]->{tape} }
+sub encoding { @_ > 1 ? $_[0]->{encoding} = $_[1] : $_[0]->{encoding} }
+
+sub escape_html {
+ @_ > 1
+ ? $_[0]->{escape_html} = $_[1]
+ : $_[0]->{escape_html};
+}
+sub code { @_ > 1 ? $_[0]->{code} = $_[1] : $_[0]->{code} }
+sub compiled { @_ > 1 ? $_[0]->{compiled} = $_[1] : $_[0]->{compiled} }
+sub helpers { @_ > 1 ? $_[0]->{helpers} = $_[1] : $_[0]->{helpers} }
+sub prepend { @_ > 1 ? $_[0]->{prepend} = $_[1] : $_[0]->{prepend} }
+sub append { @_ > 1 ? $_[0]->{append} = $_[1] : $_[0]->{append} }
+sub escape { @_ > 1 ? $_[0]->{escape} = $_[1] : $_[0]->{escape} }
+
+sub namespace {
+ @_ > 1
+ ? $_[0]->{namespace} = $_[1]
+ : $_[0]->{namespace};
+}
+
+our @AUTOCLOSE = (qw/meta img link br hr input area param col base/);
+
+sub add_helper {
+ my $self = shift;
+ my ($name, $code) = @_;
+
+ $self->helpers->{$name} = $code;
+}
+
+sub parse {
+ my $self = shift;
+ my $tmpl = shift;
+
+ $tmpl = '' unless defined $tmpl;
+
+ $self->tape([]);
+
+ my $level_token = quotemeta ' ';
+ my $escape_token = quotemeta '&';
+ my $unescape_token = quotemeta '!';
+ my $expr_token = quotemeta '=';
+ my $tag_start = quotemeta '%';
+ my $class_start = quotemeta '.';
+ my $id_start = quotemeta '#';
+ my $attributes_start = quotemeta '{';
+ my $attributes_end = quotemeta '}';
+ my $quote = "'";
+ my $comment_token = quotemeta '-#';
+ my $trim_in = quotemeta '<';
+ my $trim_out = quotemeta '>';
+
+ my $tape = $self->tape;
+
+ my $level;
+ my @lines = split /\n/, $tmpl;
+ push @lines, '' if $tmpl =~ m/\n$/;
+ @lines = ('') if $tmpl eq "\n";
+ for my $line (@lines) {
+ if ($line =~ s/^($level_token+)//) {
+ $level = length $1;
+ }
+ else {
+ $level = 0;
+ }
+
+ my $el = {level => $level, type => 'text', line => $line};
+
+ # Haml comment
+ if ($line =~ m/^$comment_token(?: (.*))?/) {
+ $el->{type} = 'comment';
+ $el->{text} = $1 if $1;
+ push @$tape, $el;
+ next;
+ }
+
+ # Doctype
+ if ($line =~ m/^!!!(?: ([^ ]+)(?: (.*))?)?$/) {
+ $el->{type} = 'text';
+ $el->{text} = $self->_doctype($1, $2);
+ push @$tape, $el;
+ next;
+ }
+
+ # HTML comment
+ if ($line =~ m/^\/(?:\[if (.*)?\])?(?: (.*))?/) {
+ $el->{type} = 'html_comment';
+ $el->{if} = $1 if $1;
+ $el->{text} = $2 if $2;
+ push @$tape, $el;
+ next;
+ }
+
+ # Escaping, everything after is a text
+ if ($line =~ s/^\\//) {
+ $el->{type} = 'text',
+ $el->{text} = $line;
+ push @$tape, $el;
+ next;
+ }
+
+ # Block
+ if ($line =~ s/^- \s*(.*)//) {
+ $el->{type} = 'block';
+ $el->{text} = $1;
+ push @$tape, $el;
+ next;
+ }
+
+ # Preserve whitespace
+ if ($line =~ s/^~ \s*(.*)//) {
+ $el->{type} = 'text';
+ $el->{text} = $1;
+ $el->{expr} = 1;
+ $el->{preserve_whitespace} = 1;
+ push @$tape, $el;
+ next;
+ }
+
+ # Tag
+ if ($line =~ m/^(?:$tag_start
+ |$class_start
+ |$id_start
+ |$attributes_start
+ |$escape_token
+ |$unescape_token
+ |$expr_token)/x
+ )
+ {
+ if ($line =~ s/^$tag_start([^ {.<>#!&=\/]+)//) {
+ $el->{type} = 'tag';
+ $el->{name} = $1;
+ }
+
+ if ($line =~ s/^\.([^ {}#!&=<>\/]+)//) {
+ my $class = join(' ', split(/\./, $1));
+
+ $el->{type} = 'tag';
+ $el->{name} ||= 'div';
+ $el->{tail} ||= '';
+ $el->{tail} .= qq| class=$quote$class$quote|;
+ }
+
+ if ($line =~ s/^\#([^ {}!&=<>\/]+)//) {
+ my $id = $1;
+
+ $el->{type} = 'tag';
+ $el->{name} ||= 'div';
+ $el->{tail} ||= '';
+ $el->{tail} .= qq| id=$quote$id$quote|;
+ }
+
+ if ($line =~ s/$attributes_start(.*?)$attributes_end//) {
+ my $attrs = $1;
+
+ my @attr = split(/\s*,\s*/, $attrs);
+ $attrs = ' ';
+ foreach my $attr (@attr) {
+ my $name;
+ if ($attr =~ s/^\s*('|")(.*?)\1\s*=>//x) {
+ $name = $2;
+ }
+ elsif ($attr =~ s/^\s*:?([^ ]+)\s*=>//x) {
+ $name = $1;
+ }
+ else {
+ next;
+ }
+ $attr =~ s/^\s*('|")(.*?)\1\s*$//x || next;
+ $attrs .= "$name=$quote$2$quote ";
+ }
+ $attrs =~ s/\s+$//;
+
+ $el->{type} = 'tag';
+ $el->{tail} ||= '';
+ $el->{tail} .= $attrs;
+ }
+
+ if ($line =~ s/^$trim_out ?//) {
+ $el->{trim_out} = 1;
+ }
+
+ if ($line =~ s/^$trim_in ?//) {
+ $el->{trim_in} = 1;
+ }
+
+ if ($line =~ s/^($escape_token|$unescape_token)?$expr_token //) {
+ $el->{expr} = 1;
+ if ($1) {
+ $el->{escape} = quotemeta($1) eq $escape_token ? 1 : 0;
+ }
+ }
+ }
+
+ if ($el->{type} eq 'tag'
+ && ($line =~ s/\/$// || grep { $el->{name} eq $_ } @AUTOCLOSE))
+ {
+ $el->{autoclose} = 1;
+ }
+
+ $line =~ s/^ // if $line;
+
+ # Multiline
+ if ($line && $line =~ s/(\s*)\|$//) {
+
+ # For the first time
+ if (!$tape->[-1] || ref $tape->[-1]->{text} ne 'ARRAY') {
+ $el->{text} = [$line];
+ $el->{line} = $el->{line} . "\n" || $line . "$1|\n";
+
+ push @$tape, $el;
+ }
+
+ # Continue concatenation
+ else {
+ my $prev_el = $tape->[-1];
+ push @{$prev_el->{text}}, $line;
+ $prev_el->{line} .= $line . "$1|\n";
+ }
+ }
+
+ # For the last time
+ elsif ($tape->[-1] && ref $tape->[-1]->{text} eq 'ARRAY') {
+ $tape->[-1]->{text} = join(" ", @{$tape->[-1]->{text}}, $line);
+ $tape->[-1]->{line} .= $line;
+ }
+
+ # Normal text
+ else {
+ $el->{text} = $line if $line;
+
+ push @$tape, $el;
+ }
+ }
+}
+
+sub build {
+ my $self = shift;
+ my %vars = @_;
+
+ my $code;
+
+ my $ESCAPE = $self->escape;
+ $ESCAPE = <<"EOF";
+no strict 'refs'; no warnings 'redefine';
+sub escape;
+*escape = sub {
+ $ESCAPE
+};
+use strict; use warnings;
+EOF
+
+ $ESCAPE =~ s/\n//g;
+
+ my $namespace = $self->namespace || ref($self) . '::template';
+ $code .= qq/package $namespace; $ESCAPE; sub { my \$_H = ''; /;
+
+ # Embed variables
+ foreach my $var (sort keys %vars) {
+ $code .= qq/my \$$var = "$vars{$var}";/;
+ }
+
+ $code .= $self->prepend;
+
+ # Install helpers
+ for my $name (sort keys %{$self->helpers}) {
+ next unless $name =~ m/^\w+$/;
+
+ $code .= "sub $name;";
+ $code .= " *$name = sub { \$self";
+ $code .= "->helpers->{'$name'}->(\$self, \@_) };";
+ }
+
+ my $stack = [];
+
+ my @lines;
+ my $count = 0;
+ for my $el (@{$self->tape}) {
+ my $offset = '';
+ $offset .= ' ' x $el->{level};
+
+ my $prev_el = $stack->[-1];
+
+ if ($prev_el && $prev_el->{type} eq 'comment') {
+ if ($prev_el->{level} == $el->{level}) {
+ pop @$stack;
+ }
+ else {
+ next;
+ }
+ }
+
+ if ($el->{line} && $prev_el && $prev_el->{level} >= $el->{level}) {
+ while (my $poped = pop @$stack) {
+ my $poped_offset = ' ' x $poped->{level};
+
+ my $ending = '';
+ if ($poped->{type} eq 'tag') {
+ $ending .= "</$poped->{name}>";
+ }
+ elsif ($poped->{type} eq 'html_comment') {
+ $ending .= "<![endif]" if $poped->{if};
+ $ending .= "-->";
+ }
+ push @lines, qq|\$_H .= "$poped_offset$ending\n";|;
+
+ last if $poped->{level} == $el->{level};
+ }
+ }
+
+ my $output = '';
+ if ($el->{type} eq 'tag') {
+ $el->{tail} ||= '';
+ $el->{tail} .= ' /' if $el->{autoclose};
+ $el->{closed} = 1;
+ $output .= qq|"$offset<$el->{name}$el->{tail}>"|;
+
+ if ($el->{text} && $el->{expr}) {
+ $output .= '. ' . $el->{text};
+ $output .= qq| . "</$el->{name}>"|;
+ }
+ elsif ($el->{text}) {
+ $output .= '. "' . quotemeta($el->{text}) . '"';
+ $output .= qq|. "</$el->{name}>"| unless $el->{autoclose};
+ }
+ elsif ($self->tape->[$count + 1]
+ && $self->tape->[$count + 1]->{level} == $el->{level})
+ {
+ $output .= qq|. "</$el->{name}>"| unless $el->{autoclose};
+ }
+ elsif (!$el->{autoclose}) {
+ $el->{closed} = 0;
+ push @$stack, $el;
+ }
+
+ #push @$stack, $el;
+
+ $output .= qq|. "\n"|;
+ $output .= qq|;|;
+ }
+ elsif ($el->{type} eq 'text') {
+ $output = qq/"$offset"/;
+
+ $el->{text} = '' unless defined $el->{text};
+
+ if ($el->{expr}) {
+ my $escape = '';
+ if ((!exists $el->{escape} && $self->escape_html) || (exists
+ $el->{escape} && $el->{escape} == 1)) {
+ $escape = 'escape'
+ }
+
+ $output .= qq/. $escape / . +$el->{text};
+ $output .= qq/;\$_H .= "\n"/;
+ }
+ elsif ($el->{text}) {
+ $output .= qq/. "/ . quotemeta($el->{text}) . '"';
+ $output .= qq/. "\n"/;
+ }
+
+ $output .= qq/;/;
+ }
+ elsif ($el->{type} eq 'block') {
+ push @lines, $el->{text};
+ }
+ elsif ($el->{type} eq 'html_comment') {
+ $output = qq/"$offset"/;
+
+ $output .= qq/ . "<!--"/;
+ $output .= qq/ . "[if $el->{if}]>"/ if $el->{if};
+
+ if ($el->{text}) {
+ $output .= qq/. " $el->{text} -->\n"/;
+ }
+ else {
+ $output .= qq/. "\n"/;
+ push @$stack, $el;
+ }
+
+ $output .= qq/;/;
+ }
+ elsif ($el->{type} eq 'comment') {
+ push @$stack, $el;
+ }
+ else {
+ die "unknown type=" . $el->{type};
+ }
+
+ push @lines, '$_H .= ' . $output if $output;
+
+ $count++;
+ }
+
+ my $last_empty_line = 0;
+ $last_empty_line = 1
+ if $self->tape->[-1] && $self->tape->[-1]->{line} eq '';
+
+ foreach my $el (reverse @$stack) {
+ my $offset = ' ' x $el->{level};
+ my $ending = '';
+ if ($el->{type} eq 'tag') {
+ $ending = "</$el->{name}>";
+ }
+ elsif ($el->{type} eq 'html_comment') {
+ $ending .= '<![endif]' if $el->{if};
+ $ending .= "-->";
+ }
+
+ push @lines, qq|\$_H .= "$offset$ending\n";|;
+ }
+
+ $lines[-1] =~ s/\n";$/";/ unless $last_empty_line;
+
+ $code .= join("\n", @lines);
+
+ $code .= $self->append;
+
+ $code .= q/return $_H; };/;
+
+ $self->code($code);
+ return $self;
+}
+
+sub compile {
+ my $self = shift;
+
+ my $code = $self->code;
+ return unless $code;
+
+ my $compiled = eval $code;
+ die $@ if $@;
+
+ $self->compiled($compiled);
+
+ return $self;
+}
+
+sub interpret {
+ my $self = shift;
+
+ my $compiled = $self->compiled;
+
+ my $output = eval { $compiled->() };
+
+ die $@ if $@;
+
+ return $output;
+}
+
+sub render {
+ my $self = shift;
+ my $tmpl = shift;
+
+ # Parse
+ $self->parse($tmpl);
+
+ # Build
+ $self->build(@_);
+
+ # Compile
+ $self->compile;
+
+ # Interpret
+ return $self->interpret;
+}
+
+sub _doctype {
+ my $self = shift;
+ my ($type, $encoding) = @_;
+
+ $type ||= '';
+ $encoding ||= 'utf-8';
+
+ if ($type eq 'XML') {
+ return qq|<?xml version='1.0' encoding='$encoding' ?>|;
+ }
+
+ if ($self->format eq 'xhtml') {
+ if ($type eq 'Strict') {
+ return q|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">|;
+ }
+ elsif ($type eq 'Frameset') {
+ return q|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">|;
+ }
+ elsif ($type eq '5') {
+ return '<!DOCTYPE html>';
+ }
+ elsif ($type eq '1.1') {
+ return q|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">|;
+ }
+ elsif ($type eq 'Basic') {
+ return q|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">|;
+ }
+ elsif ($type eq 'Mobile') {
+ return q|<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">|;
+ }
+ else {
+ return q|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">|;
+ }
+ }
+ elsif ($self->format eq 'html4') {
+ if ($type eq 'Strict') {
+ return q|<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">|;
+ }
+ elsif ($type eq 'Frameset') {
+ return q|<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">|;
+ }
+ else {
+ return q|<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">|;
+ }
+ }
+ elsif ($self->format eq 'html5') {
+ return '<!DOCTYPE html>';
+ }
+
+ return '';
+}
+
+1;
44 t/class-and-id.t
@@ -0,0 +1,44 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Text::Haml;
+
+use Test::More tests => 2;
+
+my $haml = Text::Haml->new;
+
+my $output = $haml->render(<<'EOF');
+%div#things
+ %span#rice Chicken Fried
+ %p.beans{ :food => 'true' } The magical fruit
+ %h1.class.otherclass#id La La La
+EOF
+is($output, <<'EOF');
+<div id='things'>
+ <span id='rice'>Chicken Fried</span>
+ <p class='beans' food='true'>The magical fruit</p>
+ <h1 class='class otherclass' id='id'>La La La</h1>
+</div>
+EOF
+
+$output = $haml->render(<<'EOF');
+#content
+ .articles
+ .article.title Doogie Howser Comes Out
+ .article.date 2006-11-05
+ .article.entry
+ Neil Patrick Harris would like to dispel any rumors that he is straight
+EOF
+is($output, <<'EOF');
+<div id='content'>
+ <div class='articles'>
+ <div class='article title'>Doogie Howser Comes Out</div>
+ <div class='article date'>2006-11-05</div>
+ <div class='article entry'>
+ Neil Patrick Harris would like to dispel any rumors that he is straight
+ </div>
+ </div>
+</div>
+EOF
76 t/comments.t
@@ -0,0 +1,76 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Text::Haml;
+
+use Test::More tests => 5;
+
+my $haml = Text::Haml->new;
+
+# HTML Comments: /
+
+my $output = $haml->render(<<'EOF');
+%peanutbutterjelly
+ / This is the peanutbutterjelly element
+ I like sandwiches!
+EOF
+is($output, <<'EOF');
+<peanutbutterjelly>
+ <!-- This is the peanutbutterjelly element -->
+ I like sandwiches!
+</peanutbutterjelly>
+EOF
+
+$output = $haml->render(<<'EOF');
+/
+ %p This doesn't render...
+ %div
+ %h1 Because it's commented out!
+EOF
+is($output, <<'EOF');
+<!--
+ <p>This doesn't render...</p>
+ <div>
+ <h1>Because it's commented out!</h1>
+ </div>
+-->
+EOF
+
+# Conditional Comments: /[]
+$output = $haml->render(<<'EOF');
+/[if IE]
+ %a{ :href => 'http://www.mozilla.com/en-US/firefox/' }
+ %h1 Get Firefox
+EOF
+is($output, <<'EOF');
+<!--[if IE]>
+ <a href='http://www.mozilla.com/en-US/firefox/'>
+ <h1>Get Firefox</h1>
+ </a>
+<![endif]-->
+EOF
+
+# Text::Haml Comments: -#
+$output = $haml->render(<<'EOF');
+%p foo
+-# This is a comment
+%p bar
+EOF
+is($output, <<'EOF');
+<p>foo</p>
+<p>bar</p>
+EOF
+
+$output = $haml->render(<<'EOF');
+%p foo
+-#
+ This won't be displayed
+ Nor will this
+%p bar
+EOF
+is($output, <<'EOF');
+<p>foo</p>
+<p>bar</p>
+EOF
94 t/complete.t
@@ -0,0 +1,94 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Text::Haml;
+
+use Test::More tests => 1;
+
+my $haml = Text::Haml->new;
+
+my $output = $haml->render(<<'EOF');
+!!!
+#main
+ .note
+ %h2 Quick Notes
+ %ul
+ %li
+ Text::Haml is usually indented with two spaces,
+ although more than two is allowed.
+ You have to be consistent, though.
+ %li
+ The first character of any line is called
+ the "control character" - it says "make a tag"
+ or "run Ruby code" or all sorts of things.
+ %li
+ Text::Haml takes care of nicely indenting your HTML.
+ %li
+ Text::Haml allows Ruby code and blocks.
+ But not in this example.
+ We turned it off for security.
+
+ .note
+ You can get more information by reading the
+ %a{:href => "/docs/yardoc/HAML_REFERENCE.md.html"}
+ Official Text::Haml Reference
+
+ .note
+ %p
+ This example doesn't allow Ruby to be executed,
+ but real Text::Haml does.
+ %p
+ Ruby code is included by using = at the
+ beginning of a line.
+ %p
+ Read the tutorial for more information.
+EOF
+is($output, <<'EOF');
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<div id='main'>
+ <div class='note'>
+ <h2>Quick Notes</h2>
+ <ul>
+ <li>
+ Text::Haml is usually indented with two spaces,
+ although more than two is allowed.
+ You have to be consistent, though.
+ </li>
+ <li>
+ The first character of any line is called
+ the "control character" - it says "make a tag"
+ or "run Ruby code" or all sorts of things.
+ </li>
+ <li>
+ Text::Haml takes care of nicely indenting your HTML.
+ </li>
+ <li>
+ Text::Haml allows Ruby code and blocks.
+ But not in this example.
+ We turned it off for security.
+ </li>
+ </ul>
+ </div>
+ <div class='note'>
+ You can get more information by reading the
+ <a href='/docs/yardoc/HAML_REFERENCE.md.html'>
+ Official Text::Haml Reference
+ </a>
+ </div>
+ <div class='note'>
+ <p>
+ This example doesn't allow Ruby to be executed,
+ but real Text::Haml does.
+ </p>
+ <p>
+ Ruby code is included by using = at the
+ beginning of a line.
+ </p>
+ <p>
+ Read the tutorial for more information.
+ </p>
+ </div>
+</div>
+EOF
38 t/div.t
@@ -0,0 +1,38 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Text::Haml;
+
+use Test::More tests => 2;
+
+my $haml = Text::Haml->new;
+
+# Implicit Div Elements
+
+my $output = $haml->render(<<'EOF');
+%div#collection
+ %div.item
+ %div.description What a cool item!
+EOF
+is($output, <<'EOF');
+<div id='collection'>
+ <div class='item'>
+ <div class='description'>What a cool item!</div>
+ </div>
+</div>
+EOF
+
+$output = $haml->render(<<'EOF');
+#collection
+ .item
+ .description What a cool item!
+EOF
+is($output, <<'EOF');
+<div id='collection'>
+ <div class='item'>
+ <div class='description'>What a cool item!</div>
+ </div>
+</div>
+EOF
63 t/doctype.t
@@ -0,0 +1,63 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Text::Haml;
+
+use Test::More tests => 12;
+
+my $haml = Text::Haml->new;
+
+my $output = $haml->render(<<'EOF');
+!!! XML
+!!!
+%html
+ %head
+ %title Myspace
+ %body
+ %h1 I am the international space station
+ %p Sign my guestbook
+EOF
+is($output, <<'EOF');
+<?xml version='1.0' encoding='utf-8' ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+ <head>
+ <title>Myspace</title>
+ </head>
+ <body>
+ <h1>I am the international space station</h1>
+ <p>Sign my guestbook</p>
+ </body>
+</html>
+EOF
+
+$output = $haml->render('!!! Strict');
+is($output, q|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">|);
+$output = $haml->render('!!! Frameset');
+is($output, q|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">|);
+$output = $haml->render('!!! 5');
+is($output, '<!DOCTYPE html>');
+$output = $haml->render('!!! 1.1');
+is($output, q|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">|);
+$output = $haml->render('!!! Basic');
+is($output, q|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">|);
+$output = $haml->render('!!! Mobile');
+is($output, q|<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">|);
+
+$haml->format('html4');
+$output = $haml->render('!!!');
+is($output, q|<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">|);
+$output = $haml->render('!!! Strict');
+is($output, q|<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">|);
+$output = $haml->render('!!! Frameset');
+is($output, q|<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">|);
+
+$haml->format('html5');
+$output = $haml->render('!!!');
+is($output, '<!DOCTYPE html>');
+
+# Encoding
+$output = $haml->render('!!! XML iso-8859-1');
+is($output, q|<?xml version='1.0' encoding='iso-8859-1' ?>|);
22 t/escaping.t
@@ -0,0 +1,22 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+use Text::Haml;
+
+my $haml = Text::Haml->new;
+
+my $output = $haml->render(<<'EOF');
+%title
+ = "MyPage"
+ \= $title
+EOF
+is($output, <<'EOF');
+<title>
+ MyPage
+ = $title
+</title>
+EOF
80 t/filters.t
@@ -0,0 +1,80 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Text::Haml;
+
+use Test::More skip_all => 'Not yet implemented';
+
+##Filters
+##
+##The colon character designates a filter. This allows you to pass an indented block of text as input to another filtering program and add the result to the output of Text::Haml. The syntax is simply a colon followed by the name of the filter. For example,
+##
+##%p
+## :markdown
+## Textile
+## =======
+##
+## Hello, *World*
+##
+##is compiled to
+##
+##<p>
+## <h1>Textile</h1>
+##
+## <p>Hello, <em>World</em></p>
+##</p>
+##
+##Filters can have Ruby code interpolated with #{}. For example,
+##
+##- flavor = "raspberry"
+###content
+## :textile
+## I *really* prefer _#{h flavor}_ jam.
+##
+##is compiled to
+##
+##<div id='content'>
+## <p>I <strong>really</strong> prefer <em>raspberry</em> jam.</p>
+##</div>
+##
+##Currently, filters ignore the :escape_html option. This means that #{} interpolation within filters is never HTML-escaped.
+##
+##Text::Haml has the following filters defined:
+##:plain
+##
+##Does not parse the filtered text. This is useful for large blocks of text without HTML tags, when you don’t want lines starting with . or - to be parsed.
+##:javascript
+##
+##Surrounds the filtered text with <script> and CDATA tags. Useful for including inline Javascript.
+##:cdata
+##
+##Surrounds the filtered text with CDATA tags.
+##:escaped
+##
+##Works the same as plain, but HTML-escapes the text before placing it in the document.
+##:ruby
+##
+##Parses the filtered text with the normal Ruby interpreter. All output sent to $stdout, like with puts, is output into the Text::Haml document. Not available if the :suppress_eval option is set to true. The Ruby code is evaluated in the same context as the Text::Haml template.
+##:preserve
+##
+##Inserts the filtered text into the template with whitespace preserved. preserved blocks of text aren’t indented, and newlines are replaced with the HTML escape code for newlines, to preserve nice-looking output. See also Whitespace Preservation.
+##:erb
+##
+##Parses the filtered text with ERB, like an RHTML template. Not available if the :suppress_eval option is set to true. Embedded Ruby code is evaluated in the same context as the Text::Haml template.
+##:sass
+##
+##Parses the filtered text with Sass to produce CSS output.
+##:textile
+##
+##Parses the filtered text with Textile. Only works if RedCloth is installed.
+##:markdown
+##
+##Parses the filtered text with Markdown. Only works if RDiscount, RPeg-Markdown, Maruku, or BlueCloth are installed.
+##:maruku
+##
+##Parses the filtered text with Maruku, which has some non-standard extensions to Markdown.
+##Custom Filters
+##
+##You can also define your own filters. See Text::Haml::Filters for details.
37 t/helpers.t
@@ -0,0 +1,37 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+use Text::Haml;
+
+my $haml = Text::Haml->new(
+ helpers => {
+ foo => sub {
+ my $self = shift;
+ my $string = shift;
+
+ $string =~ s/r/z/;
+
+ return $string;
+ }
+ }
+);
+
+$haml->add_helper(bar => sub {
+ my $self = shift;
+ my $string = shift;
+
+ return 'hello';
+ });
+
+my $output = $haml->render(<<'EOF');
+= foo('bar')
+= bar()
+EOF
+is($output, <<'EOF');
+baz
+hello
+EOF
93 t/html-attributes.t
@@ -0,0 +1,93 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+use Text::Haml;
+
+my $haml = Text::Haml->new;
+
+# Attributes: {} or ()
+# TODO should be <html ...></html>
+
+my $output = $haml->render(<<'EOF');
+%html{xmlns => "http://www.w3.org/1999/xhtml", "xml:lang" => "en", lang => "en"}
+EOF
+is($output, <<'EOF')
+<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'></html>
+EOF
+
+#$output = $haml->render(<<'EOF');
+#%script{:type => "text/javascript",
+# :src => "javascripts/script_#{2 + 7}"}
+#EOF
+#is($output, <<'EOF');
+#<script src='javascripts/script_9' type='text/javascript'></script>
+#EOF
+
+## HTML-style Attributes: ()
+#
+#$output = $haml->render(<<'EOF');
+#%html(xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en")
+#EOF
+#is($output, <<'EOF');
+#<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'></html>
+#EOF
+#
+## Attribute Methods
+#
+##def html_attrs(lang = 'en-US')
+## {:xmlns => "http://www.w3.org/1999/xhtml", 'xml:lang' => lang, :lang => lang}
+##end
+##
+##This can then be used in Text::Haml, like so:
+##
+##$output = $haml->render(<<'EOF');
+##%html{html_attrs('fr-fr')}
+##
+##This is compiled to:
+##
+##<html lang='fr-fr' xml:lang='fr-fr' xmlns='http://www.w3.org/1999/xhtml'>
+##</html>
+#
+##You can use as many such attribute methods as you want by separating them with commas, like a Ruby argument list. All the hashes will me merged together, from left to right. For example, if you defined
+##
+##def hash1
+## {:bread => 'white', :filling => 'peanut butter and jelly'}
+##end
+##
+##def hash2
+## {:bread => 'whole wheat'}
+##end
+##
+##then
+##
+##$output = $haml->render(<<'EOF');
+##%sandwich{hash1, hash2, :delicious => true}/
+##
+##would compile to:
+##
+##<sandwich bread='whole wheat' delicious='true' filling='peanut butter and jelly' />
+##
+#
+## Boolean Attributes
+#
+## HTML
+#
+#$output = $haml->render(<<'EOF');
+#%input{:selected => true}
+#EOF
+#is($output, <<'EOF');
+#<input selected='selected'>
+#EOF
+#
+## XHTML
+#
+#$output = $haml->render(<<'EOF');
+#%input{:selected => false}
+#EOF
+#is($output, <<'EOF');
+#<input>
+#EOF
23 t/html-elements.t
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Text::Haml;
+
+use Test::More tests => 1;
+
+my $haml = Text::Haml->new;
+
+my $output = $haml->render(<<'EOF');
+%one
+ %two
+ %three Hey there
+EOF
+is($output, <<'EOF');
+<one>
+ <two>
+ <three>Hey there</three>
+ </two>
+</one>
+EOF
36 t/html-escaping.t
@@ -0,0 +1,36 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+
+use Text::Haml;
+
+my $haml = Text::Haml->new;
+
+$haml->escape_html(0);
+
+my $output = $haml->render(<<'EOF');
+&= "I like cheese & crackers"
+EOF
+is($output, <<'EOF');
+I like cheese &amp; crackers
+EOF
+
+$haml->escape_html(1);
+
+$output = $haml->render(<<'EOF');
+&= "I like cheese & crackers"
+EOF
+is($output, <<'EOF');
+I like cheese &amp; crackers
+EOF
+
+#& can also be used on its own so that #{} interpolation is escaped. For example,
+#
+#& I like #{"cheese & crackers"}
+#
+#compiles to
+#
+#I like cheese &amp; crackers
30 t/html-unescaping.t
@@ -0,0 +1,30 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+
+use Text::Haml;
+
+my $haml = Text::Haml->new;
+
+$haml->escape_html(0);
+my $output = $haml->render(<<'EOF');
+= "I feel <strong>!"
+!= "Me <too>!"
+EOF
+is($output, <<'EOF');
+I feel <strong>!
+Me <too>!
+EOF
+
+$haml->escape_html(1);
+$output = $haml->render(<<'EOF');
+= "I feel <strong>!"
+!= "Me <too>!"
+EOF
+is($output, <<'EOF');
+I feel &lt;strong&gt;!
+Me <too>!
+EOF
45 t/multiline.t
@@ -0,0 +1,45 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Text::Haml;
+
+use Test::More tests => 2;
+
+my $haml = Text::Haml->new;
+
+my $output = $haml->render(<<'EOF');
+%whoo
+ %hoo I think this might get |
+ pretty long so I should |
+ probably make it |
+ multiline so it doesn't |
+ look awful.
+ %p This is short.
+EOF
+is($output, <<'EOF');
+<whoo>
+ <hoo>I think this might get pretty long so I should probably make it multiline so it doesn't look awful.</hoo>
+ <p>This is short.</p>
+</whoo>
+EOF
+
+$output = $haml->render(<<'EOF');
+%whoo
+ %hoo
+ I think this might get |
+ pretty long so I should |
+ probably make it |
+ multiline so it doesn't |
+ look awful.
+ %p This is short.
+EOF
+is($output, <<'EOF');
+<whoo>
+ <hoo>
+ I think this might get pretty long so I should probably make it multiline so it doesn't look awful.
+ </hoo>
+ <p>This is short.</p>
+</whoo>
+EOF
309 t/parse.t
@@ -0,0 +1,309 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Text::Haml;
+
+use Test::More tests => 15;
+
+my $haml = Text::Haml->new;
+
+$haml->parse();
+is_deeply($haml->tape, []);
+
+$haml->parse('');
+is_deeply($haml->tape, []);
+
+$haml->parse(' ');
+is_deeply(
+ $haml->tape,
+ [ { 'level' => 3,
+ 'type' => 'text',
+ 'line' => ''
+ }
+ ]
+);
+
+$haml->parse("\n");
+is_deeply($haml->tape,
+ [{type => 'text', level => 0, line => ''}]);
+
+$haml->parse("\n");
+is_deeply($haml->tape,
+ [{type => 'text', level => 0, line => ''}]);
+
+$haml->parse(<<'EOF');
+%gee
+ %whiz.class.class2#id{foo => 'bar'}
+ %baz= 1 + 2
+ Wow this is cool!
+EOF
+is_deeply(
+ $haml->tape,
+ [ {type => 'tag', level => 0, name => 'gee', line => '%gee'},
+ { type => 'tag',
+ level => 2,
+ name => 'whiz',
+ tail => q| class='class class2' id='id' foo='bar'|,
+ line => "%whiz.class.class2#id{foo => 'bar'}"
+ },
+ { type => 'tag',
+ level => 4,
+ name => 'baz',
+ expr => 1,
+ text => '1 + 2',
+ line => '%baz= 1 + 2'
+ },
+ { type => 'text',
+ level => 6,
+ text => 'Wow this is cool!',
+ line => 'Wow this is cool!'
+ },
+ { type => 'text',
+ level => 0,
+ line => ''
+ }
+ ]
+);
+
+$haml->parse(<<'EOF');
+%blockquote<
+ %foo
+ %bar> trim out
+EOF
+is_deeply(
+ $haml->tape,
+ [ { type => 'tag',
+ level => 0,
+ name => 'blockquote',
+ trim_in => 1,
+ line => '%blockquote<'
+ },
+ { type => 'tag',
+ level => 2,
+ name => 'foo',
+ line => '%foo'
+ },
+ { type => 'tag',
+ level => 2,
+ name => 'bar',
+ trim_out => 1,
+ text => 'trim out',
+ line => '%bar> trim out'
+ },
+ { type => 'text',
+ level => 0,
+ line => ''
+ }
+ ]
+);
+
+$haml->parse(<<'EOF');
+-# haml comment
+ \= "just text"
+ / html comment
+ /[if IE] if html comment
+EOF
+is_deeply(
+ $haml->tape,
+ [ { type => 'comment',
+ level => 0,
+ text => 'haml comment',
+ line => '-# haml comment'
+ },
+ { type => 'text',
+ level => 2,
+ text => '= "just text"',
+ line => '\= "just text"'
+ },
+ { type => 'html_comment',
+ level => 2,
+ text => 'html comment',
+ line => '/ html comment'
+ },
+ { type => 'html_comment',
+ level => 2,
+ if => 'IE',
+ text => 'if html comment',
+ line => '/[if IE] if html comment'
+ },
+ { type => 'text',
+ level => 0,
+ line => ''
+ }
+ ]
+);
+
+$haml->parse(<<'EOF');
+multiline |
+comment |
+parsing
+normal
+EOF
+is_deeply(
+ $haml->tape,
+ [ { type => 'text',
+ level => 0,
+ text => 'multiline comment parsing',
+ line => "multiline |\ncomment |\nparsing"
+ },
+ { type => 'text',
+ level => 0,
+ text => 'normal',
+ line => 'normal'
+ },
+ { type => 'text',
+ level => 0,
+ line => ''
+ }
+ ]
+);
+
+$haml->parse(<<'EOF');
+%p multiline |
+ comment |
+ parsing
+normal
+EOF
+is_deeply(
+ $haml->tape,
+ [ { type => 'tag',
+ level => 0,
+ name => 'p',
+ text => 'multiline comment parsing',
+ line => "%p multiline |\ncomment |\nparsing"
+ },
+ { type => 'text',
+ level => 0,
+ text => 'normal',
+ line => 'normal'
+ },
+ { type => 'text',
+ level => 0,
+ line => ''
+ }
+ ]
+);
+
+$haml->parse(<<'EOF');
+%img
+%a/
+EOF
+is_deeply(
+ $haml->tape,
+ [ { type => 'tag',
+ level => 0,
+ name => 'img',
+ autoclose => 1,
+ line => '%img'
+ },
+ { type => 'tag',
+ level => 0,
+ name => 'a',
+ autoclose => 1,
+ line => '%a/'
+ },
+ { type => 'text',
+ level => 0,
+ line => ''
+ }
+ ]
+);
+
+$haml->parse(<<'EOF');
+= 1 + 2
+- "foo"
+EOF
+is_deeply(
+ $haml->tape,
+ [ { type => 'text',
+ level => 0,
+ expr => 1,
+ text => '1 + 2',
+ line => '= 1 + 2'
+ },
+ { type => 'block',
+ level => 0,
+ text => '"foo"',
+ line => '- "foo"'
+ },
+ { type => 'text',
+ level => 0,
+ line => ''
+ }
+ ]
+);
+
+$haml->parse(<<'EOF');
+= 'foo' if 1
+&= '<escape>'
+!= '<noescape>'
+EOF
+is_deeply(
+ $haml->tape,
+ [ { type => 'text',
+ level => 0,
+ expr => 1,
+ text => "'foo' if 1",
+ line => "= 'foo' if 1"
+ },
+ { type => 'text',
+ level => 0,
+ expr => 1,
+ escape => 1,
+ text => "'<escape>'",
+ line => "&= '<escape>'"
+ },
+ { type => 'text',
+ level => 0,
+ expr => 1,
+ escape => 0,
+ text => "'<noescape>'",
+ line => "!= '<noescape>'"
+ },
+ { type => 'text',
+ level => 0,
+ line => ''
+ }
+ ]
+);
+
+$haml->parse(<<'EOF');
+%foo
+
+%bar
+EOF
+is_deeply(
+ $haml->tape,
+ [ {type => 'tag', level => 0, name => 'foo', line => '%foo'},
+ { type => 'text',
+ level => 0,
+ line => ''
+ },
+ {type => 'tag', level => 0, name => 'bar', line => '%bar'},
+ { type => 'text',
+ level => 0,
+ line => ''
+ }
+ ]
+);
+
+$haml->parse(<<'EOF');
+~ "Foo\n<pre>Bar\nBaz</pre>"
+EOF
+is_deeply(
+ $haml->tape,
+ [ { type => 'text',
+ level => 0,
+ text => '"Foo\n<pre>Bar\nBaz</pre>"',
+ expr => 1,
+ preserve_whitespace => 1,
+ line => '~ "Foo\n<pre>Bar\nBaz</pre>"'
+ },
+ { type => 'text',
+ level => 0,
+ line => ''
+ }
+ ]
+);
54 t/perl-interpolation.t
@@ -0,0 +1,54 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More skip_all => 'Not yet implemented';
+
+use Text::Haml;
+
+my $haml = Text::Haml->new;
+
+##Ruby Interpolation: #{}
+##
+##Ruby code can also be interpolated within plain text using #{}, similarly to Ruby string interpolation. For example,
+##
+##%p This is #{h quality} cake!
+##
+##is the same as
+##
+##%p= "This is the #{h quality} cake!"
+##
+##and might compile to
+##
+##<p>This is scrumptious cake!</p>
+##
+##Backslashes can be used to escape #{ strings, but they don’t act as escapes anywhere else in the string. For example:
+##
+##%p
+## Look at \\#{h word} lack of backslash: \#{foo}
+## And yon presence thereof: \{foo}
+##
+##might compile to
+##
+##<p>
+## Look at \yon lack of backslash: #{foo}
+## And yon presence thereof: \{foo}
+##</p>
+##
+##Interpolation can also be used within filters. For example:
+##
+##:javascript
+## $(document).ready(function() {
+## alert(#{@message.to_json});
+## });
+##
+##might compile to
+##
+##<script type='text/javascript'>
+## //<![CDATA[
+## $(document).ready(function() {
+## alert("Hi there!");
+## });
+## //]]>
+##</script>
84 t/perl.t
@@ -0,0 +1,84 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Text::Haml;
+
+use Test::More tests => 7;
+
+my $haml = Text::Haml->new;
+
+# Inserting Perl: =
+my $output = $haml->render(<<'EOF');
+%p
+ = join(' ', 'hi', 'there', 'reader!')
+ = "yo"
+EOF
+is($output, <<'EOF');
+<p>
+ hi there reader!
+ yo
+</p>
+EOF
+
+$output = $haml->render(<<'EOF');
+= '<script>alert("I\'m evil!");</script>'
+EOF
+is($output, <<'EOF');
+&lt;script&gt;alert(&quot;I&apos;m evil!&quot;);&lt;/script&gt;
+EOF
+
+$output = $haml->render('%p= "hello"');
+is($output, '<p>hello</p>');
+
+$output = $haml->render(<<'EOF');
+= 'foo' if 1
+EOF
+is($output, <<'EOF');
+foo
+EOF
+
+# Running Perl: -
+$output = $haml->render(<<'EOF');
+- my $foo = "hello";
+- $foo .= " there";
+- $foo .= " you!";
+%p= $foo
+EOF
+is($output, <<'EOF');
+<p>hello there you!</p>
+EOF
+
+# Perl Blocks
+$output = $haml->render(<<'EOF');
+- for my $i (42..47) {
+ %p= $i
+- }
+%p See, I can count!
+EOF
+is($output, <<'EOF');
+ <p>42</p>
+ <p>43</p>
+ <p>44</p>
+ <p>45</p>
+ <p>46</p>
+ <p>47</p>
+<p>See, I can count!</p>
+EOF
+
+$output = $haml->render(<<'EOF');
+%p
+ - if (1) {
+ = "1!"
+ %b bonus
+ - } else {
+ = "2?"
+ - }
+EOF
+is($output, <<'EOF');
+<p>
+ 1!
+ <b>bonus</b>
+</p>
+EOF
79 t/plain.t
@@ -0,0 +1,79 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Text::Haml;
+
+use Test::More tests => 5;
+
+my $haml = Text::Haml->new;
+
+my $output = $haml->render(<<'EOF');
+%gee
+ %whiz
+ Wow this is cool!
+ %whiz
+ Wow this is cool!
+ %b bold
+EOF
+is($output, <<'EOF');
+<gee>
+ <whiz>
+ Wow this is cool!
+ </whiz>
+ <whiz>
+ Wow this is cool!
+ <b>bold</b>
+ </whiz>
+</gee>
+EOF
+
+$output = $haml->render(<<'EOF');
+%p
+ <div id="blah">Blah!</div>
+EOF
+is($output, <<'EOF');
+<p>
+ <div id="blah">Blah!</div>
+</p>
+EOF
+
+$output = $haml->render(<<'EOF');
+%p
+ %b
+ Bar
+EOF
+is($output, <<'EOF');
+<p>
+ <b></b>
+ Bar
+</p>
+EOF
+
+$output = $haml->render(<<'EOF');
+%p
+ %b
+ Bar
+ Foo
+EOF
+is($output, <<'EOF');
+<p>
+ <b>
+ Bar
+ Foo
+ </b>
+</p>
+EOF
+
+$output = $haml->render(<<'EOF');
+%p
+ %b Bar
+ Foo
+EOF
+is($output, <<'EOF');
+<p>
+ <b>Bar</b>
+ Foo
+</p>
+EOF
34 t/self-closing.t
@@ -0,0 +1,34 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+
+use Text::Haml;
+
+my $haml = Text::Haml->new;
+
+# Self-Closing Tags: /
+my $output = $haml->render(<<'EOF');
+%br/
+%meta{'http-equiv' => 'Content-Type', :content => 'text/html'}/
+EOF
+is($output, <<'EOF');
+<br />
+<meta http-equiv='Content-Type' content='text/html' />
+EOF
+
+# Automatically closed tags
+$output = $haml->render(<<'EOF');
+%br
+%meta{'http-equiv' => 'Content-Type', :content => 'text/html'}
+%hr
+%img{src => 'logo.jpg'}
+EOF
+is($output, <<'EOF');
+<br />
+<meta http-equiv='Content-Type' content='text/html' />
+<hr />
+<img src='logo.jpg' />
+EOF
17 t/vars.t
@@ -0,0 +1,17 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+use Text::Haml;
+
+my $haml = Text::Haml->new;
+
+my $output = $haml->render(<<'EOF', foo => 'bar');
+= $foo
+EOF
+is($output, <<'EOF');
+bar
+EOF
34 t/whitespace-preservation.t
@@ -0,0 +1,34 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More skip_all => 'Not yet implemented';
+
+use Text::Haml;
+
+my $haml = Text::Haml->new;
+
+#my $output = $haml->render(<<'EOF');
+#~ "Foo\n<pre>Bar\nBaz</pre>"
+#EOF
+#is($output, <<'EOF');
+#Foo
+#<pre>Bar&#x000A;Baz</pre>
+#EOF
+
+#my $output = $haml->render(<<'EOF');
+#%code
+# foo
+# bar
+#
+# baz
+#EOF
+#is($output, <<'EOF');
+#<code>
+# foo
+# bar
+#
+# baz
+#</code>
+#EOF
50 t/whitespace-removal.t
@@ -0,0 +1,50 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More skip_all => 'Not yet implemented';
+
+use Text::Haml;
+
+my $haml = Text::Haml->new;
+
+my $output = $haml->render(<<'EOF');
+%blockquote<
+ %div
+ Foo!
+EOF
+is($output, <<'EOF');
+<blockquote><div>
+ Foo!
+</div></blockquote>
+EOF
+
+$output = $haml->render(<<'EOF');
+%img
+%img>
+%img
+EOF
+is($output, <<'EOF');
+<img /><img /><img />
+EOF
+
+$output = $haml->render(<<'EOF');
+%p<= "Foo\nBar"
+EOF
+is($output, <<'EOF');
+<p>Foo
+Bar</p>
+EOF
+
+$output = $haml->render(<<'EOF');
+%img
+%pre><
+ foo
+ bar
+%img
+EOF
+is($output, <<'EOF');
+<img /><pre>foo
+bar</pre><img />
+EOF
Please sign in to comment.
Something went wrong with that request. Please try again.