y-rb 0.3.2-x64-mingw32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/ext/yrb/Cargo.toml +21 -0
- data/ext/yrb/extconf.rb +6 -0
- data/ext/yrb/src/awareness.rs +425 -0
- data/ext/yrb/src/lib.rs +505 -0
- data/ext/yrb/src/utils.rs +34 -0
- data/ext/yrb/src/yany.rs +44 -0
- data/ext/yrb/src/yarray.rs +148 -0
- data/ext/yrb/src/yattrs.rs +48 -0
- data/ext/yrb/src/yawareness.rs +131 -0
- data/ext/yrb/src/ydoc.rs +35 -0
- data/ext/yrb/src/ymap.rs +151 -0
- data/ext/yrb/src/ytext.rs +204 -0
- data/ext/yrb/src/ytransaction.rs +55 -0
- data/ext/yrb/src/yvalue.rs +231 -0
- data/ext/yrb/src/yxml_element.rs +212 -0
- data/ext/yrb/src/yxml_text.rs +136 -0
- data/lib/2.7/yrb.so +0 -0
- data/lib/3.0/yrb.so +0 -0
- data/lib/y/array.rb +354 -0
- data/lib/y/awareness.rb +266 -0
- data/lib/y/doc.rb +220 -0
- data/lib/y/map.rb +202 -0
- data/lib/y/text.rb +372 -0
- data/lib/y/transaction.rb +143 -0
- data/lib/y/version.rb +5 -0
- data/lib/y/xml.rb +870 -0
- data/lib/y-rb.rb +23 -0
- data/lib/y.rb +3 -0
- metadata +136 -0
data/lib/y/xml.rb
ADDED
@@ -0,0 +1,870 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Y
|
4
|
+
# rubocop:disable Metrics/ClassLength
|
5
|
+
|
6
|
+
# A XMLElement
|
7
|
+
#
|
8
|
+
# Someone should not instantiate an element directly, but use
|
9
|
+
# {Y::Doc#get_xml_element} instead
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# doc = Y::Doc.new
|
13
|
+
# xml_element = doc.get_xml_element("my xml")
|
14
|
+
#
|
15
|
+
# puts xml_element.to_s
|
16
|
+
class XMLElement
|
17
|
+
# @!attribute [r] document
|
18
|
+
#
|
19
|
+
# @return [Y::Doc] The document this array belongs to
|
20
|
+
attr_accessor :document
|
21
|
+
|
22
|
+
# Create a new XMLElement instance
|
23
|
+
#
|
24
|
+
# @param [Y::Doc] doc
|
25
|
+
def initialize(doc = nil)
|
26
|
+
@document = doc || Y::Doc.new
|
27
|
+
|
28
|
+
super()
|
29
|
+
end
|
30
|
+
|
31
|
+
# Retrieve node at index
|
32
|
+
#
|
33
|
+
# @param [Integer] index
|
34
|
+
# @return [Y::XMLElement|nil]
|
35
|
+
def [](index)
|
36
|
+
node = yxml_element_get(index)
|
37
|
+
node&.document = document
|
38
|
+
node
|
39
|
+
end
|
40
|
+
|
41
|
+
# Create a node at index
|
42
|
+
#
|
43
|
+
# @param [Integer] index
|
44
|
+
# @param [String] name Name of node, e.g. `<p />`
|
45
|
+
# @return [Y::XMLElement]
|
46
|
+
# rubocop:disable Lint/Void
|
47
|
+
def []=(index, name)
|
48
|
+
node = yxml_element_insert_element(transaction, index, name)
|
49
|
+
node.document = document
|
50
|
+
node
|
51
|
+
end
|
52
|
+
# rubocop:enable Lint/Void
|
53
|
+
|
54
|
+
# Returns first child in list or nil if no child exists
|
55
|
+
#
|
56
|
+
# @return [Hash]
|
57
|
+
def attrs
|
58
|
+
yxml_element_attributes
|
59
|
+
end
|
60
|
+
|
61
|
+
alias attributes attrs
|
62
|
+
|
63
|
+
# Returns first child in list or nil if no child exists
|
64
|
+
#
|
65
|
+
# @return [Y::XMLElement]
|
66
|
+
def first_child
|
67
|
+
child = yxml_element_first_child
|
68
|
+
child&.document = document
|
69
|
+
child
|
70
|
+
end
|
71
|
+
|
72
|
+
# Insert text into element at given index
|
73
|
+
#
|
74
|
+
# Optional input is pushed to the text if provided
|
75
|
+
#
|
76
|
+
# @param [Integer] index
|
77
|
+
# @param [String|nil] input
|
78
|
+
# @return [Y::XMLText]
|
79
|
+
def insert_text(index, input = nil)
|
80
|
+
text = yxml_element_insert_text(transaction, index)
|
81
|
+
text.document = document
|
82
|
+
text << input unless input.nil?
|
83
|
+
text
|
84
|
+
end
|
85
|
+
|
86
|
+
# Retrieve element or text adjacent (next) to this element
|
87
|
+
#
|
88
|
+
# @return [Y::XMLElement|Y::XMLText|nil]
|
89
|
+
def next_sibling
|
90
|
+
node = yxml_element_next_sibling
|
91
|
+
node&.document = document
|
92
|
+
node
|
93
|
+
end
|
94
|
+
|
95
|
+
# Attach listener to get notified about changes to the element
|
96
|
+
#
|
97
|
+
# This supports either a `Proc` or a `Block`.
|
98
|
+
#
|
99
|
+
# @example Receive changes via Proc
|
100
|
+
# doc = Y::Doc.new
|
101
|
+
# xml_element = doc.get_xml_element("my xml element")
|
102
|
+
# xml_element.attach ->(changes) { … }
|
103
|
+
#
|
104
|
+
# @example Receive changes via Block
|
105
|
+
# doc = Y::Doc.new
|
106
|
+
# xml_element = doc.get_xml_element("my xml element")
|
107
|
+
# xml_element.attach { |changes| … }
|
108
|
+
#
|
109
|
+
# @param [Proc] callback
|
110
|
+
# @param [Block] block
|
111
|
+
# @return [Integer] The subscription ID
|
112
|
+
def attach(callback = nil, &block)
|
113
|
+
return yxml_element_observe(callback) unless callback.nil?
|
114
|
+
|
115
|
+
yxml_element_observe(block.to_proc) unless block.nil?
|
116
|
+
end
|
117
|
+
|
118
|
+
# Retrieve parent element
|
119
|
+
#
|
120
|
+
# @return [Y::XMLElement|nil]
|
121
|
+
def parent
|
122
|
+
node = yxml_element_parent
|
123
|
+
node.document = document
|
124
|
+
node
|
125
|
+
end
|
126
|
+
|
127
|
+
# Retrieve element or text adjacent (previous) to this element
|
128
|
+
#
|
129
|
+
# @return [Y::XMLElement|Y::XMLText|nil]
|
130
|
+
def prev_sibling
|
131
|
+
node = yxml_element_prev_sibling
|
132
|
+
node&.document = document
|
133
|
+
node
|
134
|
+
end
|
135
|
+
|
136
|
+
# Creates a new child an inserts at the end of the children list
|
137
|
+
#
|
138
|
+
# @param [String] name
|
139
|
+
# @return [Y::XMLElement]
|
140
|
+
def <<(name)
|
141
|
+
xml_element = yxml_element_push_elem_back(transaction, name)
|
142
|
+
xml_element.document = document
|
143
|
+
xml_element
|
144
|
+
end
|
145
|
+
|
146
|
+
alias push_child <<
|
147
|
+
|
148
|
+
# Insert new text at the end of this elements child list
|
149
|
+
#
|
150
|
+
# The optional str argument initializes the text node with its value
|
151
|
+
#
|
152
|
+
# @param [String] str
|
153
|
+
# @return [Y::XMLText]
|
154
|
+
def push_text(str = nil)
|
155
|
+
text = yxml_element_push_text_back(transaction)
|
156
|
+
text.document = document
|
157
|
+
text << str unless str.nil?
|
158
|
+
text
|
159
|
+
end
|
160
|
+
|
161
|
+
# Number of children
|
162
|
+
#
|
163
|
+
# @return [Integer]
|
164
|
+
def size
|
165
|
+
yxml_element_size
|
166
|
+
end
|
167
|
+
|
168
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
169
|
+
|
170
|
+
# Removes one or more children from XML Element
|
171
|
+
#
|
172
|
+
# @example Removes a single element
|
173
|
+
# doc = Y::Doc.new
|
174
|
+
#
|
175
|
+
# xml_element = doc.get_xml_element("my xml")
|
176
|
+
# xml_element << "A"
|
177
|
+
# xml_element << "B"
|
178
|
+
# xml_element << "C"
|
179
|
+
#
|
180
|
+
# xml_element.slice!(1)
|
181
|
+
#
|
182
|
+
# xml_element.to_s # <UNDEFINED><A></A><C></C></UNDEFINED>
|
183
|
+
#
|
184
|
+
# @overload slice!(n)
|
185
|
+
# Removes nth node from child list
|
186
|
+
#
|
187
|
+
# @overload slice!(start, length)
|
188
|
+
# Removes a range of nodes
|
189
|
+
#
|
190
|
+
# @overload slice!(range)
|
191
|
+
# Removes a range of nodes
|
192
|
+
#
|
193
|
+
# @return [void]
|
194
|
+
def slice!(*args)
|
195
|
+
if args.size.zero?
|
196
|
+
raise ArgumentError,
|
197
|
+
"Provide one of `index`, `range`, `start, length` as arguments"
|
198
|
+
end
|
199
|
+
|
200
|
+
if args.size == 1
|
201
|
+
arg = args.first
|
202
|
+
|
203
|
+
if arg.is_a?(Range)
|
204
|
+
if arg.exclude_end?
|
205
|
+
yxml_element_remove_range(transaction, arg.first,
|
206
|
+
arg.last - arg.first)
|
207
|
+
end
|
208
|
+
unless arg.exclude_end?
|
209
|
+
yxml_element_remove_range(transaction, arg.first,
|
210
|
+
arg.last + 1 - arg.first)
|
211
|
+
end
|
212
|
+
return nil
|
213
|
+
end
|
214
|
+
|
215
|
+
if arg.is_a?(Numeric)
|
216
|
+
yxml_element_remove_range(transaction, arg.to_int, 1)
|
217
|
+
return nil
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
if args.size == 2
|
222
|
+
first, second = args
|
223
|
+
|
224
|
+
if first.is_a?(Numeric) && second.is_a?(Numeric)
|
225
|
+
yxml_element_remove_range(transaction, first, second)
|
226
|
+
return nil
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
raise ArgumentError, "Please check your arguments, can't slice."
|
231
|
+
end
|
232
|
+
|
233
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
234
|
+
|
235
|
+
# Tag name
|
236
|
+
#
|
237
|
+
# @return [String]
|
238
|
+
def tag
|
239
|
+
yxml_element_tag
|
240
|
+
end
|
241
|
+
|
242
|
+
# String representation of this node and all its children
|
243
|
+
#
|
244
|
+
# @return [String]
|
245
|
+
def to_s
|
246
|
+
yxml_element_to_s
|
247
|
+
end
|
248
|
+
|
249
|
+
# Detach a listener
|
250
|
+
#
|
251
|
+
# @param [Integer] subscription_id
|
252
|
+
# @return [void]
|
253
|
+
def detach(subscription_id)
|
254
|
+
yxml_element_unobserve(subscription_id)
|
255
|
+
end
|
256
|
+
|
257
|
+
# Creates a new node and puts it in front of the child list
|
258
|
+
#
|
259
|
+
# @param [String] name
|
260
|
+
# @return [Y::XMLElement]
|
261
|
+
def unshift_child(name)
|
262
|
+
xml_element = yxml_element_push_elem_front(transaction, name)
|
263
|
+
xml_element.document = document
|
264
|
+
xml_element
|
265
|
+
end
|
266
|
+
|
267
|
+
# Insert new text at the front of this elements child list
|
268
|
+
#
|
269
|
+
# The optional str argument initializes the text node with its value
|
270
|
+
#
|
271
|
+
# @param [String] str
|
272
|
+
# @return [Y::XMLText]
|
273
|
+
def unshift_text(str = nil)
|
274
|
+
text = yxml_element_push_text_front(transaction)
|
275
|
+
text.document = document
|
276
|
+
text << args.first unless str.nil?
|
277
|
+
text
|
278
|
+
end
|
279
|
+
|
280
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
281
|
+
|
282
|
+
# make attributes just work on an element in the form of `attr_name` and
|
283
|
+
# `attr_name=`
|
284
|
+
#
|
285
|
+
# @example Set and get an attribute
|
286
|
+
# doc = Y::Doc.new
|
287
|
+
# xml_element = doc.get_xml_element("my xml")
|
288
|
+
# xml_element.attr_name = "Hello"
|
289
|
+
#
|
290
|
+
# puts xml_element.attr_name # "Hello"
|
291
|
+
#
|
292
|
+
# @!visibility private
|
293
|
+
def method_missing(method_name, *args, &block)
|
294
|
+
is_setter = method_name.to_s.end_with?("=")
|
295
|
+
|
296
|
+
setter = method_name
|
297
|
+
setter += "=" unless is_setter
|
298
|
+
getter = method_name
|
299
|
+
getter = getter.to_s.slice(0...-1)&.to_sym if is_setter
|
300
|
+
|
301
|
+
define_singleton_method(setter.to_sym) do |new_val|
|
302
|
+
yxml_element_insert_attribute(transaction,
|
303
|
+
method_name.to_s
|
304
|
+
.delete_suffix("=")
|
305
|
+
.delete_prefix("attr_"),
|
306
|
+
new_val)
|
307
|
+
end
|
308
|
+
|
309
|
+
define_singleton_method(getter) do
|
310
|
+
yxml_element_get_attribute(method_name.to_s.delete_prefix("attr_"))
|
311
|
+
end
|
312
|
+
|
313
|
+
if is_setter
|
314
|
+
value = args[0]
|
315
|
+
send(setter, value)
|
316
|
+
end
|
317
|
+
rescue StandardError
|
318
|
+
super(method_name, *args, &block)
|
319
|
+
end
|
320
|
+
|
321
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
322
|
+
|
323
|
+
# Make sure we only respond to attributes
|
324
|
+
# @!visibility private
|
325
|
+
def respond_to_missing?(method_name, include_private = false)
|
326
|
+
method_name.to_s.start_with?("attr_") || super
|
327
|
+
end
|
328
|
+
|
329
|
+
private
|
330
|
+
|
331
|
+
# @!method yxml_element_attributes
|
332
|
+
#
|
333
|
+
# @return [Hash]
|
334
|
+
|
335
|
+
# @!method yxml_element_first_child
|
336
|
+
#
|
337
|
+
# @return [Y::XMLElement|Y::XMLText]
|
338
|
+
|
339
|
+
# @!method yxml_element_get_attribute(name)
|
340
|
+
#
|
341
|
+
# @param [String] name
|
342
|
+
# @return [String|nil]
|
343
|
+
|
344
|
+
# @!method yxml_element_get(index)
|
345
|
+
#
|
346
|
+
# @param [Integer] index
|
347
|
+
# @return [Y::XMLElement|Y::XMLText|nil]
|
348
|
+
|
349
|
+
# @!method yxml_element_insert_attribute(transaction, name, value)
|
350
|
+
#
|
351
|
+
# @param [Y::Transaction] transaction
|
352
|
+
# @param [String] name
|
353
|
+
# @param [String] value
|
354
|
+
# @return [String|nil]
|
355
|
+
|
356
|
+
# @!method yxml_element_insert_element(transaction, index, name)
|
357
|
+
# Insert XML element into this XML element
|
358
|
+
# @!visibility private
|
359
|
+
# @param [Y::Transaction] transaction
|
360
|
+
# @param [Integer] index
|
361
|
+
# @param [String] name
|
362
|
+
# @return [Y::XMLElement]
|
363
|
+
|
364
|
+
# @!method yxml_element_insert_text(transaction, index)
|
365
|
+
#
|
366
|
+
# @param [Y::Transaction] transaction
|
367
|
+
# @param [Integer] index
|
368
|
+
# @return [Y::XMLText]
|
369
|
+
|
370
|
+
# @!method yxml_element_next_sibling()
|
371
|
+
#
|
372
|
+
# @return [Y::XMLElement|XMLText|nil]
|
373
|
+
|
374
|
+
# @!method yxml_element_observe(callback)
|
375
|
+
#
|
376
|
+
# @param [Proc] callback
|
377
|
+
# @return [Integer] The subscription ID
|
378
|
+
|
379
|
+
# @!method yxml_element_parent()
|
380
|
+
#
|
381
|
+
# @return [Y::XMLElement|nil]
|
382
|
+
|
383
|
+
# @!method yxml_element_prev_sibling()
|
384
|
+
#
|
385
|
+
# @return [Y::XMLElement|XMLText|nil]
|
386
|
+
|
387
|
+
# @!method yxml_element_push_elem_back(transaction, name)
|
388
|
+
#
|
389
|
+
# @param [Y::Transaction] transaction
|
390
|
+
# @param [String] name
|
391
|
+
# @return [Y::XMLElement]
|
392
|
+
|
393
|
+
# @!method yxml_element_push_elem_front(transaction, name)
|
394
|
+
#
|
395
|
+
# @param [Y::Transaction] transaction
|
396
|
+
# @param [String] name
|
397
|
+
# @return [Y::XMLElement]
|
398
|
+
|
399
|
+
# @!method yxml_element_push_text_back(transaction)
|
400
|
+
#
|
401
|
+
# @param [Y::Transaction] transaction
|
402
|
+
# @return [Y::XMLText]
|
403
|
+
|
404
|
+
# @!method yxml_element_push_text_front(transaction)
|
405
|
+
#
|
406
|
+
# @param [Y::Transaction] transaction
|
407
|
+
# @return [Y::XMLText]
|
408
|
+
|
409
|
+
# @!method yxml_element_remove_attribute(transaction, name)
|
410
|
+
#
|
411
|
+
# @param [Y::Transaction] transaction
|
412
|
+
# @param [String] name
|
413
|
+
#
|
414
|
+
# @return [void]
|
415
|
+
|
416
|
+
# @!method yxml_element_remove_range(transaction, index, length)
|
417
|
+
#
|
418
|
+
# @param [Y::Transaction] transaction
|
419
|
+
# @param [Integer] index
|
420
|
+
# @param [Integer] length
|
421
|
+
#
|
422
|
+
# @return [void]
|
423
|
+
|
424
|
+
# @!method yxml_element_size()
|
425
|
+
#
|
426
|
+
# @return [Integer]
|
427
|
+
|
428
|
+
# @!method yxml_element_tag()
|
429
|
+
#
|
430
|
+
# @return [String]
|
431
|
+
|
432
|
+
# @!method yxml_element_to_s()
|
433
|
+
#
|
434
|
+
# @return [String]
|
435
|
+
|
436
|
+
# @!method yxml_element_unobserve(subscription_id)
|
437
|
+
#
|
438
|
+
# @param [Integer] subscription_id
|
439
|
+
# @return [void]
|
440
|
+
|
441
|
+
# A reference to the current active transaction of the document this element
|
442
|
+
# belongs to.
|
443
|
+
#
|
444
|
+
# @return [Y::Transaction] A transaction object
|
445
|
+
def transaction
|
446
|
+
document.current_transaction
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
# A XMLText
|
451
|
+
#
|
452
|
+
# Someone should not instantiate a text directly, but use
|
453
|
+
# {Y::Doc#get_text_element}, {Y::XMLElement#insert_text},
|
454
|
+
# {Y::XMLElement#push_text}, {Y::XMLElement#unshift_text} instead.
|
455
|
+
#
|
456
|
+
# The XMLText API is similar to {Y::Text}, but adds a few methods to make it
|
457
|
+
# easier to work in structured XML documents.
|
458
|
+
#
|
459
|
+
# @example
|
460
|
+
# doc = Y::Doc.new
|
461
|
+
# xml_text = doc.get_xml_text("my xml text")
|
462
|
+
#
|
463
|
+
# puts xml_text.to_s
|
464
|
+
class XMLText
|
465
|
+
# @!attribute [r] document
|
466
|
+
#
|
467
|
+
# @return [Y::Doc] The document this array belongs to
|
468
|
+
attr_accessor :document
|
469
|
+
|
470
|
+
# Create a new XMLText instance
|
471
|
+
#
|
472
|
+
# @param [Y::Doc] doc
|
473
|
+
def initialize(doc = nil)
|
474
|
+
@document = doc || Y::Doc.new
|
475
|
+
|
476
|
+
super()
|
477
|
+
end
|
478
|
+
|
479
|
+
# Push a string to the end of the text node
|
480
|
+
#
|
481
|
+
# @param [String] str
|
482
|
+
# @return {void}
|
483
|
+
def <<(str)
|
484
|
+
yxml_text_push(transaction, str)
|
485
|
+
end
|
486
|
+
|
487
|
+
alias push <<
|
488
|
+
|
489
|
+
# Attach a listener to get notified about changes
|
490
|
+
#
|
491
|
+
# @param [Proc] callback
|
492
|
+
# @return [Integer] subscription_id
|
493
|
+
def attach(callback = nil, &block)
|
494
|
+
yxml_text_observe(callback) unless callback.nil?
|
495
|
+
yxml_text_observe(block.to_proc) unless block.nil?
|
496
|
+
end
|
497
|
+
|
498
|
+
# Return text attributes
|
499
|
+
#
|
500
|
+
# @return [Hash]
|
501
|
+
def attrs
|
502
|
+
yxml_text_attributes
|
503
|
+
end
|
504
|
+
|
505
|
+
# Detach a listener
|
506
|
+
#
|
507
|
+
# @param [Integer] subscription_id
|
508
|
+
# @return [void]
|
509
|
+
def detach(subscription_id)
|
510
|
+
yxml_text_unobserve(subscription_id)
|
511
|
+
end
|
512
|
+
|
513
|
+
# Format text
|
514
|
+
#
|
515
|
+
# @param [Integer] index
|
516
|
+
# @param [Integer] length
|
517
|
+
# @param [Hash] attrs
|
518
|
+
# @return [void]
|
519
|
+
def format(index, length, attrs)
|
520
|
+
yxml_text_format(transaction, index, length, attrs)
|
521
|
+
end
|
522
|
+
|
523
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
524
|
+
|
525
|
+
# Insert a value at position and with optional attributes. This method is
|
526
|
+
# similar to [String#insert](https://ruby-doc.org/core-3.1.2/String.html),
|
527
|
+
# except for the optional third `attrs` argument.
|
528
|
+
#
|
529
|
+
# @example Insert a string at position
|
530
|
+
# doc = Y::Doc.new
|
531
|
+
# text = doc.get_text("my text")
|
532
|
+
# text << "Hello, "
|
533
|
+
#
|
534
|
+
# text.insert(7, "World!")
|
535
|
+
#
|
536
|
+
# puts text.to_s == "Hello, World!" # true
|
537
|
+
#
|
538
|
+
# The value can be any of the supported types:
|
539
|
+
# - Boolean
|
540
|
+
# - String
|
541
|
+
# - Numeric
|
542
|
+
# - Array (where element types must be supported)
|
543
|
+
# - Hash (where the the types of key and values must be supported)
|
544
|
+
#
|
545
|
+
# @param [Integer] index
|
546
|
+
# @param [String, Float, Array, Hash, Boolean] value
|
547
|
+
# @param [Hash|nil] attrs
|
548
|
+
# @return [void]
|
549
|
+
def insert(index, value, attrs = nil)
|
550
|
+
if value.is_a?(String)
|
551
|
+
yxml_text_insert(transaction, index, value) if attrs.nil?
|
552
|
+
unless attrs.nil?
|
553
|
+
yxml_text_insert_with_attrs(transaction, index, value,
|
554
|
+
attrs)
|
555
|
+
end
|
556
|
+
|
557
|
+
return nil
|
558
|
+
end
|
559
|
+
|
560
|
+
if can_insert?(value)
|
561
|
+
yxml_text_insert_embed(transaction, index, value) if attrs.nil?
|
562
|
+
unless attrs.nil?
|
563
|
+
yxml_text_insert_embed_with_attrs(transaction, index, value,
|
564
|
+
attrs)
|
565
|
+
end
|
566
|
+
|
567
|
+
return nil
|
568
|
+
end
|
569
|
+
|
570
|
+
raise ArgumentError,
|
571
|
+
"Can't insert value. `#{value.class.name}` isn't supported."
|
572
|
+
end
|
573
|
+
|
574
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
575
|
+
|
576
|
+
# Return length of string
|
577
|
+
#
|
578
|
+
# @return [void]
|
579
|
+
def length
|
580
|
+
yxml_text_length
|
581
|
+
end
|
582
|
+
|
583
|
+
alias size length
|
584
|
+
|
585
|
+
# Return adjacent XMLElement or XMLText node (next)
|
586
|
+
#
|
587
|
+
# @return [Y::XMLElement|Y::XMLText|nil]
|
588
|
+
def next_sibling
|
589
|
+
node = yxml_text_next_sibling
|
590
|
+
node.document = document
|
591
|
+
node
|
592
|
+
end
|
593
|
+
|
594
|
+
# Return parent XMLElement
|
595
|
+
#
|
596
|
+
# @return [Y::XMLElement|nil]
|
597
|
+
def parent
|
598
|
+
node = yxml_text_parent
|
599
|
+
node.document = document
|
600
|
+
node
|
601
|
+
end
|
602
|
+
|
603
|
+
# Return adjacent XMLElement or XMLText node (prev)
|
604
|
+
#
|
605
|
+
# @return [Y::XMLElement|Y::XMLText|nil]
|
606
|
+
def prev_sibling
|
607
|
+
node = yxml_text_prev_sibling
|
608
|
+
node&.document = document
|
609
|
+
node
|
610
|
+
end
|
611
|
+
|
612
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
613
|
+
|
614
|
+
# Removes a part from text
|
615
|
+
#
|
616
|
+
# **Attention:** In comparison to String#slice, {XMLText#slice!} will not
|
617
|
+
# return the substring that gets removed. Even this being technically
|
618
|
+
# possible, it requires us to read the substring before removing it, which
|
619
|
+
# is not desirable in most situations.
|
620
|
+
#
|
621
|
+
# @example Removes a single character
|
622
|
+
# doc = Y::Doc.new
|
623
|
+
#
|
624
|
+
# text = doc.get_xml_text("my xml text")
|
625
|
+
# text << "Hello"
|
626
|
+
#
|
627
|
+
# text.slice!(0)
|
628
|
+
#
|
629
|
+
# text.to_s == "ello" # true
|
630
|
+
#
|
631
|
+
# @example Removes a range of characters
|
632
|
+
# doc = Y::Doc.new
|
633
|
+
#
|
634
|
+
# text = doc.get_xml_text("my xml text")
|
635
|
+
# text << "Hello"
|
636
|
+
#
|
637
|
+
# text.slice!(1..2)
|
638
|
+
# text.to_s == "Hlo" # true
|
639
|
+
#
|
640
|
+
# text.slice!(1...2)
|
641
|
+
# text.to_s == "Ho" # true
|
642
|
+
#
|
643
|
+
# @example Removes a range of chars from start and for given length
|
644
|
+
# doc = Y::Doc.new
|
645
|
+
#
|
646
|
+
# text = doc.get_xml_text("my xml text")
|
647
|
+
# text << "Hello"
|
648
|
+
#
|
649
|
+
# text.slice!(0, 3)
|
650
|
+
#
|
651
|
+
# text.to_s == "lo" # true
|
652
|
+
#
|
653
|
+
# @overload slice!(index)
|
654
|
+
# Removes a single character at index
|
655
|
+
#
|
656
|
+
# @overload slice!(start, length)
|
657
|
+
# Removes a range of characters
|
658
|
+
#
|
659
|
+
# @overload slice!(range)
|
660
|
+
# Removes a range of characters
|
661
|
+
#
|
662
|
+
# @return [void]
|
663
|
+
def slice!(*args)
|
664
|
+
if args.size.zero?
|
665
|
+
raise ArgumentError,
|
666
|
+
"Provide one of `index`, `range`, `start, length` as arguments"
|
667
|
+
end
|
668
|
+
|
669
|
+
if args.size == 1
|
670
|
+
arg = args.first
|
671
|
+
|
672
|
+
if arg.is_a?(Range)
|
673
|
+
yxml_text_remove_range(transaction, arg.first, arg.last - arg.first)
|
674
|
+
return nil
|
675
|
+
end
|
676
|
+
|
677
|
+
if arg.is_a?(Numeric)
|
678
|
+
yxml_text_remove_range(transaction, arg.to_int, 1)
|
679
|
+
return nil
|
680
|
+
end
|
681
|
+
end
|
682
|
+
|
683
|
+
if args.size == 2
|
684
|
+
first, second = args
|
685
|
+
|
686
|
+
if first.is_a?(Numeric) && second.is_a?(Numeric)
|
687
|
+
yxml_text_remove_range(transaction, first, second)
|
688
|
+
return nil
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
raise ArgumentError, "Please check your arguments, can't slice."
|
693
|
+
end
|
694
|
+
|
695
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
696
|
+
|
697
|
+
# Returns string representation of XMLText
|
698
|
+
#
|
699
|
+
# @return [String]
|
700
|
+
def to_s
|
701
|
+
yxml_text_to_s
|
702
|
+
end
|
703
|
+
|
704
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
705
|
+
|
706
|
+
# make attributes just work on an element in the form of `attr_name` and
|
707
|
+
# `attr_name=`
|
708
|
+
#
|
709
|
+
# @example Set and get an attribute
|
710
|
+
# doc = Y::Doc.new
|
711
|
+
# xml_element = doc.get_xml_element("my xml")
|
712
|
+
# xml_element.attr_name = "Hello"
|
713
|
+
#
|
714
|
+
# puts xml_element.attr_name # "Hello"
|
715
|
+
#
|
716
|
+
# @!visibility private
|
717
|
+
def method_missing(method_name, *args, &block)
|
718
|
+
is_setter = method_name.to_s.end_with?("=")
|
719
|
+
|
720
|
+
setter = method_name
|
721
|
+
setter += "=" unless is_setter
|
722
|
+
getter = method_name
|
723
|
+
getter = getter.to_s.slice(0...-1).to_sym if is_setter
|
724
|
+
|
725
|
+
define_singleton_method(setter.to_sym) do |new_val|
|
726
|
+
yxml_text_insert_attribute(transaction,
|
727
|
+
method_name.to_s
|
728
|
+
.delete_suffix("=")
|
729
|
+
.delete_prefix("attr_"),
|
730
|
+
new_val)
|
731
|
+
end
|
732
|
+
|
733
|
+
define_singleton_method(getter) do
|
734
|
+
yxml_text_get_attribute(method_name.to_s.delete_prefix("attr_"))
|
735
|
+
end
|
736
|
+
|
737
|
+
if is_setter
|
738
|
+
value = args[0]
|
739
|
+
send(setter, value)
|
740
|
+
end
|
741
|
+
rescue StandardError
|
742
|
+
super(method_name, *args, &block)
|
743
|
+
end
|
744
|
+
|
745
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
746
|
+
|
747
|
+
# Make sure we only respond to attributes
|
748
|
+
# @!visibility private
|
749
|
+
def respond_to_missing?(method_name, include_private = false)
|
750
|
+
method_name.to_s.start_with?("attr_") || super
|
751
|
+
end
|
752
|
+
|
753
|
+
private
|
754
|
+
|
755
|
+
def can_insert?(value)
|
756
|
+
value.is_a?(NilClass) ||
|
757
|
+
value.is_a?(Symbol) ||
|
758
|
+
[true, false].include?(value) ||
|
759
|
+
value.is_a?(Numeric) ||
|
760
|
+
value.is_a?(Enumerable) ||
|
761
|
+
value.is_a?(Hash)
|
762
|
+
end
|
763
|
+
|
764
|
+
# @!method yxml_text_attributes
|
765
|
+
#
|
766
|
+
# @return [Hash]
|
767
|
+
|
768
|
+
# @!method yxml_text_format(transaction, index, length, attrs)
|
769
|
+
#
|
770
|
+
# @param [Integer] index
|
771
|
+
# @param [Integer] length
|
772
|
+
# @param [Hash] attrs
|
773
|
+
# @return [void]
|
774
|
+
|
775
|
+
# @!method yxml_text_get_attribute(name)
|
776
|
+
#
|
777
|
+
# @param [String] name
|
778
|
+
# @return [String|nil]
|
779
|
+
|
780
|
+
# @!method yxml_text_insert(transaction, index, str)
|
781
|
+
#
|
782
|
+
# @param [Y::Transaction] transaction
|
783
|
+
# @param [Integer] index
|
784
|
+
# @param [String] str
|
785
|
+
# @return [void]
|
786
|
+
|
787
|
+
# @!method yxml_text_insert_attribute(transaction, name, value)
|
788
|
+
#
|
789
|
+
# @param [Y::Transaction] transaction
|
790
|
+
# @param [String] name
|
791
|
+
# @param [String] value
|
792
|
+
# @return [void]
|
793
|
+
|
794
|
+
# @!method yxml_text_insert_with_attrs(transaction, index, value, attrs)
|
795
|
+
#
|
796
|
+
# @param [Y::Transaction] transaction
|
797
|
+
# @param [Integer] index
|
798
|
+
# @param [String] value
|
799
|
+
# @param [Hash] attrs
|
800
|
+
# @return [void]
|
801
|
+
|
802
|
+
# @!method yxml_text_insert_embed(transaction, index, value)
|
803
|
+
#
|
804
|
+
# @param [Y::Transaction] transaction
|
805
|
+
# @param [Integer] index
|
806
|
+
# @param [String] value
|
807
|
+
# @return [void]
|
808
|
+
|
809
|
+
# @!method yxml_text_insert_embed_with_attrs(txn, index, value, attrs)
|
810
|
+
#
|
811
|
+
# @param [Y::Transaction] txn
|
812
|
+
# @param [Integer] index
|
813
|
+
# @param [true|false|Float|Integer|Array|Hash] value
|
814
|
+
# @param [Hash] attrs
|
815
|
+
# @return [void]
|
816
|
+
|
817
|
+
# @!method yxml_text_length
|
818
|
+
#
|
819
|
+
# @return [Integer]
|
820
|
+
|
821
|
+
# @!method yxml_text_next_sibling
|
822
|
+
#
|
823
|
+
# @return [Y::XMLElement|Y::XMLText|nil]
|
824
|
+
|
825
|
+
# @!method yxml_text_observe(callback)
|
826
|
+
#
|
827
|
+
# @param [Proc] callback
|
828
|
+
# @return [Integer] A subscription ID
|
829
|
+
|
830
|
+
# @!method yxml_text_parent
|
831
|
+
#
|
832
|
+
# @return [Y::XMLElement|nil]
|
833
|
+
|
834
|
+
# @!method yxml_text_prev_sibling
|
835
|
+
#
|
836
|
+
# @return [Y::XMLElement|Y::XMLText|nil]
|
837
|
+
|
838
|
+
# @!method yxml_text_push(transaction, str)
|
839
|
+
#
|
840
|
+
# @param [Y::Transaction] transaction
|
841
|
+
# @param [String] str
|
842
|
+
# @return [void]
|
843
|
+
|
844
|
+
# @!method yxml_text_remove_range(transaction, index, length)
|
845
|
+
#
|
846
|
+
# @param [Y::Transaction] transaction
|
847
|
+
# @param [Integer] index
|
848
|
+
# @param [Integer] length
|
849
|
+
# @return [void]
|
850
|
+
|
851
|
+
# @!method yxml_text_to_s()
|
852
|
+
#
|
853
|
+
# @return [void]
|
854
|
+
|
855
|
+
# @!method yxml_text_unobserve(subscription_id)
|
856
|
+
#
|
857
|
+
# @param [Integer] subscription_id
|
858
|
+
# @return [void]
|
859
|
+
|
860
|
+
# A reference to the current active transaction of the document this text
|
861
|
+
# belongs to.
|
862
|
+
#
|
863
|
+
# @return [Y::Transaction] A transaction object
|
864
|
+
def transaction
|
865
|
+
document.current_transaction
|
866
|
+
end
|
867
|
+
end
|
868
|
+
|
869
|
+
# rubocop:enable Metrics/ClassLength
|
870
|
+
end
|