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.
- checksums.yaml +4 -4
- data/Gemfile +0 -3
- data/Gemfile.lock +55 -52
- data/README.md +259 -99
- data/Rakefile +13 -1
- data/app/assets/builds/turbo_ready.js +1 -1
- data/app/assets/builds/turbo_ready.js.map +3 -3
- data/app/assets/images/turbo-ready-logo-dark.webp +0 -0
- data/app/assets/images/turbo-ready-logo-light.webp +0 -0
- data/app/javascript/turbo_ready.js +1 -1
- data/app/jobs/turbo_ready/broadcast_invoke_job.rb +9 -0
- data/bin/loc +1 -1
- data/lib/turbo_ready/engine.rb +13 -0
- data/lib/turbo_ready/patches/broadcastable.rb +21 -0
- data/lib/turbo_ready/patches/broadcasts.rb +18 -0
- data/lib/turbo_ready/patches/tag_builder.rb +15 -0
- data/lib/turbo_ready/patches.rb +8 -0
- data/lib/turbo_ready/string_wrapper.rb +28 -0
- data/lib/turbo_ready/tag_helper.rb +42 -0
- data/lib/turbo_ready/version.rb +1 -1
- data/lib/turbo_ready.rb +1 -10
- data/package.json +2 -2
- data/turbo_ready.gemspec +28 -19
- data/yarn-error.log +3003 -11
- data/yarn.lock +158 -153
- metadata +140 -7
- data/lib/turbo_ready/railtie.rb +0 -6
- data/lib/turbo_ready/string.rb +0 -36
- data/lib/turbo_ready/tag_builder.rb +0 -33
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
<p align="center">
|
2
|
-
<
|
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/"
|
11
|
-
<img alt="Lines of Code" src="https://img.shields.io/badge/
|
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://
|
14
|
-
<img
|
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://
|
17
|
-
<img alt="
|
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://
|
20
|
-
<img alt="
|
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
|
-
|
47
|
-
|
69
|
+
- [Method Chaining](#method-chaining)
|
70
|
+
- [Dispatching Events](#dispatching-events)
|
71
|
+
- [Syntax Styles](#syntax-styles)
|
48
72
|
- [Extending Behavior](#extending-behavior)
|
49
|
-
|
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
|
-
|
61
|
-
|
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
|
95
|
+
If you find that CRUD isn't enough, TurboReady is there to handle pretty much everything else.
|
64
96
|
|
65
|
-
|
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
|
-

|
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
|
-
|
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
|
-
|
130
|
+
Import and intialize TurboReady in your application.
|
103
131
|
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
116
|
-
|
144
|
+
```diff
|
145
|
+
# app/javascript/application.js
|
146
|
+
import '@hotwired/turbo-rails'
|
147
|
+
+import TurboReady from 'turbo_ready'
|
117
148
|
|
118
|
-
|
149
|
+
+TurboReady.initialize(Turbo.StreamActions) // Adds TurboReady stream actions to Turbo
|
150
|
+
```
|
119
151
|
|
120
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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("
|
143
|
-
.invoke("
|
144
|
-
.invoke("
|
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
|
-
|
174
|
+
### Dispatching Events
|
149
175
|
|
150
|
-
|
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
|
-
|
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
|
-
|
156
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
227
|
+
### Implementation Details
|
193
228
|
|
194
|
-
There's
|
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,
|
201
|
-
# |
|
202
|
-
# |
|
203
|
-
# |
|
204
|
-
# |
|
205
|
-
# |
|
206
|
-
# |
|
207
|
-
# |
|
208
|
-
# |
|
209
|
-
# |
|
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
|
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
|
-
|
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
|
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
|
-
|
230
|
-
|
231
|
-
|
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,
|
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,
|
5
|
-
"mappings": "AAAA,SAASA,GAAU,CACjB,IAAMC,EAAU,KAAK,MAAM,KAAK,gBAAgB,WAAW,EACrD,CAAE,GAAAC,EAAI,
|
6
|
-
"names": ["invoke", "payload", "id", "
|
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
|
}
|
Binary file
|
Binary file
|
@@ -1,6 +1,6 @@
|
|
1
1
|
function invoke () {
|
2
2
|
const payload = JSON.parse(this.templateContent.textContent)
|
3
|
-
const { id,
|
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
@@ -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
|