y-rb 0.1.4.beta.1-aarch64-linux

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.
data/lib/y/array.rb ADDED
@@ -0,0 +1,352 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Y
4
+ # An array can be used to store and retrieve elements.
5
+ #
6
+ # The array is the replicated counterpart to a Ruby Array. It supports a
7
+ # subset of the Ruby Array operations, like adding, getting and deleting
8
+ # values by position or ranges.
9
+ #
10
+ # Someone should not instantiate an array directly, but use {Y::Doc#get_array}
11
+ # instead.
12
+ #
13
+ # @example
14
+ # doc = Y::Doc.new
15
+ # array = doc.get_array("my array")
16
+ #
17
+ # array << 1
18
+ # array.push(2)
19
+ # array.concat([3, 4, 5])
20
+ #
21
+ # array.to_a == [1, 2, 3, 4, 5] # true
22
+ class Array
23
+ # @!attribute [r] document
24
+ #
25
+ # @return [Y::Doc] The document this array belongs to
26
+ attr_accessor :document
27
+
28
+ # Create a new array instance
29
+ #
30
+ # @param [Y::Doc] doc
31
+ def initialize(doc = nil)
32
+ @document = doc || Y::Doc.new
33
+
34
+ super()
35
+ end
36
+
37
+ # Retrieves element at position
38
+ #
39
+ # @return [Object]
40
+ def [](index)
41
+ yarray_get(index)
42
+ end
43
+
44
+ # Inserts value at position
45
+ #
46
+ # @param [Integer] index
47
+ # @param [true|false|Float|Integer|String|Array|Hash] value
48
+ # @return [void]
49
+ def []=(index, value)
50
+ yarray_insert(transaction, index, value)
51
+ end
52
+
53
+ # Adds an element to the end of the array
54
+ #
55
+ # @return [void]
56
+ def <<(value)
57
+ yarray_push_back(transaction, value)
58
+ end
59
+
60
+ # Attach listener to array changes
61
+ #
62
+ # @example Listen to changes in array type
63
+ # local = Y::Doc.new
64
+ #
65
+ # arr = local.get_array("my array")
66
+ # arr.attach(->(delta) { pp delta })
67
+ #
68
+ # local.transact do
69
+ # arr << 1
70
+ # end
71
+ #
72
+ # @param [Proc] callback
73
+ # @param [Block] block
74
+ # @return [Integer]
75
+ def attach(callback, &block)
76
+ return yarray_observe(callback) unless callback.nil?
77
+
78
+ yarray_observe(block.to_proc) unless block.nil?
79
+ end
80
+
81
+ # Adds to array all elements from each Array in `other_arrays`.
82
+ #
83
+ # If one of the arguments isn't an Array, it is silently ignored.
84
+ #
85
+ # @example Add multiple values to array
86
+ # doc = Y::Doc.new
87
+ # arr = doc.get_array("my array")
88
+ # arr.concat([1, 2, 3])
89
+ #
90
+ # arr.to_a == [1, 2, 3] # true
91
+ #
92
+ # @param [Array<Array<Object>>] other_arrays
93
+ # @return [void]
94
+ def concat(*other_arrays)
95
+ combined = other_arrays.reduce([]) do |values, arr|
96
+ values.concat(arr) if arr.is_a?(::Array)
97
+ end
98
+
99
+ yarray_insert_range(transaction, size, combined)
100
+ end
101
+
102
+ # Detach listener
103
+ #
104
+ # @param [Integer] subscription_id
105
+ # @return [void]
106
+ def detach(subscription_id)
107
+ yarray_unobserve(subscription_id)
108
+ end
109
+
110
+ # @return [void]
111
+ def each(&block)
112
+ yarray_each(block)
113
+ end
114
+
115
+ # Check if the array is empty
116
+ #
117
+ # @return [true|false]
118
+ def empty?
119
+ size.zero?
120
+ end
121
+
122
+ # Returns first element in array if there is at least one
123
+ #
124
+ # @return [Object|nil]
125
+ def first
126
+ yarray_get(0)
127
+ end
128
+
129
+ # Returns last element in array if there is at least one element
130
+ #
131
+ # @return [Object|nil]
132
+ def last
133
+ len = yarray_length
134
+ return yarray_get(yarray_length - 1) if len.positive?
135
+
136
+ nil
137
+ end
138
+
139
+ # rubocop:disable Naming/MethodParameterName
140
+
141
+ # Removes last (n) element(s) from array
142
+ #
143
+ # @param [Integer|nil] n Number of elements to remove
144
+ # @return [void]
145
+ def pop(n = nil)
146
+ len = size
147
+ yarray_remove(transaction, len - 1) if n.nil?
148
+ yarray_remove_range(transaction, len - n, n) unless n.nil?
149
+ end
150
+
151
+ # rubocop:enable Naming/MethodParameterName
152
+
153
+ alias push <<
154
+
155
+ # rubocop:disable Naming/MethodParameterName
156
+
157
+ # Removes first (n) element(s) from array
158
+ #
159
+ # @param [Integer|nil] n Number of elements to remove
160
+ # @return [void]
161
+ def shift(n = nil)
162
+ yarray_remove(transaction, 0) if n.nil?
163
+ yarray_remove_range(transaction, 0, n) unless nil?
164
+ end
165
+
166
+ # rubocop:enable Naming/MethodParameterName
167
+
168
+ # Size of array
169
+ #
170
+ # @return [Integer]
171
+ def size
172
+ yarray_length
173
+ end
174
+
175
+ alias length size
176
+
177
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
178
+
179
+ # Removes one or more elements from array
180
+ #
181
+ # **Attention:** In comparison to Array#slice, {Array#slice!} will not
182
+ # return the values that got removed. Even this being technically
183
+ # possible, it requires us to read the elements before removing them, which
184
+ # is not desirable in most situations.
185
+ #
186
+ # @example Removes a single element
187
+ # doc = Y::Doc.new
188
+ #
189
+ # arr = doc.get_text("my array")
190
+ # arr << 1
191
+ # arr << 2
192
+ # arr << 3
193
+ #
194
+ # arr.slice!(1)
195
+ #
196
+ # arr.to_a == [1, 3] # true
197
+ #
198
+ # @overload slice!(n)
199
+ # Removes nth element from array
200
+ #
201
+ # @overload slice!(start, length)
202
+ # Removes a range of elements
203
+ #
204
+ # @overload slice!(range)
205
+ # Removes a range of elements
206
+ #
207
+ # @return [void]
208
+ def slice!(*args)
209
+ if args.size.zero?
210
+ raise ArgumentError,
211
+ "Provide one of `index`, `range`, `start, length` as arguments"
212
+ end
213
+
214
+ if args.size == 1
215
+ arg = args.first
216
+
217
+ if arg.is_a?(Range)
218
+ if arg.exclude_end?
219
+ yarray_remove_range(transaction, arg.first,
220
+ arg.last - arg.first)
221
+ end
222
+ unless arg.exclude_end?
223
+ yarray_remove_range(transaction, arg.first,
224
+ arg.last + 1 - arg.first)
225
+ end
226
+ return nil
227
+ end
228
+
229
+ if arg.is_a?(Numeric)
230
+ yarray_remove(transaction, arg.to_int)
231
+ return nil
232
+ end
233
+ end
234
+
235
+ if args.size == 2
236
+ first, second = args
237
+
238
+ if first.is_a?(Numeric) && second.is_a?(Numeric)
239
+ yarray_remove_range(transaction, first, second)
240
+ return nil
241
+ end
242
+ end
243
+
244
+ raise ArgumentError, "Please check your arguments, can't slice."
245
+ end
246
+
247
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
248
+
249
+ # Convert this array to a Ruby Array
250
+ #
251
+ # @return [Array<Object>]
252
+ def to_a
253
+ yarray_to_a
254
+ end
255
+
256
+ # Adds an element to the beginning of the array
257
+ #
258
+ # @return [void]
259
+ def unshift(value)
260
+ yarray_push_front(transaction, value)
261
+ end
262
+
263
+ alias prepend unshift
264
+
265
+ private
266
+
267
+ # @!method yarray_each(proc)
268
+ # Iterates over all elements in Array by calling the provided proc
269
+ # with the value as argument.
270
+ #
271
+ # @param [Proc<Object>] proc A proc that is called for every element
272
+
273
+ # @!method yarray_get(index)
274
+ # Retrieves content as specified index
275
+ #
276
+ # @param [Integer] index
277
+ # @return [Object]
278
+
279
+ # @!method yarray_insert(transaction, index, content)
280
+ # Inserts content at specified index
281
+ #
282
+ # @param [Y::Transaction] transaction
283
+ # @param [Integer] index
284
+ # @param [Boolean, Float, Integer, Array, Hash, Text] content
285
+ # @return [void]
286
+
287
+ # @!method yarray_insert_range(transaction, index, arr)
288
+ # Inserts all elements of a given array at specified index
289
+ #
290
+ # @param [Y::Transaction] transaction
291
+ # @param [Integer] index
292
+ # @param [Array<Boolean, Float, Integer, Array, Hash, Text>] arr
293
+ # @return [void]
294
+
295
+ # @!method yarray_length
296
+ # Returns length of array
297
+ #
298
+ # @return [Integer] Length of array
299
+
300
+ # @!method yarray_push_back(transaction, value)
301
+ # Adds an element to the end of the array
302
+ #
303
+ # @param [Y::Transaction] transaction
304
+ # @param [Object] value
305
+ # @return [void]
306
+
307
+ # @!method yarray_push_front(transaction, value)
308
+ # Adds an element to the front of the array
309
+ #
310
+ # @param [Y::Transaction] transaction
311
+ # @param [Object] value
312
+ # @return [void]
313
+
314
+ # @!method yarray_observe(callback)
315
+ #
316
+ # @param [Proc] callback
317
+ # @return [Integer]
318
+
319
+ # @!method yarray_remove(transaction, index)
320
+ # Removes a single element from array at index
321
+ #
322
+ # @param [Y::Transaction] transaction
323
+ # @param [Integer] index
324
+ # @return [void]
325
+
326
+ # @!method yarray_remove_range(transaction, index, length)
327
+ # Removes a range of elements from array
328
+ #
329
+ # @param [Y::Transaction] transaction
330
+ # @param [Integer] index
331
+ # @param [Integer] length
332
+ # @return [void]
333
+
334
+ # @!method yarray_to_a
335
+ # Transforms the array into a Ruby array
336
+ #
337
+ # @return [Array]
338
+
339
+ # @!method yarray_unobserve(subscription_id)
340
+ #
341
+ # @param [Integer] subscription_id
342
+ # @return [void]
343
+
344
+ # A reference to the current active transaction of the document this map
345
+ # belongs to.
346
+ #
347
+ # @return [Y::Transaction] A transaction object
348
+ def transaction
349
+ document.current_transaction
350
+ end
351
+ end
352
+ end
data/lib/y/doc.rb ADDED
@@ -0,0 +1,220 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "transaction"
4
+
5
+ module Y
6
+ # @example Create a local and remote doc and syncs the diff
7
+ # local = Y::Doc.new
8
+ # local_map = local.get_map("my map")
9
+ # local_map[:hello] = "world"
10
+ #
11
+ # remote = Y::Doc.new
12
+ #
13
+ # diff = local.diff(remote.state)
14
+ # remote.sync(diff)
15
+ #
16
+ # remote_map = remote.get_map("my_map")
17
+ # pp remote_map.to_h #=> {hello: "world"}
18
+ class Doc
19
+ ZERO_STATE = [0].freeze
20
+ private_constant :ZERO_STATE
21
+
22
+ # Commit current transaction
23
+ #
24
+ # This is a convenience method that invokes {Y::Transaction#commit} on the
25
+ # current transaction used by this document.
26
+ #
27
+ # @return [void]
28
+ def commit
29
+ current_transaction.commit
30
+ end
31
+
32
+ # The currently active transaction for this document
33
+ # @return [Y::Transaction]
34
+ def current_transaction
35
+ @current_transaction ||= begin
36
+ transaction = ydoc_transact
37
+ transaction.document = self
38
+ transaction
39
+ end
40
+ end
41
+
42
+ # Create a diff between this document and another document. The diff is
43
+ # created based on a state vector provided by the other document. It only
44
+ # returns the missing blocks, as binary encoded sequence.
45
+ #
46
+ # @param [::Array<Int>] state The state to create the diff against
47
+ # @return [::Array<Int>] Binary encoded diff
48
+ def diff(state = ZERO_STATE)
49
+ ydoc_encode_diff_v1(state)
50
+ end
51
+
52
+ # Creates a full diff for the current document. It is similar to {#diff},
53
+ # but does not take a state. Instead it creates an empty state and passes it
54
+ # to the encode_diff function.
55
+ #
56
+ # @return [::Array<Int>] Binary encoded diff
57
+ def full_diff
58
+ empty_state = Y::Doc.new.state
59
+ ydoc_encode_diff_v1(empty_state)
60
+ end
61
+
62
+ # Gets or creates a new array by name
63
+ #
64
+ # If the optional values array is present, fills the array up with elements
65
+ # from the provided array. If the array already exists and isn't
66
+ # empty, elements are pushed to the end of the array.
67
+ #
68
+ # @param [String] name The name of the structure
69
+ # @param [::Array] values Optional initial values
70
+ # @return [Y::Array]
71
+ def get_array(name, values = nil)
72
+ array = current_transaction.get_array(name)
73
+ array.document = self
74
+ array.concat(values) unless values.nil?
75
+ array
76
+ end
77
+
78
+ # Gets or creates a new map by name
79
+ #
80
+ # If the optional input hash is present, fills the map up with key-value
81
+ # pairs from the provided input hash. If the map already exists and isn't
82
+ # empty, any existing keys are overridden and new keys are added.
83
+ #
84
+ # @param [String] name The name of the structure
85
+ # @param [Hash] input Optional initial map key-value pairs
86
+ # @return [Y::Map]
87
+ def get_map(name, input = nil)
88
+ map = current_transaction.get_map(name)
89
+ map.document = self
90
+ input&.each { |key, value| map[key] = value }
91
+ map
92
+ end
93
+
94
+ # Gets or creates a new text by name
95
+ #
96
+ # If the optional input string is provided, fills a new text with the string
97
+ # at creation time. If the text isn't new and not empty, appends the input
98
+ # to the end of the text.
99
+ #
100
+ # @param [String] name The name of the structure
101
+ # @param [String] input Optional initial text value
102
+ # @return [Y::Text]
103
+ def get_text(name, input = nil)
104
+ text = current_transaction.get_text(name)
105
+ text.document = self
106
+ text << input unless input.nil?
107
+ text
108
+ end
109
+
110
+ # Gets or creates a new XMLElement by name
111
+ #
112
+ # @param [String] name The name of the structure
113
+ # @return [Y::XMLElement]
114
+ def get_xml_element(name)
115
+ xml_element = current_transaction.get_xml_element(name)
116
+ xml_element.document = self
117
+ xml_element
118
+ end
119
+
120
+ # Gets or creates a new XMLText by name
121
+ #
122
+ # @param [String] name The name of the structure
123
+ # @param [String] input Optional initial text value
124
+ # @return [Y::XMLText]
125
+ def get_xml_text(name, input = nil)
126
+ xml_text = current_transaction.get_xml_text(name)
127
+ xml_text.document = self
128
+ xml_text << input unless input.nil?
129
+ xml_text
130
+ end
131
+
132
+ # Creates a state vector of this document. This can be used to compare the
133
+ # state of two documents with each other and to later on sync them.
134
+ #
135
+ # @return [::Array<Int>] Binary encoded state vector
136
+ def state
137
+ current_transaction.state
138
+ end
139
+
140
+ # Synchronizes this document with the diff from another document
141
+ #
142
+ # @param [::Array<Int>] diff Binary encoded update
143
+ # @return [void]
144
+ def sync(diff)
145
+ current_transaction.apply(diff)
146
+ end
147
+
148
+ # Restores a specific document from an update that contains full state
149
+ #
150
+ # This is doing the same as {#sync}, but it exists to be explicit about
151
+ # the intent. This is the companion to {#full_diff}.
152
+ #
153
+ # @param [::Array<Int>] full_diff Binary encoded update
154
+ # @return [void]
155
+ def restore(full_diff)
156
+ current_transaction.apply(full_diff)
157
+ end
158
+
159
+ # rubocop:disable Metrics/MethodLength
160
+
161
+ # Creates a new transaction and provides it to the given block
162
+ #
163
+ # @example Insert into text
164
+ # doc = Y::Doc.new
165
+ # text = doc.get_text("my text")
166
+ #
167
+ # doc.transact do
168
+ # text << "Hello, World!"
169
+ # end
170
+ #
171
+ # @yield [transaction]
172
+ # @yieldparam [Y::Transaction] transaction
173
+ # @yieldreturn [void]
174
+ # @return [Y::Transaction]
175
+ def transact
176
+ current_transaction.commit
177
+
178
+ if block_given?
179
+ # create new transaction just for the lifetime of this block
180
+ tmp_transaction = ydoc_transact
181
+ tmp_transaction.document = self
182
+
183
+ # override transaction for the lifetime of the block
184
+ @current_transaction = tmp_transaction
185
+
186
+ yield tmp_transaction
187
+
188
+ tmp_transaction.commit
189
+ end
190
+
191
+ # create new transaction
192
+ @current_transaction = ydoc_transact
193
+ @current_transaction.document = self
194
+
195
+ current_transaction
196
+ end
197
+
198
+ # rubocop:enable Metrics/MethodLength
199
+
200
+ # @!method ydoc_encode_diff_v1
201
+ # Encodes the diff of current document state vs provided state
202
+ #
203
+ # @example Create transaction on doc
204
+ # doc = Y::Doc.new
205
+ # tx = doc.ydoc_encode_diff_v1(other_state)
206
+ #
207
+ # @return [Array<Integer>] Binary encoded update
208
+ # @!visibility private
209
+
210
+ # @!method ydoc_transact
211
+ # Creates a new transaction for the document
212
+ #
213
+ # @example Create transaction on doc
214
+ # doc = Y::Doc.new
215
+ # tx = doc.ydoc_transact
216
+ #
217
+ # @return [Y::Transaction] The transaction object
218
+ # @!visibility private
219
+ end
220
+ end