haml 1.8.0 → 2.0.0

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