Builder Ruby Library

repository·master·Indexed 18 days ago

https://github.com/jimweirich/builder

A Ruby library for creating XML markup and data structures. It provides Builder::XmlMarkup for generating XML markup notation and Builder::XmlEvents for generating SAX-like XML events. Features include support for namespaces, CDATA, processing instructions, DTD declarations, and configurable indentation. It supports automatic attribute escaping in version 2.0+ and offers performance optimization via method call caching.

Tokens
3.9K
Snippets
17
Records
19
Agent score
62%

What's inside Builder

  1. Handle attribute escaping in Builder 2.0+

    master

    Starting with version 2.0.0, all attribute values passed as Strings are automatically escaped (e.g., & becomes &).

    If you need to include unescaped entities in an attribute value, pass the value as a Symbol instead of a String. This bypasses the escaping algorithm.

    xml = Builder::XmlMarkup.new
    # Strings are escaped:
    xml.sample(:escaped=>"This&That", :unescaped=>:"Here&There")
    # Output: <sample escaped="This&amp;That" unescaped="Here&amp;There"/>
  2. How Builder::XmlMarkup works

    master

    Builder provides two primary ways to generate XML:

    1. Builder::XmlMarkup: Generates XML markup notation (the most common usage).
    2. Builder::XmlEvents: Generates XML events (SAX-like).

    When using XmlMarkup, nested tags must be called on the builder object itself. When using blocks, the builder object is passed as a parameter to the block, allowing you to use a short alias for nested markup.

    xml_markup = Builder::XmlMarkup.new
    # You must explicitly call the builder for nested tags:
    xml_markup.div { |xml| xml.strong("text") }
  3. Enable UTF-8 support

    master

    To ensure Builder correctly translates UTF-8 characters into valid XML without encoding non-ASCII characters as entities, set the $KCODE variable to 'UTF8' and ensure the XML encoding is set to 'UTF-8' via instruct!.

    $KCODE = 'UTF8'
    xml = Builder::XmlMarkup.new
    xml.instruct!(:xml, :encoding => "UTF-8")
    xml.sample("Iñtërnâtiônàl")
    # => "<sample>Iñtërnâtiônàl</sample>"
  4. Install and use Builder for XML markup

    master

    To use Builder to generate XML markup in a Ruby project, require rubygems and use require_gem to specify the version. You can then instantiate Builder::XmlMarkup to create XML strings or write directly to a target like STDOUT.

    require 'rubygems'
    require_gem 'builder', '~> 2.0'
    
    builder = Builder::XmlMarkup.new
    xml = builder.person { |b| b.name("Jim"); b.phone("555-1234") }
    xml #=> <person><name>Jim</name><phone>555-1234</phone></person>
  5. Implement an XML Event Handler for Builder::XmlEvents

    master

    When using Builder::XmlEvents, the handler object you provide must implement the following three methods to receive the generated events:

    • start_tag(tag, attrs): Called when a new tag is found. tag is the name of the tag (Symbol), and attrs is a Hash of attributes for that tag.
    • end_tag(tag): Called when an end tag for tag is found.
    • text(text): Called when a string of characters is found. Note that a single logical block of text may be broken up into multiple text calls, so the handler should be prepared to handle fragmented text data.
    class MyHandler
      def start_tag(tag, attrs)
        puts "Start: #{tag} with #{attrs}"
      end
    
      def end_tag(tag)
        puts "End: #{tag}"
      end
    
      def text(text)
        puts "Text: #{text}"
      end
    end
    
    xe = Builder::XmlEvents.new(MyHandler.new)
    xe.title("Hello")
  6. How Builder::XmlMarkup targets work

    master

    XmlMarkup is designed to build markup into any object (the "target") that accepts the << (append) operator. This allows you to direct the XML output to strings, files, or standard output.

    Target Types

    • String: The default target. The markup is returned as a string.
    • IO Objects: Such as $stdout or a file handle. The markup is written directly to the stream.
    • Buffers: Any object that implements << and returns itself.
    • Other XmlMarkup instances: You can pass one XmlMarkup instance as the target for another, effectively piping the output.

    Example

    # Target: String
    xml = Builder::XmlMarkup.new
    result = xml.tag!("root") # result is a string
    
    # Target: IO
    xml = Builder::XmlMarkup.new($stdout)
    xml.tag!("root") # writes to STDOUT
    
    # Target: Another XmlMarkup instance
    xml1 = Builder::XmlMarkup.new
    xml2 = Builder::XmlMarkup.new(:target => xml1)
    xml2.tag!("child") # xml1 now contains '<child></child>'
  7. Generate XML markup with Builder::XmlMarkup

    master

    Builder::XmlMarkup allows you to create XML markup by calling methods that correspond to XML tags. Most methods can take a string as a value (for tag content) or a hash of attributes. If a block is provided, the method is treated as an opening tag, and the content inside the block is treated as nested markup.

    Key Behaviors

    • Automatic Escaping: Special XML characters like <, >, and & are automatically converted to &lt;, &gt;, and &amp;.
    • Namespaces: You can create namespaced tags by passing a symbol as the first argument. For example, xml.SOAP :Envelope produces <SOAP:Envelope>.
    • Tag Names with Special Characters: For tags that use characters not allowed in Ruby identifiers, use the tag! method.
    • Text Insertion: To insert raw text without enclosing it in tags, use the text! method.
    • Block Semantics: In recent versions, markup methods inside a block must be explicitly called on the builder object (or the object passed into the block) to avoid ambiguity. Use the block argument to make this cleaner.

    Example Usage

    xml = Builder::XmlMarkup.new
    
    # Simple tags
    xml.em("emphasized")             # => <em>emphasized</em>
    xml.a("A Link", "href"=>"http://example.org") # => <a href="http://example.org">A Link</a>
    
    # Nested tags with blocks
    xml.div {
      xml.p("paragraph")
    }
    # => <div><p>paragraph</p></div>
    
    # Using the block argument (recommended)
    xml.div do |xml| 
      xml.strong("bold")
    end
    # => <div><strong>bold</strong></div>
    
    # Namespaces
    xml.SOAP :Envelope do |xml|
      xml.Body
    end
    # => <SOAP:Envelope><Body></Body></SOAP:Envelope>
    
    # Handling special characters
    xml.tag!("SOAP:Envelope", "attr" => "val")
    # => <SOAP:Envelope attr="val"></SOAP:Envelope>
    require 'builder/xmlmarkup'
    
    xml = Builder::XmlMarkup.new
    xml.div {
      xml.p("Hello World")
    }
  8. Initialize an XML builder via XmlBase

    master

    The XmlBase class (the foundation for Builder::XmlMarkup and Builder::XmlEvents) is used to create XML markup. When initializing, you can control indentation, the starting indentation level, and the character encoding.

    Parameters:

    • indent: Number of spaces used for indentation. Setting this to 0 disables indentation and line breaks.
    • initial: The starting indentation level.
    • encoding: The character encoding (defaults to 'utf-8').

    Note: When encoding and $KCODE are set to 'utf-8', characters are not converted to character entities in the output stream.

    # Example conceptual initialization
    # (Actual usage typically involves subclasses like XmlMarkup)
    builder = Builder::XmlBase.new(2, 0, 'utf-8')
  9. Generate XML to a specific target with indentation

    master

    You can configure Builder::XmlMarkup to output to a specific target (e.g., STDOUT) and set an indentation level using keyword arguments in the constructor.

    require 'rubygems'
    require_gem 'builder'
    
    builder = Builder::XmlMarkup.new(:target=>STDOUT, :indent=>2)
    builder.person { |b| b.name("Jim"); b.phone("555-1234") }
    #
    # Prints:
    # <person>
    #   <name>Jim</name>
    #   <phone>555-1234</phone>
    # </person>
  10. Use XML comments, instructions, and declarations

    master

    Builder supports several specialized XML constructs:

    • Comments: Use comment!.
    • Processing Instructions: Use instruct!. If the instruction is :xml, it defaults to version 1.0 and UTF-8 encoding.
    • Entity Declarations: Use declare!.
      • Symbols are inserted without quotes.
      • Strings are inserted with double quotes.
      • You can nest declare! calls within a block to create internal DTD subsets.
    # Comments
    xml_markup.comment! "This is a comment"
    
    # Processing Instructions
    xml_markup.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
    
    # Entity Declarations
    xml_markup.declare! :DOCTYPE, :chapter, :SYSTEM, "../dtds/chapter.dtd"
    
    # Nested Declarations
    xml_markup.declare! :DOCTYPE, :chapter do |x|
      x.declare! :ELEMENT, :chapter, :"(title,para+)"
    end
  11. Use XML namespaces with Builder

    master

    To produce a namespace prefix, pass a symbol as the first argument to a tag call. Ensure there is a space before the colon in your code to correctly map to the prefix:tag format.

    # Produces <SOAP:Envelope>...</SOAP:Envelope>
    xml.SOAP :Envelope do
      # ...
    end
  12. Configure Builder::XmlMarkup options

    master

    When initializing Builder::XmlMarkup.new(options), you can pass an options hash to control the output behavior:

    • :target: The object that receives the markup (must respond to <<). Defaults to a plain string. You can provide a string buffer, STDOUT, or another XmlMarkup instance.
    • :indent: The number of spaces used for indentation. Defaults to 0 (no indentation).
    • :margin: The initial indentation level (in levels of spaces).
    • :quote: Set to :single to use single quotes for attributes instead of the default double quotes.

    Example Configuration

    # Output to a string with 2-space indentation
    xml = Builder::XmlMarkup.new(:indent => 2)
    
    # Output to STDOUT with single quotes for attributes
    xml = Builder::XmlMarkup.new(:target => $stdout, :quote => :single)
    
    # Output to a specific buffer
    buffer = ""
    xml = Builder::XmlMarkup.new(buffer)
    xml.tag!("root")
    # buffer now contains '<root></root>'
    xml = Builder::XmlMarkup.new(:indent => 2, :quote => :single)