Skip to content

NestedFilter v2.0: structure-preserving core#185

Merged
treble37 merged 10 commits into
mainfrom
v2-dev
Jul 9, 2026
Merged

NestedFilter v2.0: structure-preserving core#185
treble37 merged 10 commits into
mainfrom
v2-dev

Conversation

@treble37

@treble37 treble37 commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Summary

v2.0 rewrite per the approved plan (T1–T9):

  • filter/3 / reject/3 engines with a shared structs: :leaf | :convert | :error option (default :leaf); drop_by_value/3, drop_by_key/3, take_by_key/3 are now sugar over them
  • take_by_key/3 is structure-preserving — the 1.x flattening (and its silent data loss on duplicate keys) is gone
  • Removed undocumented drop_by/2 / take_by/2; Elixir floor raised to ~> 1.15
  • StreamData property suite (4 properties), 100% line coverage
  • Recipes-first README (every snippet doctest-backed), 2.0.0 CHANGELOG with 1.x migration table
  • CI: matrix trimmed to Elixir 1.15–1.20, new quality-gate job (format check, credo --strict, coveralls)

Test plan

  • mix test — 12 doctests, 4 properties, 37 tests, 0 failures
  • MIX_ENV=test mix coveralls — 100.0%
  • mix format --check-formatted, mix credo --strict clean
  • mix docs builds without warnings
  • Both CI jobs green on this PR

treble37 added 9 commits July 5, 2026 22:27
Scaffolding for the v2.0 Trustworthy Core release — no behavior change;
all 12 v1 tests pass unchanged.

- Add .formatter.exs and format lib/, test/, and mix.exs
- Add stream_data ~> 1.1 (test/dev only) for upcoming property tests
- Raise Elixir requirement from >= 1.9.0 to ~> 1.15
- Bump version to 2.0.0 and rewrite the package description around
  the structure-preserving, zero-dependency positioning
Introduce the documented predicate engine for removals: depth-first
traversal where the predicate receives each key and its already-cleaned
value, empty maps left by rejection are preserved, and non-container
list elements are untouched. Non-map/non-list input passes through
unchanged.

Structs are handled explicitly via the structs: option shared by the
whole v2 API: :leaf (default) treats them as opaque values, :convert
recurses via Map.from_struct/1, :error raises ArgumentError naming the
struct module.

drop_by_value and drop_by_key are now sugar delegating to reject/3 and
gain an optional opts argument. All 9 v1 drop_by_* tests pass unchanged,
proving 1.x compatibility of the drop path. New coverage: 11 unit tests
and doctests for reject/3 and both sugar functions.
The take-side counterpart to reject/3. Matched entries are kept whole
(no recursion into a matched value); unmatched container entries survive
only if they have surviving descendants; branches and list elements with
no surviving content are pruned. Matches stay at the path where they
were found — sibling branches are never merged, fixing the class of
silent data loss take_by_key exhibits in 1.x.

Shares the structs: option semantics with reject/3 (:leaf default,
:convert, :error). Pins the spec's semantics-contract cases in 12 unit
tests plus doctests: cross-branch key collisions, list-of-maps, scalars
in lists, mixed atom/string keys, struct modes, and pass-through of
non-map/non-list input.
take_by_key is now sugar over filter/3: matches stay at the path where
they were found, sibling branches are never merged, and branches without
a match are pruned. This fixes the 1.x behavior of flattening all
matches into a single-level map, which silently lost data whenever the
same key appeared in more than one branch (undefined collision winner).

The three 1.x tests that encoded the flattening behavior are replaced
with structure-preserving expectations, including the spec's headline
case: take_by_key(%{a: %{x: 1}, b: %{x: 2}}, [:x]) returns the map
unchanged. take_by_key also gains the opts argument and forwards the
structs: option.
Both were public but undocumented and are superseded by the documented
engine: drop_by/2 by reject/3, take_by/2 by filter/3. The public surface
is now exactly reject/3, filter/3, drop_by_value/3, drop_by_key/3, and
take_by_key/3.

Also replace the placeholder moduledoc with one describing the v2
engine/sugar split and the structure-preservation guarantee.
@treble37 treble37 marked this pull request as ready for review July 6, 2026 19:05
@treble37 treble37 merged commit cc61f06 into main Jul 9, 2026
7 checks passed
@treble37 treble37 deleted the v2-dev branch July 9, 2026 04:47
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