{"id":19254849,"url":"https://github.com/sciss/model","last_synced_at":"2025-10-15T00:54:39.070Z","repository":{"id":7763269,"uuid":"9131540","full_name":"Sciss/Model","owner":"Sciss","description":"A simple typed publisher-observer mechanism. Mirror of https://codeberg.org/sciss/Model","archived":false,"fork":false,"pushed_at":"2021-05-14T23:22:37.000Z","size":47,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-23T17:23:06.555Z","etag":null,"topics":["mvc"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Sciss.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-03-31T14:53:25.000Z","updated_at":"2023-09-14T10:15:02.000Z","dependencies_parsed_at":"2022-09-13T14:00:52.267Z","dependency_job_id":null,"html_url":"https://github.com/Sciss/Model","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/Sciss/Model","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sciss%2FModel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sciss%2FModel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sciss%2FModel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sciss%2FModel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Sciss","download_url":"https://codeload.github.com/Sciss/Model/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sciss%2FModel/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279032694,"owners_count":26089387,"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","status":"online","status_checked_at":"2025-10-14T02:00:06.444Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["mvc"],"created_at":"2024-11-09T18:36:54.026Z","updated_at":"2025-10-15T00:54:39.030Z","avatar_url":"https://github.com/Sciss.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Model\n\n[![Build Status](https://github.com/Sciss/Model/workflows/Scala%20CI/badge.svg?branch=main)](https://github.com/Sciss/Model/actions?query=workflow%3A%22Scala+CI%22)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/de.sciss/model_2.13/badge.svg)](https://maven-badges.herokuapp.com/maven-central/de.sciss/model_2.13)\n\n## statement\n\nModel is a simple building block for the Scala programming language, providing a typed publisher-observer mechanism. \nIt is (C)opyright 2013\u0026ndash;2020 by Hanns Holger Rutz. All rights reserved. This project is released under the\n[GNU Lesser General Public License](https://raw.github.com/Sciss/Model/main/LICENSE) and comes with absolutely no \nwarranties. To contact the author, send an e-mail to `contact at sciss.de`.\n\n## linking\n\nTo link to this library:\n\n    libraryDependencies += \"de.sciss\" %% \"model\" % v\n\nThe current version `v` is `\"0.3.5\"`\n\n## building\n\nThis project with sbt against Scala 2.13, 2.12, Dotty (JVM) and Scala 2.13 (JS).\nThe last version to support Scala 2.11 was v0.3.4.\n\n## example\n\nYou would declare the mixin of `Model[U]` where `U` is the type of update sent by the model. The actual \nimplementation would then most likely use the implementation `impl.ModelImpl`. Observers register with the model \nusing `addListener` which takes a partial function. In that respect a model is similar to Scala-Swing's `Reactor`, \nhowever being specific in the argument type `U`. The `addListener` method returns the partial function for future \nreference, useful in unregistering the observer via `removeListener` (be careful not to use Scala's \nmethod-to-function conversion for listeners, as repeated calls will produce non-identical partial function \ninstances, so `removeListener` would fail).\n\nTypically you declare the update type in the model's companion object as a sealed trait. Here is an example of an \nobserved set (included in the test sources):\n\n```scala\n\n    object SetModel {\n      sealed trait Update[A]\n      case class Added  [A](elem: A) extends Update[A]\n      case class Removed[A](elem: A) extends Update[A]\n\n      def empty[A]: SetModel[A] = new Impl[A]\n\n      private class Impl[A] extends SetModel[A] with impl.ModelImpl[Update[A]] {\n        private val peer = mutable.Set.empty[A]\n\n        def add(elem: A): Boolean = {\n          peer.synchronized {\n            val res = peer.add(elem)\n            if (res) dispatch(Added(elem))\n            res\n          }\n        }\n\n        def remove(elem: A): Boolean = {\n          peer.synchronized {\n            val res = peer.remove(elem)\n            if (res) dispatch(Removed(elem))\n            res\n          }\n        }\n      }\n    }\n    trait SetModel[A] extends Model[SetModel.Update[A]] {\n      def add   (elem: A): Boolean\n      def remove(elem: A): Boolean\n    }\n\n    val set = SetModel.empty[Int]\n    val obs = set.addListener {\n      case SetModel.Added  (elem) =\u003e println(s\"Observed addition of $elem\")\n      case SetModel.Removed(elem) =\u003e println(s\"Observed removal  of $elem\")\n    }\n    set.add(1)            // observed\n    set.add(2)            // observed\n    set.add(1)            // no-op\n    set.remove(1)         // observed\n    set.remove(3)         // no-op\n    set.removeListener(obs)\n    assert(set.remove(2)) // unobserved\n```\n\n## changes\n\n- 0.3.4 relaxes the synchronization in `ModelImpl`, making it impossible to create dead-locks by\n  registering or un-registering listeners during dispatch. Note that `startListening` and `stopListening`\n  is still called under synchronization, therefore it must be avoided that sub-classes of `ModelImpl`\n  call into anything locking within these two methods. The synchronization was preserved here to\n  guarantee a strict sequential operation of `startListening` and `stopListening`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsciss%2Fmodel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsciss%2Fmodel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsciss%2Fmodel/lists"}