haml 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of haml might be problematic. Click here for more details.
- data/MIT-LICENSE +20 -0
- data/REFERENCE +662 -0
- data/Rakefile +167 -0
- data/VERSION +1 -0
- data/bin/haml +18 -0
- data/lib/haml/buffer.rb +224 -0
- data/lib/haml/engine.rb +551 -0
- data/lib/haml/helpers.rb +220 -0
- data/lib/haml/helpers/action_view_mods.rb +53 -0
- data/lib/haml/template.rb +138 -0
- data/test/benchmark.rb +62 -0
- data/test/engine_test.rb +93 -0
- data/test/helper_test.rb +105 -0
- data/test/mocks/article.rb +6 -0
- data/test/profile.rb +45 -0
- data/test/results/content_for_layout.xhtml +16 -0
- data/test/results/eval_suppressed.xhtml +2 -0
- data/test/results/helpers.xhtml +50 -0
- data/test/results/helpful.xhtml +5 -0
- data/test/results/just_stuff.xhtml +36 -0
- data/test/results/list.xhtml +12 -0
- data/test/results/original_engine.xhtml +24 -0
- data/test/results/partials.xhtml +20 -0
- data/test/results/silent_script.xhtml +74 -0
- data/test/results/standard.xhtml +42 -0
- data/test/results/tag_parsing.xhtml +28 -0
- data/test/results/very_basic.xhtml +7 -0
- data/test/results/whitespace_handling.xhtml +51 -0
- data/test/rhtml/standard.rhtml +51 -0
- data/test/runner.rb +15 -0
- data/test/template_test.rb +137 -0
- data/test/templates/_partial.haml +7 -0
- data/test/templates/_text_area.haml +3 -0
- data/test/templates/content_for_layout.haml +10 -0
- data/test/templates/eval_suppressed.haml +5 -0
- data/test/templates/helpers.haml +39 -0
- data/test/templates/helpful.haml +6 -0
- data/test/templates/just_stuff.haml +29 -0
- data/test/templates/list.haml +12 -0
- data/test/templates/original_engine.haml +17 -0
- data/test/templates/partialize.haml +1 -0
- data/test/templates/partials.haml +12 -0
- data/test/templates/silent_script.haml +40 -0
- data/test/templates/standard.haml +40 -0
- data/test/templates/tag_parsing.haml +24 -0
- data/test/templates/very_basic.haml +4 -0
- data/test/templates/whitespace_handling.haml +66 -0
- metadata +108 -0
data/lib/haml/helpers.rb
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helpers/action_view_mods'
|
2
|
+
|
3
|
+
module Haml
|
4
|
+
# This module contains various helpful methods to make it easier to do
|
5
|
+
# various tasks. Haml::Helpers is automatically included in the context
|
6
|
+
# that a Haml template is parsed in, so all these methods are at your
|
7
|
+
# disposal from within the template.
|
8
|
+
module Helpers
|
9
|
+
self.extend self
|
10
|
+
|
11
|
+
@@action_view = false
|
12
|
+
@@force_no_action_view = false
|
13
|
+
|
14
|
+
# Returns whether or not ActionView is installed on the system.
|
15
|
+
def self.action_view?
|
16
|
+
@@action_view
|
17
|
+
end
|
18
|
+
|
19
|
+
# Takes any string, finds all the endlines and converts them to
|
20
|
+
# HTML entities for endlines so they'll render correctly in
|
21
|
+
# whitespace-sensitive tags without screwing up the indentation.
|
22
|
+
def flatten(input)
|
23
|
+
input.gsub(/\n/, '
').gsub(/\r/, '')
|
24
|
+
end
|
25
|
+
|
26
|
+
# Takes an Enumerable object and a block
|
27
|
+
# and iterates over the object,
|
28
|
+
# yielding each element to a Haml block
|
29
|
+
# and putting the result into <tt><li></tt> elements.
|
30
|
+
# This creates a list of the results of the block.
|
31
|
+
# For example:
|
32
|
+
#
|
33
|
+
# = list_of([['hello'], ['yall']]) do |i|
|
34
|
+
# = i[0]
|
35
|
+
#
|
36
|
+
# Produces:
|
37
|
+
#
|
38
|
+
# <li>hello</li>
|
39
|
+
# <li>yall</li>
|
40
|
+
#
|
41
|
+
# And
|
42
|
+
#
|
43
|
+
# = list_of({:title => 'All the stuff', :description => 'A book about all the stuff.'}) do |key, val|
|
44
|
+
# %h3= key.humanize
|
45
|
+
# %p= val
|
46
|
+
#
|
47
|
+
# Produces:
|
48
|
+
#
|
49
|
+
# <li>
|
50
|
+
# <h3>Title</h3>
|
51
|
+
# <p>All the stuff</p>
|
52
|
+
# </li>
|
53
|
+
# <li>
|
54
|
+
# <h3>Description</h3>
|
55
|
+
# <p>A book about all the stuff.</p>
|
56
|
+
# </li>
|
57
|
+
#
|
58
|
+
def list_of(array, &block) # :yields: item
|
59
|
+
to_return = array.collect do |i|
|
60
|
+
result = capture_haml(i, &block)
|
61
|
+
|
62
|
+
if result.count("\n") > 1
|
63
|
+
result.gsub!("\n", "\n ")
|
64
|
+
result = "\n #{result.strip}\n"
|
65
|
+
else
|
66
|
+
result.strip!
|
67
|
+
end
|
68
|
+
|
69
|
+
"<li>#{result}</li>"
|
70
|
+
end
|
71
|
+
to_return.join("\n")
|
72
|
+
end
|
73
|
+
|
74
|
+
# Increments the number of tabs the buffer automatically adds
|
75
|
+
# to the lines of the template.
|
76
|
+
# For example:
|
77
|
+
#
|
78
|
+
# %h1 foo
|
79
|
+
# - tab_up
|
80
|
+
# %p bar
|
81
|
+
# - tab_down
|
82
|
+
# %strong baz
|
83
|
+
#
|
84
|
+
# Produces:
|
85
|
+
#
|
86
|
+
# <h1>foo</h1>
|
87
|
+
# <p>bar</p>
|
88
|
+
# <strong>baz</strong>
|
89
|
+
#
|
90
|
+
def tab_up(i = 1)
|
91
|
+
buffer.tabulation += i
|
92
|
+
end
|
93
|
+
|
94
|
+
# Increments the number of tabs the buffer automatically adds
|
95
|
+
# to the lines of the template.
|
96
|
+
#
|
97
|
+
# See tab_up.
|
98
|
+
def tab_down(i = 1)
|
99
|
+
buffer.tabulation -= i
|
100
|
+
end
|
101
|
+
|
102
|
+
# Surrounds the given block of Haml code with the given characters,
|
103
|
+
# with no whitespace in between.
|
104
|
+
# For example:
|
105
|
+
#
|
106
|
+
# = surround '(', ')' do
|
107
|
+
# %a{:href => "food"} chicken
|
108
|
+
#
|
109
|
+
# Produces:
|
110
|
+
#
|
111
|
+
# (<a href='food'>chicken</a>)
|
112
|
+
#
|
113
|
+
# and
|
114
|
+
#
|
115
|
+
# = surround '*' do
|
116
|
+
# %strong angry
|
117
|
+
#
|
118
|
+
# Produces:
|
119
|
+
#
|
120
|
+
# *<strong>angry</strong>*
|
121
|
+
#
|
122
|
+
def surround(front, back = nil, &block)
|
123
|
+
back ||= front
|
124
|
+
output = capture_haml(&block)
|
125
|
+
|
126
|
+
"#{front}#{output.chomp}#{back}\n"
|
127
|
+
end
|
128
|
+
|
129
|
+
# Prepends the given character to the beginning of the Haml block,
|
130
|
+
# with no whitespace between.
|
131
|
+
# For example:
|
132
|
+
#
|
133
|
+
# = precede '*' do
|
134
|
+
# %span.small Not really
|
135
|
+
#
|
136
|
+
# Produces:
|
137
|
+
#
|
138
|
+
# *<span class='small'>Not really</span>
|
139
|
+
#
|
140
|
+
def precede(char, &block)
|
141
|
+
"#{char}#{capture_haml(&block).chomp}\n"
|
142
|
+
end
|
143
|
+
|
144
|
+
# Appends the given character to the end of the Haml block,
|
145
|
+
# with no whitespace between.
|
146
|
+
# For example:
|
147
|
+
#
|
148
|
+
# click
|
149
|
+
# = succeed '.' do
|
150
|
+
# %a{:href=>"thing"} here
|
151
|
+
#
|
152
|
+
# Produces:
|
153
|
+
#
|
154
|
+
# click
|
155
|
+
# <a href='thing'>here</a>.
|
156
|
+
#
|
157
|
+
def succeed(char, &block)
|
158
|
+
"#{capture_haml(&block).chomp}#{char}\n"
|
159
|
+
end
|
160
|
+
|
161
|
+
# Captures the result of the given block of Haml code,
|
162
|
+
# gets rid of the excess indentation,
|
163
|
+
# and returns it as a string.
|
164
|
+
# For example, after the following,
|
165
|
+
#
|
166
|
+
# .foo
|
167
|
+
# - foo = capture_haml(13) do |a|
|
168
|
+
# %p= a
|
169
|
+
#
|
170
|
+
# the local variable <tt>foo</tt> would be assigned to "<p>13</p>\n".
|
171
|
+
#
|
172
|
+
def capture_haml(*args, &block)
|
173
|
+
capture_haml_with_buffer(buffer.buffer, *args, &block)
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
# Sets whether or not ActionView is installed on the system.
|
179
|
+
def self.action_view(value) # :nodoc:
|
180
|
+
@@action_view = value
|
181
|
+
end
|
182
|
+
|
183
|
+
# Gets a reference to the current Haml::Buffer object.
|
184
|
+
def buffer
|
185
|
+
@haml_stack[-1]
|
186
|
+
end
|
187
|
+
|
188
|
+
# Gives a proc the same local "_hamlout" and "_erbout" variables
|
189
|
+
# that the current template has.
|
190
|
+
def bind_proc(&proc)
|
191
|
+
_hamlout = buffer
|
192
|
+
_erbout = _hamlout.buffer
|
193
|
+
proc { |*args| proc.call(*args) }
|
194
|
+
end
|
195
|
+
|
196
|
+
# Performs the function of capture_haml, assuming <tt>local_buffer</tt>
|
197
|
+
# is where the output of block goes.
|
198
|
+
def capture_haml_with_buffer(local_buffer, *args, &block)
|
199
|
+
position = local_buffer.length
|
200
|
+
|
201
|
+
block.call(*args)
|
202
|
+
|
203
|
+
captured = local_buffer.slice!(position..-1)
|
204
|
+
|
205
|
+
min_tabs = nil
|
206
|
+
captured.each do |line|
|
207
|
+
tabs = line.index(/[^ ]/)
|
208
|
+
min_tabs ||= tabs
|
209
|
+
min_tabs = min_tabs > tabs ? tabs : min_tabs
|
210
|
+
end
|
211
|
+
|
212
|
+
result = captured.map do |line|
|
213
|
+
line[min_tabs..-1]
|
214
|
+
end
|
215
|
+
result.to_s
|
216
|
+
end
|
217
|
+
|
218
|
+
include ActionViewMods if self.const_defined? "ActionViewMods"
|
219
|
+
end
|
220
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
begin
|
2
|
+
require 'rubygems'
|
3
|
+
require 'active_support'
|
4
|
+
require 'action_view'
|
5
|
+
action_view_included = true
|
6
|
+
rescue LoadError
|
7
|
+
action_view_included = false
|
8
|
+
end
|
9
|
+
|
10
|
+
if action_view_included
|
11
|
+
class ActionView::Base
|
12
|
+
alias_method :old_concat, :concat unless instance_methods.include? "old_concat"
|
13
|
+
alias_method :old_form_tag, :form_tag unless instance_methods.include? "old_form_tag"
|
14
|
+
end
|
15
|
+
|
16
|
+
module Haml
|
17
|
+
module Helpers
|
18
|
+
# This module overrides various helpers in ActionView to make them
|
19
|
+
# work more effectively with Haml. It's not available unless ActionView
|
20
|
+
# is installed.
|
21
|
+
#
|
22
|
+
#--
|
23
|
+
# Methods in this module should be nodoc'd.
|
24
|
+
#++
|
25
|
+
module ActionViewMods
|
26
|
+
def self.included(othermod) # :nodoc:
|
27
|
+
othermod.class_eval do
|
28
|
+
action_view(true)
|
29
|
+
alias_method :capture_erb_with_buffer, :capture_haml_with_buffer
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def concat(string, binding = nil) # :nodoc:
|
34
|
+
buffer.buffer.concat(string)
|
35
|
+
end
|
36
|
+
|
37
|
+
def form_tag(url_for_options = {}, options = {}, *parameters_for_url, &proc) # :nodoc:
|
38
|
+
if block_given?
|
39
|
+
oldproc = proc
|
40
|
+
proc = bind_proc do |*args|
|
41
|
+
concat "\n"
|
42
|
+
tab_up
|
43
|
+
oldproc.call(*args)
|
44
|
+
tab_down
|
45
|
+
end
|
46
|
+
end
|
47
|
+
old_form_tag(url_for_options, options, *parameters_for_url, &proc)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/engine'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'active_support'
|
4
|
+
require 'action_view'
|
5
|
+
|
6
|
+
module Haml
|
7
|
+
# This class interfaces with ActionView
|
8
|
+
# to make Haml usable as a Ruby on Rails plugin.
|
9
|
+
# It usually shouldn't need to be used by end users.
|
10
|
+
# Just in case, though, here's what you might do to render
|
11
|
+
# <tt>templates/index.haml</tt>:
|
12
|
+
#
|
13
|
+
# ActionView::Base.register_template_handler("haml", Haml::Template)
|
14
|
+
# base = ActionView::Base.new("templates")
|
15
|
+
# base.render("index")
|
16
|
+
#
|
17
|
+
# Or, if you want to really get into the nitty-gritty:
|
18
|
+
#
|
19
|
+
# base = ActionView::Base.new
|
20
|
+
# template = Haml::Template.new(base)
|
21
|
+
# template.render("templates/index.haml")
|
22
|
+
#
|
23
|
+
class Template
|
24
|
+
|
25
|
+
class << self
|
26
|
+
@@options = {}
|
27
|
+
|
28
|
+
# Gets various options for HAML. See REFERENCE for details.
|
29
|
+
def options
|
30
|
+
@@options
|
31
|
+
end
|
32
|
+
|
33
|
+
# Sets various options for HAML. See REFERENCE for details.
|
34
|
+
def options=(value)
|
35
|
+
@@options = value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Creates a new Haml::Template object that uses <tt>view</tt>
|
40
|
+
# to render its templates.
|
41
|
+
def initialize(view)
|
42
|
+
@view = view
|
43
|
+
@@precompiled_templates ||= {}
|
44
|
+
end
|
45
|
+
|
46
|
+
# Renders the file at the location <tt>template</tt>,
|
47
|
+
# with <tt>local_assigns</tt> available as local variables within the template.
|
48
|
+
# Returns the result as a string.
|
49
|
+
def render(template, local_assigns={})
|
50
|
+
unless @view.instance_variable_get("@assigns_added")
|
51
|
+
assigns = @view.assigns.dup
|
52
|
+
|
53
|
+
# Get inside the view object's world
|
54
|
+
@view.instance_eval do
|
55
|
+
# Set all the instance variables
|
56
|
+
assigns.each do |key,val|
|
57
|
+
instance_variable_set "@#{key}", val
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
@view.instance_variable_set("@assigns_added", true)
|
62
|
+
end
|
63
|
+
|
64
|
+
options = @@options.dup
|
65
|
+
locals = options[:locals] || {}
|
66
|
+
locals.merge! local_assigns
|
67
|
+
options[:locals] = locals
|
68
|
+
|
69
|
+
if @view.haml_inline
|
70
|
+
engine = Haml::Engine.new(template, options)
|
71
|
+
elsif @precompiled = get_precompiled(template)
|
72
|
+
options[:precompiled] ||= @precompiled
|
73
|
+
engine = Haml::Engine.new("", options)
|
74
|
+
else
|
75
|
+
engine = Haml::Engine.new(File.read(template), options)
|
76
|
+
set_precompiled(template, engine.precompiled)
|
77
|
+
end
|
78
|
+
|
79
|
+
yield_proc = @view.instance_eval do
|
80
|
+
proc { |*name| instance_variable_get("@content_for_#{name.first || 'layout'}") }
|
81
|
+
end
|
82
|
+
|
83
|
+
engine.to_html(@view) { |*args| yield_proc.call(*args) }
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
# Gets the cached, precompiled version of the template at location <tt>filename</tt>
|
90
|
+
# as a string.
|
91
|
+
def get_precompiled(filename)
|
92
|
+
# Do we have it on file? Is it new enough?
|
93
|
+
if (precompiled, precompiled_on = @@precompiled_templates[filename]) &&
|
94
|
+
(precompiled_on == File.mtime(filename).to_i)
|
95
|
+
precompiled
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Sets the cached, precompiled version of the template at location <tt>filename</tt>
|
100
|
+
# to <tt>precompiled</tt>.
|
101
|
+
def set_precompiled(filename, precompiled)
|
102
|
+
@@precompiled_templates[filename] = [precompiled, File.mtime(filename).to_i]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# This module refers to the ActionView module that's part of Ruby on Rails.
|
108
|
+
# Haml can be used as an alternate templating engine for it,
|
109
|
+
# and includes several modifications to make it more Haml-friendly.
|
110
|
+
# The documentation can be found
|
111
|
+
# here[http://rubyonrails.org/api/classes/ActionView/Base.html].
|
112
|
+
module ActionView
|
113
|
+
class Base # :nodoc:
|
114
|
+
attr :haml_filename, true
|
115
|
+
attr :haml_inline
|
116
|
+
|
117
|
+
alias_method :haml_old_render_file, :render_file
|
118
|
+
def render_file(template_path, use_full_path = true, local_assigns = {})
|
119
|
+
@haml_filename = File.basename(template_path)
|
120
|
+
haml_old_render_file(template_path, use_full_path, local_assigns)
|
121
|
+
end
|
122
|
+
|
123
|
+
alias_method :read_template_file_old, :read_template_file
|
124
|
+
def read_template_file(template_path, extension)
|
125
|
+
if extension =~ /haml/i
|
126
|
+
template_path
|
127
|
+
else
|
128
|
+
read_template_file_old(template_path, extension)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
alias_method :render_template_old, :render_template
|
133
|
+
def render_template(template_extension, template, file_path = nil, local_assigns = {})
|
134
|
+
@haml_inline = !template.nil?
|
135
|
+
render_template_old(template_extension, template, file_path, local_assigns)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
data/test/benchmark.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/haml/template'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'active_support'
|
4
|
+
require 'action_view'
|
5
|
+
require 'benchmark'
|
6
|
+
require 'stringio'
|
7
|
+
|
8
|
+
module Haml
|
9
|
+
class Benchmarker
|
10
|
+
|
11
|
+
# Creates a new benchmarker that looks for templates in the base
|
12
|
+
# directory.
|
13
|
+
def initialize(base = File.dirname(__FILE__))
|
14
|
+
ActionView::Base.register_template_handler("haml", Haml::Template)
|
15
|
+
unless base.class == ActionView::Base
|
16
|
+
@base = ActionView::Base.new(base)
|
17
|
+
else
|
18
|
+
@base = base
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Benchmarks HAML against ERb. If <tt>template_name</tt> is specified,
|
23
|
+
# looks for a haml template in ./templates and an rhtml template in
|
24
|
+
# ./rhtml with the name <tt>template_name</tt>. Otherwise, uses
|
25
|
+
# <tt>haml_template</tt> and <tt>rhtml_template</tt> as the location of
|
26
|
+
# the templates.
|
27
|
+
#
|
28
|
+
# Returns the results of the benchmarking as a string.
|
29
|
+
#
|
30
|
+
# :call-seq:
|
31
|
+
# benchmark(runs = 100, template_name = 'standard')
|
32
|
+
# benchmark(runs = 100, haml_template, rhtml_template)
|
33
|
+
#
|
34
|
+
def benchmark(runs = 100, template_name = 'standard', other_template = nil)
|
35
|
+
if other_template.nil?
|
36
|
+
haml_template = "templates/#{template_name}"
|
37
|
+
rhtml_template = "rhtml/#{template_name}"
|
38
|
+
else
|
39
|
+
haml_template = template_name
|
40
|
+
rhtml_template = other_template
|
41
|
+
end
|
42
|
+
|
43
|
+
old_stdout = $stdout
|
44
|
+
$stdout = StringIO.new
|
45
|
+
|
46
|
+
times = Benchmark.bmbm do |b|
|
47
|
+
b.report("haml:") { runs.times { @base.render haml_template } }
|
48
|
+
b.report("erb:") { runs.times { @base.render rhtml_template } }
|
49
|
+
end
|
50
|
+
|
51
|
+
#puts times.inspect
|
52
|
+
ratio = sprintf("%g", times[0].to_a[5] / times[1].to_a[5])
|
53
|
+
puts "Haml/ERB: " + ratio
|
54
|
+
|
55
|
+
$stdout.pos = 0
|
56
|
+
to_return = $stdout.read
|
57
|
+
$stdout = old_stdout
|
58
|
+
|
59
|
+
to_return
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|