lib/faml/compiler.rb in faml-0.5.0 vs lib/faml/compiler.rb in faml-0.5.1

- old
+ new

@@ -1,22 +1,17 @@ require 'ripper' require 'temple' require 'haml_parser/ast' +require_relative 'attribute_compiler' require_relative 'error' require_relative 'filter_compilers' require_relative 'helpers' -require_relative 'object_ref' require_relative 'rails_helpers' -require_relative 'ruby_syntax_checker' -require_relative 'static_hash_parser' require_relative 'text_compiler' module Faml class Compiler < Temple::Parser - class UnparsableRubyCode < Error - end - DEFAULT_AUTO_CLOSE_TAGS = %w[ area base basefont br col command embed frame hr img input isindex keygen link menuitem meta param source track wbr ] DEFAULT_PRESERVE_TAGS = %w[pre textarea code] @@ -180,11 +175,11 @@ def compile_element(ast) temple = [ :haml, :tag, ast.tag_name, self_closing?(ast), - compile_attributes(ast.attributes, ast.static_id, ast.static_class, ast.object_ref), + AttributeCompiler.new.compile(ast), ] if ast.oneline_child temple << compile(ast.oneline_child) elsif !ast.children.empty? @@ -220,171 +215,9 @@ children end def nuke_inner_whitespace?(ast) ast.nuke_inner_whitespace || options[:preserve].include?(ast.tag_name) - end - - def compile_attributes(text, static_id, static_class, object_ref) - if !object_ref && text.empty? - return compile_static_id_and_class(static_id, static_class) - end - - unless object_ref - attrs = try_optimize_attributes(text, static_id, static_class) - if attrs - line_count = text.count("\n") - return [:multi, [:html, :attrs, *attrs]].concat([[:newline]] * line_count) - end - end - - # Slow version - - h = {} - unless static_class.empty? - h[:class] = static_class.split(/ +/) - end - unless static_id.empty? - h[:id] = static_id - end - - codes = [] - unless h.empty? - codes << h.inspect - end - if object_ref - codes << "::Faml::ObjectRef.render(#{object_ref})" - end - unless text.empty? - codes << text - end - [:haml, :attrs, codes.join(', ')] - end - - def compile_static_id_and_class(static_id, static_class) - [:html, :attrs].tap do |html_attrs| - unless static_class.empty? - html_attrs << [:haml, :attr, 'class', [:static, static_class]] - end - unless static_id.empty? - html_attrs << [:haml, :attr, 'id', [:static, static_id]] - end - end - end - - def try_optimize_attributes(text, static_id, static_class) - parser = StaticHashParser.new - unless parser.parse("{#{text}}") - assert_valid_ruby_code!(text) - return nil - end - - static_attributes, dynamic_attributes = build_optimized_attributes(parser, static_id, static_class) - if static_attributes.nil? - return nil - end - - if dynamic_attributes.key?('data') - # XXX: Quit optimization... - return nil - end - - if text.include?("\n") && !dynamic_attributes.empty? - # XXX: Quit optimization to keep newlines - # https://github.com/eagletmt/faml/issues/18 - return nil - end - - (static_attributes.keys + dynamic_attributes.keys).sort.flat_map do |k| - if static_attributes.key?(k) - compile_static_attribute(k, static_attributes[k]) - else - compile_dynamic_attribute(k, dynamic_attributes[k]) - end - end - end - - def assert_valid_ruby_code!(text) - RubySyntaxChecker.new("call(#{text})", '(faml)').parse - true - rescue RubySyntaxChecker::Error - raise UnparsableRubyCode.new("Unparsable Ruby code is given to attributes: #{text}", nil) - end - - def build_optimized_attributes(parser, static_id, static_class) - static_attributes = build_optimized_static_attributes(parser, static_id, static_class) - dynamic_attributes = build_optimized_dynamic_attributes(parser, static_attributes) - if dynamic_attributes - [static_attributes, dynamic_attributes] - else - [nil, nil] - end - end - - def build_optimized_static_attributes(parser, static_id, static_class) - static_attributes = {} - parser.static_attributes.each do |k, v| - static_attributes[k.to_s] = v - end - - class_list = Array(static_attributes.delete('class')).select { |v| v }.flat_map { |c| c.to_s.split(/ +/) } - unless static_class.empty? - class_list.concat(static_class.split(/ +/)) - end - unless class_list.empty? - static_attributes['class'] = class_list.uniq.sort.join(' ') - end - - id_list = Array(static_attributes.delete('id')).select { |v| v } - unless static_id.empty? - id_list = [static_id].concat(id_list) - end - unless id_list.empty? - static_attributes['id'] = id_list.join('_') - end - - static_attributes - end - - def build_optimized_dynamic_attributes(parser, static_attributes) - dynamic_attributes = {} - parser.dynamic_attributes.each do |k, v| - k = k.to_s - if static_attributes.key?(k) - if StaticHashParser::SPECIAL_ATTRIBUTES.include?(k) - # XXX: Quit optimization - return nil - end - end - dynamic_attributes[k] = v - end - dynamic_attributes - end - - def compile_static_attribute(key, value) - if value.is_a?(Hash) && key == 'data' - data = AttributeBuilder.normalize_data(value) - data.keys.sort.map do |k| - compile_static_simple_attribute("data-#{k}", data[k]) - end - else - [compile_static_simple_attribute(key, value)] - end - end - - def compile_static_simple_attribute(key, value) - case - when value == true - [:haml, :attr, key, [:multi]] - when value == false || value.nil? - [:multi] - else - [:haml, :attr, key, [:static, Temple::Utils.escape_html(value)]] - end - end - - def compile_dynamic_attribute(key, value) - [[:haml, :attr, key, [:dvalue, value]]] end def compile_script(ast) sym = unique_name temple = [:multi]