haml 4.0.6 → 5.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +19 -0
- data/.gitmodules +3 -0
- data/.travis.yml +72 -0
- data/.yardopts +2 -3
- data/CHANGELOG.md +138 -4
- data/FAQ.md +4 -14
- data/Gemfile +16 -0
- data/MIT-LICENSE +2 -2
- data/README.md +79 -42
- data/REFERENCE.md +142 -67
- data/Rakefile +44 -63
- data/TODO +24 -0
- data/benchmark.rb +70 -0
- data/haml.gemspec +45 -0
- data/lib/haml.rb +2 -0
- data/lib/haml/.gitattributes +1 -0
- data/lib/haml/attribute_builder.rb +164 -0
- data/lib/haml/attribute_compiler.rb +235 -0
- data/lib/haml/attribute_parser.rb +150 -0
- data/lib/haml/buffer.rb +29 -136
- data/lib/haml/compiler.rb +110 -320
- data/lib/haml/engine.rb +34 -41
- data/lib/haml/error.rb +28 -24
- data/lib/haml/escapable.rb +77 -0
- data/lib/haml/exec.rb +38 -20
- data/lib/haml/filters.rb +22 -27
- data/lib/haml/generator.rb +42 -0
- data/lib/haml/helpers.rb +134 -89
- data/lib/haml/helpers/action_view_extensions.rb +4 -2
- data/lib/haml/helpers/action_view_mods.rb +45 -60
- data/lib/haml/helpers/action_view_xss_mods.rb +2 -0
- data/lib/haml/helpers/safe_erubi_template.rb +20 -0
- data/lib/haml/helpers/safe_erubis_template.rb +5 -1
- data/lib/haml/helpers/xss_mods.rb +23 -13
- data/lib/haml/options.rb +63 -69
- data/lib/haml/parser.rb +292 -228
- data/lib/haml/plugin.rb +37 -0
- data/lib/haml/railtie.rb +38 -12
- data/lib/haml/sass_rails_filter.rb +18 -4
- data/lib/haml/template.rb +13 -6
- data/lib/haml/template/options.rb +13 -2
- data/lib/haml/temple_engine.rb +123 -0
- data/lib/haml/temple_line_counter.rb +30 -0
- data/lib/haml/util.rb +83 -202
- data/lib/haml/version.rb +3 -1
- data/yard/default/.gitignore +1 -0
- data/yard/default/fulldoc/html/css/common.sass +15 -0
- data/yard/default/layout/html/footer.erb +12 -0
- metadata +73 -108
- data/lib/haml/template/plugin.rb +0 -41
- data/test/engine_test.rb +0 -2013
- data/test/erb/_av_partial_1.erb +0 -12
- data/test/erb/_av_partial_2.erb +0 -8
- data/test/erb/action_view.erb +0 -62
- data/test/erb/standard.erb +0 -55
- data/test/filters_test.rb +0 -254
- data/test/gemfiles/Gemfile.rails-3.0.x +0 -5
- data/test/gemfiles/Gemfile.rails-3.1.x +0 -6
- data/test/gemfiles/Gemfile.rails-3.2.x +0 -5
- data/test/gemfiles/Gemfile.rails-4.0.x +0 -5
- data/test/helper_test.rb +0 -583
- data/test/markaby/standard.mab +0 -52
- data/test/mocks/article.rb +0 -6
- data/test/parser_test.rb +0 -105
- data/test/results/content_for_layout.xhtml +0 -12
- data/test/results/eval_suppressed.xhtml +0 -9
- data/test/results/helpers.xhtml +0 -70
- data/test/results/helpful.xhtml +0 -10
- data/test/results/just_stuff.xhtml +0 -70
- data/test/results/list.xhtml +0 -12
- data/test/results/nuke_inner_whitespace.xhtml +0 -40
- data/test/results/nuke_outer_whitespace.xhtml +0 -148
- data/test/results/original_engine.xhtml +0 -20
- data/test/results/partial_layout.xhtml +0 -5
- data/test/results/partial_layout_erb.xhtml +0 -5
- data/test/results/partials.xhtml +0 -21
- data/test/results/render_layout.xhtml +0 -3
- data/test/results/silent_script.xhtml +0 -74
- data/test/results/standard.xhtml +0 -162
- data/test/results/tag_parsing.xhtml +0 -23
- data/test/results/very_basic.xhtml +0 -5
- data/test/results/whitespace_handling.xhtml +0 -90
- data/test/template_test.rb +0 -354
- data/test/templates/_av_partial_1.haml +0 -9
- data/test/templates/_av_partial_1_ugly.haml +0 -9
- data/test/templates/_av_partial_2.haml +0 -5
- data/test/templates/_av_partial_2_ugly.haml +0 -5
- data/test/templates/_layout.erb +0 -3
- data/test/templates/_layout_for_partial.haml +0 -3
- data/test/templates/_partial.haml +0 -8
- data/test/templates/_text_area.haml +0 -3
- data/test/templates/_text_area_helper.html.haml +0 -4
- data/test/templates/action_view.haml +0 -47
- data/test/templates/action_view_ugly.haml +0 -47
- data/test/templates/breakage.haml +0 -8
- data/test/templates/content_for_layout.haml +0 -8
- data/test/templates/eval_suppressed.haml +0 -11
- data/test/templates/helpers.haml +0 -55
- data/test/templates/helpful.haml +0 -11
- data/test/templates/just_stuff.haml +0 -85
- data/test/templates/list.haml +0 -12
- data/test/templates/nuke_inner_whitespace.haml +0 -32
- data/test/templates/nuke_outer_whitespace.haml +0 -144
- data/test/templates/original_engine.haml +0 -17
- data/test/templates/partial_layout.haml +0 -3
- data/test/templates/partial_layout_erb.erb +0 -4
- data/test/templates/partialize.haml +0 -1
- data/test/templates/partials.haml +0 -12
- data/test/templates/render_layout.haml +0 -2
- data/test/templates/silent_script.haml +0 -45
- data/test/templates/standard.haml +0 -43
- data/test/templates/standard_ugly.haml +0 -43
- data/test/templates/tag_parsing.haml +0 -21
- data/test/templates/very_basic.haml +0 -4
- data/test/templates/whitespace_handling.haml +0 -87
- data/test/test_helper.rb +0 -81
- data/test/util_test.rb +0 -63
data/lib/haml/engine.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'forwardable'
|
2
4
|
|
3
5
|
require 'haml/parser'
|
@@ -7,6 +9,7 @@ require 'haml/helpers'
|
|
7
9
|
require 'haml/buffer'
|
8
10
|
require 'haml/filters'
|
9
11
|
require 'haml/error'
|
12
|
+
require 'haml/temple_engine'
|
10
13
|
|
11
14
|
module Haml
|
12
15
|
# This is the frontend for using Haml programmatically.
|
@@ -35,9 +38,6 @@ module Haml
|
|
35
38
|
# @return [String]
|
36
39
|
attr_accessor :indentation
|
37
40
|
|
38
|
-
attr_accessor :compiler
|
39
|
-
attr_accessor :parser
|
40
|
-
|
41
41
|
# Tilt currently depends on these moved methods, provide a stable API
|
42
42
|
def_delegators :compiler, :precompiled, :precompiled_method_return_value
|
43
43
|
|
@@ -52,25 +52,28 @@ module Haml
|
|
52
52
|
# see {file:REFERENCE.md#options the Haml options documentation}
|
53
53
|
# @raise [Haml::Error] if there's a Haml syntax error in the template
|
54
54
|
def initialize(template, options = {})
|
55
|
+
# Reflect changes of `Haml::Options.defaults` to `Haml::TempleEngine` options, but `#initialize_encoding`
|
56
|
+
# should be run against the arguemnt `options[:encoding]` for backward compatibility with old `Haml::Engine`.
|
57
|
+
options = Options.defaults.dup.tap { |o| o.delete(:encoding) }.merge!(options)
|
55
58
|
@options = Options.new(options)
|
56
59
|
|
57
60
|
@template = check_haml_encoding(template) do |msg, line|
|
58
61
|
raise Haml::Error.new(msg, line)
|
59
62
|
end
|
60
63
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
@compiler = @options.compiler_class.new(@options)
|
64
|
+
@temple_engine = TempleEngine.new(options)
|
65
|
+
@temple_engine.compile(@template)
|
66
|
+
end
|
65
67
|
|
66
|
-
|
68
|
+
# Deprecated API for backword compatibility
|
69
|
+
def compiler
|
70
|
+
@temple_engine
|
67
71
|
end
|
68
72
|
|
69
73
|
# Processes the template and returns the result as a string.
|
70
74
|
#
|
71
75
|
# `scope` is the context in which the template is evaluated.
|
72
|
-
# If it's a `Binding
|
73
|
-
# Haml uses it as the second argument to `Kernel#eval`;
|
76
|
+
# If it's a `Binding`, Haml uses it as the second argument to `Kernel#eval`;
|
74
77
|
# otherwise, Haml just uses its `#instance_eval` context.
|
75
78
|
#
|
76
79
|
# Note that Haml modifies the evaluation context
|
@@ -95,23 +98,23 @@ module Haml
|
|
95
98
|
# within the template.
|
96
99
|
#
|
97
100
|
# Due to some Ruby quirks,
|
98
|
-
# if `scope` is a `Binding`
|
101
|
+
# if `scope` is a `Binding` object and a block is given,
|
99
102
|
# the evaluation context may not be quite what the user expects.
|
100
103
|
# In particular, it's equivalent to passing `eval("self", scope)` as `scope`.
|
101
104
|
# This won't have an effect in most cases,
|
102
105
|
# but if you're relying on local variables defined in the context of `scope`,
|
103
106
|
# they won't work.
|
104
107
|
#
|
105
|
-
# @param scope [Binding,
|
108
|
+
# @param scope [Binding, Object] The context in which the template is evaluated
|
106
109
|
# @param locals [{Symbol => Object}] Local variables that will be made available
|
107
110
|
# to the template
|
108
111
|
# @param block [#to_proc] A block that can be yielded to within the template
|
109
112
|
# @return [String] The rendered template
|
110
113
|
def render(scope = Object.new, locals = {}, &block)
|
111
|
-
parent = scope.instance_variable_defined?(
|
114
|
+
parent = scope.instance_variable_defined?(:@haml_buffer) ? scope.instance_variable_get(:@haml_buffer) : nil
|
112
115
|
buffer = Haml::Buffer.new(parent, @options.for_buffer)
|
113
116
|
|
114
|
-
if scope.is_a?(Binding)
|
117
|
+
if scope.is_a?(Binding)
|
115
118
|
scope_object = eval("self", scope)
|
116
119
|
scope = scope_object.instance_eval{binding} if block_given?
|
117
120
|
else
|
@@ -121,20 +124,16 @@ module Haml
|
|
121
124
|
|
122
125
|
set_locals(locals.merge(:_hamlout => buffer, :_erbout => buffer.buffer), scope, scope_object)
|
123
126
|
|
124
|
-
scope_object.
|
125
|
-
|
126
|
-
@haml_buffer = buffer
|
127
|
-
end
|
127
|
+
scope_object.extend(Haml::Helpers)
|
128
|
+
scope_object.instance_variable_set(:@haml_buffer, buffer)
|
128
129
|
begin
|
129
|
-
eval(@
|
130
|
+
eval(@temple_engine.precompiled_with_return_value, scope, @options.filename, @options.line)
|
130
131
|
rescue ::SyntaxError => e
|
131
132
|
raise SyntaxError, e.message
|
132
133
|
end
|
133
134
|
ensure
|
134
135
|
# Get rid of the current buffer
|
135
|
-
scope_object.
|
136
|
-
@haml_buffer = buffer.upper if buffer
|
137
|
-
end
|
136
|
+
scope_object.instance_variable_set(:@haml_buffer, buffer.upper) if buffer
|
138
137
|
end
|
139
138
|
alias_method :to_html, :render
|
140
139
|
|
@@ -159,11 +158,11 @@ module Haml
|
|
159
158
|
#
|
160
159
|
# The proc doesn't take a block; any yields in the template will fail.
|
161
160
|
#
|
162
|
-
# @param scope [Binding,
|
161
|
+
# @param scope [Binding, Object] The context in which the template is evaluated
|
163
162
|
# @param local_names [Array<Symbol>] The names of the locals that can be passed to the proc
|
164
163
|
# @return [Proc] The proc that will run the template
|
165
164
|
def render_proc(scope = Object.new, *local_names)
|
166
|
-
if scope.is_a?(Binding)
|
165
|
+
if scope.is_a?(Binding)
|
167
166
|
scope_object = eval("self", scope)
|
168
167
|
else
|
169
168
|
scope_object = scope
|
@@ -171,8 +170,13 @@ module Haml
|
|
171
170
|
end
|
172
171
|
|
173
172
|
begin
|
174
|
-
|
175
|
-
|
173
|
+
str = @temple_engine.precompiled_with_ambles(local_names)
|
174
|
+
eval(
|
175
|
+
"Proc.new { |*_haml_locals| _haml_locals = _haml_locals[0] || {}; #{str}}\n",
|
176
|
+
scope,
|
177
|
+
@options.filename,
|
178
|
+
@options.line
|
179
|
+
)
|
176
180
|
rescue ::SyntaxError => e
|
177
181
|
raise SyntaxError, e.message
|
178
182
|
end
|
@@ -182,7 +186,7 @@ module Haml
|
|
182
186
|
# that renders the template and returns the result as a string.
|
183
187
|
#
|
184
188
|
# If `object` is a class or module,
|
185
|
-
# the method will instead
|
189
|
+
# the method will instead be defined as an instance method.
|
186
190
|
# For example:
|
187
191
|
#
|
188
192
|
# t = Time.now
|
@@ -219,25 +223,14 @@ module Haml
|
|
219
223
|
def def_method(object, name, *local_names)
|
220
224
|
method = object.is_a?(Module) ? :module_eval : :instance_eval
|
221
225
|
|
222
|
-
object.send(method, "def #{name}(_haml_locals = {}); #{
|
223
|
-
@options
|
226
|
+
object.send(method, "def #{name}(_haml_locals = {}); #{@temple_engine.precompiled_with_ambles(local_names)}; end",
|
227
|
+
@options.filename, @options.line)
|
224
228
|
end
|
225
229
|
|
226
230
|
private
|
227
231
|
|
228
|
-
if RUBY_VERSION < "1.9"
|
229
|
-
def initialize_encoding(given_value)
|
230
|
-
end
|
231
|
-
else
|
232
|
-
def initialize_encoding(given_value)
|
233
|
-
unless given_value
|
234
|
-
@options.encoding = Encoding.default_internal || @template.encoding
|
235
|
-
end
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
232
|
def set_locals(locals, scope, scope_object)
|
240
|
-
scope_object.
|
233
|
+
scope_object.instance_variable_set :@_haml_locals, locals
|
241
234
|
set_locals = locals.keys.map { |k| "#{k} = @_haml_locals[#{k.inspect}]" }.join("\n")
|
242
235
|
eval(set_locals, scope)
|
243
236
|
end
|
data/lib/haml/error.rb
CHANGED
@@ -1,31 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Haml
|
2
4
|
# An exception raised by Haml code.
|
3
5
|
class Error < StandardError
|
4
6
|
|
5
7
|
MESSAGES = {
|
6
|
-
:
|
7
|
-
:
|
8
|
-
:
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
12
|
-
:
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
26
|
-
:
|
27
|
-
:
|
28
|
-
:
|
8
|
+
bad_script_indent: '"%s" is indented at wrong level: expected %d, but was at %d.',
|
9
|
+
cant_run_filter: 'Can\'t run "%s" filter; you must require its dependencies first',
|
10
|
+
cant_use_tabs_and_spaces: "Indentation can't use both tabs and spaces.",
|
11
|
+
deeper_indenting: "The line was indented %d levels deeper than the previous line.",
|
12
|
+
filter_not_defined: 'Filter "%s" is not defined.',
|
13
|
+
gem_install_filter_deps: '"%s" filter\'s %s dependency missing: try installing it or adding it to your Gemfile',
|
14
|
+
illegal_element: "Illegal element: classes and ids must have values.",
|
15
|
+
illegal_nesting_content: "Illegal nesting: nesting within a tag that already has content is illegal.",
|
16
|
+
illegal_nesting_header: "Illegal nesting: nesting within a header command is illegal.",
|
17
|
+
illegal_nesting_line: "Illegal nesting: content can't be both given on the same line as %%%s and nested within it.",
|
18
|
+
illegal_nesting_plain: "Illegal nesting: nesting within plain text is illegal.",
|
19
|
+
illegal_nesting_self_closing: "Illegal nesting: nesting within a self-closing tag is illegal.",
|
20
|
+
inconsistent_indentation: "Inconsistent indentation: %s used for indentation, but the rest of the document was indented using %s.",
|
21
|
+
indenting_at_start: "Indenting at the beginning of the document is illegal.",
|
22
|
+
install_haml_contrib: 'To use the "%s" filter, please install the haml-contrib gem.',
|
23
|
+
invalid_attribute_list: 'Invalid attribute list: %s.',
|
24
|
+
invalid_filter_name: 'Invalid filter name ":%s".',
|
25
|
+
invalid_tag: 'Invalid tag: "%s".',
|
26
|
+
missing_if: 'Got "%s" with no preceding "if"',
|
27
|
+
no_ruby_code: "There's no Ruby code for %s to evaluate.",
|
28
|
+
self_closing_content: "Self-closing tags can't have content.",
|
29
|
+
unbalanced_brackets: 'Unbalanced brackets.',
|
30
|
+
no_end: <<-END
|
29
31
|
You don't need to use "- end" in Haml. Un-indent to close a block:
|
30
32
|
- if foo?
|
31
33
|
%strong Foo!
|
@@ -33,7 +35,7 @@ You don't need to use "- end" in Haml. Un-indent to close a block:
|
|
33
35
|
Not foo.
|
34
36
|
%p This line is un-indented, so it isn't part of the "if" block
|
35
37
|
END
|
36
|
-
}
|
38
|
+
}.freeze
|
37
39
|
|
38
40
|
def self.message(key, *args)
|
39
41
|
string = MESSAGES[key] or raise "[HAML BUG] No error messages for #{key}"
|
@@ -58,4 +60,6 @@ END
|
|
58
60
|
# It's not particularly interesting,
|
59
61
|
# except in that it's a subclass of {Haml::Error}.
|
60
62
|
class SyntaxError < Error; end
|
63
|
+
|
64
|
+
class InvalidAttributeNameError < SyntaxError; end
|
61
65
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Haml
|
4
|
+
# Like Temple::Filters::Escapable, but with support for escaping by
|
5
|
+
# Haml::Herlpers.html_escape and Haml::Herlpers.escape_once.
|
6
|
+
class Escapable < Temple::Filter
|
7
|
+
# Special value of `flag` to ignore html_safe?
|
8
|
+
EscapeSafeBuffer = Struct.new(:value)
|
9
|
+
|
10
|
+
def initialize(*)
|
11
|
+
super
|
12
|
+
@escape = false
|
13
|
+
@escape_safe_buffer = false
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_escape(flag, exp)
|
17
|
+
old_escape, old_escape_safe_buffer = @escape, @escape_safe_buffer
|
18
|
+
@escape_safe_buffer = flag.is_a?(EscapeSafeBuffer)
|
19
|
+
@escape = @escape_safe_buffer ? flag.value : flag
|
20
|
+
compile(exp)
|
21
|
+
ensure
|
22
|
+
@escape, @escape_safe_buffer = old_escape, old_escape_safe_buffer
|
23
|
+
end
|
24
|
+
|
25
|
+
# The same as Haml::AttributeBuilder.build_attributes
|
26
|
+
def on_static(value)
|
27
|
+
[:static,
|
28
|
+
if @escape == :once
|
29
|
+
escape_once(value)
|
30
|
+
elsif @escape
|
31
|
+
escape(value)
|
32
|
+
else
|
33
|
+
value
|
34
|
+
end
|
35
|
+
]
|
36
|
+
end
|
37
|
+
|
38
|
+
# The same as Haml::AttributeBuilder.build_attributes
|
39
|
+
def on_dynamic(value)
|
40
|
+
[:dynamic,
|
41
|
+
if @escape == :once
|
42
|
+
escape_once_code(value)
|
43
|
+
elsif @escape
|
44
|
+
escape_code(value)
|
45
|
+
else
|
46
|
+
"(#{value}).to_s"
|
47
|
+
end
|
48
|
+
]
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def escape_once(value)
|
54
|
+
if @escape_safe_buffer
|
55
|
+
::Haml::Helpers.escape_once_without_haml_xss(value)
|
56
|
+
else
|
57
|
+
::Haml::Helpers.escape_once(value)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def escape(value)
|
62
|
+
if @escape_safe_buffer
|
63
|
+
::Haml::Helpers.html_escape_without_haml_xss(value)
|
64
|
+
else
|
65
|
+
::Haml::Helpers.html_escape(value)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def escape_once_code(value)
|
70
|
+
"::Haml::Helpers.escape_once#{('_without_haml_xss' if @escape_safe_buffer)}((#{value}))"
|
71
|
+
end
|
72
|
+
|
73
|
+
def escape_code(value)
|
74
|
+
"::Haml::Helpers.html_escape#{('_without_haml_xss' if @escape_safe_buffer)}((#{value}))"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/haml/exec.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'optparse'
|
2
|
-
require 'fileutils'
|
3
4
|
require 'rbconfig'
|
4
5
|
require 'pp'
|
5
6
|
|
@@ -120,7 +121,7 @@ module Haml
|
|
120
121
|
@options[:input], @options[:output] = input, output
|
121
122
|
end
|
122
123
|
|
123
|
-
COLORS = {
|
124
|
+
COLORS = {red: 31, green: 32, yellow: 33}.freeze
|
124
125
|
|
125
126
|
# Prints a status message about performing the given action,
|
126
127
|
# colored using the given color (via terminal escapes) if possible.
|
@@ -212,11 +213,6 @@ END
|
|
212
213
|
@options[:output] = StringIO.new
|
213
214
|
end
|
214
215
|
|
215
|
-
opts.on('-t', '--style NAME',
|
216
|
-
'Output style. Can be indented (default) or ugly.') do |name|
|
217
|
-
@options[:for_engine][:ugly] = true if name.to_sym == :ugly
|
218
|
-
end
|
219
|
-
|
220
216
|
opts.on('-f', '--format NAME',
|
221
217
|
'Output format. Can be html5 (default), xhtml, or html4.') do |name|
|
222
218
|
@options[:for_engine][:format] = name.to_sym
|
@@ -237,6 +233,11 @@ END
|
|
237
233
|
@options[:for_engine][:attr_wrapper] = '"'
|
238
234
|
end
|
239
235
|
|
236
|
+
opts.on('--remove-whitespace',
|
237
|
+
'Remove whitespace surrounding and within tags') do
|
238
|
+
@options[:for_engine][:remove_whitespace] = true
|
239
|
+
end
|
240
|
+
|
240
241
|
opts.on('--cdata',
|
241
242
|
'Always add CDATA sections to javascript and css blocks.') do
|
242
243
|
@options[:for_engine][:cdata] = true
|
@@ -260,15 +261,13 @@ END
|
|
260
261
|
@options[:load_paths] << path
|
261
262
|
end
|
262
263
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
Encoding.default_internal = internal if internal && !internal.empty?
|
268
|
-
end
|
264
|
+
opts.on('-E ex[:in]', 'Specify the default external and internal character encodings.') do |encoding|
|
265
|
+
external, internal = encoding.split(':')
|
266
|
+
Encoding.default_external = external if external && !external.empty?
|
267
|
+
Encoding.default_internal = internal if internal && !internal.empty?
|
269
268
|
end
|
270
269
|
|
271
|
-
opts.on('-d', '--debug', "Print out the precompiled Ruby source.") do
|
270
|
+
opts.on('-d', '--debug', "Print out the precompiled Ruby source, and show syntax errors in the Ruby code.") do
|
272
271
|
@options[:debug] = true
|
273
272
|
end
|
274
273
|
|
@@ -294,20 +293,33 @@ END
|
|
294
293
|
|
295
294
|
begin
|
296
295
|
|
297
|
-
|
298
|
-
|
299
|
-
|
296
|
+
if @options[:parse]
|
297
|
+
parser = ::Haml::Parser.new(::Haml::Options.new(@options))
|
298
|
+
pp parser.call(template)
|
300
299
|
return
|
301
300
|
end
|
302
301
|
|
303
|
-
|
304
|
-
|
302
|
+
engine = ::Haml::Engine.new(template, @options[:for_engine])
|
303
|
+
|
304
|
+
if @options[:check_syntax]
|
305
|
+
error = validate_ruby(engine.precompiled)
|
306
|
+
if error
|
307
|
+
puts error.message.split("\n").first
|
308
|
+
exit 1
|
309
|
+
end
|
310
|
+
puts "Syntax OK"
|
305
311
|
return
|
306
312
|
end
|
307
313
|
|
308
314
|
if @options[:debug]
|
309
315
|
puts engine.precompiled
|
310
|
-
|
316
|
+
error = validate_ruby(engine.precompiled)
|
317
|
+
if error
|
318
|
+
puts '=' * 100
|
319
|
+
puts error.message.split("\n")[0]
|
320
|
+
exit 1
|
321
|
+
end
|
322
|
+
return
|
311
323
|
end
|
312
324
|
|
313
325
|
result = engine.to_html
|
@@ -324,6 +336,12 @@ END
|
|
324
336
|
output.write(result)
|
325
337
|
output.close() if output.is_a? File
|
326
338
|
end
|
339
|
+
|
340
|
+
def validate_ruby(code)
|
341
|
+
eval("BEGIN {return nil}; #{code}", binding, @options[:filename] || "")
|
342
|
+
rescue ::SyntaxError # Not to be confused with Haml::SyntaxError
|
343
|
+
$!
|
344
|
+
end
|
327
345
|
end
|
328
346
|
end
|
329
347
|
end
|
data/lib/haml/filters.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "tilt"
|
2
4
|
|
3
5
|
module Haml
|
@@ -59,7 +61,7 @@ module Haml
|
|
59
61
|
end
|
60
62
|
|
61
63
|
# Removes a filter from Haml. If the filter was removed, it returns
|
62
|
-
# the that was
|
64
|
+
# the Module that was removed upon success, or nil on failure. If you try
|
63
65
|
# to redefine a filter, Haml will raise an error. Use this method first to
|
64
66
|
# explicitly remove the filter before redefining it.
|
65
67
|
# @return Module The filter module that has been removed
|
@@ -118,7 +120,7 @@ module Haml
|
|
118
120
|
# @param text [String] The source text for the filter to process
|
119
121
|
# @return [String] The filtered result
|
120
122
|
# @raise [Haml::Error] if it's not overridden
|
121
|
-
def render(
|
123
|
+
def render(_text)
|
122
124
|
raise Error.new("#{self.inspect}#render not defined!")
|
123
125
|
end
|
124
126
|
|
@@ -129,7 +131,7 @@ module Haml
|
|
129
131
|
# @param text [String] The source text for the filter to process
|
130
132
|
# @return [String] The filtered result
|
131
133
|
# @raise [Haml::Error] if it or \{#render} isn't overridden
|
132
|
-
def render_with_options(text,
|
134
|
+
def render_with_options(text, _options)
|
133
135
|
render(text)
|
134
136
|
end
|
135
137
|
|
@@ -163,10 +165,14 @@ module Haml
|
|
163
165
|
if contains_interpolation?(text)
|
164
166
|
return if options[:suppress_eval]
|
165
167
|
|
166
|
-
|
168
|
+
escape = options[:escape_filter_interpolations]
|
169
|
+
# `escape_filter_interpolations` defaults to `escape_html` if unset.
|
170
|
+
escape = options[:escape_html] if escape.nil?
|
171
|
+
|
172
|
+
text = unescape_interpolation(text, escape).gsub(/(\\+)n/) do |s|
|
167
173
|
escapes = $1.size
|
168
174
|
next s if escapes % 2 == 0
|
169
|
-
|
175
|
+
"#{'\\' * (escapes - 1)}\n"
|
170
176
|
end
|
171
177
|
# We need to add a newline at the beginning to get the
|
172
178
|
# filter lines to line up (since the Haml filter contains
|
@@ -174,20 +180,15 @@ module Haml
|
|
174
180
|
# filter name). Then we need to escape the trailing
|
175
181
|
# newline so that the whole filter block doesn't take up
|
176
182
|
# too many.
|
177
|
-
text =
|
183
|
+
text = %[\n#{text.sub(/\n"\Z/, "\\n\"")}]
|
178
184
|
push_script <<RUBY.rstrip, :escape_html => false
|
179
185
|
find_and_preserve(#{filter.inspect}.render_with_options(#{text}, _hamlout.options))
|
180
186
|
RUBY
|
181
187
|
return
|
182
188
|
end
|
183
189
|
|
184
|
-
rendered = Haml::Helpers::find_and_preserve(filter.render_with_options(text, compiler.options), compiler.options[:preserve])
|
185
|
-
|
186
|
-
if options[:ugly]
|
187
|
-
push_text(rendered.rstrip)
|
188
|
-
else
|
189
|
-
push_text(rendered.rstrip.gsub("\n", "\n#{' ' * @output_tabs}"))
|
190
|
-
end
|
190
|
+
rendered = Haml::Helpers::find_and_preserve(filter.render_with_options(text.to_s, compiler.options), compiler.options[:preserve])
|
191
|
+
push_text("#{rendered.rstrip}\n")
|
191
192
|
end
|
192
193
|
end
|
193
194
|
end
|
@@ -216,13 +217,10 @@ RUBY
|
|
216
217
|
type = " type=#{options[:attr_wrapper]}text/javascript#{options[:attr_wrapper]}"
|
217
218
|
end
|
218
219
|
|
219
|
-
|
220
|
-
|
221
|
-
str << "#{indent}#{text.rstrip.gsub("\n", "\n#{indent}")}\n"
|
222
|
-
str << " //]]>\n" if options[:cdata]
|
223
|
-
str << "</script>"
|
220
|
+
text = text.rstrip
|
221
|
+
text.gsub!("\n", "\n#{indent}")
|
224
222
|
|
225
|
-
|
223
|
+
%!<script#{type}>\n#{" //<![CDATA[\n" if options[:cdata]}#{indent}#{text}\n#{" //]]>\n" if options[:cdata]}</script>!
|
226
224
|
end
|
227
225
|
end
|
228
226
|
|
@@ -240,13 +238,10 @@ RUBY
|
|
240
238
|
type = " type=#{options[:attr_wrapper]}text/css#{options[:attr_wrapper]}"
|
241
239
|
end
|
242
240
|
|
243
|
-
|
244
|
-
|
245
|
-
str << "#{indent}#{text.rstrip.gsub("\n", "\n#{indent}")}\n"
|
246
|
-
str << " /*]]>*/\n" if options[:cdata]
|
247
|
-
str << "</style>"
|
241
|
+
text = text.rstrip
|
242
|
+
text.gsub!("\n", "\n#{indent}")
|
248
243
|
|
249
|
-
|
244
|
+
%(<style#{type}>\n#{" /*<![CDATA[*/\n" if options[:cdata]}#{indent}#{text}\n#{" /*]]>*/\n" if options[:cdata]}</style>)
|
250
245
|
end
|
251
246
|
end
|
252
247
|
|
@@ -256,7 +251,7 @@ RUBY
|
|
256
251
|
|
257
252
|
# @see Base#render
|
258
253
|
def render(text)
|
259
|
-
"<![CDATA[#{
|
254
|
+
"<![CDATA[#{"\n#{text.rstrip}".gsub("\n", "\n ")}\n]]>"
|
260
255
|
end
|
261
256
|
end
|
262
257
|
|
@@ -288,7 +283,7 @@ RUBY
|
|
288
283
|
def compile(compiler, text)
|
289
284
|
return if compiler.options[:suppress_eval]
|
290
285
|
compiler.instance_eval do
|
291
|
-
push_silent <<-FIRST.
|
286
|
+
push_silent "#{<<-FIRST.tr("\n", ';')}#{text}#{<<-LAST.tr("\n", ';')}"
|
292
287
|
begin
|
293
288
|
haml_io = StringIO.new(_hamlout.buffer, 'a')
|
294
289
|
FIRST
|