module Haml::AttributeBuilder
Constants
- INVALID_ATTRIBUTE_NAME_REGEX
Public Class Methods
# File lib/haml/attribute_builder.rb, line 9 def build(class_id, obj_ref, is_html, attr_wrapper, escape_attrs, hyphenate_data_attrs, *attributes_hashes) attributes = class_id attributes_hashes.each do |old| result = {} old.each { |k, v| result[k.to_s] = v } merge_attributes!(attributes, result) end merge_attributes!(attributes, parse_object_ref(obj_ref)) if obj_ref build_attributes(is_html, attr_wrapper, escape_attrs, hyphenate_data_attrs, attributes) end
# File lib/haml/attribute_builder.rb, line 20 def build_attributes(is_html, attr_wrapper, escape_attrs, hyphenate_data_attrs, attributes = {}) # @TODO this is an absolutely ridiculous amount of arguments. At least # some of this needs to be moved into an instance method. join_char = hyphenate_data_attrs ? '-' : '_' attributes.each do |key, value| if value.is_a?(Hash) data_attributes = attributes.delete(key) data_attributes = flatten_data_attributes(data_attributes, '', join_char) data_attributes = build_data_keys(data_attributes, hyphenate_data_attrs, key) verify_attribute_names!(data_attributes.keys) attributes = data_attributes.merge(attributes) end end result = attributes.collect do |attr, value| next if value.nil? value = filter_and_join(value, ' ') if attr == 'class' value = filter_and_join(value, '_') if attr == 'id' if value == true next " #{attr}" if is_html next " #{attr}=#{attr_wrapper}#{attr}#{attr_wrapper}" elsif value == false next end value = if escape_attrs == :once Haml::Helpers.escape_once_without_haml_xss(value.to_s) elsif escape_attrs Haml::Helpers.html_escape_without_haml_xss(value.to_s) else value.to_s end " #{attr}=#{attr_wrapper}#{value}#{attr_wrapper}" end result.compact! result.sort! result.join end
@return [String, nil]
# File lib/haml/attribute_builder.rb, line 64 def filter_and_join(value, separator) return '' if (value.respond_to?(:empty?) && value.empty?) if value.is_a?(Array) value = value.flatten value.map! {|item| item ? item.to_s : nil} value.compact! value = value.join(separator) else value = value ? value.to_s : nil end !value.nil? && !value.empty? && value end
Merges two attribute hashes. This is the same as ‘to.merge!(from)`, except that it merges id, class, and data attributes.
ids are concatenated with ‘“_”`, and classes are concatenated with `“ ”`. data hashes are simply merged.
Destructively modifies ‘to`.
@param to [{String => String,Hash}] The attribute hash to merge into @param from [{String => Object}] The attribute hash to merge from @return [{String => String,Hash}] ‘to`, after being merged
# File lib/haml/attribute_builder.rb, line 91 def merge_attributes!(to, from) from.keys.each do |key| to[key] = merge_value(key, to[key], from[key]) end to end
Merge multiple values to one attribute value. No destructive operation.
@param key [String] @param values [Array<Object>] @return [String,Hash]
# File lib/haml/attribute_builder.rb, line 103 def merge_values(key, *values) values.inject(nil) do |to, from| merge_value(key, to, from) end end
# File lib/haml/attribute_builder.rb, line 109 def verify_attribute_names!(attribute_names) attribute_names.each do |attribute_name| if attribute_name =~ INVALID_ATTRIBUTE_NAME_REGEX raise InvalidAttributeNameError.new("Invalid attribute name '#{attribute_name}' was rendered") end end end
Private Class Methods
# File lib/haml/attribute_builder.rb, line 150 def build_data_keys(data_hash, hyphenate, attr_name="data") Hash[data_hash.map do |name, value| if name == nil [attr_name, value] elsif hyphenate ["#{attr_name}-#{name.to_s.tr('_', '-')}", value] else ["#{attr_name}-#{name}", value] end end] end
# File lib/haml/attribute_builder.rb, line 162 def flatten_data_attributes(data, key, join_char, seen = []) return {key => data} unless data.is_a?(Hash) return {key => nil} if seen.include? data.object_id seen << data.object_id data.sort {|x, y| x[0].to_s <=> y[0].to_s}.inject({}) do |hash, (k, v)| joined = key == '' ? k : [key, k].join(join_char) hash.merge! flatten_data_attributes(v, joined, join_char, seen) end end
Merge a couple of values to one attribute value. No destructive operation.
@param to [String,Hash,nil] @param from [Object] @return [String,Hash]
# File lib/haml/attribute_builder.rb, line 124 def merge_value(key, to, from) if from.kind_of?(Hash) || to.kind_of?(Hash) from = { nil => from } if !from.is_a?(Hash) to = { nil => to } if !to.is_a?(Hash) to.merge(from) elsif key == 'id' merged_id = filter_and_join(from, '_') if to && merged_id merged_id = "#{to}_#{merged_id}" elsif to || merged_id merged_id ||= to end merged_id elsif key == 'class' merged_class = filter_and_join(from, ' ') if to && merged_class merged_class = (to.split(' ') | merged_class.split(' ')).join(' ') elsif to || merged_class merged_class ||= to end merged_class else from end end
Takes an array of objects and uses the class and id of the first one to create an attributes hash. The second object, if present, is used as a prefix, just like you can do with ‘dom_id()` and `dom_class()` in Rails
# File lib/haml/attribute_builder.rb, line 178 def parse_object_ref(ref) prefix = ref[1] ref = ref[0] # Let's make sure the value isn't nil. If it is, return the default Hash. return {} if ref.nil? class_name = if ref.respond_to?(:haml_object_ref) ref.haml_object_ref else underscore(ref.class) end ref_id = if ref.respond_to?(:to_key) key = ref.to_key key.join('_') unless key.nil? else ref.id end id = "#{class_name}_#{ref_id || 'new'}" if prefix class_name = "#{ prefix }_#{ class_name}" id = "#{ prefix }_#{ id }" end { 'id'.freeze => id, 'class'.freeze => class_name } end
Changes a word from camel case to underscores. Based on the method of the same name in Rails’ Inflector, but copied here so it’ll run properly without Rails.
# File lib/haml/attribute_builder.rb, line 208 def underscore(camel_cased_word) word = camel_cased_word.to_s.dup word.gsub!(/::/, '_') word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2') word.gsub!(/([a-z\d])([A-Z])/, '\1_\2') word.tr!('-', '_') word.downcase! word end