honkster-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 (176) hide show
  1. data/FAQ +138 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +332 -0
  4. data/Rakefile +198 -0
  5. data/VERSION.yml +4 -0
  6. data/bin/css2sass +7 -0
  7. data/bin/haml +9 -0
  8. data/bin/html2haml +7 -0
  9. data/bin/sass +8 -0
  10. data/extra/haml-mode.el +596 -0
  11. data/extra/sass-mode.el +137 -0
  12. data/init.rb +8 -0
  13. data/lib/haml/buffer.rb +255 -0
  14. data/lib/haml/engine.rb +268 -0
  15. data/lib/haml/error.rb +22 -0
  16. data/lib/haml/exec.rb +395 -0
  17. data/lib/haml/filters.rb +276 -0
  18. data/lib/haml/helpers/action_view_extensions.rb +45 -0
  19. data/lib/haml/helpers/action_view_mods.rb +181 -0
  20. data/lib/haml/helpers.rb +468 -0
  21. data/lib/haml/html.rb +218 -0
  22. data/lib/haml/precompiler.rb +901 -0
  23. data/lib/haml/shared.rb +45 -0
  24. data/lib/haml/template/patch.rb +58 -0
  25. data/lib/haml/template/plugin.rb +72 -0
  26. data/lib/haml/template.rb +51 -0
  27. data/lib/haml/util.rb +77 -0
  28. data/lib/haml/version.rb +25 -0
  29. data/lib/haml.rb +1043 -0
  30. data/lib/sass/css.rb +388 -0
  31. data/lib/sass/engine.rb +499 -0
  32. data/lib/sass/environment.rb +33 -0
  33. data/lib/sass/error.rb +35 -0
  34. data/lib/sass/plugin/merb.rb +56 -0
  35. data/lib/sass/plugin/rails.rb +24 -0
  36. data/lib/sass/plugin.rb +203 -0
  37. data/lib/sass/repl.rb +51 -0
  38. data/lib/sass/script/bool.rb +13 -0
  39. data/lib/sass/script/color.rb +97 -0
  40. data/lib/sass/script/funcall.rb +28 -0
  41. data/lib/sass/script/functions.rb +122 -0
  42. data/lib/sass/script/lexer.rb +152 -0
  43. data/lib/sass/script/literal.rb +80 -0
  44. data/lib/sass/script/number.rb +231 -0
  45. data/lib/sass/script/operation.rb +30 -0
  46. data/lib/sass/script/parser.rb +142 -0
  47. data/lib/sass/script/string.rb +18 -0
  48. data/lib/sass/script/unary_operation.rb +21 -0
  49. data/lib/sass/script/variable.rb +20 -0
  50. data/lib/sass/script.rb +38 -0
  51. data/lib/sass/tree/attr_node.rb +64 -0
  52. data/lib/sass/tree/comment_node.rb +34 -0
  53. data/lib/sass/tree/debug_node.rb +22 -0
  54. data/lib/sass/tree/directive_node.rb +50 -0
  55. data/lib/sass/tree/file_node.rb +27 -0
  56. data/lib/sass/tree/for_node.rb +29 -0
  57. data/lib/sass/tree/if_node.rb +27 -0
  58. data/lib/sass/tree/mixin_def_node.rb +18 -0
  59. data/lib/sass/tree/mixin_node.rb +34 -0
  60. data/lib/sass/tree/node.rb +99 -0
  61. data/lib/sass/tree/rule_node.rb +120 -0
  62. data/lib/sass/tree/variable_node.rb +24 -0
  63. data/lib/sass/tree/while_node.rb +20 -0
  64. data/lib/sass.rb +1062 -0
  65. data/rails/init.rb +1 -0
  66. data/test/benchmark.rb +99 -0
  67. data/test/haml/engine_test.rb +782 -0
  68. data/test/haml/helper_test.rb +224 -0
  69. data/test/haml/html2haml_test.rb +92 -0
  70. data/test/haml/markaby/standard.mab +52 -0
  71. data/test/haml/mocks/article.rb +6 -0
  72. data/test/haml/results/content_for_layout.xhtml +15 -0
  73. data/test/haml/results/eval_suppressed.xhtml +9 -0
  74. data/test/haml/results/filters.xhtml +62 -0
  75. data/test/haml/results/helpers.xhtml +93 -0
  76. data/test/haml/results/helpful.xhtml +10 -0
  77. data/test/haml/results/just_stuff.xhtml +68 -0
  78. data/test/haml/results/list.xhtml +12 -0
  79. data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
  80. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  81. data/test/haml/results/original_engine.xhtml +20 -0
  82. data/test/haml/results/partial_layout.xhtml +5 -0
  83. data/test/haml/results/partials.xhtml +21 -0
  84. data/test/haml/results/render_layout.xhtml +3 -0
  85. data/test/haml/results/silent_script.xhtml +74 -0
  86. data/test/haml/results/standard.xhtml +42 -0
  87. data/test/haml/results/tag_parsing.xhtml +23 -0
  88. data/test/haml/results/very_basic.xhtml +5 -0
  89. data/test/haml/results/whitespace_handling.xhtml +89 -0
  90. data/test/haml/rhtml/_av_partial_1.rhtml +12 -0
  91. data/test/haml/rhtml/_av_partial_2.rhtml +8 -0
  92. data/test/haml/rhtml/action_view.rhtml +62 -0
  93. data/test/haml/rhtml/standard.rhtml +54 -0
  94. data/test/haml/template_test.rb +204 -0
  95. data/test/haml/templates/_av_partial_1.haml +9 -0
  96. data/test/haml/templates/_av_partial_1_ugly.haml +9 -0
  97. data/test/haml/templates/_av_partial_2.haml +5 -0
  98. data/test/haml/templates/_av_partial_2_ugly.haml +5 -0
  99. data/test/haml/templates/_layout.erb +3 -0
  100. data/test/haml/templates/_layout_for_partial.haml +3 -0
  101. data/test/haml/templates/_partial.haml +8 -0
  102. data/test/haml/templates/_text_area.haml +3 -0
  103. data/test/haml/templates/action_view.haml +47 -0
  104. data/test/haml/templates/action_view_ugly.haml +47 -0
  105. data/test/haml/templates/breakage.haml +8 -0
  106. data/test/haml/templates/content_for_layout.haml +10 -0
  107. data/test/haml/templates/eval_suppressed.haml +11 -0
  108. data/test/haml/templates/filters.haml +66 -0
  109. data/test/haml/templates/helpers.haml +95 -0
  110. data/test/haml/templates/helpful.haml +11 -0
  111. data/test/haml/templates/just_stuff.haml +83 -0
  112. data/test/haml/templates/list.haml +12 -0
  113. data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
  114. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  115. data/test/haml/templates/original_engine.haml +17 -0
  116. data/test/haml/templates/partial_layout.haml +3 -0
  117. data/test/haml/templates/partialize.haml +1 -0
  118. data/test/haml/templates/partials.haml +12 -0
  119. data/test/haml/templates/render_layout.haml +2 -0
  120. data/test/haml/templates/silent_script.haml +40 -0
  121. data/test/haml/templates/standard.haml +42 -0
  122. data/test/haml/templates/standard_ugly.haml +1 -0
  123. data/test/haml/templates/tag_parsing.haml +21 -0
  124. data/test/haml/templates/very_basic.haml +4 -0
  125. data/test/haml/templates/whitespace_handling.haml +87 -0
  126. data/test/linked_rails.rb +12 -0
  127. data/test/sass/css2sass_test.rb +193 -0
  128. data/test/sass/engine_test.rb +655 -0
  129. data/test/sass/functions_test.rb +96 -0
  130. data/test/sass/more_results/more1.css +9 -0
  131. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  132. data/test/sass/more_results/more_import.css +29 -0
  133. data/test/sass/more_templates/_more_partial.sass +2 -0
  134. data/test/sass/more_templates/more1.sass +23 -0
  135. data/test/sass/more_templates/more_import.sass +11 -0
  136. data/test/sass/plugin_test.rb +208 -0
  137. data/test/sass/results/alt.css +4 -0
  138. data/test/sass/results/basic.css +9 -0
  139. data/test/sass/results/compact.css +5 -0
  140. data/test/sass/results/complex.css +87 -0
  141. data/test/sass/results/compressed.css +1 -0
  142. data/test/sass/results/expanded.css +19 -0
  143. data/test/sass/results/import.css +29 -0
  144. data/test/sass/results/line_numbers.css +49 -0
  145. data/test/sass/results/mixins.css +95 -0
  146. data/test/sass/results/multiline.css +24 -0
  147. data/test/sass/results/nested.css +22 -0
  148. data/test/sass/results/parent_ref.css +13 -0
  149. data/test/sass/results/script.css +16 -0
  150. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  151. data/test/sass/results/subdir/subdir.css +3 -0
  152. data/test/sass/results/units.css +11 -0
  153. data/test/sass/script_test.rb +250 -0
  154. data/test/sass/templates/_partial.sass +2 -0
  155. data/test/sass/templates/alt.sass +16 -0
  156. data/test/sass/templates/basic.sass +23 -0
  157. data/test/sass/templates/bork.sass +2 -0
  158. data/test/sass/templates/bork2.sass +2 -0
  159. data/test/sass/templates/compact.sass +17 -0
  160. data/test/sass/templates/complex.sass +309 -0
  161. data/test/sass/templates/compressed.sass +15 -0
  162. data/test/sass/templates/expanded.sass +17 -0
  163. data/test/sass/templates/import.sass +11 -0
  164. data/test/sass/templates/importee.sass +19 -0
  165. data/test/sass/templates/line_numbers.sass +13 -0
  166. data/test/sass/templates/mixins.sass +76 -0
  167. data/test/sass/templates/multiline.sass +20 -0
  168. data/test/sass/templates/nested.sass +25 -0
  169. data/test/sass/templates/parent_ref.sass +25 -0
  170. data/test/sass/templates/script.sass +101 -0
  171. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  172. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  173. data/test/sass/templates/subdir/subdir.sass +6 -0
  174. data/test/sass/templates/units.sass +11 -0
  175. data/test/test_helper.rb +21 -0
  176. metadata +272 -0
@@ -0,0 +1,137 @@
1
+ ;;; sass-mode.el --- Major mode for editing Sass files
2
+
3
+ ;; Copyright (c) 2007, 2008 Nathan Weizenbaum
4
+
5
+ ;; Author: Nathan Weizenbaum
6
+ ;; URL: http://github.com/nex3/haml/tree/master
7
+ ;; Version: 1.0
8
+ ;; Keywords: markup, language
9
+
10
+ ;;; Commentary:
11
+
12
+ ;; Because Sass's indentation schema is similar
13
+ ;; to that of YAML and Python, many indentation-related
14
+ ;; functions are similar to those in yaml-mode and python-mode.
15
+
16
+ ;; To install, save this on your load path and add the following to
17
+ ;; your .emacs file:
18
+ ;;
19
+ ;; (require 'sass-mode)
20
+
21
+ ;;; Code:
22
+
23
+ (require 'haml-mode)
24
+
25
+ ;; User definable variables
26
+
27
+ (defgroup sass nil
28
+ "Support for the Sass template language."
29
+ :group 'languages
30
+ :prefix "sass-")
31
+
32
+ (defcustom sass-mode-hook nil
33
+ "Hook run when entering Sass mode."
34
+ :type 'hook
35
+ :group 'sass)
36
+
37
+ (defcustom sass-indent-offset 2
38
+ "Amount of offset per level of indentation."
39
+ :type 'integer
40
+ :group 'sass)
41
+
42
+ (defvar sass-non-block-openers
43
+ '("^ *:[^ \t]+[ \t]+[^ \t]"
44
+ "^ *[^ \t:]+[ \t]*[=:][ \t]*[^ \t]")
45
+ "A list of regexps that match lines of Sass that couldn't have
46
+ text nested beneath them.")
47
+
48
+ ;; Font lock
49
+
50
+ (defconst sass-selector-syntax-table
51
+ (let ((st (make-syntax-table)))
52
+ (modify-syntax-entry ?- "w" st)
53
+ st))
54
+
55
+ (defconst sass-selector-font-lock-keywords
56
+ '(("&" 0 font-lock-constant-face)
57
+ ("\\.\\w+" 0 font-lock-type-face)
58
+ ("#\\w+" 0 font-lock-keyword-face)
59
+ ;; Pseudo-selectors, optionally with arguments (e.g. :first, :nth-child(12))
60
+ ("\\(::?\\w+\\)" (1 font-lock-function-name-face)
61
+ ("(\\([^)]+\\))" nil nil (1 font-lock-string-face)))
62
+ ;; Attribute selectors (e.g. p[foo=bar])
63
+ ("\\[\\([^]=]+\\)" (1 font-lock-variable-name-face)
64
+ ("[~|$^*]?=\\([^]=]+\\)" nil nil (1 font-lock-string-face)))))
65
+
66
+ (defconst sass-font-lock-keywords
67
+ '((sass-highlight-line 1 nil nil t)))
68
+
69
+ (defconst sass-line-keywords
70
+ '(("@.*" 0 font-lock-constant-face)
71
+ ("/[/*].*" 0 font-lock-comment-face)
72
+ (".*" sass-highlight-selector))
73
+ "A list of full-line Sass syntax to highlight,
74
+ used by `sass-highlight-line'.
75
+
76
+ Each item is either of the form (REGEXP SUBEXP FACE) or (REGEXP FN).
77
+ Each REGEXP is run successively on the beginning of non-whitespace
78
+ on the current line until one matches. If it has SUBEXP and FACE,
79
+ then SUBEXP is highlighted using FACE. Otherwise, FN is run.")
80
+
81
+ (defun sass-highlight-line (limit)
82
+ "Highlight a single line using some Sass single-line syntax,
83
+ taken from `sass-line-keywords'."
84
+ (save-match-data
85
+ (when (re-search-forward "^ *\\(.+\\)$" limit t)
86
+ (goto-char (match-beginning 1))
87
+ (dolist (keyword sass-line-keywords)
88
+ (destructuring-bind (keyword subexp-or-fn &optional face) keyword
89
+ (when (looking-at keyword)
90
+ (if (integerp subexp-or-fn)
91
+ (put-text-property (match-beginning subexp-or-fn)
92
+ (match-end subexp-or-fn)
93
+ 'face face)
94
+ (funcall subexp-or-fn))
95
+ (end-of-line)
96
+ (return t)))))))
97
+
98
+ (defun sass-highlight-selector ()
99
+ "Highlight a CSS selector starting at `point'
100
+ and ending at `end-of-line'."
101
+ (end-of-line)
102
+ (let ((font-lock-keywords sass-selector-font-lock-keywords)
103
+ (font-lock-syntax-table sass-selector-syntax-table))
104
+ (font-lock-fontify-region
105
+ (point) (progn (end-of-line) (point))))
106
+ t)
107
+
108
+ ;; Constants
109
+
110
+ ;; Mode setup
111
+
112
+ ;;;###autoload
113
+ (define-derived-mode sass-mode haml-mode "Sass"
114
+ "Major mode for editing Sass files."
115
+ (set-syntax-table (make-syntax-table))
116
+ (setq font-lock-extend-region-functions
117
+ '(font-lock-extend-region-wholelines font-lock-extend-region-multiline))
118
+ (setq font-lock-multiline nil)
119
+ (setq comment-start "/*")
120
+ (set (make-local-variable 'haml-indent-function) 'sass-indent-p)
121
+ (set (make-local-variable 'haml-indent-offset) sass-indent-offset)
122
+ (setq font-lock-defaults '(sass-font-lock-keywords nil t)))
123
+
124
+ ;; Indentation
125
+
126
+ (defun sass-indent-p ()
127
+ "Returns t if the current line can have lines nested beneath it."
128
+ (loop for opener in sass-non-block-openers
129
+ unless (looking-at opener) return t
130
+ return nil))
131
+
132
+ ;;;###autoload
133
+ (add-to-list 'auto-mode-alist '("\\.sass$" . sass-mode))
134
+
135
+ ;; Setup/Activation
136
+ (provide 'sass-mode)
137
+ ;;; sass-mode.el ends here
data/init.rb ADDED
@@ -0,0 +1,8 @@
1
+ begin
2
+ require File.join(File.dirname(__FILE__), 'lib', 'haml') # From here
3
+ rescue LoadError
4
+ require 'haml' # From gem
5
+ end
6
+
7
+ # Load Haml and Sass
8
+ Haml.init_rails(binding)
@@ -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
data/lib/haml/error.rb ADDED
@@ -0,0 +1,22 @@
1
+ module Haml
2
+ # An exception raised by Haml code.
3
+ class Error < StandardError
4
+ # :stopdoc:
5
+
6
+ # By default, an error is taken to refer to the line of the template
7
+ # that was being processed when the exception was raised.
8
+ # However, if line is non-nil, it + 1 is used instead.
9
+ attr_reader :line
10
+
11
+ def initialize(message = nil, line = nil)
12
+ super(message)
13
+ @line = line
14
+ end
15
+ # :startdoc:
16
+ end
17
+
18
+ # SyntaxError is the type of exception raised when Haml encounters an
19
+ # ill-formatted document.
20
+ # It's not particularly interesting, except in that it includes Haml::Error.
21
+ class SyntaxError < Haml::Error; end
22
+ end