turbo_ready 0.0.4 → 0.0.7

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-139-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,79 +118,82 @@ 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. Create a Rails intializer and patch Turbo.
130
+ Import and intialize TurboReady in your application.
103
131
 
104
- ```ruby
105
- # config/initializers/turbo_ready.rb
106
- TurboReady.patch! # Adds TurboReady stream actions to Turbo
107
- ```
108
- 2. Import and intialize TurboReady in your JavaScript application.
132
+ ```diff
133
+ # Gemfile
134
+ +gem "turbo_ready", "~> 0.0.6"
135
+ ```
109
136
 
110
- ```js
111
- // app/javascript/application.js
112
- import '@hotwired/turbo-rails'
113
- 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
+ ```
114
143
 
115
- TurboReady.initialize(Turbo.StreamActions) // Adds TurboReady stream actions to Turbo
116
- ```
144
+ ```diff
145
+ # app/javascript/application.js
146
+ import '@hotwired/turbo-rails'
147
+ +import TurboReady from 'turbo_ready'
117
148
 
118
- ## Usage
149
+ +TurboReady.initialize(Turbo.StreamActions) // Adds TurboReady stream actions to Turbo
150
+ ```
119
151
 
120
- Manipulate the DOM from anywhere you use [official Turbo Streams](https://turbo.hotwired.dev/handbook/streams#integration-with-server-side-frameworks).
121
- Namely, [**M**odels](https://github.com/hotwired/turbo-rails/blob/main/app/models/concerns/turbo/broadcastable.rb),
122
- [**V**iews](https://github.com/hotwired/turbo-rails/blob/main/app/models/concerns/turbo/broadcastable.rb),
123
- and [**C**ontrollers](https://github.com/hotwired/turbo-rails/blob/main/app/models/concerns/turbo/broadcastable.rb).
152
+ ## Usage
124
153
 
125
- You can **chain invocations.** ❤️
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.)
126
157
 
127
158
  ```ruby
128
- turbo_stream
129
- .invoke("document.body.insertAdjacentHTML", "afterbegin", "<h1>Hello World!</h1>") # dot notation
130
- .invoke("setAttribute", "data-turbo-ready", true, selector: ".button") # selector
131
- .invoke("classList.add", "turbo-ready", selector: "a") # dot notation + selector
132
- .flush # flush must be called when chaining invocations
159
+ turbo_stream.invoke "console.log", args: ["Hello World!"]
133
160
  ```
134
161
 
135
- You can use [dot notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#dot_notation)
136
- or [selectors](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll)... and can even combine them!** 🤯
162
+ ### Method Chaining
137
163
 
138
- Can I dispatch events? **You bet!** ⚡️
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!
139
166
 
140
167
  ```ruby
141
168
  turbo_stream
142
- .invoke("dispatchEvent", "turbo-ready:demo") // fires on window
143
- .invoke("dispatchEvent", "turbo-ready:demo", selector: "#my-element") // fires on matching element(s)
144
- .invoke("dispatchEvent", {bubbles: true, detail: {...}}) // set event options
145
- .flush
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
146
172
  ```
147
173
 
148
- ## Endless Possibilities
174
+ ### Dispatching Events
149
175
 
150
- **What else can I do?**
151
- MDN has your back... [learn about the DOM and web APIs here.](https://developer.mozilla.org/en-US/docs/Web/API.)
176
+ It's possible to fire events on `window`, `document`, and element(s).
152
177
 
153
- ## Advanced Usage
178
+ ```ruby
179
+ turbo_stream
180
+ .invoke("dispatchEvent", args: ["turbo-ready:demo"]) # fires on window
181
+ .invoke("document.dispatchEvent", args: ["turbo-ready:demo"]) # fires on document
182
+ .invoke("dispatchEvent", args: ["turbo-ready:demo"], selector: "#my-element") # fires on matching element(s)
183
+ .invoke("dispatchEvent", args: ["turbo-ready:demo", {bubbles: true, detail: {...}}]) # set event options
184
+ ```
154
185
 
155
- You can use symbols and [snake case](https://en.wikipedia.org/wiki/Snake_case) when invoking DOM functionality.
156
- It'll implicitly convert to [camel case](https://en.wikipedia.org/wiki/Camel_case). 💎
186
+ ### Syntax Styles
187
+
188
+ You can use [`snake_case`](https://en.wikipedia.org/wiki/Snake_case) when invoking DOM functionality.
189
+ It will implicitly convert to [`camelCase`](https://en.wikipedia.org/wiki/Camel_case).
157
190
 
158
191
  ```ruby
159
- turbo_stream
160
- .invoke(:animate, [{opacity: 0}, {opacity: 1}], 2000)
161
- .invoke(:dispatch_event, {detail: {converts_to_camel_case: true}})
162
- .flush
192
+ turbo_stream.invoke :dispatch_event,
193
+ args: ["turbo-ready:demo", {detail: {converts_to_camel_case: true}}]
163
194
  ```
164
195
 
165
- Need to opt out of camelize? No problem... just disable it.
196
+ Need to opt-out? No problem... just disable it.
166
197
 
167
198
  ```ruby
168
199
  turbo_stream.invoke :contrived_demo, camelize: false
@@ -170,10 +201,10 @@ turbo_stream.invoke :contrived_demo, camelize: false
170
201
 
171
202
  ### Extending Behavior
172
203
 
173
- Want to extend things with custom functionality? **Let's do it.** 🔌
204
+ If you add new capabilities to the browser, you can control them from the server.
174
205
 
175
206
  ```js
176
- // JavaScript
207
+ // JavaScript on the client
177
208
  import morphdom from 'morphdom'
178
209
 
179
210
  window.MyNamespace = {
@@ -184,51 +215,180 @@ window.MyNamespace = {
184
215
  ```
185
216
 
186
217
  ```ruby
187
- # Ruby
188
- turbo_stream
189
- .invoke "MyNamespace.morph", "#demo", "<div id='demo'><p>You've changed...</p></div>", {childrenOnly: true}
218
+ # Ruby on the server
219
+ turbo_stream.invoke "MyNamespace.morph",
220
+ args: [
221
+ "#demo",
222
+ "<div id='demo'><p>You've changed...</p></div>",
223
+ {children_only: true}
224
+ ]
190
225
  ```
191
226
 
192
- ## Public API
227
+ ### Implementation Details
193
228
 
194
- There's only one method to consider, `invoke` defined in the
195
- [tag builder](https://github.com/hopsoft/turbo_ready/blob/main/lib/turbo_ready/tag_builder.rb).
229
+ There's basically one method to learn... `invoke`
196
230
 
197
231
  ```ruby
198
232
  # Ruby
199
233
  turbo_stream
200
- .invoke(method, *args, selector: nil, camelize: true, id: nil)
201
- # | | | | |
202
- # | | | | |- Identifies this invocation (optional)
203
- # | | | |
204
- # | | | |- Should we camelize the JavaScript stuff? (optional)
205
- # | | | (allows us to write snake_case Ruby)
206
- # | | |
207
- # | | |- An CSS selector for the element(s) to target (optional)
208
- # | |
209
- # | |- The arguments to pass to the JavaScript method being invoked (optional)
234
+ .invoke(method, args: [], selector: nil, camelize: true, id: nil)
235
+ # | | | | |
236
+ # | | | | |- Identifies this invocation (optional)
237
+ # | | | |
238
+ # | | | |- Should we camelize the JavaScript stuff? (optional)
239
+ # | | | (allows us to write snake_case in Ruby)
240
+ # | | |
241
+ # | | |- A CSS selector for the element(s) to target (optional)
242
+ # | |
243
+ # | |- The arguments to pass to the JavaScript method (optional)
210
244
  # |
211
245
  # |- The JavaScript method to invoke (can use dot notation)
212
246
  ```
213
247
 
214
- **NOTE:** The JavaScript method will be invoked on all matching elements when a `selector` is passed.
248
+ > 📘 **NOTE:** The method will be invoked on all matching elements if a `selector` is present.
249
+
250
+ The following Ruby code,
251
+
252
+ ```ruby
253
+ turbo_stream.invoke "console.log", args: ["Hello World!"], id: "123ABC"
254
+ ```
255
+
256
+ emits this HTML markup.
257
+
258
+ ```html
259
+ <turbo-stream action="invoke" target="DOM">
260
+ <template>{"id":"123ABC","receiver":"console","method":"log","args":["Hello World!"]}</template>
261
+ </turbo-stream>
262
+ ```
263
+
264
+ When this element enters the DOM,
265
+ Turbo Streams automatically executes `invoke` on the client with the template's JSON payload and then removes the element from the DOM.
266
+
267
+ ### Broadcasting
268
+
269
+ You can also broadcast DOM invocations to subscribed users.
270
+
271
+ 1. First, setup the stream subscription.
272
+
273
+ ```erb
274
+ <!-- app/views/posts/show.html.erb -->
275
+ <%= turbo_stream_from @post %>
276
+ <!-- |
277
+ |- *streamables - model(s), string(s), etc...
278
+ -->
279
+ ```
280
+
281
+ 2. Then, broadcast to the subscription.
282
+
283
+ ```ruby
284
+ # app/models/post.rb
285
+ class Post < ApplicationRecord
286
+ after_save do
287
+ # emit a message in the browser conosle for anyone subscribed to this post
288
+ broadcast_invoke "console.log", args: ["Post was saved! #{to_gid.to_s}"]
289
+
290
+ # broadcast with a background job
291
+ broadcast_invoke_later "console.log", args: ["Post was saved! #{to_gid.to_s}"]
292
+ end
293
+ end
294
+ ```
295
+
296
+ ```ruby
297
+ # app/controllers/posts_controller.rb
298
+ class PostsController < ApplicationController
299
+ def create
300
+ @post = Post.find params[:id]
301
+
302
+ if @post.update post_params
303
+ # emit a message in the browser conosle for anyone subscribed to this post
304
+ @post.broadcast_invoke "console.log", args: ["Post was saved! #{to_gid.to_s}"]
305
+
306
+ # broadcast with a background job
307
+ @post.broadcast_invoke_later "console.log", args: ["Post was saved! #{to_gid.to_s}"]
308
+
309
+ # you can also broadcast directly from the channel
310
+ Turbo::StreamsChannel.broadcast_invoke_to @post, "console.log",
311
+ args: ["Post was saved! #{@post.to_gid.to_s}"]
312
+
313
+ # broadcast with a background job
314
+ Turbo::StreamsChannel.broadcast_invoke_later_to @post, "console.log",
315
+ args: ["Post was saved! #{@post.to_gid.to_s}"]
316
+ end
317
+ end
318
+ end
319
+ ```
320
+
321
+ > 📘 **NOTE:** [Method Chaining](#method-chaining) is not currently supported when broadcasting.
322
+
323
+ #### Background Job Queues
324
+
325
+ You may want to change the queue name for Turbo Stream background jobs in order to isolate, prioritize, and scale the workers independently.
326
+
327
+ ```ruby
328
+ # config/initializers/turbo_streams.rb
329
+ Turbo::Streams::BroadcastJob.queue_name = :turbo_streams
330
+ TurboReady::BroadcastInvokeJob.queue_name = :turbo_streams
331
+ ```
332
+
333
+ ## FAQ
334
+
335
+ - Isn't this just RJS?
336
+
337
+ > No. But, perhaps it could be considered RJS's "modern" spirtual successor. 🤷‍♂️
338
+ > Though it embraces JavaScript instead of trying to avoid it.
339
+
340
+ - Does it use `eval`?
341
+
342
+ > **No.** TurboReady can only invoke existing functions on the client.
343
+ > It's not a carte blanche invitation to emit free-form JavaScript to be evaluated on the client.
215
344
 
216
345
  ## A Word of Caution
217
346
 
218
- Manually orchestrating DOM activity gets tedious fast.
219
- **⚠️ Don't abuse this superpower!**
347
+ **Don't abuse this superpower!**
220
348
 
221
349
  > With great power comes great responsibility. *-Uncle Ben*
222
350
 
351
+ Manually orchestrating DOM activity is tedious.
352
+ *Don't overdo it... or you may find that you've created spaghetti reminiscent of the jQuery days.*
353
+
223
354
  This library is an extremely sharp tool. 🔪
224
- Consider it a low-level building block that can be used to craft additional libraries with
225
- great [DX](https://en.wikipedia.org/wiki/User_experience#Developer_experience)
355
+ Consider it a low-level building block that can be used to craft additional libraries
226
356
  like [CableReady](https://github.com/stimulusreflex/cable_ready)
227
357
  and [StimulusReflex](https://github.com/stimulusreflex/stimulus_reflex).
228
358
 
229
- Restrict your direct application usage to DOM manipulation that falls outside the purview of
230
- [Turbo's official actions](https://turbo.hotwired.dev/reference/streams#the-seven-actions)...
231
- *and for Pete's sake, don't overdo it and find yourself maintaining spaghetti code reminiscent of the jQuery days.*
359
+ ## Community
360
+
361
+ ### Discord
362
+
363
+ Please join nearly 2000 of us on [Discord](https://discord.gg/stimulus-reflex) for support getting started,
364
+ as well as active discussions around Rails, Hotwire, Stimulus, Turbo (Drive, Frames, Streams), TurboReady, CableReady, StimulusReflex, ViewComponent, Phlex, and more.
365
+
366
+ <a href="https://discord.gg/stimulus-reflex" target="_blank">
367
+ <img alt="Discord" src="https://img.shields.io/discord/629472241427415060?color=168AFE&logo=discord&logoColor=FFF">
368
+ </a>
369
+
370
+ Be sure to introduce yourselves in the #newcomers channel!
371
+
372
+ ### Discussions
373
+
374
+ Feel free to add to the conversation here on [GitHub Discussions](https://github.com/hopsoft/turbo_ready/discussions).
375
+
376
+ <a href="https://github.com/hopsoft/turbo_ready/discussions" target="_blank">
377
+ <img alt="GitHub Discussions" src="https://img.shields.io/github/discussions/hopsoft/turbo_ready?color=168AFE&logo=github">
378
+ </a>
379
+
380
+ ### Twitter
381
+
382
+ Connect with the core team on Twitter.
383
+
384
+ <a href="https://twitter.com/hopsoft" target="_blank">
385
+ <img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/hopsoft?logo=twitter&style=social">
386
+ </a>
387
+
388
+ ## TODOs
389
+
390
+ - [ ] Add system tests [(review turbo-rails for guidance)](https://github.com/hotwired/turbo-rails/blob/main/test/system/broadcasts_test.rb)
391
+ - [ ] Look into adding method chaining for broadcasts
232
392
 
233
393
  ## Releasing
234
394
 
data/Rakefile CHANGED
@@ -1,5 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler/setup"
4
-
5
4
  require "bundler/gem_tasks"
5
+ require "rake/testtask"
6
+
7
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
8
+ load "rails/tasks/engine.rake"
9
+ load "rails/tasks/statistics.rake"
10
+
11
+ Rake::TestTask.new do |test|
12
+ test.libs << "test"
13
+ test.test_files = FileList["test/**/*_test.rb"]
14
+ test.warning = false
15
+ end
16
+
17
+ task default: :test
@@ -1,2 +1,2 @@
1
- function l(){let n=JSON.parse(this.templateContent.textContent),{id:h,method:c,args:i,receiver:a,selector:r}=n,t=[self];switch(r&&(t=Array.from(document.querySelectorAll(r))),a&&(t=t.map(o=>{let e=o,s=a.split(".");for(;s.length>0;)e=e[s.shift()];return e})),c){case"dispatchEvent":let o=new CustomEvent(i[0],i[1]||{});t.forEach(e=>e.dispatchEvent(o));break;default:t.forEach(e=>e[c].apply(e,i))}}function f(n){n.invoke=l}var p={initialize:f};export{p as default};
1
+ function l(){let n=JSON.parse(this.templateContent.textContent),{id:h,selector:c,receiver:a,method:r,args:i}=n,t=[self];switch(c&&(t=Array.from(document.querySelectorAll(c))),a&&(t=t.map(o=>{let e=o,s=a.split(".");for(;s.length>0;)e=e[s.shift()];return e})),r){case"dispatchEvent":let o=new CustomEvent(i[0],i[1]||{});t.forEach(e=>e.dispatchEvent(o));break;default:t.forEach(e=>e[r].apply(e,i))}}function f(n){n.invoke=l}var p={initialize:f};export{p as default};
2
2
  //# sourceMappingURL=turbo_ready.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../javascript/turbo_ready.js"],
4
- "sourcesContent": ["function invoke () {\n const payload = JSON.parse(this.templateContent.textContent)\n const { id, method, args, receiver, selector } = payload\n let receivers = [self]\n if (selector) receivers = Array.from(document.querySelectorAll(selector))\n\n if (receiver) {\n receivers = receivers.map(r => {\n let context = r\n const chain = receiver.split('.')\n while (chain.length > 0) context = context[chain.shift()]\n return context\n })\n }\n\n switch (method) {\n case 'dispatchEvent':\n const evt = new CustomEvent(args[0], args[1] || {})\n receivers.forEach(r => r.dispatchEvent(evt))\n break\n default:\n receivers.forEach(r => r[method].apply(r, args))\n }\n}\n\nfunction initialize (streamActions) {\n streamActions.invoke = invoke\n}\n\nexport default { initialize }\n"],
5
- "mappings": "AAAA,SAASA,GAAU,CACjB,IAAMC,EAAU,KAAK,MAAM,KAAK,gBAAgB,WAAW,EACrD,CAAE,GAAAC,EAAI,OAAAC,EAAQ,KAAAC,EAAM,SAAAC,EAAU,SAAAC,CAAS,EAAIL,EAC7CM,EAAY,CAAC,IAAI,EAYrB,OAXID,IAAUC,EAAY,MAAM,KAAK,SAAS,iBAAiBD,CAAQ,CAAC,GAEpED,IACFE,EAAYA,EAAU,IAAIC,GAAK,CAC7B,IAAIC,EAAUD,EACRE,EAAQL,EAAS,MAAM,GAAG,EAChC,KAAOK,EAAM,OAAS,GAAGD,EAAUA,EAAQC,EAAM,MAAM,GACvD,OAAOD,CACT,CAAC,GAGKN,OACD,gBACH,IAAMQ,EAAM,IAAI,YAAYP,EAAK,GAAIA,EAAK,IAAM,CAAC,CAAC,EAClDG,EAAU,QAAQC,GAAKA,EAAE,cAAcG,CAAG,CAAC,EAC3C,cAEAJ,EAAU,QAAQC,GAAKA,EAAEL,GAAQ,MAAMK,EAAGJ,CAAI,CAAC,EAErD,CAEA,SAASQ,EAAYC,EAAe,CAClCA,EAAc,OAASb,CACzB,CAEA,IAAOc,EAAQ,CAAE,WAAAF,CAAW",
6
- "names": ["invoke", "payload", "id", "method", "args", "receiver", "selector", "receivers", "r", "context", "chain", "evt", "initialize", "streamActions", "turbo_ready_default"]
4
+ "sourcesContent": ["function invoke () {\n const payload = JSON.parse(this.templateContent.textContent)\n const { id, selector, receiver, method, args } = payload\n let receivers = [self]\n if (selector) receivers = Array.from(document.querySelectorAll(selector))\n\n if (receiver) {\n receivers = receivers.map(r => {\n let context = r\n const chain = receiver.split('.')\n while (chain.length > 0) context = context[chain.shift()]\n return context\n })\n }\n\n switch (method) {\n case 'dispatchEvent':\n const evt = new CustomEvent(args[0], args[1] || {})\n receivers.forEach(r => r.dispatchEvent(evt))\n break\n default:\n receivers.forEach(r => r[method].apply(r, args))\n }\n}\n\nfunction initialize (streamActions) {\n streamActions.invoke = invoke\n}\n\nexport default { initialize }\n"],
5
+ "mappings": "AAAA,SAASA,GAAU,CACjB,IAAMC,EAAU,KAAK,MAAM,KAAK,gBAAgB,WAAW,EACrD,CAAE,GAAAC,EAAI,SAAAC,EAAU,SAAAC,EAAU,OAAAC,EAAQ,KAAAC,CAAK,EAAIL,EAC7CM,EAAY,CAAC,IAAI,EAYrB,OAXIJ,IAAUI,EAAY,MAAM,KAAK,SAAS,iBAAiBJ,CAAQ,CAAC,GAEpEC,IACFG,EAAYA,EAAU,IAAIC,GAAK,CAC7B,IAAIC,EAAUD,EACRE,EAAQN,EAAS,MAAM,GAAG,EAChC,KAAOM,EAAM,OAAS,GAAGD,EAAUA,EAAQC,EAAM,MAAM,GACvD,OAAOD,CACT,CAAC,GAGKJ,OACD,gBACH,IAAMM,EAAM,IAAI,YAAYL,EAAK,GAAIA,EAAK,IAAM,CAAC,CAAC,EAClDC,EAAU,QAAQC,GAAKA,EAAE,cAAcG,CAAG,CAAC,EAC3C,cAEAJ,EAAU,QAAQC,GAAKA,EAAEH,GAAQ,MAAMG,EAAGF,CAAI,CAAC,EAErD,CAEA,SAASM,EAAYC,EAAe,CAClCA,EAAc,OAASb,CACzB,CAEA,IAAOc,EAAQ,CAAE,WAAAF,CAAW",
6
+ "names": ["invoke", "payload", "id", "selector", "receiver", "method", "args", "receivers", "r", "context", "chain", "evt", "initialize", "streamActions", "turbo_ready_default"]
7
7
  }
@@ -1,6 +1,6 @@
1
1
  function invoke () {
2
2
  const payload = JSON.parse(this.templateContent.textContent)
3
- const { id, method, args, receiver, selector } = payload
3
+ const { id, selector, receiver, method, args } = payload
4
4
  let receivers = [self]
5
5
  if (selector) receivers = Array.from(document.querySelectorAll(selector))
6
6
 
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TurboReady::BroadcastInvokeJob < ActiveJob::Base
4
+ include TurboReady::TagHelper
5
+
6
+ def perform(*streamables, method, **kwargs)
7
+ Turbo::StreamsChannel.broadcast_stream_to(*streamables, content: turbo_stream_invoke_tag(method, **kwargs))
8
+ end
9
+ end
data/bin/loc CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/bin/bash
2
2
 
3
- cloc --exclude-dir=Gemfile,Dockerfile,bin,builds,db,docs,log,node_modules,Procfile,public,storage,tmp --exclude-ext=example,json,lock,md,ru,toml,sql,svg,txt,yml "${1:-.}"
3
+ cloc --exclude-dir=assets app lib
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "turbo-rails"
4
+ require_relative "version"
5
+ require_relative "patches"
6
+
7
+ class TurboReady::Engine < ::Rails::Engine
8
+ config.after_initialize do
9
+ ::Turbo::Streams::TagBuilder.send :include, TurboReady::Patches::TagBuilder
10
+ ::Turbo::Streams::Broadcasts.send :include, TurboReady::Patches::Broadcasts
11
+ ::Turbo::Broadcastable.send :include, TurboReady::Patches::Broadcastable
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Patch for Turbo::Broadcastable which is mixed into ActiveRecord
4
+ # SEE: https://github.com/hotwired/turbo-rails/blob/main/app/models/concerns/turbo/broadcastable.rb
5
+ module TurboReady::Patches::Broadcastable
6
+ def broadcast_invoke_to(*streamables, method, **kwargs)
7
+ Turbo::StreamsChannel.broadcast_invoke_to(*streamables, method, **kwargs)
8
+ end
9
+
10
+ def broadcast_invoke_later_to(*streamables, method, **kwargs)
11
+ Turbo::StreamsChannel.broadcast_invoke_later_to(*streamables, method, **kwargs)
12
+ end
13
+
14
+ def broadcast_invoke(method, **kwargs)
15
+ broadcast_invoke_to(self, method, **kwargs)
16
+ end
17
+
18
+ def broadcast_invoke_later(method, **kwargs)
19
+ broadcast_invoke_later_to(self, method, **kwargs)
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../tag_helper"
4
+ require_relative "../../../app/jobs/turbo_ready/broadcast_invoke_job"
5
+
6
+ # Patch for Turbo::Streams::Broadcasts which is mixed into Turbo::StreamsChannel
7
+ # SEE: https://github.com/hotwired/turbo-rails/blob/main/app/channels/turbo/streams/broadcasts.rb
8
+ module TurboReady::Patches::Broadcasts
9
+ include TurboReady::TagHelper
10
+
11
+ def broadcast_invoke_to(*streamables, method, **kwargs)
12
+ broadcast_stream_to(*streamables, content: turbo_stream_invoke_tag(method, **kwargs))
13
+ end
14
+
15
+ def broadcast_invoke_later_to(*streamables, method, **kwargs)
16
+ TurboReady::BroadcastInvokeJob.perform_later(*streamables, method, **kwargs)
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../string_wrapper"
4
+ require_relative "../tag_helper"
5
+
6
+ # Patch for Turbo::Streams::TagBuilder typically exposed as `turbo_stream`
7
+ # SEE: https://github.com/hotwired/turbo-rails/blob/main/app/models/turbo/streams/tag_builder.rb
8
+ module TurboReady::Patches::TagBuilder
9
+ include TurboReady::TagHelper
10
+
11
+ def invoke(method, args: [], selector: nil, camelize: true, id: nil)
12
+ tag = turbo_stream_invoke_tag(method, args: args, selector: selector, camelize: camelize, id: id)
13
+ TurboReady::StringWrapper.new tag
14
+ end
15
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TurboReady::Patches
4
+ end
5
+
6
+ require_relative "patches/broadcastable"
7
+ require_relative "patches/broadcasts"
8
+ require_relative "patches/tag_builder"