diff --git a/periodic-core/src/main/scala/ca/dvgi/periodic/AutoUpdatingVar.scala b/periodic-core/src/main/scala/ca/dvgi/periodic/AutoUpdatingVar.scala index d98485f..b91c015 100644 --- a/periodic-core/src/main/scala/ca/dvgi/periodic/AutoUpdatingVar.scala +++ b/periodic-core/src/main/scala/ca/dvgi/periodic/AutoUpdatingVar.scala @@ -36,6 +36,9 @@ import java.util.concurrent.ScheduledExecutorService * @param handleInitializationError * A PartialFunction used to recover from exceptions in the var initialization. If unspecified, * the exception will fail the effect returned by `ready`. + * @param updateExistingVar + * If specified, will be used to update the var on all updates after initialization. Allows for + * the use of the current var when updating. * @param varNameOverride * A name for this variable, used in logging. If unspecified, the simple class name of T will be * used. @@ -46,6 +49,7 @@ class AutoUpdatingVar[U[_], R[_], T](periodic: Periodic[U, R])( updateAttemptStrategy: AttemptStrategy, blockUntilReadyTimeout: Option[Duration] = None, handleInitializationError: PartialFunction[Throwable, U[T]] = PartialFunction.empty, + updateExistingVar: Option[T => U[T]] = None, varNameOverride: Option[String] = None )(implicit ct: ClassTag[T]) extends AutoCloseable { @@ -71,7 +75,10 @@ class AutoUpdatingVar[U[_], R[_], T](periodic: Periodic[U, R])( log, "update var", updateInterval.duration(newV), - () => updateVar, + updateExistingVar match { + case Some(u) => () => u(variable.get) // will always be set at this point + case None => () => updateVar + }, v => variable = Some(v), v => updateInterval.duration(v), updateAttemptStrategy @@ -115,6 +122,7 @@ object AutoUpdatingVar { updateAttemptStrategy: AttemptStrategy, blockUntilReadyTimeout: Option[Duration] = None, handleInitializationError: PartialFunction[Throwable, U[T]] = PartialFunction.empty, + updateExistingVar: Option[T => U[T]] = None, varNameOverride: Option[String] = None )(implicit ct: ClassTag[T]): AutoUpdatingVar[U, R, T] = { new AutoUpdatingVar(periodic)( @@ -123,6 +131,7 @@ object AutoUpdatingVar { updateAttemptStrategy, blockUntilReadyTimeout, handleInitializationError, + updateExistingVar, varNameOverride ) } @@ -140,6 +149,7 @@ object AutoUpdatingVar { updateAttemptStrategy: AttemptStrategy, blockUntilReadyTimeout: Option[Duration] = None, handleInitializationError: PartialFunction[Throwable, T] = PartialFunction.empty, + updateExistingVar: Option[T => T] = None, varNameOverride: Option[String] = None, executorOverride: Option[ScheduledExecutorService] = None )(implicit ct: ClassTag[T]): AutoUpdatingVar[Identity, Future, T] = { @@ -151,6 +161,7 @@ object AutoUpdatingVar { updateAttemptStrategy, blockUntilReadyTimeout, handleInitializationError, + updateExistingVar, varNameOverride ) } @@ -168,6 +179,7 @@ object AutoUpdatingVar { updateAttemptStrategy: AttemptStrategy, blockUntilReadyTimeout: Option[Duration] = None, handleInitializationError: PartialFunction[Throwable, Future[T]] = PartialFunction.empty, + updateExistingVar: Option[T => Future[T]] = None, varNameOverride: Option[String] = None, executorOverride: Option[ScheduledExecutorService] = None )(implicit ct: ClassTag[T]): AutoUpdatingVar[Future, Future, T] = { @@ -179,6 +191,7 @@ object AutoUpdatingVar { updateAttemptStrategy, blockUntilReadyTimeout, handleInitializationError, + updateExistingVar, varNameOverride ) } diff --git a/periodic-core/src/test/scala/ca/dvgi/periodic/AutoUpdaterTestsFuture.scala b/periodic-core/src/test/scala/ca/dvgi/periodic/AutoUpdaterTestsFuture.scala index 7d2a1ae..5adc284 100644 --- a/periodic-core/src/test/scala/ca/dvgi/periodic/AutoUpdaterTestsFuture.scala +++ b/periodic-core/src/test/scala/ca/dvgi/periodic/AutoUpdaterTestsFuture.scala @@ -37,6 +37,8 @@ trait AutoUpdatingVarTestsFuture[U[_]] extends FunSuite { implicit val per = periodic testBasicsWithBlocking() + testUpdateExistingVar() + testAdjustsUpdateInterval() testReturnsFailedReady() @@ -94,6 +96,39 @@ trait AutoUpdatingVarTestsFuture[U[_]] extends FunSuite { } } + def testUpdateExistingVar( + )(implicit + loc: munit.Location, + periodic: () => Periodic[U, Future] + ): Unit = { + FunFixture( + _ => { + val holder = new VarHolder + val v = new AutoUpdatingVar(periodic())( + holder.get, + UpdateInterval.Static(1.seconds), + AttemptStrategy.Infinite(1.second), + Some(1.second), + updateExistingVar = Some((i: Int) => { assertEquals(i, 1); pureU(100) }) + ) + (v, holder) + }, + (f: (AutoCloseable, VarHolder)) => f._1.close() + ) + .test("uses updateExistingVar after initialization if specified") { case (v, holder) => + assert(v.ready.isCompleted) + assertEquals(v.ready.value, Some(Success(()))) + + assertEquals(v.latest, 1) + + Thread.sleep(1100) + + assertEquals(v.latest, 100) + + assertEquals(evalU(holder.get), 2) + } + } + def testAdjustsUpdateInterval( )(implicit loc: munit.Location,