y-rb 0.1.4.alpha.3-x86_64-linux-musl

Sign up to get free protection for your applications and to get access to all the features.
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.alpha.3"
5
+ end