From d3e2efcffaa0646077e317cd9b6f4c3b27b45524 Mon Sep 17 00:00:00 2001 From: leastbad <38150464+leastbad@users.noreply.github.com> Date: Fri, 12 Nov 2021 15:32:16 -0500 Subject: [PATCH] updates_for :only option (#163) Co-authored-by: Julian Rubisch Co-authored-by: Andrew Erlanger --- Gemfile.lock | 54 +++++++++---------- app/helpers/cable_ready_helper.rb | 3 +- app/models/concerns/cable_ready/updatable.rb | 4 +- .../updatable/collections_registry.rb | 2 +- .../updatable/model_updatable_callbacks.rb | 2 +- javascript/elements/updates_for_element.js | 10 +++- test/lib/cable_ready/updatable_test.rb | 16 +++--- 7 files changed, 50 insertions(+), 41 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 4bd3689b..879f659d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -86,7 +86,7 @@ GEM faraday async-io (1.32.2) async - async-pool (0.3.8) + async-pool (0.3.9) async (>= 1.25) builder (3.2.4) coderay (1.1.3) @@ -95,7 +95,7 @@ GEM fiber-local crass (1.0.6) erubi (1.10.0) - faraday (1.7.0) + faraday (1.8.0) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -128,7 +128,7 @@ GEM rake (>= 10.0) globalid (0.5.2) activesupport (>= 5.0) - i18n (1.8.10) + i18n (1.8.11) concurrent-ruby (~> 1.0) loofah (2.12.0) crass (~> 1.0.2) @@ -137,35 +137,35 @@ GEM mail (2.7.1) mini_mime (>= 0.1.1) marcel (1.0.2) - method_source (0.9.2) + method_source (1.0.0) mini_mime (1.1.2) minitest (5.14.4) mocha (1.13.0) multi_json (1.15.0) multipart-post (2.1.1) nio4r (2.5.8) - nokogiri (1.12.5-arm64-darwin) + nokogiri (1.12.5-x86_64-linux) racc (~> 1.4) octokit (4.21.0) faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) - parallel (1.20.1) + parallel (1.21.0) parser (3.0.2.0) ast (~> 2.4.1) protocol-hpack (1.4.2) protocol-http (0.22.5) - protocol-http1 (0.14.1) + protocol-http1 (0.14.2) protocol-http (~> 0.22) protocol-http2 (0.14.2) protocol-hpack (~> 1.4) protocol-http (~> 0.18) - pry (0.12.2) - coderay (~> 1.1.0) - method_source (~> 0.9.0) - pry-nav (0.3.0) - pry (>= 0.9.10, < 0.13.0) + pry (0.14.1) + coderay (~> 1.1) + method_source (~> 1.0) + pry-nav (1.0.0) + pry (>= 0.9.10, < 0.15) public_suffix (4.0.6) - racc (1.5.2) + racc (1.6.0) rack (2.2.3) rack-test (1.1.0) rack (>= 1.0, < 3) @@ -199,18 +199,18 @@ GEM rake (13.0.6) regexp_parser (2.1.1) rexml (3.2.5) - rubocop (1.18.4) + rubocop (1.22.3) parallel (~> 1.10) parser (>= 3.0.0.0) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml - rubocop-ast (>= 1.8.0, < 2.0) + rubocop-ast (>= 1.12.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.10.0) + rubocop-ast (1.13.0) parser (>= 3.0.1.1) - rubocop-performance (1.11.4) + rubocop-performance (1.11.5) rubocop (>= 1.7.0, < 2.0) rubocop-ast (>= 0.4.0) ruby-progressbar (1.11.0) @@ -221,14 +221,14 @@ GEM sprockets (4.0.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.2.2) - actionpack (>= 4.0) - activesupport (>= 4.0) + sprockets-rails (3.3.0) + actionpack (>= 5.2) + activesupport (>= 5.2) sprockets (>= 3.0.0) sqlite3 (1.4.2) - standard (1.1.7) - rubocop (= 1.18.4) - rubocop-performance (= 1.11.4) + standard (1.4.0) + rubocop (= 1.22.3) + rubocop-performance (= 1.11.5) standardrb (1.0.0) standard thor (1.1.0) @@ -236,14 +236,14 @@ GEM timers (4.3.3) tzinfo (2.0.4) concurrent-ruby (~> 1.0) - unicode-display_width (2.0.0) + unicode-display_width (2.1.0) websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - zeitwerk (2.4.2) + zeitwerk (2.5.1) PLATFORMS - ruby + x86_64-linux DEPENDENCIES cable_ready! @@ -257,4 +257,4 @@ DEPENDENCIES standardrb BUNDLED WITH - 2.2.19 + 2.2.27 diff --git a/app/helpers/cable_ready_helper.rb b/app/helpers/cable_ready_helper.rb index ab765bbb..728d2e8f 100644 --- a/app/helpers/cable_ready_helper.rb +++ b/app/helpers/cable_ready_helper.rb @@ -8,10 +8,11 @@ def stream_from(*keys, html_options: {}) tag.stream_from(**build_options(*keys, html_options)) end - def updates_for(*keys, url: nil, debounce: nil, html_options: {}, &block) + def updates_for(*keys, url: nil, debounce: nil, only: nil, html_options: {}, &block) options = build_options(*keys, html_options) options[:url] = url if url options[:debounce] = debounce if debounce + options[:only] = only if only tag.updates_for(**options) { capture(&block) } end diff --git a/app/models/concerns/cable_ready/updatable.rb b/app/models/concerns/cable_ready/updatable.rb index 87322a2a..84d2f0e2 100644 --- a/app/models/concerns/cable_ready/updatable.rb +++ b/app/models/concerns/cable_ready/updatable.rb @@ -43,9 +43,9 @@ def cable_ready_collections @cable_ready_collections ||= CollectionsRegistry.new end - def cable_ready_update_collection(resource, name) + def cable_ready_update_collection(resource, name, model) identifier = resource.to_global_id.to_s + ":" + name.to_s - ActionCable.server.broadcast(identifier, {}) + ActionCable.server.broadcast(identifier, model.respond_to?(:previous_changes) ? {changed: model.previous_changes.keys} : {}) end def enrich_association_with_updates(name, option) diff --git a/app/models/concerns/cable_ready/updatable/collections_registry.rb b/app/models/concerns/cable_ready/updatable/collections_registry.rb index 2b53aa60..371d4709 100644 --- a/app/models/concerns/cable_ready/updatable/collections_registry.rb +++ b/app/models/concerns/cable_ready/updatable/collections_registry.rb @@ -15,7 +15,7 @@ def broadcast_for!(model, operation) resource = find_resource_for_update(collection, model) next if resource.nil? - collection[:klass].cable_ready_update_collection(resource, collection[:name]) if collection[:options][:if].call(resource) + collection[:klass].cable_ready_update_collection(resource, collection[:name], model) if collection[:options][:if].call(resource) end end diff --git a/app/models/concerns/cable_ready/updatable/model_updatable_callbacks.rb b/app/models/concerns/cable_ready/updatable/model_updatable_callbacks.rb index 3b9888ca..e363dac2 100644 --- a/app/models/concerns/cable_ready/updatable/model_updatable_callbacks.rb +++ b/app/models/concerns/cable_ready/updatable/model_updatable_callbacks.rb @@ -21,7 +21,7 @@ def broadcast_create(model) def broadcast_update(model) ActionCable.server.broadcast(model.class, {}) - ActionCable.server.broadcast(model.to_global_id, {}) + ActionCable.server.broadcast(model.to_global_id, model.respond_to?(:previous_changes) ? {changed: model.previous_changes.keys} : {}) end end end diff --git a/javascript/elements/updates_for_element.js b/javascript/elements/updates_for_element.js index 6110e125..9879db07 100644 --- a/javascript/elements/updates_for_element.js +++ b/javascript/elements/updates_for_element.js @@ -40,12 +40,20 @@ export default class UpdatesForElement extends SubscribingElement { } } - async update () { + async update (data) { const identifier = this.getAttribute('identifier') const query = `updates-for[identifier="${identifier}"]` const blocks = document.querySelectorAll(query) if (blocks[0] !== this) return + const only = this.getAttribute('only') + if ( + only && + data.changed && + !only.split(' ').some(attribute => data.changed.includes(attribute)) + ) + return + const html = {} const template = document.createElement('template') diff --git a/test/lib/cable_ready/updatable_test.rb b/test/lib/cable_ready/updatable_test.rb index 843440e3..28135558 100644 --- a/test/lib/cable_ready/updatable_test.rb +++ b/test/lib/cable_ready/updatable_test.rb @@ -13,7 +13,7 @@ class CableReady::UpdatableTest < ActiveSupport::TestCase test "updates the collection when an item is added" do mock_server = mock("server") mock_server.expects(:broadcast).with(User, {}).once - mock_server.expects(:broadcast).with("gid://dummy/User/1:posts", {}).once + mock_server.expects(:broadcast).with("gid://dummy/User/1:posts", {changed: ["id", "title", "user_id", "created_at", "updated_at"]}).once ActionCable.stubs(:server).returns(mock_server) user = User.create(name: "John Doe") @@ -26,7 +26,7 @@ class CableReady::UpdatableTest < ActiveSupport::TestCase post = user.posts.create(title: "Lorem") mock_server = mock("server") - mock_server.expects(:broadcast).with("gid://dummy/User/1:posts", {}).once + mock_server.expects(:broadcast).with("gid://dummy/User/1:posts", {changed: ["id", "title", "user_id", "created_at", "updated_at"]}).once ActionCable.stubs(:server).returns(mock_server) @@ -38,7 +38,7 @@ class CableReady::UpdatableTest < ActiveSupport::TestCase post = user.posts.create(title: "Lorem") mock_server = mock("server") - mock_server.expects(:broadcast).with("gid://dummy/User/1:posts", {}).once + mock_server.expects(:broadcast).with("gid://dummy/User/1:posts", {changed: ["title", "updated_at"]}).once ActionCable.stubs(:server).returns(mock_server) @@ -50,7 +50,7 @@ class CableReady::UpdatableTest < ActiveSupport::TestCase mock_server = mock("server") mock_server.expects(:broadcast).with(User, {}).once - mock_server.expects(:broadcast).with(user.to_global_id, {}).once + mock_server.expects(:broadcast).with(user.to_global_id, {changed: ["name", "updated_at"]}).once ActionCable.stubs(:server).returns(mock_server) @@ -63,10 +63,10 @@ class CableReady::UpdatableTest < ActiveSupport::TestCase mock_server = mock("server") mock_server.expects(:broadcast).with(User, {}).once - mock_server.expects(:broadcast).with(user.to_global_id, {}).once - mock_server.expects(:broadcast).with("gid://dummy/Team/1:users", {}).once + mock_server.expects(:broadcast).with(user.to_global_id, {changed: ["name", "updated_at"]}).once + mock_server.expects(:broadcast).with("gid://dummy/Team/1:users", {changed: ["name", "updated_at"]}).once mock_server.expects(:broadcast).with(Team, {}).once - mock_server.expects(:broadcast).with(team.to_global_id, {}).once + mock_server.expects(:broadcast).with(team.to_global_id, {changed: ["id", "created_at", "updated_at"]}).once ActionCable.stubs(:server).returns(mock_server) @@ -94,7 +94,7 @@ class CableReady::UpdatableTest < ActiveSupport::TestCase section.updates_enabled = true mock_server.expects(:broadcast).with(Section, {}).once - mock_server.expects(:broadcast).with(section.to_global_id, {}).once + mock_server.expects(:broadcast).with(section.to_global_id, {changed: ["title", "updated_at", "updates_enabled"]}).once section.update(title: "First Section") end