dexiecable 2.0.1 → 2.0.2
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 +41 -2
- data/lib/dexiecable/concern.rb +25 -10
- data/lib/dexiecable/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: c5284c177e5517bff845edf2e5317d1ab9c676deafd605a662b3dea1b7363b6a
|
|
4
|
+
data.tar.gz: 94f3e811e2bca16d05967c0c0dbf242bcae00b880212ad0dba7d1ee44f8263c9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4999890a88da548b86e19843b40a0221654eb2a144ec52f954c7723cb652c287d7884fd34e0703d5895131e3b1213ee840a427f31defa91ff21738a38133514a
|
|
7
|
+
data.tar.gz: 67bb97e60d5d9fc7416e239e0695b69ff00a4930df8d97d8380da29bd51686af9f2d70f04bd69d92322e61ea042a71704ce4e98bbc35d14ee5f0a33d13343def
|
data/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# DexieCable
|
|
2
2
|
|
|
3
3
|
> [!NOTE]
|
|
4
|
-
> DexieCable is NOT
|
|
4
|
+
> By itself, DexieCable is NOT a local-first solution. It has no automatic capability to push client-side changes back to the server.
|
|
5
5
|
>
|
|
6
|
-
>
|
|
6
|
+
> An addon providing full synchronization based on event streams is currently in development. But for now, if you need full synchronization, you'll have to roll your own.
|
|
7
7
|
|
|
8
8
|
DexieCable gives your ActionCable channel a query DSL that mirrors the Dexie.js API, letting you push database mutations from the server to the client in real time. It also gives you a [`syncs_to_dexie`](#syncs_to_dexie-automatic-model-streaming) ActiveRecord macro for automatic change syncing.
|
|
9
9
|
|
|
@@ -26,6 +26,40 @@ class Notification < ApplicationRecord
|
|
|
26
26
|
end
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
## What's new in 2.0
|
|
30
|
+
|
|
31
|
+
DexieCable 2.0 is a mixin. Create your own `DexieChannel` and `include DexieCable`:
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
class DexieChannel < ApplicationCable::Channel
|
|
35
|
+
include DexieCable
|
|
36
|
+
end
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
On the client, subscribe to that channel and add streams as needed. `addStream` accepts a signed token or a plain string, and returns a function that removes the stream:
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
const subscription = subscribe(db);
|
|
43
|
+
const unsubscribe = subscription.addStream(userStreamToken);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
You can then use the new `subscribed_to` hook to push initial data before any live mutation arrives. The first argument is the record the token was issued for, or the plain stream name for a public stream:
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
class DexieChannel < ApplicationCable::Channel
|
|
50
|
+
include DexieCable
|
|
51
|
+
|
|
52
|
+
def subscribed_to(record, params)
|
|
53
|
+
case record
|
|
54
|
+
when "feed"
|
|
55
|
+
table(record).bulkAdd(Announcement.for_stream(record).map(&:as_json_for_dexie))
|
|
56
|
+
when User
|
|
57
|
+
table("notifications").bulkAdd(record.notifications.map(&:as_json_for_dexie))
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
```
|
|
62
|
+
|
|
29
63
|
## Installation
|
|
30
64
|
|
|
31
65
|
### Ruby gem
|
|
@@ -101,6 +135,8 @@ Tokens never expire by default. Pass `expires_in:` or `expires_at:` to limit a t
|
|
|
101
135
|
DexieChannel.stream_token_for(current_user, expires_in: 1.day)
|
|
102
136
|
```
|
|
103
137
|
|
|
138
|
+
An expired token, or one whose record no longer exists, is rejected. Only plain strings are treated as public stream names.
|
|
139
|
+
|
|
104
140
|
Send that token to the client (render it in a view, return it from an endpoint, etc.) and add it to the subscription:
|
|
105
141
|
|
|
106
142
|
```js
|
|
@@ -228,6 +264,9 @@ Add to any ActiveRecord model. Optionally provide the broadcast target.
|
|
|
228
264
|
|
|
229
265
|
```ruby
|
|
230
266
|
class Message < ApplicationRecord
|
|
267
|
+
belongs_to :conversation
|
|
268
|
+
belongs_to :receiver
|
|
269
|
+
|
|
231
270
|
# Calls send(:receiver), then broadcasts: DexieChannel.broadcast_to(receiver, ...)
|
|
232
271
|
syncs_to_dexie via: :receiver
|
|
233
272
|
|
data/lib/dexiecable/concern.rb
CHANGED
|
@@ -72,29 +72,35 @@ module DexieCable
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def add_stream(data)
|
|
75
|
-
|
|
75
|
+
token = data["stream"].to_s
|
|
76
76
|
params = data.except("action", "stream").with_indifferent_access
|
|
77
77
|
|
|
78
|
-
if
|
|
78
|
+
if signed_token?(token)
|
|
79
|
+
# A token that no longer resolves (expired, revoked, deleted record) is
|
|
80
|
+
# rejected instead of being treated as a public stream name.
|
|
81
|
+
record = resolve_subscribe_target(token)
|
|
82
|
+
return unless record
|
|
83
|
+
|
|
79
84
|
stream_for record
|
|
80
85
|
subscribed_to(record, params)
|
|
81
86
|
else
|
|
82
|
-
|
|
83
|
-
return if name.blank?
|
|
87
|
+
return if token.blank?
|
|
84
88
|
|
|
85
|
-
stream_from public_stream_name(
|
|
86
|
-
subscribed_to(
|
|
89
|
+
stream_from public_stream_name(token)
|
|
90
|
+
subscribed_to(token, params)
|
|
87
91
|
end
|
|
88
92
|
end
|
|
89
93
|
|
|
90
94
|
def remove_stream(data)
|
|
91
|
-
|
|
95
|
+
token = data["stream"].to_s
|
|
96
|
+
|
|
97
|
+
if signed_token?(token)
|
|
98
|
+
record = resolve_subscribe_target(token)
|
|
99
|
+
return unless record
|
|
92
100
|
|
|
93
|
-
if record
|
|
94
101
|
stop_stream_from self.class.broadcasting_for(record)
|
|
95
102
|
else
|
|
96
|
-
|
|
97
|
-
stop_stream_from public_stream_name(name) if name.present?
|
|
103
|
+
stop_stream_from public_stream_name(token) if token.present?
|
|
98
104
|
end
|
|
99
105
|
end
|
|
100
106
|
|
|
@@ -110,6 +116,15 @@ module DexieCable
|
|
|
110
116
|
|
|
111
117
|
private
|
|
112
118
|
|
|
119
|
+
# Distinguishes a signed stream token from a public stream name. A signed
|
|
120
|
+
# token keeps a valid signature even after it expires, which is what lets us
|
|
121
|
+
# reject expired tokens instead of falling back to a public stream.
|
|
122
|
+
def signed_token?(token)
|
|
123
|
+
return false if token.blank? || SignedGlobalID.verifier.nil?
|
|
124
|
+
|
|
125
|
+
SignedGlobalID.verifier.valid_message?(token)
|
|
126
|
+
end
|
|
127
|
+
|
|
113
128
|
def public_stream_name(name)
|
|
114
129
|
self.class.broadcasting_for("#{PUBLIC_STREAM_PREFIX}#{name}")
|
|
115
130
|
end
|
data/lib/dexiecable/version.rb
CHANGED