class Haml::AttributeCompiler

Constants

AttributeValue

@param type [Symbol] :static or :dynamic @param key [String] @param value [String] Actual string value for :static type, value’s Ruby literal for :dynamic type.

Public Class Methods

new(options) click to toggle source

@param options [Haml::Options]

# File lib/haml/attribute_compiler.rb, line 13
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

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 [Haml::Parser::DynamicAttributes] @return [Array] Temple expression

# File lib/haml/attribute_compiler.rb, line 26
def compile(attributes, object_ref, dynamic_attributes)
  if object_ref != :nil || !AttributeParser.available?
    return [:dynamic, compile_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, compile_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))

  [:multi, *group_values_for_sort(attribute_values).map { |value_group|
    compile_attribute_values(value_group)
  }]
end

Private Instance Methods

attr_literal(attr) click to toggle source

@param [Haml::AttributeCompiler::AttributeValue] attr

# File lib/haml/attribute_compiler.rb, line 217
def attr_literal(attr)
  case attr.type
  when :static
    to_literal(attr.value)
  when :dynamic
    attr.value
  end
end
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 79
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 151
def compile_attribute(key, values)
  if values.all? { |v| Temple::StaticAnalyzer.static?(attr_literal(v)) }
    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 similar key to Temple expression.

@param values [Array<AttributeValue>] whose ‘key`s are partially or fully the same from left. @return [Array] Temple expression

# File lib/haml/attribute_compiler.rb, line 96
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 186
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, Escapable::EscapeSafeBuffer.new(@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 167
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, Escapable::EscapeSafeBuffer.new(@escape_attrs), [:dynamic, var]],
             [:static, @attr_wrapper]],
    ]
   ],
  ]
end
compile_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 [Haml::Parser::DynamicAttributes] @return [String] Attributes rendering code

# File lib/haml/attribute_compiler.rb, line 53
def compile_runtime_build(attributes, object_ref, dynamic_attributes)
  arguments = [@is_html, @attr_wrapper, @escape_attrs, @hyphenate_data_attrs].map(&method(:to_literal)).join(', ')
  "::Haml::AttributeBuilder.build(#{to_literal(attributes)}, #{object_ref}, #{arguments}, #{dynamic_attributes.to_literal})"
end
frozen_string(str) click to toggle source

@param str [String] @return [String]

# File lib/haml/attribute_compiler.rb, line 142
def frozen_string(str)
  "#{to_literal(str)}.freeze"
end
group_values_for_sort(values) click to toggle source

Build array of grouped values whose sort order may go back and forth, which is also sorted with key name. This method needs to group values with the same start because it can be changed in ‘Haml::AttributeBuidler#build_data_keys`. @param values [Array<Haml::AttributeCompiler::AttributeValue>] @return [Array<Array<Haml::AttributeCompiler::AttributeValue>>]

# File lib/haml/attribute_compiler.rb, line 62
def group_values_for_sort(values)
  sorted_values = values.sort_by(&:key)
  [].tap do |value_groups|
    until sorted_values.empty?
      key = sorted_values.first.key
      value_group, sorted_values = sorted_values.partition { |v| v.key.start_with?(key) }
      value_groups << value_group
    end
  end
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 132
def merged_value(key, values)
  if values.size == 1
    attr_literal(values.first)
  else
    "::Haml::AttributeBuilder.merge_values(#{frozen_string(key)}, #{values.map(&method(:attr_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 106
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(', ')
  arguments = [@is_html, @attr_wrapper, @escape_attrs, @hyphenate_data_attrs].map(&method(:to_literal)).join(', ')
  [:dynamic, "::Haml::AttributeBuilder.build({ #{hash_content} }, nil, #{arguments})"]
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 118
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(&method(:to_literal)).join(', ')}, { #{hash_content} })"
  [:static, eval(code).to_s]
end
to_literal(value) click to toggle source

For haml/haml#972 @param [Object] value

# File lib/haml/attribute_compiler.rb, line 228
def to_literal(value)
  case value
  when true, false
    value.to_s
  else
    Haml::Util.inspect_obj(value)
  end
end
true_value(key) click to toggle source
# File lib/haml/attribute_compiler.rb, line 203
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 211
def unique_name
  @unique_name ||= 0
  "_haml_attribute_compiler#{@unique_name += 1}"
end