rest-easy 1.4.0 → 1.4.1
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 +46 -1
- data/lib/rest_easy/meta.rb +40 -2
- data/lib/rest_easy/resource.rb +16 -2
- data/lib/rest_easy/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a30780e0b38e31f1c183d1ec0a91ea93658a78e67d2415aeb8aef279d03359b7
|
|
4
|
+
data.tar.gz: 2ed7aafbcaf5e0c6fd463992a65293d1f064c693611f64b9b3256e8e4b518fdf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3ac9ea37e26b652d5fe590075d7668f70e2cf8b7290b02d7e4dd1f227bf7a7e26ea9e5faa02ed1b84b5706262d90e774db52381ea0ce2d026da40d410ae38c11
|
|
7
|
+
data.tar.gz: 8f8773d0cbacc6366daade22ceee18ee76de91ee5eb5cd718c392e0abfe1ed3a911658a867dab1bcd1f21b077f2296e0cb5ffbb1aa5597f6fee7e77a7b40b84d
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [1.4.1] - 2026-08-18
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **`Meta` no longer claims to implement methods it has no value for.**
|
|
10
|
+
`Meta#respond_to_missing?` returned `true` for every name, so any caller
|
|
11
|
+
that asks an object what it can do before calling it got a false yes, then
|
|
12
|
+
received the `nil` that `method_missing` returns for an unknown key as
|
|
13
|
+
though it were a real answer. Ruby does this constantly and implicitly —
|
|
14
|
+
coercion probes when a value is splatted or interpolated, serialisation
|
|
15
|
+
hooks, and any library that duck-types with `respond_to?` — so the failure
|
|
16
|
+
surfaced far from `Meta` and looked unrelated to it. Marshalling was the
|
|
17
|
+
sharpest case: `dump` silently wrote an empty payload and `load` then
|
|
18
|
+
raised `NoMethodError` from inside `method_missing`, which broke every
|
|
19
|
+
consumer caching resources in a store that marshals its entries.
|
|
20
|
+
|
|
21
|
+
Bare getters are now claimed only for keys the instance actually holds.
|
|
22
|
+
Setters and predicates are still claimed unconditionally, since writing is
|
|
23
|
+
how a key comes into existence and an unset predicate is meaningfully
|
|
24
|
+
`false`; no core Ruby probe uses those two shapes. Library-level
|
|
25
|
+
duck-typing can, though — ActiveSupport's `acts_like?(:date)` asks for
|
|
26
|
+
`acts_like_date?`, and `Meta` still answers yes — so the guarantee is
|
|
27
|
+
narrower than "nothing probes for `=` or `?`", and closing it properly is
|
|
28
|
+
tracked in #7. Operators are no longer claimed either: the check is
|
|
29
|
+
anchored to identifier-shaped names, so `<=` is not mistaken for a setter
|
|
30
|
+
(`method_missing` still is — #6).
|
|
31
|
+
`Resource::MetaCollector` had the identical defect and is fixed the same
|
|
32
|
+
way.
|
|
33
|
+
|
|
34
|
+
**Upgrading with a warm cache:** entries written by an earlier version are
|
|
35
|
+
not recoverable. A payload dumped before this fix carries `nil` where the
|
|
36
|
+
meta state should be, and loading it now raises `TypeError: instance of
|
|
37
|
+
RestEasy::Meta needs to have method 'marshal_load'` — legible, but still
|
|
38
|
+
an error. Consumers on a cache that survives deploys (`:file_store`,
|
|
39
|
+
Redis, Memcached) must flush it or bump the cache key namespace when
|
|
40
|
+
upgrading. A process-local `:memory_store` clears itself on restart and
|
|
41
|
+
needs nothing.
|
|
42
|
+
|
|
43
|
+
`method_missing` is unchanged: a bare getter for a key that was never set
|
|
44
|
+
still returns `nil` rather than raising, as the "gem author extensions"
|
|
45
|
+
example in `docs/model-architecture.md` depends on. `respond_to?`
|
|
46
|
+
therefore under-reports for those keys; reconciling the two halves
|
|
47
|
+
requires declaring meta keys up front and is tracked in #7.
|
|
48
|
+
|
|
5
49
|
## [1.4.0] - 2026-06-26
|
|
6
50
|
|
|
7
51
|
### Fixed
|
|
@@ -123,7 +167,8 @@
|
|
|
123
167
|
|
|
124
168
|
Initial release.
|
|
125
169
|
|
|
126
|
-
[Unreleased]: https://github.com/accodeing/rest-easy/compare/v1.4.
|
|
170
|
+
[Unreleased]: https://github.com/accodeing/rest-easy/compare/v1.4.1...HEAD
|
|
171
|
+
[1.4.1]: https://github.com/accodeing/rest-easy/compare/v1.4.0...v1.4.1
|
|
127
172
|
[1.4.0]: https://github.com/accodeing/rest-easy/compare/v1.3.1...v1.4.0
|
|
128
173
|
[1.3.1]: https://github.com/accodeing/rest-easy/compare/v1.3.0...v1.3.1
|
|
129
174
|
[1.3.0]: https://github.com/accodeing/rest-easy/compare/v1.2.0...v1.3.0
|
data/lib/rest_easy/meta.rb
CHANGED
|
@@ -25,8 +25,46 @@ module RestEasy
|
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
# Accessor shapes `method_missing` synthesises. Anchored, so operators
|
|
29
|
+
# stay out: `<=` ends with `=`, and claiming it would let `meta <= 5`
|
|
30
|
+
# read as a setter. `method_missing` itself is still unanchored and
|
|
31
|
+
# writes `@data[:<]` for that call — see issue #6.
|
|
32
|
+
#
|
|
33
|
+
# POSIX classes rather than `[a-zA-Z_]\w*`, because Ruby identifiers are
|
|
34
|
+
# not ASCII-only: `meta.företag = x` is a valid setter that
|
|
35
|
+
# `method_missing` stores, so `respond_to?` has to claim it too.
|
|
36
|
+
ACCESSOR_PATTERN = /\A[[:alpha:]_][[:word:]]*[=?]\z/
|
|
37
|
+
private_constant :ACCESSOR_PATTERN
|
|
38
|
+
|
|
39
|
+
# `method_missing` answers to any name, but this must not, because callers
|
|
40
|
+
# routinely ask an object what it can do before calling it — Ruby's own
|
|
41
|
+
# coercion probes (`to_hash` on double-splat, `to_ary`, `to_str`),
|
|
42
|
+
# serialisation hooks (`marshal_dump`, `init_with`), and any library that
|
|
43
|
+
# duck-types with `respond_to?`. Claiming those hands the caller the `nil`
|
|
44
|
+
# that `method_missing` returns for an unknown key as though it were an
|
|
45
|
+
# answer.
|
|
46
|
+
#
|
|
47
|
+
# Bare getters are therefore claimed only for keys actually held. Setters
|
|
48
|
+
# and predicates are claimed unconditionally: writing is how a key comes
|
|
49
|
+
# into existence, and an unset predicate is meaningfully `false`. No core
|
|
50
|
+
# Ruby probe uses those two shapes — though library-level duck-typing can,
|
|
51
|
+
# and ActiveSupport's `acts_like?(:date)` asking for `acts_like_date?` is
|
|
52
|
+
# a known false positive. Closing it properly means declaring meta keys
|
|
53
|
+
# up front, which is a breaking change — see issue #7.
|
|
54
|
+
#
|
|
55
|
+
# `@data` is guarded because `respond_to?` must never raise, and Ruby
|
|
56
|
+
# allocates an instance before initialising it: Psych's `revive` probes
|
|
57
|
+
# `init_with` on a bare allocation, and so does `Marshal.load` on a
|
|
58
|
+
# payload written before this fix.
|
|
59
|
+
#
|
|
60
|
+
# This under-claims a bare getter for a key that was never set, which
|
|
61
|
+
# `method_missing` still answers with `nil`. Reconciling the two halves
|
|
62
|
+
# means declaring meta keys up front, and that is a 2.0 — see issue #7.
|
|
63
|
+
def respond_to_missing?(name, include_private = false)
|
|
64
|
+
return true if ACCESSOR_PATTERN.match?(name.to_s)
|
|
65
|
+
return super unless @data
|
|
66
|
+
|
|
67
|
+
@data.key?(name.to_sym) || super
|
|
30
68
|
end
|
|
31
69
|
end
|
|
32
70
|
end
|
data/lib/rest_easy/resource.rb
CHANGED
|
@@ -119,8 +119,22 @@ module RestEasy
|
|
|
119
119
|
end
|
|
120
120
|
end
|
|
121
121
|
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
# Anchored so operators stay out — see Meta::ACCESSOR_PATTERN and
|
|
123
|
+
# issue #6. Only the setter shape here; unlike Meta, `method_missing`
|
|
124
|
+
# above has no predicate form.
|
|
125
|
+
SETTER_PATTERN = /\A[[:alpha:]_][[:word:]]*=\z/
|
|
126
|
+
private_constant :SETTER_PATTERN
|
|
127
|
+
|
|
128
|
+
# Same reasoning as Meta#respond_to_missing? — a blanket `true` makes
|
|
129
|
+
# this claim Ruby's implicit protocol methods and hand them the `nil`
|
|
130
|
+
# from `method_missing`. `@data` is guarded for the same reason too:
|
|
131
|
+
# `respond_to?` must never raise, and Psych and Marshal both probe a
|
|
132
|
+
# bare allocation before it is initialised.
|
|
133
|
+
def respond_to_missing?(name, include_private = false)
|
|
134
|
+
return true if SETTER_PATTERN.match?(name.to_s)
|
|
135
|
+
return super unless @data
|
|
136
|
+
|
|
137
|
+
@data.key?(name.to_sym) || super
|
|
124
138
|
end
|
|
125
139
|
end
|
|
126
140
|
|
data/lib/rest_easy/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rest-easy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.4.
|
|
4
|
+
version: 1.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jonas Schubert Erlandsson
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2026-
|
|
13
|
+
date: 2026-08-18 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: dry-types
|