Creating the next set of branches.
That is, the buffered branch is being removed and replaced with the edge branch. This marks the beginning of the edge branch. git-svn-id: svn://hamptoncatlin.com/haml/branches/edge@80 7063305b-7217-0410-af8c-cdc13e5119b9
This commit is contained in:
parent
4266688e30
commit
89a10b9dfe
342
README
342
README
|
@ -22,6 +22,9 @@ HAML was originally created by Hampton Catlin (hcatlin). Help with the
|
||||||
Ruby On Rails implementation and much of the documentation by
|
Ruby On Rails implementation and much of the documentation by
|
||||||
Jeff Hardy (packagethief).
|
Jeff Hardy (packagethief).
|
||||||
|
|
||||||
|
Nathan Weizenbaum (Nex3) contribued the buffered-engine code along with many
|
||||||
|
other enhancements including the silent-line syntax ("-").
|
||||||
|
|
||||||
If you use this software, you must pay Hampton a compliment. Say something
|
If you use this software, you must pay Hampton a compliment. Say something
|
||||||
nice about it. Beyond that, the implementation is licensed under the MIT
|
nice about it. Beyond that, the implementation is licensed under the MIT
|
||||||
License. Ok, fine, I guess that means compliments aren't *required*.
|
License. Ok, fine, I guess that means compliments aren't *required*.
|
||||||
|
@ -50,13 +53,21 @@ is compiled to:
|
||||||
|
|
||||||
== Characters with meaning to Haml
|
== Characters with meaning to Haml
|
||||||
|
|
||||||
Haml responds to certain special characters. To create an element in the form of
|
Various characters, when placed at a certain point in a line, instruct HAML
|
||||||
<tt><element></element></tt> use the <tt>%</tt> character, immediately followed
|
to render different types of things.
|
||||||
by the element name. To specify attributes, include a hash of attributes inside
|
|
||||||
curly braces. Example:
|
=== XHTML Tags
|
||||||
|
|
||||||
|
These characters render XHTML tags.
|
||||||
|
|
||||||
|
==== %
|
||||||
|
|
||||||
|
This element is placed at the beginning of a line. It's followed immediately
|
||||||
|
by the name of an element, then optionally by modifiers (see below), a space,
|
||||||
|
and text to be rendered inside the element. It creates an element in the form of
|
||||||
|
<tt><element></element></tt>. For example:
|
||||||
|
|
||||||
%one
|
%one
|
||||||
%meta{:content => 'something'}/
|
|
||||||
%two
|
%two
|
||||||
%three Hey there
|
%three Hey there
|
||||||
|
|
||||||
|
@ -64,58 +75,163 @@ is compiled to:
|
||||||
|
|
||||||
<one>
|
<one>
|
||||||
<two>
|
<two>
|
||||||
<meta content='something' />
|
|
||||||
<three>Hey there</three>
|
<three>Hey there</three>
|
||||||
</two>
|
</two>
|
||||||
</one>
|
</one>
|
||||||
|
|
||||||
Any string is a valid element name; Haml will automatically generate opening and
|
Any string is a valid element name; Haml will automatically generate opening and
|
||||||
closing tags for any element. When you want to force the output of a
|
closing tags for any element.
|
||||||
self-closing tag, use the forward slash character. Example:
|
|
||||||
|
|
||||||
%br/ # => <br />
|
==== {}
|
||||||
%meta{:http-equiv => 'Content-Type', :content => 'text/html'}/
|
|
||||||
# => <meta http-equiv='Content-Type' content='text/html' />
|
|
||||||
|
|
||||||
HTML div elements are assumed when no <tt>%tag</tt> is present and the line is
|
Brackets represent a Ruby hash that is used for specifying the attributes of an
|
||||||
preceeded by either the <tt>#</tt> or the <tt>.</tt> characters. This convention
|
element. It is literally evaluated as a Ruby hash, so logic will work in it. At
|
||||||
uses familiar CSS semantics: <tt>#</tt> denotes the id of the element,
|
the moment, though, it doesn't see local variables. The hash is placed after
|
||||||
<tt>.</tt> denotes its class name. Example:
|
the tag is defined. For example:
|
||||||
|
|
||||||
#collection
|
%head{ :name => "doc_head" }
|
||||||
.item
|
%script{ 'type' => "text/" + "javascript", :src => "javascripts/script_#{2 + 7}" }
|
||||||
Broken record album
|
|
||||||
|
|
||||||
is the same as:
|
is compiled to:
|
||||||
|
|
||||||
%div{:id => collection}
|
<head name="doc_head">
|
||||||
%div{:class => 'item'}
|
<script src='javascripts/script_9' type='text/javascript'>
|
||||||
Broken record album
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
and is comiled to:
|
==== []
|
||||||
|
|
||||||
<div id='collection'>
|
Square brackets follow a tag definiton and contain a Ruby object that is used to
|
||||||
<div class='item'>Broken record album</div>
|
set the class and id of that tag. The class is set to the object's class
|
||||||
|
(transformed to use underlines rather than camel case), and the id is set to the
|
||||||
|
object's class followed by its id. Because the id of an object is normally an
|
||||||
|
obscure implementation detail, this is most useful for elements that represent
|
||||||
|
instances of Models. For example:
|
||||||
|
|
||||||
|
# file: app/controllers/users_controller.rb
|
||||||
|
|
||||||
|
def show
|
||||||
|
@user = CrazyUser.find(15)
|
||||||
|
end
|
||||||
|
|
||||||
|
# file: app/views/users/show.haml
|
||||||
|
|
||||||
|
%div[@user]
|
||||||
|
%bar[290]/
|
||||||
|
Hello!
|
||||||
|
|
||||||
|
is compiled to:
|
||||||
|
|
||||||
|
<div class="crazy_user" id="crazy_user_15">
|
||||||
|
<bar class="fixnum" id="fixnum_581" />
|
||||||
|
Hello!
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
There is a shortcut when you want to specify either the id or class attributes
|
This is based off of DHH's SimplyHelpful syntax as presented at RailsConf Europe 2006.
|
||||||
of an element: follow the element name with either the <tt>#</tt> or the
|
|
||||||
<tt>.</tt> characters. Example:
|
|
||||||
|
|
||||||
#things
|
==== /
|
||||||
|
|
||||||
|
The forward slash character, when placed at the end of a tag definition, causes
|
||||||
|
the tag to be self-closed. For example:
|
||||||
|
|
||||||
|
%br/
|
||||||
|
%meta{:http-equiv => 'Content-Type', :content => 'text/html'}/
|
||||||
|
|
||||||
|
is compiled to:
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<meta http-equiv='Content-Type' content='text/html' />
|
||||||
|
|
||||||
|
==== . and #
|
||||||
|
|
||||||
|
The period and pound sign are borrowed from CSS and used as shortcuts to specify the
|
||||||
|
<tt>class</tt> and <tt>id</tt> attributes of an element, respectively. They are
|
||||||
|
placed immediately after the tag, and before an attributes hash. For example:
|
||||||
|
|
||||||
|
div#things
|
||||||
%span#rice Chicken Fried
|
%span#rice Chicken Fried
|
||||||
%p.beans The magical fruit
|
%p.beans{ :food => 'true' } The magical fruit
|
||||||
|
%h1.class#id La La La
|
||||||
|
|
||||||
is compiled to:
|
is compiled to:
|
||||||
|
|
||||||
<div id='things'>
|
<div id='things'>
|
||||||
<span id='rice'>Chicken Fried</span>
|
<span id='rice'>Chicken Fried</span>
|
||||||
<p class='beans'>The magical fruit</p>
|
<p class='beans' food='true'>The magical fruit</p>
|
||||||
|
<h1 class='class' id='id'>La La La</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
=== Specifying a document type
|
==== Assumed Divs
|
||||||
|
|
||||||
When describing xhtml documents with Haml, you can have a document type
|
Because the div element is used so often, it is the default element. If you only
|
||||||
|
define a class and/or id using the <tt>.</tt> or <tt>#</tt> syntax, a div element
|
||||||
|
is automatically used. For example:
|
||||||
|
|
||||||
|
#collection
|
||||||
|
.item
|
||||||
|
.description What a cool item!
|
||||||
|
|
||||||
|
is the same as:
|
||||||
|
|
||||||
|
%div{:id => collection}
|
||||||
|
%div{:class => 'item'}
|
||||||
|
%div{:class => 'description'} What a cool item!
|
||||||
|
|
||||||
|
and is compiled to:
|
||||||
|
|
||||||
|
<div id='collection'>
|
||||||
|
<div class='item'>Broken record album</div>
|
||||||
|
<div class='description'>What a cool item!</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
==== = and ~
|
||||||
|
|
||||||
|
<tt>=</tt> and <tt>~</tt> are placed at the end of a tag definition, after class,
|
||||||
|
id, and attribute declarations. They're just shortcuts for inserting Ruby code
|
||||||
|
into an element. They work the same as <tt>=</tt> and <tt>~</tt> without a tag;
|
||||||
|
see below for documentation of those. For example:
|
||||||
|
|
||||||
|
%p= "hello"
|
||||||
|
%h1~ 1 + 2
|
||||||
|
|
||||||
|
is the same as:
|
||||||
|
|
||||||
|
%p
|
||||||
|
= "hello"
|
||||||
|
%h1
|
||||||
|
~ 1 + 2
|
||||||
|
|
||||||
|
and is compiled to:
|
||||||
|
|
||||||
|
<p>
|
||||||
|
hello
|
||||||
|
</p>
|
||||||
|
<h1>
|
||||||
|
3
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
=== XHTML Helpers
|
||||||
|
|
||||||
|
==== No Special Character
|
||||||
|
|
||||||
|
If no special character appears at the beginning of a line, it is rendered as plain
|
||||||
|
text. For example:
|
||||||
|
|
||||||
|
%gee
|
||||||
|
%whiz
|
||||||
|
Wow this is cool!
|
||||||
|
|
||||||
|
is compiled to:
|
||||||
|
|
||||||
|
<gee>
|
||||||
|
<whiz>
|
||||||
|
Wow this is cool!
|
||||||
|
</whiz>
|
||||||
|
</gee>
|
||||||
|
|
||||||
|
==== !!!
|
||||||
|
|
||||||
|
When describing XHTML documents with Haml, you can have a document type
|
||||||
generated automatically by including the characters <tt>!!!</tt> as the first
|
generated automatically by including the characters <tt>!!!</tt> as the first
|
||||||
line in your document. Example:
|
line in your document. Example:
|
||||||
|
|
||||||
|
@ -140,10 +256,157 @@ is compiled to:
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
==== /
|
||||||
|
|
||||||
|
The forward slash character, when placed at the beginning of a line, wraps all
|
||||||
|
text after it in an HTML comment. For example:
|
||||||
|
|
||||||
|
%billabong
|
||||||
|
/ This is the billabong element
|
||||||
|
I like billabongs!
|
||||||
|
|
||||||
|
is compiled to:
|
||||||
|
|
||||||
|
<billabong>
|
||||||
|
<!-- This is the billabong element -->
|
||||||
|
I like billabongs!
|
||||||
|
</billabong>
|
||||||
|
|
||||||
|
==== |
|
||||||
|
|
||||||
|
The pipe character designates a multiline string. It's placed at the end of a line,
|
||||||
|
and means that all following lines that end with <tt>|</tt> will be evaluated as
|
||||||
|
though they were on the same line. For example:
|
||||||
|
|
||||||
|
%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.
|
||||||
|
|
||||||
|
is compiled to:
|
||||||
|
|
||||||
|
%hoo I think this might get |
|
||||||
|
pretty long so I should |
|
||||||
|
probably make it |
|
||||||
|
multiline so it doesn't |
|
||||||
|
look awful. |
|
||||||
|
|
||||||
|
=== Ruby evaluators
|
||||||
|
|
||||||
|
==== =
|
||||||
|
|
||||||
|
The equals character is followed by Ruby code, which is evaluated and the output
|
||||||
|
inserted into the document as plain text. For example:
|
||||||
|
|
||||||
|
%p
|
||||||
|
= ['hi', 'there', 'reader!'].join " "
|
||||||
|
= "yo"
|
||||||
|
|
||||||
|
is compiled to:
|
||||||
|
|
||||||
|
<p>
|
||||||
|
hi there reader!
|
||||||
|
yo
|
||||||
|
</p>
|
||||||
|
|
||||||
|
==== ~
|
||||||
|
|
||||||
|
The tilde character works the same as the equals character, but the output is
|
||||||
|
modified in such a way that newlines in whitespace-sensitive elements work
|
||||||
|
properly. For example:
|
||||||
|
|
||||||
|
%foo
|
||||||
|
= "Woah <pre> this is \n</pre> crazy"
|
||||||
|
%foo2
|
||||||
|
~ "Woah <pre> this is \n</pre> crazy"
|
||||||
|
|
||||||
|
is compiled to:
|
||||||
|
|
||||||
|
<foo>
|
||||||
|
Woah <pre> this is
|
||||||
|
</pre> crazy
|
||||||
|
</foo>
|
||||||
|
<foo2>
|
||||||
|
Woah <pre> this is 
</pre> crazy
|
||||||
|
</foo2>
|
||||||
|
|
||||||
|
==== -
|
||||||
|
|
||||||
|
The hyphen character makes the text following it into "silent script", or
|
||||||
|
Ruby script that is evaluated, but not output.
|
||||||
|
|
||||||
|
<b>It is not reccomended that you use this widely; almost all processing
|
||||||
|
code and logic should be kept to the Controller, the Helper, or partials.</b>
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
- foo = "hello"
|
||||||
|
- foo << " there"
|
||||||
|
- foo << " you!"
|
||||||
|
%p= foo
|
||||||
|
|
||||||
|
is compiled to:
|
||||||
|
|
||||||
|
<p>
|
||||||
|
hello there you!
|
||||||
|
</p>
|
||||||
|
|
||||||
|
===== Blocks
|
||||||
|
|
||||||
|
Like XHTML tags, you don't need to explicity close your Ruby blocks in
|
||||||
|
HAML. Rather, they're automatically closed based on tabs. A block begins
|
||||||
|
whenever the indentation is increased after a silent script command, and
|
||||||
|
ends when the indentation decreases (as long as it's not an +else+ clause
|
||||||
|
or something similar). For example:
|
||||||
|
|
||||||
|
- (42...47).each do |i|
|
||||||
|
%p= i
|
||||||
|
%p See, I can count!
|
||||||
|
|
||||||
|
is compiled to:
|
||||||
|
|
||||||
|
<p>
|
||||||
|
42
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
43
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
44
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
45
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
46
|
||||||
|
</p>
|
||||||
|
|
||||||
|
Another example:
|
||||||
|
|
||||||
|
%p
|
||||||
|
- case 2
|
||||||
|
- when 1
|
||||||
|
= "1!"
|
||||||
|
- when 2
|
||||||
|
= "2?"
|
||||||
|
- when 3
|
||||||
|
= "3."
|
||||||
|
|
||||||
|
is compiled to:
|
||||||
|
|
||||||
|
<p>
|
||||||
|
2?
|
||||||
|
</p>
|
||||||
|
|
||||||
== Using Haml as a Rails plugin
|
== Using Haml as a Rails plugin
|
||||||
|
|
||||||
Write Rails templates with the .haml extension. Example:
|
Write Rails templates with the .haml extension. Example:
|
||||||
|
|
||||||
|
# file: app/views/movies/teen_wolf.haml
|
||||||
|
|
||||||
%html
|
%html
|
||||||
%head
|
%head
|
||||||
%title= "Teen Wolf (1985)"
|
%title= "Teen Wolf (1985)"
|
||||||
|
@ -176,12 +439,9 @@ is compiled to:
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
You can access instance variables in Haml templates the same way you do in ERb
|
You can access instance variables in Haml templates the same way you do in ERb
|
||||||
templates. Helper methods are also available in Haml templates. To specify that
|
templates. Helper methods are also available in Haml templates. Example:
|
||||||
a line should be evaulated as Ruby, use the <tt>=</tt> character at the begining
|
|
||||||
of a line, or immediately following an element name. The return value of the
|
|
||||||
method call will be inserted into the stream. Example:
|
|
||||||
|
|
||||||
file: app/controllers/movies_controller.rb
|
# file: app/controllers/movies_controller.rb
|
||||||
|
|
||||||
class MoviesController < ApplicationController
|
class MoviesController < ApplicationController
|
||||||
def index
|
def index
|
||||||
|
@ -189,14 +449,14 @@ method call will be inserted into the stream. Example:
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
file: app/views/movies/index.haml
|
# file: app/views/movies/index.haml
|
||||||
|
|
||||||
#content
|
#content
|
||||||
.title
|
.title
|
||||||
%h1= @title
|
%h1= @title
|
||||||
= link_to 'Home', home_url
|
= link_to 'Home', home_url
|
||||||
|
|
||||||
is be compiled to:
|
may be compiled to:
|
||||||
|
|
||||||
<div id='content'>
|
<div id='content'>
|
||||||
<div class='title'>
|
<div class='title'>
|
||||||
|
@ -206,8 +466,6 @@ is be compiled to:
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
Copyright (c) 2006 Hampton Catlin
|
Copyright (c) 2006 Hampton Catlin
|
||||||
Licensed under the MIT License
|
Licensed under the MIT License
|
||||||
|
|
86
Rakefile
86
Rakefile
|
@ -1,7 +1,19 @@
|
||||||
|
require 'rubygems'
|
||||||
require 'rake'
|
require 'rake'
|
||||||
require 'rake/testtask'
|
require 'rake/testtask'
|
||||||
require 'rake/rdoctask'
|
require 'rake/rdoctask'
|
||||||
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
|
||||||
|
volatile_requires = ['rcov/rcovtask']
|
||||||
|
not_loaded = []
|
||||||
|
volatile_requires.each do |file|
|
||||||
|
begin
|
||||||
|
require file
|
||||||
|
rescue LoadError
|
||||||
|
not_loaded.push file
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ----- Default: Testing ------
|
||||||
|
|
||||||
desc 'Default: run unit tests.'
|
desc 'Default: run unit tests.'
|
||||||
task :default => :test
|
task :default => :test
|
||||||
|
@ -13,23 +25,79 @@ Rake::TestTask.new(:test) do |t|
|
||||||
t.verbose = true
|
t.verbose = true
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'Benchmark HAML against ERb. The benchmark routine is run 100. Use TIMES=n to override'
|
# ----- Benchmarking -----
|
||||||
|
|
||||||
|
temp_desc = <<END
|
||||||
|
Benchmark HAML against ERb.
|
||||||
|
TIMES=n sets the number of runs. Defaults to 100.
|
||||||
|
END
|
||||||
|
desc temp_desc.chomp
|
||||||
task :benchmark do
|
task :benchmark do
|
||||||
puts '-'*51, "+ Benchmark: HAML vs. ERb", '-'*51
|
require 'test/benchmark'
|
||||||
|
|
||||||
|
puts '-'*51, "Benchmark: HAML vs. ERb", '-'*51
|
||||||
puts "Running benchmark #{ENV['TIMES']} times..." if ENV['TIMES']
|
puts "Running benchmark #{ENV['TIMES']} times..." if ENV['TIMES']
|
||||||
puts `ruby test/benchmark.rb #{ENV['TIMES']}`
|
args = []
|
||||||
|
args.push ENV['TIMES'].to_i if ENV['TIMES']
|
||||||
|
benchmarker = Haml::Benchmarker.new
|
||||||
|
puts benchmarker.benchmark(*args)
|
||||||
puts '-'*51
|
puts '-'*51
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'Generate documentation for the haml plugin.'
|
# ----- Documentation -----
|
||||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
|
||||||
rdoc.rdoc_dir = 'rdoc'
|
rdoc_task = Proc.new do |rdoc|
|
||||||
rdoc.title = 'Haml'
|
rdoc.title = 'Haml'
|
||||||
rdoc.options << '--line-numbers' << '--inline-source'
|
rdoc.options << '--line-numbers' << '--inline-source'
|
||||||
rdoc.rdoc_files.include('README')
|
rdoc.rdoc_files.include('README')
|
||||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||||
|
rdoc.rdoc_files.exclude('lib/haml/buffer.rb')
|
||||||
end
|
end
|
||||||
|
|
||||||
task :rcov do
|
Rake::RDocTask.new do |rdoc|
|
||||||
`rcov test/*.rb`
|
rdoc_task.call(rdoc)
|
||||||
|
rdoc.rdoc_dir = 'rdoc'
|
||||||
|
end
|
||||||
|
|
||||||
|
Rake::RDocTask.new(:rdoc_devel) do |rdoc|
|
||||||
|
rdoc_task.call(rdoc)
|
||||||
|
rdoc.rdoc_dir = 'rdoc_devel'
|
||||||
|
rdoc.options << '--all'
|
||||||
|
rdoc.rdoc_files.include('test/*.rb')
|
||||||
|
rdoc.rdoc_files = Rake::FileList.new(*rdoc.rdoc_files.to_a)
|
||||||
|
rdoc.rdoc_files.include('lib/haml/buffer.rb')
|
||||||
|
end
|
||||||
|
|
||||||
|
# ----- Coverage -----
|
||||||
|
|
||||||
|
unless not_loaded.include? 'rcov/rcovtask'
|
||||||
|
Rcov::RcovTask.new do |t|
|
||||||
|
t.libs << "test"
|
||||||
|
t.test_files = FileList['test/*_test.rb']
|
||||||
|
t.verbose = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ----- Profiling -----
|
||||||
|
|
||||||
|
temp_desc = <<END
|
||||||
|
Run a profile of HAML.
|
||||||
|
TIMES=n sets the number of runs. Defaults to 100.
|
||||||
|
FILE=n sets the file to profile. Defaults to 'standard'.
|
||||||
|
END
|
||||||
|
desc temp_desc.chomp
|
||||||
|
task :profile do
|
||||||
|
require 'test/profile'
|
||||||
|
|
||||||
|
puts '-'*51, "Profiling HAML::Template", '-'*51
|
||||||
|
|
||||||
|
args = []
|
||||||
|
args.push ENV['TIMES'].to_i if ENV['TIMES']
|
||||||
|
args.push ENV['FILE'] if ENV['FILE']
|
||||||
|
|
||||||
|
profiler = Haml::Profiler.new
|
||||||
|
res = profiler.profile(*args)
|
||||||
|
puts res
|
||||||
|
|
||||||
|
puts '-'*51
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
module Haml
|
||||||
|
# This class is used only internally. It holds the buffer of XHTML that
|
||||||
|
# is eventually output by Haml::Engine's to_html method. It's called
|
||||||
|
# from within the precompiled code, and helps reduce the amount of
|
||||||
|
# processing done within instance_eval'd code.
|
||||||
|
class Buffer
|
||||||
|
include Haml::Helpers
|
||||||
|
|
||||||
|
# Set the maximum length for a line to be considered a one-liner.
|
||||||
|
# Lines <= the maximum will be rendered on one line,
|
||||||
|
# i.e. <tt><p>Hello world</p></tt>
|
||||||
|
ONE_LINER_LENGTH = 50
|
||||||
|
|
||||||
|
# The string that holds the compiled XHTML. This is aliased as
|
||||||
|
# _erbout for compatibility with ERB-specific code.
|
||||||
|
attr_accessor :buffer
|
||||||
|
|
||||||
|
# Creates a new buffer.
|
||||||
|
def initialize
|
||||||
|
@buffer = ""
|
||||||
|
@one_liner_pending = false
|
||||||
|
end
|
||||||
|
|
||||||
|
# Renders +text+ with the proper tabulation. This also deals with
|
||||||
|
# making a possible one-line tag one line or not.
|
||||||
|
def push_text(text, tabulation)
|
||||||
|
if @one_liner_pending && one_liner?(text)
|
||||||
|
@buffer << text
|
||||||
|
else
|
||||||
|
if @one_liner_pending
|
||||||
|
@buffer << "\n"
|
||||||
|
@one_liner_pending = false
|
||||||
|
end
|
||||||
|
@buffer << "#{tabs(tabulation)}#{text}\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Properly formats the output of a script that was run in the
|
||||||
|
# instance_eval.
|
||||||
|
def push_script(result, tabulation, flattened)
|
||||||
|
if flattened
|
||||||
|
result = find_and_flatten(result)
|
||||||
|
end
|
||||||
|
unless result.nil?
|
||||||
|
result = result.to_s.chomp.gsub("\n", "\n#{tabs(tabulation)}")
|
||||||
|
push_text result, tabulation
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Takes the various information about the opening tag for an
|
||||||
|
# element, formats it, and adds it to the buffer.
|
||||||
|
def open_tag(name, tabulation, atomic, try_one_line, class_id, attributes_hash, obj_ref)
|
||||||
|
attributes = {}
|
||||||
|
attributes.merge!(parse_object_ref(obj_ref)) if obj_ref
|
||||||
|
attributes.merge!(parse_class_and_id(class_id)) if class_id
|
||||||
|
attributes.merge!(attributes_hash) unless attributes_hash.nil? || attributes_hash.empty?
|
||||||
|
|
||||||
|
@buffer << "#{tabs(tabulation)}<#{name}#{build_attributes(attributes)}"
|
||||||
|
@one_liner_pending = false
|
||||||
|
if atomic
|
||||||
|
@buffer << " />\n"
|
||||||
|
else
|
||||||
|
if try_one_line
|
||||||
|
@one_liner_pending = true
|
||||||
|
@buffer << ">"
|
||||||
|
else
|
||||||
|
@buffer << ">\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Creates a closing tag with the given name.
|
||||||
|
def close_tag(name, tabulation)
|
||||||
|
if @one_liner_pending
|
||||||
|
@buffer << "</#{name}>\n"
|
||||||
|
@one_liner_pending = false
|
||||||
|
else
|
||||||
|
push_text("</#{name}>", tabulation)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Gets <tt>count</tt> tabs. Mostly for internal use.
|
||||||
|
def tabs(count)
|
||||||
|
' ' * count
|
||||||
|
end
|
||||||
|
|
||||||
|
# Iterates through the classes and ids supplied through <tt>.</tt>
|
||||||
|
# and <tt>#</tt> syntax, and returns a hash with them as attributes,
|
||||||
|
# that can then be merged with another attributes hash.
|
||||||
|
def parse_class_and_id(list)
|
||||||
|
attributes = {}
|
||||||
|
list.scan(/([#.])([-a-zA-Z_()]+)/) do |type, property|
|
||||||
|
case type
|
||||||
|
when '.'
|
||||||
|
if attributes[:class]
|
||||||
|
attributes[:class] += " "
|
||||||
|
else
|
||||||
|
attributes[:class] = ""
|
||||||
|
end
|
||||||
|
attributes[:class] += property
|
||||||
|
when '#'
|
||||||
|
attributes[:id] = property
|
||||||
|
end
|
||||||
|
end
|
||||||
|
attributes
|
||||||
|
end
|
||||||
|
|
||||||
|
# Takes an array of objects and uses the class and id of the first
|
||||||
|
# one to create an attributes hash.
|
||||||
|
def parse_object_ref(ref)
|
||||||
|
ref = ref[0]
|
||||||
|
class_name = ref.class.to_s.underscore
|
||||||
|
{:id => "#{class_name}_#{ref.id}", :class => class_name}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Takes a hash and builds a list of XHTML attributes from it, returning
|
||||||
|
# the result.
|
||||||
|
def build_attributes(attributes = {})
|
||||||
|
result = attributes.collect do |a,v|
|
||||||
|
unless v.nil?
|
||||||
|
first_quote_type = v.to_s.scan(/['"]/).first
|
||||||
|
quote_type = (first_quote_type == "'") ? '"' : "'"
|
||||||
|
"#{a.to_s}=#{quote_type}#{v.to_s}#{quote_type}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
result = result.compact.join(' ')
|
||||||
|
(attributes.empty? ? String.new : String.new(' ')) + result
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns whether or not the given value is short enough to be rendered
|
||||||
|
# on one line.
|
||||||
|
def one_liner?(value)
|
||||||
|
value.length <= ONE_LINER_LENGTH && value.scan(/\n/).empty?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,198 +1,330 @@
|
||||||
require File.dirname(__FILE__) + '/helpers'
|
require File.dirname(__FILE__) + '/helpers'
|
||||||
|
require File.dirname(__FILE__) + '/buffer'
|
||||||
|
require 'profiler'
|
||||||
|
|
||||||
module Haml #:nodoc:
|
module Haml
|
||||||
|
# This is the class where all the parsing and processing of the HAML
|
||||||
|
# template is done. It can be directly used by the user by creating a
|
||||||
|
# new instance and calling to_html to render the template. For example:
|
||||||
|
#
|
||||||
|
# template = File.load('templates/really_cool_template.haml')
|
||||||
|
# haml_engine = Haml::Engine.new(template)
|
||||||
|
# output = haml_engine.to_html
|
||||||
|
# puts output
|
||||||
class Engine
|
class Engine
|
||||||
include Haml::Helpers
|
include Haml::Helpers
|
||||||
|
|
||||||
# Set the maximum length for a line to be considered a one-liner
|
# Keeps track of the ASCII values of the characters that begin a
|
||||||
# Lines <= the maximum will be rendered on one line,
|
# specially-interpreted line.
|
||||||
# i.e. <tt><p>Hello world</p></tt>
|
|
||||||
ONE_LINER_LENGTH = 50
|
|
||||||
SPECIAL_CHARACTERS = %w(# . = ~ % /).collect { |c| c[0] }
|
SPECIAL_CHARACTERS = %w(# . = ~ % /).collect { |c| c[0] }
|
||||||
|
|
||||||
|
# The value of the character that designates that a line is part
|
||||||
|
# of a multiline string.
|
||||||
MULTILINE_CHAR_VALUE = '|'[0]
|
MULTILINE_CHAR_VALUE = '|'[0]
|
||||||
|
|
||||||
|
# Characters that designate that a multiline string may be about
|
||||||
|
# to begin.
|
||||||
MULTILINE_STARTERS = SPECIAL_CHARACTERS - ["/"[0]]
|
MULTILINE_STARTERS = SPECIAL_CHARACTERS - ["/"[0]]
|
||||||
|
|
||||||
|
# Keywords that appear in the middle of a Ruby block with lowered
|
||||||
|
# indentation. If a block has been started using indentation,
|
||||||
|
# lowering the indentation with one of these won't end the block.
|
||||||
|
# For example:
|
||||||
|
#
|
||||||
|
# - if foo
|
||||||
|
# %p yes!
|
||||||
|
# - else
|
||||||
|
# %p no!
|
||||||
|
#
|
||||||
|
# The block is ended after <tt>%p no!</tt>, because <tt>else</tt>
|
||||||
|
# is a member of this array.
|
||||||
|
MID_BLOCK_KEYWORDS = ['else', 'elsif', 'rescue', 'ensure', 'when']
|
||||||
|
|
||||||
|
# Creates a new instace of Haml::Engine to compile the given
|
||||||
|
# template string.
|
||||||
|
#
|
||||||
|
# Available options are:
|
||||||
|
#
|
||||||
|
# [<tt>scope_object</tt>] The object within which the template will
|
||||||
|
# be compiled, via instance_eval. For a Rails
|
||||||
|
# application, this will typically be an
|
||||||
|
# instance of ActionView::Base. If not specified,
|
||||||
|
# this defaults to an instance of the Object class.
|
||||||
|
# [<tt>suppress_eval</tt>] Whether or not attribute hashes and Ruby scripts
|
||||||
|
# designated by <tt>=</tt> or <tt>~</tt> should be
|
||||||
|
# evaluated. If this is true, said scripts are
|
||||||
|
# rendered as empty strings. Defaults to false.
|
||||||
def initialize(template, options = {})
|
def initialize(template, options = {})
|
||||||
#turn each of the options into instance variables for the object
|
#turn each of the options into instance variables for the object
|
||||||
options.each { |k,v| eval("@#{k} = v") }
|
options.each { |k,v| eval("@#{k} = v") }
|
||||||
|
|
||||||
@template = template #String
|
@template = template #String
|
||||||
@result, @to_close_queue = String.new, []
|
@buffer = Haml::Buffer.new
|
||||||
|
@precompiled = String.new
|
||||||
|
@to_close_stack = []
|
||||||
|
@tabulation = 0
|
||||||
|
@scope_object = Object.new if @scope_object.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Processes the template and returns the resulting (X)HTML code as
|
||||||
|
# a string.
|
||||||
def to_html
|
def to_html
|
||||||
# Process each line of the template
|
# Process each line of the template
|
||||||
@template.each_with_index do |line, index|
|
@template.each_with_index do |line, index|
|
||||||
count, line = count_soft_tabs(line)
|
count, line = count_soft_tabs(line)
|
||||||
surpress_render, line, count = handle_multiline(count, line)
|
suppress_render = handle_multiline(count, line, index)
|
||||||
|
|
||||||
if !surpress_render && count && line
|
if !suppress_render && count && line
|
||||||
count, line = process_line(count, line)
|
count, line = process_line(count, line, index)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Make sure an ending multiline gets closed
|
||||||
|
handle_multiline(0, nil, 0)
|
||||||
|
|
||||||
# Close all the open tags
|
# Close all the open tags
|
||||||
@to_close_queue.length.times { close_tag }
|
@to_close_stack.length.times { close }
|
||||||
|
|
||||||
|
# Compile the @precompiled buffer
|
||||||
|
compile
|
||||||
|
|
||||||
# Return the result string
|
# Return the result string
|
||||||
@result
|
@buffer.buffer
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_line(count, line)
|
private
|
||||||
if line.strip[0, 3] == '!!!'
|
|
||||||
@result << %|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n|
|
# Processes a single line of HAML. <tt>count</tt> does *not* represent the
|
||||||
|
# line number; rather, it represents the tabulation count (the number of
|
||||||
|
# spaces before the line divided by two).
|
||||||
|
#
|
||||||
|
# This method doesn't return anything; it simply processes the line and
|
||||||
|
# adds the appropriate code to <tt>@precompiled</tt>.
|
||||||
|
def process_line(count, line, index)
|
||||||
|
if line.lstrip[0, 3] == '!!!'
|
||||||
|
push_text '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
|
||||||
|
|
||||||
else
|
else
|
||||||
if count <= @to_close_queue.size && @to_close_queue.size > 0
|
if count > @to_close_stack.size
|
||||||
(@to_close_queue.size - count).times { close_tag }
|
|
||||||
|
# Indentation has been increased without a new tag
|
||||||
|
if @latest_command == 45 # '-'
|
||||||
|
|
||||||
|
# The indentation was increased after silent script,
|
||||||
|
# it must be a block
|
||||||
|
@to_close_stack.push '_haml_end_block'
|
||||||
|
end
|
||||||
|
|
||||||
|
elsif count <= @to_close_stack.size && @to_close_stack.size > 0 &&
|
||||||
|
(line.length == 0 || line[0] != 45 || !MID_BLOCK_KEYWORDS.include?(line[1..-1].split[0]))
|
||||||
|
|
||||||
|
# The tabulation has gone down, and it's not because of one of
|
||||||
|
# Ruby's mid-block keywords
|
||||||
|
(@to_close_stack.size - count).times { close }
|
||||||
end
|
end
|
||||||
|
|
||||||
case line[0..0]
|
if line.length > 0
|
||||||
when '.', '#'
|
@latest_command = line[0]
|
||||||
render_div(line)
|
case @latest_command
|
||||||
when '%'
|
when 46, 35 # '.', '#'
|
||||||
render_tag(line)
|
render_div(line, index)
|
||||||
when '/'
|
when 37 # '%'
|
||||||
render_comment(line)
|
render_tag(line, index)
|
||||||
when '='
|
when 47 # '/'
|
||||||
add template_eval(line[1, line.length]).to_s
|
render_comment(line)
|
||||||
when '~'
|
when 61 # '='
|
||||||
add find_and_flatten(template_eval(line[1, line.length])).to_s
|
push_script(line[1..-1], false, index)
|
||||||
else
|
when 126 # '~'
|
||||||
add line.strip
|
push_script(line[1..-1], true, index)
|
||||||
|
when 45 # '-'
|
||||||
|
sub_line = line[1..-1]
|
||||||
|
unless sub_line[0] == 35 # '#'
|
||||||
|
push_silent(sub_line, index)
|
||||||
|
else
|
||||||
|
@latest_command = 35
|
||||||
|
end
|
||||||
|
else
|
||||||
|
push_text line.strip
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
return count, line
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_multiline(count, line)
|
# Deals with all the logic of figuring out whether a given line is
|
||||||
|
# the beginning, continuation, or end of a multiline sequence. Like
|
||||||
|
# process_line, <tt>count</tt> represents the tabulation, not line
|
||||||
|
# number.
|
||||||
|
#
|
||||||
|
# This returns whether or not the line should be
|
||||||
|
# rendered normally.
|
||||||
|
def handle_multiline(count, line, index)
|
||||||
# Multilines are denoting by ending with a `|` (124)
|
# Multilines are denoting by ending with a `|` (124)
|
||||||
if (line[-1] == MULTILINE_CHAR_VALUE) && @multiline_buffer
|
if line && (line[-1] == MULTILINE_CHAR_VALUE) && @multiline_buffer
|
||||||
# A multiline string is active, and is being continued
|
# A multiline string is active, and is being continued
|
||||||
@multiline_buffer += line[0...-1]
|
@multiline_buffer += line[0...-1]
|
||||||
supress_render = true
|
suppress_render = true
|
||||||
elsif (line[-1] == MULTILINE_CHAR_VALUE) && (MULTILINE_STARTERS.include? line[0])
|
elsif line && (line[-1] == MULTILINE_CHAR_VALUE) && (MULTILINE_STARTERS.include? line[0])
|
||||||
# A multiline string has just been activated, start adding the lines
|
# A multiline string has just been activated, start adding the lines
|
||||||
@multiline_buffer = line[0...-1]
|
@multiline_buffer = line[0...-1]
|
||||||
@multiline_count = count
|
@multiline_count = count
|
||||||
supress_render = true
|
@multiline_index = index
|
||||||
|
suppress_render = true
|
||||||
elsif @multiline_buffer
|
elsif @multiline_buffer
|
||||||
# A multiline string has just ended, make line into the result
|
# A multiline string has just ended, make line into the result
|
||||||
process_line(@multiline_count, @multiline_buffer)
|
process_line(@multiline_count, @multiline_buffer, @multiline_index)
|
||||||
@multiline_buffer = nil
|
@multiline_buffer = nil
|
||||||
supress_render = false
|
suppress_render = false
|
||||||
end
|
end
|
||||||
|
|
||||||
return supress_render, line, count
|
return suppress_render
|
||||||
end
|
end
|
||||||
|
|
||||||
def add(line)
|
# Takes <tt>@precompiled</tt>, a string buffer of Ruby code, and
|
||||||
return if line.nil?
|
# evaluates it in the context of <tt>@scope_object</tt>, after preparing
|
||||||
line.to_s.each_line do |me|
|
# <tt>@scope_object</tt>. The code in <tt>@precompiled</tt> populates
|
||||||
@result << tabs(@to_close_queue.size) << me.chomp << "\n"
|
# <tt>@buffer</tt> with the compiled XHTML code.
|
||||||
|
def compile
|
||||||
|
# Set the local variables pointing to the buffer
|
||||||
|
buffer = @buffer
|
||||||
|
@scope_object.instance_eval do
|
||||||
|
@haml_stack ||= Array.new
|
||||||
|
@haml_stack.push(buffer)
|
||||||
|
self.class.instance_eval { include Haml::Helpers }
|
||||||
|
|
||||||
|
class << self
|
||||||
|
attr :haml_lineno
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@precompiled = <<END
|
||||||
|
_hamlout = @haml_stack[-1]
|
||||||
|
_erbout = _hamlout.buffer
|
||||||
|
#{@precompiled}
|
||||||
|
END
|
||||||
|
|
||||||
|
# Evaluate the buffer in the context of the scope object
|
||||||
|
begin
|
||||||
|
@scope_object.instance_eval @precompiled
|
||||||
|
rescue Exception => e
|
||||||
|
filename = "(haml)"
|
||||||
|
if @scope_object.methods.include? "haml_filename"
|
||||||
|
# For some reason that I can't figure out,
|
||||||
|
# @scope_object.methods.include? "haml_filename" && @scope_object.haml_filename
|
||||||
|
# is false when it shouldn't be. Nested if statements work, though.
|
||||||
|
|
||||||
|
if @scope_object.haml_filename
|
||||||
|
filename = "#{@scope_object.haml_filename}.haml"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
e.backtrace.unshift "#{filename}:#{@scope_object.haml_lineno}"
|
||||||
|
raise e
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get rid of the current buffer
|
||||||
|
@scope_object.instance_eval do
|
||||||
|
@haml_stack.pop
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_attributes(attributes = {})
|
# Evaluates <tt>text</tt> in the context of <tt>@scope_object</tt>, but
|
||||||
result = attributes.collect { |a,v|
|
# does not output the result.
|
||||||
unless v.nil?
|
def push_silent(text, index = nil)
|
||||||
first_quote_type = v.to_s.scan(/['"]/).first
|
if index
|
||||||
quote_type = (first_quote_type == "'") ? '"' : "'"
|
@precompiled << "@haml_lineno = #{index + 1}\n#{text}\n"
|
||||||
"#{a.to_s}=#{quote_type}#{v.to_s}#{quote_type}"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
result = result.compact.join(' ')
|
|
||||||
(attributes.empty? ? String.new : String.new(' ')) + result
|
|
||||||
end
|
|
||||||
|
|
||||||
def open_tag(name, attributes = {})
|
|
||||||
add "<#{name.to_s}#{build_attributes(attributes)}>"
|
|
||||||
@to_close_queue.push name
|
|
||||||
end
|
|
||||||
|
|
||||||
def close_tag
|
|
||||||
add "</#{@to_close_queue.pop}>"
|
|
||||||
end
|
|
||||||
|
|
||||||
def one_line_tag(name, value, attributes = {})
|
|
||||||
add "<#{name.to_s}#{build_attributes(attributes)}>#{value}</#{name.to_s}>"
|
|
||||||
end
|
|
||||||
|
|
||||||
def one_liner?(value)
|
|
||||||
value.length <= ONE_LINER_LENGTH && value.scan(/\n/).empty?
|
|
||||||
end
|
|
||||||
|
|
||||||
def print_tag(name, value, attributes = {})
|
|
||||||
unless value.empty?
|
|
||||||
if one_liner? value
|
|
||||||
one_line_tag(name, value, attributes)
|
|
||||||
else
|
|
||||||
open_tag(name, attributes)
|
|
||||||
add value
|
|
||||||
close_tag
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
open_tag(name, attributes)
|
# Not really DRY, but probably faster
|
||||||
add value
|
@precompiled << "#{text}\n"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates single line tags, i.e. <tt><hello /></tt>
|
# Adds <tt>text</tt> to <tt>@buffer</tt> with appropriate tabulation
|
||||||
def atomic_tag(name, attributes = {})
|
# without parsing it.
|
||||||
add "<#{name.to_s}#{build_attributes(attributes)} />"
|
def push_text(text)
|
||||||
|
@precompiled << "_hamlout.push_text(#{text.dump}, #{@tabulation})\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse_class_and_id(list)
|
# Causes <tt>text</tt> to be evaluated in the context of
|
||||||
attributes = {}
|
# <tt>@scope_object</tt> and the result to be added to <tt>@buffer</tt>.
|
||||||
list.scan(/([#.])([-a-zA-Z_()]+)/).each do |type, property|
|
#
|
||||||
case type
|
# If <tt>flattened</tt> is true, Haml::Helpers#find_and_flatten is run on
|
||||||
when '.'
|
# the result before it is added to <tt>@buffer</tt>
|
||||||
attributes[:class] = property
|
def push_script(text, flattened, index)
|
||||||
when '#'
|
unless @suppress_eval
|
||||||
attributes[:id] = property
|
push_silent("haml_temp = #{text}", index)
|
||||||
end
|
@precompiled << "haml_temp = _hamlout.push_script(haml_temp, #{@tabulation}, #{flattened})\n"
|
||||||
end
|
end
|
||||||
attributes
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_tag(line)
|
# Closes the most recent item in <tt>@to_close_stack</tt>.
|
||||||
line.scan(/[%]([-_a-z1-9]+)([-_a-z\.\#]*)(\{.*\})?(\[.*\])?([=\/\~]?)?(.*)?/).each do |tag_name, attributes, attributes_hash, object_ref, action, value|
|
def close
|
||||||
attributes = parse_class_and_id(attributes.to_s)
|
tag = @to_close_stack.pop
|
||||||
|
if tag == '_haml_end_block'
|
||||||
|
close_block
|
||||||
|
else
|
||||||
|
close_tag tag
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
#SimplyHelpful style logic with the [@model] helper
|
# Puts a line in <tt>@precompiled</tt> that will add the closing tag of
|
||||||
if object_ref && (object_ref = template_eval(object_ref).first)
|
# the most recently opened tag.
|
||||||
class_name = object_ref.class.to_s.underscore
|
def close_tag(tag)
|
||||||
attributes.merge!(:id => "#{class_name}_#{object_ref.id}", :class => class_name)
|
@tabulation -= 1
|
||||||
end
|
@precompiled << "_hamlout.close_tag(#{tag.dump}, #{@tabulation})\n"
|
||||||
|
end
|
||||||
|
|
||||||
unless (attributes_hash.nil? || attributes_hash.empty?)
|
# Closes a Ruby block.
|
||||||
# Determine whether to eval the attributes hash in the context of a template
|
def close_block
|
||||||
add_attributes = template_eval(attributes_hash)
|
push_silent "end"
|
||||||
attributes.merge!(add_attributes)
|
end
|
||||||
end
|
|
||||||
|
# Parses a line that will render as an XHTML tag, and adds the code that will
|
||||||
|
# render that tag to <tt>@precompiled</tt>.
|
||||||
|
def render_tag(line, index)
|
||||||
|
line.scan(/[%]([-_a-z1-9]+)([-_a-z\.\#]*)(\{.*\})?(\[.*\])?([=\/\~]?)?(.*)?/) do |tag_name, attributes, attributes_hash, object_ref, action, value|
|
||||||
|
value = value.to_s
|
||||||
|
|
||||||
case action
|
case action
|
||||||
when '/'
|
when '/'
|
||||||
atomic_tag(tag_name, attributes)
|
atomic = true
|
||||||
when '=', '~'
|
when '=', '~'
|
||||||
value = template_eval(value)
|
flattened = (action == '~')
|
||||||
value = find_and_flatten(value) if action == '~'
|
parse = true
|
||||||
print_tag(tag_name, value.to_s, attributes)
|
|
||||||
else
|
else
|
||||||
print_tag(tag_name, value.to_s.strip, attributes)
|
value = value.strip
|
||||||
|
end
|
||||||
|
|
||||||
|
value_exists = !value.empty?
|
||||||
|
attributes_hash = "nil" unless attributes_hash
|
||||||
|
object_ref = "nil" unless object_ref
|
||||||
|
|
||||||
|
@precompiled << "_hamlout.open_tag(#{tag_name.inspect}, #{@tabulation}, #{atomic.inspect}, #{value_exists.inspect}, #{attributes.inspect}, #{attributes_hash}, #{object_ref})\n"
|
||||||
|
|
||||||
|
unless atomic
|
||||||
|
@to_close_stack.push tag_name
|
||||||
|
@tabulation += 1
|
||||||
|
|
||||||
|
if value_exists
|
||||||
|
if parse
|
||||||
|
push_script(value, flattened, index)
|
||||||
|
else
|
||||||
|
push_text(value)
|
||||||
|
end
|
||||||
|
close
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_div(line)
|
# Renders a line that creates an XHTML tag and has an implicit div because of
|
||||||
render_tag('%div' + line)
|
# <tt>.</tt> or <tt>#</tt>.
|
||||||
|
def render_div(line, index)
|
||||||
|
render_tag('%div' + line, index)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Renders an XHTML comment.
|
||||||
def render_comment(line)
|
def render_comment(line)
|
||||||
add "<!-- #{line[1..line.length].strip} -->"
|
push_text "<!-- #{line[1..line.length].strip} -->"
|
||||||
end
|
|
||||||
|
|
||||||
def template_eval(args)
|
|
||||||
!@suppress_eval ? @scope_object.instance_eval(args) : ""
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,18 @@
|
||||||
module Haml
|
module Haml
|
||||||
|
# This module contains various helpful methods to make it easier to do
|
||||||
|
# various tasks. Haml::Helpers is automatically included in the context
|
||||||
|
# that a HAML template is parsed in, so all these methods are at your
|
||||||
|
# disposal from within the template.
|
||||||
module Helpers
|
module Helpers
|
||||||
# Flatten will take any string, find all the endlines (via \n)
|
# Takes any string, finds all the endlines and converts them to
|
||||||
# and convert them to html entities for endlines.
|
# html entities for endlines so they'll render correctly in
|
||||||
|
# whitespace-sensitive tags.
|
||||||
def flatten(input)
|
def flatten(input)
|
||||||
input.gsub(/\n/, '
').gsub(/\r/, '')
|
input.gsub(/\n/, '
').gsub(/\r/, '')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Isolates the whitespace-sensitive tags in the string and uses flatten
|
||||||
|
# to convert any endlines inside them into html entities.
|
||||||
def find_and_flatten(input)
|
def find_and_flatten(input)
|
||||||
input.scan(/<(textarea|code|pre)[^>]*>(.*?)<\/\1>/im).each do |thing|
|
input.scan(/<(textarea|code|pre)[^>]*>(.*?)<\/\1>/im).each do |thing|
|
||||||
input = input.gsub(thing[1], flatten(thing[1]))
|
input = input.gsub(thing[1], flatten(thing[1]))
|
||||||
|
@ -13,19 +20,15 @@ module Haml
|
||||||
input
|
input
|
||||||
end
|
end
|
||||||
|
|
||||||
def tabs(count)
|
# Counts the tabulation of a line. Mostly for internal use.
|
||||||
' ' * count
|
|
||||||
end
|
|
||||||
|
|
||||||
def count_soft_tabs(line)
|
def count_soft_tabs(line)
|
||||||
line.index(/[^ ]/) ? [line.index(/[^ ]/)/2, line.strip] : []
|
line.index(/[^ ]/) ? [line.index(/[^ ]/)/2, line.strip] : []
|
||||||
end
|
end
|
||||||
|
|
||||||
# List_for is a really nifty little helper that helps
|
# Takes an array and a block and iterates the array,
|
||||||
# cleanup your code. Basically, give it an array of
|
# yielding each element to the block and putting the
|
||||||
# objects, and then pass in a block that tells how
|
# result into <tt>li</tt> elements, creating a list
|
||||||
# what to put out, and you will get each block item
|
# of the results of the block. For example:
|
||||||
# in rows of <li> tags.
|
|
||||||
#
|
#
|
||||||
# For instance:
|
# For instance:
|
||||||
# list_of([['hello'], ['yall']]) { |i| i[0] }
|
# list_of([['hello'], ['yall']]) { |i| i[0] }
|
||||||
|
@ -36,7 +39,7 @@ module Haml
|
||||||
# <li>hello</li>
|
# <li>hello</li>
|
||||||
# <li>yall</li>
|
# <li>yall</li>
|
||||||
#
|
#
|
||||||
def list_of(array)
|
def list_of(array) # :yields: item
|
||||||
(array.collect { |i| "<li>#{yield(i)}</li>" }).join("\n")
|
(array.collect { |i| "<li>#{yield(i)}</li>" }).join("\n")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
require File.dirname(__FILE__) + '/engine'
|
require File.dirname(__FILE__) + '/engine'
|
||||||
|
require 'active_support'
|
||||||
|
require 'action_view'
|
||||||
|
|
||||||
module Haml
|
module Haml
|
||||||
class Template
|
class Template
|
||||||
|
@ -30,3 +32,15 @@ module Haml
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module ActionView
|
||||||
|
class Base
|
||||||
|
attr :haml_filename, true
|
||||||
|
|
||||||
|
alias haml_old_render_file render_file
|
||||||
|
def render_file(template_path, use_full_path = true, local_assigns = {})
|
||||||
|
@haml_filename = File.basename(template_path)
|
||||||
|
haml_old_render_file(template_path, use_full_path, local_assigns)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -1,14 +1,58 @@
|
||||||
require 'rubygems'
|
|
||||||
require 'action_view'
|
|
||||||
|
|
||||||
require File.dirname(__FILE__) + '/../lib/haml/template'
|
require File.dirname(__FILE__) + '/../lib/haml/template'
|
||||||
|
require 'rubygems'
|
||||||
|
require 'active_support'
|
||||||
|
require 'action_view'
|
||||||
|
require 'benchmark'
|
||||||
|
require 'stringio'
|
||||||
|
|
||||||
ActionView::Base.register_template_handler("haml", Haml::Template)
|
module Haml
|
||||||
@base = ActionView::Base.new(File.dirname(__FILE__))
|
class Benchmarker
|
||||||
|
|
||||||
RUNS = (ARGV[0] || 100).to_i
|
# Creates a new benchmarker that looks for templates in the base
|
||||||
|
# directory.
|
||||||
|
def initialize(base = File.dirname(__FILE__))
|
||||||
|
ActionView::Base.register_template_handler("haml", Haml::Template)
|
||||||
|
unless base.class == ActionView::Base
|
||||||
|
@base = ActionView::Base.new(base)
|
||||||
|
else
|
||||||
|
@base = base
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
Benchmark.bm do |b|
|
# Benchmarks HAML against ERb. If <tt>template_name</tt> is specified,
|
||||||
b.report("haml: ") { RUNS.times { @base.render "templates/standard" } }
|
# looks for a haml template in ./templates and an rhtml template in
|
||||||
b.report("erb: ") { RUNS.times { @base.render "rhtml/standard" } }
|
# ./rhtml with the name <tt>template_name</tt>. Otherwise, uses
|
||||||
|
# <tt>haml_template</tt> and <tt>rhtml_template</tt> as the location of
|
||||||
|
# the templates.
|
||||||
|
#
|
||||||
|
# Returns the results of the benchmarking as a string.
|
||||||
|
#
|
||||||
|
# :call-seq:
|
||||||
|
# benchmark(runs = 100, template_name = 'standard')
|
||||||
|
# benchmark(runs = 100, haml_template, rhtml_template)
|
||||||
|
#
|
||||||
|
def benchmark(runs = 100, template_name = 'standard', other_template = nil)
|
||||||
|
if other_template.nil?
|
||||||
|
haml_template = "templates/#{template_name}"
|
||||||
|
rhtml_template = "rhtml/#{template_name}"
|
||||||
|
else
|
||||||
|
haml_template = template_name
|
||||||
|
rhtml_template = other_template
|
||||||
|
end
|
||||||
|
|
||||||
|
old_stdout = $stdout
|
||||||
|
$stdout = StringIO.new
|
||||||
|
|
||||||
|
Benchmark.bmbm do |b|
|
||||||
|
b.report("haml:") { runs.times { @base.render haml_template } }
|
||||||
|
b.report("erb:") { runs.times { @base.render rhtml_template } }
|
||||||
|
end
|
||||||
|
|
||||||
|
$stdout.pos = 0
|
||||||
|
to_return = $stdout.read
|
||||||
|
$stdout = old_stdout
|
||||||
|
|
||||||
|
to_return
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
require File.dirname(__FILE__) + '/../lib/haml/engine'
|
require File.dirname(__FILE__) + '/../lib/haml/engine'
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
require File.dirname(__FILE__) + '/../lib/haml/helpers'
|
require File.dirname(__FILE__) + '/../lib/haml/helpers'
|
||||||
|
|
||||||
|
@ -24,11 +26,6 @@ class HelperTest < Test::Unit::TestCase
|
||||||
"<pre>Two
lines</pre>\n<pre>a
b
c</pre>")
|
"<pre>Two
lines</pre>\n<pre>a
b
c</pre>")
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_tabs_should_render_correctly
|
|
||||||
assert_equal(" ", tabs(1))
|
|
||||||
assert_equal(" ", tabs(5))
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_list_of_should_render_correctly
|
def test_list_of_should_render_correctly
|
||||||
assert_equal("<li>1</li>\n<li>2</li>", (list_of([1, 2]) { |i| i.to_s}))
|
assert_equal("<li>1</li>\n<li>2</li>", (list_of([1, 2]) { |i| i.to_s}))
|
||||||
assert_equal("<li>1</li>", (list_of([[1]]) { |i| i.first}))
|
assert_equal("<li>1</li>", (list_of([[1]]) { |i| i.first}))
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
require File.dirname(__FILE__) + '/../lib/haml/template'
|
||||||
|
require 'rubygems'
|
||||||
|
require 'active_support'
|
||||||
|
require 'action_view'
|
||||||
|
require 'profiler'
|
||||||
|
require 'stringio'
|
||||||
|
|
||||||
|
module Haml
|
||||||
|
# A profiler for HAML, mostly for development use. This simply implements
|
||||||
|
# the Ruby profiler for profiling HAML code.
|
||||||
|
class Profiler
|
||||||
|
|
||||||
|
# Creates a new profiler that looks for templates in the base
|
||||||
|
# directory.
|
||||||
|
def initialize(base = File.join(File.dirname(__FILE__), 'templates'))
|
||||||
|
ActionView::Base.register_template_handler("haml", Haml::Template)
|
||||||
|
unless base.class == ActionView::Base
|
||||||
|
@base = ActionView::Base.new(base)
|
||||||
|
else
|
||||||
|
@base = base
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Profiles HAML on the given template with the given number of runs.
|
||||||
|
# The template name shouldn't have a file extension; this will
|
||||||
|
# automatically look for a HAML template.
|
||||||
|
#
|
||||||
|
# Returns the results of the profiling as a string.
|
||||||
|
def profile(runs = 100, template_name = 'standard')
|
||||||
|
# Runs the profiler, collects information
|
||||||
|
Profiler__::start_profile
|
||||||
|
runs.times { @base.render template_name }
|
||||||
|
Profiler__::stop_profile
|
||||||
|
|
||||||
|
# Outputs information to a StringIO, returns result
|
||||||
|
io = StringIO.new
|
||||||
|
Profiler__::print_profile(io)
|
||||||
|
io.pos = 0
|
||||||
|
result = io.read
|
||||||
|
io.close
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -1 +1,25 @@
|
||||||
&&&&&&&&&&&
|
&&&&&&&&&&&
|
||||||
|
<div>
|
||||||
|
<p class='title'>Title</p>
|
||||||
|
<p class='text'>
|
||||||
|
Woah this is really crazy
|
||||||
|
I mean wow,
|
||||||
|
man.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class='title'>Title</p>
|
||||||
|
<p class='text'>
|
||||||
|
Woah this is really crazy
|
||||||
|
I mean wow,
|
||||||
|
man.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class='title'>Title</p>
|
||||||
|
<p class='text'>
|
||||||
|
Woah this is really crazy
|
||||||
|
I mean wow,
|
||||||
|
man.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
<div>
|
||||||
|
<h1>I can count!</h1>
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
4
|
||||||
|
5
|
||||||
|
6
|
||||||
|
7
|
||||||
|
8
|
||||||
|
9
|
||||||
|
10
|
||||||
|
11
|
||||||
|
12
|
||||||
|
13
|
||||||
|
14
|
||||||
|
15
|
||||||
|
16
|
||||||
|
17
|
||||||
|
18
|
||||||
|
19
|
||||||
|
20
|
||||||
|
<h1>I know my ABCs!</h1>
|
||||||
|
<ul>
|
||||||
|
<li>a</li>
|
||||||
|
<li>b</li>
|
||||||
|
<li>c</li>
|
||||||
|
<li>d</li>
|
||||||
|
<li>e</li>
|
||||||
|
<li>f</li>
|
||||||
|
<li>g</li>
|
||||||
|
<li>h</li>
|
||||||
|
<li>i</li>
|
||||||
|
<li>j</li>
|
||||||
|
<li>k</li>
|
||||||
|
<li>l</li>
|
||||||
|
<li>m</li>
|
||||||
|
<li>n</li>
|
||||||
|
<li>o</li>
|
||||||
|
<li>p</li>
|
||||||
|
<li>q</li>
|
||||||
|
<li>r</li>
|
||||||
|
<li>s</li>
|
||||||
|
<li>t</li>
|
||||||
|
<li>u</li>
|
||||||
|
<li>v</li>
|
||||||
|
<li>w</li>
|
||||||
|
<li>x</li>
|
||||||
|
<li>y</li>
|
||||||
|
<li>z</li>
|
||||||
|
</ul>
|
||||||
|
<h1>I can catch errors!</h1>
|
||||||
|
Oh no! "uninitialized constant Foo" happened!
|
||||||
|
<p>
|
||||||
|
"false" is:
|
||||||
|
false
|
||||||
|
</p>
|
||||||
|
</div>
|
|
@ -2,7 +2,7 @@
|
||||||
<html xml-lang='en-US'>
|
<html xml-lang='en-US'>
|
||||||
<head>
|
<head>
|
||||||
<title>Hampton Catlin Is Totally Awesome</title>
|
<title>Hampton Catlin Is Totally Awesome</title>
|
||||||
<meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
|
<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- You're In my house now! -->
|
<!-- You're In my house now! -->
|
||||||
|
@ -19,6 +19,17 @@
|
||||||
PipesIgnored|PipesIgnored|PipesIgnored|
|
PipesIgnored|PipesIgnored|PipesIgnored|
|
||||||
1|2|3
|
1|2|3
|
||||||
</p>
|
</p>
|
||||||
|
<div class='silent'>
|
||||||
|
this shouldn't evaluate but now it should!
|
||||||
|
</div>
|
||||||
|
<ul class='really cool'>
|
||||||
|
<li>a</li>
|
||||||
|
<li>b</li>
|
||||||
|
<li>c</li>
|
||||||
|
<li>d</li>
|
||||||
|
<li>e</li>
|
||||||
|
<li>f</li>
|
||||||
|
</ul>
|
||||||
<div class='of_divs_with_underscore' id='combo'>with this text</div>
|
<div class='of_divs_with_underscore' id='combo'>with this text</div>
|
||||||
<div class='footer'>
|
<div class='footer'>
|
||||||
<strong class='shout'>
|
<strong class='shout'>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
<html xml-lang='en-US'>
|
<html xml-lang='en-US'>
|
||||||
<head>
|
<head>
|
||||||
<title><%= "Hampton Catlin Is Totally Awesome" %></title>
|
<title>Hampton Catlin Is Totally Awesome</title>
|
||||||
<meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
|
<meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -11,8 +11,32 @@
|
||||||
Fantastic! This should be multi-line output
|
Fantastic! This should be multi-line output
|
||||||
The question is if this would translate! Ahah!
|
The question is if this would translate! Ahah!
|
||||||
<%= 1 + 9 + 8 + 2 %>
|
<%= 1 + 9 + 8 + 2 %>
|
||||||
|
<%# numbers should work and this should be ignored %>
|
||||||
</div>
|
</div>
|
||||||
<div id='body'><%= " Quotes should be loved! Just like people!" %></div>
|
<div id='body'><%= " Quotes should be loved! Just like people!" %></div>
|
||||||
|
Wow.
|
||||||
|
<p>
|
||||||
|
<%= "Holy cow " +
|
||||||
|
"multiline " +
|
||||||
|
"tags! " +
|
||||||
|
"A pipe (|) even!" %>
|
||||||
|
<%= [1, 2, 3].collect { |n| "PipesIgnored|" } %>
|
||||||
|
<%= [1, 2, 3].collect { |n|
|
||||||
|
n.to_s
|
||||||
|
}.join("|") %>
|
||||||
|
</p>
|
||||||
|
<div class='silent'>
|
||||||
|
<% foo = String.new
|
||||||
|
foo << "this"
|
||||||
|
foo << " shouldn't"
|
||||||
|
foo << " evaluate" %>
|
||||||
|
<%= foo + "but now it should!" %>
|
||||||
|
<%# Woah crap a comment! %>
|
||||||
|
</div>
|
||||||
|
<ul class='really cool'>
|
||||||
|
<% ('a'..'f').each do |a|%>
|
||||||
|
<li><%= a %>
|
||||||
|
<% end %>
|
||||||
<div class='of_divs_with_underscore' id='combo'><%= @should_eval = "with this text" %></div>
|
<div class='of_divs_with_underscore' id='combo'><%= @should_eval = "with this text" %></div>
|
||||||
<div class='footer'>
|
<div class='footer'>
|
||||||
<strong class='shout'>
|
<strong class='shout'>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
require 'active_support'
|
require 'active_support'
|
||||||
|
@ -7,6 +9,17 @@ require File.dirname(__FILE__) + '/../lib/haml/template'
|
||||||
require File.dirname(__FILE__) + '/mocks/article'
|
require File.dirname(__FILE__) + '/mocks/article'
|
||||||
|
|
||||||
class TemplateTest < Test::Unit::TestCase
|
class TemplateTest < Test::Unit::TestCase
|
||||||
|
# These are specific lines of templates that, for one reason or
|
||||||
|
# another, might not be exactly equivalent to the pre-rendered
|
||||||
|
# version.
|
||||||
|
EXCEPTIONS = {
|
||||||
|
'standard' => [
|
||||||
|
# Line 4 has many attributes; because attributes aren't sorted,
|
||||||
|
# this can vary unpredictably.
|
||||||
|
4
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
ActionView::Base.register_template_handler("haml", Haml::Template)
|
ActionView::Base.register_template_handler("haml", Haml::Template)
|
||||||
@base = ActionView::Base.new(File.dirname(__FILE__) + "/../test/templates/")
|
@base = ActionView::Base.new(File.dirname(__FILE__) + "/../test/templates/")
|
||||||
|
@ -24,8 +37,15 @@ class TemplateTest < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_renders_correctly(name)
|
def assert_renders_correctly(name)
|
||||||
load_result(name).split("\n").zip(@base.render(name).split("\n")).each do |pair|
|
load_result(name).split("\n").zip(@base.render(name).split("\n")).each_with_index do |pair, line|
|
||||||
assert_equal(pair.first, pair.last)
|
if (EXCEPTIONS['name'].nil? || EXCEPTIONS['name'].include?(line))
|
||||||
|
if pair.first != pair.last
|
||||||
|
puts "\nWarning: line #{line} of template \"#{name}\" may have rendered incorrectly."
|
||||||
|
end
|
||||||
|
else
|
||||||
|
message = "template: #{name}\nline: #{line}"
|
||||||
|
assert_equal(pair.first, pair.last, message)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -34,7 +54,8 @@ class TemplateTest < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_templates_should_render_correctly
|
def test_templates_should_render_correctly
|
||||||
%w{very_basic standard helpers whitespace_handling original_engine list helpful}.each do |template|
|
%w{very_basic standard helpers whitespace_handling
|
||||||
|
original_engine list helpful silent_script}.each do |template|
|
||||||
assert_renders_correctly template
|
assert_renders_correctly template
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -66,4 +87,30 @@ class TemplateTest < Test::Unit::TestCase
|
||||||
def test_template_renders_should_eval
|
def test_template_renders_should_eval
|
||||||
assert_equal("2\n", render("= 1+1"))
|
assert_equal("2\n", render("= 1+1"))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_exceptions_should_work_correctly
|
||||||
|
template = <<END
|
||||||
|
%p
|
||||||
|
%h1 Hello!
|
||||||
|
= "lots of lines"
|
||||||
|
- raise "Oh no!"
|
||||||
|
%p
|
||||||
|
this is after the exception
|
||||||
|
%strong yes it is!
|
||||||
|
ho ho ho.
|
||||||
|
END
|
||||||
|
@base.haml_filename = "(test)"
|
||||||
|
begin
|
||||||
|
render(template.chomp)
|
||||||
|
rescue Exception => e
|
||||||
|
assert_equal("(test).haml:4", e.backtrace[0])
|
||||||
|
end
|
||||||
|
|
||||||
|
@base.haml_filename = nil
|
||||||
|
begin
|
||||||
|
render(template.chomp)
|
||||||
|
rescue Exception => e
|
||||||
|
assert_equal("(haml):4", e.backtrace[0])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1 +1,10 @@
|
||||||
= h("&&&&&&&&&&&") #this is an ActionView Helper... should load
|
= h("&&&&&&&&&&&") # This is an ActionView Helper... should load
|
||||||
|
- foo = capture do # This ActionView Helper is designed for ERB, but should work with HAML
|
||||||
|
%div
|
||||||
|
%p.title Title
|
||||||
|
%p.text
|
||||||
|
Woah this is really crazy
|
||||||
|
I mean wow,
|
||||||
|
man.
|
||||||
|
- 3.times do
|
||||||
|
= foo
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
%div
|
||||||
|
%h1 I can count!
|
||||||
|
- (1..20).each do |i|
|
||||||
|
= i
|
||||||
|
%h1 I know my ABCs!
|
||||||
|
%ul
|
||||||
|
- ('a'..'z').each do |i|
|
||||||
|
%li= i
|
||||||
|
%h1 I can catch errors!
|
||||||
|
- begin
|
||||||
|
- Foo.silly
|
||||||
|
- rescue NameError => e
|
||||||
|
= "Oh no! \"#{e}\" happened!"
|
||||||
|
%p
|
||||||
|
"false" is:
|
||||||
|
- if false
|
||||||
|
= "true"
|
||||||
|
- else
|
||||||
|
= "false"
|
|
@ -21,6 +21,16 @@
|
||||||
= [1, 2, 3].collect { |n| |
|
= [1, 2, 3].collect { |n| |
|
||||||
n.to_s |
|
n.to_s |
|
||||||
}.join("|") |
|
}.join("|") |
|
||||||
|
%div.silent
|
||||||
|
- foo = String.new
|
||||||
|
- foo << "this"
|
||||||
|
- foo << " shouldn't"
|
||||||
|
- foo << " evaluate"
|
||||||
|
= foo + " but now it should!"
|
||||||
|
-# Woah crap a comment!
|
||||||
|
%ul.really.cool
|
||||||
|
- ('a'..'f').each do |a|
|
||||||
|
%li= a
|
||||||
#combo.of_divs_with_underscore= @should_eval = "with this text"
|
#combo.of_divs_with_underscore= @should_eval = "with this text"
|
||||||
.footer
|
.footer
|
||||||
%strong.shout= "This is a really long ruby quote. It should be loved and wrapped because its more than 50 characters. This value may change in the future and this test may look stupid. \nSo, I'm just making it *really* long. God, I hope this works"
|
%strong.shout= "This is a really long ruby quote. It should be loved and wrapped because its more than 50 characters. This value may change in the future and this test may look stupid. \nSo, I'm just making it *really* long. God, I hope this works"
|
||||||
|
|
Loading…
Reference in New Issue