yrby-rails 0.7.0 → 0.9.0
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 +4 -4
- data/CHANGELOG-rails.md +69 -0
- data/README.md +57 -13
- data/app/assets/javascript/yrby/yjs.js +9604 -0
- data/app/assets/javascript/yrby/yjs.js.map +7 -0
- data/app/assets/javascript/yrby/yrby-client.js +1906 -0
- data/app/assets/javascript/yrby/yrby-client.js.map +7 -0
- data/app/channels/y/document_channel.rb +3 -1
- data/lib/generators/yrby/install/USAGE +3 -1
- data/lib/generators/yrby/install/install_generator.rb +39 -3
- data/lib/y/action_cable/sync.rb +18 -4
- data/lib/y/action_cable.rb +11 -0
- data/lib/y/collaborative/attribute.rb +37 -0
- data/lib/y/collaborative/helper.rb +14 -11
- data/lib/y/collaborative.rb +20 -16
- data/lib/yrby/engine.rb +6 -1
- data/lib/yrby/rails/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 56e31c8cc1697d3894702fdfff977817fe1c22bb95318718dd3b0940171ed74f
|
|
4
|
+
data.tar.gz: 2a3a49d17af5c0d690ab98ca0c3bf6f41f7c79d0a9b6041f3dd49b98f0829e84
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 148304e573708e6ace7234f5de8ecde4020fa8139f4daf8284598b6a6c79f478791c342dc7b70ab91e4d65e9bbf11eb4dc88a82ea364c027d44714337228a9a7
|
|
7
|
+
data.tar.gz: 9b929e7b58b7524052bb18026aee2ec7a90d82b22fd8d0eb3b61275d4c912da416a3a442121a516643da8621984ff06e843f3405ad74eb64766e14005c00893b
|
data/CHANGELOG-rails.md
CHANGED
|
@@ -7,6 +7,75 @@ this project aims to follow [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.9.0] - 2026-11-09
|
|
11
|
+
|
|
12
|
+
Requires yrby 0.9.0 or later.
|
|
13
|
+
|
|
14
|
+
### Security
|
|
15
|
+
|
|
16
|
+
- `Y::ActionCable::Sync`'s methods and `Y::DocumentChannel`'s
|
|
17
|
+
`authorized_document_key` accessors are private. Action Cable treats a
|
|
18
|
+
channel's public methods as actions a client can call, so a client could call
|
|
19
|
+
`sync_subscribed` directly and skip the channel's own `subscribed` and
|
|
20
|
+
`receive`. Channels that call these from `subscribed` and `receive` keep
|
|
21
|
+
working. Code that calls them on a channel from outside, such as a test, now
|
|
22
|
+
needs `send`.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- `bin/rails generate yrby:install` pins `yrby-client`, `yrby-client/element`,
|
|
27
|
+
`yjs`, and `@rails/actioncable` when the app has `config/importmap.rb`. The
|
|
28
|
+
gem ships `yrby/yrby-client.js` and `yrby/yjs.js` for those pins, so an
|
|
29
|
+
import-map app needs no npm packages. Each shared module has one pin, so the
|
|
30
|
+
page and its editor binding load one copy of yjs. Pins the app already has
|
|
31
|
+
are left alone.
|
|
32
|
+
- `has_collaborative_document :body, channel: "PostDocumentChannel"` names
|
|
33
|
+
an app's own `Y::DocumentChannel` subclass for an attribute.
|
|
34
|
+
`collaborative_document(:body).channel` returns it, so
|
|
35
|
+
`collaborative_document_tag` and Ruby clients subscribe there without
|
|
36
|
+
passing `channel:` each time.
|
|
37
|
+
|
|
38
|
+
## [0.8.0] - 2026-11-03
|
|
39
|
+
|
|
40
|
+
Requires yrby 0.9.0 or later.
|
|
41
|
+
|
|
42
|
+
### Experimental
|
|
43
|
+
|
|
44
|
+
These are new, and they may change in a minor release. See
|
|
45
|
+
[docs/experimental.md](docs/experimental.md).
|
|
46
|
+
|
|
47
|
+
- `collaborative_document(:body).edit { |doc| ... }` edits a record's
|
|
48
|
+
document from Ruby. It records the change in the document's rows, encrypted
|
|
49
|
+
ones included, and broadcasts it to open editors.
|
|
50
|
+
`Y::ActionCable.broadcast(key, update)` sends an already recorded update.
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- `collaborative_document(name)` answers `grant(expires_in:)` and `channel`:
|
|
55
|
+
the grant a page or client subscribes with, and the channel that accepts
|
|
56
|
+
it. A gem with its own grants and channel, such as lexxy-realtime, returns
|
|
57
|
+
its own document with both.
|
|
58
|
+
|
|
59
|
+
### Changed
|
|
60
|
+
|
|
61
|
+
- `collaborative_document_tag` renders the document's grant and channel, so
|
|
62
|
+
it works for a gem that returns its own document. `channel:` still
|
|
63
|
+
overrides the channel.
|
|
64
|
+
|
|
65
|
+
### Deprecated
|
|
66
|
+
|
|
67
|
+
- `collaborative_sgid(name, expires_in:)`. Use
|
|
68
|
+
`collaborative_document(name).grant(expires_in:)`. It logs through
|
|
69
|
+
`Y.deprecator`, which the engine adds to `Rails.application.deprecators`,
|
|
70
|
+
and goes away in yrby-rails 0.9.
|
|
71
|
+
|
|
72
|
+
### Fixed
|
|
73
|
+
|
|
74
|
+
- The channel relays a client's SyncStep2 to peers as a plain update.
|
|
75
|
+
Relayed as is, a peer that was still joining could take it for the answer
|
|
76
|
+
to its own handshake and report itself loaded before it had the server's
|
|
77
|
+
state.
|
|
78
|
+
|
|
10
79
|
## [0.7.0] - 2026-10-01
|
|
11
80
|
|
|
12
81
|
### Added
|
data/README.md
CHANGED
|
@@ -76,15 +76,21 @@ doc = post.collaborative_document(:body).y_doc
|
|
|
76
76
|
doc.read_text("content") # or Y::Lexxy.new(doc).to_html for rich text
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
Install the gem and
|
|
79
|
+
Install the gem and run its generator:
|
|
80
80
|
|
|
81
81
|
```
|
|
82
|
-
|
|
83
|
-
npm install yrby-client yjs y-protocols @rails/actioncable
|
|
84
|
-
|
|
82
|
+
bundle add yrby-rails # depends on yrby
|
|
85
83
|
bin/rails generate yrby:install && bin/rails db:migrate
|
|
86
84
|
```
|
|
87
85
|
|
|
86
|
+
In an app that uses import maps, the generator pins `yrby-client`, `yjs`, and
|
|
87
|
+
`@rails/actioncable` to JavaScript the gem ships. With a bundler, install the
|
|
88
|
+
npm packages instead:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
npm install yrby-client yjs y-protocols @rails/actioncable
|
|
92
|
+
```
|
|
93
|
+
|
|
88
94
|
## Contents
|
|
89
95
|
|
|
90
96
|
- [Scope](#scope)
|
|
@@ -101,6 +107,7 @@ bin/rails generate yrby:install && bin/rails db:migrate
|
|
|
101
107
|
- [Rendering to HTML](#rendering-to-html)
|
|
102
108
|
- [Protocol codec (module functions)](#protocol-codec-module-functions)
|
|
103
109
|
- [ActionCable Integration](#actioncable-integration)
|
|
110
|
+
- [Experimental: writing from Ruby, and collaborators](#experimental-writing-from-ruby-and-collaborators)
|
|
104
111
|
- [Thread Safety](#thread-safety)
|
|
105
112
|
- [Parallelism (GVL release)](#parallelism-gvl-release)
|
|
106
113
|
- [Message Type Constants](#message-type-constants)
|
|
@@ -117,7 +124,9 @@ primitives. By default the Ruby side treats a document as opaque CRDT state. It
|
|
|
117
124
|
applies updates, answers sync handshakes, and records deltas without reading
|
|
118
125
|
the contents, and the browser editor decides what shape the document has. When
|
|
119
126
|
you do need to look inside, `Doc#read_text` and `Doc#read_map` rebuild it in
|
|
120
|
-
Ruby
|
|
127
|
+
Ruby, and live handles (`Doc#get_map`, `get_array`, `get_text`,
|
|
128
|
+
`get_xml_text`) write to it. The handles are
|
|
129
|
+
[experimental](docs/experimental.md).
|
|
121
130
|
|
|
122
131
|
## Durability and delivery
|
|
123
132
|
|
|
@@ -338,12 +347,12 @@ every editor configures its own. `Y::Lexical` is the core Lexical base
|
|
|
338
347
|
(paragraphs, headings, quotes, code, lists, tables, links, and the full
|
|
339
348
|
text-format model), and other Lexical editors extend it with rules.
|
|
340
349
|
|
|
341
|
-
It handles every node in the
|
|
342
|
-
format and their combinations, links, the
|
|
343
|
-
blockquotes, code blocks, tabs and soft breaks,
|
|
344
|
-
header cells, image galleries, and ActionText
|
|
345
|
-
both render as `<action-text-attachment>`
|
|
346
|
-
re-render.
|
|
350
|
+
It handles every node in Lexxy 1.0, which has the same set as 0.9.x:
|
|
351
|
+
paragraphs, headings, every text format and their combinations, links, the
|
|
352
|
+
four list types with nesting, blockquotes, code blocks, tabs and soft breaks,
|
|
353
|
+
horizontal rules, tables with header cells, image galleries, and ActionText
|
|
354
|
+
attachments. Uploads and mentions both render as `<action-text-attachment>`
|
|
355
|
+
elements, which ActionText can re-render.
|
|
347
356
|
|
|
348
357
|
The Lexical support has the same two layers. `Y::Lexical` handles core Lexical
|
|
349
358
|
structure natively, and everything Lexxy adds is in `Y::Lexxy`'s rule set,
|
|
@@ -401,6 +410,13 @@ attribute names your templates and blocks will read, and `children` and `text`
|
|
|
401
410
|
tell you which `contains:` to pick. Child block types mean `:blocks`, and text
|
|
402
411
|
means `:inline`.
|
|
403
412
|
|
|
413
|
+
`unknown_types` lists the types whose `handled` is nil. `to_html` still
|
|
414
|
+
renders them as well as it can. An unknown container or inline wrapper renders
|
|
415
|
+
its children without its own markup, so its text stays. A node that keeps its
|
|
416
|
+
content only in its attributes renders nothing. Either way, the node's markup is
|
|
417
|
+
missing from the HTML, and nothing raises. When you add editor nodes, add a test
|
|
418
|
+
that renders a real document and asserts `unknown_types` is empty.
|
|
419
|
+
|
|
404
420
|
When a declarative rule can't express the markup, give the node a block:
|
|
405
421
|
|
|
406
422
|
```ruby
|
|
@@ -654,6 +670,16 @@ the `authorize_document` block with the record and the attribute name, or to
|
|
|
654
670
|
accept any valid grant when there's no block. A subclass can override
|
|
655
671
|
`authorized?` directly and read the located record from `record`.
|
|
656
672
|
|
|
673
|
+
A page subscribes to the channel its document names, `Y::DocumentChannel`
|
|
674
|
+
unless the model says otherwise. Name a subclass where you declare the
|
|
675
|
+
attribute, and the tag, a Ruby client, and a collaborator all use it:
|
|
676
|
+
|
|
677
|
+
```ruby
|
|
678
|
+
class Post < ApplicationRecord
|
|
679
|
+
has_collaborative_document :body, channel: "PostDocumentChannel"
|
|
680
|
+
end
|
|
681
|
+
```
|
|
682
|
+
|
|
657
683
|
### Grant lifetime and refresh
|
|
658
684
|
|
|
659
685
|
A grant lasts as long as GlobalID's signed-id default, which is one month under
|
|
@@ -673,7 +699,7 @@ gets blocked at the next reconnect. To avoid that, pair `expires_in:` with
|
|
|
673
699
|
# app/controllers/posts_controller.rb
|
|
674
700
|
def grant
|
|
675
701
|
@post = current_user.posts.find(params[:id]) # your own authorization, again
|
|
676
|
-
render json: { grant: @post.
|
|
702
|
+
render json: { grant: @post.collaborative_document(:body).grant(expires_in: 10.minutes) }
|
|
677
703
|
end
|
|
678
704
|
```
|
|
679
705
|
|
|
@@ -737,7 +763,7 @@ up the record from it, so the page decides which document the client gets.
|
|
|
737
763
|
|
|
738
764
|
```erb
|
|
739
765
|
<%# the view picks the document and signs it %>
|
|
740
|
-
<%= tag.div data: { grant: post.
|
|
766
|
+
<%= tag.div data: { grant: post.collaborative_document(:body).grant } %>
|
|
741
767
|
```
|
|
742
768
|
|
|
743
769
|
```ruby
|
|
@@ -1067,6 +1093,24 @@ The browser clients manage presence (cursors, selections). The server doesn't
|
|
|
1067
1093
|
set or hold presence state, and it relays awareness frames without reading
|
|
1068
1094
|
them. See `yrby-client` for the client-side awareness API.
|
|
1069
1095
|
|
|
1096
|
+
## Experimental: writing from Ruby, and collaborators
|
|
1097
|
+
|
|
1098
|
+
These APIs are new. They may change in any minor release, and they aren't
|
|
1099
|
+
covered by yrby's usual compatibility promise.
|
|
1100
|
+
[docs/experimental.md](docs/experimental.md) documents them:
|
|
1101
|
+
|
|
1102
|
+
- Live `Y::Map`, `Y::Array`, and `Y::Text` handles that write to a document
|
|
1103
|
+
- Writing Lexical rich text from Ruby, with `Y::Lexical` and `Y::Lexxy`
|
|
1104
|
+
- Anchors: a block's identity as a value
|
|
1105
|
+
- Streaming into a document with `Doc#edit`
|
|
1106
|
+
- Editing a record's document from Ruby
|
|
1107
|
+
- From the `yrby-agent` gem: joining a document from a Ruby process with
|
|
1108
|
+
`Y::ActionCable::Client`, collaborators with `Y::Collaborator`, and agents
|
|
1109
|
+
with `Y::Agent`
|
|
1110
|
+
|
|
1111
|
+
`require "y/action_cable/client"`, `require "y/collaborator"`, and
|
|
1112
|
+
`require "y/agent"` print a warning in Ruby's `:experimental` category. `Warning[:experimental] = false` turns it off.
|
|
1113
|
+
|
|
1070
1114
|
## Thread Safety
|
|
1071
1115
|
|
|
1072
1116
|
You can share a `Doc` across Ruby threads. Puma workers, ActionCable connection
|