haml 4.0.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +1 -1
  3. data/CHANGELOG.md +117 -5
  4. data/FAQ.md +7 -17
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +85 -42
  7. data/REFERENCE.md +181 -86
  8. data/Rakefile +47 -51
  9. data/lib/haml/attribute_builder.rb +163 -0
  10. data/lib/haml/attribute_compiler.rb +215 -0
  11. data/lib/haml/attribute_parser.rb +144 -0
  12. data/lib/haml/buffer.rb +38 -128
  13. data/lib/haml/compiler.rb +88 -295
  14. data/lib/haml/engine.rb +25 -41
  15. data/lib/haml/error.rb +3 -0
  16. data/lib/haml/escapable.rb +49 -0
  17. data/lib/haml/exec.rb +33 -19
  18. data/lib/haml/filters.rb +20 -24
  19. data/lib/haml/generator.rb +41 -0
  20. data/lib/haml/helpers/action_view_extensions.rb +3 -2
  21. data/lib/haml/helpers/action_view_mods.rb +44 -66
  22. data/lib/haml/helpers/action_view_xss_mods.rb +1 -0
  23. data/lib/haml/helpers/safe_erubi_template.rb +27 -0
  24. data/lib/haml/helpers/safe_erubis_template.rb +16 -4
  25. data/lib/haml/helpers/xss_mods.rb +18 -12
  26. data/lib/haml/helpers.rb +122 -58
  27. data/lib/haml/options.rb +39 -46
  28. data/lib/haml/parser.rb +278 -217
  29. data/lib/haml/{template/plugin.rb → plugin.rb} +8 -15
  30. data/lib/haml/railtie.rb +21 -11
  31. data/lib/haml/sass_rails_filter.rb +17 -4
  32. data/lib/haml/template/options.rb +12 -2
  33. data/lib/haml/template.rb +12 -6
  34. data/lib/haml/temple_engine.rb +120 -0
  35. data/lib/haml/temple_line_counter.rb +29 -0
  36. data/lib/haml/util.rb +80 -199
  37. data/lib/haml/version.rb +2 -1
  38. data/lib/haml.rb +2 -1
  39. data/test/attribute_parser_test.rb +101 -0
  40. data/test/engine_test.rb +306 -176
  41. data/test/filters_test.rb +32 -19
  42. data/test/gemfiles/Gemfile.rails-4.0.x +11 -0
  43. data/test/gemfiles/Gemfile.rails-4.0.x.lock +87 -0
  44. data/test/gemfiles/Gemfile.rails-4.1.x +5 -0
  45. data/test/gemfiles/Gemfile.rails-4.2.x +5 -0
  46. data/test/gemfiles/Gemfile.rails-5.0.x +4 -0
  47. data/test/helper_test.rb +282 -96
  48. data/test/options_test.rb +22 -0
  49. data/test/parser_test.rb +71 -4
  50. data/test/results/bemit.xhtml +4 -0
  51. data/test/results/eval_suppressed.xhtml +4 -4
  52. data/test/results/helpers.xhtml +43 -41
  53. data/test/results/helpful.xhtml +6 -3
  54. data/test/results/just_stuff.xhtml +21 -20
  55. data/test/results/list.xhtml +9 -9
  56. data/test/results/nuke_inner_whitespace.xhtml +22 -22
  57. data/test/results/nuke_outer_whitespace.xhtml +84 -92
  58. data/test/results/original_engine.xhtml +17 -17
  59. data/test/results/partial_layout.xhtml +4 -3
  60. data/test/results/partial_layout_erb.xhtml +4 -3
  61. data/test/results/partials.xhtml +11 -10
  62. data/test/results/silent_script.xhtml +63 -63
  63. data/test/results/standard.xhtml +156 -159
  64. data/test/results/tag_parsing.xhtml +19 -19
  65. data/test/results/very_basic.xhtml +2 -2
  66. data/test/results/whitespace_handling.xhtml +56 -50
  67. data/test/template_test.rb +44 -53
  68. data/test/template_test_helper.rb +38 -0
  69. data/test/templates/_text_area_helper.html.haml +4 -0
  70. data/test/templates/bemit.haml +3 -0
  71. data/test/templates/just_stuff.haml +1 -0
  72. data/test/templates/partial_layout_erb.erb +1 -1
  73. data/test/templates/standard_ugly.haml +1 -0
  74. data/test/templates/with_bom.haml +1 -0
  75. data/test/temple_line_counter_test.rb +40 -0
  76. data/test/test_helper.rb +26 -12
  77. data/test/util_test.rb +6 -47
  78. metadata +88 -106
  79. data/lib/haml/helpers/rails_323_textarea_fix.rb +0 -24
  80. data/test/gemfiles/Gemfile.rails-3.0.x +0 -5
  81. data/test/gemfiles/Gemfile.rails-3.1.x +0 -6
  82. data/test/gemfiles/Gemfile.rails-3.2.x +0 -5
  83. data/test/gemfiles/Gemfile.rails-master +0 -4
  84. data/test/templates/_av_partial_1_ugly.haml +0 -9
  85. data/test/templates/_av_partial_2_ugly.haml +0 -5
  86. data/test/templates/action_view_ugly.haml +0 -47
  87. data/test/templates/standard_ugly.haml +0 -43
@@ -2,48 +2,13 @@ require 'test_helper'
2
2
  require 'mocks/article'
3
3
 
4
4
  require 'action_pack/version'
5
+ require 'template_test_helper'
5
6
 
6
- module Haml::Filters::Test
7
- include Haml::Filters::Base
8
-
9
- def render(text)
10
- "TESTING HAHAHAHA!"
11
- end
12
- end
13
-
14
- module Haml::Helpers
15
- def test_partial(name, locals = {})
16
- Haml::Engine.new(File.read(File.join(TemplateTest::TEMPLATE_PATH, "_#{name}.haml"))).render(self, locals)
17
- end
18
- end
19
-
20
- class Egocentic
21
- def method_missing(*args)
22
- self
23
- end
24
- end
25
-
26
- class DummyController
27
- attr_accessor :logger
28
- def initialize
29
- @logger = Egocentic.new
30
- end
31
-
32
- def self.controller_path
33
- ''
34
- end
35
-
36
- def controller_path
37
- ''
38
- end
39
- end
40
-
41
- class TemplateTest < MiniTest::Unit::TestCase
42
- TEMPLATE_PATH = File.join(File.dirname(__FILE__), "templates")
7
+ class TemplateTest < Haml::TestCase
43
8
  TEMPLATES = %w{ very_basic standard helpers
44
9
  whitespace_handling original_engine list helpful
45
10
  silent_script tag_parsing just_stuff partials
46
- nuke_outer_whitespace nuke_inner_whitespace
11
+ nuke_outer_whitespace nuke_inner_whitespace bemit
47
12
  render_layout partial_layout partial_layout_erb}
48
13
 
49
14
  def setup
@@ -56,10 +21,10 @@ class TemplateTest < MiniTest::Unit::TestCase
56
21
  def create_base
57
22
  vars = { 'article' => Article.new, 'foo' => 'value one' }
58
23
 
59
- base = ActionView::Base.new(TEMPLATE_PATH, vars)
24
+ base = ActionView::Base.new(TemplateTestHelper::TEMPLATE_PATH, vars)
60
25
 
61
26
  # This is needed by RJS in (at least) Rails 3
62
- base.instance_variable_set('@template', base)
27
+ base.instance_variable_set(:@template, base)
63
28
 
64
29
  # This is used by form_for.
65
30
  # It's usually provided by ActionController::Base.
@@ -83,12 +48,7 @@ class TemplateTest < MiniTest::Unit::TestCase
83
48
  def assert_renders_correctly(name, &render_method)
84
49
  old_options = Haml::Template.options.dup
85
50
  Haml::Template.options[:escape_html] = false
86
- if ActionPack::VERSION::MAJOR < 2 ||
87
- (ActionPack::VERSION::MAJOR == 2 && ActionPack::VERSION::MINOR < 2)
88
- render_method ||= proc { |n| @base.render(n) }
89
- else
90
- render_method ||= proc { |n| @base.render(:file => n) }
91
- end
51
+ render_method ||= proc { |n| @base.render(:file => n) }
92
52
 
93
53
  silence_warnings do
94
54
  load_result(name).split("\n").zip(render_method[name].split("\n")).each_with_index do |pair, line|
@@ -96,7 +56,7 @@ class TemplateTest < MiniTest::Unit::TestCase
96
56
  assert_equal(pair.first, pair.last, message)
97
57
  end
98
58
  end
99
- rescue Haml::Util.av_template_class(:Error) => e
59
+ rescue ActionView::Template::Error => e
100
60
  if e.message =~ /Can't run [\w:]+ filter; required (one of|file) ((?:'\w+'(?: or )?)+)(, but none were found| not found)/
101
61
  puts "\nCouldn't require #{$2}; skipping a test."
102
62
  else
@@ -111,28 +71,51 @@ class TemplateTest < MiniTest::Unit::TestCase
111
71
  end
112
72
 
113
73
  TEMPLATES.each do |template|
114
- define_method "test_template_should_render_correctly [template: #{template}] " do
74
+ define_method "test_template_should_render_correctly [template: #{template}]" do
115
75
  assert_renders_correctly template
116
76
  end
117
77
  end
118
78
 
79
+ def test_render_method_returning_null
80
+ @base.instance_eval do
81
+ def empty
82
+ nil
83
+ end
84
+ def render_something(&block)
85
+ capture(self, &block)
86
+ end
87
+ end
88
+
89
+ content_to_render = "%h1 This is part of the broken view.\n= render_something do |thing|\n = thing.empty do\n = 'test'"
90
+ result = render(content_to_render)
91
+ expected_result = "<h1>This is part of the broken view.</h1>\n"
92
+ assert_equal(expected_result, result)
93
+ end
94
+
95
+ def test_simple_rendering
96
+ content_to_render = "%p test\n= capture { 'foo' }"
97
+ result = render(content_to_render)
98
+ expected_result = "<p>test</p>\nfoo\n"
99
+ assert_equal(expected_result, result)
100
+ end
101
+
119
102
  def test_templates_should_render_correctly_with_render_proc
120
103
  assert_renders_correctly("standard") do |name|
121
- engine = Haml::Engine.new(File.read(File.dirname(__FILE__) + "/templates/#{name}.haml"), :format => :xhtml)
104
+ engine = Haml::Engine.new(File.read(File.dirname(__FILE__) + "/templates/#{name}.haml"), format: :xhtml)
122
105
  engine.render_proc(@base).call
123
106
  end
124
107
  end
125
108
 
126
109
  def test_templates_should_render_correctly_with_def_method
127
110
  assert_renders_correctly("standard") do |name|
128
- engine = Haml::Engine.new(File.read(File.dirname(__FILE__) + "/templates/#{name}.haml"), :format => :xhtml)
111
+ engine = Haml::Engine.new(File.read(File.dirname(__FILE__) + "/templates/#{name}.haml"), format: :xhtml)
129
112
  engine.def_method(@base, "render_standard")
130
113
  @base.render_standard
131
114
  end
132
115
  end
133
116
 
134
117
  def test_instance_variables_should_work_inside_templates
135
- @base.instance_variable_set("@content_for_layout", 'something')
118
+ @base.instance_variable_set(:@content_for_layout, 'something')
136
119
  assert_equal("<p>something</p>", render("%p= @content_for_layout").chomp)
137
120
 
138
121
  @base.instance_eval("@author = 'Hampton Catlin'")
@@ -164,8 +147,8 @@ class TemplateTest < MiniTest::Unit::TestCase
164
147
  Haml::Template.options = old_options
165
148
  end
166
149
 
167
- def test_with_output_buffer_with_ugly
168
- assert_equal(<<HTML, render(<<HAML, :ugly => true))
150
+ def test_with_output_buffer
151
+ assert_equal(<<HTML, render(<<HAML))
169
152
  <p>
170
153
  foo
171
154
  baz
@@ -328,4 +311,12 @@ HTML
328
311
  Test
329
312
  HAML
330
313
  end
314
+
315
+ class ::TosUnsafeObject; def to_s; '<hr>'; end; end
316
+ class ::TosSafeObject; def to_s; '<hr>'.html_safe; end; end
317
+
318
+ def test_object_that_returns_safe_buffer
319
+ assert_equal("<hr>\n", render('= ::TosSafeObject.new', escape_html: true))
320
+ assert_equal("&lt;hr&gt;\n", render('= ::TosUnsafeObject.new', escape_html: true))
321
+ end
331
322
  end
@@ -0,0 +1,38 @@
1
+ module TemplateTestHelper
2
+ TEMPLATE_PATH = File.join(__dir__, "templates")
3
+ end
4
+
5
+ module Haml::Filters::Test
6
+ include Haml::Filters::Base
7
+
8
+ def render(text)
9
+ "TESTING HAHAHAHA!"
10
+ end
11
+ end
12
+
13
+ module Haml::Helpers
14
+ def test_partial(name, locals = {})
15
+ Haml::Engine.new(File.read(File.join(TemplateTestHelper::TEMPLATE_PATH, "_#{name}.haml")), Haml::Template.options).render(self, locals)
16
+ end
17
+ end
18
+
19
+ class Egocentic
20
+ def method_missing(*args)
21
+ self
22
+ end
23
+ end
24
+
25
+ class DummyController
26
+ attr_accessor :logger
27
+ def initialize
28
+ @logger = Egocentic.new
29
+ end
30
+
31
+ def self.controller_path
32
+ ''
33
+ end
34
+
35
+ def controller_path
36
+ ''
37
+ end
38
+ end
@@ -0,0 +1,4 @@
1
+ - defined?(text_area_helper) and nil # silence a warning
2
+ .foo
3
+ .bar
4
+ = text_area :post, :body
@@ -0,0 +1,3 @@
1
+ .o-media@md.c-user.c-user--premium
2
+ %img.o-media__img@md.c-user__photo.c-avatar{src: '', alt: ''}
3
+ %p.o-media__body@md.c-user__bio ...
@@ -29,6 +29,7 @@
29
29
  \- character
30
30
  \%p foo
31
31
  \yee\ha
32
+ \ don't lstrip me
32
33
  / Short comment
33
34
  /
34
35
  This is a block comment
@@ -1,4 +1,4 @@
1
1
  <h1>Partial layout used with for block:</h1>
2
2
  <%= render :layout => 'layout_for_partial' do -%>
3
- <p>Some content within a layout</p>
3
+ Some content within a layout
4
4
  <% end %>
@@ -0,0 +1 @@
1
+ test/templates/standard.haml
@@ -0,0 +1 @@
1
+ BOMG
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+
3
+ class TempleLineCounterTest < Haml::TestCase
4
+ TESTED_TEMPLES = [
5
+ [:multi,
6
+ [:code, "foo"],
7
+ [:static, "bar"],
8
+ [:dynamic, "baz"],
9
+ ],
10
+ [:multi,
11
+ [:code, "foo\nbar\nbaz"],
12
+ [:static, "foo\nbar\nbaz"],
13
+ [:dynamic, "foo\nbar\nbaz"],
14
+ ],
15
+ [:case,
16
+ ["'a\nb', false", [:static, "hello\n"]],
17
+ [:else, [:code, "raise 'error\n'"]],
18
+ ],
19
+ [:escape, true, [:dynamic, "foo\nbar"]],
20
+ [:escape, :once, [:dynamic, "foo\nbar"]],
21
+ [:escape, false, [:dynamic, "foo\nbar"]],
22
+ [:escape, true, [:static, "foo\nbar"]],
23
+ [:escape, :once, [:static, "foo\nbar"]],
24
+ [:escape, false, [:dynamic, "foo\nbar"]],
25
+ ]
26
+
27
+ def test_count_lines
28
+ TESTED_TEMPLES.each do |temple|
29
+ code = Haml::TempleEngine.chain.inject(temple) do |exp, (symbol, filter)|
30
+ case symbol
31
+ when :Parser, :Compiler
32
+ exp
33
+ else
34
+ filter.call(Haml::TempleEngine).call(exp)
35
+ end
36
+ end
37
+ assert_equal code.count("\n"), Haml::TempleLineCounter.count_lines(temple)
38
+ end
39
+ end
40
+ end
data/test/test_helper.rb CHANGED
@@ -1,19 +1,32 @@
1
+ begin
2
+ if ENV['TRAVIS'] && RUBY_VERSION == '2.1.2' && !defined?(Rubinius)
3
+ require 'coveralls'
4
+ Coveralls.wear!
5
+ end
6
+ rescue LoadError
7
+ # ignore error for other test Gemfiles
8
+ end
9
+
1
10
  if ENV["COVERAGE"]
2
11
  require "simplecov"
3
12
  SimpleCov.start
4
13
  end
5
14
 
6
- require 'rubygems'
7
- gem "minitest"
8
15
  require 'bundler/setup'
9
16
  require 'minitest/autorun'
10
17
  require 'action_pack'
11
18
  require 'action_controller'
12
19
  require 'action_view'
20
+ require 'action_view/base'
13
21
  require 'nokogiri'
14
-
15
22
  require 'rails'
23
+
24
+ if defined?(I18n.enforce_available_locales)
25
+ I18n.enforce_available_locales = true
26
+ end
27
+
16
28
  class TestApp < Rails::Application
29
+ config.eager_load = false
17
30
  config.root = ""
18
31
  end
19
32
  Rails.application = TestApp
@@ -27,17 +40,23 @@ $VERBOSE = true
27
40
  require 'haml'
28
41
  require 'haml/template'
29
42
 
30
- Haml::Template.options[:ugly] = false
43
+ TestApp.initialize!
44
+
31
45
  Haml::Template.options[:format] = :xhtml
32
46
 
47
+ BASE_TEST_CLASS = if defined?(Minitest::Test)
48
+ Minitest::Test
49
+ else
50
+ MiniTest::Unit::TestCase
51
+ end
52
+
33
53
  module Declarative
34
54
  def test(name, &block)
35
- define_method("test #{name}", &block)
55
+ define_method("test_ #{name}", &block)
36
56
  end
37
57
  end
38
58
 
39
- class MiniTest::Unit::TestCase
40
-
59
+ class Haml::TestCase < BASE_TEST_CLASS
41
60
  extend Declarative
42
61
 
43
62
  def render(text, options = {}, base = nil, &block)
@@ -65,10 +84,6 @@ class MiniTest::Unit::TestCase
65
84
  Haml::Util.silence_warnings(&block)
66
85
  end
67
86
 
68
- def rails_form_opener
69
- '<div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="&#x2713;" /></div>'
70
- end
71
-
72
87
  def assert_raises_message(klass, message)
73
88
  yield
74
89
  rescue Exception => e
@@ -81,5 +96,4 @@ class MiniTest::Unit::TestCase
81
96
  def self.error(*args)
82
97
  Haml::Error.message(*args)
83
98
  end
84
-
85
99
  end
data/test/util_test.rb CHANGED
@@ -1,21 +1,8 @@
1
1
  require 'test_helper'
2
2
 
3
- class UtilTest < MiniTest::Unit::TestCase
3
+ class UtilTest < Haml::TestCase
4
4
  include Haml::Util
5
5
 
6
- def test_powerset
7
- return unless Set[Set[]] == Set[Set[]] # There's a bug in Ruby 1.8.6 that breaks nested set equality
8
- assert_equal([[].to_set].to_set,
9
- powerset([]))
10
- assert_equal([[].to_set, [1].to_set].to_set,
11
- powerset([1]))
12
- assert_equal([[].to_set, [1].to_set, [2].to_set, [1, 2].to_set].to_set,
13
- powerset([1, 2]))
14
- assert_equal([[].to_set, [1].to_set, [2].to_set, [3].to_set,
15
- [1, 2].to_set, [2, 3].to_set, [1, 3].to_set, [1, 2, 3].to_set].to_set,
16
- powerset([1, 2, 3]))
17
- end
18
-
19
6
  def test_silence_warnings
20
7
  old_stderr, $stderr = $stderr, StringIO.new
21
8
  warn "Out"
@@ -26,38 +13,10 @@ class UtilTest < MiniTest::Unit::TestCase
26
13
  $stderr = old_stderr
27
14
  end
28
15
 
29
- def test_caller_info
30
- assert_equal(["/tmp/foo.rb", 12, "fizzle"], caller_info("/tmp/foo.rb:12: in `fizzle'"))
31
- assert_equal(["/tmp/foo.rb", 12, nil], caller_info("/tmp/foo.rb:12"))
32
- assert_equal(["(haml)", 12, "blah"], caller_info("(haml):12: in `blah'"))
33
- assert_equal(["", 12, "boop"], caller_info(":12: in `boop'"))
34
- assert_equal(["/tmp/foo.rb", -12, "fizzle"], caller_info("/tmp/foo.rb:-12: in `fizzle'"))
35
- assert_equal(["/tmp/foo.rb", 12, "fizzle"], caller_info("/tmp/foo.rb:12: in `fizzle {}'"))
36
- end
37
-
38
- def test_def_static_method
39
- klass = Class.new
40
- def_static_method(klass, :static_method, [:arg1, :arg2],
41
- :sarg1, :sarg2, <<RUBY)
42
- s = "Always " + arg1
43
- s << " <% if sarg1 %>and<% else %>but never<% end %> " << arg2
44
-
45
- <% if sarg2 %>
46
- s << "."
47
- <% end %>
48
- RUBY
49
- c = klass.new
50
- assert_equal("Always brush your teeth and comb your hair.",
51
- c.send(static_method_name(:static_method, true, true),
52
- "brush your teeth", "comb your hair"))
53
- assert_equal("Always brush your teeth and comb your hair",
54
- c.send(static_method_name(:static_method, true, false),
55
- "brush your teeth", "comb your hair"))
56
- assert_equal("Always brush your teeth but never play with fire.",
57
- c.send(static_method_name(:static_method, false, true),
58
- "brush your teeth", "play with fire"))
59
- assert_equal("Always brush your teeth but never play with fire",
60
- c.send(static_method_name(:static_method, false, false),
61
- "brush your teeth", "play with fire"))
16
+ def test_check_encoding_does_not_destoy_the_given_string
17
+ string_with_bom = File.read(File.dirname(__FILE__) + '/templates/with_bom.haml', :encoding => Encoding::UTF_8)
18
+ original = string_with_bom.dup
19
+ check_encoding(string_with_bom)
20
+ assert_equal(original, string_with_bom)
62
21
  end
63
22
  end