Skip to content

Commit

Permalink
feature: update to support both scala 3 and scala 2.13
Browse files Browse the repository at this point in the history
  • Loading branch information
cloudmark committed Jul 16, 2024
1 parent dfe45cb commit 4c68b84
Show file tree
Hide file tree
Showing 21 changed files with 146 additions and 125 deletions.
10 changes: 8 additions & 2 deletions .github/workflows/release-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name: Publish Package on Release
on:
release:
types: [published]
workflow_dispatch:

jobs:
publish:
Expand Down Expand Up @@ -39,7 +40,12 @@ jobs:
mkdir -p ~/.sbt/1.0
echo "credentials += Credentials(\"GitHub Package Registry\", \"maven.pkg.github.com\", \"${{ github.actor }}\", \"${{ secrets.GITHUB_TOKEN }}\")" >> ~/.sbt/1.0/global.sbt
- name: Build and publish
run: sbt clean compile publish
- name: Build and publish for Scala 2.13
run: sbt ++2.13.12 clean compile publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Build and publish for Scala 3
run: sbt ++3.3.0 clean compile publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
51 changes: 34 additions & 17 deletions .github/workflows/scala.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: Scala CI

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
workflow_dispatch:

permissions:
contents: read
Expand All @@ -23,17 +19,38 @@ jobs:
pull-requests: write
repository-projects: write
steps:
- uses: actions/checkout@v4
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: 'sbt'
- name: Run tests
run: sbt test
# Optional: This step uploads information to the GitHub dependency graph and unblocking Dependabot alerts for the repository
- name: Sbt Dependency Submission
uses: scalacenter/sbt-dependency-submission@v3.0.1
- uses: actions/checkout@v4
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: 'sbt'

- name: Cache sbt
uses: actions/cache@v2
with:
path: ~/.sbt
key: ${{ runner.os }}-sbt-${{ hashFiles('**/build.sbt') }}
restore-keys: |
${{ runner.os }}-sbt-
- name: Cache Coursier
uses: actions/cache@v2
with:
path: ~/.coursier
key: ${{ runner.os }}-coursier-${{ hashFiles('**/build.sbt') }}
restore-keys: |
${{ runner.os }}-coursier-
- name: Run tests for Scala 2.13
run: sbt ++2.13.12 clean compile test

- name: Run tests for Scala 3
run: sbt ++3.3.0 clean compile test

- name: Sbt Dependency Submission for Scala 2.13
uses: scalacenter/sbt-dependency-submission@v3.0.1

- name: Sbt Dependency Submission for Scala 3
uses: scalacenter/sbt-dependency-submission@v3.0.1
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,5 @@ project/**/metals.sbt

.bsp
.history
scala2/target
scala3/target
90 changes: 58 additions & 32 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,41 +1,67 @@
ThisBuild / version := "2.0.0-RC1"

ThisBuild / scalaVersion := "2.13.12"

addCompilerPlugin("org.typelevel" % "kind-projector" % "0.13.2" cross CrossVersion.full)

ThisBuild / organization := "com.suprnation"
ThisBuild / name := "cats-actors"
ThisBuild / version := "2.0.0-RC2"
ThisBuild / organizationName := "SuprNation"
ThisBuild / startYear := Some(2024)
ThisBuild / licenses += ("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0.txt"))

organizationName := "SuprNation"
startYear := Some(2024)
licenses += ("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0.txt"))
ThisBuild / crossScalaVersions := Seq("2.13.12", "3.3.0")
ThisBuild / scalaVersion := crossScalaVersions.value.head

Test / parallelExecution := false
lazy val commonSettings = Seq(
Test / parallelExecution := false,
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-effect" % "3.5.2",
"org.scalatest" %% "scalatest" % "3.2.19" % Test
),
publishTo := {
val owner = "suprnation"
val repo = "cats-actors"
if (isSnapshot.value)
Some("GitHub Package Registry".at(s"https://maven.pkg.github.com/$owner/$repo"))
else
Some("GitHub Package Registry".at(s"https://maven.pkg.github.com/$owner/$repo"))
}

)

scalacOptions ++= Seq( // use ++= to add to existing options
"-deprecation",
"-encoding",
"utf8", // if an option takes an arg, supply it on the same line
"-feature", // then put the next option on a new line for easy editing
"-language:implicitConversions",
"-language:existentials",
"-unchecked",
"-Werror",
"-Xlint" // exploit "trailing comma" syntax so you can add an option without editing this line
lazy val scala2Settings = Seq(
libraryDependencies += "org.typelevel" %% "kind-projector" % "0.13.2" cross CrossVersion.full,
scalacOptions ++= Seq(
"-language:implicitConversions",
"-language:existentials"
)
)

libraryDependencies ++= Seq(
"org.typelevel" %% "cats-effect" % "3.5.2", // if needed for other dependencies
"org.typelevel" %% "cats-effect" % "3.5.0",
"org.scalatest" %% "scalatest" % "3.2.18" % Test
lazy val scala3Settings = Seq(
// Scala 3 specific settings can be added here
)

publishTo := {
val owner = "suprnation"
val repo = "cats-actors"
if (isSnapshot.value)
Some("GitHub Package Registry".at(s"https://maven.pkg.github.com/$owner/$repo"))
else
Some("GitHub Package Registry".at(s"https://maven.pkg.github.com/$owner/$repo"))
}
lazy val root = (project in file("."))
.settings(commonSettings)
.settings(
// Conditionally apply settings based on Scala version
libraryDependencies ++= (scalaVersion.value match {
case v if v.startsWith("2.") => Seq("org.typelevel" %% "kind-projector" % "0.13.2" cross CrossVersion.full)
case _ => Seq()
}),
scalacOptions ++= (scalaVersion.value match {
case v if v.startsWith("2.") => Seq(
"-deprecation",
"-encoding", "utf8",
"-feature",
"-unchecked",
"-Werror",
"-language:implicitConversions",
"-language:existentials",
"-Xlint"
)
case _ => Seq()
}),
Compile / unmanagedSourceDirectories ++= Seq(
baseDirectory.value / "src" / "main" / "scala"
),
Test / unmanagedSourceDirectories ++= Seq(
baseDirectory.value / "src" / "test" / "scala"
)
)
4 changes: 2 additions & 2 deletions src/main/scala/com/suprnation/EscalatingReplyingActor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
package com.suprnation

import cats.Parallel
import cats.effect.std.Console
import cats.effect.{Async, Concurrent, Sync, Temporal}
import cats.effect._
import cats.effect.std._
import cats.implicits._
import com.suprnation.actor.Actor.ReplyingReceive
import com.suprnation.actor.SupervisorStrategy.Escalate
Expand Down
7 changes: 4 additions & 3 deletions src/main/scala/com/suprnation/actor/Actor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ object ReplyingActor {
): Actor[F, Request] =
withReceive(Behaviour.ignoringBehaviour(name))

def withReceive[F[+_]: Parallel: Concurrent: Temporal, Request, Response](
private def withReceive[F[+_]: Parallel: Concurrent: Temporal, Request, Response](
_receive: PartialFunction[Request, F[Response]]
): ReplyingActor[F, Request, Response] = new ReplyingActor[F, Request, Response] {
override def receive: ReplyingReceive[F, Request, Response] = _receive
Expand Down Expand Up @@ -112,7 +112,7 @@ abstract class ReplyingActor[F[+_]: Concurrent: Parallel: Temporal, Request, Res
// We do this because we do not want a Ref for the context to make the DX easier.
// We also do not want a Ref on self to make the DX easier.
implicit val context: ActorContext[F, Request, Response] = null
final val self: ReplyingActorRef[F, Request, Response] = null
val self: ReplyingActorRef[F, Request, Response] = null
implicit def implicitSelf: Option[ReplyingActorRef[F, Request, Response]] = Option(self)

def init: F[Unit] = Concurrent[F].unit
Expand Down Expand Up @@ -149,7 +149,8 @@ abstract class ReplyingActor[F[+_]: Concurrent: Parallel: Temporal, Request, Res
/** User overridable callback. <p/> Is called when a message isn't handled by the current behaviour of the actor by default it fails with either [[com.suprnation.actor.DeathPactException]] (in case of an unhandled [[com.suprnation.actor.Terminated]] message) or publishes an [[com.suprnation.actor.UnhandledMessage]] to the actor system's event stream.
*/
def unhandled(message: Any): F[Any] = message match {
case Terminated(dead, _) => MonadThrow[F].raiseError(DeathPactException(dead))
case Terminated(dead, _) =>
MonadThrow[F].raiseError(DeathPactException[F](dead.asInstanceOf[NoSendActorRef[F]]))
case _ =>
context.system.eventStream
.offer(UnhandledMessage(message, context.sender, self).toString)
Expand Down
27 changes: 4 additions & 23 deletions src/main/scala/com/suprnation/actor/ActorContext.scala
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,6 @@ object ActorContext {
// Here we will call the parent to handle this..
_self match {
case local: InternalActorRef[F, ?, ?] => local.stop
case unexpected =>
MonadThrow[F].raiseError(
new IllegalArgumentException(s"ActorRef is not internal: $unexpected")
)
}
} else {
Concurrent[F].raiseError(
Expand All @@ -97,41 +93,26 @@ object ActorContext {
self match {
case local: InternalActorRef[F, ?, ?] =>
local.assertCellActiveAndDo(_.watch(actorRef, onTerminated))
case unexpected =>
MonadThrow[F].raiseError(
new IllegalArgumentException(s"ActorRef is not internal: $unexpected")
) // Done for completeness
}

def unwatch(actorRef: NoSendActorRef[F]): F[NoSendActorRef[F]] =
self match {
case local: InternalActorRef[F, ?, ?] =>
local.assertCellActiveAndDo(_.unwatch(actorRef))
case unexpected =>
MonadThrow[F].raiseError(
new IllegalArgumentException(s"ActorRef is not internal: $unexpected")
) // Done for completeness
}

override def setReceiveTimeout(timeout: FiniteDuration, onTimeout: => Request): F[Unit] =
self match {
case local: InternalActorRef[F, ?, ?] =>
local.assertCellActiveAndDo(_.setReceiveTimeout(timeout, onTimeout))
case unexpected =>
MonadThrow[F].raiseError(
new IllegalArgumentException(s"ActorRef is not internal: $unexpected")
) // Done for completeness
}

override val cancelReceiveTimeout: F[Unit] =
Temporal[F].delay(self).flatMap {
case local: InternalActorRef[F, ?, ?] =>
Temporal[F]
.delay(self)
.flatMap((local: InternalActorRef[F, ?, ?]) =>
local.assertCellActiveAndDo(_.cancelReceiveTimeout)
case unexpected =>
MonadThrow[F].raiseError(
new IllegalArgumentException(s"ActorRef is not internal: $unexpected")
) // Done for completeness
}
)

override def replyingActorOf[ChildRequest, ChildResponse](
props: F[ReplyingActor[F, ChildRequest, ChildResponse]],
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/com/suprnation/actor/ChildStats.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ final case class ChildRestartStats[F[+_]](
*/
val retriesDone = maxNrOfRetriesCount + 1
val now = System.nanoTime
val windowStart =
if (restartTimeWindowStartNanos == 0) {
val windowStart: Long =
if (restartTimeWindowStartNanos == 0L) {
restartTimeWindowStartNanos = now
now
} else restartTimeWindowStartNanos
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/com/suprnation/actor/Exceptions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,11 @@ final case class PostRestartException[F[+_]](

/** InvalidMessageException is thrown when an invalid message is sent to an Actor; Currently only `null` is an invalid message.
*/
final case class InvalidMessageException private (message: String) extends AkkaException(message)
final case class InvalidMessageException(message: String) extends AkkaException(message)

/** A DeathPactException is thrown by an Actor that receives a Terminated(someActor) message that it doesn't handle itself, effectively crashing the Actor and escalating to the supervisor.
*/
final case class DeathPactException[F[+_]] private (dead: NoSendActorRef[F])
final case class DeathPactException[F[+_]](dead: NoSendActorRef[F])
extends AkkaException("Monitored actor [" + dead + "] terminated")
with NoStackTrace

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package com.suprnation.actor
import cats.effect.std.{Console, Supervisor}
import cats.effect.{Async, Deferred, Temporal}
import cats.implicits._
import cats.{Applicative, Parallel}
import cats.Applicative
import com.suprnation.actor.Actor.Actor
import com.suprnation.actor.ActorRef.{ActorRef, NoSendActorRef}
import com.suprnation.actor.engine.ActorCell
Expand Down Expand Up @@ -121,7 +121,7 @@ trait ActorRefProvider[F[+_]] {
): F[ReplyingActorRef[F, Request, Response]]
}

class LocalActorRefProvider[F[+_]: Parallel: Async: Temporal: Console](
class LocalActorRefProvider[F[+_]: Async: Temporal: Console](
supervisor: Supervisor[F],
systemShutdownSignal: Deferred[F, Unit],
system: ActorSystem[F],
Expand Down
6 changes: 3 additions & 3 deletions src/main/scala/com/suprnation/actor/ReplyingActorRef.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package com.suprnation.actor
import cats.effect._
import cats.effect.std.{Console, Supervisor}
import cats.syntax.all._
import cats.{Monad, Parallel}
import cats.Monad
import com.suprnation.actor.ActorRef.{ActorRef, NoSendActorRef}
import com.suprnation.actor.engine.ActorCell

Expand Down Expand Up @@ -170,7 +170,7 @@ trait ReplyingActorRef[F[+_], -Request, +Response] {
}

object InternalActorRef {
def apply[F[+_]: Parallel: Async: Temporal: Console, Request, Response](
def apply[F[+_]: Async: Temporal: Console, Request, Response](
supervisor: Supervisor[F],
systemShutdownSignal: Deferred[F, Unit],
name: String,
Expand Down Expand Up @@ -204,7 +204,7 @@ object InternalActorRef {
} yield localActorRef
}

case class InternalActorRef[F[+_]: Parallel: Async: Temporal: Console, Request, Response](
case class InternalActorRef[F[+_]: Async: Temporal: Console, Request, Response](
supervisor: Supervisor[F],
systemShutdownSignal: Deferred[F, Unit],
name: String,
Expand Down
4 changes: 0 additions & 4 deletions src/main/scala/com/suprnation/actor/dungeon/Children.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package com.suprnation.actor.dungeon

import cats.Parallel
import cats.effect.std.{Semaphore, Supervisor}
import cats.effect.{Concurrent, Deferred, Ref}
import cats.implicits._
Expand Down Expand Up @@ -47,9 +46,6 @@ object Children {

trait Children[F[+_], Request, Response] {
self: ActorCell[F, Request, Response] =>

implicit val parallelF: Parallel[F]
implicit val concurrentF: Concurrent[F]
implicit val childrenContext: ChildrenContext[F]

def resumeChildren(causedByFailure: Option[Throwable], perp: Option[NoSendActorRef[F]]): F[Unit] =
Expand Down
4 changes: 1 addition & 3 deletions src/main/scala/com/suprnation/actor/dungeon/Creation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

package com.suprnation.actor.dungeon
import cats.effect.{Async, Concurrent, Temporal}
import cats.effect.{Async, Concurrent}
import cats.implicits._
import com.suprnation.actor.Actor.ReplyingReceive
import com.suprnation.actor.ActorRef.NoSendActorRef
Expand Down Expand Up @@ -53,8 +53,6 @@ trait Creation[F[+_], Request, Response] {

import Creation._

implicit val asyncF: Async[F]
implicit val temporalF: Temporal[F]
implicit val creationContext: CreationContext[F, Request, Response]

def create(failure: Option[ActorInitializationException[F]]): F[Unit] = {
Expand Down
Loading

0 comments on commit 4c68b84

Please sign in to comment.