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/map.rb ADDED
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Y
6
+ # A map can be used to store and retrieve key-value pairs.
7
+ #
8
+ # The map is the replicated counterpart to a Hash. It supports a subset
9
+ # of the Hash operations, like adding, getting and deleting values by key.
10
+ #
11
+ # Someone should not instantiate a map directly, but use {Y::Doc#get_map}
12
+ # instead.
13
+ #
14
+ # @example
15
+ # doc = Y::Doc.new
16
+ # map = doc.get_map("my map")
17
+ #
18
+ # map[:hello] = "world"
19
+ # puts map[:hello]
20
+ class Map
21
+ # @!attribute [r] document
22
+ #
23
+ # @return [Y::Doc] The document this map belongs to
24
+ attr_accessor :document
25
+
26
+ # Create a new map instance
27
+ #
28
+ # @param [Y::Doc] doc
29
+ def initialize(doc = nil)
30
+ @document = doc || Y::Doc.new
31
+
32
+ super()
33
+ end
34
+
35
+ # Attach a listener to get notified about any changes to the map
36
+ #
37
+ # @param [Proc] callback
38
+ # @param [Block] block
39
+ # @return [Integer]
40
+ def attach(callback, &block)
41
+ return ymap_observe(callback) unless callback.nil?
42
+
43
+ ymap_observe(block.to_proc) unless block.nil?
44
+ end
45
+
46
+ # Removes all map entries
47
+ #
48
+ # @return [Self]
49
+ def clear
50
+ ymap_clear(transaction)
51
+ self
52
+ end
53
+
54
+ # rubocop:disable Layout/LineLength
55
+
56
+ # Deletes the entry for the given key and returns its associated value.
57
+ #
58
+ # @example Deletes the entry and return associated value
59
+ #
60
+ # m = doc.get_map("my map")
61
+ # m[:bar] = 1
62
+ # m.delete(:bar) # => 1
63
+ # m # => {}
64
+ #
65
+ # @example Unknown key is handled in block
66
+ #
67
+ # m = doc.get_map("my map")
68
+ # m.delete(:nosuch) { |key| "Key #{key} not found" }# => "Key nosuch not found"
69
+ # m # => {}
70
+ #
71
+ # @param [String, Symbol] key
72
+ # @return [void]
73
+ def delete(key)
74
+ value = ymap_remove(transaction, key)
75
+ if block_given? && key?(key)
76
+ yield key
77
+ else
78
+ value
79
+ end
80
+ end
81
+
82
+ # rubocop:enable Layout/LineLength
83
+
84
+ # Detach listener
85
+ #
86
+ # @param [Integer] subscription_id
87
+ # @return [void]
88
+ def detach(subscription_id)
89
+ ymap_unobserve(subscription_id)
90
+ end
91
+
92
+ # @return [void]
93
+ def each(&block)
94
+ ymap_each(block)
95
+ end
96
+
97
+ # @return [true|false]
98
+ def key?(key)
99
+ ymap_contains(key)
100
+ end
101
+
102
+ alias has_key? key?
103
+
104
+ # @return [Object]
105
+ def [](key)
106
+ ymap_get(key)
107
+ end
108
+
109
+ # @return [void]
110
+ def []=(key, val)
111
+ ymap_insert(transaction, key, val)
112
+ end
113
+
114
+ # Returns size of map
115
+ #
116
+ # @return [Integer]
117
+ def size
118
+ ymap_size
119
+ end
120
+
121
+ # Returns a Hash representation of this map
122
+ #
123
+ # @return [Hash]
124
+ def to_h
125
+ ymap_to_h
126
+ end
127
+
128
+ # Returns a JSON representation of map
129
+ #
130
+ # @return [String] JSON string
131
+ def to_json(*_args)
132
+ to_h.to_json
133
+ end
134
+
135
+ private
136
+
137
+ # @!method ymap_clear()
138
+ # Removes all key-value pairs from Map
139
+
140
+ # @!method ymap_contains(key)
141
+ # Check if a certain key is in the Map
142
+ #
143
+ # @param [String|Symbol] key
144
+ # @return [Boolean] True, if and only if the key exists
145
+
146
+ # @!method ymap_each(proc)
147
+ # Iterates over all key-value pairs in Map by calling the provided proc
148
+ # with the key and the value as arguments.
149
+ #
150
+ # @param [Proc<String, Any>] proc A proc that is called for every element
151
+
152
+ # @!method ymap_get(key)
153
+ # Returns stored value for key or nil if none is present
154
+ #
155
+ # @param [String|Symbol] key
156
+ # @return [Any|Nil] Value or nil
157
+
158
+ # @!method ymap_insert(transaction, key, value)
159
+ # Insert value for key. In case the key already exists, the previous value
160
+ # will be overwritten.
161
+ #
162
+ # @param [Y::Transaction] transaction
163
+ # @param [String|Symbol] key
164
+ # @param [Any] value
165
+
166
+ # @!method ymap_observe(callback)
167
+ #
168
+ # @param [Proc] callback
169
+ # @return [Integer]
170
+
171
+ # @!method ymap_remove(transaction, key)
172
+ # Removes key-value pair from Map if key exists.
173
+ #
174
+ # @param [Y::Transaction] transaction
175
+ # @param [String|Symbol] key
176
+
177
+ # @!method ymap_size()
178
+ # Returns number of key-value pairs stored in map
179
+ #
180
+ # @return [Integer] Number of key-value pairs
181
+
182
+ # @!method ymap_to_h()
183
+ # Returns a Hash representation of the Map
184
+ #
185
+ # @return [Hash] Hash representation of Map
186
+
187
+ # @!method ymap_unobserve(subscription_id)
188
+ #
189
+ # @param [Integer] subscription_id
190
+ # @return [void]
191
+
192
+ # A reference to the current active transaction of the document this map
193
+ # belongs to.
194
+ #
195
+ # @return [Y::Transaction] A transaction object
196
+ def transaction
197
+ document.current_transaction
198
+ end
199
+ end
200
+ end
data/lib/y/text.rb ADDED
@@ -0,0 +1,372 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Y
4
+ # A text can be used insert and remove string fragments. It also supports
5
+ # formatting and the concept of embeds, which are supported data types that
6
+ # added as metadata.
7
+ #
8
+ # The text is the replicated counterpart to a String. It supports a subset
9
+ # of String operations, like appending, insert at position and slicing.
10
+ #
11
+ # Someone should not instantiate a text directly, but use {Y::Doc#get_text}
12
+ # instead.
13
+ #
14
+ # @example
15
+ # doc = Y::Doc.new
16
+ # text = doc.get_text("my text")
17
+ #
18
+ # text << "Hello, World!"
19
+ # puts text.to_s
20
+ class Text
21
+ # @!attribute [r] document
22
+ #
23
+ # @return [Y::Doc] The document this text belongs to
24
+ attr_accessor :document
25
+
26
+ # Create a new text instance
27
+ #
28
+ # @param [Y::Doc] doc
29
+ def initialize(doc = nil)
30
+ @document = doc || Y::Doc.new
31
+
32
+ super()
33
+ end
34
+
35
+ # Appends a string at the end of the text
36
+ #
37
+ # @return [void]
38
+ def <<(str)
39
+ ytext_push(transaction, str)
40
+ end
41
+
42
+ # Attach listener to text changes
43
+ #
44
+ # @example Listen to changes in text type
45
+ # local = Y::Doc.new
46
+ #
47
+ # text = local.get_text("my text")
48
+ # text.attach(->(delta) { pp delta }) # { insert: "Hello, World!" }
49
+ #
50
+ # local.transact do
51
+ # text << "Hello, World!"
52
+ # end
53
+ #
54
+ # @example Listen to changes in text type
55
+ # local = Y::Doc.new
56
+ #
57
+ # text = local.get_text("my text")
58
+ # text.attach(->(delta) { pp delta }) # { insert: "Hello, World!" }
59
+ #
60
+ # text << "Hello, World!"
61
+ #
62
+ # # todo: required, otherwise segfault
63
+ # local.commit
64
+ #
65
+ # @param [Proc] callback
66
+ # @param [Block] block
67
+ # @return [Integer]
68
+ def attach(callback, &block)
69
+ return ytext_observe(callback) unless callback.nil?
70
+
71
+ ytext_observe(block.to_proc) unless block.nil?
72
+ end
73
+
74
+ # Detach listener
75
+ #
76
+ # @param [Integer] subscription_id
77
+ # @return [void]
78
+ def detach(subscription_id)
79
+ ytext_unobserve(subscription_id)
80
+ end
81
+
82
+ # Checks if text is empty
83
+ #
84
+ # @example Check if text is empty
85
+ # doc = Y::Doc.new
86
+ # text = doc.get_text("my text")
87
+ #
88
+ # text.empty? # true
89
+ #
90
+ # @return [TrueClass,FalseClass]
91
+ def empty?
92
+ length.zero?
93
+ end
94
+
95
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
96
+
97
+ # Insert a value at position and with optional attributes. This method is
98
+ # similar to [String#insert](https://ruby-doc.org/core-3.1.2/String.html),
99
+ # except for the optional third `attrs` argument.
100
+ #
101
+ # @example Insert a string at position
102
+ # doc = Y::Doc.new
103
+ # text = doc.get_text("my text")
104
+ # text << "Hello, "
105
+ #
106
+ # text.insert(7, "World!")
107
+ #
108
+ # puts text.to_s == "Hello, World!" # true
109
+ #
110
+ # The value can be any of the supported types:
111
+ # - Boolean
112
+ # - String
113
+ # - Numeric
114
+ # - Array (where element types must be supported)
115
+ # - Hash (where the the types of key and values must be supported)
116
+ #
117
+ # @param [Integer] index
118
+ # @param [String, Float, Array, Hash] value
119
+ # @param [Hash|nil] attrs
120
+ # @return [void]
121
+ def insert(index, value, attrs = nil)
122
+ if value.is_a?(String)
123
+ ytext_insert(transaction, index, value) if attrs.nil?
124
+ unless attrs.nil?
125
+ ytext_insert_with_attributes(transaction, index, value, attrs)
126
+ end
127
+ return nil
128
+ end
129
+
130
+ if can_insert?(value)
131
+ ytext_insert_embed(transaction, index, value) if attrs.nil?
132
+ unless attrs.nil?
133
+ ytext_insert_embed_with_attributes(transaction, index, value, attrs)
134
+ end
135
+ return nil
136
+ end
137
+
138
+ raise ArgumentError,
139
+ "Can't insert value. `#{value.class.name}` isn't supported."
140
+ end
141
+
142
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
143
+
144
+ # Applies formatting to text
145
+ #
146
+ # @example Add formatting to first word
147
+ # doc = Y::Doc.new
148
+ # text = doc.get_text("my text")
149
+ #
150
+ # attrs = {format: "bold"}
151
+ # text.format(0, 2, attrs)
152
+ #
153
+ # @param [Integer] index
154
+ # @param [Integer] length
155
+ # @param [Hash] attrs
156
+ # @return [void]
157
+ def format(index, length, attrs)
158
+ ytext_format(transaction, index, length, attrs)
159
+ end
160
+
161
+ # Returns length of text
162
+ #
163
+ # @return [Integer] Length of text
164
+ def length
165
+ ytext_length
166
+ end
167
+
168
+ alias size length
169
+
170
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
171
+
172
+ # Removes a part from text
173
+ #
174
+ # **Attention:** In comparison to String#slice, {Text#slice!} will not
175
+ # return the substring that gets removed. Even this being technically
176
+ # possible, it requires us to read the substring before removing it, which
177
+ # is not desirable in most situations.
178
+ #
179
+ # @example Removes a single character
180
+ # doc = Y::Doc.new
181
+ #
182
+ # text = doc.get_text("my text")
183
+ # text << "Hello"
184
+ #
185
+ # text.slice!(0)
186
+ #
187
+ # text.to_s == "ello" # true
188
+ #
189
+ # @example Removes a range of characters
190
+ # doc = Y::Doc.new
191
+ #
192
+ # text = doc.get_text("my text")
193
+ # text << "Hello"
194
+ #
195
+ # text.slice!(1..2)
196
+ # text.to_s == "Hlo" # true
197
+ #
198
+ # text.slice!(1...2)
199
+ # text.to_s == "Ho" # true
200
+ #
201
+ # @example Removes a range of chars from start and for given length
202
+ # doc = Y::Doc.new
203
+ #
204
+ # text = doc.get_text("my text")
205
+ # text << "Hello"
206
+ #
207
+ # text.slice!(0, 3)
208
+ #
209
+ # text.to_s == "lo" # true
210
+ #
211
+ # @overload slice!(index)
212
+ # Removes a single character at index
213
+ #
214
+ # @overload slice!(start, length)
215
+ # Removes a range of characters
216
+ #
217
+ # @overload slice!(range)
218
+ # Removes a range of characters
219
+ #
220
+ # @return [void]
221
+ def slice!(*args)
222
+ if args.size.zero?
223
+ raise ArgumentError,
224
+ "Provide one of `index`, `range`, `start, length` as arguments"
225
+ end
226
+
227
+ if args.size == 1
228
+ arg = args.first
229
+
230
+ if arg.is_a?(Range)
231
+ ytext_remove_range(transaction, arg.first, arg.last - arg.first)
232
+ return nil
233
+ end
234
+
235
+ if arg.is_a?(Numeric)
236
+ ytext_remove_range(transaction, arg.to_int, 1)
237
+ return nil
238
+ end
239
+ end
240
+
241
+ if args.size == 2
242
+ start, length = args
243
+
244
+ if start.is_a?(Numeric) && length.is_a?(Numeric)
245
+ ytext_remove_range(transaction, start, length)
246
+ return nil
247
+ end
248
+ end
249
+
250
+ raise ArgumentError, "Please check your arguments, can't slice."
251
+ end
252
+
253
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
254
+
255
+ # Returns string representation of text
256
+ #
257
+ # @example
258
+ # doc = Y::Doc.new
259
+ # text = doc.get_text("my text")
260
+ # text << "Hello"
261
+ #
262
+ # puts text.to_s # "Hello"
263
+ #
264
+ # @return [String]
265
+ def to_s
266
+ ytext_to_s
267
+ end
268
+
269
+ private
270
+
271
+ def can_insert?(value)
272
+ value.is_a?(NilClass) ||
273
+ value.is_a?(Symbol) ||
274
+ [true, false].include?(value) ||
275
+ value.is_a?(Numeric) ||
276
+ value.is_a?(Enumerable) ||
277
+ value.is_a?(Hash)
278
+ end
279
+
280
+ # rubocop:disable Layout/LineLength
281
+
282
+ # @!method ytext_insert(transaction, index, chunk)
283
+ # Insert into text at position
284
+ #
285
+ # @param [Y::Transaction] transaction
286
+ # @param [Integer] index
287
+ # @param [String] chunk
288
+ # @return [nil]
289
+
290
+ # @!method ytext_insert_embed(transaction, index, content)
291
+ # Insert into text at position
292
+ #
293
+ # @param [Y::Transaction] transaction
294
+ # @param [Integer] index
295
+ # @param [Y::Text, Y::Array, Y::Map] content
296
+ # @return [nil]
297
+
298
+ # @!method ytext_insert_embed_with_attributes(transaction, index, embed, attrs)
299
+ # Insert into text at position
300
+ #
301
+ # @param [Y::Transaction] transaction
302
+ # @param [Integer] index
303
+ # @param [Y::Text, Y::Array, Y::Map] embed
304
+ # @param [Hash] attrs
305
+ # @return [nil]
306
+
307
+ # @!method ytext_insert_with_attributes(transaction, index, chunk, attrs)
308
+ # Insert into text at position
309
+ #
310
+ # @param [Y::Transaction] transaction
311
+ # @param [Integer] index
312
+ # @param [String] chunk
313
+ # @param [Hash] attrs
314
+ # @return [nil]
315
+
316
+ # @!method ytext_push(transaction, value)
317
+ # Returns length of text
318
+ #
319
+ # @param [Y::Transaction] transaction
320
+ # @param [String] value
321
+ # @return [nil]
322
+
323
+ # @!method ytext_remove_range(transaction, index, length)
324
+ # Removes a range from text
325
+ #
326
+ # @param [Y::Transaction] transaction
327
+ # @param [Integer] index
328
+ # @param [Integer] length
329
+ # @return [nil]
330
+
331
+ # @!method ytext_format(transaction, index, length, attrs)
332
+ # Formats a text range
333
+ #
334
+ # @param [Y::Transaction] transaction
335
+ # @param [Integer] index
336
+ # @param [Integer] length
337
+ # @param [Hash] attrs
338
+ # @return [nil]
339
+
340
+ # @!method ytext_length()
341
+ # Returns length of text
342
+ #
343
+ # @return [Integer]
344
+
345
+ # @!method ytext_observe(proc)
346
+ # Observe text changes
347
+ #
348
+ # @param [Proc] proc
349
+ # @return [Integer]
350
+
351
+ # @!method ytext_to_s()
352
+ # Returns string representation of text
353
+ #
354
+ # @return [String]
355
+
356
+ # @!method ytext_unobserve(subscription_id)
357
+ # Detach listener
358
+ #
359
+ # @param [Integer] subscription_id
360
+ # @return [void]
361
+
362
+ # rubocop:enable Layout/LineLength
363
+
364
+ # A reference to the current active transaction of the document this map
365
+ # belongs to.
366
+ #
367
+ # @return [Y::Transaction] A transaction object
368
+ def transaction
369
+ document.current_transaction
370
+ end
371
+ end
372
+ end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Y
4
+ class Transaction
5
+ # @!attribute [r] document
6
+ #
7
+ # @return [Y::Doc] The document this array belongs to
8
+ attr_accessor :document
9
+
10
+ def initialize(doc = nil)
11
+ @document = doc
12
+
13
+ super()
14
+ end
15
+
16
+ # Applies the binary encoded update for this document. This will bring the
17
+ # the document to the same state as the one the update is from.
18
+ #
19
+ # @param [::Array<Integer>] update
20
+ # @return [void]
21
+ def apply(update)
22
+ ytransaction_apply_update(update)
23
+ end
24
+
25
+ # Commits transaction
26
+ #
27
+ # @return [void]
28
+ def commit
29
+ ytransaction_commit
30
+ end
31
+
32
+ # Create or get array type
33
+ #
34
+ # @param [String] name
35
+ # @return [Y::Array]
36
+ def get_array(name)
37
+ array = ytransaction_get_array(name)
38
+ array.document = document
39
+ array
40
+ end
41
+
42
+ # Create or get map type
43
+ #
44
+ # @param [String] name
45
+ # @return [Y::Map]
46
+ def get_map(name)
47
+ map = ytransaction_get_map(name)
48
+ map.document = document
49
+ map
50
+ end
51
+
52
+ # Create or get text type
53
+ #
54
+ # @param [String] name
55
+ # @return [Y::Text]
56
+ def get_text(name)
57
+ text = ytransaction_get_text(name)
58
+ text.document = document
59
+ text
60
+ end
61
+
62
+ # Create or get XMLElement type
63
+ #
64
+ # @param [String] name
65
+ # @return [Y::XMLElement]
66
+ def get_xml_element(name)
67
+ xml_element = ytransaction_get_xml_element(name)
68
+ xml_element.document = document
69
+ xml_element
70
+ end
71
+
72
+ # Create or get XMLText type
73
+ #
74
+ # @param [String] name
75
+ # @return [Y::XMLText]
76
+ def get_xml_text(name)
77
+ xml_text = ytransaction_get_xml_text(name)
78
+ xml_text.document = document
79
+ xml_text
80
+ end
81
+
82
+ # Return state vector for transaction
83
+ #
84
+ # @return [::Array<Integer>]
85
+ def state
86
+ ytransaction_state_vector
87
+ end
88
+
89
+ # @!method ytransaction_apply_update(update)
90
+ # Returns or creates an array by name
91
+ #
92
+ # @param [::Array<Integer>] update
93
+ # @return [void]
94
+ # @!visibility private
95
+
96
+ # @!method ytransaction_commit()
97
+ #
98
+ # @return [void]
99
+ # @!visibility private
100
+
101
+ # @!method ytransaction_get_array(name)
102
+ # Returns or creates an array by name
103
+ #
104
+ # @param [String] name Name of the array structure to retrieve or create
105
+ # @return [Y::Array] Array structure
106
+ # @!visibility private
107
+
108
+ # @!method ytransaction_get_map(name)
109
+ # Returns or creates a map structure by name
110
+ #
111
+ # @param [String] name Name of the map structure to retrieve or create
112
+ # @return [Y::Map] Map structure
113
+ # @!visibility private
114
+
115
+ # @!method ytransaction_get_text(name)
116
+ # Returns or creates a text structure by name
117
+ #
118
+ # @param [String] name Name of the text structure to retrieve or create
119
+ # @return [Y::Text] Text structure
120
+ # @!visibility private
121
+
122
+ # @!method ytransaction_get_xml_element(name)
123
+ # Returns or creates a XML structure by name
124
+ #
125
+ # @param [String] name Name of the XML element structure to retrieve or
126
+ # create
127
+ # @return [Y::XMLElement] XMLElement structure
128
+ # @!visibility private
129
+
130
+ # @!method ytransaction_get_xml_text(name)
131
+ # Returns or creates a XML structure by name
132
+ #
133
+ # @param [String] name Name of the XML element structure to retrieve or
134
+ # create
135
+ # @return [Y::XMLElement] XMLElement structure
136
+ # @!visibility private
137
+
138
+ # @!method ytransaction_state_vector
139
+ #
140
+ # @return [Array<Integer>]
141
+ # @!visibility private
142
+ end
143
+ end
data/lib/y/version.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Y
4
+ VERSION = "0.1.4.beta.1"
5
+ end