haml 4.0.6 → 5.2.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.
- 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/plugin.rb
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Haml
|
|
4
|
+
|
|
5
|
+
# This module makes Haml work with Rails using the template handler API.
|
|
6
|
+
class Plugin
|
|
7
|
+
def handles_encoding?; true; end
|
|
8
|
+
|
|
9
|
+
def compile(template, source)
|
|
10
|
+
options = Haml::Template.options.dup
|
|
11
|
+
if template.respond_to?(:type)
|
|
12
|
+
options[:mime_type] = template.type
|
|
13
|
+
elsif template.respond_to? :mime_type
|
|
14
|
+
options[:mime_type] = template.mime_type
|
|
15
|
+
end
|
|
16
|
+
options[:filename] = template.identifier
|
|
17
|
+
Haml::Engine.new(source, options).compiler.precompiled_with_ambles(
|
|
18
|
+
[],
|
|
19
|
+
after_preamble: '@output_buffer = output_buffer ||= ActionView::OutputBuffer.new if defined?(ActionView::OutputBuffer)',
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.call(template, source = nil)
|
|
24
|
+
source ||= template.source
|
|
25
|
+
|
|
26
|
+
new.compile(template, source)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def cache_fragment(block, name = {}, options = nil)
|
|
30
|
+
@view.fragment_for(block, name, options) do
|
|
31
|
+
eval("_hamlout.buffer", block.binding)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
ActionView::Template.register_template_handler(:haml, Haml::Plugin)
|
data/lib/haml/railtie.rb
CHANGED
|
@@ -1,22 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'haml/template/options'
|
|
4
|
+
|
|
5
|
+
# check for a compatible Rails version when Haml is loaded
|
|
6
|
+
if (activesupport_spec = Gem.loaded_specs['activesupport'])
|
|
7
|
+
if activesupport_spec.version.to_s < '4.0'
|
|
8
|
+
raise Exception.new("\n\n** Haml now requires Rails 4.0 and later. Use Haml version 4.0.x\n\n")
|
|
7
9
|
end
|
|
8
10
|
end
|
|
9
11
|
|
|
10
12
|
module Haml
|
|
13
|
+
module Filters
|
|
14
|
+
module RailsErb
|
|
15
|
+
extend Plain
|
|
16
|
+
extend TiltFilter
|
|
17
|
+
extend PrecompiledTiltFilter
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
11
21
|
class Railtie < ::Rails::Railtie
|
|
12
22
|
initializer :haml do |app|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
23
|
+
ActiveSupport.on_load(:action_view) do
|
|
24
|
+
require "haml/template"
|
|
25
|
+
|
|
26
|
+
if defined?(::Sass::Rails::SassTemplate) && app.config.assets.enabled
|
|
27
|
+
require "haml/sass_rails_filter"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Any object under ActionView::Template will be defined as the root constant with the same
|
|
31
|
+
# name if it exists. If Erubi is loaded at all, ActionView::Template::Handlers::ERB::Erubi
|
|
32
|
+
# will turn out to be a reference to the ::Erubi module.
|
|
33
|
+
# In Rails 4.2, calling const_defined? results in odd exceptions, which seems to be
|
|
34
|
+
# solved by looking for ::Erubi first.
|
|
35
|
+
# However, in JRuby, the const_defined? finds it anyway, so we must make sure that it's
|
|
36
|
+
# not just a reference to ::Erubi.
|
|
37
|
+
if defined?(::Erubi) && (::ActionView::Template::Handlers::ERB.const_get('Erubi') != ::Erubi)
|
|
38
|
+
require "haml/helpers/safe_erubi_template"
|
|
39
|
+
Haml::Filters::RailsErb.template_class = Haml::SafeErubiTemplate
|
|
40
|
+
else
|
|
41
|
+
require "haml/helpers/safe_erubis_template"
|
|
42
|
+
Haml::Filters::RailsErb.template_class = Haml::SafeErubisTemplate
|
|
43
|
+
end
|
|
44
|
+
Haml::Template.options[:filters] = { 'erb' => Haml::Filters::RailsErb }
|
|
16
45
|
end
|
|
17
46
|
end
|
|
18
47
|
end
|
|
19
48
|
end
|
|
20
|
-
|
|
21
|
-
require "haml/helpers/safe_erubis_template"
|
|
22
|
-
Haml::Filters::Erb.template_class = Haml::SafeErubisTemplate
|
|
@@ -1,11 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Haml
|
|
2
4
|
module Filters
|
|
3
5
|
# This is an extension of Sass::Rails's SassTemplate class that allows
|
|
4
6
|
# Rails's asset helpers to be used inside Haml Sass filter.
|
|
5
7
|
class SassRailsTemplate < ::Sass::Rails::SassTemplate
|
|
6
|
-
|
|
7
|
-
scope
|
|
8
|
-
|
|
8
|
+
if Gem::Version.new(Sprockets::VERSION) >= Gem::Version.new('3.0.0')
|
|
9
|
+
def render(scope=Object.new, locals={}, &block)
|
|
10
|
+
environment = ::Sprockets::Railtie.build_environment(::Rails.application)
|
|
11
|
+
scope = environment.context_class.new(
|
|
12
|
+
environment: environment,
|
|
13
|
+
filename: "/",
|
|
14
|
+
metadata: {}
|
|
15
|
+
)
|
|
16
|
+
super
|
|
17
|
+
end
|
|
18
|
+
else
|
|
19
|
+
def render(scope=Object.new, locals={}, &block)
|
|
20
|
+
scope = ::Rails.application.assets.context_class.new(::Rails.application.assets, "/", "/")
|
|
21
|
+
super
|
|
22
|
+
end
|
|
9
23
|
end
|
|
10
24
|
|
|
11
25
|
def sass_options(scope)
|
|
@@ -30,4 +44,4 @@ module Haml
|
|
|
30
44
|
register_tilt_filter "Sass", :extend => "Css", :template_class => SassRailsTemplate
|
|
31
45
|
register_tilt_filter "Scss", :extend => "Css", :template_class => ScssRailsTemplate
|
|
32
46
|
end
|
|
33
|
-
end
|
|
47
|
+
end
|
data/lib/haml/template.rb
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'haml/template/options'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
require 'haml/helpers/
|
|
4
|
+
if defined?(ActiveSupport)
|
|
5
|
+
ActiveSupport.on_load(:action_view) do
|
|
6
|
+
require 'haml/helpers/action_view_mods'
|
|
7
|
+
require 'haml/helpers/action_view_extensions'
|
|
8
|
+
end
|
|
9
|
+
else
|
|
10
|
+
require 'haml/helpers/action_view_mods'
|
|
11
|
+
require 'haml/helpers/action_view_extensions'
|
|
12
|
+
end
|
|
5
13
|
require 'haml/helpers/xss_mods'
|
|
6
14
|
require 'haml/helpers/action_view_xss_mods'
|
|
7
15
|
|
|
8
16
|
module Haml
|
|
9
|
-
class
|
|
17
|
+
class TempleEngine
|
|
10
18
|
def precompiled_method_return_value_with_haml_xss
|
|
11
19
|
"::Haml::Util.html_safe(#{precompiled_method_return_value_without_haml_xss})"
|
|
12
20
|
end
|
|
@@ -26,7 +34,6 @@ module Haml
|
|
|
26
34
|
end
|
|
27
35
|
|
|
28
36
|
|
|
29
|
-
Haml::Template.options[:ugly] = defined?(Rails) ? !Rails.env.development? : true
|
|
30
37
|
Haml::Template.options[:escape_html] = true
|
|
31
38
|
|
|
32
|
-
require 'haml/
|
|
39
|
+
require 'haml/plugin'
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# We keep options in its own self-contained file
|
|
2
4
|
# so that we can load it independently in Rails 3,
|
|
3
5
|
# where the full template stuff is lazy-loaded.
|
|
@@ -6,11 +8,20 @@ module Haml
|
|
|
6
8
|
module Template
|
|
7
9
|
extend self
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
class Options < Hash
|
|
12
|
+
def []=(key, value)
|
|
13
|
+
super
|
|
14
|
+
if Haml::Options.buffer_defaults.key?(key)
|
|
15
|
+
Haml::Options.buffer_defaults[key] = value
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
@options = ::Haml::Template::Options.new
|
|
10
21
|
# The options hash for Haml when used within Rails.
|
|
11
22
|
# See {file:REFERENCE.md#options the Haml options documentation}.
|
|
12
23
|
#
|
|
13
|
-
# @return [
|
|
24
|
+
# @return [Haml::Template::Options<Symbol => Object>]
|
|
14
25
|
attr_accessor :options
|
|
15
26
|
end
|
|
16
27
|
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'temple'
|
|
4
|
+
require 'haml/escapable'
|
|
5
|
+
require 'haml/generator'
|
|
6
|
+
|
|
7
|
+
module Haml
|
|
8
|
+
class TempleEngine < Temple::Engine
|
|
9
|
+
define_options(
|
|
10
|
+
attr_wrapper: "'",
|
|
11
|
+
autoclose: %w(area base basefont br col command embed frame
|
|
12
|
+
hr img input isindex keygen link menuitem meta
|
|
13
|
+
param source track wbr),
|
|
14
|
+
encoding: nil,
|
|
15
|
+
escape_attrs: true,
|
|
16
|
+
escape_html: false,
|
|
17
|
+
escape_filter_interpolations: nil,
|
|
18
|
+
filename: '(haml)',
|
|
19
|
+
format: :html5,
|
|
20
|
+
hyphenate_data_attrs: true,
|
|
21
|
+
line: 1,
|
|
22
|
+
mime_type: 'text/html',
|
|
23
|
+
preserve: %w(textarea pre code),
|
|
24
|
+
remove_whitespace: false,
|
|
25
|
+
suppress_eval: false,
|
|
26
|
+
cdata: false,
|
|
27
|
+
parser_class: ::Haml::Parser,
|
|
28
|
+
compiler_class: ::Haml::Compiler,
|
|
29
|
+
trace: false,
|
|
30
|
+
filters: {},
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
use :Parser, -> { options[:parser_class] }
|
|
34
|
+
use :Compiler, -> { options[:compiler_class] }
|
|
35
|
+
use Escapable
|
|
36
|
+
filter :ControlFlow
|
|
37
|
+
filter :MultiFlattener
|
|
38
|
+
filter :StaticMerger
|
|
39
|
+
use Generator
|
|
40
|
+
|
|
41
|
+
def compile(template)
|
|
42
|
+
initialize_encoding(template, options[:encoding])
|
|
43
|
+
@precompiled = call(template)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# The source code that is evaluated to produce the Haml document.
|
|
47
|
+
#
|
|
48
|
+
# This is automatically converted to the correct encoding
|
|
49
|
+
# (see {file:REFERENCE.md#encodings the `:encoding` option}).
|
|
50
|
+
#
|
|
51
|
+
# @return [String]
|
|
52
|
+
def precompiled
|
|
53
|
+
encoding = Encoding.find(@encoding || '')
|
|
54
|
+
return @precompiled.dup.force_encoding(encoding) if encoding == Encoding::ASCII_8BIT
|
|
55
|
+
return @precompiled.encode(encoding)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def precompiled_with_return_value
|
|
59
|
+
"#{precompiled};#{precompiled_method_return_value}".dup
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# The source code that is evaluated to produce the Haml document.
|
|
63
|
+
#
|
|
64
|
+
# This is automatically converted to the correct encoding
|
|
65
|
+
# (see {file:REFERENCE.md#encodings the `:encoding` option}).
|
|
66
|
+
#
|
|
67
|
+
# @return [String]
|
|
68
|
+
def precompiled_with_ambles(local_names, after_preamble: '')
|
|
69
|
+
preamble = <<END.tr("\n", ';')
|
|
70
|
+
begin
|
|
71
|
+
extend Haml::Helpers
|
|
72
|
+
_hamlout = @haml_buffer = Haml::Buffer.new(haml_buffer, #{Options.new(options).for_buffer.inspect})
|
|
73
|
+
_erbout = _hamlout.buffer
|
|
74
|
+
#{after_preamble}
|
|
75
|
+
END
|
|
76
|
+
postamble = <<END.tr("\n", ';')
|
|
77
|
+
#{precompiled_method_return_value}
|
|
78
|
+
ensure
|
|
79
|
+
@haml_buffer = @haml_buffer.upper if @haml_buffer
|
|
80
|
+
end
|
|
81
|
+
END
|
|
82
|
+
"#{preamble}#{locals_code(local_names)}#{precompiled}#{postamble}".dup
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def initialize_encoding(template, given_value)
|
|
88
|
+
if given_value
|
|
89
|
+
@encoding = given_value
|
|
90
|
+
else
|
|
91
|
+
@encoding = Encoding.default_internal || template.encoding
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Returns the string used as the return value of the precompiled method.
|
|
96
|
+
# This method exists so it can be monkeypatched to return modified values.
|
|
97
|
+
def precompiled_method_return_value
|
|
98
|
+
"_erbout"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def locals_code(names)
|
|
102
|
+
names = names.keys if Hash === names
|
|
103
|
+
|
|
104
|
+
names.map do |name|
|
|
105
|
+
# Can't use || because someone might explicitly pass in false with a symbol
|
|
106
|
+
sym_local = "_haml_locals[#{inspect_obj(name.to_sym)}]"
|
|
107
|
+
str_local = "_haml_locals[#{inspect_obj(name.to_s)}]"
|
|
108
|
+
"#{name} = #{sym_local}.nil? ? #{str_local} : #{sym_local};"
|
|
109
|
+
end.join
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def inspect_obj(obj)
|
|
113
|
+
case obj
|
|
114
|
+
when String
|
|
115
|
+
%Q!"#{obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]}}"!
|
|
116
|
+
when Symbol
|
|
117
|
+
":#{inspect_obj(obj.to_s)}"
|
|
118
|
+
else
|
|
119
|
+
obj.inspect
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Haml
|
|
4
|
+
# A module to count lines of expected code. This would be faster than actual code generation
|
|
5
|
+
# and counting newlines in it.
|
|
6
|
+
module TempleLineCounter
|
|
7
|
+
class UnexpectedExpression < StandardError; end
|
|
8
|
+
|
|
9
|
+
def self.count_lines(exp)
|
|
10
|
+
type, *args = exp
|
|
11
|
+
case type
|
|
12
|
+
when :multi
|
|
13
|
+
args.map { |a| count_lines(a) }.reduce(:+) || 0
|
|
14
|
+
when :dynamic, :code
|
|
15
|
+
args.first.count("\n")
|
|
16
|
+
when :static
|
|
17
|
+
0 # It has not real newline "\n" but escaped "\\n".
|
|
18
|
+
when :case
|
|
19
|
+
arg, *cases = args
|
|
20
|
+
arg.count("\n") + cases.map do |cond, e|
|
|
21
|
+
(cond == :else ? 0 : cond.count("\n")) + count_lines(e)
|
|
22
|
+
end.reduce(:+)
|
|
23
|
+
when :escape
|
|
24
|
+
count_lines(args[1])
|
|
25
|
+
else
|
|
26
|
+
raise UnexpectedExpression.new("[HAML BUG] Unexpected Temple expression '#{type}' is given!")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
data/lib/haml/util.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
begin
|
|
2
4
|
require 'erubis/tiny'
|
|
3
5
|
rescue LoadError
|
|
@@ -12,38 +14,6 @@ module Haml
|
|
|
12
14
|
module Util
|
|
13
15
|
extend self
|
|
14
16
|
|
|
15
|
-
# Computes the powerset of the given array.
|
|
16
|
-
# This is the set of all subsets of the array.
|
|
17
|
-
#
|
|
18
|
-
# @example
|
|
19
|
-
# powerset([1, 2, 3]) #=>
|
|
20
|
-
# Set[Set[], Set[1], Set[2], Set[3], Set[1, 2], Set[2, 3], Set[1, 3], Set[1, 2, 3]]
|
|
21
|
-
# @param arr [Enumerable]
|
|
22
|
-
# @return [Set<Set>] The subsets of `arr`
|
|
23
|
-
def powerset(arr)
|
|
24
|
-
arr.inject([Set.new].to_set) do |powerset, el|
|
|
25
|
-
new_powerset = Set.new
|
|
26
|
-
powerset.each do |subset|
|
|
27
|
-
new_powerset << subset
|
|
28
|
-
new_powerset << subset + [el]
|
|
29
|
-
end
|
|
30
|
-
new_powerset
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Returns information about the caller of the previous method.
|
|
35
|
-
#
|
|
36
|
-
# @param entry [String] An entry in the `#caller` list, or a similarly formatted string
|
|
37
|
-
# @return [[String, Fixnum, (String, nil)]] An array containing the filename, line, and method name of the caller.
|
|
38
|
-
# The method name may be nil
|
|
39
|
-
def caller_info(entry = caller[1])
|
|
40
|
-
info = entry.scan(/^(.*?):(-?.*?)(?::.*`(.+)')?$/).first
|
|
41
|
-
info[1] = info[1].to_i
|
|
42
|
-
# This is added by Rubinius to designate a block, but we don't care about it.
|
|
43
|
-
info[2].sub!(/ \{\}\Z/, '') if info[2]
|
|
44
|
-
info
|
|
45
|
-
end
|
|
46
|
-
|
|
47
17
|
# Silence all output to STDERR within a block.
|
|
48
18
|
#
|
|
49
19
|
# @yield A block in which no output will be printed to STDERR
|
|
@@ -54,19 +24,6 @@ module Haml
|
|
|
54
24
|
$stderr = the_real_stderr
|
|
55
25
|
end
|
|
56
26
|
|
|
57
|
-
# Returns an ActionView::Template* class.
|
|
58
|
-
# In pre-3.0 versions of Rails, most of these classes
|
|
59
|
-
# were of the form `ActionView::TemplateFoo`,
|
|
60
|
-
# while afterwards they were of the form `ActionView;:Template::Foo`.
|
|
61
|
-
#
|
|
62
|
-
# @param name [#to_s] The name of the class to get.
|
|
63
|
-
# For example, `:Error` will return `ActionView::TemplateError`
|
|
64
|
-
# or `ActionView::Template::Error`.
|
|
65
|
-
def av_template_class(name)
|
|
66
|
-
return ActionView.const_get("Template#{name}") if ActionView.const_defined?("Template#{name}")
|
|
67
|
-
return ActionView::Template.const_get(name.to_s)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
27
|
## Rails XSS Safety
|
|
71
28
|
|
|
72
29
|
# Whether or not ActionView's XSS protection is available and enabled,
|
|
@@ -82,6 +39,9 @@ module Haml
|
|
|
82
39
|
# With older versions of the Rails XSS-safety mechanism,
|
|
83
40
|
# this destructively modifies the HTML-safety of `text`.
|
|
84
41
|
#
|
|
42
|
+
# It only works if you are using ActiveSupport or the parameter `text`
|
|
43
|
+
# implements the #html_safe method.
|
|
44
|
+
#
|
|
85
45
|
# @param text [String, nil]
|
|
86
46
|
# @return [String, nil] `text`, marked as HTML-safe
|
|
87
47
|
def html_safe(text)
|
|
@@ -89,174 +49,89 @@ module Haml
|
|
|
89
49
|
text.html_safe
|
|
90
50
|
end
|
|
91
51
|
|
|
92
|
-
# Checks that the encoding of a string is valid
|
|
52
|
+
# Checks that the encoding of a string is valid
|
|
93
53
|
# and cleans up potential encoding gotchas like the UTF-8 BOM.
|
|
94
54
|
# If it's not, yields an error string describing the invalid character
|
|
95
|
-
# and the line on which it
|
|
55
|
+
# and the line on which it occurs.
|
|
96
56
|
#
|
|
97
57
|
# @param str [String] The string of which to check the encoding
|
|
98
58
|
# @yield [msg] A block in which an encoding error can be raised.
|
|
99
59
|
# Only yields if there is an encoding error
|
|
100
60
|
# @yieldparam msg [String] The error message to be raised
|
|
101
61
|
# @return [String] `str`, potentially with encoding gotchas like BOMs removed
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return str.gsub(Regexp.new("\\A\uFEFF".encode(str.encoding.name)), '')
|
|
113
|
-
else
|
|
114
|
-
return str
|
|
115
|
-
end
|
|
62
|
+
def check_encoding(str)
|
|
63
|
+
if str.valid_encoding?
|
|
64
|
+
# Get rid of the Unicode BOM if possible
|
|
65
|
+
# Shortcut for UTF-8 which might be the majority case
|
|
66
|
+
if str.encoding == Encoding::UTF_8
|
|
67
|
+
return str.gsub(/\A\uFEFF/, '')
|
|
68
|
+
elsif str.encoding.name =~ /^UTF-(16|32)(BE|LE)?$/
|
|
69
|
+
return str.gsub(Regexp.new("\\A\uFEFF".encode(str.encoding)), '')
|
|
70
|
+
else
|
|
71
|
+
return str
|
|
116
72
|
end
|
|
73
|
+
end
|
|
117
74
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
75
|
+
encoding = str.encoding
|
|
76
|
+
newlines = Regexp.new("\r\n|\r|\n".encode(encoding).force_encoding(Encoding::ASCII_8BIT))
|
|
77
|
+
str.force_encoding(Encoding::ASCII_8BIT).split(newlines).each_with_index do |line, i|
|
|
78
|
+
begin
|
|
79
|
+
line.encode(encoding)
|
|
80
|
+
rescue Encoding::UndefinedConversionError => e
|
|
81
|
+
yield <<MSG.rstrip, i + 1
|
|
125
82
|
Invalid #{encoding.name} character #{e.error_char.dump}
|
|
126
83
|
MSG
|
|
127
|
-
end
|
|
128
84
|
end
|
|
129
|
-
return str
|
|
130
85
|
end
|
|
86
|
+
return str
|
|
131
87
|
end
|
|
132
88
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
# at the beginning of the template and uses that encoding if it exists.
|
|
136
|
-
#
|
|
137
|
-
# The Haml encoding rules are simple.
|
|
138
|
-
# If a `-# coding:` comment exists,
|
|
139
|
-
# we assume that that's the original encoding of the document.
|
|
140
|
-
# Otherwise, we use whatever encoding Ruby has.
|
|
141
|
-
#
|
|
142
|
-
# Haml uses the same rules for parsing coding comments as Ruby.
|
|
143
|
-
# This means that it can understand Emacs-style comments
|
|
144
|
-
# (e.g. `-*- encoding: "utf-8" -*-`),
|
|
145
|
-
# and also that it cannot understand non-ASCII-compatible encodings
|
|
146
|
-
# such as `UTF-16` and `UTF-32`.
|
|
147
|
-
#
|
|
148
|
-
# @param str [String] The Haml template of which to check the encoding
|
|
149
|
-
# @yield [msg] A block in which an encoding error can be raised.
|
|
150
|
-
# Only yields if there is an encoding error
|
|
151
|
-
# @yieldparam msg [String] The error message to be raised
|
|
152
|
-
# @return [String] The original string encoded properly
|
|
153
|
-
# @raise [ArgumentError] if the document declares an unknown encoding
|
|
154
|
-
def check_haml_encoding(str, &block)
|
|
155
|
-
check_encoding(str, &block)
|
|
156
|
-
end
|
|
157
|
-
else
|
|
158
|
-
def check_haml_encoding(str, &block)
|
|
159
|
-
str = str.dup if str.frozen?
|
|
160
|
-
|
|
161
|
-
bom, encoding = parse_haml_magic_comment(str)
|
|
162
|
-
if encoding; str.force_encoding(encoding)
|
|
163
|
-
elsif bom; str.force_encoding("UTF-8")
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
return check_encoding(str, &block)
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
if RUBY_VERSION < "1.9.2"
|
|
171
|
-
def inspect_obj(obj)
|
|
172
|
-
return obj.inspect
|
|
173
|
-
end
|
|
174
|
-
else
|
|
175
|
-
# Like `Object#inspect`, but preserves non-ASCII characters rather than escaping them under Ruby 1.9.2.
|
|
176
|
-
# This is necessary so that the precompiled Haml template can be `#encode`d into `@options[:encoding]`
|
|
177
|
-
# before being evaluated.
|
|
178
|
-
#
|
|
179
|
-
# @param obj {Object}
|
|
180
|
-
# @return {String}
|
|
181
|
-
def inspect_obj(obj)
|
|
182
|
-
return ':' + inspect_obj(obj.to_s) if obj.is_a?(Symbol)
|
|
183
|
-
return obj.inspect unless obj.is_a?(String)
|
|
184
|
-
'"' + obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]} + '"'
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
## Static Method Stuff
|
|
189
|
-
|
|
190
|
-
# The context in which the ERB for \{#def\_static\_method} will be run.
|
|
191
|
-
class StaticConditionalContext
|
|
192
|
-
# @param set [#include?] The set of variables that are defined for this context.
|
|
193
|
-
def initialize(set)
|
|
194
|
-
@set = set
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
# Checks whether or not a variable is defined for this context.
|
|
198
|
-
#
|
|
199
|
-
# @param name [Symbol] The name of the variable
|
|
200
|
-
# @return [Boolean]
|
|
201
|
-
def method_missing(name, *args, &block)
|
|
202
|
-
super unless args.empty? && block.nil?
|
|
203
|
-
@set.include?(name)
|
|
204
|
-
end
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
# This is used for methods in {Haml::Buffer} that need to be very fast,
|
|
208
|
-
# and take a lot of boolean parameters
|
|
209
|
-
# that are known at compile-time.
|
|
210
|
-
# Instead of passing the parameters in normally,
|
|
211
|
-
# a separate method is defined for every possible combination of those parameters;
|
|
212
|
-
# these are then called using \{#static\_method\_name}.
|
|
89
|
+
# Like {\#check\_encoding}, but also checks for a Ruby-style `-# coding:` comment
|
|
90
|
+
# at the beginning of the template and uses that encoding if it exists.
|
|
213
91
|
#
|
|
214
|
-
#
|
|
215
|
-
#
|
|
216
|
-
#
|
|
217
|
-
#
|
|
218
|
-
#
|
|
219
|
-
# def_static_method(Foo, :my_static_method, [:foo, :bar], :baz, :bang, <<RUBY)
|
|
220
|
-
# <% if baz && bang %>
|
|
221
|
-
# return foo + bar
|
|
222
|
-
# <% elsif baz || bang %>
|
|
223
|
-
# return foo - bar
|
|
224
|
-
# <% else %>
|
|
225
|
-
# return 17
|
|
226
|
-
# <% end %>
|
|
227
|
-
# RUBY
|
|
92
|
+
# The Haml encoding rules are simple.
|
|
93
|
+
# If a `-# coding:` comment exists,
|
|
94
|
+
# we assume that that's the original encoding of the document.
|
|
95
|
+
# Otherwise, we use whatever encoding Ruby has.
|
|
228
96
|
#
|
|
229
|
-
#
|
|
97
|
+
# Haml uses the same rules for parsing coding comments as Ruby.
|
|
98
|
+
# This means that it can understand Emacs-style comments
|
|
99
|
+
# (e.g. `-*- encoding: "utf-8" -*-`),
|
|
100
|
+
# and also that it cannot understand non-ASCII-compatible encodings
|
|
101
|
+
# such as `UTF-16` and `UTF-32`.
|
|
230
102
|
#
|
|
231
|
-
# @
|
|
232
|
-
# @
|
|
233
|
-
#
|
|
234
|
-
# @
|
|
235
|
-
#
|
|
236
|
-
# @
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
erb = vars.pop
|
|
240
|
-
info = caller_info
|
|
241
|
-
powerset(vars).each do |set|
|
|
242
|
-
context = StaticConditionalContext.new(set).instance_eval {binding}
|
|
243
|
-
method_content = (defined?(Erubis::TinyEruby) && Erubis::TinyEruby || ERB).new(erb).result(context)
|
|
103
|
+
# @param str [String] The Haml template of which to check the encoding
|
|
104
|
+
# @yield [msg] A block in which an encoding error can be raised.
|
|
105
|
+
# Only yields if there is an encoding error
|
|
106
|
+
# @yieldparam msg [String] The error message to be raised
|
|
107
|
+
# @return [String] The original string encoded properly
|
|
108
|
+
# @raise [ArgumentError] if the document declares an unknown encoding
|
|
109
|
+
def check_haml_encoding(str, &block)
|
|
110
|
+
str = str.dup if str.frozen?
|
|
244
111
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
end
|
|
249
|
-
METHOD
|
|
112
|
+
bom, encoding = parse_haml_magic_comment(str)
|
|
113
|
+
if encoding; str.force_encoding(encoding)
|
|
114
|
+
elsif bom; str.force_encoding(Encoding::UTF_8)
|
|
250
115
|
end
|
|
116
|
+
|
|
117
|
+
return check_encoding(str, &block)
|
|
251
118
|
end
|
|
252
119
|
|
|
253
|
-
#
|
|
120
|
+
# Like `Object#inspect`, but preserves non-ASCII characters rather than escaping them.
|
|
121
|
+
# This is necessary so that the precompiled Haml template can be `#encode`d into `@options[:encoding]`
|
|
122
|
+
# before being evaluated.
|
|
254
123
|
#
|
|
255
|
-
# @param
|
|
256
|
-
# @
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
124
|
+
# @param obj {Object}
|
|
125
|
+
# @return {String}
|
|
126
|
+
def inspect_obj(obj)
|
|
127
|
+
case obj
|
|
128
|
+
when String
|
|
129
|
+
%Q!"#{obj.gsub(/[\x00-\x7F]+/) {|s| s.dump[1...-1]}}"!
|
|
130
|
+
when Symbol
|
|
131
|
+
":#{inspect_obj(obj.to_s)}"
|
|
132
|
+
else
|
|
133
|
+
obj.inspect
|
|
134
|
+
end
|
|
260
135
|
end
|
|
261
136
|
|
|
262
137
|
# Scans through a string looking for the interoplation-opening `#{`
|
|
@@ -271,7 +146,7 @@ METHOD
|
|
|
271
146
|
# @return [String] The text remaining in the scanner after all `#{`s have been processed
|
|
272
147
|
def handle_interpolation(str)
|
|
273
148
|
scan = StringScanner.new(str)
|
|
274
|
-
yield scan while scan.scan(/(.*?)(\\*)
|
|
149
|
+
yield scan while scan.scan(/(.*?)(\\*)#([\{@$])/)
|
|
275
150
|
scan.rest
|
|
276
151
|
end
|
|
277
152
|
|
|
@@ -283,17 +158,15 @@ METHOD
|
|
|
283
158
|
# from to
|
|
284
159
|
#
|
|
285
160
|
# @param scanner [StringScanner] The string scanner to move
|
|
286
|
-
# @param start [
|
|
287
|
-
#
|
|
288
|
-
# @param finish [Character] The character closing the balanced pair.
|
|
289
|
-
# A `Fixnum` in 1.8, a `String` in 1.9
|
|
161
|
+
# @param start [String] The character opening the balanced pair.
|
|
162
|
+
# @param finish [String] The character closing the balanced pair.
|
|
290
163
|
# @param count [Fixnum] The number of opening characters matched
|
|
291
164
|
# before calling this method
|
|
292
165
|
# @return [(String, String)] The string matched within the balanced pair
|
|
293
166
|
# and the rest of the string.
|
|
294
167
|
# `["Foo (Bar (Baz bang) bop)", " (Bang (bop bip))"]` in the example above.
|
|
295
168
|
def balance(scanner, start, finish, count = 0)
|
|
296
|
-
str = ''
|
|
169
|
+
str = ''.dup
|
|
297
170
|
scanner = StringScanner.new(scanner) unless scanner.is_a? StringScanner
|
|
298
171
|
regexp = Regexp.new("(.*?)[\\#{start.chr}\\#{finish.chr}]", Regexp::MULTILINE)
|
|
299
172
|
while scanner.scan(regexp)
|
|
@@ -322,20 +195,28 @@ METHOD
|
|
|
322
195
|
end
|
|
323
196
|
|
|
324
197
|
def contains_interpolation?(str)
|
|
325
|
-
str
|
|
198
|
+
/#[\{$@]/ === str
|
|
326
199
|
end
|
|
327
200
|
|
|
328
201
|
def unescape_interpolation(str, escape_html = nil)
|
|
329
|
-
res = ''
|
|
202
|
+
res = ''.dup
|
|
330
203
|
rest = Haml::Util.handle_interpolation str.dump do |scan|
|
|
331
204
|
escapes = (scan[2].size - 1) / 2
|
|
205
|
+
char = scan[3] # '{', '@' or '$'
|
|
332
206
|
res << scan.matched[0...-3 - escapes]
|
|
333
207
|
if escapes % 2 == 1
|
|
334
|
-
res <<
|
|
208
|
+
res << "\##{char}"
|
|
335
209
|
else
|
|
336
|
-
|
|
210
|
+
interpolated = if char == '{'
|
|
211
|
+
balance(scan, ?{, ?}, 1)[0][0...-1]
|
|
212
|
+
else
|
|
213
|
+
scan.scan(/\w+/)
|
|
214
|
+
end
|
|
215
|
+
content = eval("\"#{interpolated}\"")
|
|
216
|
+
content.prepend(char) if char == '@' || char == '$'
|
|
337
217
|
content = "Haml::Helpers.html_escape((#{content}))" if escape_html
|
|
338
|
-
|
|
218
|
+
|
|
219
|
+
res << "\#{#{content}}"
|
|
339
220
|
end
|
|
340
221
|
end
|
|
341
222
|
res + rest
|
|
@@ -350,10 +231,10 @@ METHOD
|
|
|
350
231
|
# Whether the document begins with a UTF-8 BOM,
|
|
351
232
|
# and the declared encoding of the document (or nil if none is declared)
|
|
352
233
|
def parse_haml_magic_comment(str)
|
|
353
|
-
scanner = StringScanner.new(str.dup.force_encoding(
|
|
234
|
+
scanner = StringScanner.new(str.dup.force_encoding(Encoding::ASCII_8BIT))
|
|
354
235
|
bom = scanner.scan(/\xEF\xBB\xBF/n)
|
|
355
236
|
return bom unless scanner.scan(/-\s*#\s*/n)
|
|
356
|
-
if coding = try_parse_haml_emacs_magic_comment(scanner)
|
|
237
|
+
if (coding = try_parse_haml_emacs_magic_comment(scanner))
|
|
357
238
|
return bom, coding
|
|
358
239
|
end
|
|
359
240
|
|