1
0
Fork 0
mirror of https://github.com/haml/haml.git synced 2022-11-09 12:33:31 -05:00

Optimize common attributes

This commit is contained in:
Takashi Kokubun 2017-02-09 19:11:52 +09:00
parent 4d5fcbffe6
commit 9ce9b0db90
6 changed files with 117 additions and 2 deletions

View file

@ -33,6 +33,13 @@ module Haml
"_hamlout.attributes(#{Haml::Util.inspect_obj(attributes)}, #{object_ref},#{attributes_hashes.join(', ')})"
end
# @param options [Haml::Options]
def initialize(options)
@is_html = [:html4, :html5].include?(options[:format])
@attr_wrapper = options[:attr_wrapper]
@escape_attrs = options[:escape_attrs]
end
# Returns Temple expression to render attributes.
#
# @param attributes [Hash]
@ -123,7 +130,47 @@ module Haml
# @param values [Array<AttributeValue>]
# @return [Array] Temple expression
def compile_attribute(key, values)
runtime_build(values)
case key
when 'id', 'class'
runtime_build(values)
else
compile_common_attribute(key, values)
end
end
# Compiles attribute for keys except "id" and "class".
#
# @param key [String] Not "id" or "class"
# @param values [Array<AttributeValue>]
# @return [Array] Temple expression
def compile_common_attribute(key, values)
var = unique_name
[:multi,
[:code, "#{var} = (#{merged_value(key, values)})"],
[:case, var,
['Hash', runtime_build([AttributeValue.new(:dynamic, key, var)])],
['true', true_value(key)],
['false, nil', [:multi]],
[:else, [:multi,
[:static, " #{key}=#{@attr_wrapper}"],
[:escape, @escape_attrs, [:dynamic, var]],
[:static, @attr_wrapper]],
]
],
]
end
def true_value(key)
if @is_html
[:static, " #{key}"]
else
[:static, " #{key}=#{@attr_wrapper}#{key}#{@attr_wrapper}"]
end
end
def unique_name
@unique_name ||= 0
"_haml_attribute_compiler#{@unique_name += 1}"
end
end
end

View file

@ -14,7 +14,7 @@ module Haml
@to_merge = []
@temple = [:multi]
@node = nil
@attribute_compiler = AttributeCompiler.new
@attribute_compiler = AttributeCompiler.new(options)
end
def call(node)

48
lib/haml/escapable.rb Normal file
View file

@ -0,0 +1,48 @@
module Haml
# Like Temple::Filters::Escapable, but with support for escaping by
# Haml::Herlpers.html_escape and Haml::Herlpers.escape_once.
class Escapable < Temple::Filter
def initialize(*)
super
@escape_code = "::Haml::Helpers.html_escape((%s))"
@escaper = eval("proc {|v| #{@escape_code % 'v'} }")
@once_escape_code = "::Haml::Helpers.escape_once((%s))"
@once_escaper = eval("proc {|v| #{@once_escape_code % 'v'} }")
@escape = false
end
def on_escape(flag, exp)
old = @escape
@escape = flag
compile(exp)
ensure
@escape = old
end
# The same as Haml::AttributeBuilder.build_attributes
def on_static(value)
[:static,
if @escape == :once
@once_escaper[value]
elsif @escape
@escaper[value]
else
value
end
]
end
# The same as Haml::AttributeBuilder.build_attributes
def on_dynamic(value)
[:dynamic,
if @escape == :once
@once_escape_code % value
elsif @escape
@escape_code % value
else
"(#{value}).to_s"
end
]
end
end
end

View file

@ -1,4 +1,5 @@
require 'temple'
require 'haml/escapable'
require 'haml/generator'
module Haml
@ -28,6 +29,8 @@ module Haml
use :Parser, -> { options[:parser_class] }
use :Compiler, -> { options[:compiler_class] }
use Escapable
filter :ControlFlow
filter :MultiFlattener
filter :StaticMerger
use Generator

View file

@ -13,6 +13,13 @@ module Haml
args.first.count("\n")
when :static
0 # It has not real newline "\n" but escaped "\\n".
when :case
arg, *cases = args
arg.count("\n") + cases.map do |cond, e|
(cond == :else ? 0 : cond.count("\n")) + count_lines(e)
end.reduce(:+)
when :escape
count_lines(args[1])
else
raise UnexpectedExpression.new("[HAML BUG] Unexpected Temple expression '#{type}' is given!")
end

View file

@ -12,6 +12,16 @@ class TempleLineCounterTest < Haml::TestCase
[:static, "foo\nbar\nbaz"],
[:dynamic, "foo\nbar\nbaz"],
],
[:case,
["'a\nb', false", [:static, "hello\n"]],
[:else, [:code, "raise 'error\n'"]],
],
[:escape, true, [:dynamic, "foo\nbar"]],
[:escape, :once, [:dynamic, "foo\nbar"]],
[:escape, false, [:dynamic, "foo\nbar"]],
[:escape, true, [:static, "foo\nbar"]],
[:escape, :once, [:static, "foo\nbar"]],
[:escape, false, [:dynamic, "foo\nbar"]],
]
def test_count_lines