radiant 0.6.6 → 0.6.7

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of radiant might be problematic. Click here for more details.

Files changed (208) hide show
  1. data/CHANGELOG +15 -0
  2. data/CONTRIBUTORS +8 -0
  3. data/app/controllers/admin/page_controller.rb +1 -11
  4. data/app/controllers/admin/welcome_controller.rb +5 -6
  5. data/app/controllers/application.rb +2 -0
  6. data/app/controllers/site_controller.rb +1 -0
  7. data/app/helpers/admin/page_helper.rb +36 -0
  8. data/app/helpers/admin/regions_helper.rb +28 -0
  9. data/app/helpers/admin/user_helper.rb +6 -0
  10. data/app/helpers/application_helper.rb +2 -1
  11. data/app/models/user.rb +7 -8
  12. data/app/views/admin/extension/index.html.haml +28 -0
  13. data/app/views/admin/layout/edit.html.haml +44 -0
  14. data/app/views/admin/layout/index.html.haml +25 -0
  15. data/app/views/admin/layout/remove.html.haml +16 -0
  16. data/app/views/admin/page/_meta_row.html.haml +6 -0
  17. data/app/views/admin/page/_node.html.haml +25 -0
  18. data/app/views/admin/page/_part.html.haml +17 -0
  19. data/app/views/admin/page/_tag_reference.html.haml +3 -0
  20. data/app/views/admin/page/children.html.haml +2 -0
  21. data/app/views/admin/page/edit.html.haml +114 -0
  22. data/app/views/admin/page/index.html.haml +28 -0
  23. data/app/views/admin/page/remove.html.haml +20 -0
  24. data/app/views/admin/snippet/edit.html.haml +36 -0
  25. data/app/views/admin/snippet/index.html.haml +20 -0
  26. data/app/views/admin/snippet/remove.html.haml +19 -0
  27. data/app/views/admin/user/edit.html.haml +80 -0
  28. data/app/views/admin/user/index.html.haml +23 -0
  29. data/app/views/admin/user/preferences.html.haml +28 -0
  30. data/app/views/admin/user/remove.html.haml +18 -0
  31. data/app/views/admin/welcome/login.html.haml +44 -0
  32. data/app/views/layouts/application.html.haml +54 -0
  33. data/app/views/site/not_found.html.haml +3 -0
  34. data/db/migrate/019_add_salt_to_users.rb +11 -0
  35. data/db/schema.rb +2 -1
  36. data/lib/login_system.rb +1 -0
  37. data/lib/radiant.rb +1 -1
  38. data/lib/radiant/admin_ui.rb +65 -0
  39. data/lib/radiant/admin_ui/region_partials.rb +18 -0
  40. data/lib/radiant/admin_ui/region_set.rb +35 -0
  41. data/lib/tasks/extensions.rake +33 -0
  42. data/public/javascripts/admin/admin.js +8 -2
  43. data/public/javascripts/admin/sitemap.js +2 -1
  44. data/public/stylesheets/admin/main.css +4 -0
  45. data/spec/controllers/admin/abstract_model_controller_spec.rb +2 -0
  46. data/spec/controllers/admin/page_controller_spec.rb +3 -28
  47. data/spec/controllers/admin/user_controller_spec.rb +1 -1
  48. data/spec/controllers/admin/welcome_controller_spec.rb +26 -0
  49. data/spec/helpers/admin/page_helper_spec.rb +4 -0
  50. data/spec/helpers/admin/regions_helper_spec.rb +47 -0
  51. data/spec/helpers/admin/user_helper_spec.rb +7 -0
  52. data/spec/helpers/application_helper_spec.rb +7 -3
  53. data/spec/lib/login_system_spec.rb +5 -0
  54. data/spec/lib/radiant/admin_ui/region_partials_spec.rb +35 -0
  55. data/spec/lib/radiant/admin_ui/region_set_spec.rb +61 -0
  56. data/spec/lib/radiant/admin_ui_spec.rb +74 -18
  57. data/spec/models/user_spec.rb +11 -4
  58. data/spec/scenarios/users_scenario.rb +2 -2
  59. data/vendor/plugins/haml/MIT-LICENSE +20 -0
  60. data/vendor/plugins/haml/README.rdoc +319 -0
  61. data/vendor/plugins/haml/Rakefile +158 -0
  62. data/vendor/plugins/haml/TODO +9 -0
  63. data/vendor/plugins/haml/VERSION +1 -0
  64. data/vendor/plugins/haml/bin/css2sass +7 -0
  65. data/vendor/plugins/haml/bin/haml +8 -0
  66. data/vendor/plugins/haml/bin/html2haml +7 -0
  67. data/vendor/plugins/haml/bin/sass +8 -0
  68. data/vendor/plugins/haml/extra/haml-mode.el +328 -0
  69. data/vendor/plugins/haml/extra/sass-mode.el +88 -0
  70. data/vendor/plugins/haml/init.rb +2 -0
  71. data/vendor/plugins/haml/lib/haml.rb +977 -0
  72. data/vendor/plugins/haml/lib/haml/buffer.rb +229 -0
  73. data/vendor/plugins/haml/lib/haml/engine.rb +274 -0
  74. data/vendor/plugins/haml/lib/haml/error.rb +23 -0
  75. data/vendor/plugins/haml/lib/haml/exec.rb +347 -0
  76. data/vendor/plugins/haml/lib/haml/filters.rb +249 -0
  77. data/vendor/plugins/haml/lib/haml/helpers.rb +413 -0
  78. data/vendor/plugins/haml/lib/haml/helpers/action_view_extensions.rb +45 -0
  79. data/vendor/plugins/haml/lib/haml/helpers/action_view_mods.rb +122 -0
  80. data/vendor/plugins/haml/lib/haml/html.rb +188 -0
  81. data/vendor/plugins/haml/lib/haml/precompiler.rb +757 -0
  82. data/vendor/plugins/haml/lib/haml/template.rb +43 -0
  83. data/vendor/plugins/haml/lib/haml/template/patch.rb +58 -0
  84. data/vendor/plugins/haml/lib/haml/template/plugin.rb +72 -0
  85. data/vendor/plugins/haml/lib/sass.rb +833 -0
  86. data/vendor/plugins/haml/lib/sass/constant.rb +245 -0
  87. data/vendor/plugins/haml/lib/sass/constant/color.rb +101 -0
  88. data/vendor/plugins/haml/lib/sass/constant/literal.rb +53 -0
  89. data/vendor/plugins/haml/lib/sass/constant/number.rb +87 -0
  90. data/vendor/plugins/haml/lib/sass/constant/operation.rb +30 -0
  91. data/vendor/plugins/haml/lib/sass/constant/string.rb +22 -0
  92. data/vendor/plugins/haml/lib/sass/css.rb +378 -0
  93. data/vendor/plugins/haml/lib/sass/engine.rb +459 -0
  94. data/vendor/plugins/haml/lib/sass/error.rb +35 -0
  95. data/vendor/plugins/haml/lib/sass/plugin.rb +165 -0
  96. data/vendor/plugins/haml/lib/sass/plugin/merb.rb +56 -0
  97. data/vendor/plugins/haml/lib/sass/plugin/rails.rb +24 -0
  98. data/vendor/plugins/haml/lib/sass/tree/attr_node.rb +53 -0
  99. data/vendor/plugins/haml/lib/sass/tree/comment_node.rb +20 -0
  100. data/vendor/plugins/haml/lib/sass/tree/directive_node.rb +46 -0
  101. data/vendor/plugins/haml/lib/sass/tree/node.rb +42 -0
  102. data/vendor/plugins/haml/lib/sass/tree/rule_node.rb +89 -0
  103. data/vendor/plugins/haml/lib/sass/tree/value_node.rb +16 -0
  104. data/vendor/plugins/haml/test/benchmark.rb +82 -0
  105. data/vendor/plugins/haml/test/haml/engine_test.rb +587 -0
  106. data/vendor/plugins/haml/test/haml/helper_test.rb +187 -0
  107. data/vendor/plugins/haml/test/haml/html2haml_test.rb +60 -0
  108. data/vendor/plugins/haml/test/haml/markaby/standard.mab +52 -0
  109. data/vendor/plugins/haml/test/haml/mocks/article.rb +6 -0
  110. data/vendor/plugins/haml/test/haml/results/content_for_layout.xhtml +16 -0
  111. data/vendor/plugins/haml/test/haml/results/eval_suppressed.xhtml +11 -0
  112. data/vendor/plugins/haml/test/haml/results/filters.xhtml +82 -0
  113. data/vendor/plugins/haml/test/haml/results/helpers.xhtml +94 -0
  114. data/vendor/plugins/haml/test/haml/results/helpful.xhtml +10 -0
  115. data/vendor/plugins/haml/test/haml/results/just_stuff.xhtml +64 -0
  116. data/vendor/plugins/haml/test/haml/results/list.xhtml +12 -0
  117. data/vendor/plugins/haml/test/haml/results/original_engine.xhtml +22 -0
  118. data/vendor/plugins/haml/test/haml/results/partials.xhtml +21 -0
  119. data/vendor/plugins/haml/test/haml/results/silent_script.xhtml +74 -0
  120. data/vendor/plugins/haml/test/haml/results/standard.xhtml +42 -0
  121. data/vendor/plugins/haml/test/haml/results/tag_parsing.xhtml +28 -0
  122. data/vendor/plugins/haml/test/haml/results/very_basic.xhtml +7 -0
  123. data/vendor/plugins/haml/test/haml/results/whitespace_handling.xhtml +94 -0
  124. data/vendor/plugins/haml/test/haml/rhtml/_av_partial_1.rhtml +12 -0
  125. data/vendor/plugins/haml/test/haml/rhtml/_av_partial_2.rhtml +8 -0
  126. data/vendor/plugins/haml/test/haml/rhtml/action_view.rhtml +62 -0
  127. data/vendor/plugins/haml/test/haml/rhtml/standard.rhtml +54 -0
  128. data/vendor/plugins/haml/test/haml/template_test.rb +168 -0
  129. data/vendor/plugins/haml/test/haml/templates/_av_partial_1.haml +9 -0
  130. data/vendor/plugins/haml/test/haml/templates/_av_partial_2.haml +5 -0
  131. data/vendor/plugins/haml/test/haml/templates/_partial.haml +8 -0
  132. data/vendor/plugins/haml/test/haml/templates/_text_area.haml +3 -0
  133. data/vendor/plugins/haml/test/haml/templates/action_view.haml +47 -0
  134. data/vendor/plugins/haml/test/haml/templates/breakage.haml +8 -0
  135. data/vendor/plugins/haml/test/haml/templates/content_for_layout.haml +10 -0
  136. data/vendor/plugins/haml/test/haml/templates/eval_suppressed.haml +11 -0
  137. data/vendor/plugins/haml/test/haml/templates/filters.haml +81 -0
  138. data/vendor/plugins/haml/test/haml/templates/helpers.haml +69 -0
  139. data/vendor/plugins/haml/test/haml/templates/helpful.haml +11 -0
  140. data/vendor/plugins/haml/test/haml/templates/just_stuff.haml +77 -0
  141. data/vendor/plugins/haml/test/haml/templates/list.haml +12 -0
  142. data/vendor/plugins/haml/test/haml/templates/original_engine.haml +17 -0
  143. data/vendor/plugins/haml/test/haml/templates/partialize.haml +1 -0
  144. data/vendor/plugins/haml/test/haml/templates/partials.haml +12 -0
  145. data/vendor/plugins/haml/test/haml/templates/silent_script.haml +40 -0
  146. data/vendor/plugins/haml/test/haml/templates/standard.haml +42 -0
  147. data/vendor/plugins/haml/test/haml/templates/tag_parsing.haml +24 -0
  148. data/vendor/plugins/haml/test/haml/templates/very_basic.haml +4 -0
  149. data/vendor/plugins/haml/test/haml/templates/whitespace_handling.haml +87 -0
  150. data/vendor/plugins/haml/test/haml/test_helper.rb +15 -0
  151. data/vendor/plugins/haml/test/profile.rb +65 -0
  152. data/vendor/plugins/haml/test/sass/engine_test.rb +276 -0
  153. data/vendor/plugins/haml/test/sass/plugin_test.rb +159 -0
  154. data/vendor/plugins/haml/test/sass/results/alt.css +4 -0
  155. data/vendor/plugins/haml/test/sass/results/basic.css +9 -0
  156. data/vendor/plugins/haml/test/sass/results/compact.css +5 -0
  157. data/vendor/plugins/haml/test/sass/results/complex.css +87 -0
  158. data/vendor/plugins/haml/test/sass/results/compressed.css +1 -0
  159. data/vendor/plugins/haml/test/sass/results/constants.css +14 -0
  160. data/vendor/plugins/haml/test/sass/results/expanded.css +19 -0
  161. data/vendor/plugins/haml/test/sass/results/import.css +29 -0
  162. data/vendor/plugins/haml/test/sass/results/mixins.css +95 -0
  163. data/vendor/plugins/haml/test/sass/results/multiline.css +24 -0
  164. data/vendor/plugins/haml/test/sass/results/nested.css +22 -0
  165. data/vendor/plugins/haml/test/sass/results/parent_ref.css +13 -0
  166. data/vendor/plugins/haml/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  167. data/vendor/plugins/haml/test/sass/results/subdir/subdir.css +1 -0
  168. data/vendor/plugins/haml/test/sass/templates/_partial.sass +2 -0
  169. data/vendor/plugins/haml/test/sass/templates/alt.sass +16 -0
  170. data/vendor/plugins/haml/test/sass/templates/basic.sass +23 -0
  171. data/vendor/plugins/haml/test/sass/templates/bork.sass +2 -0
  172. data/vendor/plugins/haml/test/sass/templates/bork2.sass +2 -0
  173. data/vendor/plugins/haml/test/sass/templates/compact.sass +17 -0
  174. data/vendor/plugins/haml/test/sass/templates/complex.sass +310 -0
  175. data/vendor/plugins/haml/test/sass/templates/compressed.sass +15 -0
  176. data/vendor/plugins/haml/test/sass/templates/constants.sass +97 -0
  177. data/vendor/plugins/haml/test/sass/templates/expanded.sass +17 -0
  178. data/vendor/plugins/haml/test/sass/templates/import.sass +11 -0
  179. data/vendor/plugins/haml/test/sass/templates/importee.sass +14 -0
  180. data/vendor/plugins/haml/test/sass/templates/mixins.sass +76 -0
  181. data/vendor/plugins/haml/test/sass/templates/multiline.sass +20 -0
  182. data/vendor/plugins/haml/test/sass/templates/nested.sass +25 -0
  183. data/vendor/plugins/haml/test/sass/templates/parent_ref.sass +25 -0
  184. data/vendor/plugins/haml/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  185. data/vendor/plugins/haml/test/sass/templates/subdir/subdir.sass +6 -0
  186. metadata +185 -24
  187. data/app/views/admin/extension/index.html.erb +0 -40
  188. data/app/views/admin/layout/edit.html.erb +0 -39
  189. data/app/views/admin/layout/index.html.erb +0 -38
  190. data/app/views/admin/layout/remove.html.erb +0 -17
  191. data/app/views/admin/page/_meta_row.html.erb +0 -4
  192. data/app/views/admin/page/_node.html.erb +0 -28
  193. data/app/views/admin/page/_part.html.erb +0 -13
  194. data/app/views/admin/page/_tag_reference.html.erb +0 -4
  195. data/app/views/admin/page/children.html.erb +0 -4
  196. data/app/views/admin/page/edit.html.erb +0 -140
  197. data/app/views/admin/page/index.html.erb +0 -31
  198. data/app/views/admin/page/remove.html.erb +0 -14
  199. data/app/views/admin/snippet/edit.html.erb +0 -29
  200. data/app/views/admin/snippet/index.html.erb +0 -36
  201. data/app/views/admin/snippet/remove.html.erb +0 -16
  202. data/app/views/admin/user/edit.html.erb +0 -54
  203. data/app/views/admin/user/index.html.erb +0 -43
  204. data/app/views/admin/user/preferences.html.erb +0 -29
  205. data/app/views/admin/user/remove.html.erb +0 -16
  206. data/app/views/admin/welcome/login.html.erb +0 -51
  207. data/app/views/layouts/application.html.erb +0 -83
  208. data/app/views/site/not_found.html.erb +0 -3
@@ -0,0 +1,89 @@
1
+ require 'sass/tree/node'
2
+ require 'sass/tree/attr_node'
3
+
4
+ module Sass::Tree
5
+ class RuleNode < ValueNode
6
+ # The character used to include the parent selector
7
+ PARENT = '&'
8
+
9
+ alias_method :rule, :value
10
+ alias_method :rule=, :value=
11
+
12
+ def rules
13
+ Array(rule)
14
+ end
15
+
16
+ def add_rules(node)
17
+ self.rule = rules
18
+ self.rule += node.rules
19
+ end
20
+
21
+ def continued?
22
+ rule[-1] == ?,
23
+ end
24
+
25
+ def to_s(tabs, super_rules = nil)
26
+ attributes = []
27
+ sub_rules = []
28
+
29
+ rule_split = /\s*,\s*/
30
+ rule_separator = @style == :compressed ? ',' : ', '
31
+ line_separator = [:nested, :expanded].include?(@style) ? ",\n" : rule_separator
32
+ rule_indent = ' ' * (tabs - 1)
33
+ total_rule = if super_rules
34
+ super_rules.split(",\n").map do |super_line|
35
+ super_line.strip.split(rule_split).map do |super_rule|
36
+ self.rules.map do |line|
37
+ rule_indent + line.gsub(/,$/, '').split(rule_split).map do |rule|
38
+ if rule.include?(PARENT)
39
+ rule.gsub(PARENT, super_rule)
40
+ else
41
+ "#{super_rule} #{rule}"
42
+ end
43
+ end.join(rule_separator)
44
+ end.join(line_separator)
45
+ end.join(rule_separator)
46
+ end.join(line_separator)
47
+ elsif self.rules.any? { |r| r.include?(PARENT) }
48
+ raise Sass::SyntaxError.new("Base-level rules cannot contain the parent-selector-referencing character '#{PARENT}'.", line)
49
+ else
50
+ per_rule_indent, total_indent = [:nested, :expanded].include?(@style) ? [rule_indent, ''] : ['', rule_indent]
51
+ total_indent + self.rules.map do |r|
52
+ per_rule_indent + r.gsub(/,$/, '').gsub(rule_split, rule_separator).rstrip
53
+ end.join(line_separator)
54
+ end
55
+
56
+ children.each do |child|
57
+ if child.is_a? RuleNode
58
+ sub_rules << child
59
+ else
60
+ attributes << child
61
+ end
62
+ end
63
+
64
+ to_return = ''
65
+ if !attributes.empty?
66
+ old_spaces = ' ' * (tabs - 1)
67
+ spaces = ' ' * tabs
68
+ if @style == :compact
69
+ attributes = attributes.map { |a| a.to_s(1) }.join(' ')
70
+ to_return << "#{total_rule} { #{attributes} }\n"
71
+ elsif @style == :compressed
72
+ attributes = attributes.map { |a| a.to_s(1) }.join(';')
73
+ to_return << "#{total_rule}{#{attributes}}"
74
+ else
75
+ attributes = attributes.map { |a| a.to_s(tabs + 1) }.join("\n")
76
+ end_attrs = (@style == :expanded ? "\n" + old_spaces : ' ')
77
+ to_return << "#{total_rule} {\n#{attributes}#{end_attrs}}\n"
78
+ end
79
+ end
80
+
81
+ tabs += 1 unless attributes.empty? || @style != :nested
82
+ sub_rules.each do |sub|
83
+ to_return << sub.to_s(tabs, total_rule)
84
+ end
85
+
86
+ to_return
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,16 @@
1
+ require 'sass/tree/node'
2
+
3
+ module Sass::Tree
4
+ class ValueNode < Node
5
+ attr_accessor :value
6
+
7
+ def initialize(value, style)
8
+ @value = value
9
+ super(style)
10
+ end
11
+
12
+ def to_s(tabs = 0)
13
+ value
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ times = (ARGV.first || 1000).to_i
4
+
5
+ if times == 0 # Invalid parameter
6
+ puts <<END
7
+ ruby #$0 [times=1000]
8
+ Benchmark Haml against various other templating languages and Sass
9
+ on its own.
10
+ END
11
+ exit 1
12
+ end
13
+
14
+ require File.dirname(__FILE__) + '/../lib/haml'
15
+ %w[sass rubygems erb erubis markaby active_support action_controller
16
+ action_view haml/template].each(&method(:require))
17
+
18
+ begin
19
+ require 'benchwarmer'
20
+ rescue LoadError
21
+ # Since it's not as simple as gem install at the time of writing,
22
+ # we need to direct folks to the benchwarmer gem.
23
+ raise "The Haml benchmarks require the benchwarmer gem, available from http://github.com/wycats/benchwarmer"
24
+ end
25
+
26
+ Benchmark.warmer(times) do
27
+ columns :haml, :erb, :erubis, :mab
28
+ titles :haml => "Haml", :erb => "ERB", :erubis => "Erubis", :mab => "Markaby"
29
+
30
+ template_name = 'standard'
31
+ directory = File.dirname(__FILE__) + '/haml'
32
+ haml_template = File.read("#{directory}/templates/#{template_name}.haml")
33
+ erb_template = File.read("#{directory}/rhtml/#{template_name}.rhtml")
34
+ markaby_template = File.read("#{directory}/markaby/#{template_name}.mab")
35
+
36
+ report "Uncached" do
37
+ haml { Haml::Engine.new(haml_template).render }
38
+ erb { ERB.new(erb_template, nil, '-').result }
39
+ erubis { Erubis::Eruby.new(erb_template).result }
40
+ mab { Markaby::Template.new(markaby_template).render }
41
+ end
42
+
43
+ report "Cached" do
44
+ obj = Object.new
45
+
46
+ Haml::Engine.new(haml_template).def_method(obj, :haml)
47
+ Erubis::Eruby.new(erb_template).def_method(obj, :erubis)
48
+ obj.instance_eval("def erb; #{ERB.new(erb_template, nil, '-').src}; end")
49
+
50
+ haml { obj.haml }
51
+ erb { obj.erb }
52
+ erubis { obj.erubis }
53
+ end
54
+
55
+ report "ActionView" do
56
+ @base = ActionView::Base.new(File.dirname(__FILE__))
57
+
58
+ # To cache the template
59
+ @base.render 'haml/templates/standard'
60
+ @base.render 'haml/rhtml/standard'
61
+
62
+ haml { @base.render 'haml/templates/standard' }
63
+ erb { @base.render 'haml/rhtml/standard' }
64
+ end
65
+
66
+ report "ActionView with deep partials" do
67
+ @base = ActionView::Base.new(File.dirname(__FILE__))
68
+
69
+ # To cache the template
70
+ @base.render 'haml/templates/action_view'
71
+ @base.render 'haml/rhtml/action_view'
72
+
73
+ haml { @base.render 'haml/templates/action_view' }
74
+ erb { @base.render 'haml/rhtml/action_view' }
75
+ end
76
+ end
77
+
78
+ Benchmark.warmer(times) do
79
+ sass_template = File.read("#{File.dirname(__FILE__)}/sass/templates/complex.sass")
80
+
81
+ report("Sass") { Sass::Engine.new(sass_template).render }
82
+ end
@@ -0,0 +1,587 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/test_helper'
3
+
4
+ class EngineTest < Test::Unit::TestCase
5
+ # A map of erroneous Sass documents to the error messages they should produce.
6
+ # The error messages may be arrays;
7
+ # if so, the second element should be the line number that should be reported for the error.
8
+ # If this isn't provided, the tests will assume the line number should be the last line of the document.
9
+ EXCEPTION_MAP = {
10
+ "!!!\n a" => "Illegal nesting: nesting within a header command is illegal.",
11
+ "a\n b" => "Illegal nesting: nesting within plain text is illegal.",
12
+ "/ a\n b" => "Illegal nesting: nesting within a tag that already has content is illegal.",
13
+ "% a" => 'Invalid tag: "% a".',
14
+ "%p a\n b" => "Illegal nesting: content can't be both given on the same line as %p and nested within it.",
15
+ "%p=" => "There's no Ruby code for = to evaluate.",
16
+ "%p~" => "There's no Ruby code for ~ to evaluate.",
17
+ "~" => "There's no Ruby code for ~ to evaluate.",
18
+ "=" => "There's no Ruby code for = to evaluate.",
19
+ "%p/\n a" => "Illegal nesting: nesting within a self-closing tag is illegal.",
20
+ "%p\n\ta" => <<END.strip,
21
+ A tab character was used for indentation. Haml must be indented using two spaces.
22
+ Are you sure you have soft tabs enabled in your editor?
23
+ END
24
+ "%p\n a" => "1 space was used for indentation. Haml must be indented using two spaces.",
25
+ "%p\n a" => "3 spaces were used for indentation. Haml must be indented using two spaces.",
26
+ "%p\n a" => "4 spaces were used for indentation. Haml must be indented using two spaces.",
27
+ ":a\n b" => ['Filter "a" is not defined.', 1],
28
+ ":a= b" => 'Invalid filter name ":a= b".',
29
+ "." => "Illegal element: classes and ids must have values.",
30
+ ".#" => "Illegal element: classes and ids must have values.",
31
+ ".{} a" => "Illegal element: classes and ids must have values.",
32
+ ".= a" => "Illegal element: classes and ids must have values.",
33
+ "%p..a" => "Illegal element: classes and ids must have values.",
34
+ "%a/ b" => "Self-closing tags can't have content.",
35
+
36
+ # Regression tests
37
+ "- raise 'foo'\n\n\n\nbar" => ["foo", 1],
38
+ "= 'foo'\n-raise 'foo'" => ["foo", 2],
39
+ "\n\n\n- raise 'foo'" => ["foo", 4],
40
+ }
41
+
42
+ User = Struct.new('User', :id)
43
+
44
+ def render(text, options = {}, &block)
45
+ scope = options.delete(:scope) || Object.new
46
+ locals = options.delete(:locals) || {}
47
+ Haml::Engine.new(text, options).to_html(scope, locals, &block)
48
+ end
49
+
50
+ def test_empty_render_should_remain_empty
51
+ assert_equal('', render(''))
52
+ end
53
+
54
+ def test_attributes_should_render_correctly
55
+ assert_equal("<div class='atlantis' style='ugly'>\n</div>", render(".atlantis{:style => 'ugly'}").chomp)
56
+ end
57
+
58
+ def test_ruby_code_should_work_inside_attributes
59
+ author = 'hcatlin'
60
+ assert_equal("<p class='3'>foo</p>", render("%p{:class => 1+2} foo").chomp)
61
+ end
62
+
63
+ def test_nil_should_render_empty_tag
64
+ assert_equal("<div class='no_attributes'>\n</div>",
65
+ render(".no_attributes{:nil => nil}").chomp)
66
+ end
67
+
68
+ def test_strings_should_get_stripped_inside_tags
69
+ assert_equal("<div class='stripped'>This should have no spaces in front of it</div>",
70
+ render(".stripped This should have no spaces in front of it").chomp)
71
+ end
72
+
73
+ def test_one_liner_should_be_one_line
74
+ assert_equal("<p>Hello</p>", render('%p Hello').chomp)
75
+ end
76
+
77
+ def test_one_liner_with_newline_shouldnt_be_one_line
78
+ assert_equal("<p>\n foo\n bar\n</p>", render('%p= "foo\nbar"').chomp)
79
+ end
80
+
81
+ def test_multi_render
82
+ engine = Haml::Engine.new("%strong Hi there!")
83
+ assert_equal("<strong>Hi there!</strong>\n", engine.to_html)
84
+ assert_equal("<strong>Hi there!</strong>\n", engine.to_html)
85
+ assert_equal("<strong>Hi there!</strong>\n", engine.to_html)
86
+ end
87
+
88
+ def test_double_equals
89
+ assert_equal("<p>Hello World</p>\n", render('%p== Hello #{who}', :locals => {:who => 'World'}))
90
+ assert_equal("<p>\n Hello World\n</p>\n", render("%p\n == Hello \#{who}", :locals => {:who => 'World'}))
91
+ end
92
+
93
+ def test_double_equals_in_the_middle_of_a_string
94
+ assert_equal("\"title 'Title'. \"\n",
95
+ render("== \"title '\#{\"Title\"}'. \""))
96
+ end
97
+
98
+ def test_nil_tag_value_should_render_as_empty
99
+ assert_equal("<p></p>\n", render("%p= nil"))
100
+ end
101
+
102
+ def test_tag_with_failed_if_should_render_as_empty
103
+ assert_equal("<p></p>\n", render("%p= 'Hello' if false"))
104
+ end
105
+
106
+ def test_static_attributes_with_empty_attr
107
+ assert_equal("<img alt='' src='/foo.png' />\n", render("%img{:src => '/foo.png', :alt => ''}"))
108
+ end
109
+
110
+ def test_dynamic_attributes_with_empty_attr
111
+ assert_equal("<img alt='' src='/foo.png' />\n", render("%img{:width => nil, :src => '/foo.png', :alt => String.new}"))
112
+ end
113
+
114
+ def test_end_of_file_multiline
115
+ assert_equal("<p>0</p>\n<p>1</p>\n<p>2</p>\n", render("- for i in (0...3)\n %p= |\n i |"))
116
+ end
117
+
118
+ def test_cr_newline
119
+ assert_equal("<p>foo</p>\n<p>bar</p>\n<p>baz</p>\n<p>boom</p>\n", render("%p foo\r%p bar\r\n%p baz\n\r%p boom"))
120
+ end
121
+
122
+ def test_textareas
123
+ assert_equal("<textarea>Foo&#x000A; bar&#x000A; baz</textarea>\n",
124
+ render('%textarea= "Foo\n bar\n baz"'))
125
+
126
+ assert_equal("<pre>Foo&#x000A; bar&#x000A; baz</pre>\n",
127
+ render('%pre= "Foo\n bar\n baz"'))
128
+
129
+ assert_equal("<textarea>#{'a' * 100}</textarea>\n",
130
+ render("%textarea #{'a' * 100}"))
131
+ end
132
+
133
+ def test_boolean_attributes
134
+ assert_equal("<p bar baz='true' foo='bar'>\n</p>\n",
135
+ render("%p{:foo => 'bar', :bar => true, :baz => 'true'}", :format => :html4))
136
+ assert_equal("<p bar='bar' baz='true' foo='bar'>\n</p>\n",
137
+ render("%p{:foo => 'bar', :bar => true, :baz => 'true'}", :format => :xhtml))
138
+
139
+ assert_equal("<p baz='false' foo='bar'>\n</p>\n",
140
+ render("%p{:foo => 'bar', :bar => false, :baz => 'false'}", :format => :html4))
141
+ assert_equal("<p baz='false' foo='bar'>\n</p>\n",
142
+ render("%p{:foo => 'bar', :bar => false, :baz => 'false'}", :format => :xhtml))
143
+ end
144
+
145
+ # HTML escaping tests
146
+
147
+ def test_ampersand_equals_should_escape
148
+ assert_equal("<p>\n foo &amp; bar\n</p>\n", render("%p\n &= 'foo & bar'", :escape_html => false))
149
+ end
150
+
151
+ def test_ampersand_equals_inline_should_escape
152
+ assert_equal("<p>foo &amp; bar</p>\n", render("%p&= 'foo & bar'", :escape_html => false))
153
+ end
154
+
155
+ def test_bang_equals_should_not_escape
156
+ assert_equal("<p>\n foo & bar\n</p>\n", render("%p\n != 'foo & bar'", :escape_html => true))
157
+ end
158
+
159
+ def test_bang_equals_inline_should_not_escape
160
+ assert_equal("<p>foo & bar</p>\n", render("%p!= 'foo & bar'", :escape_html => true))
161
+ end
162
+
163
+ def test_static_attributes_should_be_escaped
164
+ assert_equal("<img class='atlantis' style='ugly&amp;stupid' />\n",
165
+ render("%img.atlantis{:style => 'ugly&stupid'}", :escape_html => true))
166
+ assert_equal("<div class='atlantis' style='ugly&amp;stupid'>foo</div>\n",
167
+ render(".atlantis{:style => 'ugly&stupid'} foo", :escape_html => true))
168
+ assert_equal("<p class='atlantis' style='ugly&amp;stupid'>foo</p>\n",
169
+ render("%p.atlantis{:style => 'ugly&stupid'}= 'foo'", :escape_html => true))
170
+ end
171
+
172
+ def test_dynamic_attributes_should_be_escaped
173
+ assert_equal("<img alt='' src='/foo.png' />\n",
174
+ render("%img{:width => nil, :src => '/foo.png', :alt => String.new}", :escape_html => true))
175
+ assert_equal("<p alt='' src='/foo.png'>foo</p>\n",
176
+ render("%p{:width => nil, :src => '/foo.png', :alt => String.new} foo", :escape_html => true))
177
+ assert_equal("<div alt='' src='/foo.png'>foo</div>\n",
178
+ render("%div{:width => nil, :src => '/foo.png', :alt => String.new}= 'foo'", :escape_html => true))
179
+ end
180
+
181
+ def test_string_interpolation_should_be_esaped
182
+ assert_equal("<p>4&amp;3</p>\n", render("%p== #{2+2}&#{2+1}", :escape_html => true))
183
+ assert_equal("<p>4&3</p>\n", render("%p== #{2+2}&#{2+1}", :escape_html => false))
184
+ end
185
+
186
+ def test_escaped_inline_string_interpolation
187
+ assert_equal("<p>4&amp;3</p>\n", render("%p&== #{2+2}&#{2+1}", :escape_html => true))
188
+ assert_equal("<p>4&amp;3</p>\n", render("%p&== #{2+2}&#{2+1}", :escape_html => false))
189
+ end
190
+
191
+ def test_unescaped_inline_string_interpolation
192
+ assert_equal("<p>4&3</p>\n", render("%p!== #{2+2}&#{2+1}", :escape_html => true))
193
+ assert_equal("<p>4&3</p>\n", render("%p!== #{2+2}&#{2+1}", :escape_html => false))
194
+ end
195
+
196
+ def test_escaped_string_interpolation
197
+ assert_equal("<p>\n 4&amp;3\n</p>\n", render("%p\n &== #{2+2}&#{2+1}", :escape_html => true))
198
+ assert_equal("<p>\n 4&amp;3\n</p>\n", render("%p\n &== #{2+2}&#{2+1}", :escape_html => false))
199
+ end
200
+
201
+ def test_unescaped_string_interpolation
202
+ assert_equal("<p>\n 4&3\n</p>\n", render("%p\n !== #{2+2}&#{2+1}", :escape_html => true))
203
+ assert_equal("<p>\n 4&3\n</p>\n", render("%p\n !== #{2+2}&#{2+1}", :escape_html => false))
204
+ end
205
+
206
+ def test_scripts_should_respect_escape_html_option
207
+ assert_equal("<p>\n foo &amp; bar\n</p>\n", render("%p\n = 'foo & bar'", :escape_html => true))
208
+ assert_equal("<p>\n foo & bar\n</p>\n", render("%p\n = 'foo & bar'", :escape_html => false))
209
+ end
210
+
211
+ def test_inline_scripts_should_respect_escape_html_option
212
+ assert_equal("<p>foo &amp; bar</p>\n", render("%p= 'foo & bar'", :escape_html => true))
213
+ assert_equal("<p>foo & bar</p>\n", render("%p= 'foo & bar'", :escape_html => false))
214
+ end
215
+
216
+ def test_script_ending_in_comment_should_render_when_html_is_escaped
217
+ assert_equal("foo&amp;bar\n", render("= 'foo&bar' #comment", :escape_html => true))
218
+ end
219
+
220
+ # Options tests
221
+
222
+ def test_filename_and_line
223
+ begin
224
+ render("\n\n = abc", :filename => 'test', :line => 2)
225
+ rescue Exception => e
226
+ assert_kind_of Haml::SyntaxError, e
227
+ assert_match /test:4/, e.backtrace.first
228
+ end
229
+
230
+ begin
231
+ render("\n\n= 123\n\n= nil[]", :filename => 'test', :line => 2)
232
+ rescue Exception => e
233
+ assert_kind_of NoMethodError, e
234
+ assert_match /test:6/, e.backtrace.first
235
+ end
236
+ end
237
+
238
+ def test_stop_eval
239
+ assert_equal("", render("= 'Hello'", :suppress_eval => true))
240
+ assert_equal("", render("- puts 'foo'", :suppress_eval => true))
241
+ assert_equal("<div id='foo' yes='no' />\n", render("#foo{:yes => 'no'}/", :suppress_eval => true))
242
+ assert_equal("<div id='foo' />\n", render("#foo{:yes => 'no', :call => a_function() }/", :suppress_eval => true))
243
+ assert_equal("<div />\n", render("%div[1]/", :suppress_eval => true))
244
+
245
+ begin
246
+ assert_equal("", render(":ruby\n puts 'hello'", :suppress_eval => true))
247
+ rescue Haml::Error => err
248
+ caught = true
249
+ assert_equal('Filter "ruby" is not defined.', err.message)
250
+ end
251
+ assert(caught, "Rendering a ruby filter without evaluating didn't throw an error!")
252
+ end
253
+
254
+ def test_attr_wrapper
255
+ assert_equal("<p strange=*attrs*>\n</p>\n", render("%p{ :strange => 'attrs'}", :attr_wrapper => '*'))
256
+ assert_equal("<p escaped='quo\"te'>\n</p>\n", render("%p{ :escaped => 'quo\"te'}", :attr_wrapper => '"'))
257
+ assert_equal("<p escaped=\"quo'te\">\n</p>\n", render("%p{ :escaped => 'quo\\'te'}", :attr_wrapper => '"'))
258
+ assert_equal("<p escaped=\"q'uo&quot;te\">\n</p>\n", render("%p{ :escaped => 'q\\'uo\"te'}", :attr_wrapper => '"'))
259
+ assert_equal("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n", render("!!! XML", :attr_wrapper => '"'))
260
+ end
261
+
262
+ def test_attrs_parsed_correctly
263
+ assert_equal("<p boom=>biddly='bar =&gt; baz'>\n</p>\n", render("%p{'boom=>biddly' => 'bar => baz'}"))
264
+ assert_equal("<p foo,bar='baz, qux'>\n</p>\n", render("%p{'foo,bar' => 'baz, qux'}"))
265
+ assert_equal("<p escaped='quo\nte'>\n</p>\n", render("%p{ :escaped => \"quo\\nte\"}"))
266
+ assert_equal("<p escaped='quo4te'>\n</p>\n", render("%p{ :escaped => \"quo\#{2 + 2}te\"}"))
267
+ end
268
+
269
+ def test_correct_parsing_with_brackets
270
+ assert_equal("<p class='foo'>{tada} foo</p>\n", render("%p{:class => 'foo'} {tada} foo"))
271
+ assert_equal("<p class='foo'>deep {nested { things }}</p>\n", render("%p{:class => 'foo'} deep {nested { things }}"))
272
+ assert_equal("<p class='bar foo'>{a { d</p>\n", render("%p{{:class => 'foo'}, :class => 'bar'} {a { d"))
273
+ assert_equal("<p foo='bar'>a}</p>\n", render("%p{:foo => 'bar'} a}"))
274
+
275
+ foo = []
276
+ foo[0] = Struct.new('Foo', :id).new
277
+ assert_equal("<p class='struct_foo' id='struct_foo_new'>New User]</p>\n",
278
+ render("%p[foo[0]] New User]", :locals => {:foo => foo}))
279
+ assert_equal("<p class='prefix_struct_foo' id='prefix_struct_foo_new'>New User]</p>\n",
280
+ render("%p[foo[0], :prefix] New User]", :locals => {:foo => foo}))
281
+
282
+ foo[0].id = 1
283
+ assert_equal("<p class='struct_foo' id='struct_foo_1'>New User]</p>\n",
284
+ render("%p[foo[0]] New User]", :locals => {:foo => foo}))
285
+ assert_equal("<p class='prefix_struct_foo' id='prefix_struct_foo_1'>New User]</p>\n",
286
+ render("%p[foo[0], :prefix] New User]", :locals => {:foo => foo}))
287
+ end
288
+
289
+ def test_empty_attrs
290
+ assert_equal("<p attr=''>empty</p>\n", render("%p{ :attr => '' } empty"))
291
+ assert_equal("<p attr=''>empty</p>\n", render("%p{ :attr => x } empty", :locals => {:x => ''}))
292
+ end
293
+
294
+ def test_nil_attrs
295
+ assert_equal("<p>nil</p>\n", render("%p{ :attr => nil } nil"))
296
+ assert_equal("<p>nil</p>\n", render("%p{ :attr => x } nil", :locals => {:x => nil}))
297
+ end
298
+
299
+ def test_nil_id_with_syntactic_id
300
+ assert_equal("<p id='foo'>nil</p>\n", render("%p#foo{:id => nil} nil"))
301
+ assert_equal("<p id='foo_bar'>nil</p>\n", render("%p#foo{{:id => 'bar'}, :id => nil} nil"))
302
+ assert_equal("<p id='foo_bar'>nil</p>\n", render("%p#foo{{:id => nil}, :id => 'bar'} nil"))
303
+ end
304
+
305
+ def test_nil_class_with_syntactic_class
306
+ assert_equal("<p class='foo'>nil</p>\n", render("%p.foo{:class => nil} nil"))
307
+ assert_equal("<p class='bar foo'>nil</p>\n", render("%p.bar.foo{:class => nil} nil"))
308
+ assert_equal("<p class='bar foo'>nil</p>\n", render("%p.foo{{:class => 'bar'}, :class => nil} nil"))
309
+ assert_equal("<p class='bar foo'>nil</p>\n", render("%p.foo{{:class => nil}, :class => 'bar'} nil"))
310
+ end
311
+
312
+ def test_locals
313
+ assert_equal("<p>Paragraph!</p>\n", render("%p= text", :locals => { :text => "Paragraph!" }))
314
+ end
315
+
316
+ def test_deprecated_locals_option
317
+ Kernel.module_eval do
318
+ def warn_with_stub(msg); end
319
+ alias_method :warn_without_stub, :warn
320
+ alias_method :warn, :warn_with_stub
321
+ end
322
+
323
+ assert_equal("<p>Paragraph!</p>\n", Haml::Engine.new("%p= text", :locals => { :text => "Paragraph!" }).render)
324
+
325
+ Kernel.module_eval { alias_method :warn, :warn_without_stub }
326
+ end
327
+
328
+ def test_dynamic_attrs_shouldnt_register_as_literal_values
329
+ assert_equal("<p a='b2c'>\n</p>\n", render('%p{:a => "b#{1 + 1}c"}'))
330
+ assert_equal("<p a='b2c'>\n</p>\n", render("%p{:a => 'b' + (1 + 1).to_s + 'c'}"))
331
+ end
332
+
333
+ def test_dynamic_attrs_with_self_closed_tag
334
+ assert_equal("<a b='2' />\nc\n", render("%a{'b' => 1 + 1}/\n= 'c'\n"))
335
+ end
336
+
337
+ def test_exceptions
338
+ EXCEPTION_MAP.each do |key, value|
339
+ begin
340
+ render(key)
341
+ rescue Exception => err
342
+ value = [value] unless value.is_a?(Array)
343
+
344
+ assert_equal(value.first, err.message, "Line: #{key}")
345
+ assert_equal(value[1] || key.split("\n").length, err.backtrace[0].gsub('(haml):', '').to_i, "Line: #{key}")
346
+ else
347
+ assert(false, "Exception not raised for\n#{key}")
348
+ end
349
+ end
350
+ end
351
+
352
+ def test_exception_line
353
+ render("a\nb\n!!!\n c\nd")
354
+ rescue Haml::SyntaxError => e
355
+ assert_equal("(haml):4", e.backtrace[0])
356
+ else
357
+ assert(false, '"a\nb\n!!!\n c\nd" doesn\'t produce an exception')
358
+ end
359
+
360
+ def test_exception
361
+ render("%p\n hi\n %a= undefined\n= 12")
362
+ rescue Exception => e
363
+ assert_match("(haml):3", e.backtrace[0])
364
+ else
365
+ # Test failed... should have raised an exception
366
+ assert(false)
367
+ end
368
+
369
+ def test_compile_error
370
+ render("a\nb\n- fee)\nc")
371
+ rescue Exception => e
372
+ assert_match(/^compile error\n\(haml\):3: syntax error/i, e.message)
373
+ else
374
+ assert(false,
375
+ '"a\nb\n- fee)\nc" doesn\'t produce an exception!')
376
+ end
377
+
378
+ def test_unbalanced_brackets
379
+ render('== #{1 + 5} foo #{6 + 7 bar #{8 + 9}')
380
+ rescue Haml::SyntaxError => e
381
+ assert_equal("Unbalanced brackets.", e.message)
382
+ end
383
+
384
+ def test_balanced_conditional_comments
385
+ assert_equal("<!--[if !(IE 6)|(IE 7)]> Bracket: ] <![endif]-->\n",
386
+ render("/[if !(IE 6)|(IE 7)] Bracket: ]"))
387
+ end
388
+
389
+ def test_no_bluecloth
390
+ Kernel.module_eval do
391
+ def gem_original_require_with_bluecloth(file)
392
+ raise LoadError if file == 'bluecloth'
393
+ gem_original_require_without_bluecloth(file)
394
+ end
395
+ alias_method :gem_original_require_without_bluecloth, :gem_original_require
396
+ alias_method :gem_original_require, :gem_original_require_with_bluecloth
397
+ end
398
+
399
+ begin
400
+ assert_equal("<h1>Foo</h1>\t<p>- a\n- b</p>\n",
401
+ Haml::Engine.new(":markdown\n Foo\n ===\n - a\n - b").to_html)
402
+ rescue Haml::Error => e
403
+ if e.message == "Can't run Markdown filter; required 'bluecloth' or 'redcloth', but none were found"
404
+ puts "\nCouldn't require 'bluecloth' or 'redcloth'; skipping a test."
405
+ else
406
+ raise e
407
+ end
408
+ end
409
+
410
+ Kernel.module_eval do
411
+ alias_method :gem_original_require, :gem_original_require_without_bluecloth
412
+ end
413
+ end
414
+
415
+ def test_no_redcloth
416
+ Kernel.module_eval do
417
+ def gem_original_require_with_redcloth(file)
418
+ raise LoadError if file == 'redcloth'
419
+ gem_original_require_without_redcloth(file)
420
+ end
421
+ alias_method :gem_original_require_without_redcloth, :gem_original_require
422
+ alias_method :gem_original_require, :gem_original_require_with_redcloth
423
+ end
424
+
425
+ begin
426
+ Haml::Engine.new(":redcloth\n _foo_").to_html
427
+ rescue Haml::Error
428
+ else
429
+ assert(false, "No exception raised!")
430
+ end
431
+
432
+ Kernel.module_eval do
433
+ alias_method :gem_original_require, :gem_original_require_without_redcloth
434
+ end
435
+ end
436
+
437
+ def test_no_redcloth_or_bluecloth
438
+ Kernel.module_eval do
439
+ def gem_original_require_with_redcloth_and_bluecloth(file)
440
+ raise LoadError if file == 'redcloth' || file == 'bluecloth'
441
+ gem_original_require_without_redcloth_and_bluecloth(file)
442
+ end
443
+ alias_method :gem_original_require_without_redcloth_and_bluecloth, :gem_original_require
444
+ alias_method :gem_original_require, :gem_original_require_with_redcloth_and_bluecloth
445
+ end
446
+
447
+ begin
448
+ Haml::Engine.new(":markdown\n _foo_").to_html
449
+ rescue Haml::Error
450
+ else
451
+ assert(false, "No exception raised!")
452
+ end
453
+
454
+ Kernel.module_eval do
455
+ alias_method :gem_original_require, :gem_original_require_without_redcloth_and_bluecloth
456
+ end
457
+ end
458
+
459
+ def test_empty_filter
460
+ assert_equal(<<END, render(':javascript'))
461
+ <script type='text/javascript'>
462
+ //<![CDATA[
463
+
464
+ //]]>
465
+ </script>
466
+ END
467
+ end
468
+
469
+ def test_local_assigns_dont_modify_class
470
+ assert_equal("bar\n", render("= foo", :locals => {:foo => 'bar'}))
471
+ assert_equal(nil, defined?(foo))
472
+ end
473
+
474
+ def test_object_ref_with_nil_id
475
+ user = User.new
476
+ assert_equal("<p class='struct_user' id='struct_user_new'>New User</p>\n",
477
+ render("%p[user] New User", :locals => {:user => user}))
478
+ end
479
+
480
+ def test_object_ref_before_attrs
481
+ user = User.new 42
482
+ assert_equal("<p class='struct_user' id='struct_user_42' style='width: 100px;'>New User</p>\n",
483
+ render("%p[user]{:style => 'width: 100px;'} New User", :locals => {:user => user}))
484
+ end
485
+
486
+ def test_non_literal_attributes
487
+ assert_equal("<p a1='foo' a2='bar' a3='baz' />\n",
488
+ render("%p{a2, a1, :a3 => 'baz'}/",
489
+ :locals => {:a1 => {:a1 => 'foo'}, :a2 => {:a2 => 'bar'}}))
490
+ end
491
+
492
+ def test_render_should_accept_a_binding_as_scope
493
+ string = "This is a string!"
494
+ string.instance_variable_set("@var", "Instance variable")
495
+ b = string.instance_eval do
496
+ var = "Local variable"
497
+ binding
498
+ end
499
+
500
+ assert_equal("<p>THIS IS A STRING!</p>\n<p>Instance variable</p>\n<p>Local variable</p>\n",
501
+ render("%p= upcase\n%p= @var\n%p= var", :scope => b))
502
+ end
503
+
504
+ def test_yield_should_work_with_binding
505
+ assert_equal("12\nFOO\n", render("= yield\n= upcase", :scope => "foo".instance_eval{binding}) { 12 })
506
+ end
507
+
508
+ def test_yield_should_work_with_def_method
509
+ s = "foo"
510
+ Haml::Engine.new("= yield\n= upcase").def_method(s, :render)
511
+ assert_equal("12\nFOO\n", s.render { 12 })
512
+ end
513
+
514
+ def test_def_method_with_module
515
+ Haml::Engine.new("= yield\n= upcase").def_method(String, :render_haml)
516
+ assert_equal("12\nFOO\n", "foo".render_haml { 12 })
517
+ end
518
+
519
+ def test_def_method_locals
520
+ obj = Object.new
521
+ Haml::Engine.new("%p= foo\n.bar{:baz => baz}= boom").def_method(obj, :render, :foo, :baz, :boom)
522
+ assert_equal("<p>1</p>\n<div baz='2' class='bar'>3</div>\n", obj.render(:foo => 1, :baz => 2, :boom => 3))
523
+ end
524
+
525
+ def test_render_proc_locals
526
+ proc = Haml::Engine.new("%p= foo\n.bar{:baz => baz}= boom").render_proc(Object.new, :foo, :baz, :boom)
527
+ assert_equal("<p>1</p>\n<div baz='2' class='bar'>3</div>\n", proc[:foo => 1, :baz => 2, :boom => 3])
528
+ end
529
+
530
+ def test_render_proc_with_binding
531
+ assert_equal("FOO\n", Haml::Engine.new("= upcase").render_proc("foo".instance_eval{binding}).call)
532
+ end
533
+
534
+ def test_ugly_true
535
+ assert_equal("<div id='outer'>\n<div id='inner'>\n<p>hello world</p>\n</div>\n</div>\n",
536
+ render("#outer\n #inner\n %p hello world", :ugly => true))
537
+
538
+ assert_equal("<p>#{'s' * 75}</p>\n",
539
+ render("%p #{'s' * 75}", :ugly => true))
540
+
541
+ assert_equal("<p>#{'s' * 75}</p>\n",
542
+ render("%p= 's' * 75", :ugly => true))
543
+ end
544
+
545
+ def test_xhtml_output_option
546
+ assert_equal "<p>\n <br />\n</p>\n", render("%p\n %br", :format => :xhtml)
547
+ assert_equal "<a />\n", render("%a/", :format => :xhtml)
548
+ end
549
+
550
+ def test_arbitrary_output_option
551
+ assert_raise(Haml::Error, "Invalid output format :html1") { Haml::Engine.new("%br", :format => :html1) }
552
+ end
553
+
554
+ # HTML 4.0
555
+
556
+ def test_html_has_no_self_closing_tags
557
+ assert_equal "<p>\n <br>\n</p>\n", render("%p\n %br", :format => :html4)
558
+ assert_equal "<br>\n", render("%br/", :format => :html4)
559
+ end
560
+
561
+ def test_html_renders_empty_node_with_closing_tag
562
+ assert_equal %{<div class='foo'>\n</div>\n}, render(".foo", :format => :html4)
563
+ end
564
+
565
+ def test_html_ignores_explicit_self_closing_declaration
566
+ assert_equal "<a>\n</a>\n", render("%a/", :format => :html4)
567
+ end
568
+
569
+ def test_html_ignores_xml_prolog_declaration
570
+ assert_equal "", render('!!! XML', :format => :html4)
571
+ end
572
+
573
+ def test_html_has_different_doctype
574
+ assert_equal %{<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n},
575
+ render('!!!', :format => :html4)
576
+ end
577
+
578
+ # because anything before the doctype triggers quirks mode in IE
579
+ def test_xml_prolog_and_doctype_dont_result_in_a_leading_whitespace_in_html
580
+ assert_no_match /^\s+/, render("!!! xml\n!!!", :format => :html4)
581
+ end
582
+
583
+ # HTML5
584
+ def test_html5_doctype
585
+ assert_equal %{<!DOCTYPE html>\n}, render('!!!', :format => :html5)
586
+ end
587
+ end