moxml 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/main.yml +27 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +8 -0
- data/Gemfile +12 -0
- data/README.adoc +770 -0
- data/Rakefile +12 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/lib/moxml/adapter.rb +175 -0
- data/lib/moxml/attribute.rb +30 -0
- data/lib/moxml/cdata.rb +39 -0
- data/lib/moxml/comment.rb +35 -0
- data/lib/moxml/config.rb +23 -0
- data/lib/moxml/declaration.rb +50 -0
- data/lib/moxml/document.rb +99 -0
- data/lib/moxml/element.rb +145 -0
- data/lib/moxml/error_handler.rb +77 -0
- data/lib/moxml/errors.rb +169 -0
- data/lib/moxml/namespace.rb +54 -0
- data/lib/moxml/node.rb +113 -0
- data/lib/moxml/node_set.rb +268 -0
- data/lib/moxml/processing_instruction.rb +44 -0
- data/lib/moxml/text.rb +39 -0
- data/lib/moxml/version.rb +5 -0
- data/lib/moxml.rb +44 -0
- data/moxml.gemspec +34 -0
- data/sig/moxml.rbs +4 -0
- metadata +74 -0
data/lib/moxml/errors.rb
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
# lib/moxml/errors.rb
|
2
|
+
module Moxml
|
3
|
+
# Base error class for all Moxml errors
|
4
|
+
class Error < StandardError; end
|
5
|
+
|
6
|
+
# Parsing related errors
|
7
|
+
class ParseError < Error
|
8
|
+
attr_reader :line, :column, :source
|
9
|
+
|
10
|
+
def initialize(message, line: nil, column: nil, source: nil)
|
11
|
+
@line = line
|
12
|
+
@column = column
|
13
|
+
@source = source
|
14
|
+
super(build_message(message))
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def build_message(message)
|
20
|
+
parts = [message]
|
21
|
+
parts << "Line: #{line}" if line
|
22
|
+
parts << "Column: #{column}" if column
|
23
|
+
parts << "\nSource: #{source}" if source
|
24
|
+
parts.join(" | ")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Validation errors
|
29
|
+
class ValidationError < Error; end
|
30
|
+
class DTDValidationError < ValidationError; end
|
31
|
+
class SchemaValidationError < ValidationError; end
|
32
|
+
class NamespaceError < ValidationError; end
|
33
|
+
|
34
|
+
# Structure errors
|
35
|
+
class MalformedXMLError < ParseError; end
|
36
|
+
class UnbalancedTagError < ParseError; end
|
37
|
+
class UndefinedEntityError < ParseError; end
|
38
|
+
class DuplicateAttributeError < ParseError; end
|
39
|
+
|
40
|
+
# Encoding errors
|
41
|
+
class EncodingError < Error
|
42
|
+
attr_reader :encoding
|
43
|
+
|
44
|
+
def initialize(message, encoding)
|
45
|
+
@encoding = encoding
|
46
|
+
super("#{message} (Encoding: #{encoding})")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Security related errors
|
51
|
+
class SecurityError < Error; end
|
52
|
+
class MaxDepthExceededError < SecurityError; end
|
53
|
+
class MaxAttributesExceededError < SecurityError; end
|
54
|
+
class MaxNameLengthExceededError < SecurityError; end
|
55
|
+
class EntityExpansionError < SecurityError; end
|
56
|
+
|
57
|
+
# Backend errors
|
58
|
+
class BackendError < Error
|
59
|
+
attr_reader :backend
|
60
|
+
|
61
|
+
def initialize(message, backend)
|
62
|
+
@backend = backend
|
63
|
+
super("#{message} (Backend: #{backend})")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class BackendNotFoundError < BackendError; end
|
68
|
+
class BackendConfigurationError < BackendError; end
|
69
|
+
|
70
|
+
# Node manipulation errors
|
71
|
+
class NodeError < Error
|
72
|
+
attr_reader :node
|
73
|
+
|
74
|
+
def initialize(message, node)
|
75
|
+
@node = node
|
76
|
+
super("#{message} (Node: #{node.class})")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class InvalidNodeTypeError < NodeError; end
|
81
|
+
class InvalidOperationError < NodeError; end
|
82
|
+
class NodeNotFoundError < NodeError; end
|
83
|
+
|
84
|
+
# Visitor pattern errors
|
85
|
+
class VisitorError < Error; end
|
86
|
+
|
87
|
+
class InvalidSelectorError < VisitorError
|
88
|
+
attr_reader :selector
|
89
|
+
|
90
|
+
def initialize(message, selector)
|
91
|
+
@selector = selector
|
92
|
+
super("#{message} (Selector: #{selector})")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class VisitorMethodError < VisitorError
|
97
|
+
attr_reader :method_name
|
98
|
+
|
99
|
+
def initialize(message, method_name)
|
100
|
+
@method_name = method_name
|
101
|
+
super("#{message} (Method: #{method_name})")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Serialization errors
|
106
|
+
class SerializationError < Error; end
|
107
|
+
|
108
|
+
class InvalidOptionsError < SerializationError
|
109
|
+
attr_reader :options
|
110
|
+
|
111
|
+
def initialize(message, options)
|
112
|
+
@options = options
|
113
|
+
super("#{message} (Options: #{options})")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# IO errors
|
118
|
+
class IOError < Error
|
119
|
+
attr_reader :path
|
120
|
+
|
121
|
+
def initialize(message, path)
|
122
|
+
@path = path
|
123
|
+
super("#{message} (Path: #{path})")
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class FileNotFoundError < IOError; end
|
128
|
+
class WriteError < IOError; end
|
129
|
+
|
130
|
+
# Memory errors
|
131
|
+
class MemoryError < Error
|
132
|
+
attr_reader :size
|
133
|
+
|
134
|
+
def initialize(message, size)
|
135
|
+
@size = size
|
136
|
+
super("#{message} (Size: #{size} bytes)")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class DocumentTooLargeError < MemoryError; end
|
141
|
+
|
142
|
+
# CDATA related errors
|
143
|
+
class CDATAError < Error; end
|
144
|
+
class NestedCDATAError < CDATAError; end
|
145
|
+
class InvalidCDATAContentError < CDATAError; end
|
146
|
+
|
147
|
+
# Namespace related errors
|
148
|
+
class NamespaceDeclarationError < Error
|
149
|
+
attr_reader :prefix, :uri
|
150
|
+
|
151
|
+
def initialize(message, prefix, uri)
|
152
|
+
@prefix = prefix
|
153
|
+
@uri = uri
|
154
|
+
super("#{message} (Prefix: #{prefix}, URI: #{uri})")
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# XPath related errors
|
159
|
+
class XPathError < Error
|
160
|
+
attr_reader :expression
|
161
|
+
|
162
|
+
def initialize(message, expression)
|
163
|
+
@expression = expression
|
164
|
+
super("#{message} (Expression: #{expression})")
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class InvalidXPathError < XPathError; end
|
169
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Moxml
|
2
|
+
class Namespace < Node
|
3
|
+
def initialize(prefix_or_native = nil, uri = nil)
|
4
|
+
case prefix_or_native
|
5
|
+
when String
|
6
|
+
super(adapter.create_namespace(nil, prefix_or_native, uri))
|
7
|
+
else
|
8
|
+
super(prefix_or_native)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def prefix
|
13
|
+
adapter.namespace_prefix(native)
|
14
|
+
end
|
15
|
+
|
16
|
+
def prefix=(new_prefix)
|
17
|
+
adapter.set_namespace_prefix(native, new_prefix)
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def uri
|
22
|
+
adapter.namespace_uri(native)
|
23
|
+
end
|
24
|
+
|
25
|
+
def uri=(new_uri)
|
26
|
+
adapter.set_namespace_uri(native, new_uri)
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def blank?
|
31
|
+
uri.nil? || uri.empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
def namespace?
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
def ==(other)
|
39
|
+
other.is_a?(Namespace) &&
|
40
|
+
other.prefix == prefix &&
|
41
|
+
other.uri == uri
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
prefix ? "xmlns:#{prefix}='#{uri}'" : "xmlns='#{uri}'"
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def create_native_node
|
51
|
+
adapter.create_namespace(nil, "", "")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/moxml/node.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
module Moxml
|
2
|
+
class Node
|
3
|
+
attr_reader :native
|
4
|
+
|
5
|
+
def initialize(native_node = nil)
|
6
|
+
@native = native_node || create_native_node
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.wrap(native_node)
|
10
|
+
return nil if native_node.nil?
|
11
|
+
|
12
|
+
klass = case Moxml.adapter.node_type(native_node)
|
13
|
+
when :element then Element
|
14
|
+
when :text then Text
|
15
|
+
when :cdata then Cdata
|
16
|
+
when :comment then Comment
|
17
|
+
when :processing_instruction then ProcessingInstruction
|
18
|
+
when :document then Document
|
19
|
+
when :attribute then Attribute
|
20
|
+
when :namespace then Namespace
|
21
|
+
else
|
22
|
+
raise Error, "Unknown node type: #{native_node.class}"
|
23
|
+
end
|
24
|
+
|
25
|
+
klass.new(native_node)
|
26
|
+
end
|
27
|
+
|
28
|
+
def parent
|
29
|
+
wrap_node(adapter.parent(native))
|
30
|
+
end
|
31
|
+
|
32
|
+
def children
|
33
|
+
NodeSet.new(adapter.children(native))
|
34
|
+
end
|
35
|
+
|
36
|
+
def next_sibling
|
37
|
+
wrap_node(adapter.next_sibling(native))
|
38
|
+
end
|
39
|
+
|
40
|
+
def previous_sibling
|
41
|
+
wrap_node(adapter.previous_sibling(native))
|
42
|
+
end
|
43
|
+
|
44
|
+
def remove
|
45
|
+
adapter.remove(native)
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def replace(node)
|
50
|
+
adapter.replace(native, node.native)
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_previous_sibling(node)
|
55
|
+
adapter.add_previous_sibling(native, node.native)
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_next_sibling(node)
|
60
|
+
adapter.add_next_sibling(native, node.native)
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
def text
|
65
|
+
adapter.text_content(native)
|
66
|
+
end
|
67
|
+
|
68
|
+
def text=(content)
|
69
|
+
adapter.set_text_content(native, content)
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
def inner_html
|
74
|
+
adapter.inner_html(native)
|
75
|
+
end
|
76
|
+
|
77
|
+
def inner_html=(html)
|
78
|
+
adapter.set_inner_html(native, html)
|
79
|
+
end
|
80
|
+
|
81
|
+
def outer_html
|
82
|
+
adapter.outer_html(native)
|
83
|
+
end
|
84
|
+
|
85
|
+
def path
|
86
|
+
adapter.path(native)
|
87
|
+
end
|
88
|
+
|
89
|
+
def line
|
90
|
+
adapter.line(native)
|
91
|
+
end
|
92
|
+
|
93
|
+
def column
|
94
|
+
adapter.column(native)
|
95
|
+
end
|
96
|
+
|
97
|
+
protected
|
98
|
+
|
99
|
+
def wrap_node(native_node)
|
100
|
+
self.class.wrap(native_node)
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def adapter
|
106
|
+
Moxml.adapter
|
107
|
+
end
|
108
|
+
|
109
|
+
def create_native_node
|
110
|
+
raise NotImplementedError, "Subclasses must implement create_native_node"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,268 @@
|
|
1
|
+
# lib/moxml/node_set.rb
|
2
|
+
module Moxml
|
3
|
+
class NodeSet
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
attr_reader :native_nodes
|
7
|
+
|
8
|
+
def initialize(native_nodes = [])
|
9
|
+
@native_nodes = Array(native_nodes)
|
10
|
+
end
|
11
|
+
|
12
|
+
def each
|
13
|
+
return enum_for(:each) unless block_given?
|
14
|
+
native_nodes.each { |node| yield Node.wrap(node) }
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def [](index)
|
19
|
+
case index
|
20
|
+
when Integer
|
21
|
+
Node.wrap(native_nodes[index])
|
22
|
+
when Range
|
23
|
+
NodeSet.new(native_nodes[index])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def first
|
28
|
+
Node.wrap(native_nodes.first)
|
29
|
+
end
|
30
|
+
|
31
|
+
def last
|
32
|
+
Node.wrap(native_nodes.last)
|
33
|
+
end
|
34
|
+
|
35
|
+
def empty?
|
36
|
+
native_nodes.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
def size
|
40
|
+
native_nodes.size
|
41
|
+
end
|
42
|
+
|
43
|
+
alias length size
|
44
|
+
|
45
|
+
def to_a
|
46
|
+
map { |node| node }
|
47
|
+
end
|
48
|
+
|
49
|
+
def filter(selector)
|
50
|
+
NodeSet.new(
|
51
|
+
native_nodes.select { |node| Moxml.adapter.matches?(node, selector) }
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
def remove
|
56
|
+
each(&:remove)
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def text
|
61
|
+
map(&:text).join
|
62
|
+
end
|
63
|
+
|
64
|
+
def inner_html
|
65
|
+
map(&:inner_html).join
|
66
|
+
end
|
67
|
+
|
68
|
+
def wrap(html_or_element)
|
69
|
+
each do |node|
|
70
|
+
wrapper = case html_or_element
|
71
|
+
when String
|
72
|
+
Document.parse("<div>#{html_or_element}</div>").root.children.first
|
73
|
+
when Element
|
74
|
+
html_or_element.dup
|
75
|
+
else
|
76
|
+
raise ArgumentError, "Expected String or Element"
|
77
|
+
end
|
78
|
+
|
79
|
+
node.add_previous_sibling(wrapper)
|
80
|
+
wrapper.add_child(node)
|
81
|
+
end
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
def add_class(names)
|
86
|
+
each do |node|
|
87
|
+
next unless node.is_a?(Element)
|
88
|
+
current = (node["class"] || "").split(/\s+/)
|
89
|
+
new_classes = names.is_a?(Array) ? names : names.split(/\s+/)
|
90
|
+
node["class"] = (current + new_classes).uniq.join(" ")
|
91
|
+
end
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
def remove_class(names)
|
96
|
+
each do |node|
|
97
|
+
next unless node.is_a?(Element)
|
98
|
+
current = (node["class"] || "").split(/\s+/)
|
99
|
+
remove_classes = names.is_a?(Array) ? names : names.split(/\s+/)
|
100
|
+
node["class"] = (current - remove_classes).join(" ")
|
101
|
+
end
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
def attr(name, value = nil)
|
106
|
+
if value.nil?
|
107
|
+
first&.[](name)
|
108
|
+
else
|
109
|
+
each { |node| node[name] = value if node.is_a?(Element) }
|
110
|
+
self
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Collection operations
|
115
|
+
def +(other)
|
116
|
+
NodeSet.new(native_nodes + other.native_nodes)
|
117
|
+
end
|
118
|
+
|
119
|
+
def -(other)
|
120
|
+
NodeSet.new(native_nodes - other.native_nodes)
|
121
|
+
end
|
122
|
+
|
123
|
+
def &(other)
|
124
|
+
NodeSet.new(native_nodes & other.native_nodes)
|
125
|
+
end
|
126
|
+
|
127
|
+
def |(other)
|
128
|
+
NodeSet.new(native_nodes | other.native_nodes)
|
129
|
+
end
|
130
|
+
|
131
|
+
def uniq
|
132
|
+
NodeSet.new(native_nodes.uniq)
|
133
|
+
end
|
134
|
+
|
135
|
+
def reverse
|
136
|
+
NodeSet.new(native_nodes.reverse)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Search and filtering
|
140
|
+
def find_by_id(id)
|
141
|
+
detect { |node| node.is_a?(Element) && node["id"] == id }
|
142
|
+
end
|
143
|
+
|
144
|
+
def find_by_class(class_name)
|
145
|
+
select { |node| node.is_a?(Element) && (node["class"] || "").split(/\s+/).include?(class_name) }
|
146
|
+
end
|
147
|
+
|
148
|
+
def find_by_attribute(name, value = nil)
|
149
|
+
select do |node|
|
150
|
+
next unless node.is_a?(Element)
|
151
|
+
if value.nil?
|
152
|
+
node.attributes.key?(name)
|
153
|
+
else
|
154
|
+
node[name] == value
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def of_type(type)
|
160
|
+
select { |node| node.is_a?(type) }
|
161
|
+
end
|
162
|
+
|
163
|
+
# DOM Manipulation
|
164
|
+
def before(node_or_nodes)
|
165
|
+
each { |node| node.add_previous_sibling(node_or_nodes) }
|
166
|
+
self
|
167
|
+
end
|
168
|
+
|
169
|
+
def after(node_or_nodes)
|
170
|
+
each { |node| node.add_next_sibling(node_or_nodes) }
|
171
|
+
self
|
172
|
+
end
|
173
|
+
|
174
|
+
def replace_with(node_or_nodes)
|
175
|
+
each { |node| node.replace(node_or_nodes) }
|
176
|
+
self
|
177
|
+
end
|
178
|
+
|
179
|
+
def wrap_all(wrapper)
|
180
|
+
return self if empty?
|
181
|
+
|
182
|
+
wrapper_node = case wrapper
|
183
|
+
when String
|
184
|
+
Document.parse(wrapper).root
|
185
|
+
when Element
|
186
|
+
wrapper
|
187
|
+
else
|
188
|
+
raise ArgumentError, "Expected String or Element"
|
189
|
+
end
|
190
|
+
|
191
|
+
first.add_previous_sibling(wrapper_node)
|
192
|
+
wrapper_node.add_child(self)
|
193
|
+
self
|
194
|
+
end
|
195
|
+
|
196
|
+
# Content manipulation
|
197
|
+
def inner_text=(text)
|
198
|
+
each { |node| node.inner_text = text }
|
199
|
+
self
|
200
|
+
end
|
201
|
+
|
202
|
+
def inner_html=(html)
|
203
|
+
each { |node| node.inner_html = html }
|
204
|
+
self
|
205
|
+
end
|
206
|
+
|
207
|
+
# Attribute operations
|
208
|
+
def toggle_class(names)
|
209
|
+
names = names.split(/\s+/) if names.is_a?(String)
|
210
|
+
each do |node|
|
211
|
+
next unless node.is_a?(Element)
|
212
|
+
current = (node["class"] || "").split(/\s+/)
|
213
|
+
names.each do |name|
|
214
|
+
if current.include?(name)
|
215
|
+
current.delete(name)
|
216
|
+
else
|
217
|
+
current << name
|
218
|
+
end
|
219
|
+
end
|
220
|
+
node["class"] = current.uniq.join(" ")
|
221
|
+
end
|
222
|
+
self
|
223
|
+
end
|
224
|
+
|
225
|
+
def has_class?(name)
|
226
|
+
any? { |node| node.is_a?(Element) && (node["class"] || "").split(/\s+/).include?(name) }
|
227
|
+
end
|
228
|
+
|
229
|
+
def remove_attr(*attrs)
|
230
|
+
each do |node|
|
231
|
+
next unless node.is_a?(Element)
|
232
|
+
attrs.each { |attr| node.remove_attribute(attr) }
|
233
|
+
end
|
234
|
+
self
|
235
|
+
end
|
236
|
+
|
237
|
+
# Position and hierarchy
|
238
|
+
def parents
|
239
|
+
NodeSet.new(
|
240
|
+
map { |node| node.parent }.compact.uniq
|
241
|
+
)
|
242
|
+
end
|
243
|
+
|
244
|
+
def children
|
245
|
+
NodeSet.new(
|
246
|
+
flat_map { |node| node.children.to_a }
|
247
|
+
)
|
248
|
+
end
|
249
|
+
|
250
|
+
def siblings
|
251
|
+
NodeSet.new(
|
252
|
+
flat_map { |node| node.parent ? node.parent.children.reject { |sibling| sibling == node } : [] }
|
253
|
+
).uniq
|
254
|
+
end
|
255
|
+
|
256
|
+
def next
|
257
|
+
NodeSet.new(
|
258
|
+
map { |node| node.next_sibling }.compact
|
259
|
+
)
|
260
|
+
end
|
261
|
+
|
262
|
+
def previous
|
263
|
+
NodeSet.new(
|
264
|
+
map { |node| node.previous_sibling }.compact
|
265
|
+
)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Moxml
|
2
|
+
class ProcessingInstruction < Node
|
3
|
+
def initialize(target_or_native = nil, content = nil)
|
4
|
+
case target_or_native
|
5
|
+
when String
|
6
|
+
super(adapter.create_processing_instruction(nil, target_or_native, content))
|
7
|
+
else
|
8
|
+
super(target_or_native)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def target
|
13
|
+
adapter.processing_instruction_target(native)
|
14
|
+
end
|
15
|
+
|
16
|
+
def target=(new_target)
|
17
|
+
adapter.set_processing_instruction_target(native, new_target)
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def content
|
22
|
+
adapter.processing_instruction_content(native)
|
23
|
+
end
|
24
|
+
|
25
|
+
def content=(new_content)
|
26
|
+
adapter.set_processing_instruction_content(native, new_content)
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def blank?
|
31
|
+
content.strip.empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
def processing_instruction?
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def create_native_node
|
41
|
+
adapter.create_processing_instruction(nil, "", "")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/moxml/text.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module Moxml
|
2
|
+
class Text < Node
|
3
|
+
def initialize(content_or_native = nil)
|
4
|
+
case content_or_native
|
5
|
+
when String
|
6
|
+
super(adapter.create_text(nil, content_or_native))
|
7
|
+
else
|
8
|
+
super(content_or_native)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def content
|
13
|
+
adapter.text_content(native)
|
14
|
+
end
|
15
|
+
|
16
|
+
def content=(text)
|
17
|
+
adapter.set_text_content(native, text)
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def blank?
|
22
|
+
content.strip.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
def cdata?
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
def text?
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def create_native_node
|
36
|
+
adapter.create_text(nil, "")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|