dexiecable 0.2.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0959acfdf7865fc834899dd5ec5f14c5fbc85bdbc16a874f269bca2688d2579
4
- data.tar.gz: 9e1b0ea9fb9a89c8289aa0faa77b47c6cdd09bb6fd336eb14c9292fc7c67d0a1
3
+ metadata.gz: b462191000153b424593099689d503a34152404f08cd33911f8db01a61d154cf
4
+ data.tar.gz: 3fd86cd2c4b9cb4a73b43fc0fee7e686a91a9c4d1a1e390cc544d87f0dddbdc0
5
5
  SHA512:
6
- metadata.gz: a7106c7ed40f0fcf680109f1f476f4195e911836633766c0706d84852621f8a36b76a7eeaa78b2b9fb09ffa5f750a0147fae11a73fc4d8028e75b00840a77799
7
- data.tar.gz: 8f78e58b67faef035cf225ce08d79f4ab0b954123fe76385e7f458e995f955f4880827b3c62353b3763ab888b57f47af4d8954978758fb4edbf5e50601d68ec5
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 (this might be added later). For now, think of it as a real-time local-cache solution. Also, check out this [blog post introducing DexieCable](https://dev.to/buhrmi/real-time-rails-without-turbo-modern-reactive-uis-with-inertia-and-dexiecable-4lge).
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 [`streams_to_dexie`](#streams_to_dexie--automatic-model-syncing) ActiveRecord macro for automatic change syncing.
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 `streams_to_dexie` macro (more info [below](#streams_to_dexie--automatic-model-syncing))
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
- streams_to_dexie via: UserChannel, to: :user
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 `streams_to_dexie`.
53
+ Then `bundle install`. The Railtie automatically extends `ActiveRecord::Base` with `streams_via`.
52
54
 
53
55
  ### npm package
54
56
 
@@ -131,25 +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
- ### `streams_to_dexie` — automatic model syncing
136
+ ### `streams_via` — automatic model streaming
135
137
 
136
- Add to any ActiveRecord model. Just provide the channel class and the broadcast target.
138
+ Add to any ActiveRecord model. Just provide the channel class and, optionally, the broadcast target.
137
139
 
138
140
  ```ruby
139
141
  class Message < ApplicationRecord
140
142
  # Calls send(:receiver), then broadcasts: UserChannel.broadcast_to(receiver, ...)
141
- streams_to_dexie via: UserChannel, to: :receiver
143
+ streams_via UserChannel, to: :receiver
142
144
 
143
- # String used directly: UserChannel.broadcast_to("global_feed", ...)
144
- streams_to_dexie via: UserChannel, to: "global_feed"
145
+ # String used directly: RoomChannel.broadcast_to("public", ...)
146
+ streams_via RoomChannel, to: "public"
145
147
 
146
148
  # Procs are also supported. If an array is returned, multiple broadcasts are made
147
149
  # conversation.users.each { |u| UserChannel.broadcast_to(u, ...) }
148
- streams_to_dexie via: UserChannel, to: -> { conversation.users }
150
+ streams_via UserChannel, to: -> { conversation.users }
149
151
  end
150
152
  ```
151
153
 
152
- Internally, `streams_to_dexie` sets up the following ActiveRecord callbacks:
154
+ Internally, `streams_via` sets up the following ActiveRecord callbacks:
153
155
 
154
156
  | Event | Action |
155
157
  |---|---|
@@ -161,23 +163,23 @@ Internally, `streams_to_dexie` sets up the following ActiveRecord callbacks:
161
163
 
162
164
  | Option | Default | Description |
163
165
  |---|---|---|
164
- | `via:` | *(required)* | A DexieCable channel class |
165
- | `to:` | *(none)* | 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. |
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. |
166
168
  | `table:` | model's `table_name` | Override the Dexie table name. A Proc is evaluated in the record's context. |
167
169
  | `only:` | `[:create, :update, :destroy]` | Limit which events trigger a sync |
168
170
  | `with:` | `:as_json_for_dexie` | Method name (Symbol) or Proc for serializing records |
169
171
  | `if:` | *(none)* | Symbol (method name) or Proc — only sync when it returns truthy |
170
172
  | `unless:` | *(none)* | Symbol (method name) or Proc — skip sync when it returns truthy |
171
173
 
172
- You can combine multiple `streams_to_dexie` declarations, each with different conditions:
174
+ You can combine multiple `streams_via` declarations, each with different conditions:
173
175
 
174
176
  ```ruby
175
177
  class Message < ApplicationRecord
176
- streams_to_dexie via: UserChannel, to: -> { sender },
177
- if: :published?
178
+ streams_via UserChannel, to: -> { sender },
179
+ if: :published?
178
180
 
179
- streams_to_dexie via: AdminChannel,
180
- unless: -> { draft? }
181
+ streams_via AdminChannel,
182
+ unless: -> { draft? }
181
183
  end
182
184
  ```
183
185
 
@@ -188,23 +190,23 @@ Override `as_json_for_dexie` in your model, or use the `with` option to specify
188
190
  ```ruby
189
191
  class Message < ApplicationRecord
190
192
  # Using the default as_json_for_dexie override:
191
- streams_to_dexie via: UserChannel, to: :sender
193
+ streams_via UserChannel, to: :sender
192
194
 
193
195
  def as_json_for_dexie
194
196
  super.merge(room_name: room.name)
195
197
  end
196
198
 
197
199
  # Or use a custom serializer method:
198
- streams_to_dexie via: AdminChannel, to: :admin,
199
- with: :admin_payload
200
+ streams_via AdminChannel, to: :admin,
201
+ with: :admin_payload
200
202
 
201
203
  def admin_payload
202
204
  attributes.slice("id", "body", "flagged")
203
205
  end
204
206
 
205
207
  # Or a Proc:
206
- streams_to_dexie via: PublicChannel,
207
- with: -> { { id: id, summary: body.truncate(100) } }
208
+ streams_via PublicChannel,
209
+ with: -> { { id: id, summary: body.truncate(100) } }
208
210
  end
209
211
  ```
210
212
 
@@ -9,22 +9,21 @@ module DexieCable
9
9
  # DexieCable channel.
10
10
  #
11
11
  # class Message < ApplicationRecord
12
- # streams_to_dexie via: UserChannel, to: :sender
13
- # streams_to_dexie via: UserChannel, to: "global_feed"
14
- # streams_to_dexie via: UserChannel, to: -> { conversation.users }
15
- # streams_to_dexie via: PublicChannel
12
+ # streams_via UserChannel, to: :sender
13
+ # streams_via UserChannel, to: "global_feed"
14
+ # streams_via UserChannel, to: -> { conversation.users }
15
+ # streams_via PublicChannel
16
16
  # end
17
17
  #
18
- # @param via [Class] A DexieCable channel class. When +to+
19
- # is given, each recipient is mapped through
20
- # +via[to]+ to produce scoped channels.
21
- # Without +to+, +via+ is used directly as an
22
- # unscoped channel.
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.
23
22
  # @param to [Proc, Symbol, String] A Proc evaluated in record
24
23
  # context, a Symbol to call via +send+, or a String
25
24
  # used directly as the stream name for +broadcast_to+.
26
25
  # Must return a single recipient or collection of
27
- # recipients.
26
+ # recipients. Defaults to the record itself.
28
27
  # @param table [String, Symbol, Proc] Override the Dexie table name
29
28
  # (defaults to the model's table_name). A Proc is
30
29
  # evaluated in the record's context.
@@ -36,7 +35,7 @@ module DexieCable
36
35
  # returns truthy (evaluated in the record's context).
37
36
  # @param unless [Symbol, Proc] Skip sync if the given method or proc
38
37
  # returns truthy (evaluated in the record's context).
39
- def streams_to_dexie(via:, to: nil, table: nil, only: nil, with: nil, **options)
38
+ def streams_via(via, to: nil, table: nil, only: nil, with: nil, **options)
40
39
  events = Array(only || %i[create update destroy])
41
40
  conditions = options.slice(:if, :unless)
42
41
  serializer = with || :as_json_for_dexie
@@ -48,7 +47,7 @@ module DexieCable
48
47
  before_destroy :dexie_sync_before_destroy
49
48
 
50
49
  after_commit on: :destroy, **conditions do
51
- Array(resolve_channel(via, to)).each do |channel|
50
+ resolve_channels(via, to).each do |channel|
52
51
  next unless channel
53
52
  channel.table(resolve_table(table)).delete(dexie_destroy_id)
54
53
  end
@@ -57,19 +56,19 @@ module DexieCable
57
56
 
58
57
  if events.include?(:create)
59
58
  after_commit on: :create, **conditions do
60
- Array(resolve_channel(via, to)).each do |channel|
59
+ resolve_channels(via, to).each do |channel|
61
60
  next unless channel
62
- channel.table(resolve_table(table)).add(serialize_record(serializer))
61
+ channel.table(resolve_table(table)).add(resolve(serializer))
63
62
  end
64
63
  end
65
64
  end
66
65
 
67
66
  if events.include?(:update)
68
67
  after_commit on: :update, **conditions do
69
- Array(resolve_channel(via, to)).each do |channel|
68
+ resolve_channels(via, to).each do |channel|
70
69
  next unless channel
71
70
 
72
- changes = serialize_record(serializer).slice(*saved_changes.keys)
71
+ changes = resolve(serializer).slice(*saved_changes.keys)
73
72
  channel.table(resolve_table(table)).update(id, changes)
74
73
  end
75
74
  end
@@ -79,20 +78,16 @@ module DexieCable
79
78
 
80
79
  private
81
80
 
82
- def resolve_channel(via, to = nil)
83
- if to
84
- recipients = resolve_recipient(to)
85
- Array(recipients).map { |r| via[r] }
86
- else
87
- via
88
- end
81
+ def resolve_channels(via, to = nil)
82
+ recipients = to ? resolve(to) : self
83
+ Array(recipients).map { |r| via[r] }
89
84
  end
90
85
 
91
- def resolve_recipient(to)
92
- case to
93
- when Proc then instance_exec(&to)
94
- when Symbol then send(to)
95
- else to
86
+ def resolve(val)
87
+ case val
88
+ when Proc then instance_exec(&val)
89
+ when Symbol then send(val)
90
+ else val
96
91
  end
97
92
  end
98
93
 
@@ -104,14 +99,6 @@ module DexieCable
104
99
  end
105
100
  end
106
101
 
107
- def serialize_record(serializer)
108
- case serializer
109
- when Proc then instance_exec(&serializer)
110
- when Symbol then send(serializer)
111
- else serializer
112
- end
113
- end
114
-
115
102
  # Override in your model to customise the payload synced to Dexie.
116
103
  def as_json_for_dexie
117
104
  as_json
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DexieCable
4
- VERSION = "0.2.0"
4
+ VERSION = "1.0.0"
5
5
  end
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.2.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 (streams_to_dexie) for automatic change
42
+ in real time. Includes an ActiveRecord macro (streams_via) for automatic change
43
43
  syncing.
44
44
  email:
45
45
  - stefan@buhrmi.de