phlex-reactive 0.2.2 → 0.2.3
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/CHANGELOG.md +15 -0
- data/lib/phlex/reactive/component.rb +45 -16
- data/lib/phlex/reactive/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5644272b11bc8ff62496e05c81f01a92b1925ffe2b471ab123c05df573ee1343
|
|
4
|
+
data.tar.gz: 56d13ad853e1b6da4e40e8e239838514171b9e908a59ad6db60f6f4a4b199b0e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a489e7a7cdce253735c59c4b5196c52d2051e83f7fb783e7c892cbbd8b9be98934141392d7a84b58b938e80bfd5aba6e4bfe1995888b2b322d232a3fdc207a4a
|
|
7
|
+
data.tar.gz: b9126364a92d1be370e6db639969803c25936e61910967fe05cac00ce908216dd5fa6f6f2f42f16d3c3c23c813d4e2e1c54614babb96f162f77bd71b6e3a5f6b
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,21 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
8
8
|
|
|
9
9
|
### Fixed
|
|
10
10
|
|
|
11
|
+
- **Record-backed components silently lost `reactive_state` every action.** When
|
|
12
|
+
a component declared BOTH `reactive_record` and `reactive_state`, the state
|
|
13
|
+
branch was dead: `reactive_token` signed only the record GID and
|
|
14
|
+
`from_identity` rebuilt with only the record — so the listed instance vars
|
|
15
|
+
(e.g. `attribute`, `editing`) reset to their `initialize` defaults on every
|
|
16
|
+
action. This broke the documented `inline_edit` example (clicking "edit"
|
|
17
|
+
couldn't stay in edit mode; `save` wrote the wrong/blank column) and quietly
|
|
18
|
+
voided the docs' promise that `@attribute` is signed/tamper-proof.
|
|
19
|
+
`reactive_token` now signs the record GID AND the declared state into one
|
|
20
|
+
`MessageVerifier` payload, and `from_identity` restores both — record + state
|
|
21
|
+
are composable, and the state stays tamper-proof. Record-only (`{c, gid}`) and
|
|
22
|
+
state-only (`{c, s}`) token shapes are unchanged; a signed `false` survives the
|
|
23
|
+
round trip (only a genuinely absent value falls back to the `initialize`
|
|
24
|
+
default). No API changes. Closes #6.
|
|
25
|
+
|
|
11
26
|
- **Record-backed component built with a different init keyword by the action
|
|
12
27
|
endpoint vs the broadcast path.** The click path (`Component.from_identity`)
|
|
13
28
|
builds with `reactive_record_key` (the `reactive_record :name`), but the
|
|
@@ -20,8 +20,12 @@ module Phlex
|
|
|
20
20
|
# can neither forge the component class nor swap the record. State =
|
|
21
21
|
# the database.
|
|
22
22
|
# * State-backed (record-less, e.g. a counter): reactive_state :count
|
|
23
|
-
# signs the listed instance variables. Use
|
|
24
|
-
#
|
|
23
|
+
# signs the listed instance variables. Use when there is genuinely no
|
|
24
|
+
# record to re-find.
|
|
25
|
+
# * Both (the inline_edit pattern): reactive_record :record plus
|
|
26
|
+
# reactive_state :attribute, :editing signs the record's GlobalID AND
|
|
27
|
+
# the transient mode in one token, so "which field / what mode" survives
|
|
28
|
+
# every action round trip and stays tamper-proof.
|
|
25
29
|
#
|
|
26
30
|
# Actions are DEFAULT-DENY: only methods declared with `action :name` may be
|
|
27
31
|
# invoked. The signature proves the token is ours, NOT that this user may
|
|
@@ -102,17 +106,35 @@ module Phlex
|
|
|
102
106
|
|
|
103
107
|
# Rebuild a component instance from a verified identity payload. Called
|
|
104
108
|
# by the action endpoint after the token signature is verified.
|
|
109
|
+
#
|
|
110
|
+
# A component may carry a record (re-found via GlobalID), signed state
|
|
111
|
+
# (instance vars listed in reactive_state), or BOTH (the inline_edit
|
|
112
|
+
# pattern: a record plus "which field / what mode"). We assemble the
|
|
113
|
+
# init kwargs from whichever identity pieces are declared.
|
|
105
114
|
def from_identity(payload)
|
|
115
|
+
kwargs = {}
|
|
116
|
+
|
|
106
117
|
if reactive_record_key
|
|
107
118
|
record = GlobalID::Locator.locate(payload.fetch("gid"))
|
|
108
119
|
raise(ActiveRecord::RecordNotFound, "reactive record missing") unless record
|
|
109
120
|
|
|
110
|
-
|
|
111
|
-
|
|
121
|
+
kwargs[reactive_record_key] = record
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
if reactive_state_keys.any?
|
|
112
125
|
state = payload.fetch("s", {})
|
|
113
|
-
|
|
114
|
-
|
|
126
|
+
reactive_state_keys.each do |key|
|
|
127
|
+
# Use key presence, not the value: a signed `nil` (nullable state)
|
|
128
|
+
# must round-trip distinctly. Only a genuinely absent key falls
|
|
129
|
+
# back to the component's initialize default; `false` and `nil`
|
|
130
|
+
# both survive.
|
|
131
|
+
next unless state.key?(key.to_s)
|
|
132
|
+
|
|
133
|
+
kwargs[key] = state[key.to_s]
|
|
134
|
+
end
|
|
115
135
|
end
|
|
136
|
+
|
|
137
|
+
new(**kwargs)
|
|
116
138
|
end
|
|
117
139
|
end
|
|
118
140
|
|
|
@@ -164,18 +186,25 @@ module Phlex
|
|
|
164
186
|
|
|
165
187
|
private
|
|
166
188
|
|
|
167
|
-
# Signed
|
|
189
|
+
# Signed identity payload: the class name plus whichever identity pieces
|
|
190
|
+
# the component declares — a record GlobalID (`gid`), signed state (`s`),
|
|
191
|
+
# or both. Keeping them in ONE MessageVerifier payload makes the state
|
|
192
|
+
# (e.g. which column an inline_edit may write) tamper-proof alongside the
|
|
193
|
+
# record. Record-only ({c, gid}) and state-only ({c, s}) shapes are
|
|
194
|
+
# unchanged.
|
|
168
195
|
def reactive_token
|
|
169
|
-
payload =
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
196
|
+
payload = {"c" => self.class.name}
|
|
197
|
+
|
|
198
|
+
if self.class.reactive_record_key
|
|
199
|
+
record = instance_variable_get(:"@#{self.class.reactive_record_key}")
|
|
200
|
+
payload["gid"] = record.to_gid.to_s
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
if self.class.reactive_state_keys.any?
|
|
204
|
+
payload["s"] = self.class.reactive_state_keys.to_h do |k|
|
|
205
|
+
[k.to_s, instance_variable_get(:"@#{k}").as_json]
|
|
178
206
|
end
|
|
207
|
+
end
|
|
179
208
|
|
|
180
209
|
Phlex::Reactive.sign(payload)
|
|
181
210
|
end
|