turbo_ready 0.0.3 → 0.0.6

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/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  <p align="center">
2
- <img height="200" src="https://ik.imagekit.io/hopsoft/turbo-ready-logo_jYFJI1jgT.png?ik-sdk-version=javascript-1.4.3&updatedAt=1661471047153" />
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://ik.imagekit.io/hopsoft/turbo-ready-logo-light_df4jcvbDL.webp?ik-sdk-version=javascript-1.4.3&updatedAt=1661615678275">
4
+ <img height="200" src="https://ik.imagekit.io/hopsoft/turbo-ready-logo-dark_VN4hA2ctc.webp?ik-sdk-version=javascript-1.4.3&updatedAt=1661615678278" />
5
+ </picture>
3
6
  <h3 align="center">
4
7
  Turbo Stream's Swiss Army Knife
5
8
  </h3>
@@ -7,44 +10,76 @@
7
10
  Welcome to TurboReady 👋
8
11
  </h1>
9
12
  <p align="center">
10
- <a href="http://blog.codinghorror.com/the-best-code-is-no-code-at-all/" target="_blank">
11
- <img alt="Lines of Code" src="https://img.shields.io/badge/lines_of_code-275-brightgreen.svg?style=flat" />
13
+ <a href="http://blog.codinghorror.com/the-best-code-is-no-code-at-all/">
14
+ <img alt="Lines of Code" src="https://img.shields.io/badge/loc-143-47d299.svg" />
12
15
  </a>
13
- <a href="https://github.com/testdouble/standard" target="_blank">
14
- <img alt="Ruby Code Style" src="https://img.shields.io/badge/Ruby_Code_Style-standard-brightgreen.svg" />
16
+ <a href="https://codeclimate.com/github/hopsoft/turbo_ready/maintainability">
17
+ <img src="https://api.codeclimate.com/v1/badges/a69b6f73abc3ccd49261/maintainability" />
15
18
  </a>
16
- <a href="https://github.com/sheerun/prettier-standard" target="_blank">
17
- <img alt="JavaScript Code Style" src="https://img.shields.io/badge/JavaScript_Code_Style-prettier_standard-ff69b4.svg" />
19
+ <a href="https://rubygems.org/gems/turbo_ready">
20
+ <img alt="GEM" src="https://img.shields.io/gem/v/turbo_ready?color=168AFE&include_prereleases&logo=ruby&logoColor=FE1616">
21
+ </a>
22
+ <a href="https://rubygems.org/gems/turbo_ready">
23
+ <img alt="Gem" src="https://img.shields.io/gem/dt/turbo_ready?color=168AFE&logo=ruby&logoColor=FE1616">
24
+ </a>
25
+ <a href="https://github.com/testdouble/standard">
26
+ <img alt="Ruby Style" src="https://img.shields.io/badge/style-standard-168AFE?logo=ruby&logoColor=FE1616" />
27
+ </a>
28
+ <a href="https://www.npmjs.com/package/turbo_ready">
29
+ <img alt="NPM" src="https://img.shields.io/npm/v/turbo_ready?color=168AFE&logo=npm">
30
+ </a>
31
+ <a href="https://www.npmjs.com/package/turbo_ready">
32
+ <img alt="npm" src="https://img.shields.io/npm/dm/turbo_ready?color=168AFE&logo=npm">
33
+ </a>
34
+ <a href="https://bundlephobia.com/package/turbo_ready@">
35
+ <img alt="npm bundle size" src="https://img.shields.io/bundlephobia/minzip/turbo_ready?label=bundle%20size&logo=npm&color=47d299">
36
+ </a>
37
+ <a href="https://github.com/sheerun/prettier-standard">
38
+ <img alt="JavaScript Style" src="https://img.shields.io/badge/style-prettier--standard-168AFE?logo=javascript&logoColor=f4e137" />
39
+ </a>
40
+ <a href="https://github.com/hopsoft/turbo_ready/actions/workflows/tests.yml">
41
+ <img alt="Tests" src="https://github.com/hopsoft/turbo_ready/actions/workflows/tests.yml/badge.svg" />
42
+ </a>
43
+ <a href="https://twitter.com/hopsoft">
44
+ <img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/hopsoft?logo=twitter&style=social">
18
45
  </a>
19
46
  </p>
20
47
  </p>
21
48
 
22
- TurboReady extends [Turbo Streams](https://turbo.hotwired.dev/reference/streams) to give you full control of the
23
- browser's [Document Object Model (DOM).](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model)
24
-
25
- **Thats right!**
26
- You can `invoke` any DOM method on any DOM object *(including 3rd party libs)* using Turbo Streams.
49
+ **TurboReady extends [Turbo Streams](https://turbo.hotwired.dev/reference/streams) to give you full control of the
50
+ browser's [Document Object Model (DOM).](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model)**
27
51
 
28
52
  ```ruby
29
- turbo_stream.invoke "console.log", "Hello World!"
53
+ turbo_stream.invoke "console.log", args: ["Hello World!"]
30
54
  ```
31
55
 
56
+ **Thats right!**
57
+ You can `invoke` any DOM method on the client with Turbo Streams.
58
+
32
59
  <!-- Tocer[start]: Auto-generated, don't remove. -->
33
60
 
34
61
  ## Table of Contents
35
62
 
36
63
  - [Why TurboReady?](#why-turboready)
37
- - [Discord Community](#discord-community)
38
64
  - [Sponsors](#sponsors)
39
65
  - [Dependencies](#dependencies)
40
66
  - [Installation](#installation)
41
67
  - [Setup](#setup)
42
68
  - [Usage](#usage)
43
- - [Endless Possibilities](#endless-possibilities)
44
- - [Advanced Usage](#advanced-usage)
69
+ - [Method Chaining](#method-chaining)
70
+ - [Dispatching Events](#dispatching-events)
71
+ - [Syntax Styles](#syntax-styles)
45
72
  - [Extending Behavior](#extending-behavior)
46
- - [Public API](#public-api)
73
+ - [Implementation Details](#implementation-details)
74
+ - [Broadcasting](#broadcasting)
75
+ - [Background Job Queues](#background-job-queues)
76
+ - [FAQ](#faq)
47
77
  - [A Word of Caution](#a-word-of-caution)
78
+ - [Community](#community)
79
+ - [Discord](#discord)
80
+ - [Discussions](#discussions)
81
+ - [Twitter](#twitter)
82
+ - [TODOs](#todos)
48
83
  - [Releasing](#releasing)
49
84
  - [License](#license)
50
85
 
@@ -52,21 +87,17 @@ turbo_stream.invoke "console.log", "Hello World!"
52
87
 
53
88
  ## Why TurboReady?
54
89
 
55
- Turbo Streams [intentionally restricts](https://turbo.hotwired.dev/handbook/streams#but-what-about-running-javascript)
90
+ Turbo Streams [intentionally restricts](https://turbo.hotwired.dev/handbook/streams#but-what-about-running-javascript%3F)
56
91
  official actions to CRUD related activity.
57
- The [official actions](https://turbo.hotwired.dev/reference/streams#the-seven-actions) work well for a
58
- considerable number of use cases and you should push Streams as far as possible before reaching for TurboReady.
59
-
60
- If you discover that CRUD isn't enough, TurboReady covers pretty much everything else.
61
-
62
- ## Community
92
+ These [official actions](https://turbo.hotwired.dev/reference/streams#the-seven-actions) work well for a considerable number of use cases.
93
+ *Try pushing Turbo Streams as far as possible before reaching for TurboReady.*
63
94
 
64
- Please join nearly 2000 of us on [Discord](https://discord.gg/stimulus-reflex) for support getting started,
65
- as well as active discussions around Rails, Hotwire, Stimulus, Turbo (Drive, Frames, Streams), TurboReady, CableReady, StimulusReflex, ViewComponent, Phlex, and more.
95
+ If you find that CRUD isn't enough, TurboReady is there to handle pretty much everything else.
66
96
 
67
- ![](https://img.shields.io/discord/629472241427415060)
97
+ > ⚠️ TurboReady is intended for Rails apps that use Hotwire but not [CableReady](https://github.com/stimulusreflex/cable_ready).
98
+ This is because CableReady already provides a rich set of powerful [DOM operations](https://cableready.stimulusreflex.com/reference/operations).
68
99
 
69
- Stop by #newcomers and introduce yourselves!
100
+ > 📘 **NOTE:** Efforts are underway to bring [CableReady's DOM operations to Turbo Streams](https://github.com/marcoroth/turbo_power).
70
101
 
71
102
  ## Sponsors
72
103
 
@@ -87,79 +118,84 @@ Stop by #newcomers and introduce yourselves!
87
118
 
88
119
  ## Installation
89
120
 
121
+ Be sure to install the same version for each libary.
122
+
90
123
  ```sh
91
124
  bundle add "turbo_ready --version VERSION"
92
125
  yarn add "turbo_ready@VERSION --exact"
93
126
  ```
94
127
 
95
- **IMPORTANT:** Be sure to use the same version for each libary.
96
-
97
128
  ## Setup
98
129
 
99
- 1. Create a Rails intializer and patch Turbo.
130
+ Import and intialize TurboReady in your application.
100
131
 
101
- ```ruby
102
- # config/initializers/turbo_ready.rb
103
- TurboReady.patch! # Adds TurboReady stream actions to Turbo
104
- ```
105
- 2. Import and intialize TurboReady in your JavaScript application.
132
+ ```diff
133
+ # Gemfile
134
+ +gem "turbo_ready", "~> 0.0.6"
135
+ ```
106
136
 
107
- ```js
108
- // app/javascript/application.js
109
- import '@hotwired/turbo-rails'
110
- import TurboReady from 'turbo_ready'
137
+ ```diff
138
+ # package.json
139
+ "dependencies": {
140
+ + "@hotwired/turbo-rails": ">=7.2.0-beta.2",
141
+ + "turbo_ready": "^0.0.6"
142
+ ```
111
143
 
112
- TurboReady.initialize(Turbo.StreamActions) // Adds TurboReady stream actions to Turbo
113
- ```
144
+ ```diff
145
+ # app/javascript/application.js
146
+ import '@hotwired/turbo-rails'
147
+ +import TurboReady from 'turbo_ready'
148
+
149
+ +TurboReady.initialize(Turbo.StreamActions) // Adds TurboReady stream actions to Turbo
150
+ ```
114
151
 
115
152
  ## Usage
116
153
 
117
- Manipulate the DOM from anywhere you use [official Turbo Streams](https://turbo.hotwired.dev/handbook/streams#integration-with-server-side-frameworks).
118
- Namely, [**M**odels](https://github.com/hotwired/turbo-rails/blob/main/app/models/concerns/turbo/broadcastable.rb),
119
- [**V**iews](https://github.com/hotwired/turbo-rails/blob/main/app/models/concerns/turbo/broadcastable.rb),
120
- and [**C**ontrollers](https://github.com/hotwired/turbo-rails/blob/main/app/models/concerns/turbo/broadcastable.rb).
154
+ Manipulate the DOM from anywhere you use official [Turbo Streams](https://turbo.hotwired.dev/handbook/streams#integration-with-server-side-frameworks).
155
+ The possibilities are endless.
156
+ [Learn more about the DOM at MDN.](https://developer.mozilla.org/en-US/docs/Web/API.)
121
157
 
122
- You can **chain invocations.** ❤️
158
+ ```ruby
159
+ turbo_stream.invoke "console.log", args: ["Hello World!"]
160
+ ```
161
+
162
+ ### Method Chaining
163
+
164
+ You can use [dot notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#dot_notation)
165
+ or [selectors](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) and even combine them!
123
166
 
124
167
  ```ruby
125
168
  turbo_stream
126
- .invoke("document.body.insertAdjacentHTML", "afterbegin", "<h1>Hello World!</h1>") # dot notation
127
- .invoke("setAttribute", "data-turbo-ready", true, selector: ".button") # selector
128
- .invoke("classList.add", "turbo-ready", selector: "a") # dot notation + selector
129
- .flush # flush must be called when chaining invocations
169
+ .invoke("document.body.insertAdjacentHTML", args: ["afterbegin", "<h1>Hello World!</h1>"]) # dot notation
170
+ .invoke("setAttribute", args: ["data-turbo-ready", true], selector: ".button") # selector
171
+ .invoke("classList.add", args: ["turbo-ready"], selector: "a") # dot notation + selector
172
+ .flush # call flush when chaining invocations
130
173
  ```
131
174
 
132
- You can use [dot notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#dot_notation)
133
- or [selectors](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll)... and can even combine them!** 🤯
175
+ ### Dispatching Events
134
176
 
135
- Can I dispatch events? **You bet!** ⚡️
177
+ It's possible to fire events on `window`, `document`, and element(s).
136
178
 
137
179
  ```ruby
138
180
  turbo_stream
139
- .invoke("dispatchEvent", "turbo-ready:demo") // fires on window
140
- .invoke("dispatchEvent", "turbo-ready:demo", selector: "#my-element") // fires on matching element(s)
141
- .invoke("dispatchEvent", {bubbles: true, detail: {...}}) // set event options
181
+ .invoke("dispatchEvent", args: ["turbo-ready:demo"]) # fires on window
182
+ .invoke("document.dispatchEvent", args: ["turbo-ready:demo"]) # fires on document
183
+ .invoke("dispatchEvent", args: ["turbo-ready:demo"], selector: "#my-element") # fires on matching element(s)
184
+ .invoke("dispatchEvent", args: ["turbo-ready:demo", {bubbles: true, detail: {...}}]) # set event options
142
185
  .flush
143
186
  ```
144
187
 
145
- ## Endless Possibilities
146
-
147
- **What else can I do?**
148
- MDN has your back... [learn about the DOM and web APIs here.](https://developer.mozilla.org/en-US/docs/Web/API.)
188
+ ### Syntax Styles
149
189
 
150
- ## Advanced Usage
151
-
152
- You can use symbols and [snake case](https://en.wikipedia.org/wiki/Snake_case) when invoking DOM functionality.
153
- It'll implicitly convert to [camel case](https://en.wikipedia.org/wiki/Camel_case). 💎
190
+ You can use [`snake_case`](https://en.wikipedia.org/wiki/Snake_case) when invoking DOM functionality.
191
+ It will implicitly convert to [`camelCase`](https://en.wikipedia.org/wiki/Camel_case).
154
192
 
155
193
  ```ruby
156
- turbo_stream
157
- .invoke(:animate, [{opacity: 0}, {opacity: 1}], 2000)
158
- .invoke(:dispatch_event, {detail: {converts_to_camel_case: true}})
159
- .flush
194
+ turbo_stream.invoke :dispatch_event,
195
+ args: ["turbo-ready:demo", {detail: {converts_to_camel_case: true}}]
160
196
  ```
161
197
 
162
- Need to opt out of camelize? No problem... just disable it.
198
+ Need to opt-out? No problem... just disable it.
163
199
 
164
200
  ```ruby
165
201
  turbo_stream.invoke :contrived_demo, camelize: false
@@ -167,10 +203,10 @@ turbo_stream.invoke :contrived_demo, camelize: false
167
203
 
168
204
  ### Extending Behavior
169
205
 
170
- Want to extend things with custom functionality? **Let's do it.** 🔌
206
+ If you add new capabilities to the browser, you can control them from the server.
171
207
 
172
208
  ```js
173
- // JavaScript
209
+ // JavaScript on the client
174
210
  import morphdom from 'morphdom'
175
211
 
176
212
  window.MyNamespace = {
@@ -181,51 +217,180 @@ window.MyNamespace = {
181
217
  ```
182
218
 
183
219
  ```ruby
184
- # Ruby
185
- turbo_stream
186
- .invoke "MyNamespace.morph", "#demo", "<div id='demo'><p>You've changed...</p></div>", {childrenOnly: true}
220
+ # Ruby on the server
221
+ turbo_stream.invoke "MyNamespace.morph",
222
+ args: [
223
+ "#demo",
224
+ "<div id='demo'><p>You've changed...</p></div>",
225
+ {children_only: true}
226
+ ]
187
227
  ```
188
228
 
189
- ## Public API
229
+ ### Implementation Details
190
230
 
191
- There's only one method to consider, `invoke` defined in the
192
- [tag builder](https://github.com/hopsoft/turbo_ready/blob/main/lib/turbo_ready/tag_builder.rb).
231
+ There's basically one method to learn... `invoke`
193
232
 
194
233
  ```ruby
195
234
  # Ruby
196
235
  turbo_stream
197
- .invoke(method, *args, selector: nil, camelize: true, id: nil)
198
- # | | | | |
199
- # | | | | |- Identifies this invocation (optional)
200
- # | | | |
201
- # | | | |- Should we camelize the JavaScript stuff? (optional)
202
- # | | | (allows us to write snake_case Ruby)
203
- # | | |
204
- # | | |- An CSS selector for the element(s) to target (optional)
205
- # | |
206
- # | |- The arguments to pass to the JavaScript method being invoked (optional)
236
+ .invoke(method, args: [], selector: nil, camelize: true, id: nil)
237
+ # | | | | |
238
+ # | | | | |- Identifies this invocation (optional)
239
+ # | | | |
240
+ # | | | |- Should we camelize the JavaScript stuff? (optional)
241
+ # | | | (allows us to write snake_case in Ruby)
242
+ # | | |
243
+ # | | |- A CSS selector for the element(s) to target (optional)
244
+ # | |
245
+ # | |- The arguments to pass to the JavaScript method (optional)
207
246
  # |
208
247
  # |- The JavaScript method to invoke (can use dot notation)
209
248
  ```
210
249
 
211
- **NOTE:** The JavaScript method will be invoked on all matching elements when a `selector` is passed.
250
+ > 📘 **NOTE:** The method will be invoked on all matching elements if a `selector` is present.
251
+
252
+ The following Ruby code,
253
+
254
+ ```ruby
255
+ turbo_stream.invoke "console.log", args: ["Hello World!"], id: "1"
256
+ ```
257
+
258
+ emits this HTML markup.
259
+
260
+ ```html
261
+ <turbo-stream action="invoke" target="DOM">
262
+ <template>{"id":"1","receiver":"console","method":"log","args":["Hello World!"]}</template>
263
+ </turbo-stream>
264
+ ```
265
+
266
+ When this element enters the DOM,
267
+ Turbo Streams automatically executes `invoke` on the client with the template's JSON payload and then removes the element from the DOM.
268
+
269
+ ### Broadcasting
270
+
271
+ You can also broadcast DOM invocations to subscribed users.
272
+
273
+ 1. First, setup the stream subscription.
274
+
275
+ ```erb
276
+ <!-- app/views/posts/show.html.erb -->
277
+ <%= turbo_stream_from @post %>
278
+ <!-- |
279
+ |- *streamables - model(s), string(s), etc...
280
+ -->
281
+ ```
282
+
283
+ 2. Then, broadcast to the subscription.
284
+
285
+ ```ruby
286
+ # app/models/post.rb
287
+ class Post < ApplicationRecord
288
+ after_save do
289
+ # emit a message in the browser conosle for anyone subscribed to this post
290
+ broadcast_invoke "console.log", args: ["Post was saved! #{to_gid.to_s}"]
291
+
292
+ # broadcast with a background job
293
+ broadcast_invoke_later "console.log", args: ["Post was saved! #{to_gid.to_s}"]
294
+ end
295
+ end
296
+ ```
297
+
298
+ ```ruby
299
+ # app/controllers/posts_controller.rb
300
+ class PostsController < ApplicationController
301
+ def create
302
+ @post = Post.find params[:id]
303
+
304
+ if @post.update post_params
305
+ # emit a message in the browser conosle for anyone subscribed to this post
306
+ @post.broadcast_invoke "console.log", args: ["Post was saved! #{to_gid.to_s}"]
307
+
308
+ # broadcast with a background job
309
+ @post.broadcast_invoke_later "console.log", args: ["Post was saved! #{to_gid.to_s}"]
310
+
311
+ # you can also broadcast directly from the channel
312
+ Turbo::StreamsChannel.broadcast_invoke_to @post, "console.log",
313
+ args: ["Post was saved! #{@post.to_gid.to_s}"]
314
+
315
+ # broadcast with a background job
316
+ Turbo::StreamsChannel.broadcast_invoke_later_to @post, "console.log",
317
+ args: ["Post was saved! #{@post.to_gid.to_s}"]
318
+ end
319
+ end
320
+ end
321
+ ```
322
+
323
+ > 📘 **NOTE:** [Method Chaining](#method-chaining) is not currently supported when broadcasting.
324
+
325
+ #### Background Job Queues
326
+
327
+ You may want to change the queue name for Turbo Stream background jobs in order to isolate, prioritize, and scale the workers independently.
328
+
329
+ ```ruby
330
+ # config/initializers/turbo_streams.rb
331
+ Turbo::Streams::BroadcastJob.queue_name = :turbo_streams
332
+ TurboReady::BroadcastInvokeJob.queue_name = :turbo_streams
333
+ ```
334
+
335
+ ## FAQ
336
+
337
+ - Isn't this just RJS?
338
+
339
+ > No. But, perhaps it could be considered RJS's "modern" spirtual successor. 🤷‍♂️
340
+ > Though it embraces JavaScript instead of trying to avoid it.
341
+
342
+ - Does it use `eval`?
343
+
344
+ > **No.** TurboReady can only invoke existing functions on the client.
345
+ > It's not a carte blanche invitation to emit free-form JavaScript to be evaluated on the client.
212
346
 
213
347
  ## A Word of Caution
214
348
 
215
- Manually orchestrating DOM activity gets tedious fast.
216
- **⚠️ Don't abuse this superpower!**
349
+ **Don't abuse this superpower!**
217
350
 
218
351
  > With great power comes great responsibility. *-Uncle Ben*
219
352
 
353
+ Manually orchestrating DOM activity is tedious.
354
+ *Don't overdo it... or you may find that you've created spaghetti reminiscent of the jQuery days.*
355
+
220
356
  This library is an extremely sharp tool. 🔪
221
- Consider it a low-level building block that can be used to craft additional libraries with
222
- great [DX](https://en.wikipedia.org/wiki/User_experience#Developer_experience)
357
+ Consider it a low-level building block that can be used to craft additional libraries
223
358
  like [CableReady](https://github.com/stimulusreflex/cable_ready)
224
359
  and [StimulusReflex](https://github.com/stimulusreflex/stimulus_reflex).
225
360
 
226
- Restrict your direct application usage to DOM manipulation that falls outside the purview of
227
- [Turbo's official actions](https://turbo.hotwired.dev/reference/streams#the-seven-actions)...
228
- *and for Pete's sake, don't overdo it and find yourself maintaining spaghetti code reminiscent of the jQuery days.*
361
+ ## Community
362
+
363
+ ### Discord
364
+
365
+ Please join nearly 2000 of us on [Discord](https://discord.gg/stimulus-reflex) for support getting started,
366
+ as well as active discussions around Rails, Hotwire, Stimulus, Turbo (Drive, Frames, Streams), TurboReady, CableReady, StimulusReflex, ViewComponent, Phlex, and more.
367
+
368
+ <a href="https://discord.gg/stimulus-reflex" target="_blank">
369
+ <img alt="Discord" src="https://img.shields.io/discord/629472241427415060?color=168AFE&logo=discord&logoColor=FFF">
370
+ </a>
371
+
372
+ Be sure to introduce yourselves in the #newcomers channel!
373
+
374
+ ### Discussions
375
+
376
+ Feel free to add to the conversation here on [GitHub Discussions](https://github.com/hopsoft/turbo_ready/discussions).
377
+
378
+ <a href="https://github.com/hopsoft/turbo_ready/discussions" target="_blank">
379
+ <img alt="GitHub Discussions" src="https://img.shields.io/github/discussions/hopsoft/turbo_ready?color=168AFE&logo=github">
380
+ </a>
381
+
382
+ ### Twitter
383
+
384
+ Connect with the core team on Twitter.
385
+
386
+ <a href="https://twitter.com/hopsoft" target="_blank">
387
+ <img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/hopsoft?logo=twitter&style=social">
388
+ </a>
389
+
390
+ ## TODOs
391
+
392
+ - [ ] Add system tests [(review turbo-rails for guidance)](https://github.com/hotwired/turbo-rails/blob/main/test/system/broadcasts_test.rb)
393
+ - [ ] Look into adding method chaining for broadcasts
229
394
 
230
395
  ## Releasing
231
396