Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ jobs:
- uses: olafurpg/setup-scala@v13
- uses: olafurpg/setup-gpg@v3
- name: Cache dependencies
uses: actions/cache@v3.0.1
uses: actions/cache@v4
with:
path: ~/.cache/coursier/v1
key: ${{ runner.os }}-coursier-${{ hashFiles('build.sbt') }}-${{ hashFiles('project/*.scala') }}
restore-keys: ${{ runner.os }}-coursier-
- name: Cache .sbt
uses: actions/cache@v3.0.1
uses: actions/cache@v4
with:
path: ~/.sbt
key: ${{ runner.os }}-sbt-${{ hashFiles('build.sbt') }}-${{ hashFiles('project/*.scala') }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/scala.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ jobs:
- name: Checking your code format
run: sbt +scalafmtCheck
- name: Cache dependencies
uses: actions/cache@v3.0.1
uses: actions/cache@v4
with:
path: ~/.cache/coursier/v1
key: ${{ runner.os }}-coursier-${{ hashFiles('build.sbt') }}-${{ hashFiles('project/*.scala') }}
restore-keys: ${{ runner.os }}-coursier-
- name: Cache .sbt
uses: actions/cache@v3.0.1
uses: actions/cache@v4
with:
path: ~/.sbt
key: ${{ runner.os }}-sbt-${{ hashFiles('build.sbt') }}-${{ hashFiles('project/*.scala') }}
Expand Down
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = 3.5.8
version = 3.10.6
style = default
align {
preset = "more"
Expand Down
30 changes: 20 additions & 10 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import sbt.Keys._
import sbt._
import sbt.Keys.*
import sbt.*
// shadow sbt-scalajs' crossProject and CrossType from Scala.js 0.6.x
import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType}
import Util._
import Util.*

ThisBuild / scalafmtOnCompile := true

Global / onChangedBuildSource := ReloadOnSourceChanges

ThisBuild / scalaVersion := "2.13.15"
ThisBuild / crossScalaVersions := Seq("2.13.15", "3.1.3")
ThisBuild / scalaVersion := "2.13.17"
ThisBuild / crossScalaVersions := Seq("2.13.17", "3.7.4")

val commonSettings = Seq(
scalacOptions := Seq(
semanticdbEnabled := true,
semanticdbVersion := scalafixSemanticdb.revision,
scalacOptions := Seq(
"-deprecation",
"-encoding",
"UTF-8",
Expand All @@ -22,13 +24,21 @@ val commonSettings = Seq(
scalacOptions ++= scalaVerDependentSeq {
case (2, _) =>
Seq(
"-Xsource:3",
"-Wconf:cat=scala3-migration:w",
"-Xlint",
"-Ywarn-dead-code",
"-Ywarn-numeric-widen",
"-Ywarn-value-discard",
"-language:experimental.macros",
"-language:existentials"
)
case (3, _) =>
// Suppress warnings for cross-compiled code that can't use Scala 3 syntax
Seq(
"-Wconf:msg=Implicit parameters should be provided:s",
"-Wconf:msg=has been deprecated.*uninitialized:s"
)
}.value,
scalacOptions ++= scalaVerDependentSeq {
case (2, 13) => Seq("-Werror")
Expand All @@ -55,11 +65,11 @@ inThisBuild(
homepage := Some(url("https://github.com/suzaku-io/diode")),
licenses := List("Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0")),
sonatypeProfileName := "io.suzaku",
developers := List(
developers := List(
Developer("ochrons", "Otto Chrons", "", url("https://github.com/ochrons"))
),
organization := "io.suzaku",
scmInfo := Some(
scmInfo := Some(
ScmInfo(
url("https://github.com/suzaku-io/diode"),
"scm:git:git@github.com:suzaku-io/diode.git",
Expand Down Expand Up @@ -87,7 +97,7 @@ val commonJsSettings = Seq(
scalacOptions += sourceMapSetting.value,
scalacOptions ++= scalaVerDependent {
case (2, _) => "-P:scalajs:nowarnGlobalExecutionContext"
case (3, _) => "-scalajs:nowarnGlobalExecutionContext"
case (3, _) => "-scalajs"
}.value
)

Expand Down Expand Up @@ -147,7 +157,7 @@ lazy val diodeReact: Project = project
.settings(
name := "diode-react",
libraryDependencies ++= Seq(
"com.github.japgolly.scalajs-react" %%% "core" % "2.1.1"
"com.github.japgolly.scalajs-react" %%% "core" % "3.0.0"
)
)
.dependsOn(diode.js)
2 changes: 1 addition & 1 deletion diode-core/js/src/main/scala/diode/util/RunAfterJS.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import scala.concurrent.duration.FiniteDuration
import scala.scalajs.js.timers._

class RunAfterJS extends RunAfter {
override def runAfter[A](delay: FiniteDuration)(f: => A) = {
override def runAfter[A](delay: FiniteDuration)(f: => A): Future[A] = {
val p = Promise[A]()
setTimeout(delay)(p.success(f))
p.future
Expand Down
4 changes: 2 additions & 2 deletions diode-core/jvm/src/main/scala/diode/util/RunAfterJVM.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import scala.concurrent._
import scala.concurrent.duration.FiniteDuration

class RunAfterJVM extends RunAfter {
override def runAfter[A](delay: FiniteDuration)(f: => A) = {
val p = Promise[A]()
override def runAfter[A](delay: FiniteDuration)(f: => A): Future[A] = {
val p = Promise[A]()
val task = new Runnable {
def run() = p.success(f)
}
Expand Down
8 changes: 4 additions & 4 deletions diode-core/jvm/src/test/scala/diode/CircuitJVMTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ object CircuitJVMTests extends TestSuite {

class TestCircuit extends Circuit[Model] {
import diode.ActionResult._
override def initialModel = Model(Vector.empty)
override def initialModel = Model(Vector.empty)
override protected def actionHandler: HandlerFunction =
(model, action) =>
({
Expand All @@ -41,7 +41,7 @@ object CircuitJVMTests extends TestSuite {
}: PartialFunction[Any, ActionResult[Model]]).lift.apply(action)
}

def tests = TestSuite {
def tests = Tests {
def circuit = new TestCircuit

"ParallelActions" - {
Expand Down Expand Up @@ -97,14 +97,14 @@ object CircuitJVMTests extends TestSuite {
"SequenceActions" - {
val c = circuit
val actions = for (i <- 0 until 1000) yield Append(i)
c.dispatch(ActionBatch(actions: _*))
c.dispatch(ActionBatch(actions*))
assert(c.model.list.size == 1000)
assert(c.model.list == Vector.range(0, 1000))
}
"SequenceActionEffects" - {
val c = circuit
val actions = for (i <- 0 until 1000) yield RunEffects(Seq(() => Future(Append(i))))
c.dispatch(ActionBatch(actions: _*))
c.dispatch(ActionBatch(actions*))
// wait for futures to complete
Thread.sleep(300)
assert(c.model.list.size == 1000)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,5 @@ import scala.collection.BuildFrom

trait EffectSetExecutionOps { self: EffectSet =>
private[diode] def executeWith[A](f: Effect => Future[A]): Future[Set[A]] =
Future.traverse(tail + head)(f(_))(
summon[BuildFrom[Set[Effect], A, Set[A]]],
(EffectSetExecutionOps.this: diode.EffectSet).ec
)
Future.traverse(tail + head)(f(_))(using summon[BuildFrom[Set[Effect], A, Set[A]]], self.ec)
}
2 changes: 1 addition & 1 deletion diode-core/shared/src/main/scala-3/diode/ZoomTo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ trait ZoomTo[M, S] {
* @param set
* Function to update the model with a new value
*/
def zoomRW[T](get: S => T)(set: (S, T) => S)(implicit feq: FastEq[_ >: T]): ModelRW[M, T]
def zoomRW[T](get: S => T)(set: (S, T) => S)(implicit feq: FastEq[? >: T]): ModelRW[M, T]

/**
* An easier way to zoom into a RW model by just specifying a single chained accessor for the field. This works for cases
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ private[macros] object CaseClass {
if (obj.tpe.typeSymbol.caseFields.isEmpty) {
Left(s"Type ${obj.tpe.typeSymbol.fullName} of ${obj.tpe.show} field must be a case class.")
} else {
Right(new CaseClass[q.type]()(obj))
Right(new CaseClass[q.type](obj))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ private[diode] object GenLens {
field.asTerm match {
case Inlined(_, _, Block(List(DefDef(_, _, _, Some(fieldChain))), _)) =>
val setExpr = generateSetExpression(fieldChain)
'{ $zoomer.zoomRW($field)($setExpr)($feq) }
'{ $zoomer.zoomRW($field)($setExpr)(using $feq) }

case _ => reportIllegalFieldReference()
}
Expand Down
2 changes: 1 addition & 1 deletion diode-core/shared/src/main/scala/diode/ActionHandler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,6 @@ abstract class ActionHandler[M, T](val modelRW: ModelRW[M, T]) {
}

object ActionHandler {
implicit def extractHandler[M <: AnyRef](actionHandler: ActionHandler[M, _]): (M, Any) => Option[ActionResult[M]] =
implicit def extractHandler[M <: AnyRef](actionHandler: ActionHandler[M, ?]): (M, Any) => Option[ActionResult[M]] =
actionHandler.handleAction
}
23 changes: 12 additions & 11 deletions diode-core/shared/src/main/scala/diode/Circuit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ trait ActionType[-A]
trait Dispatcher {
def dispatch[A: ActionType](action: A): Unit

def apply[A: ActionType](action: A) = dispatch(action)
def apply[A: ActionType](action: A): Unit = dispatch(action)
}

/**
Expand Down Expand Up @@ -142,13 +142,13 @@ trait Circuit[M <: AnyRef] extends Dispatcher with ZoomTo[M, M] {
private var isDispatching = false
private var dispatchQueue = Queue.empty[Any]
private var listenerId = 0
private var listeners = Map.empty[Int, Subscription[_]]
private var listeners = Map.empty[Int, Subscription[?]]
private var processors = List.empty[ActionProcessor[M]]
private var processChain = buildProcessChain

private def buildProcessChain = {
// chain processing functions
processors.reverse.foldLeft(process _)((next, processor) =>
processors.reverse.foldLeft(process(_))((next, processor) =>
(action: Any) => processor.process(this, action, next, model)
)
}
Expand All @@ -161,13 +161,13 @@ trait Circuit[M <: AnyRef] extends Dispatcher with ZoomTo[M, M] {
* @return
* A `ModelR[T]` giving you read-only access to part of the model
*/
def zoom[T](get: M => T)(implicit feq: FastEq[_ >: T]): ModelR[M, T] =
def zoom[T](get: M => T)(implicit feq: FastEq[? >: T]): ModelR[M, T] =
modelRW.zoom[T](get)

def zoomMap[F[_], A, B](fa: M => F[A])(f: A => B)(implicit monad: Monad[F], feq: FastEq[_ >: B]): ModelR[M, F[B]] =
def zoomMap[F[_], A, B](fa: M => F[A])(f: A => B)(implicit monad: Monad[F], feq: FastEq[? >: B]): ModelR[M, F[B]] =
modelRW.zoomMap(fa)(f)

def zoomFlatMap[F[_], A, B](fa: M => F[A])(f: A => F[B])(implicit monad: Monad[F], feq: FastEq[_ >: B]): ModelR[M, F[B]] =
def zoomFlatMap[F[_], A, B](fa: M => F[A])(f: A => F[B])(implicit monad: Monad[F], feq: FastEq[? >: B]): ModelR[M, F[B]] =
modelRW.zoomFlatMap(fa)(f)

/**
Expand All @@ -180,16 +180,16 @@ trait Circuit[M <: AnyRef] extends Dispatcher with ZoomTo[M, M] {
* @return
* A `ModelRW[T]` giving you read/update access to part of the model
*/
def zoomRW[T](get: M => T)(set: (M, T) => M)(implicit feq: FastEq[_ >: T]): ModelRW[M, T] = modelRW.zoomRW(get)(set)
def zoomRW[T](get: M => T)(set: (M, T) => M)(implicit feq: FastEq[? >: T]): ModelRW[M, T] = modelRW.zoomRW(get)(set)

def zoomMapRW[F[_], A, B](fa: M => F[A])(f: A => B)(
set: (M, F[B]) => M
)(implicit monad: Monad[F], feq: FastEq[_ >: B]): ModelRW[M, F[B]] =
)(implicit monad: Monad[F], feq: FastEq[? >: B]): ModelRW[M, F[B]] =
modelRW.zoomMapRW(fa)(f)(set)

def zoomFlatMapRW[F[_], A, B](fa: M => F[A])(f: A => F[B])(
set: (M, F[B]) => M
)(implicit monad: Monad[F], feq: FastEq[_ >: B]): ModelRW[M, F[B]] =
)(implicit monad: Monad[F], feq: FastEq[? >: B]): ModelRW[M, F[B]] =
modelRW.zoomFlatMapRW(fa)(f)(set)

/**
Expand Down Expand Up @@ -309,7 +309,7 @@ trait Circuit[M <: AnyRef] extends Dispatcher with ZoomTo[M, M] {
}

@deprecated("Use composeHandlers or foldHandlers instead", "0.5.1")
def combineHandlers(handlers: HandlerFunction*): HandlerFunction = composeHandlers(handlers: _*)
def combineHandlers(handlers: HandlerFunction*): HandlerFunction = composeHandlers(handlers*)

/**
* Folds multiple handlers into a single function so that each handler is called in turn and an updated model is passed on
Expand Down Expand Up @@ -391,10 +391,11 @@ trait Circuit[M <: AnyRef] extends Dispatcher with ZoomTo[M, M] {
isDispatching = false
}
// if there is an item in the queue, dispatch it
import AnyAction._
dispatchQueue.dequeueOption foreach {
case (nextAction, queue) =>
dispatchQueue = queue
dispatch(nextAction)(null)
dispatch(nextAction)
}
} else {
// add to the queue
Expand Down
24 changes: 12 additions & 12 deletions diode-core/shared/src/main/scala/diode/Effect.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package diode

import diode.util.RunAfter

import scala.concurrent._
import scala.concurrent.*
import scala.concurrent.duration.FiniteDuration
import scala.language.implicitConversions

Expand Down Expand Up @@ -99,7 +99,7 @@ abstract class EffectBase(val ec: ExecutionContext) extends Effect { self =>
* The effect function, returning a `Future[A]`
*/
class EffectSingle[A] private[diode] (f: () => Future[A], ec: ExecutionContext) extends EffectBase(ec) {
override def run(dispatch: Any => Unit) = f().map(dispatch)(ec)
override def run(dispatch: Any => Unit): Future[Unit] = f().map(dispatch)(ec)

override def toFuture: Future[A] = f()
}
Expand All @@ -119,7 +119,7 @@ class EffectSeq(head: Effect, tail: Seq[Effect], ec: ExecutionContext) extends E
prev.flatMap(_ => f(effect))(ec)
}

override def run(dispatch: Any => Unit) =
override def run(dispatch: Any => Unit): Future[Unit] =
executeWith(_.run(dispatch))

override def >>(that: Effect) =
Expand All @@ -128,16 +128,16 @@ class EffectSeq(head: Effect, tail: Seq[Effect], ec: ExecutionContext) extends E
override def <<(that: Effect) =
new EffectSeq(that, head +: tail, ec)

override def size =
override def size: Int =
head.size + tail.foldLeft(0)((acc, e) => acc + e.size)

override def toFuture =
override def toFuture: Future[Any] =
executeWith(_.toFuture)

override def map[B: ActionType](g: Any => B) =
override def map[B: ActionType](g: Any => B): EffectSeq =
new EffectSeq(head.map(g), tail.map(_.map(g)), ec)

override def flatMap[B: ActionType](g: Any => Future[B]) =
override def flatMap[B: ActionType](g: Any => Future[B]): EffectSeq =
new EffectSeq(head.flatMap(g), tail.map(_.flatMap(g)), ec)
}

Expand All @@ -153,22 +153,22 @@ class EffectSet(val head: Effect, val tail: Set[Effect], ec: ExecutionContext)
extends EffectBase(ec)
with EffectSetExecutionOps {

override def run(dispatch: Any => Unit) =
override def run(dispatch: Any => Unit): Future[Unit] =
executeWith(_.run(dispatch)).map(_ => ())(ec)

override def +(that: Effect) =
new EffectSet(head, tail + that, ec)

override def size =
override def size: Int =
head.size + tail.foldLeft(0)((acc, e) => acc + e.size)

override def toFuture =
override def toFuture: Future[Set[Any]] =
executeWith(_.toFuture)

override def map[B: ActionType](g: Any => B) =
override def map[B: ActionType](g: Any => B): EffectSet =
new EffectSet(head.map(g), tail.map(_.map(g)), ec)

override def flatMap[B: ActionType](g: Any => Future[B]) =
override def flatMap[B: ActionType](g: Any => Future[B]): EffectSet =
new EffectSet(head.flatMap(g), tail.map(_.flatMap(g)), ec)
}

Expand Down
Loading