inertia_cable 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +86 -2
- data/lib/inertia_cable/broadcastable.rb +31 -0
- data/lib/inertia_cable/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 25d0d8cfcadeb38db163745b520998196f61fa48865253227abe0e3ee22d5686
|
|
4
|
+
data.tar.gz: 7f7f8affa983fa0c0a1543d1118cf67eb3abd609606b1c16fac076b8e5767ad4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 504d3453823e7cf2887da87a0aa9d56839292e2d8fbe2504e7ca76648d0addafef076e2c655eb3d4a1a88d6f688c7709b60237c3c9ed320676dd9fc7a8544a9f
|
|
7
|
+
data.tar.gz: aba9941405aea7ca05100162c5f7555c0257a9aee672662614167b30d4d75b3e32dd418f8e24d1ce8d014e2e81c4638d3e7d2ab172714d14cc677b51f3bac3a3
|
data/README.md
CHANGED
|
@@ -19,6 +19,7 @@ Inertia HTTP request → controller re-evaluates props → React re-renders
|
|
|
19
19
|
- [Model DSL](#model-dsl)
|
|
20
20
|
- [Controller Helper](#controller-helper)
|
|
21
21
|
- [React Hook](#react-hook)
|
|
22
|
+
- [Direct Messages](#direct-messages)
|
|
22
23
|
- [Suppressing Broadcasts](#suppressing-broadcasts)
|
|
23
24
|
- [Server-Side Debounce](#server-side-debounce)
|
|
24
25
|
- [Testing](#testing)
|
|
@@ -181,11 +182,16 @@ post.broadcast_refresh_later_to(board, debounce: 2.0)
|
|
|
181
182
|
|
|
182
183
|
# With inline condition (block — skips broadcast if falsy)
|
|
183
184
|
post.broadcast_refresh_to(board) { published? }
|
|
185
|
+
|
|
186
|
+
# Direct messages (ephemeral data, no prop reload)
|
|
187
|
+
post.broadcast_message_to(board, data: { progress: 50 })
|
|
188
|
+
post.broadcast_message_later_to(board, data: { progress: 50 })
|
|
189
|
+
post.broadcast_message_to(board, data: { progress: 50 }) { running? }
|
|
184
190
|
```
|
|
185
191
|
|
|
186
192
|
### Broadcast payload
|
|
187
193
|
|
|
188
|
-
|
|
194
|
+
Refresh broadcasts send this JSON:
|
|
189
195
|
|
|
190
196
|
```json
|
|
191
197
|
{
|
|
@@ -200,6 +206,17 @@ Every broadcast sends this JSON:
|
|
|
200
206
|
|
|
201
207
|
The `action` field is `"create"`, `"update"`, or `"destroy"`. The `extra` field contains data from the `extra:` option (empty object if not set).
|
|
202
208
|
|
|
209
|
+
Message broadcasts send a minimal payload:
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
{
|
|
213
|
+
"type": "message",
|
|
214
|
+
"data": { "progress": 50, "total": 200 }
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Messages are ephemeral — no `model`, `id`, `action`, or `timestamp` fields.
|
|
219
|
+
|
|
203
220
|
---
|
|
204
221
|
|
|
205
222
|
## Controller Helper
|
|
@@ -234,6 +251,9 @@ const { connected } = useInertiaCable(cable_stream, {
|
|
|
234
251
|
console.log(`${data.model} #${data.id} was ${data.action}`)
|
|
235
252
|
if (data.extra?.priority === 'high') toast.warn('Priority update!')
|
|
236
253
|
},
|
|
254
|
+
onMessage: (data) => { // receive direct messages (no reload)
|
|
255
|
+
setProgress(data.progress)
|
|
256
|
+
},
|
|
237
257
|
onConnected: () => {}, // subscription connected
|
|
238
258
|
onDisconnected: () => {}, // connection dropped
|
|
239
259
|
debounce: 200, // client-side debounce in ms (default: 100)
|
|
@@ -246,6 +266,7 @@ const { connected } = useInertiaCable(cable_stream, {
|
|
|
246
266
|
| `only` | `string[]` | — | Only reload these props |
|
|
247
267
|
| `except` | `string[]` | — | Reload all props except these |
|
|
248
268
|
| `onRefresh` | `(data) => void` | — | Callback before each reload |
|
|
269
|
+
| `onMessage` | `(data) => void` | — | Receive direct message data (no reload) |
|
|
249
270
|
| `onConnected` | `() => void` | — | Called when subscription connects |
|
|
250
271
|
| `onDisconnected` | `() => void` | — | Called when connection drops |
|
|
251
272
|
| `debounce` | `number` | `100` | Debounce delay in ms |
|
|
@@ -297,7 +318,70 @@ createInertiaApp({
|
|
|
297
318
|
|
|
298
319
|
`getConsumer()` and `setConsumer()` are also exported for low-level access to the ActionCable consumer singleton.
|
|
299
320
|
|
|
300
|
-
TypeScript types (`RefreshPayload`, `UseInertiaCableOptions`, `UseInertiaCableReturn`, `InertiaCableProviderProps`) are exported from `@inertia-cable/react`.
|
|
321
|
+
TypeScript types (`RefreshPayload`, `MessagePayload`, `CablePayload`, `UseInertiaCableOptions`, `UseInertiaCableReturn`, `InertiaCableProviderProps`) are exported from `@inertia-cable/react`. `CablePayload` is a discriminated union of `RefreshPayload | MessagePayload` for type-safe handling of raw payloads.
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Direct Messages
|
|
326
|
+
|
|
327
|
+
Push ephemeral data directly into React state over the same signed stream — no prop reload, no extra hook.
|
|
328
|
+
|
|
329
|
+
### Job progress example
|
|
330
|
+
|
|
331
|
+
```ruby
|
|
332
|
+
# app/jobs/csv_import_job.rb
|
|
333
|
+
class CsvImportJob < ApplicationJob
|
|
334
|
+
def perform(import)
|
|
335
|
+
rows = CSV.read(import.file.path)
|
|
336
|
+
rows.each_with_index do |row, i|
|
|
337
|
+
process_row(row)
|
|
338
|
+
import.broadcast_message_to(import.user, data: { progress: i + 1, total: rows.size })
|
|
339
|
+
end
|
|
340
|
+
import.broadcast_refresh_to(import.user) # final reload with completed data
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
```tsx
|
|
346
|
+
import { useState } from 'react'
|
|
347
|
+
import { useInertiaCable } from '@inertia-cable/react'
|
|
348
|
+
|
|
349
|
+
export default function ImportShow({ import_record, cable_stream }) {
|
|
350
|
+
const [progress, setProgress] = useState<{ progress: number; total: number } | null>(null)
|
|
351
|
+
|
|
352
|
+
useInertiaCable(cable_stream, {
|
|
353
|
+
only: ['import_record'],
|
|
354
|
+
onMessage: (data) => setProgress({ progress: data.progress as number, total: data.total as number }),
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
return (
|
|
358
|
+
<div>
|
|
359
|
+
<h1>Import #{import_record.id}</h1>
|
|
360
|
+
{progress && <p>Processing {progress.progress} / {progress.total}</p>}
|
|
361
|
+
</div>
|
|
362
|
+
)
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Usage patterns
|
|
367
|
+
|
|
368
|
+
```tsx
|
|
369
|
+
// Prop reload only (unchanged)
|
|
370
|
+
useInertiaCable(stream, { only: ['messages'] })
|
|
371
|
+
|
|
372
|
+
// Direct data only (no reload)
|
|
373
|
+
useInertiaCable(stream, {
|
|
374
|
+
onMessage: (data) => setProgress(data.progress)
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
// Both — progress during job, final reload on completion
|
|
378
|
+
useInertiaCable(stream, {
|
|
379
|
+
only: ['imports'],
|
|
380
|
+
onMessage: (data) => setProgress(data.progress)
|
|
381
|
+
})
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Messages are delivered immediately with no debouncing. Each `broadcast_message_to` call triggers exactly one `onMessage` callback.
|
|
301
385
|
|
|
302
386
|
---
|
|
303
387
|
|
|
@@ -128,6 +128,33 @@ module InertiaCable
|
|
|
128
128
|
broadcast_refresh_later_to(self.class.model_name.plural, &block)
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
+
# Broadcast a direct message synchronously to explicit stream(s).
|
|
132
|
+
#
|
|
133
|
+
# post.broadcast_message_to(board, data: { progress: 50 })
|
|
134
|
+
# post.broadcast_message_to(board, :posts, data: { progress: 50 })
|
|
135
|
+
# post.broadcast_message_to(board, data: { progress: 50 }) { running? }
|
|
136
|
+
#
|
|
137
|
+
def broadcast_message_to(*streamables, data:, &block)
|
|
138
|
+
return if self.class.suppressed_inertia_cable_broadcasts?
|
|
139
|
+
return if block && !instance_exec(&block)
|
|
140
|
+
|
|
141
|
+
InertiaCable.broadcast(streamables, message_payload(data: data))
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Broadcast a direct message asynchronously to explicit stream(s).
|
|
145
|
+
#
|
|
146
|
+
# post.broadcast_message_later_to(board, data: { progress: 50 })
|
|
147
|
+
# post.broadcast_message_later_to(board, :posts, data: { progress: 50 })
|
|
148
|
+
# post.broadcast_message_later_to(board, data: { progress: 50 }) { running? }
|
|
149
|
+
#
|
|
150
|
+
def broadcast_message_later_to(*streamables, data:, &block)
|
|
151
|
+
return if self.class.suppressed_inertia_cable_broadcasts?
|
|
152
|
+
return if block && !instance_exec(&block)
|
|
153
|
+
|
|
154
|
+
resolved = InertiaCable::Streams::StreamName.stream_name_from(streamables)
|
|
155
|
+
InertiaCable::BroadcastJob.perform_later(resolved, message_payload(data: data))
|
|
156
|
+
end
|
|
157
|
+
|
|
131
158
|
private
|
|
132
159
|
|
|
133
160
|
def resolve_stream(stream)
|
|
@@ -168,5 +195,9 @@ module InertiaCable
|
|
|
168
195
|
payload[:extra] = extra if extra.present?
|
|
169
196
|
payload
|
|
170
197
|
end
|
|
198
|
+
|
|
199
|
+
def message_payload(data:)
|
|
200
|
+
{ type: "message", data: data }
|
|
201
|
+
end
|
|
171
202
|
end
|
|
172
203
|
end
|