Skip to content

Commit c1ef0a1

Browse files
committed
Added map and collect support in Observable, Channel, Var, and Val.
Updated to Scala 2.12.3 and SBT 0.13.16.
1 parent 0ad8a80 commit c1ef0a1

File tree

8 files changed

+68
-6
lines changed

8 files changed

+68
-6
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,12 @@ We need implicits to be able to convert between the two, but now changes to one
276276

277277
## Versions
278278

279+
### Features for 2.1.0 (In-Progress)
280+
281+
* [ ] Features for mapping from one Observable to another
282+
* [X] map
283+
* [X] collect
284+
279285
### Features for 2.0.0 (Released 2017.06.15)
280286

281287
* [X] Complete re-write of internal and recursive state system for better modularity and stability

build.sbt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
name := "reactify"
22
organization in ThisBuild := "com.outr"
3-
version in ThisBuild := "2.0.6"
4-
scalaVersion in ThisBuild := "2.12.2"
5-
crossScalaVersions in ThisBuild := List("2.12.2", "2.11.11", "2.13.0-M1")
3+
version in ThisBuild := "2.1.0-SNAPSHOT"
4+
scalaVersion in ThisBuild := "2.12.3"
5+
crossScalaVersions in ThisBuild := List("2.12.3", "2.11.11", "2.13.0-M1")
66

77
lazy val root = project.in(file("."))
88
.aggregate(js, jvm)

project/build.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
sbt.version=0.13.15
1+
sbt.version=0.13.16

shared/src/main/scala/reactify/Channel.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ trait Channel[T] extends Observable[T] {
1414
* @param value the value to apply
1515
*/
1616
def set(value: => T): Unit
17+
18+
override def map[R](f: (T) => R): Channel[R] = super.map(f).asInstanceOf[Channel[R]]
19+
20+
override def collect[R](f: PartialFunction[T, R]): Channel[R] = super.collect(f).asInstanceOf[Channel[R]]
1721
}
1822

1923
object Channel {

shared/src/main/scala/reactify/Observable.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,28 @@ trait Observable[T] {
8989
*/
9090
def changes(listener: ChangeListener[T]): Listener[T] = attach(ChangeListener.createFunction(listener, None))
9191

92+
/**
93+
* Maps the Observable to another type.
94+
*
95+
* @param f function to handle the mapping from T to R
96+
* @tparam R the type of the new Observable
97+
* @return Observable[R]
98+
*/
99+
def map[R](f: T => R): Observable[R] = {
100+
val channel = Channel[R]
101+
attach(t => channel := f(t))
102+
channel
103+
}
104+
105+
def collect[R](f: PartialFunction[T, R]): Observable[R] = {
106+
val channel = Channel[R]
107+
val lifted = f.lift
108+
attach { t =>
109+
lifted(t).foreach(v => channel.set(v))
110+
}
111+
channel
112+
}
113+
92114
protected[reactify] def fire(value: T, `type`: InvocationType): Unit = Invocation().wrap {
93115
fireRecursive(value, `type`, Invocation(), observers)
94116
}

shared/src/main/scala/reactify/Val.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ class Val[T](function: () => T,
1818
onUpdate: Boolean = false) extends AbstractState[T](distinct, cache, recursion, transactional, onUpdate) {
1919
set(function())
2020

21+
override def map[R](f: (T) => R): Val[R] = Val(f(this()))
22+
2123
override def toString: String = s"Val($get)"
2224
}
2325

shared/src/main/scala/reactify/Var.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ class Var[T](function: () => T,
1919
override def set(value: => T): Unit = super.set(value)
2020
def asVal: Val[T] = this
2121

22+
override def map[R](f: (T) => R): Var[R] = Var(f(this()))
23+
2224
override def toString: String = s"Var($get)"
2325
}
2426

shared/src/test/scala/specs/BasicSpec.scala

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,32 @@ class BasicSpec extends WordSpec with Matchers {
2828
changes should be(2)
2929
lastChange should be(Some("Test 2"))
3030
}
31+
"map a Channel to another typed Channel" in {
32+
val c1 = Channel[String]
33+
val c2 = c1.map(_.reverse)
34+
35+
var lastValue: Option[String] = None
36+
c2.attach(v => lastValue = Some(v))
37+
38+
c1 := "Testing"
39+
lastValue should be(Some("gnitseT"))
40+
}
41+
"collect a Channel to another typed Channel" in {
42+
val IntRegex = """(\d+)""".r
43+
val c1 = Channel[String]
44+
val c2 = c1.collect {
45+
case IntRegex(v) => v.toInt
46+
}
47+
48+
var lastValue: Option[Int] = None
49+
c2.attach(v => lastValue = Some(v))
50+
51+
c1 := "Testing"
52+
lastValue should be(None)
53+
54+
c1 := "50"
55+
lastValue should be(Some(50))
56+
}
3157
}
3258
"Vals" should {
3359
"contain the proper value" in {
@@ -253,7 +279,7 @@ class BasicSpec extends WordSpec with Matchers {
253279
}
254280
users := List(adam, betty, chris, debby)
255281

256-
val fiveLetterNames = Val(users.collect {
282+
val fiveLetterNames = Val(users().collect {
257283
case user if user.name.length == 5 => user.name()
258284
})
259285
fiveLetterNames() should be(List("Betty", "Chris", "Debby"))
@@ -383,7 +409,7 @@ class BasicSpec extends WordSpec with Matchers {
383409
}
384410
val complex: Var[Option[Complex]] = Var[Option[Complex]](None)
385411
var active = false
386-
val enabled: Val[Boolean] = Val(complex.flatMap(_.screen.map(_.active())).getOrElse(false))
412+
val enabled: Val[Boolean] = Val(complex.flatMap(_.screen().map(_.active())).getOrElse(false))
387413
enabled.attach(active = _)
388414
active should be(false)
389415
enabled() should be(false)

0 commit comments

Comments
 (0)