phlex-reactive 0.4.1 → 0.4.2

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: 786d397de0be7e32424c73079906cdf8d32d0325727d06027025d888693754e6
4
- data.tar.gz: b080f12ff8c8f4271066993548f9c951278fe2dacabf9b7d21c2113704f58da4
3
+ metadata.gz: 8ec8a731ff4ae0e729288d805070e04c8860b7a51591a69b946ed42bd4996d97
4
+ data.tar.gz: '018dafe55b68a05a46ef52c4e896803a9933beecf50e87f1d428073065fd3205'
5
5
  SHA512:
6
- metadata.gz: 316b9b2094490dd5045d1362276a8f31ba4deee96df08e6614dc9db8f62a2ceb822f5a474d2204dcb65f7ed1bb27b3c99303f14a180a336c2d5bc3d21c3bf31a
7
- data.tar.gz: f54ecfe74e977b3d4a399cd004071792a3a7245636d1c82f19441b0240fb2d24d32bb072a3ae863a9e04efcab5a31182560628237f72bf77ff73ac387592e4c4
6
+ metadata.gz: 949549bc92c39def933aea8d83cf61db41ad686c8184c8a284657bdd99e1178dece129258e0e874ec1dd157748f69fcd11dc77fe140557c20d7dab217500a46b
7
+ data.tar.gz: ab245aa3bf1a4f65f392ae2c4b3e98cf20a6d45064375a2c2168b2c7214ed4d27bc78c6cd0f069d2ef82cad771ee233376742de6e22636f5b0efb81390f5b8ea
data/CHANGELOG.md CHANGED
@@ -72,6 +72,31 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
72
72
 
73
73
  ### Fixed
74
74
 
75
+ - **Collections of *reactive* rows were still add-once-only — a prepended/appended
76
+ row's OWN token suppressed the container's token refresh (#44).** When an action
77
+ appended/prepended a *reactive* child row into a container whose `#id` equals the
78
+ append target (the most common collection shape — rows go directly into the
79
+ container element), that single `append`/`prepend` stream carried BOTH
80
+ `target="<container>"` and the row's own `data-reactive-token-value` (embedded in
81
+ the `<template>`). `carries_token_for?` matched
82
+ `include?("data-reactive-token-value") && include?(target)` in that one string →
83
+ both true → it concluded the container's token was already fresh and **skipped**
84
+ the container's `reactive:token` refresh. So the container (which owns the
85
+ add/remove trigger) never rolled its token forward, and the second dispatch was
86
+ rejected with the stale token — the list silently stopped after the first add.
87
+ This hit both the hand-rolled `reply.streams(Row.prepend(...), …)` form **and**
88
+ the `reactive_collection` / `reply.append`/`reply.prepend` helper, since they
89
+ share the gate. Fixed by tightening `carries_token_for?`: a stream "carries the
90
+ token for C" only when its action RE-RENDERS C itself (`replace`/`update`/
91
+ `reactive:token` at C's target) — `append`/`prepend` (which insert *children*
92
+ into C) never count, because a reactive child's token is not the container's. The
93
+ idempotency the gate provided is preserved (a caller's own self-replace still
94
+ suppresses the duplicate token) and the #30 sibling case still refreshes
95
+ correctly. Covered at the request level: a reactive row appended twice in a row
96
+ (the second using the token the first rolled forward) now succeeds, and each
97
+ response carries a `reactive:token` stream targeting the container with a
98
+ non-empty token. Refs cosmos#1939, #35, #30.
99
+
75
100
  - **0.4.0 regression: re-renders lost the `request`, crashing `form_authenticity_token`
76
101
  and other request-dependent helpers (#42).** The 0.4.0 render-path perf rework
77
102
  built the cached off-request view context from a bare
@@ -107,15 +107,49 @@ module Phlex
107
107
  streams
108
108
  end
109
109
 
110
- # True when one of `streams` already carries a fresh token TARGETING this
111
- # component i.e. the caller hand-built the actor's own token-bearing
112
- # stream, so appending to_stream_token would double it. Scoped to the
113
- # component's target id (not a global substring) so a sibling component's
114
- # stream, which carries its OWN token for a DIFFERENT target, doesn't fool
115
- # us into skipping this component's refresh.
110
+ # Actions that RE-RENDER the component's own root (so the root's fresh
111
+ # data-reactive-token-value rolls the signed token forward). `append`/
112
+ # `prepend` are deliberately excluded: they insert CHILDREN into the
113
+ # component, and a reactive child carries its OWN token that child token is
114
+ # not the component's (issue #44). reactive:token is our inert token-only
115
+ # refresh; replace/update re-render the root.
116
+ SELF_RENDER_ACTIONS = %w[replace update reactive:token].freeze
117
+ private_constant :SELF_RENDER_ACTIONS
118
+
119
+ # True when one of `streams` already carries a fresh token by RE-RENDERING
120
+ # this component itself — i.e. the caller hand-built the actor's own
121
+ # token-bearing stream, so appending to_stream_token would double it.
122
+ #
123
+ # The match requires BOTH (a) the stream's action re-renders the component's
124
+ # ROOT (replace/update/reactive:token — never append/prepend, which insert
125
+ # children) AND (b) it targets the component's id AND (c) it actually carries
126
+ # a token. This is stricter than a same-string substring check on purpose:
127
+ # * A sibling component's replace targets a DIFFERENT id → (b) fails, so we
128
+ # still refresh ours (issue #30).
129
+ # * A reactive child row appended/prepended INTO the component carries its
130
+ # own token at the component's target, but the action is append/prepend →
131
+ # (a) fails, so we still refresh the CONTAINER's token (issue #44). Before
132
+ # this, the child's token at the container target suppressed the
133
+ # container's refresh and the list was add-once-only.
116
134
  def carries_token_for?(streams, component)
117
135
  target = %(target="#{ERB::Util.html_escape(component.id)}")
118
- streams.any? { it.include?("data-reactive-token-value") && it.include?(target) }
136
+ streams.any? do
137
+ it.include?("data-reactive-token-value") &&
138
+ it.include?(target) &&
139
+ self_render_stream_for?(it, target)
140
+ end
141
+ end
142
+
143
+ # Does this turbo-stream's OPENING tag re-render `target` itself? Matches a
144
+ # `<turbo-stream action="<self-render>" ... target="<id>">` opening tag — the
145
+ # action and target on the SAME tag — so a child row's token embedded in an
146
+ # append/prepend `<template>` can never count as the container's own refresh.
147
+ def self_render_stream_for?(stream, target)
148
+ open_tag = stream[/<turbo-stream\b[^>]*>/]
149
+ return false unless open_tag&.include?(target)
150
+
151
+ action = open_tag[/\baction="([^"]+)"/, 1]
152
+ SELF_RENDER_ACTIONS.include?(action)
119
153
  end
120
154
 
121
155
  # A 200 turbo-stream carrying a namespaced custom action the client turns
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Phlex
4
4
  module Reactive
5
- VERSION = "0.4.1"
5
+ VERSION = "0.4.2"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phlex-reactive
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson