Class: Moxml::Builder

Inherits:
Object
  • Object
show all
Defined in:
lib/moxml/builder.rb

Constant Summary collapse

RESERVED_METHOD_PATTERN =
/\A(to_|as_json|marshal_|inspect|freeze|dup|clone)/

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(context) ⇒ Builder

Returns a new instance of Builder.



10
11
12
13
14
# File 'lib/moxml/builder.rb', line 10

def initialize(context)
  @context = context
  @current = @document = context.create_document
  @namespaces = {}
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

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

Dynamic element creation DSL. xml.schema(attrs) { } creates <schema> with those attributes. Uses yield so blocks preserve the caller’s self context. Supported call shapes: (), (String), (Hash), (String, Hash).

Raises:

  • (ArgumentError)


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

def method_missing(method_name, *args, &block)
  return super if RESERVED_METHOD_PATTERN.match?(method_name.to_s)

  text_content = args.first.is_a?(String) ? args.shift : nil
  attrs = args.first.is_a?(Hash) ? args.shift : {}

  raise ArgumentError, "unexpected arguments for #{method_name}: #{args.inspect}" unless args.empty?

  if text_content && block
    raise ArgumentError, "#{method_name}: cannot combine text content with a block"
  end

  # Strip trailing underscore to allow reserved Ruby method names as tags
  # (e.g., type_, class_, id_ become <type>, <class>, <id>)
  tag_name = method_name.to_s.chomp("_")

  create_element_node(tag_name, attrs, text_content: text_content,
                                       block: block, eval_block: false)
end

Instance Attribute Details

#documentObject (readonly) Also known as: doc

Returns the value of attribute document.



7
8
9
# File 'lib/moxml/builder.rb', line 7

def document
  @document
end

Instance Method Details

#build(&block) ⇒ Object



16
17
18
19
# File 'lib/moxml/builder.rb', line 16

def build(&block)
  instance_eval(&block)
  @document
end

#cdata(content) ⇒ Object



45
46
47
# File 'lib/moxml/builder.rb', line 45

def cdata(content)
  @current.add_child(@document.create_cdata(content))
end

#comment(content) ⇒ Object



49
50
51
# File 'lib/moxml/builder.rb', line 49

def comment(content)
  @current.add_child(@document.create_comment(content))
end

#declaration(version: "1.0", encoding: "UTF-8", standalone: nil) ⇒ Object



21
22
23
24
25
# File 'lib/moxml/builder.rb', line 21

def declaration(version: "1.0", encoding: "UTF-8", standalone: nil)
  @current.add_child(
    @document.create_declaration(version, encoding, standalone),
  )
end

#doctype(name, external_id = nil, system_id = nil) ⇒ Object

Convenience method for DOCTYPE



69
70
71
72
73
# File 'lib/moxml/builder.rb', line 69

def doctype(name, external_id = nil, system_id = nil)
  @current.add_child(
    @document.create_doctype(name, external_id, system_id),
  )
end

#element(name_or_attrs = nil, attributes = {}, &block) ⇒ Object

When called with a String name: creates element via instance_eval (DSL block context). When called with a Hash (e.g., element(name: “foo”)): creates <element> tag via yield — handles collision where “element” is both a builder method and a valid XML tag name (XSD/RelaxNG).

Raises:

  • (ArgumentError)


31
32
33
34
35
36
37
38
39
# File 'lib/moxml/builder.rb', line 31

def element(name_or_attrs = nil, attributes = {}, &block)
  if name_or_attrs.is_a?(Hash)
    return create_element_node("element", name_or_attrs, block: block, eval_block: false)
  end

  raise ArgumentError, "element requires a tag name" if name_or_attrs.nil?

  create_element_node(name_or_attrs, attributes, block: block, eval_block: true)
end

#elements(element_specs) ⇒ Object

Batch element creation



76
77
78
79
80
81
82
83
84
# File 'lib/moxml/builder.rb', line 76

def elements(element_specs)
  element_specs.each do |name, content_or_attrs|
    if content_or_attrs.is_a?(Hash)
      element(name, content_or_attrs)
    else
      element(name) { text(content_or_attrs) }
    end
  end
end

#entity_reference(name) ⇒ Object



53
54
55
# File 'lib/moxml/builder.rb', line 53

def entity_reference(name)
  @current.add_child(@document.create_entity_reference(name))
end

#namespace(prefix, uri) ⇒ Object



63
64
65
66
# File 'lib/moxml/builder.rb', line 63

def namespace(prefix, uri)
  @current.add_namespace(prefix, uri)
  @namespaces[prefix] = uri
end

#ns_element(namespace_uri, name, attributes = {}, &block) ⇒ Object

Helper for creating namespaced elements



87
88
89
90
91
92
# File 'lib/moxml/builder.rb', line 87

def ns_element(namespace_uri, name, attributes = {}, &block)
  el = element(name, attributes, &block)
  prefix = @namespaces.key(namespace_uri)
  el.namespace = { prefix => namespace_uri } if prefix
  el
end

#processing_instruction(target, content) ⇒ Object



57
58
59
60
61
# File 'lib/moxml/builder.rb', line 57

def processing_instruction(target, content)
  @current.add_child(
    @document.create_processing_instruction(target, content),
  )
end

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

Returns:

  • (Boolean)


118
119
120
121
122
# File 'lib/moxml/builder.rb', line 118

def respond_to_missing?(method_name, _include_private = false)
  return super if RESERVED_METHOD_PATTERN.match?(method_name.to_s)

  true
end

#text(content) ⇒ Object



41
42
43
# File 'lib/moxml/builder.rb', line 41

def text(content)
  @current.add_child(@document.create_text(content))
end