https://github.com/sciss/model
A simple typed publisher-observer mechanism. Mirror of https://codeberg.org/sciss/Model
https://github.com/sciss/model
mvc
Last synced: 8 months ago
JSON representation
A simple typed publisher-observer mechanism. Mirror of https://codeberg.org/sciss/Model
- Host: GitHub
- URL: https://github.com/sciss/model
- Owner: Sciss
- License: other
- Created: 2013-03-31T14:53:25.000Z (about 13 years ago)
- Default Branch: main
- Last Pushed: 2021-05-14T23:22:37.000Z (about 5 years ago)
- Last Synced: 2025-02-23T17:23:06.555Z (over 1 year ago)
- Topics: mvc
- Language: Scala
- Homepage:
- Size: 45.9 KB
- Stars: 0
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Model
[](https://github.com/Sciss/Model/actions?query=workflow%3A%22Scala+CI%22)
[](https://maven-badges.herokuapp.com/maven-central/de.sciss/model_2.13)
## statement
Model is a simple building block for the Scala programming language, providing a typed publisher-observer mechanism.
It is (C)opyright 2013–2020 by Hanns Holger Rutz. All rights reserved. This project is released under the
[GNU Lesser General Public License](https://raw.github.com/Sciss/Model/main/LICENSE) and comes with absolutely no
warranties. To contact the author, send an e-mail to `contact at sciss.de`.
## linking
To link to this library:
libraryDependencies += "de.sciss" %% "model" % v
The current version `v` is `"0.3.5"`
## building
This project with sbt against Scala 2.13, 2.12, Dotty (JVM) and Scala 2.13 (JS).
The last version to support Scala 2.11 was v0.3.4.
## example
You would declare the mixin of `Model[U]` where `U` is the type of update sent by the model. The actual
implementation would then most likely use the implementation `impl.ModelImpl`. Observers register with the model
using `addListener` which takes a partial function. In that respect a model is similar to Scala-Swing's `Reactor`,
however being specific in the argument type `U`. The `addListener` method returns the partial function for future
reference, useful in unregistering the observer via `removeListener` (be careful not to use Scala's
method-to-function conversion for listeners, as repeated calls will produce non-identical partial function
instances, so `removeListener` would fail).
Typically you declare the update type in the model's companion object as a sealed trait. Here is an example of an
observed set (included in the test sources):
```scala
object SetModel {
sealed trait Update[A]
case class Added [A](elem: A) extends Update[A]
case class Removed[A](elem: A) extends Update[A]
def empty[A]: SetModel[A] = new Impl[A]
private class Impl[A] extends SetModel[A] with impl.ModelImpl[Update[A]] {
private val peer = mutable.Set.empty[A]
def add(elem: A): Boolean = {
peer.synchronized {
val res = peer.add(elem)
if (res) dispatch(Added(elem))
res
}
}
def remove(elem: A): Boolean = {
peer.synchronized {
val res = peer.remove(elem)
if (res) dispatch(Removed(elem))
res
}
}
}
}
trait SetModel[A] extends Model[SetModel.Update[A]] {
def add (elem: A): Boolean
def remove(elem: A): Boolean
}
val set = SetModel.empty[Int]
val obs = set.addListener {
case SetModel.Added (elem) => println(s"Observed addition of $elem")
case SetModel.Removed(elem) => println(s"Observed removal of $elem")
}
set.add(1) // observed
set.add(2) // observed
set.add(1) // no-op
set.remove(1) // observed
set.remove(3) // no-op
set.removeListener(obs)
assert(set.remove(2)) // unobserved
```
## changes
- 0.3.4 relaxes the synchronization in `ModelImpl`, making it impossible to create dead-locks by
registering or un-registering listeners during dispatch. Note that `startListening` and `stopListening`
is still called under synchronization, therefore it must be avoided that sub-classes of `ModelImpl`
call into anything locking within these two methods. The synchronization was preserved here to
guarantee a strict sequential operation of `startListening` and `stopListening`.