Module: Moxml::Attribute

Includes:
Node
Defined in:
lib/moxml/attribute.rb

Constant Summary

Constants included from NodeBehavior

NodeBehavior::TYPES

Constants included from XmlUtils

XmlUtils::VALID_ELEMENT_NAMES

Instance Attribute Summary

Attributes included from NodeBehavior

#context, #native, #parent_node

Instance Method Summary collapse

Methods included from Node

adapter, contract_module, contract_modules, node_type_map, wrap

Methods included from NodeBehavior

#add_child, #add_next_sibling, #add_previous_sibling, #after, #ancestors, #at_xpath, #before, #blank?, #children, #clear_native_memo!, #descendants, #digest, #document, #dup, #each, #each_node, #entity_bearing?, #find, #find_all, #first_child, #following_siblings, #has_children?, #initialize, #invalidate_namespace_cache!, #last_child, #line_number, #materialize, #materialize_fields, #namespaces, #next_sibling, #outer_xml, #path, #preceding_siblings, #previous_sibling, #prime_contract, #refresh_native!, #replace, #source_position, #to_xml, #xpath

Methods included from XmlUtils

#encode_entities, #normalize_xml_value, #validate_comment_content, #validate_declaration_encoding, #validate_declaration_standalone, #validate_declaration_version, #validate_element_name, #validate_entity_reference_name, #validate_pi_target, #validate_prefix, #validate_uri

Instance Method Details

#==(other) ⇒ Object



106
107
108
109
110
# File 'lib/moxml/attribute.rb', line 106

def ==(other)
  return false unless other.is_a?(Attribute)

  name == other.name && value == other.value && namespace == other.namespace
end

#attribute?Boolean

Returns:



120
121
122
# File 'lib/moxml/attribute.rb', line 120

def attribute?
  true
end

#elementObject



77
78
79
80
# File 'lib/moxml/attribute.rb', line 77

def element
  native_elem = adapter.attribute_element(@native)
  native_elem && Moxml::Node.wrap(native_elem, context)
end

#identifierString

Returns the primary identifier for this attribute (its name)

Returns:

  • the attribute name



25
26
27
# File 'lib/moxml/attribute.rb', line 25

def identifier
  name
end

#nameObject

Immutable between name= writes (which clear it via clear_native_memo!), unlike element names that can change via native adoption; the memo removes an adapter read per access.



10
11
12
# File 'lib/moxml/attribute.rb', line 10

def name
  @name ||= adapter.attribute_name(@native)
end

#name=(new_name) ⇒ Object



14
15
16
17
18
19
20
21
# File 'lib/moxml/attribute.rb', line 14

def name=(new_name)
  # Mutation return contract (Adapter::Base): returns the native
  # to keep tracking — same object for in-place adapters, fresh
  # object for value-object adapters (leptris).
  context.bump_namespace_scope_generation
  @name = nil
  @native = adapter.set_attribute_name(@native, new_name)
end

#namespaceObject



67
68
69
70
# File 'lib/moxml/attribute.rb', line 67

def namespace
  ns = adapter.namespace(@native)
  ns && Wrappers::Namespace.new(ns, context)
end

#namespace=(ns) ⇒ Object



72
73
74
75
# File 'lib/moxml/attribute.rb', line 72

def namespace=(ns)
  # See name= for the mutation return contract.
  @native = adapter.set_namespace(@native, ns&.native)
end

#parentObject

The wrapper's parent tracking is authoritative for attributes (set at materialization by Element#attributes): the generic adapter read raises on engines whose Attr natives expose no #parent (leptris), and the tracked value needs no wrap.



86
87
88
# File 'lib/moxml/attribute.rb', line 86

def parent
  @parent_node
end

#raw_valueObject

Returns raw native value without entity marker restoration.



46
47
48
# File 'lib/moxml/attribute.rb', line 46

def raw_value
  @native.value
end

#removeObject



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/moxml/attribute.rb', line 90

def remove
  # The name must be read before the removal — engines free the
  # attribute native, and post-removal reads are use-after-free.
  name = self.name
  declaration = name == "xmlns" || name.start_with?("xmlns:")
  adapter.remove_attribute_native(@native)
  if @parent_node.is_a?(Moxml::Element)
    if declaration
      @parent_node.invalidate_attribute_cache!
    else
      @parent_node.invalidate_local_attribute_cache!
    end
  end
  self
end

#textObject

XPath conversion compatibility - attributes need .text method that returns their value for XPath comparisons



63
64
65
# File 'lib/moxml/attribute.rb', line 63

def text
  value
end

#to_sObject



112
113
114
115
116
117
118
# File 'lib/moxml/attribute.rb', line 112

def to_s
  if namespace&.prefix
    "#{namespace.prefix}:#{name}=\"#{value}\""
  else
    "#{name}=\"#{value}\""
  end
end

#valueObject Also known as: content



29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/moxml/attribute.rb', line 29

def value
  val = @native.value.to_s
  # Same guard as Element#text: entity-free documents skip the
  # marker restore scans. The memo rides the owning element's
  # (attr natives have no entity probe); a detached attribute
  # wrapper falls back to the unconditional restore.
  parent = @parent_node
  if parent.nil? || parent.entity_bearing?
    adapter.restore_entities(val)
  else
    val
  end
end

#value=(new_value) ⇒ Object



50
51
52
53
54
55
56
57
58
59
# File 'lib/moxml/attribute.rb', line 50

def value=(new_value)
  name = self.name
  if name == "xmlns" || name.start_with?("xmlns:")
    # Declaration rewrite — namespace scope changed
    context.bump_namespace_scope_generation
  else
    @parent_node&.invalidate_attribute_value_cache!
  end
  adapter.set_attribute_value(@native, new_value)
end