This is an automated email from the ASF dual-hosted git repository.
He-Pin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko-projection.git
The following commit(s) were added to refs/heads/main by this push:
new 38d83125 refactor: replace deprecated `extends App` with explicit main
method (#560)
38d83125 is described below
commit 38d83125a28059e03b78d4a7d2e9c4e7e79be1da
Author: He-Pin(kerr) <[email protected]>
AuthorDate: Mon Jul 6 16:23:22 2026 +0800
refactor: replace deprecated `extends App` with explicit main method (#560)
Motivation:
`extends App` is deprecated in Scala 3. Replace with explicit `def main`
method for forward compatibility.
Modification:
Replace `object X extends App { ... }` with
`object X { def main(args: Array[String]): Unit = { ... } }`
in 3 example files.
Result:
No more usage of deprecated `scala.App` trait. Code is compatible with
Scala 3.
Tests:
Not run - docs only
References:
None - Scala 3 compatibility
---
.../test/scala/docs/guide/EventGeneratorApp.scala | 210 +++++++++++----------
.../test/scala/docs/guide/ShoppingCartApp.scala | 62 +++---
.../scala/docs/guide/ShoppingCartClusterApp.scala | 70 +++----
3 files changed, 174 insertions(+), 168 deletions(-)
diff --git a/examples/src/test/scala/docs/guide/EventGeneratorApp.scala
b/examples/src/test/scala/docs/guide/EventGeneratorApp.scala
index 29fa7e30..2b550e7c 100644
--- a/examples/src/test/scala/docs/guide/EventGeneratorApp.scala
+++ b/examples/src/test/scala/docs/guide/EventGeneratorApp.scala
@@ -39,109 +39,111 @@ import com.typesafe.config.ConfigFactory
* Generate a shopping cart every 1 second and check it out. Each cart will
contain a variety of `ItemAdded`,
* `ItemQuantityAdjusted` and `ItemRemoved` events preceding the the cart
`Checkout` event.
*/
-object EventGeneratorApp extends App {
- import ShoppingCartEvents._
-
- val Products = List("cat t-shirt", "pekko t-shirt", "skis", "bowling shoes")
-
- val MaxQuantity = 5
- val MaxItems = 3
- val MaxItemsAdjusted = 3
-
- val EntityKey: EntityTypeKey[Event] =
EntityTypeKey[Event]("shopping-cart-event")
-
- val config = ConfigFactory
- .parseString("pekko.actor.provider = cluster")
- .withFallback(ConfigFactory.load("guide-shopping-cart-app.conf"))
-
- ActorSystem(Behaviors.setup[String] {
- ctx =>
- implicit val system = ctx.system
- val cluster = Cluster(system)
- cluster.manager ! Join(cluster.selfMember.address)
- val sharding = ClusterSharding(system)
- val _ = sharding.init(Entity(EntityKey) { entityCtx =>
- cartBehavior(entityCtx.entityId, tagFactory(entityCtx.entityId))
- })
-
- Source
- .tick(1.second, 1.second, "checkout")
- .mapConcat {
- case "checkout" =>
- val cartId = java.util.UUID.randomUUID().toString.take(5)
- val items = randomItems()
- val itemEvents = (0 to items).flatMap {
- _ =>
- val itemId = Products(Random.nextInt(Products.size))
-
- // add the item
- val quantity = randomQuantity()
- val itemAdded = ItemAdded(cartId, itemId, quantity)
-
- // make up to `MaxItemAdjusted` adjustments to quantity of
item
- val adjustments = Random.nextInt(MaxItemsAdjusted)
- val itemQuantityAdjusted = (0 to
adjustments).foldLeft(Seq[ItemQuantityAdjusted]()) {
- case (events, _) =>
- val newQuantity = randomQuantity()
- val oldQuantity =
- if (events.isEmpty) itemAdded.quantity
- else events.last.newQuantity
- events :+ ItemQuantityAdjusted(cartId, itemId,
newQuantity, oldQuantity)
- }
-
- // flip a coin to decide whether or not to remove the item
- val itemRemoved =
- if (Random.nextBoolean())
- List(ItemRemoved(cartId, itemId,
itemQuantityAdjusted.last.newQuantity))
- else Nil
-
- List(itemAdded) ++ itemQuantityAdjusted ++ itemRemoved
- }
-
- // checkout the cart and all its preceding item events
- itemEvents :+ CheckedOut(cartId, Instant.now())
- }
- // send each event to the sharded entity represented by the event's
cartId
- .runWith(Sink.foreach(event => sharding.entityRefFor(EntityKey,
event.cartId).ref.tell(event)))
-
- Behaviors.empty
- }, "EventGeneratorApp", config)
-
- /**
- * Random non-zero based quantity for `ItemAdded` and `ItemQuantityAdjusted`
events
- */
- def randomQuantity(): Int = Random.nextInt(MaxQuantity - 1) + 1
-
- /**
- * Random non-zero based count for how many `ItemAdded` events to generate
- */
- def randomItems(): Int = Random.nextInt(MaxItems - 1) + 1
-
- /**
- * Choose a tag from `ShoppingCartTags` based on the entity id (cart id)
- */
- def tagFactory(entityId: String): String =
- if (args.contains("cluster")) {
- val n = math.abs(entityId.hashCode % ShoppingCartTags.Tags.size)
- val selectedTag = ShoppingCartTags.Tags(n)
- selectedTag
- } else ShoppingCartTags.Single
-
- /**
- * Construct an Actor that persists shopping cart events for a particular
persistence id (cart id) and tag.
- * This is not how real Event Sourced actors should be be implemented.
Please look at
- * https://pekko.apache.org/docs/pekko/current/typed/persistence.html for
more information about `EventSourcedBehavior`.
- */
- def cartBehavior(persistenceId: String, tag: String): Behavior[Event] =
- Behaviors.setup { ctx =>
- EventSourcedBehavior[Event, Event, List[Any]](
- persistenceId = PersistenceId.ofUniqueId(persistenceId),
- Nil,
- (_, event) => {
- ctx.log.info("id [{}] tag [{}] event: {}", persistenceId, tag, event)
- Effect.persist(event)
- },
- (_, _) => Nil).withTagger(_ => Set(tag))
- }
+object EventGeneratorApp {
+ def main(args: Array[String]): Unit = {
+ import ShoppingCartEvents._
+
+ val Products = List("cat t-shirt", "pekko t-shirt", "skis", "bowling
shoes")
+
+ val MaxQuantity = 5
+ val MaxItems = 3
+ val MaxItemsAdjusted = 3
+
+ val EntityKey: EntityTypeKey[Event] =
EntityTypeKey[Event]("shopping-cart-event")
+
+ val config = ConfigFactory
+ .parseString("pekko.actor.provider = cluster")
+ .withFallback(ConfigFactory.load("guide-shopping-cart-app.conf"))
+
+ ActorSystem(Behaviors.setup[String] {
+ ctx =>
+ implicit val system = ctx.system
+ val cluster = Cluster(system)
+ cluster.manager ! Join(cluster.selfMember.address)
+ val sharding = ClusterSharding(system)
+ val _ = sharding.init(Entity(EntityKey) { entityCtx =>
+ cartBehavior(entityCtx.entityId, tagFactory(entityCtx.entityId))
+ })
+
+ Source
+ .tick(1.second, 1.second, "checkout")
+ .mapConcat {
+ case "checkout" =>
+ val cartId = java.util.UUID.randomUUID().toString.take(5)
+ val items = randomItems()
+ val itemEvents = (0 to items).flatMap {
+ _ =>
+ val itemId = Products(Random.nextInt(Products.size))
+
+ // add the item
+ val quantity = randomQuantity()
+ val itemAdded = ItemAdded(cartId, itemId, quantity)
+
+ // make up to `MaxItemAdjusted` adjustments to quantity of
item
+ val adjustments = Random.nextInt(MaxItemsAdjusted)
+ val itemQuantityAdjusted = (0 to
adjustments).foldLeft(Seq[ItemQuantityAdjusted]()) {
+ case (events, _) =>
+ val newQuantity = randomQuantity()
+ val oldQuantity =
+ if (events.isEmpty) itemAdded.quantity
+ else events.last.newQuantity
+ events :+ ItemQuantityAdjusted(cartId, itemId,
newQuantity, oldQuantity)
+ }
+
+ // flip a coin to decide whether or not to remove the item
+ val itemRemoved =
+ if (Random.nextBoolean())
+ List(ItemRemoved(cartId, itemId,
itemQuantityAdjusted.last.newQuantity))
+ else Nil
+
+ List(itemAdded) ++ itemQuantityAdjusted ++ itemRemoved
+ }
+
+ // checkout the cart and all its preceding item events
+ itemEvents :+ CheckedOut(cartId, Instant.now())
+ }
+ // send each event to the sharded entity represented by the
event's cartId
+ .runWith(Sink.foreach(event => sharding.entityRefFor(EntityKey,
event.cartId).ref.tell(event)))
+
+ Behaviors.empty
+ }, "EventGeneratorApp", config)
+
+ /**
+ * Random non-zero based quantity for `ItemAdded` and
`ItemQuantityAdjusted` events
+ */
+ def randomQuantity(): Int = Random.nextInt(MaxQuantity - 1) + 1
+
+ /**
+ * Random non-zero based count for how many `ItemAdded` events to generate
+ */
+ def randomItems(): Int = Random.nextInt(MaxItems - 1) + 1
+
+ /**
+ * Choose a tag from `ShoppingCartTags` based on the entity id (cart id)
+ */
+ def tagFactory(entityId: String): String =
+ if (args.contains("cluster")) {
+ val n = math.abs(entityId.hashCode % ShoppingCartTags.Tags.size)
+ val selectedTag = ShoppingCartTags.Tags(n)
+ selectedTag
+ } else ShoppingCartTags.Single
+
+ /**
+ * Construct an Actor that persists shopping cart events for a particular
persistence id (cart id) and tag.
+ * This is not how real Event Sourced actors should be be implemented.
Please look at
+ * https://pekko.apache.org/docs/pekko/current/typed/persistence.html for
more information about `EventSourcedBehavior`.
+ */
+ def cartBehavior(persistenceId: String, tag: String): Behavior[Event] =
+ Behaviors.setup { ctx =>
+ EventSourcedBehavior[Event, Event, List[Any]](
+ persistenceId = PersistenceId.ofUniqueId(persistenceId),
+ Nil,
+ (_, event) => {
+ ctx.log.info("id [{}] tag [{}] event: {}", persistenceId, tag,
event)
+ Effect.persist(event)
+ },
+ (_, _) => Nil).withTagger(_ => Set(tag))
+ }
+ }
}
// #guideEventGeneratorApp
diff --git a/examples/src/test/scala/docs/guide/ShoppingCartApp.scala
b/examples/src/test/scala/docs/guide/ShoppingCartApp.scala
index ed8df67e..dd333a54 100644
--- a/examples/src/test/scala/docs/guide/ShoppingCartApp.scala
+++ b/examples/src/test/scala/docs/guide/ShoppingCartApp.scala
@@ -39,41 +39,43 @@ import
pekko.stream.connectors.cassandra.scaladsl.CassandraSessionRegistry
//#guideSetup
-object ShoppingCartApp extends App {
- val config = ConfigFactory.load("guide-shopping-cart-app.conf")
+object ShoppingCartApp {
+ def main(args: Array[String]): Unit = {
+ val config = ConfigFactory.load("guide-shopping-cart-app.conf")
- ActorSystem(
- Behaviors.setup[String] { context =>
- val system = context.system
+ ActorSystem(
+ Behaviors.setup[String] { context =>
+ val system = context.system
- // ...
+ // ...
- // #guideSetup
- // #guideSourceProviderSetup
- val sourceProvider: SourceProvider[Offset,
EventEnvelope[ShoppingCartEvents.Event]] =
- EventSourcedProvider
- .eventsByTag[ShoppingCartEvents.Event](
- system,
- readJournalPluginId = CassandraReadJournal.Identifier,
- tag = ShoppingCartTags.Single)
- // #guideSourceProviderSetup
+ // #guideSetup
+ // #guideSourceProviderSetup
+ val sourceProvider: SourceProvider[Offset,
EventEnvelope[ShoppingCartEvents.Event]] =
+ EventSourcedProvider
+ .eventsByTag[ShoppingCartEvents.Event](
+ system,
+ readJournalPluginId = CassandraReadJournal.Identifier,
+ tag = ShoppingCartTags.Single)
+ // #guideSourceProviderSetup
- // #guideProjectionSetup
- implicit val ec = system.executionContext
- val session =
CassandraSessionRegistry(system).sessionFor("pekko.projection.cassandra.session-config")
- val repo = new ItemPopularityProjectionRepositoryImpl(session)
- val projection = CassandraProjection.atLeastOnce(
- projectionId = ProjectionId("shopping-carts", ShoppingCartTags.Single),
- sourceProvider,
- handler = () => new
ItemPopularityProjectionHandler(ShoppingCartTags.Single, system, repo))
+ // #guideProjectionSetup
+ implicit val ec = system.executionContext
+ val session =
CassandraSessionRegistry(system).sessionFor("pekko.projection.cassandra.session-config")
+ val repo = new ItemPopularityProjectionRepositoryImpl(session)
+ val projection = CassandraProjection.atLeastOnce(
+ projectionId = ProjectionId("shopping-carts",
ShoppingCartTags.Single),
+ sourceProvider,
+ handler = () => new
ItemPopularityProjectionHandler(ShoppingCartTags.Single, system, repo))
- context.spawn(ProjectionBehavior(projection), projection.projectionId.id)
- // #guideProjectionSetup
+ context.spawn(ProjectionBehavior(projection),
projection.projectionId.id)
+ // #guideProjectionSetup
- // #guideSetup
- Behaviors.empty
- },
- "ShoppingCartApp",
- config)
+ // #guideSetup
+ Behaviors.empty
+ },
+ "ShoppingCartApp",
+ config)
+ }
}
//#guideSetup
diff --git a/examples/src/test/scala/docs/guide/ShoppingCartClusterApp.scala
b/examples/src/test/scala/docs/guide/ShoppingCartClusterApp.scala
index 65ed54e0..4b03753a 100644
--- a/examples/src/test/scala/docs/guide/ShoppingCartClusterApp.scala
+++ b/examples/src/test/scala/docs/guide/ShoppingCartClusterApp.scala
@@ -29,45 +29,47 @@ import pekko.projection.scaladsl.SourceProvider
import pekko.stream.connectors.cassandra.scaladsl.CassandraSessionRegistry
import com.typesafe.config.ConfigFactory
-object ShoppingCartClusterApp extends App {
- val port = args.headOption match {
- case Some(portString) if portString.matches("""\d+""") => portString.toInt
- case _ => throw new
IllegalArgumentException("A pekko cluster port argument is required")
- }
+object ShoppingCartClusterApp {
+ def main(args: Array[String]): Unit = {
+ val port = args.headOption match {
+ case Some(portString) if portString.matches("""\d+""") =>
portString.toInt
+ case _ => throw new
IllegalArgumentException("A pekko cluster port argument is required")
+ }
- val config = ConfigFactory
- .parseString(s"pekko.remote.artery.canonical.port = $port")
- .withFallback(ConfigFactory.load("guide-shopping-cart-cluster-app.conf"))
+ val config = ConfigFactory
+ .parseString(s"pekko.remote.artery.canonical.port = $port")
+ .withFallback(ConfigFactory.load("guide-shopping-cart-cluster-app.conf"))
- ActorSystem(
- Behaviors.setup[String] { context =>
- val system = context.system
- implicit val ec = system.executionContext
- val session =
CassandraSessionRegistry(system).sessionFor("pekko.projection.cassandra.session-config")
- val repo = new ItemPopularityProjectionRepositoryImpl(session)
+ ActorSystem(
+ Behaviors.setup[String] { context =>
+ val system = context.system
+ implicit val ec = system.executionContext
+ val session =
CassandraSessionRegistry(system).sessionFor("pekko.projection.cassandra.session-config")
+ val repo = new ItemPopularityProjectionRepositoryImpl(session)
- def sourceProvider(tag: String): SourceProvider[Offset,
EventEnvelope[ShoppingCartEvents.Event]] =
- EventSourcedProvider
- .eventsByTag[ShoppingCartEvents.Event](
- system,
- readJournalPluginId = CassandraReadJournal.Identifier,
- tag = tag)
+ def sourceProvider(tag: String): SourceProvider[Offset,
EventEnvelope[ShoppingCartEvents.Event]] =
+ EventSourcedProvider
+ .eventsByTag[ShoppingCartEvents.Event](
+ system,
+ readJournalPluginId = CassandraReadJournal.Identifier,
+ tag = tag)
- def projection(tag: String) =
- CassandraProjection.atLeastOnce(
- projectionId = ProjectionId("shopping-carts", tag),
- sourceProvider(tag),
- handler = () => new ItemPopularityProjectionHandler(tag, system,
repo))
+ def projection(tag: String) =
+ CassandraProjection.atLeastOnce(
+ projectionId = ProjectionId("shopping-carts", tag),
+ sourceProvider(tag),
+ handler = () => new ItemPopularityProjectionHandler(tag, system,
repo))
- ShardedDaemonProcess(system).init[ProjectionBehavior.Command](
- name = "shopping-carts",
- numberOfInstances = ShoppingCartTags.Tags.size,
- behaviorFactory = (i: Int) =>
ProjectionBehavior(projection(ShoppingCartTags.Tags(i))),
- stopMessage = ProjectionBehavior.Stop)
+ ShardedDaemonProcess(system).init[ProjectionBehavior.Command](
+ name = "shopping-carts",
+ numberOfInstances = ShoppingCartTags.Tags.size,
+ behaviorFactory = (i: Int) =>
ProjectionBehavior(projection(ShoppingCartTags.Tags(i))),
+ stopMessage = ProjectionBehavior.Stop)
- Behaviors.empty
- },
- "ShoppingCartClusterApp",
- config)
+ Behaviors.empty
+ },
+ "ShoppingCartClusterApp",
+ config)
+ }
}
//#guideClusterSetup
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]