Skip to content

Commit

Permalink
Add __nested__attributes__ (#746)
Browse files Browse the repository at this point in the history
Special-cases the serialization of nested attributes to reduce the
number of allocations. This avoids having to build up a new hash on each
recursive call, and lets us "bypass" a lot of the safety features (such
as dangerous attribute sanitation) that Phlex includes when serializing
nested attributes on elements (since, in the nested case, we *know* that
our key cannot be something like `href` or `onclick`).

This should reduce the number of allocations and make the code more
performant.
  • Loading branch information
joeldrapper authored Aug 12, 2024
2 parents 63cde9c + bad756b commit 37700b2
Showing 1 changed file with 45 additions and 8 deletions.
53 changes: 45 additions & 8 deletions lib/phlex/sgml.rb
Original file line number Diff line number Diff line change
Expand Up @@ -416,14 +416,7 @@ def __attributes__(attributes, buffer = +"")
when :style
buffer << " " << name << '="' << __styles__(v).gsub('"', "&quot;") << '"'
else
__attributes__(
v.transform_keys { |subkey|
case subkey
when Symbol then"#{name}-#{subkey.name.tr('_', '-')}"
else "#{name}-#{subkey}"
end
}, buffer
)
__nested_attributes__(v, "#{name}-", buffer)
end
when Array
value = case k
Expand Down Expand Up @@ -454,6 +447,50 @@ def __attributes__(attributes, buffer = +"")
buffer
end

# @api private
#
# Provides the nested-attributes case for serializing out attributes.
# This allows us to skip many of the checks the `__attributes__` method must perform.
def __nested_attributes__(attributes, base_name, buffer = +"")
attributes.each do |k, v|
next unless v

name = case k
when String then k
when Symbol then k.name.tr("_", "-")
else raise ArgumentError.new("Attribute keys should be Strings or Symbols")
end

case v
when true
buffer << " " << base_name << name
when String
buffer << " " << base_name << name << '="' << v.gsub('"', "&quot;") << '"'
when Symbol
buffer << " " << base_name << name << '="' << v.name.gsub('"', "&quot;") << '"'
when Integer, Float
buffer << " " << base_name << name << '="' << v.to_s << '"'
when Hash
__nested_attributes__(v, "#{base_name}-#{name}-", buffer)
when Array
buffer << " " << base_name << name << '="' << v.compact.join(" ").gsub('"', "&quot;") << '"'
when Set
buffer << " " << base_name << name << '="' << v.to_a.compact.join(" ").gsub('"', "&quot;") << '"'
else
value = if v.respond_to?(:to_phlex_attribute_value)
v.to_phlex_attribute_value
elsif v.respond_to?(:to_str)
v.to_str
else
v.to_s
end
buffer << " " << base_name << name << '="' << value.gsub('"', "&quot;") << '"'
end

buffer
end
end

# @api private
def __classes__(c)
case c
Expand Down

0 comments on commit 37700b2

Please sign in to comment.