Rx

com.thoughtworks.binding.Binding$.Rx$
object Rx

Reactive operators for Observables.

Attributes

See also:
Note:

Rx operators are incomplete. Feel free to create a Pull Request if you need a certain operator.

Graph
Supertypes
class Object
trait Matchable
class Any
Self type
Rx.type

Members list

Concise view

Type members

Types

type Observable[A] = Binding[Option[A]]

A Binding that can be terminated.

A Binding that can be terminated.

Once the value turned into a scala.None, this Observable would be considered as terminated, and any future changes of this Observable will be ignored by any Rx operators derived from this Observable, even if this Observable turns into a scala.Some value again.

Attributes

Note:

Even though an Observable is technically a Binding, an Observable created from a Rx operator does not actually indicates data-binding. For example, given an Observable created from Rx.concat,

 import com.thoughtworks.binding.Binding._
 val sourceObservable0 = Var[Option[String]](Some("0"))
 val sourceObservable1 = Var[Option[String]](Some("1"))
 val sourceObservables = List(sourceObservable0, sourceObservable1)
 val derivedObservable = Rx.concat(sourceObservables)
 derivedObservable.watch()

when a source value gets changed,

 val originalDerivedObservableValue = derivedObservable.get
 sourceObservable0.value = None

and the source value is changed back to the original value,

 sourceObservable0.value = Some("0")

then the value of the derived observable might not be the original value.

 derivedObservable.get shouldNot be(originalDerivedObservableValue)

In contrast, if the concat operator is implemented by ordinary Binding.bind macros, the derived Binding is indeed a data-binding, i.e. it always perform the same calculation for the same values of source Bindings.

 import com.thoughtworks.binding.Binding._
 val sourceBinding0 = Var[Option[String]](Some("0"))
 val sourceBinding1 = Var[Option[String]](Some("1"))
 val sourceBindings = List(sourceBinding0, sourceBinding1)
 def concatBinding(
     sourceBindings: collection.LinearSeq[Rx.Observable[String]]
 ): Rx.Observable[String] = {
   sourceBindings match {
     case head +: tail =>
       Binding {
         head.bind match {
           case None =>
             concatBinding(tail).bind
           case someValue =>
             someValue
         }
       }
     case _ =>
       Constant(None)
   }
 }
 val derivedBinding = concatBinding(sourceBindings)
 derivedBinding.watch()
 val originalDerivedBindingValue = derivedBinding.get
 sourceBinding0.value = None
 sourceBinding0.value = Some("0")
 derivedBinding.get should be(originalDerivedBindingValue)

Value members

Concrete methods

def concat[A](observables: IterableOnce[Observable[A]]): Observable[A]

Emit the emissions from two or more Observables without interleaving them.

Emit the emissions from two or more Observables without interleaving them.

Attributes

See also:
Example:

Given a sequence of Observables,

import com.thoughtworks.binding.Binding._
val observable0 = Var[Option[String]](None)
val observable1 = Var[Option[String]](Some("1"))
val observable2 = Var[Option[String]](Some("2"))
val observable3 = Var[Option[String]](None)
val observable4 = Var[Option[String]](None)
val observable7 = Var[Option[String]](Some("7"))
val observable8 = Binding { observable7.bind.map { v => s"8-$v-derived" } }
val observable5 = Binding { observable7.bind.map { v => s"5-$v-derived" } }
val observable6 = Var[Option[String]](None)
val observable9 = Var[Option[String]](Some("9"))
val observables = Seq(
 observable0,
 observable1,
 observable2,
 observable3,
 observable4,
 observable5,
 observable6,
 observable7,
 observable8,
 observable9,
)

when concatenate them together,

val concatenated = Rx.concat(observables).map(identity)
concatenated.watch()

the concatenated value should be the first scala.Some value in the sequence of observables;

concatenated.get should be(Some("1"))

when the current observable becomes None,

observable1.value = None

the concatenated value should be the next scala.Some value in the sequence of observables,

concatenated.get should be(Some("2"))

even when the next scala.Some value is derived from another Binding;

observable2.value = None
concatenated.get should be(Some("5-7-derived"))

when the value of the upstream Binding is changed to another scala.Some value,

observable7.value = Some("7-running")

the concatenated value should be changed accordingly;

concatenated.get should be(Some("5-7-running-derived"))

when multiple observables become scala.None at once,

observable7.value = None

they all should be skipped when calculate the concatenated value;

concatenated.get should be(Some("9"))

when the last observable in the sequence becomes scala.None,

observable9.value = None

the concatenated value should become scala.None permanently,

concatenated.get should be(None)

even when some observables in the sequence become scala.Some again.

observable9.value = Some("9-after-termination")
concatenated.get should be(None)
observable7.value = Some("7-after-termination")
concatenated.get should be(None)
def defer[A](upstream: => Observable[A]): Observable[A]

do not create the Observable until the observer subscribes

do not create the Observable until the observer subscribes

Attributes

See also:
Note:

This defer is slightly different from other implementation the ReactiveX Defer operator, because this defer shares the same upstream Observable instance for all subscribes.

Example:

Circular referenced Observables can be created with the help of defer

 import Binding._
 val source = Var("init")
 lazy val observable1: Rx.Observable[String] =
   Binding[Option[String]] {
     source.bind match {
       case "init" =>
         None
       case v =>
         observable2.bind.map(_ + "_" + v)
     }
   }
 lazy val observable2: Rx.Observable[String] = Rx.defer(
   Binding[Option[String]] {
     Some(observable1.getClass.getSimpleName)
   }
 )

Initially, observable1 did not subscribe observable2 because source is init,

 observable1.watch()

therefore observable2 should be None,

 observable1.get should be (None)
 observable2.get should be (None)

when source changed,

 source.value = "changed"

observable1 should subscribe observable2, and there should be Some values.

 observable1.get should be (Some("FlatMap_changed"))
 observable2.get should be (Some("FlatMap"))

Even though circular referenced Observables can be created in this way, their calculation must not be mutually dependent.

def merge[A](bindingSeq: BindingSeq[A]): Observable[A]

Attributes

See also:
def merge[A](observables: IterableOnce[Observable[A]]): Observable[A]

Combine multiple Observables into one by merging their emissions.

Combine multiple Observables into one by merging their emissions.

Attributes

See also:
Example:

Given a sequence of Observables,

import com.thoughtworks.binding.Binding._
val observable0 = Var[Option[String]](None)
val observable1 = Var[Option[String]](Some("1"))
val observable2 = Var[Option[String]](Some("2"))
val observable3 = Var[Option[String]](None)
val observable4 = Var[Option[String]](None)
val observable7 = Var[Option[String]](Some("7"))
val observable8 = Binding { observable7.bind.map { v => s"8-$v-derived" } }
val observable5 = Binding { observable7.bind.map { v => s"5-$v-derived" } }
val observable6 = Var[Option[String]](None)
val observable9 = Var[Option[String]](Some("9"))
val observables = Seq(
 observable0,
 observable1,
 observable2,
 observable3,
 observable4,
 observable5,
 observable6,
 observable7,
 observable8,
 observable9,
)

when merge them together,

val merged = Rx.merge(observables).map(identity)
merged.watch()

the merged value should be the first scala.Some value in the sequence of observables;

merged.get should be(Some("1"))

when the some but not all of the observable becomes None,

observable1.value = None

the merged value should be unchanged,

merged.get should be(Some("1"))

when any of the observable becomes Some value,

observable2.value = Some("2-changed")

the merged value should be the new value,

merged.get should be(Some("2-changed"))

even when a previous None observable becomes Some value,,

observable3.value = Some("3-previous-None")

the merged value should be the new value of the previous None observable,

merged.get should be(Some("3-previous-None"))

when multiple observables are changed at once,

observable7.value = Some("7-changed")

the merged value should be the value of the last derived observable

merged.get should be(Some("8-7-changed-derived"))

when all the observables become None,

observable1.value = None
merged.get should be(Some("8-7-changed-derived"))
observable2.value = None
merged.get should be(Some("8-7-changed-derived"))
observable3.value = None
merged.get should be(Some("8-7-changed-derived"))
observable6.value = None
merged.get should be(Some("8-7-changed-derived"))
observable7.value = None
merged.get should be(Some("8-7-changed-derived"))
observable9.value = None
merged.get should be(None)
def repeat[A](source: => Observable[A]): Observable[A]

Attributes

See also:
def toBindingSeq[A](observable: Observable[A]): BindingSeq[A]

convert an Observable into a BindingSeq.

convert an Observable into a BindingSeq.

Attributes

See also:
Example:

Given a source observable,

 import com.thoughtworks.binding.Binding._
 val observable = Var[Option[String]](Some("1"))

when converting it into a BindingSeq,

 val bindingSeq = Rx.toBindingSeq(observable)

and flat-mapping to the result,

 val result = new BindingSeq.FlatMap(
   bindingSeq,
   { value: String => Constants("the value is", value) }
 ).all
 result.watch()

then result should have the values corresponding to the source observable,

 result.get.toSeq should contain theSameElementsInOrderAs Seq("the value is", "1")

when the source observable changes,

 observable.value = Some("2")

then the corresponding new value should be appended to the result,

 result.get.toSeq should contain theSameElementsInOrderAs Seq(
   "the value is", "1",
   "the value is", "2"
 )

when the source observable terminates,

 observable.value = None

then the result should be empty

 result.get.toSeq should be(empty)