merbjedi-haml 2.1.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 (177) hide show
  1. data/FAQ +138 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +332 -0
  4. data/REVISION +1 -0
  5. data/Rakefile +184 -0
  6. data/VERSION +1 -0
  7. data/bin/css2sass +7 -0
  8. data/bin/haml +9 -0
  9. data/bin/html2haml +7 -0
  10. data/bin/sass +8 -0
  11. data/extra/haml-mode.el +434 -0
  12. data/extra/sass-mode.el +98 -0
  13. data/init.rb +8 -0
  14. data/lib/haml.rb +1025 -0
  15. data/lib/haml/buffer.rb +255 -0
  16. data/lib/haml/engine.rb +268 -0
  17. data/lib/haml/error.rb +22 -0
  18. data/lib/haml/exec.rb +395 -0
  19. data/lib/haml/filters.rb +276 -0
  20. data/lib/haml/helpers.rb +465 -0
  21. data/lib/haml/helpers/action_view_extensions.rb +45 -0
  22. data/lib/haml/helpers/action_view_mods.rb +181 -0
  23. data/lib/haml/html.rb +218 -0
  24. data/lib/haml/precompiler.rb +896 -0
  25. data/lib/haml/shared.rb +45 -0
  26. data/lib/haml/template.rb +51 -0
  27. data/lib/haml/template/patch.rb +58 -0
  28. data/lib/haml/template/plugin.rb +72 -0
  29. data/lib/haml/util.rb +77 -0
  30. data/lib/haml/version.rb +47 -0
  31. data/lib/sass.rb +1062 -0
  32. data/lib/sass/css.rb +388 -0
  33. data/lib/sass/engine.rb +501 -0
  34. data/lib/sass/environment.rb +33 -0
  35. data/lib/sass/error.rb +35 -0
  36. data/lib/sass/plugin.rb +203 -0
  37. data/lib/sass/plugin/merb.rb +56 -0
  38. data/lib/sass/plugin/rails.rb +24 -0
  39. data/lib/sass/repl.rb +44 -0
  40. data/lib/sass/script.rb +38 -0
  41. data/lib/sass/script/bool.rb +13 -0
  42. data/lib/sass/script/color.rb +97 -0
  43. data/lib/sass/script/funcall.rb +28 -0
  44. data/lib/sass/script/functions.rb +122 -0
  45. data/lib/sass/script/lexer.rb +144 -0
  46. data/lib/sass/script/literal.rb +60 -0
  47. data/lib/sass/script/number.rb +231 -0
  48. data/lib/sass/script/operation.rb +30 -0
  49. data/lib/sass/script/parser.rb +142 -0
  50. data/lib/sass/script/string.rb +42 -0
  51. data/lib/sass/script/unary_operation.rb +21 -0
  52. data/lib/sass/script/variable.rb +20 -0
  53. data/lib/sass/tree/attr_node.rb +64 -0
  54. data/lib/sass/tree/comment_node.rb +30 -0
  55. data/lib/sass/tree/debug_node.rb +22 -0
  56. data/lib/sass/tree/directive_node.rb +50 -0
  57. data/lib/sass/tree/file_node.rb +27 -0
  58. data/lib/sass/tree/for_node.rb +29 -0
  59. data/lib/sass/tree/if_node.rb +27 -0
  60. data/lib/sass/tree/mixin_def_node.rb +18 -0
  61. data/lib/sass/tree/mixin_node.rb +34 -0
  62. data/lib/sass/tree/node.rb +97 -0
  63. data/lib/sass/tree/rule_node.rb +120 -0
  64. data/lib/sass/tree/variable_node.rb +24 -0
  65. data/lib/sass/tree/while_node.rb +20 -0
  66. data/rails/init.rb +1 -0
  67. data/test/benchmark.rb +99 -0
  68. data/test/haml/engine_test.rb +852 -0
  69. data/test/haml/helper_test.rb +224 -0
  70. data/test/haml/html2haml_test.rb +92 -0
  71. data/test/haml/markaby/standard.mab +52 -0
  72. data/test/haml/mocks/article.rb +6 -0
  73. data/test/haml/results/content_for_layout.xhtml +15 -0
  74. data/test/haml/results/eval_suppressed.xhtml +9 -0
  75. data/test/haml/results/filters.xhtml +62 -0
  76. data/test/haml/results/helpers.xhtml +93 -0
  77. data/test/haml/results/helpful.xhtml +10 -0
  78. data/test/haml/results/just_stuff.xhtml +68 -0
  79. data/test/haml/results/list.xhtml +12 -0
  80. data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
  81. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  82. data/test/haml/results/original_engine.xhtml +20 -0
  83. data/test/haml/results/partial_layout.xhtml +5 -0
  84. data/test/haml/results/partials.xhtml +21 -0
  85. data/test/haml/results/render_layout.xhtml +3 -0
  86. data/test/haml/results/silent_script.xhtml +74 -0
  87. data/test/haml/results/standard.xhtml +42 -0
  88. data/test/haml/results/tag_parsing.xhtml +23 -0
  89. data/test/haml/results/very_basic.xhtml +5 -0
  90. data/test/haml/results/whitespace_handling.xhtml +89 -0
  91. data/test/haml/rhtml/_av_partial_1.rhtml +12 -0
  92. data/test/haml/rhtml/_av_partial_2.rhtml +8 -0
  93. data/test/haml/rhtml/action_view.rhtml +62 -0
  94. data/test/haml/rhtml/standard.rhtml +54 -0
  95. data/test/haml/template_test.rb +204 -0
  96. data/test/haml/templates/_av_partial_1.haml +9 -0
  97. data/test/haml/templates/_av_partial_1_ugly.haml +9 -0
  98. data/test/haml/templates/_av_partial_2.haml +5 -0
  99. data/test/haml/templates/_av_partial_2_ugly.haml +5 -0
  100. data/test/haml/templates/_layout.erb +3 -0
  101. data/test/haml/templates/_layout_for_partial.haml +3 -0
  102. data/test/haml/templates/_partial.haml +8 -0
  103. data/test/haml/templates/_text_area.haml +3 -0
  104. data/test/haml/templates/action_view.haml +47 -0
  105. data/test/haml/templates/action_view_ugly.haml +47 -0
  106. data/test/haml/templates/breakage.haml +8 -0
  107. data/test/haml/templates/content_for_layout.haml +10 -0
  108. data/test/haml/templates/eval_suppressed.haml +11 -0
  109. data/test/haml/templates/filters.haml +66 -0
  110. data/test/haml/templates/helpers.haml +95 -0
  111. data/test/haml/templates/helpful.haml +11 -0
  112. data/test/haml/templates/just_stuff.haml +83 -0
  113. data/test/haml/templates/list.haml +12 -0
  114. data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
  115. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  116. data/test/haml/templates/original_engine.haml +17 -0
  117. data/test/haml/templates/partial_layout.haml +3 -0
  118. data/test/haml/templates/partialize.haml +1 -0
  119. data/test/haml/templates/partials.haml +12 -0
  120. data/test/haml/templates/render_layout.haml +2 -0
  121. data/test/haml/templates/silent_script.haml +40 -0
  122. data/test/haml/templates/standard.haml +42 -0
  123. data/test/haml/templates/standard_ugly.haml +42 -0
  124. data/test/haml/templates/tag_parsing.haml +21 -0
  125. data/test/haml/templates/very_basic.haml +4 -0
  126. data/test/haml/templates/whitespace_handling.haml +87 -0
  127. data/test/linked_rails.rb +12 -0
  128. data/test/sass/css2sass_test.rb +193 -0
  129. data/test/sass/engine_test.rb +752 -0
  130. data/test/sass/functions_test.rb +96 -0
  131. data/test/sass/more_results/more1.css +9 -0
  132. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  133. data/test/sass/more_results/more_import.css +29 -0
  134. data/test/sass/more_templates/_more_partial.sass +2 -0
  135. data/test/sass/more_templates/more1.sass +23 -0
  136. data/test/sass/more_templates/more_import.sass +11 -0
  137. data/test/sass/plugin_test.rb +208 -0
  138. data/test/sass/results/alt.css +4 -0
  139. data/test/sass/results/basic.css +9 -0
  140. data/test/sass/results/compact.css +5 -0
  141. data/test/sass/results/complex.css +87 -0
  142. data/test/sass/results/compressed.css +1 -0
  143. data/test/sass/results/expanded.css +19 -0
  144. data/test/sass/results/import.css +29 -0
  145. data/test/sass/results/line_numbers.css +49 -0
  146. data/test/sass/results/mixins.css +95 -0
  147. data/test/sass/results/multiline.css +24 -0
  148. data/test/sass/results/nested.css +22 -0
  149. data/test/sass/results/parent_ref.css +13 -0
  150. data/test/sass/results/script.css +16 -0
  151. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  152. data/test/sass/results/subdir/subdir.css +3 -0
  153. data/test/sass/results/units.css +11 -0
  154. data/test/sass/script_test.rb +152 -0
  155. data/test/sass/templates/_partial.sass +2 -0
  156. data/test/sass/templates/alt.sass +16 -0
  157. data/test/sass/templates/basic.sass +23 -0
  158. data/test/sass/templates/bork.sass +2 -0
  159. data/test/sass/templates/bork2.sass +2 -0
  160. data/test/sass/templates/compact.sass +17 -0
  161. data/test/sass/templates/complex.sass +309 -0
  162. data/test/sass/templates/compressed.sass +15 -0
  163. data/test/sass/templates/expanded.sass +17 -0
  164. data/test/sass/templates/import.sass +11 -0
  165. data/test/sass/templates/importee.sass +19 -0
  166. data/test/sass/templates/line_numbers.sass +13 -0
  167. data/test/sass/templates/mixins.sass +76 -0
  168. data/test/sass/templates/multiline.sass +20 -0
  169. data/test/sass/templates/nested.sass +25 -0
  170. data/test/sass/templates/parent_ref.sass +25 -0
  171. data/test/sass/templates/script.sass +101 -0
  172. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  173. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  174. data/test/sass/templates/subdir/subdir.sass +6 -0
  175. data/test/sass/templates/units.sass +11 -0
  176. data/test/test_helper.rb +21 -0
  177. metadata +273 -0
@@ -0,0 +1,255 @@
1
+ module Haml
2
+ # This class is used only internally. It holds the buffer of XHTML that
3
+ # is eventually output by Haml::Engine's to_html method. It's called
4
+ # from within the precompiled code, and helps reduce the amount of
5
+ # processing done within instance_eval'd code.
6
+ class Buffer
7
+ include Haml::Helpers
8
+ include Haml::Util
9
+
10
+ # The string that holds the compiled XHTML. This is aliased as
11
+ # _erbout for compatibility with ERB-specific code.
12
+ attr_accessor :buffer
13
+
14
+ # The options hash passed in from Haml::Engine.
15
+ attr_accessor :options
16
+
17
+ # The Buffer for the enclosing Haml document.
18
+ # This is set for partials and similar sorts of nested templates.
19
+ # It's nil at the top level (see #toplevel?).
20
+ attr_accessor :upper
21
+
22
+ # See #active?
23
+ attr_writer :active
24
+
25
+ # True if the format is XHTML
26
+ def xhtml?
27
+ not html?
28
+ end
29
+
30
+ # True if the format is any flavor of HTML
31
+ def html?
32
+ html4? or html5?
33
+ end
34
+
35
+ # True if the format is HTML4
36
+ def html4?
37
+ @options[:format] == :html4
38
+ end
39
+
40
+ # True if the format is HTML5
41
+ def html5?
42
+ @options[:format] == :html5
43
+ end
44
+
45
+ # True if this buffer is a top-level template,
46
+ # as opposed to a nested partial.
47
+ def toplevel?
48
+ upper.nil?
49
+ end
50
+
51
+ # True if this buffer is currently being used to render a Haml template.
52
+ # However, this returns false if a subtemplate is being rendered,
53
+ # even if it's a subtemplate of this buffer's template.
54
+ def active?
55
+ @active
56
+ end
57
+
58
+ # Gets the current tabulation of the document.
59
+ def tabulation
60
+ @real_tabs + @tabulation
61
+ end
62
+
63
+ # Sets the current tabulation of the document.
64
+ def tabulation=(val)
65
+ val = val - @real_tabs
66
+ @tabulation = val > -1 ? val : 0
67
+ end
68
+
69
+ # Creates a new buffer.
70
+ def initialize(upper = nil, options = {})
71
+ @active = true
72
+ @upper = upper
73
+ @options = {
74
+ :attr_wrapper => "'",
75
+ :ugly => false,
76
+ :format => :xhtml
77
+ }.merge options
78
+ @buffer = ""
79
+ @tabulation = 0
80
+
81
+ # The number of tabs that Engine thinks we should have
82
+ # @real_tabs + @tabulation is the number of tabs actually output
83
+ @real_tabs = 0
84
+ end
85
+
86
+ def push_text(text, tab_change, dont_tab_up)
87
+ if @tabulation > 0
88
+ # Have to push every line in by the extra user set tabulation.
89
+ # Don't push lines with just whitespace, though,
90
+ # because that screws up precompiled indentation.
91
+ text.gsub!(/^(?!\s+$)/m, tabs)
92
+ text.sub!(tabs, '') if dont_tab_up
93
+ end
94
+
95
+ @buffer << text
96
+ @real_tabs += tab_change
97
+ end
98
+
99
+ def adjust_tabs(tab_change)
100
+ @real_tabs += tab_change
101
+ end
102
+
103
+ Haml::Util.def_static_method(self, :format_script, [:result],
104
+ :preserve_script, :in_tag, :preserve_tag, :escape_html,
105
+ :nuke_inner_whitespace, :interpolated, :ugly, <<RUBY)
106
+ <% unless ugly %>
107
+ # If we're interpolated,
108
+ # then the custom tabulation is handled in #push_text.
109
+ # The easiest way to avoid it here is to reset @tabulation.
110
+ <% if interpolated %>
111
+ old_tabulation = @tabulation
112
+ @tabulation = 0
113
+ <% end %>
114
+
115
+ tabulation = @real_tabs
116
+ result = result.to_s.<% if nuke_inner_whitespace %>strip<% else %>rstrip<% end %>
117
+ <% else %>
118
+ result = result.to_s<% if nuke_inner_whitespace %>.strip<% end %>
119
+ <% end %>
120
+
121
+ <% if escape_html %> result = html_escape(result) <% end %>
122
+
123
+ <% if preserve_tag %>
124
+ result = Haml::Helpers.preserve(result)
125
+ <% elsif preserve_script %>
126
+ result = Haml::Helpers.find_and_preserve(result, options[:preserve])
127
+ <% end %>
128
+
129
+ <% if ugly %>
130
+ return result
131
+ <% else %>
132
+
133
+ has_newline = result.include?("\\n")
134
+ <% if in_tag && !nuke_inner_whitespace %>
135
+ <% unless preserve_tag %> if !has_newline <% end %>
136
+ @real_tabs -= 1
137
+ <% if interpolated %> @tabulation = old_tabulation <% end %>
138
+ return result
139
+ <% unless preserve_tag %> end <% end %>
140
+ <% end %>
141
+
142
+ # Precompiled tabulation may be wrong
143
+ <% if !interpolated && !in_tag %>
144
+ result = tabs + result if @tabulation > 0
145
+ <% end %>
146
+
147
+ if has_newline
148
+ result = result.gsub "\\n", "\\n" + tabs(tabulation)
149
+
150
+ # Add tabulation if it wasn't precompiled
151
+ <% if in_tag && !nuke_inner_whitespace %> result = tabs(tabulation) + result <% end %>
152
+ end
153
+
154
+ <% if in_tag && !nuke_inner_whitespace %>
155
+ result = "\\n\#{result}\\n\#{tabs(tabulation-1)}"
156
+ @real_tabs -= 1
157
+ <% end %>
158
+ <% if interpolated %> @tabulation = old_tabulation <% end %>
159
+ result
160
+ <% end %>
161
+ RUBY
162
+
163
+ # Takes the various information about the opening tag for an
164
+ # element, formats it, and adds it to the buffer.
165
+ def open_tag(name, self_closing, try_one_line, preserve_tag, escape_html, class_id,
166
+ nuke_outer_whitespace, nuke_inner_whitespace, obj_ref, content, *attributes_hashes)
167
+ tabulation = @real_tabs
168
+
169
+ attributes = class_id
170
+ attributes_hashes.each do |old|
171
+ self.class.merge_attrs(attributes, to_hash(old.map {|k, v| [k.to_s, v]}))
172
+ end
173
+ self.class.merge_attrs(attributes, parse_object_ref(obj_ref)) if obj_ref
174
+
175
+ if self_closing && xhtml?
176
+ str = " />" + (nuke_outer_whitespace ? "" : "\n")
177
+ else
178
+ str = ">" + ((if self_closing && html?
179
+ nuke_outer_whitespace
180
+ else
181
+ try_one_line || preserve_tag || nuke_inner_whitespace
182
+ end) ? "" : "\n")
183
+ end
184
+
185
+ attributes = Precompiler.build_attributes(html?, @options[:attr_wrapper], attributes)
186
+ @buffer << "#{nuke_outer_whitespace || @options[:ugly] ? '' : tabs(tabulation)}<#{name}#{attributes}#{str}"
187
+
188
+ if content
189
+ @buffer << "#{content}</#{name}>" << (nuke_outer_whitespace ? "" : "\n")
190
+ return
191
+ end
192
+
193
+ @real_tabs += 1 unless self_closing || nuke_inner_whitespace
194
+ end
195
+
196
+ def self.merge_attrs(to, from)
197
+ if to['id'] && from['id']
198
+ to['id'] << '_' << from.delete('id')
199
+ elsif to['id'] || from['id']
200
+ from['id'] ||= to['id']
201
+ end
202
+
203
+ if to['class'] && from['class']
204
+ # Make sure we don't duplicate class names
205
+ from['class'] = (from['class'].split(' ') | to['class'].split(' ')).join(' ')
206
+ elsif to['class'] || from['class']
207
+ from['class'] ||= to['class']
208
+ end
209
+
210
+ to.merge!(from)
211
+ end
212
+
213
+ private
214
+
215
+ # Some of these methods are exposed as public class methods
216
+ # so they can be re-used in helpers.
217
+
218
+ @@tab_cache = {}
219
+ # Gets <tt>count</tt> tabs. Mostly for internal use.
220
+ def tabs(count = 0)
221
+ tabs = [count + @tabulation, 0].max
222
+ @@tab_cache[tabs] ||= ' ' * tabs
223
+ end
224
+
225
+ # Takes an array of objects and uses the class and id of the first
226
+ # one to create an attributes hash.
227
+ # The second object, if present, is used as a prefix,
228
+ # just like you can do with dom_id() and dom_class() in Rails
229
+ def parse_object_ref(ref)
230
+ prefix = ref[1]
231
+ ref = ref[0]
232
+ # Let's make sure the value isn't nil. If it is, return the default Hash.
233
+ return {} if ref.nil?
234
+ class_name = underscore(ref.class)
235
+ id = "#{class_name}_#{ref.id || 'new'}"
236
+ if prefix
237
+ class_name = "#{ prefix }_#{ class_name}"
238
+ id = "#{ prefix }_#{ id }"
239
+ end
240
+
241
+ {'id' => id, 'class' => class_name}
242
+ end
243
+
244
+ # Changes a word from camel case to underscores.
245
+ # Based on the method of the same name in Rails' Inflector,
246
+ # but copied here so it'll run properly without Rails.
247
+ def underscore(camel_cased_word)
248
+ camel_cased_word.to_s.gsub(/::/, '_').
249
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
250
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
251
+ tr("-", "_").
252
+ downcase
253
+ end
254
+ end
255
+ end
@@ -0,0 +1,268 @@
1
+ require 'haml/helpers'
2
+ require 'haml/buffer'
3
+ require 'haml/precompiler'
4
+ require 'haml/filters'
5
+ require 'haml/error'
6
+
7
+ module Haml
8
+ # This is the class where all the parsing and processing of the Haml
9
+ # template is done. It can be directly used by the user by creating a
10
+ # new instance and calling <tt>to_html</tt> to render the template. For example:
11
+ #
12
+ # template = File.read('templates/really_cool_template.haml')
13
+ # haml_engine = Haml::Engine.new(template)
14
+ # output = haml_engine.to_html
15
+ # puts output
16
+ class Engine
17
+ include Precompiler
18
+
19
+ # Allow reading and writing of the options hash
20
+ attr :options, true
21
+
22
+ # This string contains the source code that is evaluated
23
+ # to produce the Haml document.
24
+ attr :precompiled, true
25
+
26
+ # A string containing the indentation used for the Haml document.
27
+ # nil if the indentation is ambiguous
28
+ # (for example, for a single-level document).
29
+ attr :indentation, true
30
+
31
+ # True if the format is XHTML
32
+ def xhtml?
33
+ not html?
34
+ end
35
+
36
+ # True if the format is any flavor of HTML
37
+ def html?
38
+ html4? or html5?
39
+ end
40
+
41
+ # True if the format is HTML4
42
+ def html4?
43
+ @options[:format] == :html4
44
+ end
45
+
46
+ # True if the format is HTML5
47
+ def html5?
48
+ @options[:format] == :html5
49
+ end
50
+
51
+ # Creates a new instace of Haml::Engine that will compile the given
52
+ # template string when <tt>render</tt> is called.
53
+ # See the Haml module documentation for available options.
54
+ #
55
+ #--
56
+ # When adding options, remember to add information about them
57
+ # to lib/haml.rb!
58
+ #++
59
+ #
60
+ def initialize(template, options = {})
61
+ @options = {
62
+ :suppress_eval => false,
63
+ :attr_wrapper => "'",
64
+
65
+ # Don't forget to update the docs in lib/haml.rb if you update these
66
+ :autoclose => %w[meta img link br hr input area param col base],
67
+ :preserve => %w[textarea pre],
68
+
69
+ :filename => '(haml)',
70
+ :line => 1,
71
+ :ugly => false,
72
+ :format => :xhtml,
73
+ :escape_html => false
74
+ }
75
+ @options.merge! options
76
+ @index = 0
77
+
78
+ unless [:xhtml, :html4, :html5].include?(@options[:format])
79
+ raise Haml::Error, "Invalid format #{@options[:format].inspect}"
80
+ end
81
+
82
+ # :eod is a special end-of-document marker
83
+ @template = (template.rstrip).split(/\r\n|\r|\n/) + [:eod, :eod]
84
+ @template_index = 0
85
+ @to_close_stack = []
86
+ @output_tabs = 0
87
+ @template_tabs = 0
88
+ @flat = false
89
+ @newlines = 0
90
+ @precompiled = ''
91
+ @to_merge = []
92
+ @tab_change = 0
93
+ @temp_count = 0
94
+
95
+ if @options[:filters]
96
+ warn <<END
97
+ DEPRECATION WARNING:
98
+ The Haml :filters option is deprecated and will be removed in version 2.2.
99
+ Filters are now automatically registered.
100
+ END
101
+ end
102
+
103
+ precompile
104
+ rescue Haml::Error => e
105
+ e.backtrace.unshift "#{@options[:filename]}:#{(e.line ? e.line + 1 : @index) + @options[:line] - 1}" if @index
106
+ raise
107
+ end
108
+
109
+ # Processes the template and returns the result as a string.
110
+ #
111
+ # +scope+ is the context in which the template is evaluated.
112
+ # If it's a Binding or Proc object,
113
+ # Haml uses it as the second argument to Kernel#eval;
114
+ # otherwise, Haml just uses its #instance_eval context.
115
+ #
116
+ # Note that Haml modifies the evaluation context
117
+ # (either the scope object or the "self" object of the scope binding).
118
+ # It extends Haml::Helpers, and various instance variables are set
119
+ # (all prefixed with "haml").
120
+ # For example:
121
+ #
122
+ # s = "foobar"
123
+ # Haml::Engine.new("%p= upcase").render(s) #=> "<p>FOOBAR</p>"
124
+ #
125
+ # # s now extends Haml::Helpers
126
+ # s.responds_to?(:html_attrs) #=> true
127
+ #
128
+ # +locals+ is a hash of local variables to make available to the template.
129
+ # For example:
130
+ #
131
+ # Haml::Engine.new("%p= foo").render(Object.new, :foo => "Hello, world!") #=> "<p>Hello, world!</p>"
132
+ #
133
+ # If a block is passed to render,
134
+ # that block is run when +yield+ is called
135
+ # within the template.
136
+ #
137
+ # Due to some Ruby quirks,
138
+ # if scope is a Binding or Proc object and a block is given,
139
+ # the evaluation context may not be quite what the user expects.
140
+ # In particular, it's equivalent to passing <tt>eval("self", scope)</tt> as scope.
141
+ # This won't have an effect in most cases,
142
+ # but if you're relying on local variables defined in the context of scope,
143
+ # they won't work.
144
+ def render(scope = Object.new, locals = {}, &block)
145
+ buffer = Haml::Buffer.new(scope.instance_variable_get('@haml_buffer'), options_for_buffer)
146
+
147
+ if scope.is_a?(Binding) || scope.is_a?(Proc)
148
+ scope_object = eval("self", scope)
149
+ scope = scope_object.instance_eval{binding} if block_given?
150
+ else
151
+ scope_object = scope
152
+ scope = scope_object.instance_eval{binding}
153
+ end
154
+
155
+ set_locals(locals.merge(:_hamlout => buffer, :_erbout => buffer.buffer), scope, scope_object)
156
+
157
+ scope_object.instance_eval do
158
+ extend Haml::Helpers
159
+ @haml_buffer = buffer
160
+ end
161
+
162
+ eval(@precompiled, scope, @options[:filename], @options[:line])
163
+
164
+ # Get rid of the current buffer
165
+ scope_object.instance_eval do
166
+ @haml_buffer = buffer.upper
167
+ end
168
+
169
+ buffer.buffer
170
+ end
171
+ alias_method :to_html, :render
172
+
173
+ # Returns a proc that, when called,
174
+ # renders the template and returns the result as a string.
175
+ #
176
+ # +scope+ works the same as it does for render.
177
+ #
178
+ # The first argument of the returned proc is a hash of local variable names to values.
179
+ # However, due to an unfortunate Ruby quirk,
180
+ # the local variables which can be assigned must be pre-declared.
181
+ # This is done with the +local_names+ argument.
182
+ # For example:
183
+ #
184
+ # # This works
185
+ # Haml::Engine.new("%p= foo").render_proc(Object.new, :foo).call :foo => "Hello!"
186
+ # #=> "<p>Hello!</p>"
187
+ #
188
+ # # This doesn't
189
+ # Haml::Engine.new("%p= foo").render_proc.call :foo => "Hello!"
190
+ # #=> NameError: undefined local variable or method `foo'
191
+ #
192
+ # The proc doesn't take a block;
193
+ # any yields in the template will fail.
194
+ def render_proc(scope = Object.new, *local_names)
195
+ if scope.is_a?(Binding) || scope.is_a?(Proc)
196
+ scope_object = eval("self", scope)
197
+ else
198
+ scope_object = scope
199
+ scope = scope_object.instance_eval{binding}
200
+ end
201
+
202
+ eval("Proc.new { |*_haml_locals| _haml_locals = _haml_locals[0] || {};" +
203
+ precompiled_with_ambles(local_names) + "}\n", scope, @options[:filename], @options[:line])
204
+ end
205
+
206
+ # Defines a method on +object+
207
+ # with the given name
208
+ # that renders the template and returns the result as a string.
209
+ #
210
+ # If +object+ is a class or module,
211
+ # the method will instead by defined as an instance method.
212
+ # For example:
213
+ #
214
+ # t = Time.now
215
+ # Haml::Engine.new("%p\n Today's date is\n .date= self.to_s").def_method(t, :render)
216
+ # t.render #=> "<p>\n Today's date is\n <div class='date'>Fri Nov 23 18:28:29 -0800 2007</div>\n</p>\n"
217
+ #
218
+ # Haml::Engine.new(".upcased= upcase").def_method(String, :upcased_div)
219
+ # "foobar".upcased_div #=> "<div class='upcased'>FOOBAR</div>\n"
220
+ #
221
+ # The first argument of the defined method is a hash of local variable names to values.
222
+ # However, due to an unfortunate Ruby quirk,
223
+ # the local variables which can be assigned must be pre-declared.
224
+ # This is done with the +local_names+ argument.
225
+ # For example:
226
+ #
227
+ # # This works
228
+ # obj = Object.new
229
+ # Haml::Engine.new("%p= foo").def_method(obj, :render, :foo)
230
+ # obj.render(:foo => "Hello!") #=> "<p>Hello!</p>"
231
+ #
232
+ # # This doesn't
233
+ # obj = Object.new
234
+ # Haml::Engine.new("%p= foo").def_method(obj, :render)
235
+ # obj.render(:foo => "Hello!") #=> NameError: undefined local variable or method `foo'
236
+ #
237
+ # Note that Haml modifies the evaluation context
238
+ # (either the scope object or the "self" object of the scope binding).
239
+ # It extends Haml::Helpers, and various instance variables are set
240
+ # (all prefixed with "haml").
241
+ def def_method(object, name, *local_names)
242
+ method = object.is_a?(Module) ? :module_eval : :instance_eval
243
+
244
+ object.send(method, "def #{name}(_haml_locals = {}); #{precompiled_with_ambles(local_names)}; end",
245
+ @options[:filename], @options[:line])
246
+ end
247
+
248
+ private
249
+
250
+ def set_locals(locals, scope, scope_object)
251
+ scope_object.send(:instance_variable_set, '@_haml_locals', locals)
252
+ set_locals = locals.keys.map { |k| "#{k} = @_haml_locals[#{k.inspect}]" }.join("\n")
253
+ eval(set_locals, scope)
254
+ end
255
+
256
+ # Returns a hash of options that Haml::Buffer cares about.
257
+ # This should remain loadable from #inspect.
258
+ def options_for_buffer
259
+ {
260
+ :autoclose => @options[:autoclose],
261
+ :preserve => @options[:preserve],
262
+ :attr_wrapper => @options[:attr_wrapper],
263
+ :ugly => @options[:ugly],
264
+ :format => @options[:format]
265
+ }
266
+ end
267
+ end
268
+ end