y-rb 0.6.0-x86_64-linux-gnu
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/ext/yrb/Cargo.toml +21 -0
- data/ext/yrb/extconf.rb +6 -0
- data/ext/yrb/src/lib.rs +649 -0
- data/ext/yrb/src/utils.rs +64 -0
- data/ext/yrb/src/yany.rs +46 -0
- data/ext/yrb/src/yarray.rs +191 -0
- data/ext/yrb/src/yattrs.rs +39 -0
- data/ext/yrb/src/yawareness.rs +151 -0
- data/ext/yrb/src/ydiff.rs +19 -0
- data/ext/yrb/src/ydoc.rs +113 -0
- data/ext/yrb/src/ymap.rs +175 -0
- data/ext/yrb/src/ytext.rs +235 -0
- data/ext/yrb/src/ytransaction.rs +127 -0
- data/ext/yrb/src/yvalue.rs +256 -0
- data/ext/yrb/src/yxml_element.rs +280 -0
- data/ext/yrb/src/yxml_fragment.rs +129 -0
- data/ext/yrb/src/yxml_text.rs +177 -0
- data/lib/3.1/yrb.so +0 -0
- data/lib/3.2/yrb.so +0 -0
- data/lib/3.3/yrb.so +0 -0
- data/lib/3.4/yrb.so +0 -0
- data/lib/y/array.rb +371 -0
- data/lib/y/awareness.rb +290 -0
- data/lib/y/diff.rb +38 -0
- data/lib/y/doc.rb +313 -0
- data/lib/y/map.rb +199 -0
- data/lib/y/text.rb +383 -0
- data/lib/y/transaction.rb +189 -0
- data/lib/y/version.rb +5 -0
- data/lib/y/xml.rb +1141 -0
- data/lib/y-rb.rb +24 -0
- data/lib/y.rb +3 -0
- metadata +143 -0
data/lib/y/awareness.rb
ADDED
@@ -0,0 +1,290 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Y
|
4
|
+
# The Awareness class implements a simple shared state protocol that can be
|
5
|
+
# used for non-persistent data like awareness information (cursor, username,
|
6
|
+
# status, ..). Each client can update its own local state and listen to state
|
7
|
+
# changes of remote clients.
|
8
|
+
#
|
9
|
+
# Each client is identified by a unique client id (something we borrow from
|
10
|
+
# doc.client_id). A client can override its own state by propagating a message
|
11
|
+
# with an increasing timestamp (clock). If such a message is received, it is
|
12
|
+
# applied if the known state of that client is older than the new state
|
13
|
+
# (`clock < new_clock`). If a client thinks that a remote client is offline,
|
14
|
+
# it may propagate a message with `{ clock, state: null, client }`. If such a
|
15
|
+
# message is received, and the known clock of that client equals the received
|
16
|
+
# clock, it will clean the state.
|
17
|
+
#
|
18
|
+
# Before a client disconnects, it should propagate a null state with an
|
19
|
+
# updated clock.
|
20
|
+
#
|
21
|
+
# Awareness is an integral part of collaborative applications, you can read
|
22
|
+
# more about the concept here: https://docs.yjs.dev/getting-started/adding-awareness
|
23
|
+
#
|
24
|
+
# @example Instantiate awareness instance and encode update for broadcast
|
25
|
+
# local_state = {
|
26
|
+
# editing: { field: "description", pos: 0 },
|
27
|
+
# name: "Hannes Moser"
|
28
|
+
# }
|
29
|
+
#
|
30
|
+
# awareness = Y::Awareness.new
|
31
|
+
# awareness.local_state = local_state
|
32
|
+
# awareness.diff # [1,227,245,175,195,11,1,65,123, …]
|
33
|
+
#
|
34
|
+
# @example Two connected clients
|
35
|
+
# local_state_a = { name: "User A" }
|
36
|
+
#
|
37
|
+
# client_a = Y::Awareness.new
|
38
|
+
# client_a.local_state = local_state
|
39
|
+
#
|
40
|
+
# local_state_b = { name: "User B" }
|
41
|
+
#
|
42
|
+
# client_b = Y::Awareness.new
|
43
|
+
# client_b.local_state = local_state_b
|
44
|
+
#
|
45
|
+
# client_a.sync(client_b.diff)
|
46
|
+
# client_a.clients # {1242157267=>"{\"name\":\"User A\"}", 2401067547=>…
|
47
|
+
class Awareness
|
48
|
+
# Applies an incoming update. This gets the local awareness instance in
|
49
|
+
# sync with changes from another client. i.e., updates the state of another
|
50
|
+
# user in the local awareness instance.
|
51
|
+
#
|
52
|
+
# @example Apply an incoming update
|
53
|
+
# update = [1,227,245,175,195,11,1,65,123, …]
|
54
|
+
#
|
55
|
+
# awareness = Y::Awareness.new
|
56
|
+
# awareness.sync(update)
|
57
|
+
#
|
58
|
+
# @param diff [Array<Integer>] A binary encoded update
|
59
|
+
# @return [void]
|
60
|
+
def sync(diff)
|
61
|
+
yawareness_apply_update(diff)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Clears out a state of a current client, effectively marking it as
|
65
|
+
# disconnected.
|
66
|
+
#
|
67
|
+
# @return [void]
|
68
|
+
def clean_local_state
|
69
|
+
yawareness_clean_local_state
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns a globally unique client ID of an underlying Doc.
|
73
|
+
#
|
74
|
+
# @return [Integer] Returns the client_id of the local user
|
75
|
+
def client_id
|
76
|
+
yawareness_client_id
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns a state map of all of the clients tracked by current Awareness
|
80
|
+
# instance. Those states are identified by their corresponding ClientIDs.
|
81
|
+
# The associated state is represented and replicated to other clients as a
|
82
|
+
# JSON string.
|
83
|
+
#
|
84
|
+
# @example Instantiate awareness instance and encode update for broadcast
|
85
|
+
# local_state = {
|
86
|
+
# editing: { field: "description", pos: 0 },
|
87
|
+
# name: "Hannes Moser"
|
88
|
+
# }
|
89
|
+
#
|
90
|
+
# awareness = Y::Awareness.new
|
91
|
+
# awareness.local_state = local_state
|
92
|
+
# awareness.clients # {312134501=>"{\"editing\":{\"field\":\"descriptio …
|
93
|
+
#
|
94
|
+
# @return [Hash] All clients and their current state
|
95
|
+
def clients
|
96
|
+
transform = yawareness_clients.map do |client_id, state|
|
97
|
+
[client_id, JSON.parse!(state)]
|
98
|
+
end
|
99
|
+
transform.to_h
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns the state of the local Awareness instance.
|
103
|
+
#
|
104
|
+
# @example Create local state and inspect it
|
105
|
+
# local_state = {
|
106
|
+
# editing: { field: "description", pos: 0 },
|
107
|
+
# name: "Hannes Moser"
|
108
|
+
# }
|
109
|
+
#
|
110
|
+
# awareness = Y::Awareness.new
|
111
|
+
# awareness.local_state = local_state
|
112
|
+
# awareness.local_state # { editing: { field: "description", ...
|
113
|
+
#
|
114
|
+
# @return [String] The current state of the local client
|
115
|
+
def local_state
|
116
|
+
json = yawareness_local_state
|
117
|
+
JSON.parse!(json) if json
|
118
|
+
end
|
119
|
+
|
120
|
+
# Sets a current Awareness instance state to a corresponding JSON string.
|
121
|
+
# This state will be replicated to other clients as part of the
|
122
|
+
# AwarenessUpdate.
|
123
|
+
#
|
124
|
+
# @example Set local state
|
125
|
+
# local_state = {
|
126
|
+
# editing: { field: "description", pos: 0 },
|
127
|
+
# name: "Hannes Moser"
|
128
|
+
# }
|
129
|
+
#
|
130
|
+
# awareness = Y::Awareness.new
|
131
|
+
# awareness.local_state = local_state
|
132
|
+
#
|
133
|
+
# @param [#to_json] state
|
134
|
+
# @return [void]
|
135
|
+
def local_state=(state)
|
136
|
+
raise "state cannot be encoded to JSON" unless state.respond_to? :to_json
|
137
|
+
|
138
|
+
yawareness_set_local_state(state.to_json)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Subscribes to changes
|
142
|
+
#
|
143
|
+
# @return [Integer] The subscription ID
|
144
|
+
def attach(callback = nil, &block)
|
145
|
+
return yawareness_on_update(callback) unless callback.nil?
|
146
|
+
|
147
|
+
yawareness_on_update(block.to_proc) unless block.nil?
|
148
|
+
end
|
149
|
+
|
150
|
+
# Clears out a state of a given client, effectively marking it as
|
151
|
+
# disconnected.
|
152
|
+
#
|
153
|
+
# @param client_id [Integer] Clears the state for given client_id
|
154
|
+
# @return [void]
|
155
|
+
def remove_state(client_id)
|
156
|
+
yawareness_remove_state(client_id)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Returns a serializable update object which is representation of a current
|
160
|
+
# Awareness state.
|
161
|
+
#
|
162
|
+
# @return [::Array<Integer>] Binary encoded update of the local instance
|
163
|
+
def diff
|
164
|
+
yawareness_update
|
165
|
+
end
|
166
|
+
|
167
|
+
# Returns a serializable update object which is representation of a current
|
168
|
+
# Awareness state. Unlike Awareness::update, this method variant allows to
|
169
|
+
# prepare update only for a subset of known clients. These clients must all
|
170
|
+
# be known to a current Awareness instance, otherwise a
|
171
|
+
# Error::ClientNotFound error will be returned.
|
172
|
+
#
|
173
|
+
# @param clients [::Array<Integer>] A list of client IDs
|
174
|
+
# @return [String] Binary encoded update including all given client IDs
|
175
|
+
def diff_with_clients(*clients)
|
176
|
+
yawareness_update_with_clients(clients)
|
177
|
+
end
|
178
|
+
|
179
|
+
# rubocop:disable Lint/UselessAccessModifier
|
180
|
+
private
|
181
|
+
|
182
|
+
# @!method yawareness_apply_update(update)
|
183
|
+
# Applies an update
|
184
|
+
#
|
185
|
+
# @param A [Y::AwarenessUpdate] Structure that represents an encodable state
|
186
|
+
# of an Awareness struct.
|
187
|
+
# @!visibility private
|
188
|
+
|
189
|
+
# @!method yawareness_apply_update(update)
|
190
|
+
# Applies an update
|
191
|
+
#
|
192
|
+
# @param A [Y::AwarenessUpdate] Structure that represents an encodable state
|
193
|
+
# of an Awareness struct.
|
194
|
+
# @!visibility private
|
195
|
+
|
196
|
+
# @!method yawareness_clean_local_state
|
197
|
+
# Clears out a state of a current client , effectively marking it as
|
198
|
+
# disconnected.
|
199
|
+
# @!visibility private
|
200
|
+
|
201
|
+
# @!method yawareness_client_id
|
202
|
+
# Returns a globally unique client ID of an underlying Doc.
|
203
|
+
# @return [Integer] The Client ID
|
204
|
+
# @!visibility private
|
205
|
+
|
206
|
+
# @!method yawareness_clients
|
207
|
+
# Returns a state map of all of the clients
|
208
|
+
# tracked by current Awareness instance. Those states are identified by
|
209
|
+
# their corresponding ClientIDs. The associated state is represented and
|
210
|
+
# replicated to other clients as a JSON string.
|
211
|
+
#
|
212
|
+
# @return [Hash<Integer, String>] Map of clients
|
213
|
+
# @!visibility private
|
214
|
+
|
215
|
+
# @!method yawareness_local_state
|
216
|
+
#
|
217
|
+
# @return [String, nil] Returns a JSON string state representation of a
|
218
|
+
# current Awareness instance.
|
219
|
+
# @!visibility private
|
220
|
+
|
221
|
+
# @!method yawareness_on_update(callback, &block)
|
222
|
+
#
|
223
|
+
# @param callback [callback]
|
224
|
+
# @return [Integer] The subscription ID
|
225
|
+
# @!visibility private
|
226
|
+
|
227
|
+
# @!method yawareness_remove_on_update(subscription_id)
|
228
|
+
#
|
229
|
+
# @param subscription_id [Integer] The subscription id to remove
|
230
|
+
# @!visibility private
|
231
|
+
|
232
|
+
# @!method yawareness_remove_state(client_id)
|
233
|
+
# Clears out a state of a given client, effectively marking it as
|
234
|
+
# disconnected.
|
235
|
+
#
|
236
|
+
# @param client_id [Integer] A Client ID
|
237
|
+
# @return [String, nil] Returns a JSON string state representation of a
|
238
|
+
# current Awareness instance.
|
239
|
+
# @!visibility private
|
240
|
+
|
241
|
+
# @!method yawareness_set_local_state(state)
|
242
|
+
# Sets a current Awareness instance state to a corresponding JSON string.
|
243
|
+
# This state will be replicated to other clients as part of the
|
244
|
+
# AwarenessUpdate and it will trigger an event to be emitted if current
|
245
|
+
# instance was created using [Awareness::with_observer] method.
|
246
|
+
#
|
247
|
+
# @param Returns [String] A state map of all of the clients tracked by
|
248
|
+
# current Awareness instance. Those states are identified by their
|
249
|
+
# corresponding ClientIDs. The associated state is represented and
|
250
|
+
# replicated to other clients as a JSON string.
|
251
|
+
# @!visibility private
|
252
|
+
|
253
|
+
# @!method yawareness_update
|
254
|
+
# Returns a serializable update object which is representation of a
|
255
|
+
# current Awareness state.
|
256
|
+
#
|
257
|
+
# @return [Y::AwarenessUpdate] The update object
|
258
|
+
# @!visibility private
|
259
|
+
|
260
|
+
# @!method yawareness_update_with_clients(clients)
|
261
|
+
# Returns a serializable update object which is representation of a
|
262
|
+
# current Awareness state. Unlike [Y::Awareness#update], this method
|
263
|
+
# variant allows to prepare update only for a subset of known clients.
|
264
|
+
# These clients must all be known to a current Awareness instance,
|
265
|
+
# otherwise an error will be returned.
|
266
|
+
#
|
267
|
+
# @param clients [::Array<Integer>]
|
268
|
+
# @return [::Array<Integer>] A serialized (binary encoded) update object
|
269
|
+
# @!visibility private
|
270
|
+
|
271
|
+
# rubocop:enable Lint/UselessAccessModifier
|
272
|
+
end
|
273
|
+
|
274
|
+
# @!visibility private
|
275
|
+
class AwarenessEvent
|
276
|
+
private # rubocop:disable Lint/UselessAccessModifier
|
277
|
+
|
278
|
+
# @!method added
|
279
|
+
# @return [::Array<Integer>] Added clients
|
280
|
+
# @!visibility private
|
281
|
+
|
282
|
+
# @!method updated
|
283
|
+
# @return [::Array<Integer>] Updated clients
|
284
|
+
# @!visibility private
|
285
|
+
|
286
|
+
# @!method removed
|
287
|
+
# @return [::Array<Integer>] Removed clients
|
288
|
+
# @!visibility private
|
289
|
+
end
|
290
|
+
end
|
data/lib/y/diff.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Y
|
4
|
+
# A representation of an uniformly-formatted chunk of rich context stored by
|
5
|
+
# Text or XmlText. It contains a value (which could be a string, embedded
|
6
|
+
# object or another shared type) with optional formatting attributes wrapping
|
7
|
+
# around this chunk.
|
8
|
+
class Diff
|
9
|
+
# @return [Object]
|
10
|
+
def insert
|
11
|
+
ydiff_insert
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Hash]
|
15
|
+
def attrs
|
16
|
+
ydiff_attrs
|
17
|
+
end
|
18
|
+
|
19
|
+
# Convert the diff to a Hash representation
|
20
|
+
#
|
21
|
+
# @return [Hash]
|
22
|
+
def to_h
|
23
|
+
{
|
24
|
+
insert: ydiff_insert,
|
25
|
+
attrs: ydiff_attrs
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
# @!method ydiff_insert()
|
30
|
+
# Returns string representation of text
|
31
|
+
#
|
32
|
+
# @return [Object]
|
33
|
+
|
34
|
+
# @!method ydiff_attrs()
|
35
|
+
#
|
36
|
+
# @return [Hash]
|
37
|
+
end
|
38
|
+
end
|
data/lib/y/doc.rb
ADDED
@@ -0,0 +1,313 @@
|
|
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
|
+
ZERO_STATE_V2 = [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0].freeze
|
23
|
+
private_constant :ZERO_STATE_V2
|
24
|
+
|
25
|
+
# Attach a listener to document changes. If one of the data structures is
|
26
|
+
# changes, the block is called with the update as its only argument.
|
27
|
+
#
|
28
|
+
# @yield [update] Called when document is updated
|
29
|
+
# @yieldparam [Array<Integer>] update The encoded document updates
|
30
|
+
|
31
|
+
# Example: Attach listener to document changes
|
32
|
+
# doc = described_class.new
|
33
|
+
# doc.attach { |update| pp update }
|
34
|
+
#
|
35
|
+
# text = doc.get_text("my text")
|
36
|
+
# text << "1"
|
37
|
+
def attach(&block)
|
38
|
+
ydoc_observe_update(block)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Commit current transaction
|
42
|
+
#
|
43
|
+
# This is a convenience method that invokes {Y::Transaction#commit} on the
|
44
|
+
# current transaction used by this document.
|
45
|
+
#
|
46
|
+
# @return [void]
|
47
|
+
def commit
|
48
|
+
current_transaction(&:commit)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Create a diff between this document and another document. The diff is
|
52
|
+
# created based on a state vector provided by the other document. It only
|
53
|
+
# returns the missing blocks, as binary encoded sequence.
|
54
|
+
#
|
55
|
+
# @param state [::Array<Integer>] The state to create the diff against
|
56
|
+
# @return [::Array<Integer>] Binary encoded diff
|
57
|
+
def diff(state = ZERO_STATE)
|
58
|
+
current_transaction { |tx| ydoc_encode_diff_v1(tx, state) }
|
59
|
+
end
|
60
|
+
|
61
|
+
# Create a v2 diff between this document and another document. The diff is
|
62
|
+
# created based on a state vector provided by the other document. It only
|
63
|
+
# returns the missing blocks, as binary encoded sequence.
|
64
|
+
#
|
65
|
+
# @param state [::Array<Integer>] The state to create the diff against
|
66
|
+
# @return [::Array<Integer>] Binary encoded diff
|
67
|
+
def diff_v2(state = ZERO_STATE_V2)
|
68
|
+
current_transaction { |tx| ydoc_encode_diff_v2(tx, state) }
|
69
|
+
end
|
70
|
+
|
71
|
+
# Creates a full diff for the current document. It is similar to {#diff},
|
72
|
+
# but does not take a state. Instead it creates an empty state and passes it
|
73
|
+
# to the encode_diff function.
|
74
|
+
#
|
75
|
+
# @return [::Array<Integer>] Binary encoded diff
|
76
|
+
def full_diff
|
77
|
+
diff
|
78
|
+
end
|
79
|
+
|
80
|
+
# Gets or creates a new array by name
|
81
|
+
#
|
82
|
+
# If the optional values array is present, fills the array up with elements
|
83
|
+
# from the provided array. If the array already exists and isn't
|
84
|
+
# empty, elements are pushed to the end of the array.
|
85
|
+
#
|
86
|
+
# @param name [String] The name of the structure
|
87
|
+
# @param values [::Array] Optional initial values
|
88
|
+
# @return [Y::Array]
|
89
|
+
def get_array(name, values = nil)
|
90
|
+
array = ydoc_get_or_insert_array(name)
|
91
|
+
array.document = self
|
92
|
+
array.concat(values) unless values.nil?
|
93
|
+
array
|
94
|
+
end
|
95
|
+
|
96
|
+
# Gets or creates a new map by name
|
97
|
+
#
|
98
|
+
# If the optional input hash is present, fills the map up with key-value
|
99
|
+
# pairs from the provided input hash. If the map already exists and isn't
|
100
|
+
# empty, any existing keys are overridden and new keys are added.
|
101
|
+
#
|
102
|
+
# @param name [String] The name of the structure
|
103
|
+
# @param input [Hash] Optional initial map key-value pairs
|
104
|
+
# @return [Y::Map]
|
105
|
+
def get_map(name, input = nil)
|
106
|
+
map = ydoc_get_or_insert_map(name)
|
107
|
+
map.document = self
|
108
|
+
input&.each { |key, value| map[key] = value }
|
109
|
+
map
|
110
|
+
end
|
111
|
+
|
112
|
+
# Gets or creates a new text by name
|
113
|
+
#
|
114
|
+
# If the optional input string is provided, fills a new text with the string
|
115
|
+
# at creation time. If the text isn't new and not empty, appends the input
|
116
|
+
# to the end of the text.
|
117
|
+
#
|
118
|
+
# @param name [String] The name of the structure
|
119
|
+
# @param input [String] Optional initial text value
|
120
|
+
# @return [Y::Text]
|
121
|
+
def get_text(name, input = nil)
|
122
|
+
text = ydoc_get_or_insert_text(name)
|
123
|
+
text.document = self
|
124
|
+
text << input unless input.nil?
|
125
|
+
text
|
126
|
+
end
|
127
|
+
|
128
|
+
# Gets or creates a new XMLElement by name
|
129
|
+
#
|
130
|
+
# @param name [String] The name of the structure
|
131
|
+
# @return [Y::XMLElement]
|
132
|
+
def get_xml_element(name)
|
133
|
+
xml_element = ydoc_get_or_insert_xml_element(name)
|
134
|
+
xml_element.document = self
|
135
|
+
xml_element
|
136
|
+
end
|
137
|
+
|
138
|
+
# Gets or creates a new XMLFragment by name
|
139
|
+
#
|
140
|
+
# @param name [String] The name of the fragment
|
141
|
+
# @return [Y::XMLFragment]
|
142
|
+
def get_xml_fragment(name)
|
143
|
+
xml_fragment = ydoc_get_or_insert_xml_fragment(name)
|
144
|
+
xml_fragment.document = self
|
145
|
+
xml_fragment
|
146
|
+
end
|
147
|
+
|
148
|
+
# Gets or creates a new XMLText by name
|
149
|
+
#
|
150
|
+
# @param name [String] The name of the structure
|
151
|
+
# @param input [String] Optional initial text value
|
152
|
+
# @return [Y::XMLText]
|
153
|
+
def get_xml_text(name, input = nil)
|
154
|
+
xml_text = ydoc_get_or_insert_xml_text(name)
|
155
|
+
xml_text.document = self
|
156
|
+
xml_text << input unless input.nil?
|
157
|
+
xml_text
|
158
|
+
end
|
159
|
+
|
160
|
+
# Creates a state vector of this document. This can be used to compare the
|
161
|
+
# state of two documents with each other and to later on sync them.
|
162
|
+
#
|
163
|
+
# @return [::Array<Integer>] Binary encoded state vector
|
164
|
+
def state
|
165
|
+
current_transaction(&:state)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Creates a v2 state vector of this document. This can be used to compare
|
169
|
+
# the state of two documents with each other and to later on sync them.
|
170
|
+
#
|
171
|
+
# @return [::Array<Integer>] Binary encoded state vector
|
172
|
+
def state_v2
|
173
|
+
current_transaction(&:state_v2)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Synchronizes this document with the diff from another document
|
177
|
+
#
|
178
|
+
# @param diff [::Array<Integer>] Binary encoded update
|
179
|
+
# @return [void]
|
180
|
+
def sync(diff)
|
181
|
+
current_transaction { |tx| tx.apply(diff) }
|
182
|
+
end
|
183
|
+
|
184
|
+
# Synchronizes this document with the v2 diff from another document
|
185
|
+
#
|
186
|
+
# @param diff [::Array<Integer>] Binary encoded update
|
187
|
+
# @return [void]
|
188
|
+
def sync_v2(diff)
|
189
|
+
current_transaction { |tx| tx.apply_v2(diff) }
|
190
|
+
end
|
191
|
+
|
192
|
+
# Restores a specific document from an update that contains full state
|
193
|
+
#
|
194
|
+
# This is doing the same as {#sync}, but it exists to be explicit about
|
195
|
+
# the intent. This is the companion to {#full_diff}.
|
196
|
+
#
|
197
|
+
# @param full_diff [::Array<Integer>] Binary encoded update
|
198
|
+
# @return [void]
|
199
|
+
def restore(full_diff)
|
200
|
+
current_transaction { |tx| tx.apply(full_diff) }
|
201
|
+
end
|
202
|
+
|
203
|
+
# Creates a new transaction
|
204
|
+
def transact
|
205
|
+
# 1. release potentially existing transaction
|
206
|
+
if @current_transaction
|
207
|
+
@current_transaction.free
|
208
|
+
@current_transaction = nil
|
209
|
+
end
|
210
|
+
|
211
|
+
# 2. store new transaction in instance variable
|
212
|
+
@current_transaction = ydoc_transact
|
213
|
+
@current_transaction.document = self
|
214
|
+
|
215
|
+
# 3. call block with reference to current_transaction
|
216
|
+
yield @current_transaction
|
217
|
+
ensure
|
218
|
+
@current_transaction&.free
|
219
|
+
@current_transaction = nil
|
220
|
+
end
|
221
|
+
|
222
|
+
# @!visibility private
|
223
|
+
def current_transaction(&block)
|
224
|
+
raise "provide a block" unless block
|
225
|
+
|
226
|
+
# 1. instance variable is set, just use it
|
227
|
+
return yield @current_transaction if @current_transaction
|
228
|
+
|
229
|
+
# 2. forward block to transact
|
230
|
+
transact(&block) unless @current_transaction
|
231
|
+
end
|
232
|
+
|
233
|
+
# @!method ydoc_encode_diff_v1(tx, state_vector)
|
234
|
+
# Encodes the diff of current document state vs provided state
|
235
|
+
#
|
236
|
+
# @example Create transaction on doc
|
237
|
+
# doc = Y::Doc.new
|
238
|
+
# tx = doc.ydoc_encode_diff_v1(other_state)
|
239
|
+
#
|
240
|
+
# @return [Array<Integer>] Binary encoded update
|
241
|
+
# @!visibility private
|
242
|
+
|
243
|
+
# @!method ydoc_encode_diff_v2(tx, state_vector)
|
244
|
+
# Encodes the diff of current document state vs provided state in the v2
|
245
|
+
# format
|
246
|
+
#
|
247
|
+
# @example Create transaction on doc
|
248
|
+
# doc = Y::Doc.new
|
249
|
+
# tx = doc.ydoc_encode_diff_v2(other_state)
|
250
|
+
#
|
251
|
+
# @return [Array<Integer>] Binary encoded update
|
252
|
+
# @!visibility private
|
253
|
+
|
254
|
+
# @!method ydoc_transact
|
255
|
+
# Creates a new transaction for the document
|
256
|
+
#
|
257
|
+
# @example Create transaction on doc
|
258
|
+
# doc = Y::Doc.new
|
259
|
+
# tx = doc.ydoc_transact
|
260
|
+
#
|
261
|
+
# @return [Y::Transaction] The transaction object
|
262
|
+
# @!visibility private
|
263
|
+
|
264
|
+
# @!method ydoc_get_or_insert_array(name)
|
265
|
+
# Creates a new array for the document
|
266
|
+
#
|
267
|
+
# @param [String] name
|
268
|
+
# @return [Y::Array]
|
269
|
+
# @!visibility private
|
270
|
+
|
271
|
+
# @!method ydoc_get_or_insert_map(name)
|
272
|
+
# Creates a new map for the document
|
273
|
+
#
|
274
|
+
# @param [String] name
|
275
|
+
# @return [Y::Map]
|
276
|
+
# @!visibility private
|
277
|
+
|
278
|
+
# @!method ydoc_get_or_insert_text(name)
|
279
|
+
# Creates a new text for the document
|
280
|
+
#
|
281
|
+
# @param [String] name
|
282
|
+
# @return [Y::Text]
|
283
|
+
# @!visibility private
|
284
|
+
|
285
|
+
# @!method ydoc_get_or_insert_xml_element(name)
|
286
|
+
# Creates a new XMLText for the document
|
287
|
+
#
|
288
|
+
# @param [String] name
|
289
|
+
# @return [Y::XMLElement]
|
290
|
+
# @!visibility private
|
291
|
+
|
292
|
+
# @!method ydoc_get_or_insert_xml_fragment(name)
|
293
|
+
# Creates a new XMLFragment for the document
|
294
|
+
#
|
295
|
+
# @param [String] name
|
296
|
+
# @return [Y::XMLFragment]
|
297
|
+
# @!visibility private
|
298
|
+
|
299
|
+
# @!method ydoc_get_or_insert_xml_text(name)
|
300
|
+
# Creates a new XMLText for the document
|
301
|
+
#
|
302
|
+
# @param [String] name
|
303
|
+
# @return [Y::XMLText]
|
304
|
+
# @!visibility private
|
305
|
+
|
306
|
+
# @!method ydoc_observe_update(block)
|
307
|
+
# Creates a subscription to observe changes to the document
|
308
|
+
#
|
309
|
+
# @param [Proc] block
|
310
|
+
# @return [Integer]
|
311
|
+
# @!visibility private
|
312
|
+
end
|
313
|
+
end
|