turbo_ready 0.0.5 → 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,47 +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-278-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">
18
21
  </a>
19
- <a href="https://bundlephobia.com/package/turbo_ready" target="_blank">
20
- <img alt="npm bundle size" src="https://img.shields.io/bundlephobia/minzip/turbo_ready?label=minified%20size">
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">
21
45
  </a>
22
46
  </p>
23
47
  </p>
24
48
 
25
- TurboReady extends [Turbo Streams](https://turbo.hotwired.dev/reference/streams) to give you full control of the
26
- browser's [Document Object Model (DOM).](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model)
27
-
28
- **Thats right!**
29
- 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)**
30
51
 
31
52
  ```ruby
32
- turbo_stream.invoke "console.log", "Hello World!"
53
+ turbo_stream.invoke "console.log", args: ["Hello World!"]
33
54
  ```
34
55
 
56
+ **Thats right!**
57
+ You can `invoke` any DOM method on the client with Turbo Streams.
58
+
35
59
  <!-- Tocer[start]: Auto-generated, don't remove. -->
36
60
 
37
61
  ## Table of Contents
38
62
 
39
63
  - [Why TurboReady?](#why-turboready)
40
- - [Discord Community](#discord-community)
41
64
  - [Sponsors](#sponsors)
42
65
  - [Dependencies](#dependencies)
43
66
  - [Installation](#installation)
44
67
  - [Setup](#setup)
45
68
  - [Usage](#usage)
46
- - [Endless Possibilities](#endless-possibilities)
47
- - [Advanced Usage](#advanced-usage)
69
+ - [Method Chaining](#method-chaining)
70
+ - [Dispatching Events](#dispatching-events)
71
+ - [Syntax Styles](#syntax-styles)
48
72
  - [Extending Behavior](#extending-behavior)
49
- - [Public API](#public-api)
73
+ - [Implementation Details](#implementation-details)
74
+ - [Broadcasting](#broadcasting)
75
+ - [Background Job Queues](#background-job-queues)
76
+ - [FAQ](#faq)
50
77
  - [A Word of Caution](#a-word-of-caution)
78
+ - [Community](#community)
79
+ - [Discord](#discord)
80
+ - [Discussions](#discussions)
81
+ - [Twitter](#twitter)
82
+ - [TODOs](#todos)
51
83
  - [Releasing](#releasing)
52
84
  - [License](#license)
53
85
 
@@ -55,21 +87,17 @@ turbo_stream.invoke "console.log", "Hello World!"
55
87
 
56
88
  ## Why TurboReady?
57
89
 
58
- 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)
59
91
  official actions to CRUD related activity.
60
- The [official actions](https://turbo.hotwired.dev/reference/streams#the-seven-actions) work well for a
61
- considerable number of use cases and you should push Streams as far as possible before reaching for TurboReady.
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.*
62
94
 
63
- If you discover that CRUD isn't enough, TurboReady covers pretty much everything else.
95
+ If you find that CRUD isn't enough, TurboReady is there to handle pretty much everything else.
64
96
 
65
- ## Community
66
-
67
- Please join nearly 2000 of us on [Discord](https://discord.gg/stimulus-reflex) for support getting started,
68
- as well as active discussions around Rails, Hotwire, Stimulus, Turbo (Drive, Frames, Streams), TurboReady, CableReady, StimulusReflex, ViewComponent, Phlex, and more.
69
-
70
- ![](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).
71
99
 
72
- 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).
73
101
 
74
102
  ## Sponsors
75
103
 
@@ -90,73 +118,84 @@ Stop by #newcomers and introduce yourselves!
90
118
 
91
119
  ## Installation
92
120
 
121
+ Be sure to install the same version for each libary.
122
+
93
123
  ```sh
94
124
  bundle add "turbo_ready --version VERSION"
95
125
  yarn add "turbo_ready@VERSION --exact"
96
126
  ```
97
127
 
98
- **IMPORTANT:** Be sure to use the same version for each libary.
99
-
100
128
  ## Setup
101
129
 
102
- 1. Import and intialize TurboReady in your JavaScript application.
130
+ Import and intialize TurboReady in your application.
103
131
 
104
- ```js
105
- // app/javascript/application.js
106
- import '@hotwired/turbo-rails'
107
- import TurboReady from 'turbo_ready'
132
+ ```diff
133
+ # Gemfile
134
+ +gem "turbo_ready", "~> 0.0.6"
135
+ ```
108
136
 
109
- TurboReady.initialize(Turbo.StreamActions) // Adds TurboReady stream actions to Turbo
110
- ```
137
+ ```diff
138
+ # package.json
139
+ "dependencies": {
140
+ + "@hotwired/turbo-rails": ">=7.2.0-beta.2",
141
+ + "turbo_ready": "^0.0.6"
142
+ ```
143
+
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
+ ```
111
151
 
112
152
  ## Usage
113
153
 
114
- Manipulate the DOM from anywhere you use [official Turbo Streams](https://turbo.hotwired.dev/handbook/streams#integration-with-server-side-frameworks).
115
- Namely, [**M**odels](https://github.com/hotwired/turbo-rails/blob/main/app/models/concerns/turbo/broadcastable.rb),
116
- [**V**iews](https://github.com/hotwired/turbo-rails/blob/main/app/models/concerns/turbo/broadcastable.rb),
117
- 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.)
157
+
158
+ ```ruby
159
+ turbo_stream.invoke "console.log", args: ["Hello World!"]
160
+ ```
161
+
162
+ ### Method Chaining
118
163
 
119
- You can **chain invocations.** ❤️
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!
120
166
 
121
167
  ```ruby
122
168
  turbo_stream
123
- .invoke("document.body.insertAdjacentHTML", "afterbegin", "<h1>Hello World!</h1>") # dot notation
124
- .invoke("setAttribute", "data-turbo-ready", true, selector: ".button") # selector
125
- .invoke("classList.add", "turbo-ready", selector: "a") # dot notation + selector
126
- .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
127
173
  ```
128
174
 
129
- You can use [dot notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#dot_notation)
130
- or [selectors](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll)... and can even combine them!** 🤯
175
+ ### Dispatching Events
131
176
 
132
- Can I dispatch events? **You bet!** ⚡️
177
+ It's possible to fire events on `window`, `document`, and element(s).
133
178
 
134
179
  ```ruby
135
180
  turbo_stream
136
- .invoke("dispatchEvent", "turbo-ready:demo") // fires on window
137
- .invoke("dispatchEvent", "turbo-ready:demo", selector: "#my-element") // fires on matching element(s)
138
- .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
139
185
  .flush
140
186
  ```
141
187
 
142
- ## Endless Possibilities
143
-
144
- **What else can I do?**
145
- MDN has your back... [learn about the DOM and web APIs here.](https://developer.mozilla.org/en-US/docs/Web/API.)
188
+ ### Syntax Styles
146
189
 
147
- ## Advanced Usage
148
-
149
- You can use symbols and [snake case](https://en.wikipedia.org/wiki/Snake_case) when invoking DOM functionality.
150
- 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).
151
192
 
152
193
  ```ruby
153
- turbo_stream
154
- .invoke(:animate, [{opacity: 0}, {opacity: 1}], 2000)
155
- .invoke(:dispatch_event, {detail: {converts_to_camel_case: true}})
156
- .flush
194
+ turbo_stream.invoke :dispatch_event,
195
+ args: ["turbo-ready:demo", {detail: {converts_to_camel_case: true}}]
157
196
  ```
158
197
 
159
- Need to opt out of camelize? No problem... just disable it.
198
+ Need to opt-out? No problem... just disable it.
160
199
 
161
200
  ```ruby
162
201
  turbo_stream.invoke :contrived_demo, camelize: false
@@ -164,10 +203,10 @@ turbo_stream.invoke :contrived_demo, camelize: false
164
203
 
165
204
  ### Extending Behavior
166
205
 
167
- 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.
168
207
 
169
208
  ```js
170
- // JavaScript
209
+ // JavaScript on the client
171
210
  import morphdom from 'morphdom'
172
211
 
173
212
  window.MyNamespace = {
@@ -178,51 +217,180 @@ window.MyNamespace = {
178
217
  ```
179
218
 
180
219
  ```ruby
181
- # Ruby
182
- turbo_stream
183
- .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
+ ]
184
227
  ```
185
228
 
186
- ## Public API
229
+ ### Implementation Details
187
230
 
188
- There's only one method to consider, `invoke` defined in the
189
- [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`
190
232
 
191
233
  ```ruby
192
234
  # Ruby
193
235
  turbo_stream
194
- .invoke(method, *args, selector: nil, camelize: true, id: nil)
195
- # | | | | |
196
- # | | | | |- Identifies this invocation (optional)
197
- # | | | |
198
- # | | | |- Should we camelize the JavaScript stuff? (optional)
199
- # | | | (allows us to write snake_case Ruby)
200
- # | | |
201
- # | | |- An CSS selector for the element(s) to target (optional)
202
- # | |
203
- # | |- 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)
204
246
  # |
205
247
  # |- The JavaScript method to invoke (can use dot notation)
206
248
  ```
207
249
 
208
- **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.
209
346
 
210
347
  ## A Word of Caution
211
348
 
212
- Manually orchestrating DOM activity gets tedious fast.
213
- **⚠️ Don't abuse this superpower!**
349
+ **Don't abuse this superpower!**
214
350
 
215
351
  > With great power comes great responsibility. *-Uncle Ben*
216
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
+
217
356
  This library is an extremely sharp tool. 🔪
218
- Consider it a low-level building block that can be used to craft additional libraries with
219
- 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
220
358
  like [CableReady](https://github.com/stimulusreflex/cable_ready)
221
359
  and [StimulusReflex](https://github.com/stimulusreflex/stimulus_reflex).
222
360
 
223
- Restrict your direct application usage to DOM manipulation that falls outside the purview of
224
- [Turbo's official actions](https://turbo.hotwired.dev/reference/streams#the-seven-actions)...
225
- *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
226
394
 
227
395
  ## Releasing
228
396