Skip to content

Commit 85845cf

Browse files
authored
Merge pull request #13 from outr/2.0
2.0
2 parents 969334f + 4e09675 commit 85845cf

21 files changed

+576
-338
lines changed

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name := "reactify"
22
organization in ThisBuild := "com.outr"
3-
version in ThisBuild := "1.6.0"
3+
version in ThisBuild := "2.0.0-SNAPSHOT"
44
scalaVersion in ThisBuild := "2.12.2"
55
crossScalaVersions in ThisBuild := List("2.12.2", "2.11.11")
66

Lines changed: 10 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,132 +1,18 @@
11
package reactify
22

3-
import java.util.concurrent.atomic.AtomicReference
3+
import reactify.instance.{RecursionMode, StateInstanceManager}
44

5-
abstract class AbstractState[T] private(distinct: Boolean, cache: Boolean) extends State[T] {
6-
private var lastValue: T = _
7-
private val function = new AtomicReference[() => T]
8-
private val previous = new AtomicReference[Option[PreviousFunction[T]]](None)
9-
private val monitoring = new AtomicReference[Set[Observable[_]]](Set.empty)
5+
class AbstractState[T](override val distinct: Boolean,
6+
cache: Boolean,
7+
recursion: RecursionMode,
8+
transactional: Boolean) extends State[T] {
9+
protected val manager = new StateInstanceManager[T](this, cache, recursion, transactional)
1010

11-
private val replacement = new ThreadLocal[Option[PreviousFunction[T]]] {
12-
override def initialValue(): Option[PreviousFunction[T]] = None
13-
}
14-
15-
private val monitor: Listener[Any] = (_: Any) => {
16-
replace(function.get(), newFunction = false)
17-
}
18-
19-
private def updateValue(value: T): Unit = {
20-
if (!distinct || value != lastValue) {
21-
lastValue = value
22-
fire(value)
23-
}
24-
}
25-
26-
def this(function: () => T,
27-
distinct: Boolean = true,
28-
cache: Boolean = true) = {
29-
this(distinct, cache)
30-
replace(function, newFunction = true)
31-
}
32-
33-
override def observing: Set[Observable[_]] = monitoring.get()
34-
35-
override def get: T = get(cache)
36-
37-
def get(cache: Boolean): T = replacement.get() match {
38-
case Some(p) => {
39-
replacement.set(p.previous)
40-
p.function()
41-
}
42-
case None => {
43-
AbstractState.reference(this)
44-
replacement.set(previous.get())
45-
try {
46-
if (cache) {
47-
lastValue
48-
} else {
49-
function.get()()
50-
}
51-
} finally {
52-
replacement.set(None)
53-
}
54-
}
55-
}
56-
57-
protected def set(value: => T): Unit = synchronized {
58-
replace(() => value, newFunction = true)
59-
}
60-
61-
protected def replace(function: () => T, newFunction: Boolean): Unit = {
62-
if (newFunction) {
63-
val p = Option(this.function.get()).map { f =>
64-
new PreviousFunction[T](f, previous.get())
65-
}.getOrElse(new PreviousFunction[T](() => lastValue, previous.get()))
66-
previous.set(Some(p))
67-
}
68-
val previousObservables = AbstractState.observables.get()
69-
AbstractState.observables.set(Set.empty)
70-
try {
71-
this.function.set(function)
72-
val value: T = get(cache = false)
73-
74-
val oldObservables = observing
75-
var newObservables = AbstractState.observables.get()
76-
if (newFunction && !newObservables.contains(this)) {
77-
// No recursive reference, we can clear previous
78-
previous.set(None)
79-
}
80-
newObservables -= this
81-
82-
if (oldObservables != newObservables) {
83-
// Out with the old
84-
oldObservables.foreach { ob =>
85-
if (!newObservables.contains(ob)) {
86-
ob.asInstanceOf[Observable[Any]].detach(monitor)
87-
}
88-
}
89-
90-
// In with the new
91-
newObservables.foreach { ob =>
92-
if (!oldObservables.contains(ob)) {
93-
ob.asInstanceOf[Observable[Any]].observe(monitor)
94-
}
95-
}
96-
97-
monitoring.set(newObservables)
98-
}
99-
updateValue(value)
100-
} finally {
101-
AbstractState.observables.set(previousObservables)
102-
}
103-
}
104-
105-
/**
106-
* Convenience method to pre-evaluate the value instead of as an anonymous function.
107-
*
108-
* @param value the value to be set
109-
*/
110-
protected def setStatic(value: T): Unit = synchronized {
111-
val v: T = value
112-
replace(() => v, newFunction = true)
113-
}
114-
115-
override def dispose(): Unit = synchronized {
116-
super.dispose()
117-
118-
monitoring.get().foreach { ob =>
119-
ob.asInstanceOf[Observable[Any]].detach(monitor)
120-
}
121-
monitoring.set(Set.empty)
122-
}
123-
}
11+
override def observing: Set[Observable[_]] = manager.observables
12412

125-
object AbstractState {
126-
private val observables = new ThreadLocal[Set[Observable[_]]]
13+
override protected def value(): T = manager.get
12714

128-
def reference(observable: Observable[_]): Unit = Option(observables.get()) match {
129-
case Some(obs) => observables.set(obs + observable)
130-
case None => // Nothing being updated
15+
override def set(value: => T): Unit = {
16+
manager.replaceInstance(() => value)
13117
}
13218
}
Lines changed: 107 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package reactify
22

3+
import java.util.concurrent.atomic.AtomicBoolean
4+
5+
import reactify.bind.{BindSet, Binding}
6+
7+
import scala.concurrent.Future
8+
39
/**
410
* Dep is very much like a `Val`, but is also a `Channel`. The basic purpose is to represent a value dependent upon
511
* another variable. An example of this might be if you are representing a position `left` and you also wanted to
@@ -17,41 +23,43 @@ package reactify
1723
* if you change `width` to 50.0? Should `left` change to 75.0 (`submissive = false`) or should `right` change to 75.0
1824
* (`submissive = true`)?
1925
*/
20-
class Dep[T, V](variable: Var[V],
21-
adjustment: => T,
22-
submissive: Boolean)
23-
(implicit connector: DepConnector[T, V]) extends StateChannel[T] {
24-
private val internal = Val[T](connector.combine(variable, adjustment))
25-
26-
override def attach(f: (T) => Unit, priority: Double = Listener.Priority.Normal): Listener[T] = {
27-
internal.attach(f, priority)
28-
}
29-
30-
override def detach(listener: Listener[T]): Unit = internal.detach(listener)
31-
32-
override def changes(listener: ChangeListener[T]): Listener[T] = internal.changes(listener)
33-
34-
override protected[reactify] def fire(value: T): Unit = {}
35-
36-
override def set(value: => T): Unit = set(value, submissive)
37-
38-
override def observing: Set[Observable[_]] = internal.observing
39-
40-
override def get: T = internal.get
41-
42-
def set(value: => T, submissive: Boolean): Unit = {
43-
if (submissive) {
44-
val adj: T = adjustment
45-
variable := connector.extract(value, adj)
46-
} else {
47-
variable := connector.extract(value, adjustment)
48-
}
49-
}
50-
51-
override def apply(): T = internal.apply()
52-
53-
override def toString: String = s"Dep($get)"
54-
}
26+
//class Dep[T, V](variable: Var[V],
27+
// adjustment: => T,
28+
// submissive: Boolean)
29+
// (implicit connector: DepConnector[T, V]) extends StateChannel[T] {
30+
// override def distinct: Boolean = true
31+
//
32+
// override protected def value(): T = internal.get
33+
//
34+
// private val internal = Val[T](connector.combine(variable, adjustment))
35+
//
36+
// override def attach(f: (T) => Unit, priority: Double = Listener.Priority.Normal): Listener[T] = {
37+
// internal.attach(f, priority)
38+
// }
39+
//
40+
// override def detach(listener: Listener[T]): Unit = internal.detach(listener)
41+
//
42+
// override def changes(listener: ChangeListener[T]): Listener[T] = internal.changes(listener)
43+
//
44+
// override protected[reactify] def fire(value: T): Unit = {}
45+
//
46+
// override def set(value: => T): Unit = set(value, submissive)
47+
//
48+
// override def observing: Set[Observable[_]] = internal.observing
49+
//
50+
// def set(value: => T, submissive: Boolean): Unit = {
51+
// if (submissive) {
52+
// val adj: T = adjustment
53+
// variable := connector.extract(value, adj)
54+
// } else {
55+
// variable := connector.extract(value, adjustment)
56+
// }
57+
// }
58+
//
59+
// override def apply(): T = internal.apply()
60+
//
61+
// override def toString: String = s"Dep($get)"
62+
//}
5563

5664
object Dep {
5765
/**
@@ -63,10 +71,73 @@ object Dep {
6371
*
6472
* @return dependency instance
6573
*/
74+
// def apply[T, V](variable: Var[V],
75+
// adjustment: => T,
76+
// submissive: Boolean = false)
77+
// (implicit connector: DepConnector[T, V]): Dep[T, V] = {
78+
// new Dep[T, V](variable, adjustment, submissive)
79+
// }
80+
6681
def apply[T, V](variable: Var[V],
6782
adjustment: => T,
6883
submissive: Boolean = false)
6984
(implicit connector: DepConnector[T, V]): Dep[T, V] = {
85+
// val v = Var[T](connector.combine(variable, adjustment))
86+
// v.bind(variable, BindSet.None)(
87+
// t2v = (t: T) => connector.extract(t, adjustment),
88+
// v2t = (v: V) => connector.combine(v, adjustment)
89+
// )
90+
// v
7091
new Dep[T, V](variable, adjustment, submissive)
7192
}
72-
}
93+
}
94+
95+
class Dep[T, V](variable: Var[V],
96+
adjustment: => T,
97+
submissive: Boolean)
98+
(implicit connector: DepConnector[T, V]) extends Var[T](() => connector.combine(variable, adjustment)) {
99+
assert(!submissive, "Submissive is currently disabled until it can be more thoroughly tested.")
100+
101+
override def set(value: => T): Unit = set(value, submissive)
102+
103+
private lazy val changing = new AtomicBoolean(false)
104+
105+
def set(value: => T, submissive: Boolean): Unit = if (changing.compareAndSet(false, true)) {
106+
try {
107+
super.set(value)
108+
if (submissive) {
109+
val adj: T = adjustment
110+
variable := connector.extract(get, adj)
111+
} else {
112+
variable := connector.extract(get, adjustment)
113+
}
114+
} finally {
115+
changing.set(false)
116+
}
117+
}
118+
119+
variable.attach { v =>
120+
if (changing.compareAndSet(false, true)) {
121+
try {
122+
super.set(connector.combine(variable, adjustment))
123+
} finally {
124+
changing.set(false)
125+
}
126+
}
127+
}
128+
}
129+
130+
/*
131+
class Dep[T, V](variable: Var[V],
132+
adjustment: => T,
133+
submissive: Boolean)
134+
(implicit connector: DepConnector[T, V]) extends Val[T](() => connector.combine(variable, adjustment)) with StateChannel[T] {
135+
override def set(value: => T): Unit = {
136+
if (submissive) {
137+
val adj: T = adjustment
138+
variable := connector.extract(get, adj)
139+
} else {
140+
variable := connector.extract(get, adjustment)
141+
}
142+
}
143+
}*/

shared/src/main/scala/reactify/DirtyObservable.scala

Lines changed: 0 additions & 32 deletions
This file was deleted.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package reactify
2+
3+
import java.util.concurrent.atomic.AtomicReference
4+
5+
// TODO: apply in StateInstanceManager
6+
trait DirtyState[T] extends AbstractState[T] {
7+
private lazy val dirty = new AtomicReference[Option[() => T]](None)
8+
9+
def isDirty: Boolean = dirty.get().nonEmpty
10+
11+
def update(): Boolean = dirty.getAndSet(None) match {
12+
case Some(value) => {
13+
manager.replaceInstance(value)
14+
true
15+
}
16+
case None => false
17+
}
18+
19+
override def set(value: => T): Unit = if (manager.isEmpty) {
20+
super.set(value)
21+
} else {
22+
dirty.set(Some(() => value))
23+
}
24+
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package reactify
22

33
import scala.concurrent.{Future, Promise}
4+
import scala.concurrent.ExecutionContext.Implicits.global
45

56
/**
67
* Observable, as the name suggests, observes values being fired against it. This is the core functionality of Reactify
@@ -120,4 +121,8 @@ trait Observable[T] {
120121

121122
object Observable {
122123
def wrap[T](observables: Observable[T]*): Observable[T] = new WrappedObservable[T](observables.toList)
124+
def apply[T](init: (T => Unit) => Unit): Observable[T] = new Observable[T] {
125+
init(fire)
126+
}
127+
def apply[T](future: Future[T]): Observable[T] = apply(fire => future.foreach(fire))
123128
}

0 commit comments

Comments
 (0)