Skip to content

Get attribute fixes - #119

Merged
isometriks merged 6 commits into
mainfrom
get-attribute-fixes
Aug 16, 2026
Merged

Get attribute fixes#119
isometriks merged 6 commits into
mainfrom
get-attribute-fixes

Conversation

@isometriks

Copy link
Copy Markdown
Owner

No description provided.

isometriks and others added 6 commits August 13, 2026 20:31
The no-arguments branch subscripted Hash and Array with the attribute
name instead of calling it, which is only ever reached when the key/index
lookup above has already missed — so the subscript could only miss too.
On an Array it raised TypeError ({{ list.any? }}), and on a Hash it
returned nil and silently rendered nothing. Collections now dispatch
methods like any other object, while a real key or index still wins over
a method of the same name.

Also public_send rather than send: the respond_to? guard above already
excludes private and protected methods, but an object with a loose
respond_to_missing? can report true for one, and templates should only
reach the public interface.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The bounds check was only an upper one, so every negative index passed it.
Ruby indexes from the end with a negative, so -1 is genuinely the last
element, but -99 on a three-element array is as absent as 99 is — and it
fell through to `object[attribute] || object[attribute.to_s]`, where the
String subscript raised TypeError out of the array. Because the exception
came from inside the lookup rather than the strict check, `??` and
`default()` couldn't suppress it either.

Negative indices within range still resolve from the end; out of range now
reports the same "can't find key" as an out-of-range positive, or returns
nil when strict_variables is off.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`object[attribute] || object[attribute.to_s]` cannot distinguish a key
that is absent from a key that is present and holds a falsy value, so it
fell through to the other form of the name and returned whatever that
gave — usually nothing. { 'billable' => false } read as h['billable']
came back nil, and so did { billable: false } read as h[:billable]. The
cases that appeared to work only did so by accident: when the name's form
mismatched the key's, the fallback lookup happened to be the real one.

The guard already knew which key matched; it just threw that away and
re-derived it by fetching. subscript_key now returns the matching key (or
a KEY_NOT_FOUND sentinel, since nil is both a valid key and a valid
value) and the caller subscripts once with it. Where both forms of a name
exist, the one the template asked for now wins rather than whichever is
truthy.

The array bounds check moves into the same helper, since it answers the
same question.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The two paths out of get_attribute described the same failure differently.
Dot access produced Twig's own wording — Key "x" for sequence/mapping with
keys "a, b" does not exist — while bracket access produced "Can't find key
x in " followed by the whole object inspected, which for anything the size
of a request's parameters buries the one absent key under everything else.
It also raised the bare message, so unlike its neighbour it carried no
lineno or source.

Both now build the message through key_not_found_message and raise with
the source attached. Also drops the double space the old wording left when
the object has no keys to list, as an array doesn't.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The version was a literal in the gemspec, so it existed in exactly one place
that nothing else could see: the library couldn't report its own version at
runtime, and neither could anything depending on it.

Twig::VERSION now holds it and the gemspec reads that. require_relative
rather than requiring the gem, since this needs one constant and evaluating
the whole library would pull ActiveSupport in at packaging time. The
existing loader globs lib/twig/*.rb, so the constant is there at runtime
without anything else being wired up.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@isometriks
isometriks merged commit 3c0bd89 into main Aug 16, 2026
3 of 4 checks passed
@isometriks
isometriks deleted the get-attribute-fixes branch August 16, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant