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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 397e3f48e611d521d0afa26ddca292826276406b1fd04fb2bcfd09858a73081d
4
- data.tar.gz: 51e88f6856f8a2859d18823e87ff2d5cd901e678e8b7a618ff7b2b0308d80c1d
3
+ metadata.gz: 56e31c8cc1697d3894702fdfff977817fe1c22bb95318718dd3b0940171ed74f
4
+ data.tar.gz: 2a3a49d17af5c0d690ab98ca0c3bf6f41f7c79d0a9b6041f3dd49b98f0829e84
5
5
  SHA512:
6
- metadata.gz: 3299b4149a6dea79105be6bf69e643c8079029ac751bb50f68ce4b8d49ce570541b184f5b601215214d92b7848694a8b9a9a685eaf593f885378cbc8454e0a18
7
- data.tar.gz: 4490087a7702972c2e447abcb2f4afff938d72e77704eb7cfc04d5a5272bc6e5248d22a883b193b3840db0aa5c3e602e6a0c50b57fb1d3169839e7530a764ff4
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 the npm package:
79
+ Install the gem and run its generator:
80
80
 
81
81
  ```
82
- gem install yrby-rails # depends on yrby
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 Lexxy 0.9.x set: paragraphs, headings, every text
342
- format and their combinations, links, the four list types with nesting,
343
- blockquotes, code blocks, tabs and soft breaks, horizontal rules, tables with
344
- header cells, image galleries, and ActionText attachments. Uploads and mentions
345
- both render as `<action-text-attachment>` elements, which ActionText can
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.collaborative_sgid(:body, expires_in: 10.minutes) }
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.collaborative_sgid(:body) } %>
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