dexiecable 0.1.9 → 1.0.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 +29 -33
- data/lib/dexiecable/active_record_ext.rb +23 -39
- data/lib/dexiecable/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b462191000153b424593099689d503a34152404f08cd33911f8db01a61d154cf
|
|
4
|
+
data.tar.gz: 3fd86cd2c4b9cb4a73b43fc0fee7e686a91a9c4d1a1e390cc544d87f0dddbdc0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 933c9999fa8d60d04fb3571a094568f1fe78ace8f3a13de6e48dd18d71471e647a1b33c6eb49e2ec6123321933967a8479a1fb6c10906412b5a518302b141f66
|
|
7
|
+
data.tar.gz: 13751b561bb8f7d57d05ba7639b474c15d3077262a3ec488a8f217f1d5c20be4235757172eac5fa1b5e558af50b5c97d6d253d5a04544c60c80a768f44ab3049
|
data/README.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# DexieCable
|
|
2
2
|
|
|
3
3
|
> [!NOTE]
|
|
4
|
-
> DexieCable is NOT a local-first solution, because it lacks the capability to automatically sync updates back to the server
|
|
4
|
+
> The current version of DexieCable is NOT a local-first solution, because it lacks the capability to automatically sync updates back to the server. For now, think of it more as an alternative to turbo streams that works well with component frameworks.
|
|
5
|
+
>
|
|
6
|
+
> Full synchronization utilizing event streams will arrive in DexieCable 2.0.
|
|
5
7
|
|
|
6
|
-
DexieCable augments ActionCable channels with 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 [`
|
|
8
|
+
DexieCable augments ActionCable channels with 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 [`streams_via`](#streams_via--automatic-model-syncing) ActiveRecord macro for automatic change syncing.
|
|
7
9
|
|
|
8
10
|
You can run any Dexie table update directly inside a channel:
|
|
9
11
|
|
|
@@ -30,11 +32,11 @@ class NotificationsController < ApplicationController
|
|
|
30
32
|
end
|
|
31
33
|
```
|
|
32
34
|
|
|
33
|
-
An even more convenient way is to use the `
|
|
35
|
+
An even more convenient way is to use the `streams_via` macro (more info [below](#streams_via--automatic-model-syncing))
|
|
34
36
|
|
|
35
37
|
```ruby
|
|
36
38
|
class Notification < ApplicationRecord
|
|
37
|
-
|
|
39
|
+
streams_via UserChannel, to: :user
|
|
38
40
|
end
|
|
39
41
|
```
|
|
40
42
|
|
|
@@ -48,7 +50,7 @@ Add to your `Gemfile`:
|
|
|
48
50
|
gem "dexiecable"
|
|
49
51
|
```
|
|
50
52
|
|
|
51
|
-
Then `bundle install`. The Railtie automatically extends `ActiveRecord::Base` with `
|
|
53
|
+
Then `bundle install`. The Railtie automatically extends `ActiveRecord::Base` with `streams_via`.
|
|
52
54
|
|
|
53
55
|
### npm package
|
|
54
56
|
|
|
@@ -131,33 +133,25 @@ UserChannel[current_user]
|
|
|
131
133
|
|
|
132
134
|
The full query chain is serialized as JSON and sent over ActionCable. The JS client replays every method call against the local Dexie database in order.
|
|
133
135
|
|
|
134
|
-
### `
|
|
136
|
+
### `streams_via` — automatic model streaming
|
|
135
137
|
|
|
136
|
-
Add to any ActiveRecord model.
|
|
137
|
-
|
|
138
|
-
```ruby
|
|
139
|
-
class Message < ApplicationRecord
|
|
140
|
-
syncs_to_dexie via: -> { UserChannel[current_user] }
|
|
141
|
-
end
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
You can also pass a channel class with `to:` to have DexieCable handle the scoping:
|
|
138
|
+
Add to any ActiveRecord model. Just provide the channel class and, optionally, the broadcast target.
|
|
145
139
|
|
|
146
140
|
```ruby
|
|
147
141
|
class Message < ApplicationRecord
|
|
148
142
|
# Calls send(:receiver), then broadcasts: UserChannel.broadcast_to(receiver, ...)
|
|
149
|
-
|
|
143
|
+
streams_via UserChannel, to: :receiver
|
|
150
144
|
|
|
151
|
-
# String used directly:
|
|
152
|
-
|
|
145
|
+
# String used directly: RoomChannel.broadcast_to("public", ...)
|
|
146
|
+
streams_via RoomChannel, to: "public"
|
|
153
147
|
|
|
154
148
|
# Procs are also supported. If an array is returned, multiple broadcasts are made
|
|
155
149
|
# conversation.users.each { |u| UserChannel.broadcast_to(u, ...) }
|
|
156
|
-
|
|
150
|
+
streams_via UserChannel, to: -> { conversation.users }
|
|
157
151
|
end
|
|
158
152
|
```
|
|
159
153
|
|
|
160
|
-
Internally, `
|
|
154
|
+
Internally, `streams_via` sets up the following ActiveRecord callbacks:
|
|
161
155
|
|
|
162
156
|
| Event | Action |
|
|
163
157
|
|---|---|
|
|
@@ -169,21 +163,23 @@ Internally, `syncs_to_dexie` sets up the following ActiveRecord callbacks:
|
|
|
169
163
|
|
|
170
164
|
| Option | Default | Description |
|
|
171
165
|
|---|---|---|
|
|
172
|
-
|
|
|
173
|
-
| `to:` |
|
|
166
|
+
| *(first argument)* | *(required)* | A DexieCable channel class |
|
|
167
|
+
| `to:` | the record itself | The stream target passed to `broadcast_to`. Symbol → calls `send`. String → used as-is. Proc → evaluated in record context. Returns a single recipient or collection. |
|
|
174
168
|
| `table:` | model's `table_name` | Override the Dexie table name. A Proc is evaluated in the record's context. |
|
|
175
169
|
| `only:` | `[:create, :update, :destroy]` | Limit which events trigger a sync |
|
|
176
170
|
| `with:` | `:as_json_for_dexie` | Method name (Symbol) or Proc for serializing records |
|
|
177
171
|
| `if:` | *(none)* | Symbol (method name) or Proc — only sync when it returns truthy |
|
|
178
172
|
| `unless:` | *(none)* | Symbol (method name) or Proc — skip sync when it returns truthy |
|
|
179
173
|
|
|
180
|
-
You can combine multiple `
|
|
174
|
+
You can combine multiple `streams_via` declarations, each with different conditions:
|
|
181
175
|
|
|
182
176
|
```ruby
|
|
183
177
|
class Message < ApplicationRecord
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
178
|
+
streams_via UserChannel, to: -> { sender },
|
|
179
|
+
if: :published?
|
|
180
|
+
|
|
181
|
+
streams_via AdminChannel,
|
|
182
|
+
unless: -> { draft? }
|
|
187
183
|
end
|
|
188
184
|
```
|
|
189
185
|
|
|
@@ -194,23 +190,23 @@ Override `as_json_for_dexie` in your model, or use the `with` option to specify
|
|
|
194
190
|
```ruby
|
|
195
191
|
class Message < ApplicationRecord
|
|
196
192
|
# Using the default as_json_for_dexie override:
|
|
197
|
-
|
|
193
|
+
streams_via UserChannel, to: :sender
|
|
198
194
|
|
|
199
195
|
def as_json_for_dexie
|
|
200
196
|
super.merge(room_name: room.name)
|
|
201
197
|
end
|
|
202
198
|
|
|
203
199
|
# Or use a custom serializer method:
|
|
204
|
-
|
|
205
|
-
|
|
200
|
+
streams_via AdminChannel, to: :admin,
|
|
201
|
+
with: :admin_payload
|
|
206
202
|
|
|
207
203
|
def admin_payload
|
|
208
204
|
attributes.slice("id", "body", "flagged")
|
|
209
205
|
end
|
|
210
206
|
|
|
211
207
|
# Or a Proc:
|
|
212
|
-
|
|
213
|
-
|
|
208
|
+
streams_via PublicChannel,
|
|
209
|
+
with: -> { { id: id, summary: body.truncate(100) } }
|
|
214
210
|
end
|
|
215
211
|
```
|
|
216
212
|
|
|
@@ -255,7 +251,7 @@ dexie.messages.where("room_id").equals(5).add({ id: 1, text: "hello" })
|
|
|
255
251
|
|
|
256
252
|
### Use sequence IDs to avoid data loss
|
|
257
253
|
|
|
258
|
-
A common pattern to avoid data loss during transient disconnections is using sequence IDs to bridge the offline gap. When a connection drops, updates continue on the server. Sending the client’s latest known sequence ID upon reconnect allows the backend to query and stream only the records missed while offline.
|
|
254
|
+
A common pattern to avoid data loss during transient disconnections is using sequence IDs to bridge the offline gap and detect gaps in transmitted records. When a connection drops, updates continue on the server. Sending the client’s latest known sequence ID upon reconnect allows the backend to query and stream only the records missed while offline.
|
|
259
255
|
|
|
260
256
|
To enable this, DexieCable ships its own version of the ActionCable client with one key
|
|
261
257
|
extension: **channel params can be functions**. When a param value is a
|
|
@@ -286,7 +282,7 @@ class RoomChannel < ApplicationChannel:Base
|
|
|
286
282
|
end
|
|
287
283
|
```
|
|
288
284
|
|
|
289
|
-
|
|
285
|
+
To add Sequence IDs, you might want to have look at the [Sequenced](https://github.com/derrickreimer/sequenced). Another option is to use [AnyCable](https://docs.anycable.io/rails/getting_started) since it guarantees deliveries of ActionCable messages.
|
|
290
286
|
|
|
291
287
|
### Multi-user environments
|
|
292
288
|
|
|
@@ -9,24 +9,21 @@ module DexieCable
|
|
|
9
9
|
# DexieCable channel.
|
|
10
10
|
#
|
|
11
11
|
# class Message < ApplicationRecord
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# syncs_to_dexie via: -> { tenant_channel }
|
|
12
|
+
# streams_via UserChannel, to: :sender
|
|
13
|
+
# streams_via UserChannel, to: "global_feed"
|
|
14
|
+
# streams_via UserChannel, to: -> { conversation.users }
|
|
15
|
+
# streams_via PublicChannel
|
|
17
16
|
# end
|
|
18
17
|
#
|
|
19
|
-
# @param via [Class
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
# is given, each recipient is mapped through
|
|
24
|
-
# +via[to]+ to produce scoped channels.
|
|
18
|
+
# @param via [Class] A DexieCable channel class, passed as the
|
|
19
|
+
# first positional argument. Each recipient is
|
|
20
|
+
# mapped through +via[to]+ to produce scoped
|
|
21
|
+
# channels.
|
|
25
22
|
# @param to [Proc, Symbol, String] A Proc evaluated in record
|
|
26
23
|
# context, a Symbol to call via +send+, or a String
|
|
27
24
|
# used directly as the stream name for +broadcast_to+.
|
|
28
25
|
# Must return a single recipient or collection of
|
|
29
|
-
# recipients.
|
|
26
|
+
# recipients. Defaults to the record itself.
|
|
30
27
|
# @param table [String, Symbol, Proc] Override the Dexie table name
|
|
31
28
|
# (defaults to the model's table_name). A Proc is
|
|
32
29
|
# evaluated in the record's context.
|
|
@@ -38,7 +35,7 @@ module DexieCable
|
|
|
38
35
|
# returns truthy (evaluated in the record's context).
|
|
39
36
|
# @param unless [Symbol, Proc] Skip sync if the given method or proc
|
|
40
37
|
# returns truthy (evaluated in the record's context).
|
|
41
|
-
def
|
|
38
|
+
def streams_via(via, to: nil, table: nil, only: nil, with: nil, **options)
|
|
42
39
|
events = Array(only || %i[create update destroy])
|
|
43
40
|
conditions = options.slice(:if, :unless)
|
|
44
41
|
serializer = with || :as_json_for_dexie
|
|
@@ -50,7 +47,7 @@ module DexieCable
|
|
|
50
47
|
before_destroy :dexie_sync_before_destroy
|
|
51
48
|
|
|
52
49
|
after_commit on: :destroy, **conditions do
|
|
53
|
-
|
|
50
|
+
resolve_channels(via, to).each do |channel|
|
|
54
51
|
next unless channel
|
|
55
52
|
channel.table(resolve_table(table)).delete(dexie_destroy_id)
|
|
56
53
|
end
|
|
@@ -59,19 +56,19 @@ module DexieCable
|
|
|
59
56
|
|
|
60
57
|
if events.include?(:create)
|
|
61
58
|
after_commit on: :create, **conditions do
|
|
62
|
-
|
|
59
|
+
resolve_channels(via, to).each do |channel|
|
|
63
60
|
next unless channel
|
|
64
|
-
channel.table(resolve_table(table)).add(
|
|
61
|
+
channel.table(resolve_table(table)).add(resolve(serializer))
|
|
65
62
|
end
|
|
66
63
|
end
|
|
67
64
|
end
|
|
68
65
|
|
|
69
66
|
if events.include?(:update)
|
|
70
67
|
after_commit on: :update, **conditions do
|
|
71
|
-
|
|
68
|
+
resolve_channels(via, to).each do |channel|
|
|
72
69
|
next unless channel
|
|
73
70
|
|
|
74
|
-
changes =
|
|
71
|
+
changes = resolve(serializer).slice(*saved_changes.keys)
|
|
75
72
|
channel.table(resolve_table(table)).update(id, changes)
|
|
76
73
|
end
|
|
77
74
|
end
|
|
@@ -81,21 +78,16 @@ module DexieCable
|
|
|
81
78
|
|
|
82
79
|
private
|
|
83
80
|
|
|
84
|
-
def
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
recipients = resolve_recipient(to)
|
|
88
|
-
Array(recipients).map { |r| channel[r] }
|
|
89
|
-
else
|
|
90
|
-
channel
|
|
91
|
-
end
|
|
81
|
+
def resolve_channels(via, to = nil)
|
|
82
|
+
recipients = to ? resolve(to) : self
|
|
83
|
+
Array(recipients).map { |r| via[r] }
|
|
92
84
|
end
|
|
93
85
|
|
|
94
|
-
def
|
|
95
|
-
case
|
|
96
|
-
when Proc then instance_exec(&
|
|
97
|
-
when Symbol then send(
|
|
98
|
-
else
|
|
86
|
+
def resolve(val)
|
|
87
|
+
case val
|
|
88
|
+
when Proc then instance_exec(&val)
|
|
89
|
+
when Symbol then send(val)
|
|
90
|
+
else val
|
|
99
91
|
end
|
|
100
92
|
end
|
|
101
93
|
|
|
@@ -107,14 +99,6 @@ module DexieCable
|
|
|
107
99
|
end
|
|
108
100
|
end
|
|
109
101
|
|
|
110
|
-
def serialize_record(serializer)
|
|
111
|
-
case serializer
|
|
112
|
-
when Proc then instance_exec(&serializer)
|
|
113
|
-
when Symbol then send(serializer)
|
|
114
|
-
else serializer
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
102
|
# Override in your model to customise the payload synced to Dexie.
|
|
119
103
|
def as_json_for_dexie
|
|
120
104
|
as_json
|
data/lib/dexiecable/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dexiecable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stefan Buhrmester
|
|
@@ -39,7 +39,7 @@ dependencies:
|
|
|
39
39
|
version: '7.0'
|
|
40
40
|
description: DexieCable augments ActionCable channels with a query DSL that mirrors
|
|
41
41
|
the Dexie.js API, letting you push database mutations from the server to the client
|
|
42
|
-
in real time. Includes an ActiveRecord macro (
|
|
42
|
+
in real time. Includes an ActiveRecord macro (streams_via) for automatic change
|
|
43
43
|
syncing.
|
|
44
44
|
email:
|
|
45
45
|
- stefan@buhrmi.de
|
|
@@ -75,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
75
75
|
- !ruby/object:Gem::Version
|
|
76
76
|
version: '0'
|
|
77
77
|
requirements: []
|
|
78
|
-
rubygems_version: 4.0.
|
|
78
|
+
rubygems_version: 4.0.16
|
|
79
79
|
specification_version: 4
|
|
80
80
|
summary: Run Dexie.js IndexedDB operations from your Rails ActionCable channels.
|
|
81
81
|
test_files: []
|