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.

Files changed (48) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/REFERENCE +662 -0
  3. data/Rakefile +167 -0
  4. data/VERSION +1 -0
  5. data/bin/haml +18 -0
  6. data/lib/haml/buffer.rb +224 -0
  7. data/lib/haml/engine.rb +551 -0
  8. data/lib/haml/helpers.rb +220 -0
  9. data/lib/haml/helpers/action_view_mods.rb +53 -0
  10. data/lib/haml/template.rb +138 -0
  11. data/test/benchmark.rb +62 -0
  12. data/test/engine_test.rb +93 -0
  13. data/test/helper_test.rb +105 -0
  14. data/test/mocks/article.rb +6 -0
  15. data/test/profile.rb +45 -0
  16. data/test/results/content_for_layout.xhtml +16 -0
  17. data/test/results/eval_suppressed.xhtml +2 -0
  18. data/test/results/helpers.xhtml +50 -0
  19. data/test/results/helpful.xhtml +5 -0
  20. data/test/results/just_stuff.xhtml +36 -0
  21. data/test/results/list.xhtml +12 -0
  22. data/test/results/original_engine.xhtml +24 -0
  23. data/test/results/partials.xhtml +20 -0
  24. data/test/results/silent_script.xhtml +74 -0
  25. data/test/results/standard.xhtml +42 -0
  26. data/test/results/tag_parsing.xhtml +28 -0
  27. data/test/results/very_basic.xhtml +7 -0
  28. data/test/results/whitespace_handling.xhtml +51 -0
  29. data/test/rhtml/standard.rhtml +51 -0
  30. data/test/runner.rb +15 -0
  31. data/test/template_test.rb +137 -0
  32. data/test/templates/_partial.haml +7 -0
  33. data/test/templates/_text_area.haml +3 -0
  34. data/test/templates/content_for_layout.haml +10 -0
  35. data/test/templates/eval_suppressed.haml +5 -0
  36. data/test/templates/helpers.haml +39 -0
  37. data/test/templates/helpful.haml +6 -0
  38. data/test/templates/just_stuff.haml +29 -0
  39. data/test/templates/list.haml +12 -0
  40. data/test/templates/original_engine.haml +17 -0
  41. data/test/templates/partialize.haml +1 -0
  42. data/test/templates/partials.haml +12 -0
  43. data/test/templates/silent_script.haml +40 -0
  44. data/test/templates/standard.haml +40 -0
  45. data/test/templates/tag_parsing.haml +24 -0
  46. data/test/templates/very_basic.haml +4 -0
  47. data/test/templates/whitespace_handling.haml +66 -0
  48. metadata +108 -0
@@ -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
@@ -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