Add a way to make "static" methods for faster Haml::Buffer dispatch.

Rather than passing in a bunch of boolean variables at runtime,
we compile the method once for each possible combination of variable values.
It makes the code twisty and nasty, but Buffer needs all the speed it can get.
This commit is contained in:
Nathan Weizenbaum 2008-12-27 22:34:52 -08:00
parent 9b7dfe28af
commit 4f5a9b2861
3 changed files with 57 additions and 15 deletions

View File

@ -83,20 +83,21 @@ module Haml
@real_tabs = 0
end
# Renders +text+ with the proper tabulation. This also deals with
# making a possible one-line tag one line or not.
def push_text(text, dont_tab_up = false, tab_change = 0)
if @tabulation > 0 && !@options[:ugly]
# Have to push every line in by the extra user set tabulation.
# Don't push lines with just whitespace, though,
# because that screws up precompiled indentation.
text.gsub!(/^(?!\s+$)/m, tabs)
text.sub!(tabs, '') if dont_tab_up
end
Haml::Util.def_static_method(self, :push_text, [:text, :tab_change],
:dont_tab_up, :ugly, <<RUBY)
<% if !ugly %>
if @tabulation > 0
# Have to push every line in by the extra user set tabulation.
# Don't push lines with just whitespace, though,
# because that screws up precompiled indentation.
text.gsub!(/^(?!\s+$)/m, tabs)
<% if dont_tab_up %> text.sub!(tabs, '') <% end %>
end
<% end %>
@buffer << text
@real_tabs += tab_change
end
RUBY
def adjust_tabs(tab_change)
@real_tabs += tab_change

View File

@ -3,6 +3,8 @@ require 'haml/shared'
module Haml
module Precompiler
include Haml::Util
# Designates an XHTML/XML element.
ELEMENT = ?%
@ -281,10 +283,8 @@ END
end
end
@precompiled << "_hamlout.push_text(#{unescape_interpolation(text)}"
@precompiled << ", #{@dont_tab_up_next_text.inspect}" if @dont_tab_up_next_text || tab_change != 0
@precompiled << ", #{tab_change}" if tab_change != 0
@precompiled << ");"
@precompiled << "_hamlout.#{static_method_name :push_text, @dont_tab_up_next_text, @options[:ugly]}"
@precompiled << "(#{unescape_interpolation(text)}, #{tab_change});"
@to_merge = []
@dont_tab_up_next_text = false
end

View File

@ -1,3 +1,6 @@
require 'erb'
require 'set'
module Haml
module Util
class << self; include Haml::Util; end
@ -17,5 +20,43 @@ module Haml
def map_hash(hash, &block)
to_hash(hash.map(&block))
end
def powerset(arr)
arr.inject([Set.new].to_set) do |powerset, el|
new_powerset = Set.new
powerset.each do |subset|
new_powerset << subset
new_powerset << subset + [el]
end
new_powerset
end
end
class StaticConditionalContext
def initialize(set)
@set = set
end
def method_missing(name, *args, &block)
super unless args.empty? && block.nil?
@set.include?(name)
end
end
def def_static_method(klass, name, args, *vars)
erb = vars.pop
powerset(vars).each do |set|
context = StaticConditionalContext.new(set).instance_eval {binding}
klass.class_eval(<<METHOD)
def #{static_method_name(name, *vars.map {|v| set.include?(v)})}(#{args.join(', ')})
#{ERB.new(erb).result(context)}
end
METHOD
end
end
def static_method_name(name, *vars)
"#{name}_#{vars.map {|v| !!v}.join('_')}"
end
end
end