once_new 0.0.1
once_new: ^0.0.1 copied to clipboard
Run a block once, or at most once per hour / day / week / month / year / app version. The bookkeeping lives in SharedPreferences; there is nothing to initialize.
0.0.1 #
Initial release.
- Added
Once.claim, the ask-and-record gate: the call that answerstruealso writes the mark, so there is no separate bookkeeping step to forget. Concurrent calls on the same key are serialized, so only one of them opens the gate. - Added
OncePolicy, a sealed set of typed policies:once(),every(Duration),everyDay(),everyMonth(),everyYear(), andonChange(token, runOnFirstSeen:). Calendar policies store a fullyyyy-MM-dd/yyyy-MM/yyyybucket, andonChangecompares tokens for inequality, so a rolled-back version fires too. - Added
Once.warmUp()plus the synchronousOnce.claimSyncandOnce.isDueSync, for deciding insidebuildwithout rendering a "don't know yet" frame.claimSyncthrows aStateErrorwhen the cache was never warmed instead of silently answering "not done". - Added
Once.run, returningOnceResultwith a separateranflag, so "the callback returned null" and "the gate was closed" are distinguishable. - Added
Once.markDone,Once.clear, andOnce.clearAll.clearAllonly removes keys under theonce_new.prefix, matched withstartsWith. - Added
Once.flush(). All persistence goes through a single queue, so the writeclaimSyncfires off cannot land after a laterclearand resurrect the record;flushawaits that queue when the disk needs to be settled. - A policy that sees a key for the first time records its anchor even when it does not fire, so
onChange(token, runOnFirstSeen: false)seeds the current token on a fresh install instead of staying in the "never seen" state forever. - Added the
OnceStoreinterface withSharedPreferencesOnceStoreandMemoryOnceStore, plusOnce.debugOverride/Once.debugResetfor injecting a store and a clock, so the decision logic time-travels in plain unit tests. - Records are persisted with the policy tag alongside the anchor, so changing a key's policy starts a new record instead of misreading the old one. Unparseable values are treated as "no record" rather than throwing.
every(Duration)treats an anchor in the future as expired, so a backwards system clock or an NTP step doesn't lock the gate until real time catches up.- Only depends on
shared_preferences; version tokens are passed in by the caller rather than pulling inpackage_info_plus.