class Haml::AttributeCompiler

Public Class Methods

new(options) click to toggle source

@param options [Haml::Options]

# File lib/haml/attribute_compiler.rb, line 37
def initialize(options)
  @is_html = [:html4, :html5].include?(options[:format])
  @attr_wrapper = options[:attr_wrapper]
  @escape_attrs = options[:escape_attrs]
  @hyphenate_data_attrs = options[:hyphenate_data_attrs]
end
runtime_build(attributes, object_ref, dynamic_attributes) click to toggle source

Returns a script to render attributes on runtime.

@param attributes [Hash] @param object_ref [String,:nil] @param dynamic_attributes [DynamicAttributes] @return [String] Attributes rendering code

# File lib/haml/attribute_compiler.rb, line 32
def self.runtime_build(attributes, object_ref, dynamic_attributes)
  "_hamlout.attributes(#{Haml::Util.inspect_obj(attributes)}, #{object_ref},#{dynamic_attributes.to_literal})"
end

Public Instance Methods

compile(attributes, object_ref, dynamic_attributes) click to toggle source

Returns Temple expression to render attributes.

@param attributes [Hash] @param object_ref [String,:nil] @param dynamic_attributes [DynamicAttributes] @return [Array] Temple expression

# File lib/haml/attribute_compiler.rb, line 50
def compile(attributes, object_ref, dynamic_attributes)
  if object_ref != :nil || !AttributeParser.available?
    return [:dynamic, AttributeCompiler.runtime_build(attributes, object_ref, dynamic_attributes)]
  end

  parsed_hashes = [dynamic_attributes.new, dynamic_attributes.old].compact.map do |attribute_hash|
    unless (hash = AttributeParser.parse(attribute_hash))
      return [:dynamic, AttributeCompiler.runtime_build(attributes, object_ref, dynamic_attributes)]
    end
    hash
  end
  attribute_values = build_attribute_values(attributes, parsed_hashes)
  AttributeBuilder.verify_attribute_names!(attribute_values.map(&:key))

  values_by_base_key = attribute_values.group_by(&:base_key)
  [:multi, *values_by_base_key.keys.sort.map { |base_key|
    compile_attribute_values(values_by_base_key[base_key])
  }]
end

Private Instance Methods

build_attribute_values(attributes, parsed_hashes) click to toggle source

Returns array of AttributeValue instances from static attributes and dynamic_attributes. For each key, the values' order in returned value is preserved in the same order as Haml::Buffer#attributes's merge order.

@param attributes [{ String => String }] @param parsed_hashes [{ String => String }] @return [Array<AttributeValue>]

# File lib/haml/attribute_compiler.rb, line 78
def build_attribute_values(attributes, parsed_hashes)
  [].tap do |attribute_values|
    attributes.each do |key, static_value|
      attribute_values << AttributeValue.new(:static, key, static_value)
    end
    parsed_hashes.each do |parsed_hash|
      parsed_hash.each do |key, dynamic_value|
        attribute_values << AttributeValue.new(:dynamic, key, dynamic_value)
      end
    end
  end
end
compile_attribute(key, values) click to toggle source

Compiles attribute values for one key to Temple expression that generates ` key='value'`.

@param key [String] @param values [Array<AttributeValue>] @return [Array] Temple expression

# File lib/haml/attribute_compiler.rb, line 149
def compile_attribute(key, values)
  if values.all? { |v| Temple::StaticAnalyzer.static?(v.to_literal) }
    return static_build(values)
  end

  case key
  when 'id', 'class'
    compile_id_or_class_attribute(key, values)
  else
    compile_common_attribute(key, values)
  end
end
compile_attribute_values(values) click to toggle source

Compiles attribute values with the same base_key to Temple expression.

@param values [Array<AttributeValue>] `base_key`'s results are the same. `key`'s result may differ. @return [Array] Temple expression

# File lib/haml/attribute_compiler.rb, line 95
def compile_attribute_values(values)
  if values.map(&:key).uniq.size == 1
    compile_attribute(values.first.key, values)
  else
    runtime_build(values)
  end
end
compile_common_attribute(key, values) click to toggle source

@param key [String] Not “id” or “class” @param values [Array<AttributeValue>] @return [Array] Temple expression

# File lib/haml/attribute_compiler.rb, line 184
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
compile_id_or_class_attribute(id_or_class, values) click to toggle source

@param id_or_class [String] “id” or “class” @param values [Array<AttributeValue>] @return [Array] Temple expression

# File lib/haml/attribute_compiler.rb, line 165
def compile_id_or_class_attribute(id_or_class, values)
  var = unique_name
  [:multi,
   [:code, "#{var} = (#{merged_value(id_or_class, values)})"],
   [:case, var,
    ['Hash, Array', runtime_build([AttributeValue.new(:dynamic, id_or_class, var)])],
    ['false, nil', [:multi]],
    [:else, [:multi,
             [:static, " #{id_or_class}=#{@attr_wrapper}"],
             [:escape, @escape_attrs, [:dynamic, var]],
             [:static, @attr_wrapper]],
    ]
   ],
  ]
end
frozen_string(str) click to toggle source

@param str [String] @return [String]

# File lib/haml/attribute_compiler.rb, line 140
def frozen_string(str)
  "#{Haml::Util.inspect_obj(str)}.freeze"
end
merged_value(key, values) click to toggle source

@param key [String] @param values [Array<AttributeValue>] @return [String]

# File lib/haml/attribute_compiler.rb, line 130
def merged_value(key, values)
  if values.size == 1
    values.first.to_literal
  else
    "::Haml::AttributeBuilder.merge_values(#{frozen_string(key)}, #{values.map(&:to_literal).join(', ')})"
  end
end
runtime_build(values) click to toggle source

@param values [Array<AttributeValue>] @return [Array] Temple expression

# File lib/haml/attribute_compiler.rb, line 105
def runtime_build(values)
  hash_content = values.group_by(&:key).map do |key, values_for_key|
    "#{frozen_string(key)} => #{merged_value(key, values_for_key)}"
  end.join(', ')
  [:dynamic, "_hamlout.attributes({ #{hash_content} }, nil)"]
end
static_build(values) click to toggle source

Renders attribute values statically.

@param values [Array<AttributeValue>] @return [Array] Temple expression

# File lib/haml/attribute_compiler.rb, line 116
def static_build(values)
  hash_content = values.group_by(&:key).map do |key, values_for_key|
    "#{frozen_string(key)} => #{merged_value(key, values_for_key)}"
  end.join(', ')

  arguments = [@is_html, @attr_wrapper, @escape_attrs, @hyphenate_data_attrs]
  code = "::Haml::AttributeBuilder.build_attributes"         "(#{arguments.map { |a| Haml::Util.inspect_obj(a) }.join(', ')}, { #{hash_content} })"
  [:static, eval(code).to_s]
end
true_value(key) click to toggle source
# File lib/haml/attribute_compiler.rb, line 201
def true_value(key)
  if @is_html
    [:static, " #{key}"]
  else
    [:static, " #{key}=#{@attr_wrapper}#{key}#{@attr_wrapper}"]
  end
end
unique_name() click to toggle source
# File lib/haml/attribute_compiler.rb, line 209
def unique_name
  @unique_name ||= 0
  "_haml_attribute_compiler#{@unique_name += 1}"
end