Class: Lutaml::Xml::Builder::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/lutaml/xml/builder/base.rb

Overview

Base builder for XML construction using moxml. All adapter-specific builders inherit from this class.

The builder creates XML documents through moxml’s document model. Declaration, doctype, indentation, and line endings are handled by moxml — no manual string assembly.

Direct Known Subclasses

Nokogiri, Oga, Ox, Rexml

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(doc, context, options = {}) ⇒ Base

Returns a new instance of Base.



78
79
80
81
82
83
84
# File 'lib/lutaml/xml/builder/base.rb', line 78

def initialize(doc, context, options = {})
  @doc = doc
  @context = context
  @encoding = options[:encoding]
  @current_stack = [doc]
  @declaration_mode = :none
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args) ⇒ Object



182
183
184
185
# File 'lib/lutaml/xml/builder/base.rb', line 182

def method_missing(method_name, *args, &)
  attrs = args.first.is_a?(Hash) ? args.first : {}
  create_and_add_element(method_name.to_s, attributes: attrs, &)
end

Instance Attribute Details

#declaration_modeObject

Returns the value of attribute declaration_mode.



76
77
78
# File 'lib/lutaml/xml/builder/base.rb', line 76

def declaration_mode
  @declaration_mode
end

#docObject (readonly)

Returns the value of attribute doc.



75
76
77
# File 'lib/lutaml/xml/builder/base.rb', line 75

def doc
  @doc
end

#encodingObject

Returns the value of attribute encoding.



76
77
78
# File 'lib/lutaml/xml/builder/base.rb', line 76

def encoding
  @encoding
end

Class Method Details

.build(options = {}) {|instance| ... } ⇒ Object

Yields:

  • (instance)


15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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
# File 'lib/lutaml/xml/builder/base.rb', line 15

def self.build(options = {})
  context = Moxml.new(moxml_backend)
  if Lutaml::Model::RuntimeCompatibility.opal?
    context.config.namespace_validation_mode = :lenient
  end

  encoding_value = options.delete(:encoding)
  context.config.default_indent = options.delete(:indent) if options.key?(:indent)
  context.config.default_line_ending = options.delete(:line_ending) if options.key?(:line_ending)

  doc = context.create_document

  # Capture doctype — added after root to avoid Ox incompatibility
  doctype = options.delete(:doctype)

  instance = new(doc, context, options)
  instance.encoding = encoding_value if encoding_value
  yield(instance) if block_given?

  # Add doctype before root (after build block sets root)
  if doctype && doc.root
    dt = doc.create_doctype(
      doctype[:name],
      doctype[:public_id],
      doctype[:system_id],
    )
    doc.add_child(dt)
  end

  # Handle declaration — configure it on the document so moxml
  # serializes it natively (works across all adapters)
  xml_decl = options.delete(:xml_declaration) || {}
  include_decl = options.delete(:include_declaration)
  force_decl = options.delete(:force_declaration)

  if include_decl
    version = xml_decl[:version] || "1.0"
    encoding = xml_decl[:encoding]
    encoding ||= "UTF-8" unless xml_decl[:had_declaration]
    standalone = xml_decl[:standalone]
    decl = doc.create_declaration(version, encoding, standalone)
    doc.add_child(decl)
    instance.declaration_mode = :default
  elsif force_decl
    decl_encoding = encoding_value || "UTF-8"
    decl = doc.create_declaration("1.0", decl_encoding, nil)
    doc.add_child(decl)
    instance.declaration_mode = :default
  else
    instance.declaration_mode = :none
  end

  instance
end

.moxml_backendObject

Override in subclass to set the moxml backend



71
72
73
# File 'lib/lutaml/xml/builder/base.rb', line 71

def self.moxml_backend
  nil
end

Instance Method Details

#add_cdata(element, value) ⇒ Object



131
132
133
# File 'lib/lutaml/xml/builder/base.rb', line 131

def add_cdata(element, value)
  resolve_target(element).add_child(@doc.create_cdata(value.to_s))
end

#add_comment(element_or_text, text = nil) ⇒ Object



135
136
137
138
139
140
141
142
143
144
# File 'lib/lutaml/xml/builder/base.rb', line 135

def add_comment(element_or_text, text = nil)
  if text.nil?
    target = current_element
    comment_text = element_or_text
  else
    target = resolve_target(element_or_text)
    comment_text = text
  end
  target.add_child(@doc.create_comment(comment_text.to_s))
end

#add_processing_instruction(target, content) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/lutaml/xml/builder/base.rb', line 146

def add_processing_instruction(target, content)
  pi = @doc.create_processing_instruction(target.to_s, content.to_s)
  if current_element.is_a?(Moxml::Document)
    root_node = current_element.root
    if root_node
      root_node.add_previous_sibling(pi)
    else
      current_element.add_child(pi)
    end
  else
    current_element.add_child(pi)
  end
  pi
end

#add_text(element, text_content, cdata: false) ⇒ Object



125
126
127
128
129
# File 'lib/lutaml/xml/builder/base.rb', line 125

def add_text(element, text_content, cdata: false)
  return add_cdata(element, text_content) if cdata

  resolve_target(element).add_child(@doc.create_text(text_content.to_s))
end

#add_xml_fragment(element, content) ⇒ Object



117
118
119
120
121
122
123
# File 'lib/lutaml/xml/builder/base.rb', line 117

def add_xml_fragment(element, content)
  target = resolve_target(element)
  parsed = @context.parse("<__root__>#{content}</__root__>")
  parsed.root&.children&.each { |child| target.add_child(child) }
rescue Moxml::ParseError
  target.add_child(@doc.create_text(content.to_s))
end

#cdata(content) ⇒ Object



165
166
167
# File 'lib/lutaml/xml/builder/base.rb', line 165

def cdata(content)
  add_cdata(current_element, content)
end

#create_and_add_element(element_name, prefix: (prefix_unset = true nil), attributes: {}, blank_xmlns: false) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/lutaml/xml/builder/base.rb', line 99

def create_and_add_element(
  element_name,
  prefix: (prefix_unset = true
           nil),
  attributes: {},
  blank_xmlns: false
)
  element_name = element_name.first if element_name.is_a?(Array)

  new_el = @doc.create_element(element_name)
  apply_attributes(new_el, attributes, blank_xmlns)
  resolve_namespace(new_el, prefix, prefix_unset)
  attach_to_parent(new_el, prefix, prefix_unset)
  with_element_context(new_el) { yield(self) } if block_given?

  new_el
end

#current_elementObject Also known as: current_node



86
87
88
# File 'lib/lutaml/xml/builder/base.rb', line 86

def current_element
  @current_stack.last
end

#parentObject



95
96
97
# File 'lib/lutaml/xml/builder/base.rb', line 95

def parent
  current_element
end

#respond_to_missing?(_method_name, _include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


187
188
189
# File 'lib/lutaml/xml/builder/base.rb', line 187

def respond_to_missing?(_method_name, _include_private = false)
  true
end

#text(content) ⇒ Object



161
162
163
# File 'lib/lutaml/xml/builder/base.rb', line 161

def text(content)
  add_text(current_element, content)
end

#to_xmlObject



169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/lutaml/xml/builder/base.rb', line 169

def to_xml
  return "" unless @doc.root

  result = if @declaration_mode == :none && !has_document_level_nodes?
             @doc.root.to_xml(declaration: false, expand_empty: false)
           else
             @doc.to_xml(declaration: @declaration_mode == :default, expand_empty: false)
           end

  result = result.encode(encoding) if encoding && result.encoding.to_s != encoding
  result
end

#xmlObject



91
92
93
# File 'lib/lutaml/xml/builder/base.rb', line 91

def xml
  self
end