dexiecable 2.0.0.alpha1 → 2.0.0.alpha3

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: 5262c05d144bc7c002152fd8da1918e87421f4ec81a060100b71c57237c152be
4
- data.tar.gz: 339cf7b68cd9923b0b20b4a74c0caf563699be7ff8fbaf32ace71728a87fc787
3
+ metadata.gz: 2c2de6d52cf60491a659aebc8452a88ca8b8878142d08e842fadc8fb4bb1050b
4
+ data.tar.gz: 6be72c303e815a4809aff6b88a718b3e9ec1df8d5ebd341af03768225ee86bc3
5
5
  SHA512:
6
- metadata.gz: e315b48c4d222dd5c4fb04056dded3f61d9d76584dfc7ee02d1645b6d28521120130b945cda897ca6682b7d6483232a3f6b92a3220bc417dc9fb6d8718169416
7
- data.tar.gz: a5c155688bd21d6970842b86f28d2f9e4d901561fb5e786a59ed3447ae7b96db083d5b63e1d724b7fc8c5b7c2428661d713b00a5dc41a5c5b0f2a0f62ca568cc
6
+ metadata.gz: ac607ef7627a7a0562459042bcc8b23244362553624cb2f7543a7997430842c5185d5c889b8557c3ad28f10265d286f7a557a1ce76c397473adc97ead82b13d5
7
+ data.tar.gz: 1386543cc03df9409d17f184a8e1b4b80e85f993663009a32324da9b8e79561000c907c42e25c84c683bef4d4f11e525a5c1cd6dda58a0817595d53417880786
data/README.md CHANGED
@@ -73,7 +73,7 @@ setConsumer(createConsumer("wss://example.com/cable"));
73
73
 
74
74
  ### DexieChannel
75
75
 
76
- DexieCable ships one channel — `DexieChannel` — so you never define your own channels or include anything. Every Dexie broadcast goes through it.
76
+ DexieCable ships one channel — `DexieChannel` — so you don't need to define channels yourself. Every Dexie broadcast goes through it, and you can reopen it to add custom actions or push initial data.
77
77
 
78
78
  Stream tokens are signed with the application secret, so a client can only subscribe to streams the server has issued for it:
79
79
 
@@ -85,7 +85,7 @@ For an ActiveRecord model it signs the record's GlobalID:
85
85
 
86
86
  ```ruby
87
87
  DexieChannel.stream_token_for(current_user)
88
- # => "eyJkYXRhIjoiZGV4aWVfY2FibGU6ZGV4aWVfY2hhbm5lbDpnaWQ6Ly9hcHAvVXNlci8xIn0=--signature"
88
+ # => "signed token for gid://app/User/1"
89
89
  ```
90
90
 
91
91
  Send that token to the client (render it in a view, return it from an endpoint, etc.) and add it to the subscription:
@@ -107,6 +107,44 @@ subscription.removeStream(userStream);
107
107
  subscription.removeAllStreams();
108
108
  ```
109
109
 
110
+ #### Extending DexieChannel
111
+
112
+ To add custom actions or push initial data, reopen `DexieChannel` in your app:
113
+
114
+ ```ruby
115
+ # config/initializers/dexiecable.rb
116
+ class DexieCable::DexieChannel
117
+ # Push a snapshot when a private stream is added.
118
+ def subscribed_to(record, params)
119
+ case record
120
+ when User
121
+ table("notifications").bulkAdd(record.notifications.map(&:as_json_for_dexie))
122
+ when Conversation
123
+ table("messages").bulkAdd(record.messages.where("seq_id > ?", params[:last_seq_id]).map(&:as_json_for_dexie))
124
+ end
125
+ end
126
+
127
+ # Any public method is a custom action the client can perform.
128
+ def mark_as_read(data)
129
+ Message.find(data["id"]).update!(read: true)
130
+ end
131
+ end
132
+ ```
133
+
134
+ The client still subscribes to `DexieCable::DexieChannel`, so no extra client config is needed:
135
+
136
+ ```js
137
+ const subscription = subscribe(db);
138
+ ```
139
+
140
+ Pass params from the client when adding a stream:
141
+
142
+ ```js
143
+ subscription.addStream(userStream, { last_seq_id: 100 });
144
+ ```
145
+
146
+ `subscribed_to` runs after the stream is opened, and `table(...)` transmits to just this subscriber — so the snapshot arrives before any live mutation. Custom actions are triggered like any ActionCable action: `subscription.perform("mark_as_read", { id: 42 })`.
147
+
110
148
  #### Public streams
111
149
 
112
150
  For data that's public (a global feed, announcements, etc.), skip the signature. Use a string target — it's namespaced under `public:` automatically:
@@ -13,6 +13,22 @@ module DexieCable
13
13
  # # public (no signature, namespaced under "public:")
14
14
  # subscription.addPublicStream("feed")
15
15
  #
16
+ # Reopen this channel in your app to push initial data or add custom
17
+ # actions:
18
+ #
19
+ # class DexieCable::DexieChannel
20
+ # def subscribed_to(record, params)
21
+ # case record
22
+ # when User
23
+ # table("notifications").bulkAdd(record.notifications.map(&:as_json_for_dexie))
24
+ # end
25
+ # end
26
+ #
27
+ # def my_custom_action(data)
28
+ # # ...
29
+ # end
30
+ # end
31
+ #
16
32
  class DexieChannel < ActionCable::Channel::Base
17
33
  PUBLIC_STREAM_PREFIX = "public:"
18
34
 
@@ -40,11 +56,11 @@ module DexieCable
40
56
  Query.new(self, name)
41
57
  end
42
58
 
43
- # Returns a signed token for the stream identifier of +target+ (private
44
- # record-based streams). Send it to the client, which passes it to
59
+ # Returns a signed token for +target+ (a record or other private
60
+ # target). Send it to the client, which passes it to
45
61
  # +subscription.addStream+.
46
62
  def self.stream_token_for(target)
47
- verifier.generate(broadcasting_for(target))
63
+ verifier.generate(target.respond_to?(:to_gid_param) ? target.to_gid_param : target.to_s)
48
64
  end
49
65
 
50
66
  def self.verifier
@@ -56,13 +72,19 @@ module DexieCable
56
72
  end
57
73
 
58
74
  def add_stream(data)
59
- stream = verified_stream(data["stream"])
60
- stream_from stream if stream
75
+ target = verified_target(data["stream"])
76
+ return unless target
77
+
78
+ stream_for target
79
+ params = data.except("action", "stream").with_indifferent_access
80
+ subscribed_to(resolve_subscribe_target(target), params)
61
81
  end
62
82
 
63
83
  def remove_stream(data)
64
- stream = verified_stream(data["stream"])
65
- stop_stream_from stream if stream
84
+ target = verified_target(data["stream"])
85
+ return unless target
86
+
87
+ stop_stream_from self.class.broadcasting_for(target)
66
88
  end
67
89
 
68
90
  def remove_all_streams(_data)
@@ -79,18 +101,28 @@ module DexieCable
79
101
  stop_stream_from public_stream_name(name) if name.present?
80
102
  end
81
103
 
104
+ # Override by reopening the channel to push initial data when a private
105
+ # stream is added. +record+ is the resolved target and +params+ are any
106
+ # extra params sent from the client.
107
+ def subscribed_to(_record, _params)
108
+ end
109
+
82
110
  private
83
111
 
84
112
  def public_stream_name(name)
85
113
  self.class.broadcasting_for("#{PUBLIC_STREAM_PREFIX}#{name}")
86
114
  end
87
115
 
88
- def verified_stream(token)
116
+ def verified_target(token)
89
117
  return unless token.present?
90
118
 
91
119
  self.class.verifier.verify(token)
92
120
  rescue ActiveSupport::MessageVerifier::InvalidSignature
93
121
  nil
94
122
  end
123
+
124
+ def resolve_subscribe_target(target)
125
+ target.start_with?("gid://") ? GlobalID::Locator.locate(target) : target
126
+ end
95
127
  end
96
128
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DexieCable
4
- VERSION = "2.0.0.alpha1"
4
+ VERSION = "2.0.0.alpha3"
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: 2.0.0.alpha1
4
+ version: 2.0.0.alpha3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Buhrmester