README.adoc in moxml-0.1.0 vs README.adoc in moxml-0.1.1
- old
+ new
@@ -1,770 +1,576 @@
-= Moxml: Modular XML processing for Ruby
+= Moxml: Modern XML processing for Ruby
+:toc: macro
+:toclevels: 3
+:toc-title: Contents
+:source-highlighter: highlight.js
-Moxml provides a unified API for XML processing in Ruby, supporting multiple XML parsing backends (Nokogiri, Ox, and Oga).
+image:https://github.com/lutaml/moxml/workflows/rake/badge.svg["Build Status", link="https://github.com/lutaml/moxml/actions?workflow=rake"]
-Moxml ("mox-em-el") stands for "Modular XML" and aims to provide a consistent
-interface for working with XML documents, regardless of the underlying XML
-library.
+toc::[]
-== Installation
+== Introduction and purpose
+Moxml provides a unified, modern XML processing interface for Ruby applications.
+It offers a consistent API that abstracts away the underlying XML implementation
+details while maintaining high performance through efficient node mapping and
+native XPath querying.
+
+Key features:
+
+* Intuitive, Ruby-idiomatic API for XML manipulation
+* Consistent interface across different XML libraries
+* Efficient node mapping for XPath queries
+* Support for all XML node types and features
+* Easy switching between XML processing engines
+* Clean separation between interface and implementation
+
+== Getting started
+
+Install the gem and at least one supported XML library:
+
[source,ruby]
----
+# In your Gemfile
gem 'moxml'
+gem 'nokogiri' # Or 'ox' or 'oga'
----
-== Basic usage
+=== Basic document creation
-=== Configuration
-
-Configure Moxml to use your preferred XML backend:
-
[source,ruby]
----
require 'moxml'
-Moxml.configure do |config|
- config.backend = :nokogiri # or :ox, :oga
-end
-----
+# Create a new XML document
+doc = Moxml.new.create_document
-=== Creating and parsing documents
+# Add XML declaration
+doc.add_declaration(version: "1.0", encoding: "UTF-8")
-[source,ruby]
-----
-# Create new empty document
-doc = Moxml::Document.new
+# Create root element with namespace
+root = doc.create_element('book')
+root.add_namespace('dc', 'http://purl.org/dc/elements/1.1/')
+doc.add_child(root)
-# Parse from string
-doc = Moxml::Document.parse("<root><child>content</child></root>")
+# Add content
+title = doc.create_element('dc:title')
+title.text = 'XML Processing with Ruby'
+root.add_child(title)
-# Parse with encoding
-doc = Moxml::Document.parse(xml_string, encoding: 'UTF-8')
+# Output formatted XML
+puts doc.to_xml(indent: 2)
----
-=== Document creation patterns
+== Working with documents
+=== Using the builder pattern
+
+The builder pattern provides a clean DSL for creating XML documents:
+
[source,ruby]
----
-# Method 1: Create and build
-doc = Moxml::Document.new
-root = doc.create_element('root')
-doc.add_child(root)
+doc = Moxml.new.build do
+ declaration version: "1.0", encoding: "UTF-8"
-# Method 2: Parse from string
-doc = Moxml::Document.parse("<root/>")
+ element 'library', xmlns: 'http://example.org/library' do
+ element 'book' do
+ element 'title' do
+ text 'Ruby Programming'
+ end
-# Method 3: Parse with encoding
-doc = Moxml::Document.parse(xml_string, encoding: 'UTF-8')
+ element 'author' do
+ text 'Jane Smith'
+ end
-# Method 4: Parse with options
-doc = Moxml::Document.parse(xml_string, {
- encoding: 'UTF-8',
- strict: true
-})
+ comment 'Publication details'
+ element 'published', year: '2024'
+
+ cdata '<custom>metadata</custom>'
+ end
+ end
+end
----
-=== Common XML patterns
+=== Direct document manipulation
[source,ruby]
----
-# Working with namespaces
-doc = Moxml::Document.new
-root = doc.create_element('root')
-root['xmlns:custom'] = 'http://example.com/ns'
-child = doc.create_element('custom:element')
-root.add_child(child)
+doc = Moxml.new.create_document
-# Creating structured data
-person = doc.create_element('person')
-person['id'] = '123'
-name = doc.create_element('name')
-name.add_child(doc.create_text('John Doe'))
-person.add_child(name)
+# Add declaration
+doc.add_declaration(version: "1.0", encoding: "UTF-8")
-# Working with attributes
-element = doc.create_element('div')
-element['class'] = 'container'
-element['data-id'] = '123'
-element['style'] = 'color: blue'
+# Create root with namespace
+root = doc.create_element('library')
+root.add_namespace(nil, 'http://example.org/library')
+doc.add_child(root)
-# Handling special characters
-text = doc.create_text('Special chars: < > & " \'')
-cdata = doc.create_cdata('<script>alert("Hello!");</script>')
+# Add elements with attributes
+book = doc.create_element('book')
+book['id'] = 'b1'
+root.add_child(book)
-# Processing instructions
-pi = doc.create_processing_instruction('xml-stylesheet',
- 'type="text/xsl" href="style.xsl"')
-doc.add_child(pi)
+# Add mixed content
+book.add_child(doc.create_comment('Book details'))
+title = doc.create_element('title')
+title.text = 'Ruby Programming'
+book.add_child(title)
----
-=== Working with elements
+== XML objects and their methods
+=== Document object
+
+The Document object represents an XML document and serves as the root container
+for all XML nodes.
+
[source,ruby]
----
-# Create new element
-element = Moxml::Element.new('tagname')
+# Creating a document
+doc = Moxml.new.create_document
+doc = Moxml.new.parse(xml_string)
-# Add attributes
-element['class'] = 'content'
+# Document properties and methods
+doc.encoding # Get document encoding
+doc.encoding = "UTF-8" # Set document encoding
+doc.version # Get XML version
+doc.version = "1.1" # Set XML version
+doc.standalone # Get standalone declaration
+doc.standalone = "yes" # Set standalone declaration
-# Access attributes
-class_attr = element['class']
+# Document structure
+doc.root # Get root element
+doc.children # Get all top-level nodes
+doc.add_child(node) # Add a child node
+doc.remove_child(node) # Remove a child node
-# Add child elements
-child = element.create_element('child')
-element.add_child(child)
+# Node creation methods
+doc.create_element(name) # Create new element
+doc.create_text(content) # Create text node
+doc.create_cdata(content) # Create CDATA section
+doc.create_comment(content) # Create comment
+doc.create_processing_instruction(target, content) # Create PI
-# Access text content
-text_content = element.text
+# Document querying
+doc.xpath(expression) # Find nodes by XPath
+doc.at_xpath(expression) # Find first node by XPath
-# Add text content
-text = element.create_text('content')
-element.add_child(text)
-
-# Chaining operations
-element
- .add_child(doc.create_element('child'))
- .add_child(doc.create_text('content'))
- ['class'] = 'new-class'
-
-# Complex element creation
-div = doc.create_element('div')
-div['class'] = 'container'
-div.add_child(doc.create_element('span'))
- .add_child(doc.create_text('Hello'))
-div.add_child(doc.create_element('br'))
-div.add_child(doc.create_text('World'))
+# Serialization
+doc.to_xml(options) # Convert to XML string
----
-=== Working with different node types
+=== Element object
+Elements are the primary structural components of an XML document, representing
+tags with attributes and content.
+
[source,ruby]
----
-# Text nodes with various content
-plain_text = Moxml::Text.new("Simple text")
-multiline_text = Moxml::Text.new("Line 1\nLine 2")
-special_chars = Moxml::Text.new("Special: & < > \" '")
+# Element properties
+element.name # Get element name
+element.name = "new_name" # Set element name
+element.text # Get text content
+element.text = "content" # Set text content
+element.inner_html # Get inner XML content
+element.inner_html = xml # Set inner XML content
-# CDATA sections for different content types
-script_cdata = Moxml::Cdata.new("function() { alert('Hello!'); }")
-xml_cdata = Moxml::Cdata.new("<data><item>value</item></data>")
-mixed_cdata = Moxml::Cdata.new("Text with ]]> characters")
+# Attributes
+element[name] # Get attribute value
+element[name] = value # Set attribute value
+element.attributes # Get all attributes
+element.remove_attribute(name) # Remove attribute
-# Comments for documentation
-todo_comment = Moxml::Comment.new("TODO: Add validation")
-section_comment = Moxml::Comment.new("----- Section Break -----")
-debug_comment = Moxml::Comment.new("DEBUG: Remove in production")
+# Namespace handling
+element.namespace # Get element's namespace
+element.namespace = ns # Set element's namespace
+element.add_namespace(prefix, uri) # Add new namespace
+element.namespaces # Get all namespace definitions
-# Processing instructions for various uses
-style_pi = Moxml::ProcessingInstruction.new(
- "xml-stylesheet",
- 'type="text/css" href="style.css"'
-)
-php_pi = Moxml::ProcessingInstruction.new(
- "php",
- 'echo "<?php echo $var; ?>>";'
-)
-custom_pi = Moxml::ProcessingInstruction.new(
- "custom-processor",
- 'param1="value1" param2="value2"'
-)
+# Node structure
+element.parent # Get parent node
+element.children # Get child nodes
+element.add_child(node) # Add child node
+element.remove_child(node) # Remove child node
+element.add_previous_sibling(node) # Add sibling before
+element.add_next_sibling(node) # Add sibling after
+element.replace(node) # Replace with another node
+element.remove # Remove from document
+
+# Node type checking
+element.element? # Returns true
+element.text? # Returns false
+element.cdata? # Returns false
+element.comment? # Returns false
+element.processing_instruction? # Returns false
+
+# Node querying
+element.xpath(expression) # Find nodes by XPath
+element.at_xpath(expression) # Find first node by XPath
----
-=== Element manipulation examples
+=== Text object
+Text nodes represent character data in the XML document.
+
[source,ruby]
----
-# Building complex structures
-doc = Moxml::Document.new
-root = doc.create_element('html')
-doc.add_child(root)
+# Creating text nodes
+text = doc.create_text("content")
-# Create head section
-head = doc.create_element('head')
-root.add_child(head)
+# Text properties
+text.content # Get text content
+text.content = "new" # Set text content
-title = doc.create_element('title')
-title.add_child(doc.create_text('Example Page'))
-head.add_child(title)
+# Node type checking
+text.text? # Returns true
-meta = doc.create_element('meta')
-meta['charset'] = 'UTF-8'
-head.add_child(meta)
+# Structure
+text.parent # Get parent node
+text.remove # Remove from document
+text.replace(node) # Replace with another node
+----
-# Create body section
-body = doc.create_element('body')
-root.add_child(body)
+=== CDATA object
-div = doc.create_element('div')
-div['class'] = 'container'
-body.add_child(div)
+CDATA sections contain text that should not be parsed as markup.
-# Add multiple paragraphs
-3.times do |i|
- p = doc.create_element('p')
- p.add_child(doc.create_text("Paragraph #{i + 1}"))
- div.add_child(p)
-end
+[source,ruby]
+----
+# Creating CDATA sections
+cdata = doc.create_cdata("<raw>content</raw>")
-# Working with lists
-ul = doc.create_element('ul')
-div.add_child(ul)
+# CDATA properties
+cdata.content # Get CDATA content
+cdata.content = "new" # Set CDATA content
-['Item 1', 'Item 2', 'Item 3'].each do |text|
- li = doc.create_element('li')
- li.add_child(doc.create_text(text))
- ul.add_child(li)
-end
+# Node type checking
+cdata.cdata? # Returns true
-# Adding link element
-a = doc.create_element('a')
-a['href'] = 'https://example.com'
-a.add_child(doc.create_text('Visit Example'))
-div.add_child(a)
+# Structure
+cdata.parent # Get parent node
+cdata.remove # Remove from document
+cdata.replace(node) # Replace with another node
----
-=== Advanced node manipulation
+=== Comment object
+Comments contain human-readable notes in the XML document.
+
[source,ruby]
----
-# Cloning nodes
-original = doc.create_element('div')
-original['id'] = 'original'
-clone = original.clone
+# Creating comments
+comment = doc.create_comment("Note")
-# Moving nodes
-target = doc.create_element('target')
-source = doc.create_element('source')
-source.add_child(doc.create_text('Content'))
-target.add_child(source)
+# Comment properties
+comment.content # Get comment content
+comment.content = "new" # Set comment content
-# Replacing nodes
-old_node = doc.at_xpath('//old')
-new_node = doc.create_element('new')
-old_node.replace(new_node)
+# Node type checking
+comment.comment? # Returns true
-# Inserting before/after
-reference = doc.create_element('reference')
-before = doc.create_element('before')
-after = doc.create_element('after')
-reference.add_previous_sibling(before)
-reference.add_next_sibling(after)
-
-# Conditional manipulation
-element = doc.at_xpath('//conditional')
-if element['flag'] == 'true'
- element.add_child(doc.create_text('Flag is true'))
-else
- element.remove
-end
+# Structure
+comment.parent # Get parent node
+comment.remove # Remove from document
+comment.replace(node) # Replace with another node
----
-=== Working with namespaces
+=== Processing instruction object
+Processing instructions provide instructions to applications processing the XML.
+
[source,ruby]
----
-# Creating namespaced document
-doc = Moxml::Document.new
-root = doc.create_element('root')
-root['xmlns'] = 'http://example.com/default'
-root['xmlns:custom'] = 'http://example.com/custom'
-doc.add_child(root)
+# Creating processing instructions
+pi = doc.create_processing_instruction("xml-stylesheet",
+ 'type="text/xsl" href="style.xsl"')
-# Adding namespaced elements
-default_elem = doc.create_element('default-elem')
-custom_elem = doc.create_element('custom:elem')
+# PI properties
+pi.target # Get PI target
+pi.target = "new" # Set PI target
+pi.content # Get PI content
+pi.content = "new" # Set PI content
-root.add_child(default_elem)
-root.add_child(custom_elem)
+# Node type checking
+pi.processing_instruction? # Returns true
-# Working with attributes in namespaces
-custom_elem['custom:attr'] = 'value'
-
-# Accessing namespaced content
-ns_elem = doc.at_xpath('//custom:elem')
-ns_attr = ns_elem['custom:attr']
+# Structure
+pi.parent # Get parent node
+pi.remove # Remove from document
+pi.replace(node) # Replace with another node
----
-=== Document serialization examples
+=== Attribute object
+Attributes represent name-value pairs on elements.
+
[source,ruby]
----
-# Basic serialization
-xml_string = doc.to_xml
+# Attribute properties
+attr.name # Get attribute name
+attr.name = "new" # Set attribute name
+attr.value # Get attribute value
+attr.value = "new" # Set attribute value
-# Pretty printing with indentation
-formatted_xml = doc.to_xml(
- indent: 2,
- pretty: true
-)
+# Namespace handling
+attr.namespace # Get attribute's namespace
+attr.namespace = ns # Set attribute's namespace
-# Controlling XML declaration
-with_declaration = doc.to_xml(
- xml_declaration: true,
- encoding: 'UTF-8',
- standalone: 'yes'
-)
-
-# Compact output
-minimal_xml = doc.to_xml(
- indent: 0,
- pretty: false,
- xml_declaration: false
-)
-
-# Custom formatting
-custom_format = doc.to_xml(
- indent: 4,
- encoding: 'ISO-8859-1',
- xml_declaration: true
-)
+# Node type checking
+attr.attribute? # Returns true
----
-== Implementation details
+=== Namespace object
-=== Memory management
+Namespaces define XML namespaces used in the document.
[source,ruby]
----
-# Efficient document handling
-doc = Moxml::Document.parse(large_xml)
-begin
- # Process document
- result = process_document(doc)
-ensure
- # Clear references
- doc = nil
- GC.start
-end
+# Namespace properties
+ns.prefix # Get namespace prefix
+ns.uri # Get namespace URI
-# Streaming large node sets
-doc.xpath('//large-set/*').each do |node|
- # Process node
- process_node(node)
- # Clear reference
- node = nil
-end
+# Formatting
+ns.to_s # Format as xmlns declaration
-# Handling large collections
-def process_large_nodeset(nodeset)
- nodeset.each do |node|
- yield node if block_given?
- end
-ensure
- # Clear references
- nodeset = nil
- GC.start
-end
+# Node type checking
+ns.namespace? # Returns true
----
-=== Backend-specific optimizations
+=== Node traversal and inspection
+Each node type provides methods for traversing the document structure:
+
[source,ruby]
----
-# Nokogiri-specific optimizations
-if Moxml.config.backend == :nokogiri
- # Use native CSS selectors
- nodes = doc.native.css('complex > selector')
- nodes.each do |native_node|
- node = Moxml::Node.wrap(native_node)
- # Process node
- end
+node.parent # Get parent node
+node.children # Get child nodes
+node.next_sibling # Get next sibling
+node.previous_sibling # Get previous sibling
+node.ancestors # Get all ancestor nodes
+node.descendants # Get all descendant nodes
- # Use native XPath
- results = doc.native.xpath('//complex/xpath/expression')
-end
+# Type checking
+node.element? # Is it an element?
+node.text? # Is it a text node?
+node.cdata? # Is it a CDATA section?
+node.comment? # Is it a comment?
+node.processing_instruction? # Is it a PI?
+node.attribute? # Is it an attribute?
+node.namespace? # Is it a namespace?
-# Ox-specific optimizations
-if Moxml.config.backend == :ox
- # Use native parsing options
- doc = Moxml::Document.parse(xml, {
- mode: :generic,
- effort: :tolerant,
- smart: true
- })
+# Node information
+node.document # Get owning document
+node.path # Get XPath to node
+node.line_number # Get source line number (if available)
+----
- # Direct element creation
- element = Ox::Element.new('name')
- wrapped = Moxml::Element.new(element)
-end
+== Advanced features
-# Oga-specific optimizations
-if Moxml.config.backend == :oga
- # Use native parsing features
- doc = Moxml::Document.parse(xml, {
- encoding: 'UTF-8',
- strict: true
- })
+=== XPath querying and node mapping
- # Direct access to native methods
- nodes = doc.native.xpath('//element')
-end
-----
+Moxml provides efficient XPath querying by leveraging the native XML library's
+implementation while maintaining consistent node mapping:
-=== Threading patterns
-
[source,ruby]
----
-# Thread-safe document creation
-require 'thread'
+# Find all book elements
+books = doc.xpath('//book')
+# Returns Moxml::Element objects mapped to native nodes
-class ThreadSafeXmlProcessor
- def initialize
- @mutex = Mutex.new
- end
+# Find with namespaces
+titles = doc.xpath('//dc:title',
+ 'dc' => 'http://purl.org/dc/elements/1.1/')
- def process_document(xml_string)
- @mutex.synchronize do
- doc = Moxml::Document.parse(xml_string)
- # Process document
- result = doc.to_xml
- doc = nil
- result
- end
- end
-end
+# Find first matching node
+first_book = doc.at_xpath('//book')
-# Parallel document processing
-def process_documents(xml_strings)
- threads = xml_strings.map do |xml|
- Thread.new do
- doc = Moxml::Document.parse(xml)
- # Process document
- doc = nil
- end
- end
- threads.each(&:join)
+# Chain queries
+doc.xpath('//book').each do |book|
+ # Each book is a mapped Moxml::Element
+ title = book.at_xpath('.//title')
+ puts "#{book['id']}: #{title.text}"
end
-
-# Thread-local document storage
-Thread.new do
- Thread.current[:document] = Moxml::Document.new
- # Process document
-ensure
- Thread.current[:document] = nil
-end
----
-== Troubleshooting
+=== Namespace handling
-=== Common issues and solutions
-
-==== Parsing errors
-
[source,ruby]
----
-# Handle malformed XML
-begin
- doc = Moxml::Document.parse(xml_string)
-rescue Moxml::ParseError => e
- puts "Parse error at line #{e.line}, column #{e.column}: #{e.message}"
- # Attempt recovery
- xml_string = cleanup_xml(xml_string)
- retry
-end
+# Add namespace to element
+element.add_namespace('dc', 'http://purl.org/dc/elements/1.1/')
-# Handle encoding issues
-begin
- doc = Moxml::Document.parse(xml_string, encoding: 'UTF-8')
-rescue Moxml::ParseError => e
- if e.message =~ /encoding/
- # Try detecting encoding
- detected_encoding = detect_encoding(xml_string)
- retry if detected_encoding
- end
- raise
-end
+# Create element in namespace
+title = doc.create_element('dc:title')
+title.text = 'Document Title'
+
+# Query with namespaces
+doc.xpath('//dc:title',
+ 'dc' => 'http://purl.org/dc/elements/1.1/')
----
-==== Memory issues
+=== Accessing native implementation
+While not typically needed, you can access the underlying XML library's nodes:
+
[source,ruby]
----
-# Handle large documents
-def process_large_document(path)
- # Read and process in chunks
- File.open(path) do |file|
- doc = Moxml::Document.parse(file)
- doc.xpath('//chunk').each do |chunk|
- process_chunk(chunk)
- chunk = nil
- end
- doc = nil
- end
- GC.start
-end
+# Get native node
+native_node = element.native
-# Monitor memory usage
-require 'get_process_mem'
+# Get adapter being used
+adapter = element.context.config.adapter
-def memory_safe_processing(xml)
- memory = GetProcessMem.new
- initial_memory = memory.mb
+# Create from native node
+element = Moxml::Element.new(native_node, context)
+----
- doc = Moxml::Document.parse(xml)
- result = process_document(doc)
- doc = nil
- GC.start
+== Error handling
- final_memory = memory.mb
- puts "Memory usage: #{final_memory - initial_memory}MB"
+Moxml provides specific error classes for different types of errors that may
+occur during XML processing:
- result
+[source,ruby]
+----
+begin
+ doc = context.parse(xml_string)
+rescue Moxml::ParseError => e
+ # Handles XML parsing errors
+ puts "Parse error at line #{e.line}, column #{e.column}"
+ puts "Message: #{e.message}"
+rescue Moxml::ValidationError => e
+ # Handles XML validation errors
+ puts "Validation error: #{e.message}"
+rescue Moxml::XPathError => e
+ # Handles XPath expression errors
+ puts "XPath error: #{e.message}"
+rescue Moxml::Error => e
+ # Handles other Moxml-specific errors
+ puts "Error: #{e.message}"
end
----
-==== Backend-specific issues
+== Configuration
+Moxml can be configured globally or per instance:
+
[source,ruby]
----
-# Handle backend limitations
-def safe_xpath(doc, xpath)
- case Moxml.config.backend
- when :nokogiri
- doc.xpath(xpath)
- when :ox
- # Ox has limited XPath support
- fallback_xpath_search(doc, xpath)
- when :oga
- # Handle Oga-specific XPath syntax
- modified_xpath = adjust_xpath_for_oga(xpath)
- doc.xpath(modified_xpath)
- end
+# Global configuration
+Moxml.configure do |config|
+ config.default_adapter = :nokogiri
+ config.strict = true
+ config.encoding = 'UTF-8'
end
-# Handle backend switching
-def with_backend(backend)
- original_backend = Moxml.config.backend
- Moxml.config.backend = backend
- yield
-ensure
- Moxml.config.backend = original_backend
+# Instance configuration
+moxml = Moxml.new do |config|
+ config.adapter = :ox
+ config.strict = false
end
----
-=== Performance optimization
+== Thread safety
-==== Document creation
+Moxml is thread-safe when used properly. Each instance maintains its own state
+and can be used safely in concurrent operations:
[source,ruby]
----
-# Efficient document building
-def build_large_document
- doc = Moxml::Document.new
- root = doc.create_element('root')
- doc.add_child(root)
-
- # Pre-allocate elements
- elements = Array.new(1000) do |i|
- elem = doc.create_element('item')
- elem['id'] = i.to_s
- elem
+class XmlProcessor
+ def initialize
+ @mutex = Mutex.new
+ @context = Moxml.new
end
- # Batch add elements
- elements.each do |elem|
- root.add_child(elem)
+ def process(xml)
+ @mutex.synchronize do
+ doc = @context.parse(xml)
+ # Modify document
+ doc.to_xml
+ end
end
-
- doc
end
+----
-# Memory-efficient processing
-def process_large_xml(xml_string)
- result = []
- doc = Moxml::Document.parse(xml_string)
+== Performance considerations
- doc.xpath('//item').each do |item|
- # Process and immediately discard
- result << process_item(item)
- item = nil
- end
+=== Memory management
- doc = nil
- GC.start
+Moxml maintains a node registry to ensure consistent object mapping:
- result
-end
-----
-
-==== Query optimization
-
[source,ruby]
----
-# Optimize node selection
-def efficient_node_selection(doc)
- # Cache frequently used nodes
- @header_nodes ||= doc.xpath('//header').to_a
-
- # Use specific selectors
- doc.xpath('//specific/path') # Better than '//*[name()="specific"]'
-
- # Combine queries when possible
- doc.xpath('//a | //b') # Better than two separate queries
-end
-
-# Optimize attribute access
-def efficient_attribute_handling(element)
- # Cache attribute values
- @cached_attrs ||= element.attributes
-
- # Direct attribute access
- value = element['attr'] # Better than element.attributes['attr']
-
- # Batch attribute updates
- attrs = {'id' => '1', 'class' => 'new', 'data' => 'value'}
- attrs.each { |k,v| element[k] = v }
-end
+doc = context.parse(large_xml)
+# Process document
+doc = nil # Allow garbage collection of document and registry
+GC.start # Force garbage collection if needed
----
-==== Serialization optimization
+=== Efficient querying
+Use specific XPath expressions for better performance:
+
[source,ruby]
----
-# Efficient output generation
-def optimized_serialization(doc)
- # Minimal output
- compact = doc.to_xml(
- indent: 0,
- pretty: false,
- xml_declaration: false
- )
+# More efficient - specific path
+doc.xpath('//book/title')
- # Balanced formatting
- readable = doc.to_xml(
- indent: 2,
- pretty: true,
- xml_declaration: true
- )
+# Less efficient - requires full document scan
+doc.xpath('//title')
- # Stream large documents
- File.open('large.xml', 'w') do |file|
- doc.write_to(file, indent: 2)
- end
-end
+# Most efficient - direct child access
+root.xpath('./title')
----
-=== Debugging tips
+== Best practices
-==== Inspection helpers
+=== Document creation
[source,ruby]
----
-# Debug node structure
-def inspect_node(node, level = 0)
- indent = " " * level
- puts "#{indent}#{node.class.name}: #{node.name}"
-
- if node.respond_to?(:attributes)
- node.attributes.each do |name, attr|
- puts "#{indent} @#{name}=#{attr.value.inspect}"
+# Preferred - using builder pattern
+doc = Moxml.new.build do
+ declaration version: "1.0", encoding: "UTF-8"
+ element 'root' do
+ element 'child' do
+ text 'content'
end
end
-
- if node.respond_to?(:children)
- node.children.each { |child| inspect_node(child, level + 1) }
- end
end
-# Track node operations
-def debug_node_operations
- nodes_created = 0
- nodes_removed = 0
-
- yield
-ensure
- puts "Nodes created: #{nodes_created}"
- puts "Nodes removed: #{nodes_removed}"
-end
+# Alternative - direct manipulation
+doc = Moxml.new.create_document
+doc.add_declaration(version: "1.0", encoding: "UTF-8")
+root = doc.create_element('root')
+doc.add_child(root)
----
-==== Backend validation
+=== Node manipulation
[source,ruby]
----
-# Verify backend behavior
-def verify_backend_compatibility
- doc = Moxml::Document.new
+# Preferred - chainable operations
+element
+ .add_namespace('dc', 'http://purl.org/dc/elements/1.1/')
+ .add_child(doc.create_text('content'))
- # Test basic operations
- element = doc.create_element('test')
- doc.add_child(element)
-
- # Verify node handling
- raise "Node creation failed" unless doc.root
- raise "Node type wrong" unless doc.root.is_a?(Moxml::Element)
-
- # Verify serialization
- xml = doc.to_xml
- raise "Serialization failed" unless xml.include?('<test/>')
-
- puts "Backend verification successful"
-rescue => e
- puts "Backend verification failed: #{e.message}"
+# Preferred - clear node type checking
+if node.element?
+ node.add_child(doc.create_text('content'))
end
----
-== Error handling
-
-Moxml provides unified error handling:
-
-* `Moxml::Error` - Base error class
-* `Moxml::ParseError` - XML parsing errors
-* `Moxml::ArgumentError` - Invalid argument errors
-
-=== Error handling patterns
-
-[source,ruby]
-----
-# Handle parsing errors
-begin
- doc = Moxml::Document.parse(xml_string)
-rescue Moxml::ParseError => e
- logger.error "Parse error: #{e.message}"
- logger.error "At line #{e.line}, column #{e.column}"
- raise
-end
-
-# Handle invalid operations
-begin
- element['invalid/name'] = 'value'
-rescue Moxml::ArgumentError => e
- logger.warn "Invalid operation: #{e.message}"
- # Use alternative approach
-end
-
-# Custom error handling
-class XmlProcessor
- def process(xml)
- doc = Moxml::Document.parse(xml)
- yield doc
- rescue Moxml::Error => e
- handle_moxml_error(e)
- rescue StandardError => e
- handle_standard_error(e)
- ensure
- doc = nil
- end
-end
-----
-
== Contributing
-Bug reports and pull requests are welcome on GitHub at
-https://github.com/lutaml/moxml.
+1. Fork the repository
+2. Create your feature branch (`git checkout -b feature/my-new-feature`)
+3. Commit your changes (`git commit -am 'Add some feature'`)
+4. Push to the branch (`git push origin feature/my-new-feature`)
+5. Create a new Pull Request
-=== Development guidelines
+== License
-* Follow Ruby style guide
-* Add tests for new features
-* Update documentation
-* Ensure backwards compatibility
-* Consider performance implications
-* Test with all supported backends
+Copyright (c) 2024 Ribose Inc.
-== Copyright and license
+This project is licensed under the BSD-2-Clause License. See the LICENSE file for details.
-Copyright Ribose.
-
-The gem is available as open source under the terms of the BSD-2-Clause License.