Skip to content

Add Scope plumbing and custom operator extension point - #4047

Merged
ajpallares merged 2 commits into
mainfrom
pallares/rules-engine-enable-custom-operators
Aug 21, 2026
Merged

Add Scope plumbing and custom operator extension point#4047
ajpallares merged 2 commits into
mainfrom
pallares/rules-engine-enable-custom-operators

Conversation

@ajpallares

@ajpallares ajpallares commented Aug 20, 2026

Copy link
Copy Markdown
Member

Checklist

  • If applicable, unit tests
  • If applicable, create follow-up issues for purchases-ios and hybrids

Motivation

Port of RevenueCat/purchases-ios#7433, the first of the rules-engine custom operator PRs. Custom rc.* operators need somewhere to live, and rc.rootVar needs the predicate's root data to survive iteration, which today is lost as soon as some / map / reduce rebind the scope to the current item.

Description

  • Thread a Scope (current data plus the never-replaced root) through the evaluator instead of a bare Value, and delegate unknown operators to an empty CustomOperators dispatcher.
  • Behavior-preserving: var still sees only the current item inside iteration, no fixtures were added or changed, and the pinned fixture count stays at 382.

Notes

The @Suppress("UnusedParameter") on the empty dispatcher is temporary — args and vars become used as soon as the first rc.* operator lands.


Note

Medium Risk
Touches the rules-engine evaluator and every operator signature, so a scope mix-up could change predicate results. Intended to be behavior-preserving for existing var/iteration semantics.

Overview
Prepares the rules engine for RevenueCat rc.* operators by replacing a bare data Value with a Scope that keeps both current data and the original root.

Iteration (some/all/none/map/filter/reduce) now rebinds only current via scoped(), so var still sees the item (or reduce’s {current, accumulator}) while custom ops can later read top-level data. Unknown operators fall through to an empty CustomOperators dispatcher (still UnsupportedOperator). firstArgEvaluated moves onto Operators.

No new operator behavior; tests wrap existing calls in Scope.

Reviewed by Cursor Bugbot for commit fa1fa3c. Bugbot is set up for automated code reviews on this repo. Configure here.

ajpallares and others added 2 commits August 20, 2026 18:28
Thread evaluation scope through the rules engine and delegate unknown
operators to an empty CustomOperators dispatcher, without changing behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
Only detekt's UnusedParameter fails the build. The Kotlin compiler doesn't
warn about unused parameters on non-private functions, and the project
doesn't set allWarningsAsErrors, so the compiler-level suppressions were
silencing nothing.

Co-authored-by: Cursor <cursoragent@cursor.com>
@codecov

codecov Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.65217% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 83.14%. Comparing base (bc89182) to head (fa1fa3c).

Files with missing lines Patch % Lines
.../revenuecat/purchases/rules/operators/Operators.kt 66.66% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4047      +/-   ##
==========================================
+ Coverage   83.11%   83.14%   +0.02%     
==========================================
  Files         437      439       +2     
  Lines       17930    17937       +7     
  Branches     2688     2688              
==========================================
+ Hits        14903    14913      +10     
+ Misses       2145     2143       -2     
+ Partials      882      881       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@tonidero tonidero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@ajpallares
ajpallares added this pull request to the merge queue Aug 21, 2026
Merged via the queue into main with commit 40e3de6 Aug 21, 2026
36 checks passed
@ajpallares
ajpallares deleted the pallares/rules-engine-enable-custom-operators branch August 21, 2026 12:14
ajpallares added a commit that referenced this pull request Aug 21, 2026
#4047 landed on main as a squashed commit, so its Scope plumbing existed
twice. All three conflicts were the rc.rootVar additions layered on identical
#4047 content, so this branch's side was kept: the dispatch case, the shared
resolveVar with its operatorName parameter, and the iteration docs naming
rc.rootVar.

Co-authored-by: Cursor <cursoragent@cursor.com>
ajpallares added a commit that referenced this pull request Aug 24, 2026
main gained the Scope plumbing (#4047) and rc.rootVar (#4049), which moved
the lookup this branch changes into a resolveVar shared by var and
rc.rootVar. The merge is therefore semantic, not just textual:

- AccessorOperators: keep main's opVar/resolveVar split and re-apply the
  throw at the one point where the miss is decided, so rc.rootVar inherits
  it. The KDoc keeps both sides' paragraphs.
- rc_root_var.json: adopt the version from the iOS branch, where two
  fixtures that pinned a missing root key as null now pin the error. Still
  byte-identical to iOS.
- AccessorOperatorsTest: main's Scope-wrapped call with this branch's
  expectation that the fractional path throws rather than warns.
- PredicateFixtureLoaderTest: recomputed from disk as 413.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants