Class: Xbuilder

Inherits:
BlankSlate
  • Object
show all
Defined in:
lib/xbuilder.rb

Overview

Usage

Xbuilder supports almost all of the Builder’s features. Here is a small example:

xml = Xbuilder.new(indent: 2)
xml.node attr: 1 do |xml|     #=> <node attr="1">
  xml.ns :child, attr: 2      #=>   <ns:child attr="2"/>
end                           #=> </node>

Direct Known Subclasses

XbuilderTemplate

Constant Summary collapse

XML =

:nodoc:

::LibXML::XML

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Xbuilder

Create an XML builder. Available options are:

:root

Root element. All nodes created by the builder will be attached to it.

:encoding

Document encoding, e.g. “UTF-8”. Will be used in XML instruction: <?xml version="1.0" encoding="UTF-8"?>

Builder compatibility options:

:indent

Number of spaces used for indentation. Default is 0.

:margin

Amount of initial indentation (specified in levels, not spaces).



28
29
30
31
32
33
34
35
36
37
# File 'lib/xbuilder.rb', line 28

def initialize(options = {})
  if options[:target]
    ::Kernel.raise ::ArgumentError, "':target' option is not supported."
  end

  @indent = options[:indent].to_i
  @margin = options[:margin].to_i
  @root = options[:root] || XML::Document.new
  @encoding = options[:encoding] || "UTF-8"
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object

Append a tag with the method’s name to the output.

xml.node { |xml| xml.child } #=> <node><child/></node>


41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/xbuilder.rb', line 41

def method_missing(name, *args, &block)
  name = "#{name}:#{args.shift}" if args.first.kind_of?(::Symbol)
  node = XML::Node.new(name.to_s)
  text = nil

  args.each do |arg|
    case arg
    when ::Hash
      arg.each do |key, val|
        k = key.to_s
        v = val.to_s
        node[k] = v
      end
    else
      text ||= ''
      text << arg.to_s
    end
  end

  if block && text
    ::Kernel.raise ::ArgumentError, "Cannot mix a text argument with a block"
  end

  # FIXME `__escape` is a temp solution till bugfix release.
  # https://github.com/xml4r/libxml-ruby/pull/46
  node.content = __escape(text) if text

  if block
    unless block.arity > 0
      ::Kernel.raise ::ArgumentError, "Provide at least 1 block argument: `xml.node { |xml| xml.child }'"
    end
    block.call(__new_instance(root: node))
  end

  __append_node(node)
end

Instance Method Details

#<<(text, escape = false) ⇒ Object

Append text to the output. Do not escape by default.

xml.node { xml << "unescaped & text" } #=> <node>unescaped & text</node>


97
98
99
100
# File 'lib/xbuilder.rb', line 97

def <<(text, escape = false)
  __ensure_no_block(::Kernel.block_given?)
  text!(text, escape)
end

#cdata!(text) ⇒ Object

Insert CDATA node.



139
140
141
142
143
# File 'lib/xbuilder.rb', line 139

def cdata!(text)
  __ensure_no_block(::Kernel.block_given?)
  node = XML::Node.new_cdata(text)
  __append_node(node)
end

#comment!(comment_text) ⇒ Object

Insert comment node.



120
121
122
123
124
# File 'lib/xbuilder.rb', line 120

def comment!(comment_text)
  __ensure_no_block(::Kernel.block_given?)
  node = XML::Node.new_comment(comment_text)
  __append_node(node)
end

#declare!(inst, *args, &block) ⇒ Object

XML declarations are not yet supported.



127
128
129
# File 'lib/xbuilder.rb', line 127

def declare!(inst, *args, &block)
  __warn("XML declarations are not yet supported. Pull requests are welcome!")
end

#instruct!(*args) ⇒ Object

Custom XML instructions are not supported. Left here for Builder API compatibility.



133
134
135
136
# File 'lib/xbuilder.rb', line 133

def instruct!(*args)
  # TODO should we switch XML instruction off if `instruct!` is not called?
  __warn("Custom XML instructions are not supported")
end

#tag!(name, *args, &block) ⇒ Object

Append a tag to the output. The first argument is a tag name. The rest of arguments are the same as method_missing ones.

xml.tag!("node") { |xml| xml.tag!("child") } #=> <node><child/></node>


81
82
83
# File 'lib/xbuilder.rb', line 81

def tag!(name, *args, &block)
  method_missing(name, *args, &block)
end

#target!Object

Returns the target XML string.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/xbuilder.rb', line 103

def target!
  # FIXME Temp solution for encoding constant lookup.
  # (till bugfix release https://github.com/xml4r/libxml-ruby/pull/45 to be published)
  const_name = @encoding.upcase.gsub!("-", "_")
  encoding = XML::Encoding.const_get(const_name)

  XML.indent_tree_output = (@indent > 0)
  XML.default_tree_indent_string = (" " * @indent)

  @root.to_s(encoding: encoding, indent: XML.indent_tree_output).tap do |xml|
    if @margin > 0
      xml.gsub!(/^/, (" " * @indent) * @margin)
    end
  end
end

#text!(text, escape = true) ⇒ Object

Append text to the output. Escape by default.

xml.node { xml.text!("escaped & text") } #=> <node>escaped &amp; text</node>


88
89
90
91
92
93
# File 'lib/xbuilder.rb', line 88

def text!(text, escape = true)
  __ensure_no_block(::Kernel.block_given?)
  node = XML::Node.new_text(text)
  node.output_escaping = escape
  __append_node(node)
end