{"id":21023213,"url":"https://github.com/simerplaha/actor","last_synced_at":"2025-05-15T08:32:53.056Z","repository":{"id":57722799,"uuid":"138962014","full_name":"simerplaha/Actor","owner":"simerplaha","description":"A type-safe Actor class and WiredActor for sending functions as messages","archived":false,"fork":false,"pushed_at":"2021-02-12T04:56:35.000Z","size":70,"stargazers_count":18,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-03T06:41:56.818Z","etag":null,"topics":["actor","functions"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/simerplaha.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["simerplaha"]}},"created_at":"2018-06-28T03:32:08.000Z","updated_at":"2023-03-19T19:45:09.000Z","dependencies_parsed_at":"2022-08-28T10:11:39.603Z","dependency_job_id":null,"html_url":"https://github.com/simerplaha/Actor","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simerplaha%2FActor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simerplaha%2FActor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simerplaha%2FActor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simerplaha%2FActor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simerplaha","download_url":"https://codeload.github.com/simerplaha/Actor/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254304646,"owners_count":22048446,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["actor","functions"],"created_at":"2024-11-19T11:17:16.038Z","updated_at":"2025-05-15T08:32:48.359Z","avatar_url":"https://github.com/simerplaha.png","language":"Scala","funding_links":["https://github.com/sponsors/simerplaha"],"categories":[],"sub_categories":[],"readme":"# Actor\n\n**[`Actor`](https://github.com/simerplaha/Actor#actor-1)** - A small type-safe class that implements the most commonly used Actor APIs\nincluding ask (`?`) which returns a typed `Future[R]`.\n\n**[`WiredActor`](https://github.com/simerplaha/Actor#wiredactor)** - Convert any `object` to an `Actor` that allows invoking the `object`'s function as messages.\n\n\n# Setup\n```scala\nlibraryDependencies += \"com.github.simerplaha\" %% \"actor\" % \"0.3\"\n```\nMake sure you have `ExecutionContext` in scope\n```scala\nimport scala.concurrent.ExecutionContext.Implicits.global\n```\n\n# Actor\n\n## Stateless Actor\n\n```scala\nval actor =\n  Actor[Int](\n    (message, self) =\u003e\n      println(message)\n  )\n\nactor ! 1\n```\n\n## Stateful Actor\n```scala\ncase class State(var counter: Int)\n\nval actor =\n  Actor[Int, State](State(0))(\n    (message, self) =\u003e\n      self.state.counter += 1\n  )\n```\n\n## Timer actor\nA timer actor will process messages in batches after the set delays. Similar to above a stateful timer Actor\ncan also be created.\n\n```scala\nimport scala.concurrent.duration._\n\n//stateless timer actor\nval actor =\n  Actor.timer[Int](delays = 1.second) {\n    (message, self) =\u003e\n    //do something\n  }\n```\n\n## Scheduling messages to self\n`self.schedule` returns a java `TimerTask` which is cancellable.\n\n```scala\nval actor =\n  Actor[Int](\n    (message, self) =\u003e\n      self.schedule(message = 1, delay = 1.second)  \n  )\n```\n\n\n## Terminating an Actor\n\n```scala\nval actor =\n  Actor[Int](\n    (message, self) =\u003e\n      println(message)\n  )\n\nactor.terminate()\n//cannot send messages to a terminated actor.\n(actor ! 1) shouldBe Left(Result.TerminatedActor)\n```\n\n## Ask - Get a Future response\n```scala\ncase class CreateUser(name: String)(val replyTo: ActorRef[Boolean])\n\nval actor = Actor[CreateUser] {\n  (message: CreateUser, _) =\u003e\n    message.replyTo ! true\n}\n\nval response: Future[Boolean] = (actor ? CreateUser(\"Tony Stark\")).right.get\n\nAwait.result(response, 1.second)\n```\n\n## Terminating an Actor on message failure\nBy default actors are not terminated if there is a failure processing a message. The\nfollowing actor enables termination if there is a failure processing a message.\n \n```scala\nval actor =\n  Actor[Int](\n    (message, self) =\u003e\n      throw new Exception(\"Kaboom!\")\n  ).terminateOnException() //enable terminate on exception\n\n(actor ! 1) shouldBe Right(Result.Sent) //first message sent is successful\neventually(actor.isTerminated() shouldBe true) //actor is terminated\n(actor ! 2) shouldBe Left(Result.TerminatedActor) //cannot sent messages to a terminated actor\n```\n\n## Testing\nBorrowing ideas from Akka the `TestActor` implements APIs to test messages in an Actor's mailbox.\n\n```scala\nval actor = TestActor[Int]()\n\nactor.expectNoMessage(after = 1.second) //expect a message after delay in the Actor's mailbox\nval got = actor.getMessage() //fetch the first message in the actor's mailbox\nactor.expectMessage[Int]() //expect a message of some type\n```\n\n## PingPong example\n\n```scala\nimport scala.concurrent.ExecutionContext.Implicits.global\nimport scala.concurrent.duration._\nimport com.github.simerplaha.actor._\n\ncase class Pong(replyTo: ActorRef[Ping])\ncase class Ping(replyTo: ActorRef[Pong])\ncase class State(var count: Int)\n\nval ping =\n  Actor[Ping, State](State(0)) {\n    case (message, self) =\u003e\n      self.state.count += 1\n      println(s\"Ping: ${self.state.count}\")\n      message.replyTo ! Pong(self)\n  }\n\nval pong =\n  Actor[Pong, State](State(0)) {\n    case (message, self) =\u003e\n      self.state.count += 1\n      println(s\"Pong: ${self.state.count}\")\n      message.replyTo ! Ping(self)\n  }\n\npong ! Pong(ping)\n\n//run this for 1 seconds\nThread.sleep(1.second.toMillis)\n```\n\n\n# WiredActor\nFunctions can be sent, invoked \u0026 scheduled as messages to `WiredActor`s similar to messages in an `Actor`.\n\n`WiredActor`s can be created on any `class` instance or `object`.\n\n## Create a `WiredActor`\n```scala\n//your class that contains Actor functions\nobject MyImpl {\n  def hello(name: String): String =\n    s\"Hello $name\"\n\n  def helloFuture(name: String): Future[String] =\n    Future(s\"Hello $name from the Future!\") //some future operation\n}\n\n//create WiredActor\nval actor = Actor.wire(MyImpl)\n```\n\n## ask \n```scala\n//invoke function\nval response: Future[String] = actor.ask(_.hello(\"World\"))\nresponse.foreach(println)\n```\n\n## askFlatMap\n\n```scala\nval responseFlatMap: Future[String] = actor.askFlatMap(_.helloFuture(\"World\"))\nresponseFlatMap.foreach(println)\n```\n\n## send\n\n```scala\nval unitResponse: Unit = actor.send(_.hello(\"World again!\"))\n```\n\n## schedule\n\n```scala\n//schedule a function call on the actor. Returns Future response and TimerTask to cancel.\nval scheduleResponse: (Future[String], TimerTask) = actor.scheduleAsk(delay = 1.second)(_.hello(\"World\"))\nscheduleResponse._1.foreach(println)\n```\n\n## PingPong example using `WiredActor`\n```scala\nimport scala.concurrent.duration._\nimport scala.concurrent.ExecutionContext.Implicits.global\n\nobject WiredPingPongDemo extends App {\n\n  class WiredPingPong(var pingCount: Int, var pongCount: Int) {\n    def ping(replyTo: WiredActor[WiredPingPong]): Unit = {\n      pingCount += 1\n      println(s\"pingCount: $pingCount\")\n      replyTo.send(_.pong(replyTo))\n    }\n\n    def pong(replyTo: WiredActor[WiredPingPong]): Unit = {\n      pongCount += 1\n      println(s\"pongCount: $pongCount\")\n      replyTo.send(_.ping(replyTo))\n    }\n  }\n\n  Actor\n    .wire(new WiredPingPong(0, 0))\n    .send {\n      (impl, self) =\u003e\n        impl.ping(self)\n    }\n\n  Thread.sleep(1.seconds.toMillis)\n}\n```\n\nSee [WiredPingPongStateless](src/test/scala/com/github/simerplaha/actor/WiredPingPongStateless.scala) for a stateless\nversion of the above `WiredPingPong` WiredActor.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimerplaha%2Factor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimerplaha%2Factor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimerplaha%2Factor/lists"}