Class: Oga::XML::NodeSet

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/oga/xml/node_set.rb

Overview

The NodeSet class contains a set of unique Node instances that can be queried and modified. Optionally NodeSet instances can take ownership of a node (besides just containing it). This allows the nodes to query their previous and next elements.

There are two types of sets:

  1. Regular node sets
  2. Owned node sets

Both behave similar to Ruby's Array class. The difference between an owned and regular node set is that an owned set modifies nodes that are added or removed by certain operations. For example, when a node is added to an owned set the node_set attribute of said node points to the set it was just added to.

Owned node sets are used when building a DOM tree with Parser. By taking ownership of nodes in a set Oga makes it possible to use these sets as following:

document = Oga::XML::Document.new
element  = Oga::XML::Element.new

document.children << element

element.node_set == document.children # => true

If ownership was not handled then you'd have to manually set the element variable's node_set attribute after pushing it into a set.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(nodes = [], owner = nil) ⇒ NodeSet

Returns a new instance of NodeSet

Parameters:

  • nodes (Array) (defaults to: [])

    The nodes to add to the set.

  • owner (Oga::XML::NodeSet) (defaults to: nil)

    The owner of the set.


46
47
48
49
50
51
# File 'lib/oga/xml/node_set.rb', line 46

def initialize(nodes = [], owner = nil)
  @nodes = nodes.uniq
  @owner = owner

  @nodes.each { |node| take_ownership(node) }
end

Instance Attribute Details

#ownerOga::XML::Node

Returns:


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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/oga/xml/node_set.rb', line 37

class NodeSet
  include Enumerable

  attr_accessor :owner

  ##
  # @param [Array] nodes The nodes to add to the set.
  # @param [Oga::XML::NodeSet] owner The owner of the set.
  #
  def initialize(nodes = [], owner = nil)
    @nodes = nodes.uniq
    @owner = owner

    @nodes.each { |node| take_ownership(node) }
  end

  ##
  # Yields the supplied block for every node.
  #
  # @yieldparam [Oga::XML::Node]
  #
  def each
    @nodes.each { |node| yield node }
  end

  ##
  # Returns the last node in the set.
  #
  # @return [Oga::XML::Node]
  #
  def last
    return @nodes[-1]
  end

  ##
  # Returns `true` if the set is empty.
  #
  # @return [TrueClass|FalseClass]
  #
  def empty?
    return @nodes.empty?
  end

  ##
  # Returns the amount of nodes in the set.
  #
  # @return [Fixnum]
  #
  def length
    return @nodes.length
  end

  alias_method :count, :length
  alias_method :size, :length

  ##
  # Returns the index of the given node.
  #
  # @param [Oga::XML::Node] node
  # @return [Fixnum]
  #
  def index(node)
    return @nodes.index(node)
  end

  ##
  # Pushes the node at the end of the set.
  #
  # @param [Oga::XML::Node] node
  #
  def push(node)
    return if @nodes.include?(node)

    @nodes << node

    take_ownership(node)
  end

  alias_method :<<, :push

  ##
  # Pushes the node at the start of the set.
  #
  # @param [Oga::XML::Node] node
  #
  def unshift(node)
    return if @nodes.include?(node)

    @nodes.unshift(node)

    take_ownership(node)
  end

  ##
  # Shifts a node from the start of the set.
  #
  # @return [Oga::XML::Node]
  #
  def shift
    node = @nodes.shift

    remove_ownership(node)

    return node
  end

  ##
  # Pops a node from the end of the set.
  #
  # @return [Oga::XML::Node]
  #
  def pop
    node = @nodes.pop

    remove_ownership(node)

    return node
  end

  ##
  # Inserts a node into the set at the given index.
  #
  # @param [Fixnum] index The index to insert the node at.
  # @param [Oga::XML::Node] node
  #
  def insert(index, node)
    return if @nodes.include?(node)

    @nodes.insert(index, node)

    take_ownership(node)
  end

  ##
  # Returns the node for the given index.
  #
  # @param [Fixnum] index
  # @return [Oga::XML::Node]
  #
  def [](index)
    return @nodes[index]
  end

  ##
  # Converts the current set to an Array.
  #
  # @return [Array]
  #
  def to_a
    return @nodes
  end

  ##
  # Creates a new set based on the current and the specified set. The newly
  # created set does not inherit ownership rules of the current set.
  #
  # @param [Oga::XML::NodeSet] other
  # @return [Oga::XML::NodeSet]
  #
  def +(other)
    return self.class.new(to_a | other.to_a)
  end

  ##
  # Returns `true` if the current node set and the one given in `other` are
  # equal to each other.
  #
  # @param [Oga::XML::NodeSet] other
  #
  def ==(other)
    return other.is_a?(NodeSet) && other.equal_nodes?(@nodes)
  end

  ##
  # Returns `true` if the nodes given in `nodes` are equal to those
  # specified in the current `@nodes` variable. This method allows two
  # NodeSet instances to compare each other without the need of exposing
  # `@nodes` to the public.
  #
  # @param [Array<Oga::XML::Node>] nodes
  #
  def equal_nodes?(nodes)
    return @nodes == nodes
  end

  ##
  # Adds the nodes of the given node set to the current node set.
  #
  # @param [Oga::XML::NodeSet] other
  #
  def concat(other)
    other.each { |node| push(node) }
  end

  ##
  # Removes the current nodes from their owning set. The nodes are *not*
  # removed from the current set.
  #
  # This method is intended to remove nodes from an XML document/node.
  #
  def remove
    sets = []

    # First we gather all the sets to remove nodse from, then we remove the
    # actual nodes. This is done as you can not reliably remove elements
    # from an Array while iterating on that same Array.
    @nodes.each do |node|
      if node.node_set
        sets << node.node_set

        node.node_set = nil
      end
    end

    sets.each do |set|
      @nodes.each { |node| set.delete(node) }
    end
  end

  ##
  # Removes a node from the current set only.
  #
  def delete(node)
    removed = @nodes.delete(node)

    remove_ownership(removed) if removed

    return removed
  end

  ##
  # Returns the values of the given attribute.
  #
  # @param [String|Symbol] name The name of the attribute.
  # @return [Array]
  #
  def attribute(name)
    values = []

    @nodes.each do |node|
      if node.respond_to?(:attribute)
        values << node.attribute(name)
      end
    end

    return values
  end

  alias_method :attr, :attribute

  ##
  # Returns the text of all nodes in the set, ignoring comment nodes.
  #
  # @return [String]
  #
  def text
    text = ''

    @nodes.each do |node|
      if node.respond_to?(:text) and !node.is_a?(Comment)
        text << node.text
      end
    end

    return text
  end

  ##
  # @return [String]
  #
  def inspect
    values = @nodes.map(&:inspect).join(', ')

    return "NodeSet(#{values})"
  end

  private

  ##
  # Takes ownership of the given node. This only occurs when the current
  # set has an owner.
  #
  # @param [Oga::XML::Node] node
  #
  def take_ownership(node)
    node.node_set = self if owner
  end

  ##
  # Removes ownership of the node if it belongs to the current set.
  #
  # @param [Oga::XML::Node] node
  #
  def remove_ownership(node)
    node.node_set = nil if node.node_set == self
  end
end

Instance Method Details

#+(other) ⇒ Oga::XML::NodeSet

Creates a new set based on the current and the specified set. The newly created set does not inherit ownership rules of the current set.

Parameters:

Returns:


196
197
198
# File 'lib/oga/xml/node_set.rb', line 196

def +(other)
  return self.class.new(to_a | other.to_a)
end

#==(other) ⇒ Object

Returns true if the current node set and the one given in other are equal to each other.

Parameters:


206
207
208
# File 'lib/oga/xml/node_set.rb', line 206

def ==(other)
  return other.is_a?(NodeSet) && other.equal_nodes?(@nodes)
end

#[](index) ⇒ Oga::XML::Node

Returns the node for the given index.

Parameters:

  • index (Fixnum)

Returns:


176
177
178
# File 'lib/oga/xml/node_set.rb', line 176

def [](index)
  return @nodes[index]
end

#attribute(name) ⇒ Array Also known as: attr

Returns the values of the given attribute.

Parameters:

  • name (String|Symbol)

    The name of the attribute.

Returns:

  • (Array)

273
274
275
276
277
278
279
280
281
282
283
# File 'lib/oga/xml/node_set.rb', line 273

def attribute(name)
  values = []

  @nodes.each do |node|
    if node.respond_to?(:attribute)
      values << node.attribute(name)
    end
  end

  return values
end

#concat(other) ⇒ Object

Adds the nodes of the given node set to the current node set.

Parameters:


227
228
229
# File 'lib/oga/xml/node_set.rb', line 227

def concat(other)
  other.each { |node| push(node) }
end

#delete(node) ⇒ Object

Removes a node from the current set only.


259
260
261
262
263
264
265
# File 'lib/oga/xml/node_set.rb', line 259

def delete(node)
  removed = @nodes.delete(node)

  remove_ownership(removed) if removed

  return removed
end

#each {|| ... } ⇒ Object

Yields the supplied block for every node.

Yield Parameters:


58
59
60
# File 'lib/oga/xml/node_set.rb', line 58

def each
  @nodes.each { |node| yield node }
end

#empty?TrueClass|FalseClass

Returns true if the set is empty.

Returns:

  • (TrueClass|FalseClass)

76
77
78
# File 'lib/oga/xml/node_set.rb', line 76

def empty?
  return @nodes.empty?
end

#equal_nodes?(nodes) ⇒ Boolean

Returns true if the nodes given in nodes are equal to those specified in the current @nodes variable. This method allows two NodeSet instances to compare each other without the need of exposing @nodes to the public.

Parameters:

Returns:

  • (Boolean)

218
219
220
# File 'lib/oga/xml/node_set.rb', line 218

def equal_nodes?(nodes)
  return @nodes == nodes
end

#index(node) ⇒ Fixnum

Returns the index of the given node.

Parameters:

Returns:

  • (Fixnum)

98
99
100
# File 'lib/oga/xml/node_set.rb', line 98

def index(node)
  return @nodes.index(node)
end

#insert(index, node) ⇒ Object

Inserts a node into the set at the given index.

Parameters:

  • index (Fixnum)

    The index to insert the node at.

  • node (Oga::XML::Node)

162
163
164
165
166
167
168
# File 'lib/oga/xml/node_set.rb', line 162

def insert(index, node)
  return if @nodes.include?(node)

  @nodes.insert(index, node)

  take_ownership(node)
end

#inspectString

Returns:

  • (String)

307
308
309
310
311
# File 'lib/oga/xml/node_set.rb', line 307

def inspect
  values = @nodes.map(&:inspect).join(', ')

  return "NodeSet(#{values})"
end

#lastOga::XML::Node

Returns the last node in the set.

Returns:


67
68
69
# File 'lib/oga/xml/node_set.rb', line 67

def last
  return @nodes[-1]
end

#lengthFixnum Also known as: count, size

Returns the amount of nodes in the set.

Returns:

  • (Fixnum)

85
86
87
# File 'lib/oga/xml/node_set.rb', line 85

def length
  return @nodes.length
end

#popOga::XML::Node

Pops a node from the end of the set.

Returns:


148
149
150
151
152
153
154
# File 'lib/oga/xml/node_set.rb', line 148

def pop
  node = @nodes.pop

  remove_ownership(node)

  return node
end

#push(node) ⇒ Object Also known as: <<

Pushes the node at the end of the set.

Parameters:


107
108
109
110
111
112
113
# File 'lib/oga/xml/node_set.rb', line 107

def push(node)
  return if @nodes.include?(node)

  @nodes << node

  take_ownership(node)
end

#removeObject

Removes the current nodes from their owning set. The nodes are not removed from the current set.

This method is intended to remove nodes from an XML document/node.


237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/oga/xml/node_set.rb', line 237

def remove
  sets = []

  # First we gather all the sets to remove nodse from, then we remove the
  # actual nodes. This is done as you can not reliably remove elements
  # from an Array while iterating on that same Array.
  @nodes.each do |node|
    if node.node_set
      sets << node.node_set

      node.node_set = nil
    end
  end

  sets.each do |set|
    @nodes.each { |node| set.delete(node) }
  end
end

#remove_ownership(node) ⇒ Object (private)

Removes ownership of the node if it belongs to the current set.

Parameters:


330
331
332
# File 'lib/oga/xml/node_set.rb', line 330

def remove_ownership(node)
  node.node_set = nil if node.node_set == self
end

#shiftOga::XML::Node

Shifts a node from the start of the set.

Returns:


135
136
137
138
139
140
141
# File 'lib/oga/xml/node_set.rb', line 135

def shift
  node = @nodes.shift

  remove_ownership(node)

  return node
end

#take_ownership(node) ⇒ Object (private)

Takes ownership of the given node. This only occurs when the current set has an owner.

Parameters:


321
322
323
# File 'lib/oga/xml/node_set.rb', line 321

def take_ownership(node)
  node.node_set = self if owner
end

#textString

Returns the text of all nodes in the set, ignoring comment nodes.

Returns:

  • (String)

292
293
294
295
296
297
298
299
300
301
302
# File 'lib/oga/xml/node_set.rb', line 292

def text
  text = ''

  @nodes.each do |node|
    if node.respond_to?(:text) and !node.is_a?(Comment)
      text << node.text
    end
  end

  return text
end

#to_aArray

Converts the current set to an Array.

Returns:

  • (Array)

185
186
187
# File 'lib/oga/xml/node_set.rb', line 185

def to_a
  return @nodes
end

#unshift(node) ⇒ Object

Pushes the node at the start of the set.

Parameters:


122
123
124
125
126
127
128
# File 'lib/oga/xml/node_set.rb', line 122

def unshift(node)
  return if @nodes.include?(node)

  @nodes.unshift(node)

  take_ownership(node)
end