public
Description: HTML Abstraction Markup Language - A Markup Haiku
Homepage: http://haml.hamptoncatlin.com
Clone URL: git://github.com/nex3/haml.git
Enginification

git-svn-id: svn://hamptoncatlin.com/haml/trunk@48 
7063305b-7217-0410-af8c-cdc13e5119b9
packagethief (author)
Fri Sep 29 11:39:13 -0700 2006
commit  563d5d748ed736455a695a74ea7609ea3e525a82
tree    c04cca9dd5e36b1b7ea612cdfad6c863ee320d19
parent  8e245a6de76823db59f6797bd0853569ee89cb45
...
1
2
3
4
5
 
6
...
1
2
3
 
4
5
6
0
@@ -1,4 +1,4 @@
0
 require 'haml/engine'
0
 require 'haml/helpers'
0
 
0
-ActionView::Base.register_template_handler('haml', Haml::Engine)
0
\ No newline at end of file
0
+ActionView::Base.register_template_handler('haml', Haml::Template)
0
\ No newline at end of file
...
3
4
5
6
 
7
8
9
10
11
12
13
14
 
 
 
 
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 
 
 
 
41
42
43
44
45
46
47
48
49
 
50
51
52
 
53
54
55
...
61
62
63
64
 
65
66
67
...
70
71
72
73
 
74
75
 
76
77
78
...
81
82
83
84
85
 
 
 
86
87
88
 
89
 
90
91
92
93
 
94
 
95
96
97
98
99
 
100
101
102
...
107
108
109
 
 
 
 
110
111
112
113
114
 
 
 
 
115
116
117
118
 
 
 
 
119
120
121
...
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
 
 
 
 
 
 
 
 
 
 
 
153
154
155
156
157
158
159
160
161
162
 
 
 
 
 
163
 
164
165
 
 
 
 
 
 
 
 
 
166
167
168
169
 
 
 
170
171
172
...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
 
 
189
190
191
192
 
 
193
194
195
 
196
197
198
...
3
4
5
 
6
7
8
9
10
11
 
 
 
12
13
14
15
16
17
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
20
21
22
23
 
24
25
26
27
28
29
 
30
31
32
 
33
34
35
36
...
42
43
44
 
45
46
47
48
...
51
52
53
 
54
55
 
56
57
58
59
...
62
63
64
 
 
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
...
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
...
135
136
137
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
 
 
 
 
 
154
155
156
157
158
159
160
161
 
162
163
164
165
166
167
168
169
170
171
 
 
 
172
173
174
175
176
177
...
179
180
181
 
 
 
 
 
 
 
 
 
 
 
 
182
183
184
185
 
 
186
187
188
 
 
189
190
191
192
0
@@ -3,53 +3,34 @@ require File.dirname(__FILE__) + '/helpers'
0
 module Haml #:nodoc:
0
   class Engine
0
     include Haml::Helpers
0
-
0
+
0
     # Set the maximum length for a line to be considered a one-liner
0
     # Lines <= the maximum will be rendered on one line,
0
     # i.e. <tt><p>Hello world</p></tt>
0
     ONE_LINER_LENGTH = 50
0
     MULTILINE_CHAR_VALUE = '|'[0]
0
-
0
- def initialize(view)
0
- @view = view
0
+
0
+ def initialize(template, action_view=nil)
0
+ @view = action_view
0
+ @template = template #String
0
       @result = String.new
0
       @to_close_queue = []
0
     end
0
-
0
- def render(template, local_assigns={})
0
- assigns = @view.assigns.dup
0
-
0
- # Do content for layout on its own to keep things working in partials
0
- if content_for_layout = @view.instance_variable_get("@content_for_layout")
0
- assigns['content_for_layout'] = content_for_layout
0
- end
0
-
0
- # Get inside the view object's world
0
- @view.instance_eval do
0
- # Set all the instance variables
0
- assigns.each do |key,val|
0
- instance_variable_set "@#{key}", val
0
- end
0
- # Set all the local assigns
0
- local_assigns.each do |key,val|
0
- class << self; self; end.send(:define_method, key) { val }
0
- end
0
- end
0
-
0
- # Process each line of the template returning the resulting string
0
- template.each_with_index do |line, index|
0
+
0
+ def to_html
0
+ # Process each line of the template
0
+ @template.each_with_index do |line, index|
0
         count, line = count_soft_tabs(line)
0
-
0
         surpress_render, line, count = handle_multiline(count, line)
0
 
0
         if !surpress_render && count && line
0
           count, line = process_line(count, line)
0
         end
0
       end
0
-
0
+
0
       # Close all the open tags
0
       @to_close_queue.length.times { close_tag }
0
-
0
+
0
       # Return the result string
0
       @result
0
     end
0
@@ -61,7 +42,7 @@ module Haml #:nodoc:
0
         if count <= @to_close_queue.size && @to_close_queue.size > 0
0
           (@to_close_queue.size - count).times { close_tag }
0
         end
0
-
0
+
0
         case line.first
0
         when '.', '#'
0
           render_div(line)
0
@@ -70,9 +51,9 @@ module Haml #:nodoc:
0
         when '/'
0
           render_comment(line)
0
         when '='
0
- add template_eval(line[1, line.length]).to_s
0
+ add template_eval(line[1, line.length]).to_s if @view
0
         when '~'
0
- add find_and_flatten(template_eval(line[1, line.length])).to_s
0
+ add find_and_flatten(template_eval(line[1, line.length])).to_s if @view
0
         else
0
           add line.strip
0
         end
0
@@ -81,22 +62,28 @@ module Haml #:nodoc:
0
     end
0
 
0
     def handle_multiline(count, line)
0
- # The code to handle how a multi-line object should work.
0
- if @multiline_buffer && line[-1] == MULTILINE_CHAR_VALUE # '|' is 124
0
+ # Multilines are denoting by ending with a `|` (124)
0
+ if @multiline_buffer && line[-1] == MULTILINE_CHAR_VALUE
0
+
0
         # A multiline string is active, and is being continued
0
         @multiline_buffer += line[0...-1]
0
         supress_render = true
0
+
0
       elsif line[-1] == MULTILINE_CHAR_VALUE
0
+
0
         # A multiline string has just been activated, start adding the lines
0
         @multiline_buffer = line[0...-1]
0
         @multiline_count = count
0
         supress_render = true
0
+
0
       elsif @multiline_buffer
0
+
0
         # A multiline string has just ended, make line into the result
0
         process_line(@multiline_count, @multiline_buffer)
0
         @multiline_buffer = nil
0
         supress_render = false
0
       end
0
+
0
       return supress_render, line, count
0
     end
0
 
0
@@ -107,15 +94,27 @@ module Haml #:nodoc:
0
       end
0
     end
0
 
0
+ def build_attributes(attributes = {})
0
+ attributes.empty? ? String.new : String.new(' ') << (attributes.collect {|a,v| "#{a.to_s}='#{v.to_s}'" unless v.nil? }).compact.join(' ')
0
+ end
0
+
0
     def open_tag(name, attributes = {})
0
       add "<#{name.to_s}#{build_attributes(attributes)}>"
0
       @to_close_queue.push name
0
     end
0
 
0
+ def close_tag
0
+ add "</#{@to_close_queue.pop}>"
0
+ end
0
+
0
     def one_line_tag(name, value, attributes = {})
0
       add "<#{name.to_s}#{build_attributes(attributes)}>#{value}</#{name.to_s}>"
0
     end
0
 
0
+ def one_liner?(value)
0
+ value.length <= ONE_LINER_LENGTH && value.scan(/\n/).empty?
0
+ end
0
+
0
     def print_tag(name, value, attributes = {})
0
       unless value.empty?
0
         if one_liner? value
0
@@ -136,37 +135,43 @@ module Haml #:nodoc:
0
       add "<#{name.to_s}#{build_attributes(attributes)} />"
0
     end
0
 
0
- def build_attributes(attributes = {})
0
- attributes.empty? ? String.new : String.new(' ') << (attributes.collect {|a,v| "#{a.to_s}='#{v.to_s}'" unless v.nil? }).compact.join(' ')
0
- end
0
-
0
- def close_tag
0
- add "</#{@to_close_queue.pop}>"
0
- end
0
-
0
- def render_div(line)
0
- render_tag('%div' + line)
0
- end
0
-
0
- def render_comment(line)
0
- add "<!-- #{line[1..line.length].strip} -->"
0
+ def parse_class_and_id(list)
0
+ attributes = {}
0
+ list.scan(/([#.])([-a-zA-Z_()]+)/).each do |type, property|
0
+ case type
0
+ when '.'
0
+ attributes[:class] = property
0
+ when '#'
0
+ attributes[:id] = property
0
+ end
0
+ end
0
+ attributes
0
     end
0
 
0
     def render_tag(line)
0
       line.scan(/[%]([-_a-z1-9]+)([-_a-z\.\#]*)(\{.*\})?(\[.*\])?([=\/\~]?)?(.*)?/).each do |tag_name, attributes, attributes_hash, object_ref, action, value|
0
         attributes = parse_class_and_id(attributes.to_s)
0
- attributes.merge!(template_eval(attributes_hash)) unless (attributes_hash.nil? || attributes_hash.empty?)
0
-
0
- if object_ref && (object_ref = template_eval(object_ref).first)
0
- class_name = object_ref.class.to_s.underscore
0
- attributes.merge!(:id => "#{class_name}_#{object_ref.id}", :class => class_name)
0
+
0
+ unless (attributes_hash.nil? || attributes_hash.empty?)
0
+ # Determine whether to eval the attributes hash in the context of a template
0
+ add_attributes = @view ? template_eval(attributes_hash) : eval(attributes_hash)
0
+ attributes.merge!(add_attributes)
0
         end
0
+
0
 
0
- if action == '/'
0
+ if @view
0
+ if object_ref && (object_ref = template_eval(object_ref).first)
0
+ class_name = object_ref.class.to_s.underscore
0
+ attributes.merge!(:id => "#{class_name}_#{object_ref.id}", :class => class_name)
0
+ end
0
+ end
0
+
0
+ case action
0
+ when '/'
0
           atomic_tag(tag_name, attributes)
0
- elsif action == '=' || action == '~'
0
- value = template_eval(value)
0
- value = find_and_flatten(value) if action == '~'
0
+ when '=', '~'
0
+ value = template_eval(value) if @view
0
+ value = find_and_flatten(value) if action == '~' and @view
0
           print_tag(tag_name, value.to_s, attributes) if value
0
         else
0
           print_tag(tag_name, value.to_s.strip, attributes)
0
@@ -174,25 +179,14 @@ module Haml #:nodoc:
0
       end
0
     end
0
 
0
- # Searches for `#` and `.` characters indicating id and class attributes
0
- def parse_class_and_id(list)
0
- attributes = {}
0
- list.scan(/([#.])([-a-zA-Z_()]+)/).each do |type, property|
0
- case type
0
- when '.'
0
- attributes[:class] = property
0
- when '#'
0
- attributes[:id] = property
0
- end
0
- end
0
- attributes
0
+ def render_div(line)
0
+ render_tag('%div' + line)
0
     end
0
 
0
- def one_liner?(value)
0
- value.length <= ONE_LINER_LENGTH && value.scan(/\n/).empty?
0
+ def render_comment(line)
0
+ add "<!-- #{line[1..line.length].strip} -->"
0
     end
0
-
0
- # Evaluates input in the context of the current ActionView instance
0
+
0
     def template_eval(args)
0
       @view.instance_eval(args)
0
     end
...
1
2
3
4
5
6
7
8
 
9
10
 
11
12
13
14
15
16
17
 
18
...
 
 
 
 
1
2
3
 
4
5
 
6
7
8
9
10
11
12
 
13
14
0
@@ -1,18 +1,14 @@
0
-require File.dirname(__FILE__) + '/../lib/haml/engine'
0
-
0
-$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
0
-
0
 require 'rubygems'
0
 require 'action_view'
0
 
0
-include Haml::Helpers
0
+require File.dirname(__FILE__) + '/../lib/haml/template'
0
 
0
-ActionView::Base.register_template_handler("haml", Haml::Engine)
0
+ActionView::Base.register_template_handler("haml", Haml::Template)
0
 @base = ActionView::Base.new(File.dirname(__FILE__))
0
 
0
 RUNS = (ARGV[0] || 100).to_i
0
 
0
 Benchmark.bm do |b|
0
   b.report("haml: ") { RUNS.times { @base.render "templates/standard" } }
0
- b.report("rhtml:") { RUNS.times { @base.render "rhtml/standard" } }
0
+ b.report("erb: ") { RUNS.times { @base.render "rhtml/standard" } }
0
 end
...
1
2
3
4
5
6
7
8
9
10
11
12
 
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 
28
29
30
31
32
33
34
 
 
 
35
36
37
38
39
40
41
 
 
 
 
42
43
44
45
 
 
 
 
 
 
 
46
47
48
49
50
51
52
53
54
55
 
 
 
56
57
58
59
60
61
62
63
 
 
 
64
65
66
67
68
 
 
 
69
70
71
72
73
 
 
 
74
75
76
77
78
 
 
79
80
...
1
2
 
 
 
 
 
 
 
 
 
3
4
5
 
 
 
 
6
7
8
 
 
 
 
 
 
 
9
10
 
 
 
 
 
 
11
12
13
14
15
 
 
 
 
 
16
17
18
19
20
21
 
 
22
23
24
25
26
27
28
29
30
 
 
 
 
 
 
 
 
31
32
33
34
35
 
 
 
 
 
 
36
37
38
39
40
 
 
 
41
42
43
44
 
 
 
 
45
46
47
48
49
 
 
 
50
51
52
53
0
@@ -1,80 +1,53 @@
0
 require 'test/unit'
0
 require File.dirname(__FILE__) + '/../lib/haml/engine'
0
-require File.dirname(__FILE__) + '/mocks/article'
0
-
0
-$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
0
-
0
-require 'rubygems'
0
-require 'action_view'
0
-
0
-class HamlTest < Test::Unit::TestCase
0
- include Haml::Helpers
0
 
0
+class EngineTest < Test::Unit::TestCase
0
   def setup
0
- ActionView::Base.register_template_handler("haml", Haml::Engine)
0
- @base = ActionView::Base.new(File.dirname(__FILE__) + "/templates/")
0
- @engine = Haml::Engine.new(@base)
0
- @base.instance_variable_set("@article", Article.new)
0
   end
0
 
0
   def render(text)
0
- @engine.render(text)
0
- end
0
-
0
- def load_result(name)
0
- @result = ''
0
- File.new(File.dirname(__FILE__) + "/results/#{name}.xhtml").each_line { |l| @result += l}
0
- @result
0
+ Haml::Engine.new(text).to_html
0
   end
0
-
0
- def assert_renders_correctly(name)
0
- load_result(name).split("\n").zip(@base.render(name).split("\n")).each do |pair|
0
- assert_equal(pair.first, pair.last)
0
- #puts pair.inspect
0
- end
0
+
0
+ def test_empty_render_should_remain_empty
0
+ assert_equal('', render(''))
0
   end
0
 
0
- # Make sure our little environment builds
0
- def test_build_stub
0
- assert_not_nil(@engine)
0
- assert @engine.is_a?(Haml::Engine)
0
- assert_equal(Haml::Engine, @engine.class)
0
+ def test_normal_renders_should_not_eval
0
+ assert_equal("", render("= 1+1"))
0
+ assert_equal("", render("= @content_for_layout"))
0
+ assert_equal("", render("~ @foobar"))
0
   end
0
 
0
- def test_empty_render
0
- assert_equal('', render(''))
0
+ # This is ugly because Hashes are unordered; we don't always know the order
0
+ # in which attributes will be returned.
0
+ # There is probably a better way to do this.
0
+ def test_attributes_should_render_correctly
0
+ assert_equal("<div class='atlantis' style='ugly'>\n</div>", render(".atlantis{:style => 'ugly'}").chomp)
0
+ rescue
0
+ assert_equal("<div style='ugly' class='atlantis'>\n</div>", render(".atlantis{:style => 'ugly'}").chomp)
0
   end
0
 
0
- def test_renderings
0
- assert_renders_correctly("very_basic")
0
- assert_renders_correctly("standard")
0
- assert_renders_correctly("helpers")
0
- assert_renders_correctly("whitespace_handling")
0
- assert_renders_correctly("original_engine")
0
- assert_renders_correctly("list")
0
- assert_renders_correctly("helpful")
0
+ def test_ruby_code_should_work_inside_attributes
0
+ author = 'hcatlin'
0
+ assert_equal("<p class='3'>foo</p>", render("%p{:class => 1+2} foo").chomp)
0
   end
0
 
0
- def test_instance_variables
0
- @base.instance_eval("@content_for_layout = 'Hampton'")
0
- @base.instance_eval("@assigns['last_name'] = 'Catlin'")
0
- #make sure to reload!
0
- @engine = Haml::Engine.new(@base)
0
- assert_equal("<div class='author'>Hampton Catlin</div>\n", render(".author= @content_for_layout + ' ' + @last_name"))
0
+ def test_nil_should_render_empty_tag
0
+ assert_equal("<div class='no_attributes'>\n</div>",
0
+ render(".no_attributes{:nil => nil}").chomp)
0
   end
0
 
0
- def test_instance_variables_changing
0
- @base.instance_eval("@author = 'Hampton'")
0
- assert_equal("Hampton\n", render("= @author"))
0
+ def test_strings_should_get_stripped_inside_tags
0
+ assert_equal("<div class='stripped'>This should have no spaces in front of it</div>",
0
+ render(".stripped This should have no spaces in front of it").chomp)
0
   end
0
-
0
- def test_nil_attribute
0
- assert_equal("<div class='no_attributes'>\n</div>\n",
0
- render(".no_attributes{:nil => nil}"))
0
+
0
+ def test_one_liner_should_be_one_line
0
+ assert_equal("<p>Hello</p>", render('%p Hello').chomp)
0
   end
0
 
0
- def test_stripped_strings
0
- assert_equal("<div class='stripped'>This should have no spaces in front of it</div>\n",
0
- render(".stripped This should have no spaces in front of it"))
0
+ def test_long_liner_should_not_print_on_one_line
0
+ assert_equal("<div>\n #{'x' * 51}\n</div>", render("%div #{'x' * 51}").chomp)
0
   end
0
 end
...
1
2
 
3
4
5
6
7
8
9
10
 
11
12
13
14
15
 
16
17
 
18
19
20
21
22
23
24
25
 
 
 
 
 
 
 
 
 
26
27
28
 
29
30
31
32
33
 
34
35
36
...
 
 
1
2
 
 
 
 
 
 
 
3
4
5
6
7
8
9
10
11
12
13
14
 
 
 
 
 
 
15
16
17
18
19
20
21
22
23
24
25
 
26
27
28
29
30
 
31
32
33
34
0
@@ -1,36 +1,34 @@
0
-require 'test/unit'
0
-require File.dirname(__FILE__) + '/../lib/haml/engine'
0
+require File.dirname(__FILE__) + '/../lib/haml/helpers'
0
 
0
-$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
0
-
0
-require 'rubygems'
0
-require 'action_view'
0
-require 'active_support'
0
-
0
-class HamlTest < Test::Unit::TestCase
0
+class HelperTest < Test::Unit::TestCase
0
   include Haml::Helpers
0
 
0
   def test_find_and_flatten
0
     assert_equal(find_and_flatten("<br/><textarea></textarea><br/>"),
0
                                   "<br/><textarea></textarea><br/>")
0
+
0
     assert_equal(find_and_flatten("<code lang='ruby'>TEST!</code>\t\t<p></p>"),
0
                                   "<code lang='ruby'>TEST!</code>\t\t<p></p>")
0
+
0
     assert_equal(find_and_flatten("<pre>Hello\nWorld!\nYOU ARE \rFLAT?\n\rOMGZ!</pre></br>"),
0
                                   "<pre>Hello&#x000A;World!&#x000A;YOU ARE FLAT?&#x000A;OMGZ!</pre></br>")
0
- assert_equal( "<div class='text_area_test_area'>\n <textarea>Two&#x000A; lines</textarea>\n</div>\n",
0
- find_and_flatten("<div class='text_area_test_area'>\n <textarea>Two\n lines</textarea>\n</div>\n"))
0
- assert_equal( "<code>Two&#x000A;lines</code><pre>a&#x000A;b&#x000A;c</pre>",
0
- find_and_flatten("<code>Two\nlines</code><pre>a\nb\nc</pre>"))
0
- assert_equal( "<pre>Two&#x000A;lines</pre>\n<pre>a&#x000A;b&#x000A;c</pre>",
0
- find_and_flatten("<pre>Two\nlines</pre>\n<pre>a\nb\nc</pre>"))
0
+
0
+ assert_equal(find_and_flatten("<div class='text_area_test_area'>\n <textarea>Two\n lines</textarea>\n</div>\n"),
0
+ "<div class='text_area_test_area'>\n <textarea>Two&#x000A; lines</textarea>\n</div>\n")
0
+
0
+ assert_equal(find_and_flatten("<code>Two\nlines</code><pre>a\nb\nc</pre>"),
0
+ "<code>Two&#x000A;lines</code><pre>a&#x000A;b&#x000A;c</pre>")
0
+
0
+ assert_equal(find_and_flatten("<pre>Two\nlines</pre>\n<pre>a\nb\nc</pre>"),
0
+ "<pre>Two&#x000A;lines</pre>\n<pre>a&#x000A;b&#x000A;c</pre>")
0
   end
0
 
0
- def test_tabs
0
+ def test_tabs_should_render_correctly
0
     assert_equal(" ", tabs(1))
0
     assert_equal(" ", tabs(5))
0
   end
0
 
0
- def test_list_of
0
+ def test_list_of_should_render_correctly
0
     assert_equal("<li>1</li>\n<li>2</li>", (list_of([1, 2]) { |i| i.to_s}))
0
     assert_equal("<li>1</li>", (list_of([[1]]) { |i| i.first}))
0
   end

Comments

    No one has commented yet.