haml 1.8.0 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. data/FAQ +138 -0
  2. data/MIT-LICENSE +1 -1
  3. data/{README → README.rdoc} +66 -3
  4. data/REVISION +1 -0
  5. data/Rakefile +115 -147
  6. data/VERSION +1 -1
  7. data/bin/css2sass +0 -0
  8. data/bin/haml +2 -1
  9. data/bin/html2haml +0 -0
  10. data/bin/sass +0 -0
  11. data/init.rb +6 -1
  12. data/lib/haml/buffer.rb +122 -64
  13. data/lib/haml/engine.rb +77 -46
  14. data/lib/haml/error.rb +15 -6
  15. data/lib/haml/exec.rb +61 -10
  16. data/lib/haml/filters.rb +229 -74
  17. data/lib/haml/helpers/action_view_extensions.rb +1 -1
  18. data/lib/haml/helpers/action_view_mods.rb +109 -24
  19. data/lib/haml/helpers.rb +137 -76
  20. data/lib/haml/html.rb +8 -8
  21. data/lib/haml/precompiler.rb +280 -153
  22. data/lib/haml/template/patch.rb +10 -3
  23. data/lib/haml/template/plugin.rb +61 -10
  24. data/lib/haml/template.rb +14 -9
  25. data/lib/haml.rb +483 -214
  26. data/lib/sass/constant/color.rb +13 -13
  27. data/lib/sass/constant/literal.rb +8 -7
  28. data/lib/sass/constant/nil.rb +9 -0
  29. data/lib/sass/constant/number.rb +10 -10
  30. data/lib/sass/constant/operation.rb +4 -4
  31. data/lib/sass/constant/string.rb +3 -3
  32. data/lib/sass/constant.rb +46 -77
  33. data/lib/sass/css.rb +130 -56
  34. data/lib/sass/engine.rb +131 -43
  35. data/lib/sass/plugin/merb.rb +48 -12
  36. data/lib/sass/plugin/rails.rb +10 -4
  37. data/lib/sass/plugin.rb +33 -10
  38. data/lib/sass/tree/attr_node.rb +5 -5
  39. data/lib/sass/tree/directive_node.rb +2 -7
  40. data/lib/sass/tree/node.rb +1 -12
  41. data/lib/sass/tree/rule_node.rb +39 -31
  42. data/lib/sass/tree/value_node.rb +1 -1
  43. data/lib/sass.rb +194 -19
  44. data/rails/init.rb +1 -0
  45. data/test/benchmark.rb +67 -80
  46. data/test/haml/engine_test.rb +368 -152
  47. data/test/haml/helper_test.rb +68 -16
  48. data/test/haml/html2haml_test.rb +3 -4
  49. data/test/haml/results/content_for_layout.xhtml +1 -2
  50. data/test/haml/results/eval_suppressed.xhtml +2 -4
  51. data/test/haml/results/filters.xhtml +38 -30
  52. data/test/haml/results/helpers.xhtml +4 -8
  53. data/test/haml/results/just_stuff.xhtml +8 -7
  54. data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
  55. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  56. data/test/haml/results/original_engine.xhtml +3 -7
  57. data/test/haml/results/partials.xhtml +1 -0
  58. data/test/haml/results/tag_parsing.xhtml +1 -6
  59. data/test/haml/results/very_basic.xhtml +2 -4
  60. data/test/haml/results/whitespace_handling.xhtml +13 -21
  61. data/test/haml/template_test.rb +42 -57
  62. data/test/haml/templates/_partial.haml +1 -0
  63. data/test/haml/templates/filters.haml +39 -21
  64. data/test/haml/templates/helpers.haml +10 -10
  65. data/test/haml/templates/just_stuff.haml +8 -3
  66. data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
  67. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  68. data/test/haml/templates/partials.haml +1 -1
  69. data/test/haml/templates/tag_parsing.haml +0 -3
  70. data/test/haml/templates/whitespace_handling.haml +10 -10
  71. data/test/sass/engine_test.rb +97 -39
  72. data/test/sass/plugin_test.rb +4 -7
  73. data/test/sass/results/constants.css +2 -0
  74. data/test/sass/results/import.css +2 -2
  75. data/test/sass/results/mixins.css +95 -0
  76. data/test/sass/results/multiline.css +24 -0
  77. data/test/sass/templates/constants.sass +3 -0
  78. data/test/sass/templates/import.sass +4 -1
  79. data/test/sass/templates/importee.sass +4 -0
  80. data/test/sass/templates/mixins.sass +76 -0
  81. data/test/sass/templates/multiline.sass +20 -0
  82. data/test/test_helper.rb +18 -0
  83. metadata +70 -53
  84. data/lib/haml/helpers/action_view_mods.rb.rej +0 -30
  85. data/lib/haml/util.rb +0 -18
  86. data/lib/sass/constant.rb.rej +0 -42
  87. data/test/haml/runner.rb +0 -16
  88. data/test/profile.rb +0 -65
  89. data/test/sass/engine_test.rb.rej +0 -18
data/lib/haml/filters.rb CHANGED
@@ -1,116 +1,271 @@
1
- # This file contains redefinitions of and wrappers around various text
2
- # filters so they can be used as Haml filters.
3
-
4
- # :stopdoc:
5
-
6
- require 'erb'
7
- require 'sass/engine'
8
- require 'stringio'
9
-
10
- begin
11
- require 'rubygems'
12
- rescue LoadError; end
13
-
14
- class ERB; alias_method :render, :result; end
15
-
16
1
  module Haml
2
+ # The module containing the default filters,
3
+ # as well as the base module,
4
+ # Haml::Filters::Base.
17
5
  module Filters
18
- class Plain
19
- def initialize(text)
20
- @text = text
6
+ # Returns a hash of defined filters.
7
+ def self.defined
8
+ @defined ||= {}
9
+ end
10
+
11
+ # The base module for Haml filters.
12
+ # User-defined filters should be modules including this module.
13
+ #
14
+ # A user-defined filter should override either Base#render or Base #compile.
15
+ # Base#render is the most common.
16
+ # It takes a string, the filter source,
17
+ # and returns another string,
18
+ # the result of the filter.
19
+ # For example:
20
+ #
21
+ # module Haml::Filters::Sass
22
+ # include Haml::Filters::Base
23
+ #
24
+ # def render(text)
25
+ # ::Sass::Engine.new(text).render
26
+ # end
27
+ # end
28
+ #
29
+ # For details on overriding #compile, see its documentation.
30
+ #
31
+ module Base
32
+ def self.included(base) # :nodoc:
33
+ Filters.defined[base.name.split("::").last.downcase] = base
34
+ base.extend(base)
35
+ base.instance_variable_set "@lazy_requires", nil
21
36
  end
22
37
 
23
- def render
24
- @text
38
+ # Takes a string, the source text that should be passed to the filter,
39
+ # and returns the string resulting from running the filter on <tt>text</tt>.
40
+ #
41
+ # This should be overridden in most individual filter modules
42
+ # to render text with the given filter.
43
+ # If compile is overridden, however, render doesn't need to be.
44
+ def render(text)
45
+ raise Error.new("#{self.inspect}#render not defined!")
25
46
  end
26
- end
27
47
 
28
- class Ruby
29
- def initialize(text)
30
- @text = text
48
+ # Same as render, but takes the Haml options hash as well.
49
+ # It's only safe to rely on options made available in Haml::Engine#options_for_buffer.
50
+ def render_with_options(text, options)
51
+ render(text)
31
52
  end
32
53
 
33
- def render
34
- old_stdout = $stdout
35
- $stdout = StringIO.new
36
- Object.new.instance_eval(@text)
37
- old_stdout, $stdout = $stdout, old_stdout
38
- old_stdout.pos = 0
39
- old_stdout.read
54
+ def internal_compile(*args) # :nodoc:
55
+ resolve_lazy_requires
56
+ compile(*args)
40
57
  end
41
- end
42
58
 
43
- class Preserve
44
- def initialize(text)
45
- @text = text
59
+ # compile should be overridden when a filter needs to have access
60
+ # to the Haml evaluation context.
61
+ # Rather than applying a filter to a string at compile-time,
62
+ # compile uses the Haml::Precompiler instance to compile the string to Ruby code
63
+ # that will be executed in the context of the active Haml template.
64
+ #
65
+ # Warning: the Haml::Precompiler interface is neither well-documented
66
+ # nor guaranteed to be stable.
67
+ # If you want to make use of it,
68
+ # you'll probably need to look at the source code
69
+ # and should test your filter when upgrading to new Haml versions.
70
+ def compile(precompiler, text)
71
+ resolve_lazy_requires
72
+ filter = self
73
+ precompiler.instance_eval do
74
+ if contains_interpolation?(text)
75
+ return if options[:suppress_eval]
76
+
77
+ push_script(<<RUBY, false)
78
+ find_and_preserve(#{filter.inspect}.render_with_options(#{unescape_interpolation(text)}, _hamlout.options))
79
+ RUBY
80
+ return
81
+ end
82
+
83
+ rendered = Haml::Helpers::find_and_preserve(filter.render_with_options(text, precompiler.options), precompiler.options[:preserve])
84
+
85
+ if !options[:ugly]
86
+ push_text(rendered.rstrip.gsub("\n", "\n#{' ' * @output_tabs}"))
87
+ else
88
+ push_text(rendered.rstrip)
89
+ end
90
+ end
46
91
  end
47
92
 
48
- def render
49
- Haml::Helpers.preserve(@text)
93
+ # This becomes a class method of modules that include Base.
94
+ # It allows the module to specify one or more Ruby files
95
+ # that Haml should try to require when compiling the filter.
96
+ #
97
+ # The first file specified is tried first,
98
+ # then the second, etc.
99
+ # If none are found, the compilation throws an exception.
100
+ #
101
+ # For example:
102
+ #
103
+ # module Haml::Filters::Markdown
104
+ # lazy_require 'rdiscount', 'peg_markdown', 'maruku', 'bluecloth'
105
+ #
106
+ # ...
107
+ # end
108
+ #
109
+ def lazy_require(*reqs)
110
+ @lazy_requires = reqs
50
111
  end
51
- end
52
112
 
53
- class LazyLoaded
54
- def initialize(*reqs)
55
- reqs[0...-1].each do |req|
113
+ private
114
+
115
+ def resolve_lazy_requires
116
+ return unless @lazy_requires
117
+
118
+ @lazy_requires[0...-1].each do |req|
56
119
  begin
57
120
  @required = req
58
121
  require @required
59
122
  return
60
123
  rescue LoadError; end # RCov doesn't see this, but it is run
61
124
  end
62
-
125
+
63
126
  begin
64
- @required = reqs[-1]
127
+ @required = @lazy_requires[-1]
65
128
  require @required
66
129
  rescue LoadError => e
67
- classname = self.class.to_s.gsub(/\w+::/, '')
130
+ classname = self.name.match(/\w+$/)[0]
68
131
 
69
- if reqs.size == 1
70
- raise HamlError.new("Can't run #{classname} filter; required file '#{reqs.first}' not found")
132
+ if @lazy_requires.size == 1
133
+ raise Error.new("Can't run #{classname} filter; required file '#{@lazy_requires.first}' not found")
71
134
  else
72
- raise HamlError.new("Can't run #{classname} filter; required #{reqs.map { |r| "'#{r}'" }.join(' or ')}, but none were found")
135
+ raise Error.new("Can't run #{classname} filter; required #{@lazy_requires.map { |r| "'#{r}'" }.join(' or ')}, but none were found")
73
136
  end
74
137
  end
75
138
  end
76
139
  end
77
-
78
- class RedCloth < LazyLoaded
79
- def initialize(text)
80
- super('redcloth')
81
- @engine = ::RedCloth.new(text)
140
+ end
141
+ end
142
+
143
+ # :stopdoc:
144
+
145
+ begin
146
+ require 'rubygems'
147
+ rescue LoadError; end
148
+
149
+ module Haml
150
+ module Filters
151
+ module Plain
152
+ include Base
153
+
154
+ def render(text); text; end
155
+ end
156
+
157
+ module Javascript
158
+ include Base
159
+
160
+ def render_with_options(text, options)
161
+ <<END
162
+ <script type=#{options[:attr_wrapper]}text/javascript#{options[:attr_wrapper]}>
163
+ //<![CDATA[
164
+ #{text.rstrip.gsub("\n", "\n ")}
165
+ //]]>
166
+ </script>
167
+ END
168
+ end
169
+ end
170
+
171
+ module Cdata
172
+ include Base
173
+
174
+ def render(text)
175
+ "<![CDATA[#{("\n" + text).rstrip.gsub("\n", "\n ")}\n]]>"
176
+ end
177
+ end
178
+
179
+ module Escaped
180
+ include Base
181
+
182
+ def render(text)
183
+ Haml::Helpers.html_escape text
184
+ end
185
+ end
186
+
187
+ module Ruby
188
+ include Base
189
+ lazy_require 'stringio'
190
+
191
+ def compile(precompiler, text)
192
+ return if precompiler.options[:suppress_eval]
193
+ precompiler.instance_eval do
194
+ push_silent <<-FIRST.gsub("\n", ';') + text + <<-LAST.gsub("\n", ';')
195
+ _haml_old_stdout = $stdout
196
+ $stdout = StringIO.new(_hamlout.buffer, 'a')
197
+ FIRST
198
+ _haml_old_stdout, $stdout = $stdout, _haml_old_stdout
199
+ _haml_old_stdout.close
200
+ LAST
201
+ end
202
+ end
203
+ end
204
+
205
+ module Preserve
206
+ include Base
207
+
208
+ def render(text)
209
+ Haml::Helpers.preserve text
82
210
  end
211
+ end
83
212
 
84
- def render
85
- @engine.to_html
213
+ module Sass
214
+ include Base
215
+ lazy_require 'sass/engine'
216
+
217
+ def render(text)
218
+ ::Sass::Engine.new(text,::Sass::Plugin.engine_options).render
86
219
  end
87
220
  end
88
-
89
- # Uses RedCloth to provide only Textile (not Markdown) parsing
90
- class Textile < RedCloth
91
- def render
92
- @engine.to_html(:textile)
221
+
222
+ module ERB
223
+ include Base
224
+ lazy_require 'erb'
225
+
226
+ def compile(precompiler, text)
227
+ return if precompiler.options[:suppress_eval]
228
+ src = ::ERB.new(text).src.sub(/^_erbout = '';/, "").gsub("\n", ';')
229
+ precompiler.send(:push_silent, src)
93
230
  end
94
231
  end
95
232
 
233
+ module Textile
234
+ include Base
235
+ lazy_require 'redcloth'
236
+
237
+ def render(text)
238
+ ::RedCloth.new(text).to_html(:textile)
239
+ end
240
+ end
241
+ RedCloth = Textile
242
+
96
243
  # Uses BlueCloth or RedCloth to provide only Markdown (not Textile) parsing
97
- class Markdown < LazyLoaded
98
- def initialize(text)
99
- super('bluecloth', 'redcloth')
100
-
101
- if @required == 'bluecloth'
102
- @engine = ::BlueCloth.new(text)
103
- else
104
- @engine = ::RedCloth.new(text)
105
- end
244
+ module Markdown
245
+ include Base
246
+ lazy_require 'rdiscount', 'peg_markdown', 'maruku', 'bluecloth'
247
+
248
+ def render(text)
249
+ engine = case @required
250
+ when 'rdiscount'
251
+ RDiscount
252
+ when 'peg_markdown'
253
+ PEGMarkdown
254
+ when 'maruku'
255
+ Maruku
256
+ when 'bluecloth'
257
+ BlueCloth
258
+ end
259
+ engine.new(text).to_html
106
260
  end
261
+ end
107
262
 
108
- def render
109
- if @engine.is_a?(::BlueCloth)
110
- @engine.to_html
111
- else
112
- @engine.to_html(:markdown)
113
- end
263
+ module Maruku
264
+ include Base
265
+ lazy_require 'maruku'
266
+
267
+ def render(text)
268
+ Maruku.new(text).to_html
114
269
  end
115
270
  end
116
271
  end
@@ -31,7 +31,7 @@ if defined?(ActionView)
31
31
  #
32
32
  # .entry
33
33
  # :color #00f
34
- #
34
+ #
35
35
  def page_class
36
36
  controller.controller_name + " " + controller.action_name
37
37
  end
@@ -2,42 +2,127 @@ if defined?(ActionView) and not defined?(Merb::Plugins)
2
2
  module ActionView
3
3
  class Base # :nodoc:
4
4
  def render_with_haml(*args, &block)
5
- was_haml = is_haml?
6
- @haml_is_haml = false
7
- res = render_without_haml(*args, &block)
8
- @haml_is_haml = was_haml
9
- res
5
+ return non_haml { render_without_haml(*args, &block) } if is_haml?
6
+ render_without_haml(*args, &block)
10
7
  end
11
8
  alias_method :render_without_haml, :render
12
9
  alias_method :render, :render_with_haml
10
+
11
+ # Rails >2.1
12
+ if instance_methods.include?('output_buffer')
13
+ def output_buffer_with_haml
14
+ return haml_buffer.buffer if is_haml?
15
+ output_buffer_without_haml
16
+ end
17
+ alias_method :output_buffer_without_haml, :output_buffer
18
+ alias_method :output_buffer, :output_buffer_with_haml
19
+
20
+ def set_output_buffer_with_haml(new)
21
+ if is_haml?
22
+ haml_buffer.buffer = new
23
+ else
24
+ set_output_buffer_without_haml new
25
+ end
26
+ end
27
+ alias_method :set_output_buffer_without_haml, :output_buffer=
28
+ alias_method :output_buffer=, :set_output_buffer_with_haml
29
+ end
13
30
  end
14
31
 
15
32
  # This overrides various helpers in ActionView
16
33
  # to make them work more effectively with Haml.
17
34
  module Helpers
18
35
  # :stopdoc:
19
- module CaptureHelper
20
- def capture_erb_with_buffer_with_haml(*args, &block)
21
- if is_haml?
22
- capture_haml_with_buffer(*args, &block)
23
- else
24
- capture_erb_with_buffer_without_haml(*args, &block)
36
+ # In Rails <=2.1, we've got to override considerable capturing infrastructure.
37
+ # In Rails >2.1, we can make do with only overriding #capture
38
+ # (which no longer behaves differently in helper contexts).
39
+ unless ActionView::Base.instance_methods.include?('output_buffer')
40
+ module CaptureHelper
41
+ def capture_with_haml(*args, &block)
42
+ # Rails' #capture helper will just return the value of the block
43
+ # if it's not actually in the template context,
44
+ # as detected by the existance of an _erbout variable.
45
+ # We've got to do the same thing for compatibility.
46
+ block_is_haml =
47
+ begin
48
+ eval('_hamlout', block)
49
+ true
50
+ rescue
51
+ false
52
+ end
53
+
54
+ if block_is_haml && is_haml?
55
+ capture_haml(*args, &block)
56
+ else
57
+ capture_without_haml(*args, &block)
58
+ end
59
+ end
60
+ alias_method :capture_without_haml, :capture
61
+ alias_method :capture, :capture_with_haml
62
+
63
+ def capture_erb_with_buffer_with_haml(*args, &block)
64
+ if is_haml?
65
+ capture_haml_with_buffer(*args, &block)
66
+ else
67
+ capture_erb_with_buffer_without_haml(*args, &block)
68
+ end
69
+ end
70
+ alias_method :capture_erb_with_buffer_without_haml, :capture_erb_with_buffer
71
+ alias_method :capture_erb_with_buffer, :capture_erb_with_buffer_with_haml
72
+ end
73
+
74
+ module TextHelper
75
+ def concat_with_haml(string, binding = nil)
76
+ if is_haml?
77
+ haml_buffer.buffer.concat(string)
78
+ else
79
+ concat_without_haml(string, binding)
80
+ end
25
81
  end
82
+ alias_method :concat_without_haml, :concat
83
+ alias_method :concat, :concat_with_haml
84
+ end
85
+ else
86
+ module CaptureHelper
87
+ def capture_with_haml(*args, &block)
88
+ if is_haml?
89
+ capture_haml(*args, &block)
90
+ else
91
+ capture_without_haml(*args, &block)
92
+ end
93
+ end
94
+ alias_method :capture_without_haml, :capture
95
+ alias_method :capture, :capture_with_haml
26
96
  end
27
- alias_method :capture_erb_with_buffer_without_haml, :capture_erb_with_buffer
28
- alias_method :capture_erb_with_buffer, :capture_erb_with_buffer_with_haml
29
97
  end
30
98
 
31
- module TextHelper
32
- def concat_with_haml(string, binding = nil)
33
- if is_haml?
34
- buffer.buffer.concat(string)
35
- else
36
- concat_without_haml(string, binding)
99
+ module TagHelper
100
+ def content_tag_with_haml(name, *args, &block)
101
+ content = content_tag_without_haml(name, *args, &block)
102
+
103
+ if is_haml? && haml_buffer.options[:preserve].include?(name.to_s)
104
+ content = Haml::Helpers.preserve content
37
105
  end
106
+
107
+ content
108
+ end
109
+ alias_method :content_tag_without_haml, :content_tag
110
+ alias_method :content_tag, :content_tag_with_haml
111
+ end
112
+
113
+ class InstanceTag
114
+ # Includes TagHelper
115
+
116
+ def haml_buffer
117
+ @template_object.send :haml_buffer
118
+ end
119
+
120
+ def is_haml?
121
+ @template_object.send :is_haml?
38
122
  end
39
- alias_method :concat_without_haml, :concat
40
- alias_method :concat, :concat_with_haml
123
+
124
+ alias_method :content_tag_without_haml, :content_tag
125
+ alias_method :content_tag, :content_tag_with_haml
41
126
  end
42
127
 
43
128
  module FormTagHelper
@@ -45,7 +130,7 @@ if defined?(ActionView) and not defined?(Merb::Plugins)
45
130
  if is_haml?
46
131
  if block_given?
47
132
  oldproc = proc
48
- proc = bind_proc do |*args|
133
+ proc = haml_bind_proc do |*args|
49
134
  concat "\n"
50
135
  tab_up
51
136
  oldproc.call(*args)
@@ -66,8 +151,8 @@ if defined?(ActionView) and not defined?(Merb::Plugins)
66
151
  module FormHelper
67
152
  def form_for_with_haml(object_name, *args, &proc)
68
153
  if block_given? && is_haml?
69
- oldproc = proc
70
- proc = bind_proc do |*args|
154
+ oldproc = proc
155
+ proc = haml_bind_proc do |*args|
71
156
  tab_up
72
157
  oldproc.call(*args)
73
158
  tab_down