cable_ready 5.0.0.pre10 → 5.0.0.rc1
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/Gemfile.lock +2 -2
- data/README.md +12 -12
- data/app/assets/javascripts/cable_ready.js +15 -11
- data/app/assets/javascripts/cable_ready.umd.js +15 -11
- data/app/jobs/{cable_ready_broadcast_job.rb → cable_ready/broadcast_job.rb} +1 -1
- data/app/models/concerns/cable_ready/updatable/collections_registry.rb +29 -5
- data/app/models/concerns/cable_ready/updatable/model_updatable_callbacks.rb +6 -4
- data/app/models/concerns/cable_ready/updatable.rb +49 -29
- data/lib/cable_ready/channel.rb +2 -2
- data/lib/cable_ready/config.rb +2 -1
- data/lib/cable_ready/engine.rb +20 -11
- data/lib/cable_ready/importmap.rb +1 -1
- data/lib/cable_ready/updatable/memory_cache_debounce_adapter.rb +22 -0
- data/lib/cable_ready/version.rb +1 -1
- data/lib/cable_ready.rb +2 -0
- data/lib/generators/cable_ready/templates/config/initializers/cable_ready.rb +5 -0
- data/lib/install/importmap.rb +1 -1
- data/lib/install/shakapacker.rb +4 -0
- data/lib/install/vite.rb +4 -0
- data/lib/install/webpacker.rb +12 -0
- data/lib/tasks/cable_ready/cable_ready.rake +6 -8
- data/package.json +13 -9
- data/rollup.config.mjs +0 -19
- data/yarn.lock +976 -122
- metadata +4 -8
- data/IMPLEMENTATION.md +0 -93
- data/app/assets/javascripts/cable_ready.min.js +0 -2
- data/app/assets/javascripts/cable_ready.min.js.map +0 -1
- data/app/assets/javascripts/cable_ready.umd.min.js +0 -2
- data/app/assets/javascripts/cable_ready.umd.min.js.map +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 727abefeeee7c2b5775ce2ca9b9e4521cf8172ba99f0a3ce4d6c0e4c49a77fae
|
4
|
+
data.tar.gz: f42905f8cc53e8effade6db00fa24d569c398b7f163e9a06aa19425c1bcdd888
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99bb047390e54d2143be5cfa9c7115203b0464d39aca3eab2d28486560043b26338f1d9f18b74507aa928afb87c7e08c93ecc475666730d6f1caae0eaf1486c2
|
7
|
+
data.tar.gz: 96e773ba9e75a9603a47850cd14784a47576fdb6b98b58d40c701deabc363342be1e626d55f845fa6186e57957f9cd52d9c67c79e72837d33a0d50be015630de
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
cable_ready (5.0.0.
|
4
|
+
cable_ready (5.0.0.rc1)
|
5
5
|
actionpack (>= 5.2)
|
6
6
|
actionview (>= 5.2)
|
7
7
|
activesupport (>= 5.2)
|
@@ -121,7 +121,7 @@ GEM
|
|
121
121
|
pry-nav (1.0.0)
|
122
122
|
pry (>= 0.9.10, < 0.15)
|
123
123
|
racc (1.6.2)
|
124
|
-
rack (2.2.6.
|
124
|
+
rack (2.2.6.4)
|
125
125
|
rack-test (2.0.2)
|
126
126
|
rack (>= 1.3)
|
127
127
|
rails (6.1.7.2)
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
<p align="center">
|
2
|
-
<img src="https://raw.githubusercontent.com/stimulusreflex/cable_ready/
|
2
|
+
<img src="https://raw.githubusercontent.com/stimulusreflex/cable_ready/main/assets/cable-ready-logo-with-copy.svg" width="360" />
|
3
3
|
<h1 align="center">Welcome to CableReady 👋</h1>
|
4
4
|
<p align="center">
|
5
5
|
<a href="https://rubygems.org/gems/cable_ready">
|
@@ -11,16 +11,13 @@
|
|
11
11
|
<a href="https://www.npmjs.com/package/cable_ready">
|
12
12
|
<img alt="downloads" src="https://img.shields.io/npm/dm/cable_ready.svg?color=blue" target="_blank" />
|
13
13
|
</a>
|
14
|
-
<a href="https://github.com/stimulusreflex/cable_ready/blob/
|
14
|
+
<a href="https://github.com/stimulusreflex/cable_ready/blob/main/LICENSE">
|
15
15
|
<img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-brightgreen.svg" target="_blank" />
|
16
16
|
</a>
|
17
|
-
<a href="http://blog.codinghorror.com/the-best-code-is-no-code-at-all/" target="_blank">
|
18
|
-
<img alt="Lines of Code" src="https://img.shields.io/badge/lines_of_code-1203-brightgreen.svg?style=flat" />
|
19
|
-
</a>
|
20
|
-
<br />
|
21
17
|
<a href="https://cableready.stimulusreflex.com" target="_blank">
|
22
18
|
<img alt="Documentation" src="https://img.shields.io/badge/documentation-yes-brightgreen.svg" />
|
23
19
|
</a>
|
20
|
+
<br />
|
24
21
|
<a href="#badge">
|
25
22
|
<img alt="semantic-release" src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg">
|
26
23
|
</a>
|
@@ -31,12 +28,15 @@
|
|
31
28
|
<img alt="JavaScript Code Style" src="https://img.shields.io/badge/JavaScript_Code_Style-prettier_standard-ff69b4.svg" />
|
32
29
|
</a>
|
33
30
|
<br />
|
34
|
-
<a target="_blank" rel="noopener noreferrer" href="https://github.com/stimulusreflex/cable_ready/workflows/
|
31
|
+
<a target="_blank" rel="noopener noreferrer" href="https://github.com/stimulusreflex/cable_ready/actions/workflows/prettier-standard.yml">
|
35
32
|
<img src="https://github.com/stimulusreflex/cable_ready/workflows/Prettier-Standard/badge.svg" alt="Prettier-Standard" style="max-width:100%;">
|
36
33
|
</a>
|
37
|
-
<a target="_blank" rel="noopener noreferrer" href="https://github.com/stimulusreflex/cable_ready/workflows/
|
34
|
+
<a target="_blank" rel="noopener noreferrer" href="https://github.com/stimulusreflex/cable_ready/actions/workflows/standardrb.yml">
|
38
35
|
<img src="https://github.com/stimulusreflex/cable_ready/workflows/StandardRB/badge.svg" alt="StandardRB" style="max-width:100%;">
|
39
36
|
</a>
|
37
|
+
<a target="_blank" rel="noopener noreferrer" href="https://github.com/stimulusreflex/cable_ready/actions/workflows/tests.yml">
|
38
|
+
<img src="https://github.com/stimulusreflex/cable_ready/workflows/Tests/badge.svg" alt="Tests" style="max-width:100%;">
|
39
|
+
</a>
|
40
40
|
</p>
|
41
41
|
</p>
|
42
42
|
<br />
|
@@ -49,7 +49,7 @@ to learn more about ActionCable before proceeding.
|
|
49
49
|
## 📚 Docs
|
50
50
|
|
51
51
|
- [Official Documentation](https://cableready.stimulusreflex.com)
|
52
|
-
- [Documentation Source Code](https://github.com/stimulusreflex/cable_ready/tree/
|
52
|
+
- [Documentation Source Code](https://github.com/stimulusreflex/cable_ready/tree/main/docs)
|
53
53
|
|
54
54
|
## 💙 Community
|
55
55
|
|
@@ -80,7 +80,7 @@ yarn add cable_ready
|
|
80
80
|
|
81
81
|
# ...
|
82
82
|
|
83
|
-
pin 'cable_ready', to: 'cable_ready.
|
83
|
+
pin 'cable_ready', to: 'cable_ready.js', preload: true
|
84
84
|
```
|
85
85
|
|
86
86
|
#### Rails Asset pipeline (Sprockets):
|
@@ -88,7 +88,7 @@ pin 'cable_ready', to: 'cable_ready.min.js', preload: true
|
|
88
88
|
```html+erb
|
89
89
|
<!-- app/views/layouts/application.html.erb -->
|
90
90
|
|
91
|
-
<%= javascript_include_tag "cable_ready.umd.
|
91
|
+
<%= javascript_include_tag "cable_ready.umd.js", "data-turbo-track": "reload" %>
|
92
92
|
```
|
93
93
|
|
94
94
|
Checkout the [documentation](https://cableready.stimulusreflex.com) to continue!
|
@@ -111,7 +111,7 @@ Please run `./bin/standardize` prior submitting pull requests.
|
|
111
111
|
1. Make sure that you run `yarn` and `bundle` to pick up the latest.
|
112
112
|
1. Bump version number at `lib/cable_ready/version.rb`. Pre-release versions use `.preN`
|
113
113
|
1. Run `rake build` and `yarn build`
|
114
|
-
1. Commit and push changes to
|
114
|
+
1. Commit and push changes to GitHub
|
115
115
|
1. Run `rake release`
|
116
116
|
1. Run `yarn publish --no-git-tag-version`
|
117
117
|
1. Yarn will prompt you for the new version. Pre-release versions use `-preN`
|
@@ -2,7 +2,7 @@ import morphdom from "morphdom";
|
|
2
2
|
|
3
3
|
var name = "cable_ready";
|
4
4
|
|
5
|
-
var version = "5.0.0-
|
5
|
+
var version = "5.0.0-rc1";
|
6
6
|
|
7
7
|
var description = "CableReady helps you create great real-time user experiences by making it simple to trigger client-side DOM changes from server-side Ruby.";
|
8
8
|
|
@@ -33,13 +33,14 @@ var umd = "./dist/cable_ready.umd.js";
|
|
33
33
|
var files = [ "dist/*", "javascript/*" ];
|
34
34
|
|
35
35
|
var scripts = {
|
36
|
-
lint: "yarn run
|
37
|
-
format: "yarn run prettier-standard
|
38
|
-
"prettier-standard:check": "yarn run prettier-standard --check ./javascript/**/*.js rollup.config.js",
|
39
|
-
"prettier-standard:format": "yarn run prettier-standard ./javascript/**/*.js rollup.config.js",
|
36
|
+
lint: "yarn run format --check",
|
37
|
+
format: "yarn run prettier-standard ./javascript/**/*.js rollup.config.mjs",
|
40
38
|
build: "yarn rollup -c",
|
41
39
|
watch: "yarn rollup -wc",
|
42
|
-
test: "web-test-runner javascript/test/**/*.test.js"
|
40
|
+
test: "web-test-runner javascript/test/**/*.test.js",
|
41
|
+
"docs:dev": "vitepress dev docs",
|
42
|
+
"docs:build": "vitepress build docs && cp ./docs/_redirects ./docs/.vitepress/dist",
|
43
|
+
"docs:preview": "vitepress preview docs"
|
43
44
|
};
|
44
45
|
|
45
46
|
var dependencies = {
|
@@ -53,10 +54,13 @@ var devDependencies = {
|
|
53
54
|
"@rollup/plugin-terser": "^0.4.0",
|
54
55
|
"@web/dev-server-esbuild": "^0.3.3",
|
55
56
|
"@web/dev-server-rollup": "^0.3.21",
|
56
|
-
"@web/test-runner": "^0.15.
|
57
|
+
"@web/test-runner": "^0.15.1",
|
57
58
|
"prettier-standard": "^16.4.1",
|
58
|
-
rollup: "^3.
|
59
|
-
sinon: "^15.0.
|
59
|
+
rollup: "^3.19.1",
|
60
|
+
sinon: "^15.0.2",
|
61
|
+
vite: "^4.1.4",
|
62
|
+
vitepress: "^1.0.0-alpha.56",
|
63
|
+
"vitepress-plugin-search": "^1.0.4-alpha.19"
|
60
64
|
};
|
61
65
|
|
62
66
|
var packageInfo = {
|
@@ -1057,7 +1061,7 @@ class UpdatesForElement extends SubscribingElement {
|
|
1057
1061
|
Log.cancel(this.lastUpdateTimestamp, "All elements filtered out");
|
1058
1062
|
return;
|
1059
1063
|
}
|
1060
|
-
// first updates-for element in the DOM *at any given moment* updates all of the others
|
1064
|
+
// first <cable-ready-updates-for> element in the DOM *at any given moment* updates all of the others
|
1061
1065
|
if (blocks[0].element !== this) {
|
1062
1066
|
Log.cancel(this.lastUpdateTimestamp, "Update already requested");
|
1063
1067
|
return;
|
@@ -1142,7 +1146,7 @@ class Block {
|
|
1142
1146
|
const selector = `turbo-frame#${frame.id}`;
|
1143
1147
|
const frameContent = frameTemplate.content.querySelector(selector);
|
1144
1148
|
const content = frameContent ? frameContent.innerHTML.trim() : "";
|
1145
|
-
|
1149
|
+
documentFragment.querySelector(selector).innerHTML = content;
|
1146
1150
|
resolve();
|
1147
1151
|
})))));
|
1148
1152
|
}
|
@@ -4,7 +4,7 @@
|
|
4
4
|
})(this, (function(exports, morphdom) {
|
5
5
|
"use strict";
|
6
6
|
var name = "cable_ready";
|
7
|
-
var version = "5.0.0-
|
7
|
+
var version = "5.0.0-rc1";
|
8
8
|
var description = "CableReady helps you create great real-time user experiences by making it simple to trigger client-side DOM changes from server-side Ruby.";
|
9
9
|
var keywords = [ "ruby", "rails", "websockets", "actioncable", "cable", "ssr", "stimulus_reflex", "client-side", "dom" ];
|
10
10
|
var homepage = "https://cableready.stimulusreflex.com";
|
@@ -20,13 +20,14 @@
|
|
20
20
|
var umd = "./dist/cable_ready.umd.js";
|
21
21
|
var files = [ "dist/*", "javascript/*" ];
|
22
22
|
var scripts = {
|
23
|
-
lint: "yarn run
|
24
|
-
format: "yarn run prettier-standard
|
25
|
-
"prettier-standard:check": "yarn run prettier-standard --check ./javascript/**/*.js rollup.config.js",
|
26
|
-
"prettier-standard:format": "yarn run prettier-standard ./javascript/**/*.js rollup.config.js",
|
23
|
+
lint: "yarn run format --check",
|
24
|
+
format: "yarn run prettier-standard ./javascript/**/*.js rollup.config.mjs",
|
27
25
|
build: "yarn rollup -c",
|
28
26
|
watch: "yarn rollup -wc",
|
29
|
-
test: "web-test-runner javascript/test/**/*.test.js"
|
27
|
+
test: "web-test-runner javascript/test/**/*.test.js",
|
28
|
+
"docs:dev": "vitepress dev docs",
|
29
|
+
"docs:build": "vitepress build docs && cp ./docs/_redirects ./docs/.vitepress/dist",
|
30
|
+
"docs:preview": "vitepress preview docs"
|
30
31
|
};
|
31
32
|
var dependencies = {
|
32
33
|
morphdom: "2.6.1"
|
@@ -38,10 +39,13 @@
|
|
38
39
|
"@rollup/plugin-terser": "^0.4.0",
|
39
40
|
"@web/dev-server-esbuild": "^0.3.3",
|
40
41
|
"@web/dev-server-rollup": "^0.3.21",
|
41
|
-
"@web/test-runner": "^0.15.
|
42
|
+
"@web/test-runner": "^0.15.1",
|
42
43
|
"prettier-standard": "^16.4.1",
|
43
|
-
rollup: "^3.
|
44
|
-
sinon: "^15.0.
|
44
|
+
rollup: "^3.19.1",
|
45
|
+
sinon: "^15.0.2",
|
46
|
+
vite: "^4.1.4",
|
47
|
+
vitepress: "^1.0.0-alpha.56",
|
48
|
+
"vitepress-plugin-search": "^1.0.4-alpha.19"
|
45
49
|
};
|
46
50
|
var packageInfo = {
|
47
51
|
name: name,
|
@@ -978,7 +982,7 @@
|
|
978
982
|
Log.cancel(this.lastUpdateTimestamp, "All elements filtered out");
|
979
983
|
return;
|
980
984
|
}
|
981
|
-
// first updates-for element in the DOM *at any given moment* updates all of the others
|
985
|
+
// first <cable-ready-updates-for> element in the DOM *at any given moment* updates all of the others
|
982
986
|
if (blocks[0].element !== this) {
|
983
987
|
Log.cancel(this.lastUpdateTimestamp, "Update already requested");
|
984
988
|
return;
|
@@ -1062,7 +1066,7 @@
|
|
1062
1066
|
const selector = `turbo-frame#${frame.id}`;
|
1063
1067
|
const frameContent = frameTemplate.content.querySelector(selector);
|
1064
1068
|
const content = frameContent ? frameContent.innerHTML.trim() : "";
|
1065
|
-
|
1069
|
+
documentFragment.querySelector(selector).innerHTML = content;
|
1066
1070
|
resolve();
|
1067
1071
|
})))));
|
1068
1072
|
}
|
@@ -2,6 +2,18 @@
|
|
2
2
|
|
3
3
|
module CableReady
|
4
4
|
module Updatable
|
5
|
+
Collection = Struct.new(
|
6
|
+
:klass,
|
7
|
+
:name,
|
8
|
+
:reflection,
|
9
|
+
:options,
|
10
|
+
:foreign_key,
|
11
|
+
:inverse_association,
|
12
|
+
:through_association,
|
13
|
+
:debounce_time,
|
14
|
+
keyword_init: true
|
15
|
+
)
|
16
|
+
|
5
17
|
class CollectionsRegistry
|
6
18
|
def initialize
|
7
19
|
@registered_collections = []
|
@@ -12,23 +24,35 @@ module CableReady
|
|
12
24
|
end
|
13
25
|
|
14
26
|
def broadcast_for!(model, operation)
|
15
|
-
@registered_collections.select { |c| c
|
27
|
+
@registered_collections.select { |c| c.options[:on].include?(operation) }
|
16
28
|
.each do |collection|
|
17
29
|
resource = find_resource_for_update(collection, model)
|
18
30
|
next if resource.nil?
|
19
31
|
|
20
|
-
collection
|
32
|
+
collection.klass.cable_ready_update_collection(resource, collection.name, model, debounce: collection.debounce_time) if collection.options[:if].call(resource)
|
21
33
|
end
|
22
34
|
end
|
23
35
|
|
24
36
|
private
|
25
37
|
|
26
38
|
def find_resource_for_update(collection, model)
|
27
|
-
|
39
|
+
collection.reflection ||= collection.klass.reflect_on_association(collection.name)
|
40
|
+
|
41
|
+
collection.foreign_key ||= collection.reflection&.foreign_key
|
42
|
+
|
43
|
+
# lazy load and store through and inverse associations
|
44
|
+
if collection.reflection&.through_reflection?
|
45
|
+
collection.inverse_association ||= collection.reflection&.through_reflection&.inverse_of&.name&.to_s
|
46
|
+
collection.through_association = collection.reflection&.through_reflection&.name&.to_s&.singularize
|
47
|
+
else
|
48
|
+
collection.inverse_association ||= collection.reflection&.inverse_of&.name&.to_s
|
49
|
+
end
|
50
|
+
|
51
|
+
raise ArgumentError, "Could not find inverse_of for #{collection.name}" unless collection.inverse_association
|
28
52
|
|
29
53
|
resource = model
|
30
|
-
resource = resource.send(collection
|
31
|
-
resource.send(collection
|
54
|
+
resource = resource.send(collection.through_association.underscore) if collection.through_association
|
55
|
+
resource.send(collection.inverse_association.underscore)
|
32
56
|
end
|
33
57
|
end
|
34
58
|
end
|
@@ -3,9 +3,10 @@
|
|
3
3
|
module CableReady
|
4
4
|
module Updatable
|
5
5
|
class ModelUpdatableCallbacks
|
6
|
-
def initialize(operation, enabled_operations = %i[create update destroy])
|
6
|
+
def initialize(operation, enabled_operations = %i[create update destroy], debounce: CableReady.config.updatable_debounce_time)
|
7
7
|
@operation = operation
|
8
8
|
@enabled_operations = enabled_operations
|
9
|
+
@debounce = debounce
|
9
10
|
end
|
10
11
|
|
11
12
|
def after_commit(model)
|
@@ -17,14 +18,15 @@ module CableReady
|
|
17
18
|
private
|
18
19
|
|
19
20
|
def broadcast_create(model)
|
20
|
-
model.class.send(:broadcast_updates, model.class, {})
|
21
|
+
model.class.send(:broadcast_updates, model.class, {debounce: @debounce})
|
21
22
|
end
|
22
23
|
alias_method :broadcast_destroy, :broadcast_create
|
23
24
|
|
24
25
|
def broadcast_update(model)
|
25
26
|
changeset = model.respond_to?(:previous_changes) ? {changed: model.previous_changes.keys} : {}
|
26
|
-
|
27
|
-
model.class.send(:broadcast_updates, model.
|
27
|
+
options = changeset.merge({debounce: @debounce})
|
28
|
+
model.class.send(:broadcast_updates, model.class, options.dup)
|
29
|
+
model.class.send(:broadcast_updates, model.to_global_id, options)
|
28
30
|
end
|
29
31
|
end
|
30
32
|
end
|
@@ -6,6 +6,8 @@ module CableReady
|
|
6
6
|
module Updatable
|
7
7
|
extend ::ActiveSupport::Concern
|
8
8
|
|
9
|
+
mattr_accessor :debounce_adapter, default: MemoryCacheDebounceAdapter.instance
|
10
|
+
|
9
11
|
included do |base|
|
10
12
|
if defined?(ActiveRecord) && base < ActiveRecord::Base
|
11
13
|
include ExtendHasMany
|
@@ -30,14 +32,15 @@ module CableReady
|
|
30
32
|
options = options.extract_options!
|
31
33
|
options = {
|
32
34
|
on: [:create, :update, :destroy],
|
33
|
-
if: -> { true }
|
35
|
+
if: -> { true },
|
36
|
+
debounce: CableReady.config.updatable_debounce_time
|
34
37
|
}.merge(options)
|
35
38
|
|
36
39
|
enabled_operations = Array(options[:on])
|
37
40
|
|
38
|
-
after_commit(ModelUpdatableCallbacks.new(:create, enabled_operations), {on: :create, if: options[:if]})
|
39
|
-
after_commit(ModelUpdatableCallbacks.new(:update, enabled_operations), {on: :update, if: options[:if]})
|
40
|
-
after_commit(ModelUpdatableCallbacks.new(:destroy, enabled_operations), {on: :destroy, if: options[:if]})
|
41
|
+
after_commit(ModelUpdatableCallbacks.new(:create, enabled_operations, debounce: options[:debounce]), {on: :create, if: options[:if]})
|
42
|
+
after_commit(ModelUpdatableCallbacks.new(:update, enabled_operations, debounce: options[:debounce]), {on: :update, if: options[:if]})
|
43
|
+
after_commit(ModelUpdatableCallbacks.new(:destroy, enabled_operations, debounce: options[:debounce]), {on: :destroy, if: options[:if]})
|
41
44
|
end
|
42
45
|
|
43
46
|
def self.skip_cable_ready_updates
|
@@ -52,6 +55,8 @@ module CableReady
|
|
52
55
|
private
|
53
56
|
|
54
57
|
module ClassMethods
|
58
|
+
include Compoundable
|
59
|
+
|
55
60
|
def has_many(name, scope = nil, **options, &extension)
|
56
61
|
option = if options.has_key?(:enable_updates)
|
57
62
|
warn "DEPRECATED: please use `enable_cable_ready_updates` instead. The `enable_updates` option will be removed from a future version of CableReady 5"
|
@@ -61,10 +66,11 @@ module CableReady
|
|
61
66
|
end
|
62
67
|
|
63
68
|
descendants = options.delete(:descendants)
|
69
|
+
debounce_time = options.delete(:debounce)
|
64
70
|
|
65
71
|
broadcast = option.present?
|
66
72
|
result = super
|
67
|
-
enrich_association_with_updates(name, option, descendants) if broadcast
|
73
|
+
enrich_association_with_updates(name, option, descendants, debounce: debounce_time) if broadcast
|
68
74
|
result
|
69
75
|
end
|
70
76
|
|
@@ -77,10 +83,11 @@ module CableReady
|
|
77
83
|
end
|
78
84
|
|
79
85
|
descendants = options.delete(:descendants)
|
86
|
+
debounce_time = options.delete(:debounce)
|
80
87
|
|
81
88
|
broadcast = option.present?
|
82
89
|
result = super
|
83
|
-
enrich_association_with_updates(name, option, descendants) if broadcast
|
90
|
+
enrich_association_with_updates(name, option, descendants, debounce: debounce_time) if broadcast
|
84
91
|
result
|
85
92
|
end
|
86
93
|
|
@@ -94,9 +101,11 @@ module CableReady
|
|
94
101
|
options.delete(:enable_cable_ready_updates)
|
95
102
|
end
|
96
103
|
|
104
|
+
debounce_time = options.delete(:debounce)
|
105
|
+
|
97
106
|
broadcast = option.present?
|
98
107
|
result = super
|
99
|
-
enrich_attachments_with_updates(name, option) if broadcast
|
108
|
+
enrich_attachments_with_updates(name, option, debounce: debounce_time) if broadcast
|
100
109
|
result
|
101
110
|
end
|
102
111
|
|
@@ -104,50 +113,45 @@ module CableReady
|
|
104
113
|
@cable_ready_collections ||= CollectionsRegistry.new
|
105
114
|
end
|
106
115
|
|
107
|
-
def cable_ready_update_collection(resource, name, model)
|
116
|
+
def cable_ready_update_collection(resource, name, model, debounce: CableReady.config.updatable_debounce_time)
|
108
117
|
identifier = resource.to_global_id.to_s + ":" + name.to_s
|
109
|
-
|
118
|
+
changeset = model.respond_to?(:previous_changes) ? {changed: model.previous_changes.keys} : {}
|
119
|
+
options = changeset.merge({debounce: debounce})
|
120
|
+
|
121
|
+
broadcast_updates(identifier, options)
|
110
122
|
end
|
111
123
|
|
112
|
-
def enrich_association_with_updates(name, option, descendants = nil)
|
124
|
+
def enrich_association_with_updates(name, option, descendants = nil, debounce: CableReady.config.updatable_debounce_time)
|
113
125
|
reflection = reflect_on_association(name)
|
114
126
|
|
115
|
-
inverse_of = reflection.inverse_of&.name&.to_s
|
116
|
-
through_association = nil
|
117
|
-
|
118
|
-
if reflection.through_reflection?
|
119
|
-
inverse_of = reflection.through_reflection.inverse_of&.name&.to_s
|
120
|
-
through_association = reflection.through_reflection.name.to_s.singularize
|
121
|
-
end
|
122
|
-
|
123
127
|
options = build_options(option)
|
124
128
|
|
125
129
|
[reflection.klass, *descendants&.map(&:to_s)&.map(&:constantize)].each do |klass|
|
126
130
|
klass.send(:include, CableReady::Updatable) unless klass.respond_to?(:cable_ready_collections)
|
127
|
-
klass.cable_ready_collections.register(
|
131
|
+
klass.cable_ready_collections.register(Collection.new(
|
128
132
|
klass: self,
|
129
|
-
foreign_key: reflection.foreign_key,
|
130
133
|
name: name,
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
134
|
+
options: options,
|
135
|
+
reflection: reflection,
|
136
|
+
debounce_time: debounce
|
137
|
+
))
|
135
138
|
end
|
136
139
|
end
|
137
140
|
|
138
|
-
def enrich_attachments_with_updates(name, option)
|
141
|
+
def enrich_attachments_with_updates(name, option, debounce: CableReady.config.updatable_debounce_time)
|
139
142
|
options = build_options(option)
|
140
143
|
|
141
144
|
ActiveStorage::Attachment.send(:include, CableReady::Updatable) unless ActiveStorage::Attachment.respond_to?(:cable_ready_collections)
|
142
145
|
|
143
|
-
ActiveStorage::Attachment.cable_ready_collections.register(
|
146
|
+
ActiveStorage::Attachment.cable_ready_collections.register(Collection.new(
|
144
147
|
klass: self,
|
145
148
|
foreign_key: "record_id",
|
146
149
|
name: name,
|
147
150
|
inverse_association: "record",
|
148
151
|
through_association: nil,
|
149
|
-
options: options
|
150
|
-
|
152
|
+
options: options,
|
153
|
+
debounce_time: debounce
|
154
|
+
))
|
151
155
|
end
|
152
156
|
|
153
157
|
def build_options(option)
|
@@ -180,7 +184,23 @@ module CableReady
|
|
180
184
|
def broadcast_updates(model_class, options)
|
181
185
|
return if skip_updates_classes.any? { |klass| klass >= self }
|
182
186
|
raise("ActionCable must be enabled to use Updatable") unless defined?(ActionCable)
|
183
|
-
|
187
|
+
|
188
|
+
debounce_time = options.delete(:debounce)
|
189
|
+
debounce_time ||= CableReady.config.updatable_debounce_time
|
190
|
+
|
191
|
+
if debounce_time.to_f > 0
|
192
|
+
key = compound([model_class, *options])
|
193
|
+
old_wait_until = CableReady::Updatable.debounce_adapter[key]
|
194
|
+
now = Time.now.to_f
|
195
|
+
|
196
|
+
if old_wait_until.nil? || old_wait_until < now
|
197
|
+
new_wait_until = now + debounce_time.to_f
|
198
|
+
CableReady::Updatable.debounce_adapter[key] = new_wait_until
|
199
|
+
ActionCable.server.broadcast(model_class, options)
|
200
|
+
end
|
201
|
+
else
|
202
|
+
ActionCable.server.broadcast(model_class, options)
|
203
|
+
end
|
184
204
|
end
|
185
205
|
|
186
206
|
def skip_updates_classes
|
data/lib/cable_ready/channel.rb
CHANGED
@@ -28,7 +28,7 @@ module CableReady
|
|
28
28
|
|
29
29
|
def broadcast_later(clear: true, queue: nil)
|
30
30
|
raise("Action Cable must be enabled to use broadcast_later") unless defined?(ActionCable)
|
31
|
-
|
31
|
+
CableReady::BroadcastJob
|
32
32
|
.set(queue: queue ? queue.to_sym : CableReady.config.broadcast_job_queue)
|
33
33
|
.perform_later(identifier: identifier, operations: operations_payload)
|
34
34
|
reset! if clear
|
@@ -36,7 +36,7 @@ module CableReady
|
|
36
36
|
|
37
37
|
def broadcast_later_to(model, clear: true, queue: nil)
|
38
38
|
raise("Action Cable must be enabled to use broadcast_later_to") unless defined?(ActionCable)
|
39
|
-
|
39
|
+
CableReady::BroadcastJob
|
40
40
|
.set(queue: queue ? queue.to_sym : CableReady.config.broadcast_job_queue)
|
41
41
|
.perform_later(identifier: identifier.name, operations: operations_payload, model: model)
|
42
42
|
reset! if clear
|
data/lib/cable_ready/config.rb
CHANGED
@@ -11,7 +11,7 @@ module CableReady
|
|
11
11
|
include Observable
|
12
12
|
include Singleton
|
13
13
|
|
14
|
-
attr_accessor :on_failed_sanity_checks, :broadcast_job_queue, :precompile_assets
|
14
|
+
attr_accessor :on_failed_sanity_checks, :broadcast_job_queue, :precompile_assets, :updatable_debounce_time
|
15
15
|
attr_writer :verifier_key
|
16
16
|
|
17
17
|
def on_new_version_available
|
@@ -28,6 +28,7 @@ module CableReady
|
|
28
28
|
@on_failed_sanity_checks = :exit
|
29
29
|
@broadcast_job_queue = :default
|
30
30
|
@precompile_assets = true
|
31
|
+
@updatable_debounce_time = 0.1.seconds
|
31
32
|
end
|
32
33
|
|
33
34
|
def observers
|
data/lib/cable_ready/engine.rb
CHANGED
@@ -12,31 +12,40 @@ module CableReady
|
|
12
12
|
# end
|
13
13
|
PRECOMPILE_ASSETS = %w[
|
14
14
|
cable_ready.js
|
15
|
-
cable_ready.min.js
|
16
|
-
cable_ready.min.js.map
|
17
15
|
cable_ready.umd.js
|
18
|
-
cable_ready.umd.min.js
|
19
|
-
cable_ready.umd.min.js.map
|
20
16
|
]
|
21
17
|
|
18
|
+
initializer "cable_ready.assets" do |app|
|
19
|
+
if app.config.respond_to?(:assets) && CableReady.config.precompile_assets
|
20
|
+
app.config.assets.precompile += PRECOMPILE_ASSETS
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
22
24
|
initializer "cable_ready.sanity_check" do
|
23
25
|
SanityChecker.check! unless Rails.env.production?
|
24
26
|
end
|
25
27
|
|
28
|
+
initializer "cable_ready.mimetype" do
|
29
|
+
Mime::Type.register "text/vnd.cable-ready.json", :cable_ready
|
30
|
+
end
|
31
|
+
|
26
32
|
initializer "cable_ready.renderer" do
|
27
33
|
ActiveSupport.on_load(:action_controller) do
|
28
34
|
ActionController::Renderers.add :operations do |operations, options|
|
35
|
+
warn "DEPRECATED: CableReady's `render operations:` call has been renamed to `render cable_ready:`. Please update your render call."
|
36
|
+
|
29
37
|
response.content_type ||= Mime[:cable_ready]
|
30
|
-
|
38
|
+
response.headers["X-Cable-Ready-Version"] = CableReady::VERSION
|
39
|
+
|
40
|
+
render json: operations.respond_to?(:dispatch) ? operations.dispatch : operations
|
31
41
|
end
|
32
42
|
|
33
|
-
|
34
|
-
|
35
|
-
|
43
|
+
ActionController::Renderers.add :cable_ready do |operations, options|
|
44
|
+
response.content_type ||= Mime[:cable_ready]
|
45
|
+
response.headers["X-Cable-Ready-Version"] = CableReady::VERSION
|
36
46
|
|
37
|
-
|
38
|
-
|
39
|
-
app.config.assets.precompile += PRECOMPILE_ASSETS
|
47
|
+
render json: operations.respond_to?(:dispatch) ? operations.dispatch : operations
|
48
|
+
end
|
40
49
|
end
|
41
50
|
end
|
42
51
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module CableReady
|
2
|
+
module Updatable
|
3
|
+
class MemoryCacheDebounceAdapter
|
4
|
+
include Singleton
|
5
|
+
|
6
|
+
delegate_missing_to :@store
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
super
|
10
|
+
@store = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes, size: 8.megabytes)
|
11
|
+
end
|
12
|
+
|
13
|
+
def []=(key, value)
|
14
|
+
@store.write(key, value)
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](key)
|
18
|
+
@store.read(key)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/cable_ready/version.rb
CHANGED
data/lib/cable_ready.rb
CHANGED
@@ -19,4 +19,9 @@ CableReady.configure do |config|
|
|
19
19
|
# Change the default Active Job queue used for broadcast_later and broadcast_later_to
|
20
20
|
#
|
21
21
|
# config.broadcast_job_queue = :default
|
22
|
+
|
23
|
+
# Specify a default debounce time for CableReady::Updatable callbacks
|
24
|
+
# Doing so is a best practice to avoid heavy ActionCable traffic
|
25
|
+
#
|
26
|
+
# config.updatable_debounce_time = 0.1.seconds
|
22
27
|
end
|