Skip to content

Commit 2fa69fd

Browse files
committed
Added documentation
1 parent 29d4f68 commit 2fa69fd

File tree

10 files changed

+197
-4
lines changed

10 files changed

+197
-4
lines changed

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,61 @@ import reactify.standard.StandardChannel
66
import scala.concurrent.Future
77
import scala.concurrent.ExecutionContext.Implicits.global
88

9+
/**
10+
* Channel is a stateless Reactive implementation exposing a public method to fire values.
11+
*
12+
* @tparam T the type of value this Reactive receives
13+
*/
914
trait Channel[T] extends Reactive[T] {
15+
/**
16+
* Public method to fire a value against the Reactions attached to this Channel
17+
*
18+
* @param value the function value
19+
*/
1020
def set(value: => T): Unit
21+
22+
/**
23+
* Convenience method to fire a value
24+
*
25+
* @see #set
26+
* @param value the function value
27+
*/
1128
def :=(value: => T): Unit = set(value)
29+
30+
/**
31+
* Convenience functionality to assign the result of a future (upon completion) to this Channel
32+
*/
1233
def !(future: Future[T]): Future[Unit] = future.map { value =>
1334
set(value)
1435
}
1536

37+
/**
38+
* Group multiple channels together
39+
*/
1640
def and(that: Channel[T]): Channel[T] = ChannelGroup(None, List(this, that))
1741

42+
/**
43+
* Functional mapping of this Channel into another Channel. All values received by this Channel will be mapped and
44+
* forwarded to the new Channel.
45+
*
46+
* @param f conversion function
47+
* @tparam R the type of the new Channel
48+
* @return Channel[R]
49+
*/
1850
def map[R](f: T => R): Channel[R] = {
1951
val channel = Channel[R]
2052
attach(channel := f(_))
2153
channel
2254
}
2355

56+
/**
57+
* Functional collection of this Channel into another Channel. All values received by this Channel will be collected
58+
* and forwarded to the new Channel if they are collected by the conversion function.
59+
*
60+
* @param f conversion partial function
61+
* @tparam R the type of the Channel
62+
* @return Channel[R]
63+
*/
2464
def collect[R](f: PartialFunction[T, R]): Channel[R] = {
2565
val channel = Channel[R]
2666
val lifted = f.lift

shared/src/main/scala/reactify/Dep.scala

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,26 @@
11
package reactify
22

3-
import reactify.reaction.{Reaction, ReactionStatus}
43
import reactify.standard.StandardDep
54

5+
/**
6+
* Dep allows creation of a dependent `Var` on another `Var` allowing conversion between the two. This can be useful for
7+
* different representations of the same value. For example, in a graphical environment `left`, `center`, and `right`
8+
* are all different representations of the value (horizontal position). Maintaining three distinct values while
9+
* keeping them in-sync is painful. With `Dep` you can simply define one `Var` and two `Dep` values like:
10+
*
11+
* <code>
12+
* val left: Var[Double] = Var(0.0)
13+
* val width: Var[Double] = Var(0.0)
14+
* val center: Dep[Double, Double] = Dep(left)(_ + (width / 2.0), _ - (width / 2.0))
15+
* val right: Dep[Double, Double] = Dep(left)(_ + width, _ - width)
16+
* </code>
17+
*
18+
* Now, modification to `left`, `center`, or `right` will maintain the appropriate value for each without any additional
19+
* boilerplate.
20+
*
21+
* @tparam T the type of value this Reactive receives
22+
* @tparam R the type that this Dep receives
23+
*/
624
trait Dep[T, R] extends Var[T] {
725
def owner: Var[R]
826
def t2R(t: T): R

shared/src/main/scala/reactify/Priority.scala

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

3+
/**
4+
* Convenience values for Priorities
5+
*/
36
object Priority {
47
val Lowest: Double = Double.MinValue
58
val Low: Double = -100.0

shared/src/main/scala/reactify/Reactive.scala

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,82 @@ import reactify.standard.StandardReactions
66
import scala.annotation.tailrec
77
import scala.concurrent.{Future, Promise}
88

9+
/**
10+
* Reactive is the core trait for Reactify. The basic premise is that a Reactive represents an instance that can attach
11+
* Reactions and fire instances of `T` that are received by those Reactions.
12+
*
13+
* @tparam T the type of value this Reactive receives
14+
*/
915
trait Reactive[T] {
16+
/**
17+
* An optional name associated. This is primarily used for distinguishing between instances as well as logging.
18+
*/
1019
def name: Option[String]
20+
1121
private lazy val _status = new ThreadLocal[Option[ReactionStatus]]
1222

23+
/**
24+
* If the current thread is reacting to a value currently, status represents the status of the reaction. This can be
25+
* set to ReactionStatus.Stop in order to stop propagation. This can also be achieved via stopPropagation().
26+
*/
1327
def status: Option[ReactionStatus] = _status.get()
1428
def status_=(status: ReactionStatus): Unit = {
1529
assert(_status.get().nonEmpty, "Cannot set the status without an active reaction on this thread")
1630
_status.set(Some(status))
1731
}
1832

33+
/**
34+
* Shortcut functionality to call `status = ReactionStatus.Stop`
35+
*/
1936
def stopPropagation(): Unit = status = ReactionStatus.Stop
2037

38+
/**
39+
* Reactions currently associated with this Reactive
40+
*/
2141
lazy val reactions: Reactions[T] = new StandardReactions[T]
2242

43+
/**
44+
* Convenience method to create a Reaction to attach to this Reactive
45+
*
46+
* @param f the function reaction
47+
* @param priority the priority in comparison to other reactions (Defaults to Priority.Normal)
48+
* @return created Reaction[T]
49+
*/
2350
def attach(f: T => Unit, priority: Double = Priority.Normal): Reaction[T] = {
2451
reactions += Reaction[T](f, priority)
2552
}
2653

54+
/**
55+
* Convenience method to create a Reaction to monitor changes to this Reactive
56+
*
57+
* @param f the function reaction to receive changes
58+
* @param priority the priority in comparison to other reactions (Defaults to Priority.Normal)
59+
* @return created Reaction[T]
60+
*/
2761
def changes(f: (T, T) => Unit, priority: Double = Priority.Normal): Reaction[T] = {
2862
reactions += Reaction.changes[T](f, priority)
2963
}
3064

65+
/**
66+
* Convenience method to create a Reaction to monitor changes to this Reactive when you don't care about the actual
67+
* value.
68+
*
69+
* @param f the function reaction to invoke in reaction to a value received
70+
* @param priority the priority in comparison to other reactions (Defaults to Priority.Normal)
71+
* @return created Reaction[T]
72+
*/
3173
def on(f: => Unit, priority: Double = Priority.Normal): Reaction[T] = {
3274
attach(_ => f, priority)
3375
}
3476

77+
/**
78+
* Convenience method to create a Reaction to monitor a single reaction based on an optional condition.
79+
*
80+
* @param f the function reaction
81+
* @param condition optional condition that must be true for this to fire (Defaults to accept anything)
82+
* @param priority the priority in comparison to other reactions (Defaults to Priority.Normal)
83+
* @return created Reaction[T]
84+
*/
3585
def once(f: T => Unit, condition: T => Boolean = _ => true, priority: Double = Priority.Normal): Reaction[T] = {
3686
var reaction: Reaction[T] = null
3787
val function = (t: T) => if (condition(t)) {
@@ -43,12 +93,22 @@ trait Reactive[T] {
4393
reaction
4494
}
4595

96+
/**
97+
* Convenience method to create a `Future[T]` that will complete upon the next reaction that meets to supplied
98+
* condition.
99+
*
100+
* @param condition optional condition that must be true for this to fire (Defaults to accept anything)
101+
* @return Future[T]
102+
*/
46103
def future(condition: T => Boolean = _ => true): Future[T] = {
47104
val promise = Promise[T]
48105
once(promise.success, condition)
49106
promise.future
50107
}
51108

109+
/**
110+
* Fires the value to the Reactions
111+
*/
52112
protected def fire(value: T, previous: Option[T], reactions: List[Reaction[T]] = this.reactions()): ReactionStatus = {
53113
_status.set(Some(ReactionStatus.Continue))
54114
try {
@@ -77,7 +137,7 @@ trait Reactive[T] {
77137
}
78138

79139
object Reactive {
80-
def fire[T](reactive: Reactive[T], value: T, previous: Option[T]): Unit = {
140+
private[reactify] def fire[T](reactive: Reactive[T], value: T, previous: Option[T]): Unit = {
81141
reactive.fire(value, previous, reactive.reactions())
82142
}
83143
}

shared/src/main/scala/reactify/State.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package reactify
22

33
import reactify.reaction.{Reaction, ReactionStatus}
44

5+
/**
6+
* State is an internal class to represent the assigned state of a `Val`, `Var`, or `Dep`
7+
*/
58
case class State[T](owner: Reactive[T], index: Long, function: () => T) extends Reaction[Any] {
69
private var _previousState: Option[State[T]] = None
710
private var _nextState: Option[State[T]] = None

shared/src/main/scala/reactify/StateCounter.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,18 @@ class StateCounter {
44
var references: List[State[_]] = Nil
55
}
66

7+
/**
8+
* StateCounter provides infrastructure to glean the references to `State`s within a functional block of code. This is
9+
* primarily for internal use, but can be used externally to get additional information regarding references.
10+
*/
711
object StateCounter {
812
private val instance = new ThreadLocal[Option[StateCounter]] {
913
override def initialValue(): Option[StateCounter] = None
1014
}
1115

16+
/**
17+
* Use this method to get a list of `State` references used by the underlying function block
18+
*/
1219
def transaction[Return](f: => Return): (Return, List[State[_]]) = {
1320
val previous = instance.get()
1421
val counter = new StateCounter

shared/src/main/scala/reactify/Trigger.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ package reactify
22

33
import reactify.standard.StandardChannel
44

5+
/**
6+
* Trigger is a convenience class wrapping `Channel[Unit]` specifically for scenarios where the value doesn't matter,
7+
* just the reactions themselves.
8+
*/
59
trait Trigger extends Channel[Unit] {
610
def trigger(): Unit = fire((), None)
711
}

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,40 @@ package reactify
33
import reactify.group.ValGroup
44
import reactify.standard.StandardVal
55

6+
/**
7+
* Val represents a final variable that cannot be set apart from its instantiation. However, unlike a Scala `val`, a
8+
* `Val` may still fire changes if its value is derived from `Var`s that make it up. A `Val` is a stateful `Reactive`.
9+
*
10+
* @tparam T the type of value this Reactive receives
11+
*/
612
trait Val[T] extends Reactive[T] {
13+
/**
14+
* The current State representation
15+
*/
716
def state: State[T]
817

18+
/**
19+
* Gets the current value from the current `State`
20+
*/
921
def get: T = state.value
22+
23+
/**
24+
* Convenience wrapper around `get`
25+
*/
1026
def apply(): T = get
1127

28+
/**
29+
* Group multiple Vals together
30+
*/
1231
def and(that: Val[T]): Val[T] = ValGroup[T](None, List(this, that))
1332

33+
/**
34+
* Functional mapping of this Val into another Val.
35+
*
36+
* @param f conversion function
37+
* @tparam R the type of the new Val
38+
* @return Val[R]
39+
*/
1440
def map[R](f: T => R): Val[R] = Val[R](f(get))
1541

1642
override def toString: String = name.getOrElse("Val")

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

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,53 @@ import reactify.group.VarGroup
77
import reactify.reaction.Reaction
88
import reactify.standard.StandardVar
99

10+
/**
11+
* Var represents the combination of `Val` and `Channel` into a stateful and mutable underlying value.
12+
*
13+
* @tparam T the type of value this Reactive receives
14+
*/
1015
trait Var[T] extends Val[T] with Channel[T] {
16+
/**
17+
* Convenience functionality to attach a Reaction and immediately fire the current state on the Reaction.
18+
*
19+
* @param f the function reaction
20+
* @param priority the priority in comparison to other reactions (Defaults to Priority.Normal)
21+
* @return Reaction[T]
22+
*/
1123
def attachAndFire(f: T => Unit, priority: Double = Priority.Normal): Reaction[T] = {
1224
val reaction = attach(f, priority)
13-
fire(get, Some(get), reactions())
25+
fire(get, Some(get), List(reaction))
1426
reaction
1527
}
1628

29+
/**
30+
* Group multiple Vars together
31+
*/
1732
def and(that: Var[T]): Var[T] = VarGroup[T](None, List(this, that))
1833

34+
/**
35+
* Functional mapping of this Var into another Var.
36+
*
37+
* @param f conversion function
38+
* @tparam R the type of the new Var
39+
* @return Var[R]
40+
*/
1941
override def map[R](f: T => R): Var[R] = {
2042
val v = Var[R](f(get))
2143
attach(v := f(_))
2244
v
2345
}
2446

47+
/**
48+
* Convenience method to create a binding between two `Var`s
49+
*
50+
* @param that the second `Var` to bind between
51+
* @param setNow the `BindSet` value (Defaults to LeftToRight)
52+
* @param t2v implicit function conversion from T to V
53+
* @param v2t implicit function conversion from V to T
54+
* @tparam V the type of the second `Var`
55+
* @return Binding[T, V]
56+
*/
2557
def bind[V](that: Var[V], setNow: BindSet = BindSet.LeftToRight)
2658
(implicit t2v: T => V, v2t: V => T): Binding[T, V] = {
2759
setNow match {

shared/src/test/scala/test/DepSpec.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,4 +165,4 @@ class DepSpec extends WordSpec with Matchers {
165165
))
166166
}
167167
}
168-
}
168+
}

0 commit comments

Comments
 (0)