Class: Makiri::XML::Builder
- Inherits:
-
Object
- Object
- Makiri::XML::Builder
- Defined in:
- lib/makiri/xml/builder.rb
Overview
A Nokogiri-compatible DSL for building an XML document (or subtree) from scratch. It is a thin, pure-Ruby layer over the public construction surface (XML::Document.new, Document#create_element / #create_text_node / #create_cdata / #create_comment, and Node#add_child); no C code is involved.
An element is created by calling a method named after the tag. Trailing arguments follow the Nokogiri convention: a Hash sets attributes (including xmlns / xmlns:prefix namespace declarations), any other argument becomes the element’s text content, and a block builds nested children.
Tag names that collide with a Ruby/Kernel method (or with one of this builder’s own helpers below - text, cdata, comment, doc, parent, to_xml, to_s, descend) must be written with a trailing underscore, which is stripped: xml.id_(“9”) produces <id>9</id>. This matches Nokogiri.
A namespace prefix is selected for the next element with []: xml.title produces <dc:title> (the prefix must be in scope, i.e. declared via an “xmlns:dc” attribute on an ancestor or on the element itself, exactly as Makiri resolves prefixes at insertion time).
Defined Under Namespace
Classes: NodeBuilder
Instance Attribute Summary collapse
-
#doc ⇒ Object
readonly
The document being built (a Document).
-
#parent ⇒ Object
readonly
The node new children are currently appended to.
Class Method Summary collapse
-
.with(node, &block) ⇒ Builder
Build into an existing node: top-level calls append to
node, usingnode‘s document.
Instance Method Summary collapse
-
#<<(string) ⇒ self
Parse
stringas an XML fragment (against the document’s in-scope namespaces) and append its children to the current parent. -
#[](ns_prefix) ⇒ self
Select the namespace prefix for the next element (consumed by the next tag method).
-
#cdata(string) ⇒ NodeBuilder
Append a CDATA section to the current parent.
-
#comment(string) ⇒ NodeBuilder
Append a comment node to the current parent.
-
#descend(node, &block) ⇒ Object
Run
blockwithnodeas the current parent, restoring the previous parent afterward (even if the block raises) and returning the block’s value. -
#initialize(options = {}, root = nil) {|self| ... } ⇒ Builder
constructor
A new instance of Builder.
-
#method_missing(name, *args, &block) ⇒ Object
Any other method name is a tag: create the element and insert it.
-
#respond_to_missing?(_name, _include_private = false) ⇒ Boolean
Tag methods are open-ended, so report respond_to? truthfully for them (anything that is not already a real method is a candidate tag).
-
#text(string) ⇒ NodeBuilder
Append a text node to the current parent.
-
#to_xml ⇒ Object
(also: #to_s)
Serialize the built document.
Constructor Details
#initialize(options = {}, root = nil) {|self| ... } ⇒ Builder
Returns a new instance of Builder.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/makiri/xml/builder.rb', line 55 def initialize( = {}, root = nil, &block) if root @doc = root.document @parent = root else @parent = @doc = Makiri::XML::Document.new end @ns_prefix = nil @arity = nil return unless block run(&block) @parent = @doc # like Nokogiri: after a build block, settle back at the document end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *args, &block) ⇒ Object
Any other method name is a tag: create the element and insert it.
122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/makiri/xml/builder.rb', line 122 def method_missing(name, *args, &block) tag = name.to_s.sub(/[_!]\z/, "") prefix = @ns_prefix if prefix tag = "#{prefix}:#{tag}" @ns_prefix = nil end node = create_element(tag, args) check_prefix_defined!(node, prefix) if prefix insert(node, &block) end |
Instance Attribute Details
#doc ⇒ Object (readonly)
The document being built (a Document).
42 43 44 |
# File 'lib/makiri/xml/builder.rb', line 42 def doc @doc end |
#parent ⇒ Object (readonly)
The node new children are currently appended to. While a nested block is running this is that block’s element; otherwise it is #doc.
46 47 48 |
# File 'lib/makiri/xml/builder.rb', line 46 def parent @parent end |
Class Method Details
.with(node, &block) ⇒ Builder
Build into an existing node: top-level calls append to node, using node‘s document. Mirrors Nokogiri::XML::Builder.with.
75 76 77 |
# File 'lib/makiri/xml/builder.rb', line 75 def self.with(node, &block) new({}, node, &block) end |
Instance Method Details
#<<(string) ⇒ self
Parse string as an XML fragment (against the document’s in-scope namespaces) and append its children to the current parent. The Builder analogue of Nokogiri::XML::Builder#<<.
101 102 103 104 |
# File 'lib/makiri/xml/builder.rb', line 101 def <<(string) @doc.fragment(string).children.to_a.each { |child| insert(child) } self end |
#[](ns_prefix) ⇒ self
Select the namespace prefix for the next element (consumed by the next tag method). Returns self so it reads as xml.title.
109 110 111 112 |
# File 'lib/makiri/xml/builder.rb', line 109 def [](ns_prefix) @ns_prefix = ns_prefix.to_s self end |
#cdata(string) ⇒ NodeBuilder
Append a CDATA section to the current parent.
87 88 89 |
# File 'lib/makiri/xml/builder.rb', line 87 def cdata(string) insert(@doc.create_cdata(string.to_s)) end |
#comment(string) ⇒ NodeBuilder
Append a comment node to the current parent.
93 94 95 |
# File 'lib/makiri/xml/builder.rb', line 93 def comment(string) insert(@doc.create_comment(string.to_s)) end |
#descend(node, &block) ⇒ Object
Run block with node as the current parent, restoring the previous parent afterward (even if the block raises) and returning the block’s value. The single place @parent is pushed/popped - shared by #insert and by NodeBuilder‘s nested-block chain, so neither manipulates the parent state directly. Public so NodeBuilder (a separate class) can reuse it without reaching into a private method.
146 147 148 149 150 151 152 153 154 |
# File 'lib/makiri/xml/builder.rb', line 146 def descend(node, &block) previous = @parent @parent = node begin run(&block) ensure @parent = previous end end |
#respond_to_missing?(_name, _include_private = false) ⇒ Boolean
Tag methods are open-ended, so report respond_to? truthfully for them (anything that is not already a real method is a candidate tag).
136 137 138 |
# File 'lib/makiri/xml/builder.rb', line 136 def respond_to_missing?(_name, _include_private = false) true end |
#text(string) ⇒ NodeBuilder
Append a text node to the current parent.
81 82 83 |
# File 'lib/makiri/xml/builder.rb', line 81 def text(string) insert(@doc.create_text_node(string.to_s)) end |
#to_xml ⇒ Object Also known as: to_s
Serialize the built document. Forwards to NodeMethods#to_xml (so pretty: works).
116 117 118 |
# File 'lib/makiri/xml/builder.rb', line 116 def to_xml(...) @doc.to_xml(...) end |