cable_ready 5.0.0.pre10 → 5.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7240cc96726bfeff504657e69096170f4971cd94357bb0fcf0431d14db26c953
4
- data.tar.gz: b32acb97a059ee03bab8c8a973917d934e208a4aaf44015a2fe082fe5d42086f
3
+ metadata.gz: 727abefeeee7c2b5775ce2ca9b9e4521cf8172ba99f0a3ce4d6c0e4c49a77fae
4
+ data.tar.gz: f42905f8cc53e8effade6db00fa24d569c398b7f163e9a06aa19425c1bcdd888
5
5
  SHA512:
6
- metadata.gz: 8d641446ac2e71ce6cb901e38c37851886e10a0076c796429ca8476cfde68094a1e7ad096eba8c058d24b9acd9125f81f7b85c7cf0385e6d898931185629ccfb
7
- data.tar.gz: ac2d450ccd867b52c154053337f80d56af0221eee3a4ddd0e9597d93c8defb0a67b28efc34c14cf454241875303e43a01ecfe51bf13f2180e23e5e2df23e3a94
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.pre10)
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.2)
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/master/assets/cable-ready-logo-with-copy.svg" width="360" />
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/master/LICENSE">
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/Prettier-Standard/badge.svg">
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/StandardRB/badge.svg">
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/master/docs)
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.min.js', preload: true
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.min.js", "data-turbo-track": "reload" %>
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 github
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-pre10";
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 prettier-standard:check",
37
- format: "yarn run prettier-standard:format",
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.0",
57
+ "@web/test-runner": "^0.15.1",
57
58
  "prettier-standard": "^16.4.1",
58
- rollup: "^3.15.0",
59
- sinon: "^15.0.1"
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
- docFragment.querySelector(selector).innerHTML = content;
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-pre10";
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 prettier-standard:check",
24
- format: "yarn run prettier-standard:format",
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.0",
42
+ "@web/test-runner": "^0.15.1",
42
43
  "prettier-standard": "^16.4.1",
43
- rollup: "^3.15.0",
44
- sinon: "^15.0.1"
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
- docFragment.querySelector(selector).innerHTML = content;
1069
+ documentFragment.querySelector(selector).innerHTML = content;
1066
1070
  resolve();
1067
1071
  })))));
1068
1072
  }
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  if defined?(ActiveJob::Base)
4
- class CableReadyBroadcastJob < ActiveJob::Base
4
+ class CableReady::BroadcastJob < ActiveJob::Base
5
5
  include CableReady::Broadcaster
6
6
 
7
7
  def perform(identifier:, operations:, model: nil)
@@ -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[:options][:on].include?(operation) }
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[:klass].cable_ready_update_collection(resource, collection[:name], model) if collection[:options][:if].call(resource)
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
- raise ArgumentError, "Could not find inverse_of for #{collection[:name]}" unless collection[:inverse_association]
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[:through_association].underscore) if collection[:through_association]
31
- resource.send(collection[:inverse_association].underscore)
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
- model.class.send(:broadcast_updates, model.class, changeset)
27
- model.class.send(:broadcast_updates, model.to_global_id, changeset)
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
- broadcast_updates(identifier, model.respond_to?(:previous_changes) ? {changed: model.previous_changes.keys} : {})
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
- inverse_association: inverse_of,
132
- through_association: through_association,
133
- options: options
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
- ActionCable.server.broadcast(model_class, options)
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
@@ -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
- CableReadyBroadcastJob
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
- CableReadyBroadcastJob
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
@@ -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
@@ -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
- render json: operations.dispatch
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
- Mime::Type.register "application/vnd.cable-ready.json", :cable_ready
34
- end
35
- end
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
- initializer "cable_ready.assets" do |app|
38
- if app.config.respond_to?(:assets) && CableReady.config.precompile_assets
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
 
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  pin "morphdom", to: "https://ga.jspm.io/npm:morphdom@2.6.1/dist/morphdom.js", preload: true
4
- pin "cable_ready", to: "cable_ready.min.js", preload: true
4
+ pin "cable_ready", to: "cable_ready.js", preload: true
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CableReady
4
- VERSION = "5.0.0.pre10"
4
+ VERSION = "5.0.0.rc1"
5
5
  end
data/lib/cable_ready.rb CHANGED
@@ -17,6 +17,8 @@ require "cable_ready/version"
17
17
  require "cable_ready_helper"
18
18
 
19
19
  module CableReady
20
+ autoload :MemoryCacheDebounceAdapter, "cable_ready/updatable/memory_cache_debounce_adapter"
21
+
20
22
  class << self
21
23
  def config
22
24
  CableReady::Config.instance
@@ -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