├── .gitignore ├── README.md ├── build.sbt ├── project └── plugins.sbt └── src ├── Main.scala └── test └── scala └── coursera ├── Extensions.scala ├── Utils.scala ├── adventure ├── Coin.scala ├── GameOver.scala ├── Treasure.scala ├── safe │ └── SafeAdventure.scala └── unsafe │ └── Adventure.scala ├── combinators └── Combinators.scala ├── geocode ├── Geocode.scala └── countrySubdivision.scala ├── iterable └── ReadLinesFromDisk.scala ├── rx ├── Blocking.scala ├── Creation.scala ├── EarthQuakes.scala ├── HelloObservables.scala ├── Nested.scala ├── Quizzes.scala ├── Schedulers.scala ├── Subjects.scala ├── Subscriptions.scala └── ToObservable.scala ├── socket ├── EmailMessage.scala ├── safe │ ├── Http.scala │ └── Socket.scala └── unsafe │ └── Socket.scala └── usgs ├── Feature.scala ├── FeatureCollection.scala ├── Magnitude.scala ├── MetaData.scala ├── Point.scala ├── Properties.scala └── Usgs.scala /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # made by sbt 3 | target/ 4 | 5 | # made by eclipse 6 | # you can create the eclipse project file by running `sbt eclipse` 7 | .cache 8 | .classpath 9 | .project 10 | .settings 11 | 12 | # made by idea 13 | # you can create the intellij idea project file by running `sbt gen-idea` 14 | .idea 15 | .idea_modules 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Coursera Principles of Reactive Programming Samples: Futures & Promises, Rx 2 | --------------------------------------------------------------------------- 3 | 4 | `src/test/scala/coursera/adventure` contains the sample code for the "adventure game" lessons. 5 | It introduces the `Try[T]` monad that makes failure explicit. 6 | 7 | `src/test/scala/coursera/socket` contains the sample code for the "network programming" lessons. 8 | It introduces the `Future[T]` monad that makes latency and failure explicit. 9 | 10 | `src/test/scala/coursera/combinators` and `src/test/scala/coursera/Extensions.scala` contain various combinators and extensions on `Future[T]` 11 | using both `async{ await{} }` and `val p = Promise[T](); ...; p.future`. 12 | 13 | `src/test/scala/coursera/geocode` and `src/test/scala/coursera/usgs` contain bindings and code for accessing the usgs 14 | earthquake and the geonames.org reverse geocode Web services. Earthequakes are exposed as an `Observablestream, 15 | while rever geocoding of a single lat/lng pair returns a `Future`. 16 | 17 | `src/test/scala/coursera/iterable` shows how to do blocking file IO using iterables. 18 | 19 | `src/test/scala/coursera/rx` contains the sample code for all the Rx examples, allowing you to play with `Subscription`, 20 | `Observable[T]` and `Observer[T]`, the various subjects, and (TODO) schedulers. 21 | 22 | Inevitably slides contain typos because we don't have IntelliJ integration in Keynote yet. 23 | When in doubt, consult the code samples here for the correct code. 24 | 25 | ### Running the examples 26 | 27 | You can build and run some of the examples with sbt and Eclipse or sbt and IntelliJ. 28 | 29 | With sbt only: In the sbt console, execute `test-only `, 30 | for instance `test-only coursera.rx.Subscriptions`. 31 | 32 | With sbt+Eclipse: In the sbt console, execute `eclipse`, then import the project into eclipse, 33 | right-click the name of the test you're interested in, and choose "Run As" > "Scala JUnit Test". 34 | 35 | With sbt+IntelliJ: In the sbt console, execute `gen-idea`, then open the project in IntelliJ, 36 | right-click the name of the test you're interested in, and choose "Run '_TestName_'". 37 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | name := "CourseraCodeSamplesReactiveProgramming" 2 | 3 | version := "1.0" 4 | 5 | scalaVersion := "2.11.6" 6 | 7 | scalacOptions ++= Seq("-deprecation", "-feature") 8 | 9 | libraryDependencies ++= Seq( 10 | "com.netflix.rxjava" % "rxjava-scala" % "0.15.1", 11 | "org.scalatest" %% "scalatest" % "2.2.4", 12 | "junit" % "junit" % "4.11", 13 | "org.scala-lang.modules" %% "scala-async" % "0.9.2", 14 | "com.squareup.retrofit" % "retrofit" % "1.2.2", 15 | "com.typesafe.akka" %% "akka-actor" % "2.3.10" 16 | ) 17 | 18 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "3.0.0") 2 | 3 | addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.5.2") 4 | -------------------------------------------------------------------------------- /src/Main.scala: -------------------------------------------------------------------------------- 1 | import coursera.adventure.unsafe.Adventure 2 | import coursera.rx._ 3 | import coursera.usgs.{Magnitude, Usgs} 4 | import rx.lang.scala.{Scheduler, Observable} 5 | import rx.lang.scala.subjects._ 6 | 7 | import scala.language.postfixOps 8 | import scala.concurrent.duration._ 9 | 10 | object Main { 11 | def main(args: Array[String]): Unit = { 12 | // coursera.rx.HelloObservables.ticks() 13 | // Quizzes.quizI() 14 | // Nested.flattenNestedStreams() 15 | // Nested.concatenateNestedStreams() 16 | // EarthQuakes.quakes().subscribe(println(_)) 17 | // EarthQuakes.major().subscribe(println(_)) 18 | // EarthQuakes.ofMagnitude(Magnitude.Minor).subscribe(println(_)) 19 | // EarthQuakes.withCountryMerged().subscribe(println(_), e => println(e.getMessage), () => println(">>>>>>")) 20 | // EarthQuakes.withCountryConcatenated().subscribe(println(_), e => println(e.getMessage), () => println(">>>>>>")) 21 | // EarthQuakes.groupedByCountry().subscribe(println(_), e => println(e.getMessage), () => println(">>>>>>")) 22 | // Subscriptions.FishingI() 23 | // Subscriptions.FishingII() 24 | // Subscriptions.Composite() 25 | // Subscriptions.Multi() 26 | // Creation.from(Range(1,10).inclusive).subscribe(println(_)) 27 | // Creation.startWith(Observable(4,5,6), 0, 1, 2, 3).subscribe(println(_)) 28 | // Creation.filter(Observable(Range(1,10).inclusive), (x: Int) => x%2 == 0).subscribe(println(_)) 29 | // Creation.map(Observable(Range(1,10).inclusive), (x: Int) => x*2).subscribe(println(_)) 30 | // coursera.rx.Subjects.PublishSubjectIsAChannel() 31 | // Subjects.ReplaySubjectIsAChannel() 32 | // Subjects.AsyncSubjectIsAFuture() 33 | // Subjects.BehaviorSubjectIsACache() 34 | // Blocking.dontDoThisAtHomeKids() 35 | 36 | 37 | 38 | } 39 | 40 | } 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/test/scala/coursera/Extensions.scala: -------------------------------------------------------------------------------- 1 | package coursera 2 | 3 | import scala.concurrent.{ExecutionContext, Future} 4 | import scala.util.{Failure, Success, Try} 5 | 6 | package object extensions { 7 | 8 | implicit class ListExtensions[T](val source: List[T]) extends AnyVal { 9 | def sumBy(keySelector: T => Int): Int = ??? 10 | 11 | private def sumBy[B](keySelector: T => B)(implicit num: Numeric[B]): B = { 12 | source.map(keySelector).sum 13 | } 14 | 15 | } 16 | 17 | def f[T](that: Future[T]): PartialFunction[Throwable, Future[T]] = { case _: Throwable => that } 18 | def g[T]: PartialFunction[Throwable, Failure[T]] = { case t: Throwable => Failure(t) } 19 | 20 | implicit class FutureExtensions[T](val future: Future[T]) extends AnyVal { 21 | 22 | def fallbackTo[U >: T](that: =>Future[U])(implicit executor: ExecutionContext): Future[U] = { 23 | future.recoverWith(f(that.recoverWith(f(future)))) 24 | } 25 | 26 | def withTry()(implicit executor: ExecutionContext): Future[Try[T]] = { 27 | future.map(Success(_)) recover g 28 | } 29 | } 30 | 31 | def withTry[T](future: Future[T])(implicit executor: ExecutionContext): Future[Try[T]] = { 32 | future. 33 | future.map(Success(_)) recover { case t: Throwable => Failure(t) } 34 | } 35 | 36 | def fallbackTo[U](future: Future[U], that: =>Future[U])(implicit executor: ExecutionContext): Future[U] = { 37 | future.recoverWith { case _: Throwable => that.recoverWith { case _: Throwable => future } } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/test/scala/coursera/Utils.scala: -------------------------------------------------------------------------------- 1 | package coursera 2 | 3 | import scala.concurrent.duration._ 4 | import org.junit.Test 5 | import org.scalatest.junit.JUnitSuite 6 | import _root_.rx.lang.scala.Observable 7 | import _root_.rx.lang.scala.Notification 8 | 9 | object Utils { 10 | 11 | /** 12 | * Print an observable to stdout, blocking the calling thread. 13 | */ 14 | def displayObservable[T](o: Observable[T]): Unit = { 15 | println() 16 | toPrintableNotifications(o).toBlockingObservable.foreach(println(_)) 17 | println() 18 | } 19 | 20 | def toPrintableNotifications[T](o: Observable[T]): Observable[String] = { 21 | val t0 = System.currentTimeMillis 22 | for ((millis, notif) <- o.materialize.timestamp) 23 | yield f"t = ${(millis-t0)/1000.0}%.3f: ${notificationToString(notif)}" 24 | } 25 | 26 | /** 27 | * does what Notification.toString (or its subclasses) should do 28 | */ 29 | def notificationToString[T](n: Notification[T]): String = n match { 30 | case Notification.OnNext(value) => s"OnNext($value)" 31 | case Notification.OnError(err) => s"OnError($err)" 32 | case Notification.OnCompleted(()) => "OnCompleted()" 33 | } 34 | 35 | implicit class ExtendedObservable[T](o: Observable[T]) { 36 | def doOnEach(onNext: T => Unit): Observable[T] = { 37 | val action: _root_.rx.util.functions.Action1[T] = _root_.rx.lang.scala.ImplicitFunctionConversions.scalaFunction1ProducingUnitToAction1(onNext) 38 | // casting because Java Observable lacks "? super T" 39 | val jObs: _root_.rx.Observable[T] = o.asJavaObservable.asInstanceOf[_root_.rx.Observable[T]].doOnEach(action) 40 | Observable[T](jObs) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/scala/coursera/adventure/Coin.scala: -------------------------------------------------------------------------------- 1 | package coursera.adventure 2 | 3 | class Coin(val value: Int){} 4 | 5 | case class Gold() extends Coin(value = 200) {} 6 | case class Silver() extends Coin(value = 100) {} 7 | 8 | -------------------------------------------------------------------------------- /src/test/scala/coursera/adventure/GameOver.scala: -------------------------------------------------------------------------------- 1 | package coursera.adventure 2 | 3 | class GameOver(message: String) extends Exception {} 4 | -------------------------------------------------------------------------------- /src/test/scala/coursera/adventure/Treasure.scala: -------------------------------------------------------------------------------- 1 | package coursera.adventure 2 | 3 | class Treasure(){} 4 | 5 | case class Diamond() extends Treasure {} 6 | -------------------------------------------------------------------------------- /src/test/scala/coursera/adventure/safe/SafeAdventure.scala: -------------------------------------------------------------------------------- 1 | package coursera.adventure.safe 2 | 3 | import coursera.adventure._ 4 | import scala.util.{Failure, Success, Try} 5 | 6 | object Adventure { 7 | def apply(): Adventure = new Adventure(){ 8 | var eatenByMonster: Boolean = true 9 | val treasureCost: Int = 42 10 | } 11 | } 12 | 13 | trait Adventure { 14 | 15 | var eatenByMonster: Boolean 16 | val treasureCost: Int 17 | 18 | def collectCoins(): Try[List[Coin]] = ??? 19 | 20 | def buyTreasure(coins: List[Coin]): Try[Treasure] = ??? 21 | 22 | def PlayI(): Unit = { 23 | val adventure = Adventure() 24 | val coins: Try[List[Coin]] = adventure.collectCoins() 25 | val treasure: Try[Treasure] = coins match { 26 | case Success(cs) => adventure.buyTreasure(cs) 27 | case Failure(t) => Failure(t) 28 | } 29 | } 30 | 31 | def PlayII(): Unit = { 32 | val adventure = Adventure() 33 | val coins: Try[List[Coin]] = adventure.collectCoins() 34 | val treasure: Try[Treasure] = coins.flatMap(cs => adventure.buyTreasure(cs)) 35 | } 36 | 37 | def PlayIII(): Unit = { 38 | val adventure = Adventure() 39 | val treasure: Try[Treasure] = for { 40 | coins <- adventure.collectCoins() 41 | treasure <- buyTreasure(coins) 42 | } yield treasure 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/scala/coursera/adventure/unsafe/Adventure.scala: -------------------------------------------------------------------------------- 1 | package coursera.adventure.unsafe 2 | 3 | import coursera.adventure._ 4 | import coursera.adventure.Silver 5 | import coursera.adventure.Gold 6 | import coursera.extensions 7 | 8 | object Adventure { 9 | def apply(): Adventure = new Adventure(){ 10 | var eatenByMonster: Boolean = true 11 | val treasureCost: Int = 42 12 | } 13 | } 14 | 15 | trait Adventure { 16 | import extensions._ 17 | 18 | var eatenByMonster: Boolean 19 | val treasureCost: Int 20 | 21 | def collectCoins(): List[Coin] = { 22 | if (eatenByMonster) throw new GameOver("Ooops") 23 | List(Gold(), Gold(), Silver()) 24 | } 25 | 26 | def buyTreasure(coins: List[Coin]): Treasure = { 27 | coins.sumBy(x => x.value) < treasureCost 28 | if (true) throw new GameOver("Nice try!") 29 | Diamond() 30 | } 31 | 32 | def Play() : Unit = { 33 | val adventure = Adventure() 34 | val coins = adventure.collectCoins() 35 | val treasure = adventure.buyTreasure(coins) 36 | } 37 | 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/test/scala/coursera/combinators/Combinators.scala: -------------------------------------------------------------------------------- 1 | package coursera 2 | 3 | import scala.concurrent.{Promise, ExecutionContext, Future} 4 | import scala.async.Async._ 5 | import scala.util.{Success, Failure, Try} 6 | import coursera.extensions._ 7 | 8 | package object combinators { 9 | 10 | def retryI[T](n: Int)(block: =>Future[T]): Future[T] = { 11 | if (n == 0) { 12 | Future.failed(new Exception("Sorry")) 13 | } else { 14 | block fallbackTo { retryI(n-1) { block } } 15 | } 16 | } 17 | 18 | def retryII[T](n: Int)(block: =>Future[T]): Future[T] = { 19 | val ns: Iterator[Int] = (1 to n).iterator 20 | val attempts: Iterator[()=>Future[T]] = ns.map(_ => ()=>block) 21 | val failed: Future[T] = Future.failed(new Exception) 22 | attempts.foldLeft(failed)((a, block) => a fallbackTo { block() }) 23 | } 24 | 25 | def retryIII[T](n: Int)(block: =>Future[T]): Future[T] = { 26 | val ns: Iterator[Int] = (1 to n).iterator 27 | val attempts: Iterator[()=>Future[T]] = ns.map(_ => ()=>block) 28 | val failed: Future[T] = Future.failed(new Exception) 29 | attempts.foldRight(()=>failed)((block, a) => ()=> { block() fallbackTo{ a() }}) () 30 | } 31 | 32 | def retryIV[T](n: Int)(block: =>Future[T])(implicit executor: ExecutionContext): Future[T] = async { 33 | var i: Int = 0 34 | var result: Try[T] = Failure(new Exception("Oops")) 35 | 36 | while (i < n) { 37 | result = await { block.withTry() } 38 | 39 | result match { 40 | case Success(s) => { i = n } 41 | case Failure(f) => { i += 1 } 42 | } 43 | } 44 | 45 | result.get 46 | } 47 | 48 | def filterI[T](future: Future[T], predicate: T => Boolean)(implicit executor: ExecutionContext): Future[T] = async{ 49 | val x: T = await{ future } 50 | if(!predicate(x)) { 51 | throw new NoSuchElementException("No such element") 52 | } else { 53 | x 54 | } 55 | } 56 | 57 | def filterII[T](future: Future[T], predicate: T => Boolean)(implicit executor: ExecutionContext): Future[T] = { 58 | val p = Promise[T]() 59 | future.onComplete { 60 | case Success(s) => { 61 | if(!predicate(s)) { 62 | p.failure(new NoSuchElementException("No such element")) 63 | } else { 64 | p.success(s) 65 | } 66 | } 67 | case Failure(f) => { p.failure(f) } 68 | } 69 | p.future 70 | } 71 | 72 | def flatMap[T,S](future: Future[T], selector: T => Future[S])(implicit executor: ExecutionContext): Future[S] = async{ 73 | val x: T = await{ future } 74 | await{ selector(x) }: S 75 | } 76 | 77 | def race[T](left: Future[T], right: Future[T])(implicit executor: ExecutionContext): Future[T] = { 78 | val p = Promise[T]() 79 | 80 | left onComplete { p.tryComplete } 81 | right onComplete { p.tryComplete } 82 | 83 | p.future 84 | } 85 | 86 | def zipI[T, S, R](future: Future[T], other: Future[S], combine: (T, S) => R) 87 | (implicit executor: ExecutionContext): Future[R] = async { 88 | combine(await{ future }: T, await{ other }: S) 89 | } 90 | 91 | def zipII[T, S, R](future: Future[T], other: Future[S], combine: (T, S) => R) 92 | (implicit executor: ExecutionContext): Future[R] = { 93 | val p = Promise[R]() 94 | 95 | future onComplete { 96 | case Failure(f) => { p.failure(f) } 97 | case Success(t) => { other onComplete { 98 | case Failure(f) => { p.failure(f) } 99 | case Success(s) => p.success(combine(t,s)) 100 | }} 101 | } 102 | 103 | p.future 104 | } 105 | 106 | def never[T]()(implicit executor: ExecutionContext): Future[T] = { Promise[T].future } 107 | } -------------------------------------------------------------------------------- /src/test/scala/coursera/geocode/Geocode.scala: -------------------------------------------------------------------------------- 1 | package coursera.geocode 2 | 3 | import retrofit.http.{Query, GET} 4 | import retrofit.{RetrofitError, RestAdapter, Callback} 5 | import scala.concurrent.{Promise, Future} 6 | import retrofit.http.{Query, Path, GET} 7 | import retrofit.{RetrofitError, RestAdapter, Callback} 8 | import scala.concurrent.{ExecutionContext, Promise, Future} 9 | import retrofit.client.Response 10 | import coursera.usgs.Point 11 | 12 | object ReverseGeocode { 13 | 14 | private val restAdapter = new RestAdapter.Builder().setServer("http://ws.geonames.org").build() 15 | 16 | def apply(point: Point): Future[CountrySubdivision] = { 17 | ReverseGeocode(point.latitude, point.longitude) 18 | } 19 | 20 | def apply(latitude: Double, longitude: Double): Future[CountrySubdivision] = { 21 | 22 | // Promise/Future is isomorphic to Observer/Observable as a Subject 23 | 24 | val promise = Promise[CountrySubdivision]() 25 | 26 | restAdapter.create(classOf[ReverseGeocode]).get(latitude, longitude, new Callback[CountrySubdivision] { 27 | 28 | def failure(error: RetrofitError): Unit = { 29 | promise.failure(error.getCause) 30 | } 31 | 32 | def success(t: CountrySubdivision, response: Response): Unit = { 33 | promise.success(t) 34 | } 35 | 36 | }) 37 | 38 | promise.future 39 | } 40 | } 41 | 42 | private trait ReverseGeocode { 43 | @GET("/countrySubdivisionJSON") 44 | def get(@Query("lat")latitude: Double, @Query("lng")longitude: Double, callback: Callback[CountrySubdivision]) 45 | } 46 | -------------------------------------------------------------------------------- /src/test/scala/coursera/geocode/countrySubdivision.scala: -------------------------------------------------------------------------------- 1 | package coursera.geocode 2 | 3 | class CountrySubdivision { 4 | val countryCode: String = null 5 | var countryName: String = null 6 | val adminCode1: String = null 7 | val adminName1: String = null 8 | 9 | override def toString(): String = { 10 | s"{ 'countryCode':'${countryCode}', 'countryName':'${countryName}', 'adminCode1':'${adminCode1}', 'adminName1':'${adminName1}'}"; 11 | } 12 | } -------------------------------------------------------------------------------- /src/test/scala/coursera/iterable/ReadLinesFromDisk.scala: -------------------------------------------------------------------------------- 1 | package coursera.iterable 2 | 3 | import scala.io.Source 4 | 5 | class Blocking { 6 | 7 | def ReadLinesFromDisk(path: String): Iterator[String] = { 8 | Source.fromFile(path).getLines() 9 | } 10 | 11 | def Example(): Unit = { 12 | val lines = ReadLinesFromDisk("\\c:\\tmp.txt") 13 | 14 | for(line <- lines) { 15 | ??? 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/scala/coursera/rx/Blocking.scala: -------------------------------------------------------------------------------- 1 | package coursera.rx 2 | 3 | import rx.lang.scala.Observable 4 | 5 | import scala.language.postfixOps 6 | import scala.concurrent.duration._ 7 | import org.junit.Test 8 | import org.scalatest.junit.JUnitSuite 9 | 10 | 11 | class Blocking extends JUnitSuite { 12 | 13 | // List(0, 1, 2, 3, 4) 14 | // 10 15 | @Test def dontDoThisAtHomeKids(): Unit = { 16 | 17 | val xs: Observable[Long] = Observable.interval(1 second).take(5) 18 | 19 | val ys: List[Long] = xs.toBlockingObservable.toList 20 | println(ys) 21 | 22 | val zs: Observable[Long] = xs.sum 23 | 24 | val sum: Long = zs.toBlockingObservable.single 25 | println(sum) 26 | 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/test/scala/coursera/rx/Creation.scala: -------------------------------------------------------------------------------- 1 | package coursera.rx 2 | 3 | import rx.lang.scala._ 4 | import rx.lang.scala.subscriptions._ 5 | import scala.concurrent.{ExecutionContext, Future} 6 | import scala.async.Async._ 7 | 8 | 9 | object Creation { 10 | 11 | def from[T](source: Iterable[T]): Observable[T] = { 12 | Observable(observer => { 13 | source.foreach(observer.onNext(_)) 14 | observer.onCompleted() 15 | 16 | // When you return an empty subscription 17 | // the alarm bells should go off. 18 | Subscription{} 19 | }) 20 | } 21 | 22 | def never[T]: Observable[T] = { 23 | // except here 24 | Observable(observer => Subscription{}) 25 | } 26 | 27 | def empty[T](): Observable[T] = { 28 | Observable(observer => { 29 | // here 30 | observer.onCompleted() 31 | Subscription{} 32 | }) 33 | } 34 | 35 | def error[T](error: Throwable): Observable[T] = { 36 | Observable(observer => { 37 | // here 38 | observer.onError(error) 39 | Subscription{} 40 | }) 41 | } 42 | 43 | def single[T](value: T): Observable[T] = { 44 | Observable(observer => { 45 | // and here 46 | observer.onNext(value) 47 | Subscription{} 48 | }) 49 | } 50 | 51 | def startWith[T](source: Observable[T], values: T*): Observable[T] = { 52 | Observable(observer => { 53 | values.foreach(observer.onNext) 54 | source.subscribe(observer) 55 | }) 56 | } 57 | 58 | def filter[T](source: Observable[T], predicate: T=>Boolean): Observable[T] = { 59 | Observable(observer => { 60 | source.subscribe( 61 | value => if(predicate(value)) observer.onNext(value), 62 | error => observer.onError(error), 63 | () => observer.onCompleted 64 | ) 65 | }) 66 | } 67 | 68 | def map[T, R](source: Observable[T], selector: T=>R): Observable[R] = { 69 | Observable(observer => { 70 | source.subscribe( 71 | value => observer.onNext(selector(value)), 72 | error => observer.onError(error), 73 | () => observer.onCompleted 74 | ) 75 | }) 76 | } 77 | 78 | def map[T,R](source: Iterable[T], selector: T=>R): Iterable[R] = { 79 | new Iterable[R] { 80 | 81 | def iterator = new Iterator[R] { 82 | 83 | // should grab outer here. 84 | val outer: Iterator[T] = source.iterator 85 | 86 | def hasNext: Boolean = { 87 | outer.hasNext 88 | } 89 | 90 | def next(): R = { 91 | selector(outer.next()) 92 | } 93 | } 94 | } 95 | } 96 | 97 | def map[T,R](source: Future[T], selector: T=>R)(implicit executor: ExecutionContext): Future[R] = async { 98 | selector(await{ source }) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/test/scala/coursera/rx/EarthQuakes.scala: -------------------------------------------------------------------------------- 1 | package coursera.rx 2 | 3 | import rx.lang.scala.Observable 4 | import scala.languageFeature.postfixOps._ 5 | import coursera.usgs.{Point, Usgs, Feature, Magnitude} 6 | import coursera.usgs.Magnitude.Magnitude 7 | import coursera.geocode.{ReverseGeocode, CountrySubdivision} 8 | import scala.concurrent.{ExecutionContext, Future} 9 | import ExecutionContext.Implicits.global 10 | 11 | 12 | object EarthQuakes { 13 | 14 | def quakes(): Observable[Feature] = Usgs() 15 | 16 | def major(): Observable[(Point, Magnitude)] = ofMagnitude(Magnitude.Major) 17 | 18 | def ofMagnitude(atLeast: Magnitude) = { 19 | 20 | quakes().map(quake => (quake.geometry, Magnitude(quake.properties.magnitude))).filter { 21 | case (location, magnitude) => magnitude >= atLeast 22 | } 23 | 24 | } 25 | 26 | def withCountry(flatten: Observable[Observable[(Feature, CountrySubdivision)]] => Observable[(Feature, CountrySubdivision)]) 27 | : Observable[(Feature, CountrySubdivision)] = { 28 | flatten(quakes().map(quake => { 29 | val country: Future[CountrySubdivision] = ReverseGeocode(quake.geometry) 30 | ToObservable(country.map(country => (quake,country))) 31 | .filter{ case(quake, country) => country.countryName != null } 32 | })) 33 | } 34 | 35 | def withCountryMerged(): Observable[(Feature, CountrySubdivision)] = { 36 | withCountry(xss => xss.flatten) 37 | } 38 | 39 | def withCountryConcatenated(): Observable[(Feature, CountrySubdivision)] = { 40 | withCountry(xss => xss.concat) 41 | } 42 | 43 | def groupedByCountry(): Observable[(String, Observable[(Feature, CountrySubdivision)])] = { 44 | withCountryMerged().groupBy{ case (quake, country) => country.countryName } 45 | } 46 | } 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/test/scala/coursera/rx/HelloObservables.scala: -------------------------------------------------------------------------------- 1 | package coursera.rx 2 | 3 | import rx.lang.scala.{Observable, Subscription} 4 | import scala.language.postfixOps 5 | import scala.concurrent.duration._ 6 | import org.junit.Test 7 | import org.scalatest.junit.JUnitSuite 8 | import scala.concurrent.Promise 9 | 10 | 11 | class HelloObservables extends JUnitSuite { 12 | 13 | @Test def ticks(): Unit = { 14 | 15 | val ticks: Observable[Long] = Observable.interval(1 second) 16 | val evens: Observable[Long] = ticks.filter(s => s%2 == 0) 17 | val buffers: Observable[Seq[Long]] = evens.buffer(2,1) 18 | 19 | // run the program for a while 20 | val subscription: Subscription = buffers.subscribe(println(_)) 21 | 22 | readLine() 23 | 24 | // stop the stream 25 | subscription.unsubscribe() 26 | } 27 | 28 | } 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/test/scala/coursera/rx/Nested.scala: -------------------------------------------------------------------------------- 1 | package coursera.rx 2 | 3 | import rx.lang.scala.Observable 4 | import scala.language.postfixOps 5 | import scala.concurrent.duration._ 6 | import org.junit.Test 7 | import org.scalatest.junit.JUnitSuite 8 | import org.junit.Assert._ 9 | import coursera.Utils._ 10 | 11 | class Nested extends JUnitSuite { 12 | 13 | @Test def flattenNestedStreams(): Unit = { 14 | 15 | val xs: Observable[Int] = Observable(3,2,1) 16 | val yss: Observable[Observable[Int]] = xs.map(x => Observable.interval(x seconds).map(_ => x).take(2)) 17 | val zs = yss.flatten 18 | 19 | val list = zs.toBlockingObservable.toList 20 | val isFirst = list == List(1, 1, 2, 3, 2, 3) 21 | val isSecond = list == List(1, 2, 1, 3, 2, 3) 22 | // behavior of flatten is non-deterministic 23 | assertTrue(isFirst || isSecond) 24 | if (isFirst) println("first option") 25 | if (isSecond) println("second option") 26 | } 27 | 28 | @Test def concatenateNestedStreams(): Unit = { 29 | 30 | val xs: Observable[Int] = Observable(3,2,1) 31 | val yss: Observable[Observable[Int]] = xs.map(x => Observable.interval(x seconds).map(_ => x).take(2)) 32 | val zs = yss.concat 33 | 34 | assertEquals(List(3, 3, 2, 2, 1, 1), zs.toBlockingObservable.toList) 35 | } 36 | 37 | /** 38 | * The marble diagram on the slides is not entirely correct, but the final Observable zs is correct. 39 | * Interested students can investigate what happens in between using this test. 40 | * The difference is that Observable.interval produces a cold Observable, which only starts emitting 41 | * values once concat subscribes to it. So there is no buffering here, and concat is only subscribed 42 | * to one inner observable at the same time. 43 | */ 44 | @Test def concatenateNestedStreamsWhatIsReallyGoingOn() { 45 | val t0 = System.currentTimeMillis 46 | 47 | val xs: Observable[Int] = Observable(3, 2, 1) 48 | val yss: Observable[Observable[Int]] = 49 | xs map { x => Observable.interval(x seconds).doOnEach( 50 | n => println(f"${(System.currentTimeMillis-t0)/1000.0}%.3f:Observable.interval($x seconds) emits $n") 51 | ).map(_ => x).take(2) } 52 | val zs: Observable[Int] = yss.concat 53 | 54 | displayObservable(zs) 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/test/scala/coursera/rx/Quizzes.scala: -------------------------------------------------------------------------------- 1 | package coursera.rx 2 | 3 | import rx.lang.scala.Observable 4 | import org.junit.Test 5 | import org.scalatest.junit.JUnitSuite 6 | import org.junit.Assert._ 7 | 8 | class Quizzes extends JUnitSuite { 9 | 10 | @Test def quizI(): Unit = { 11 | 12 | val xs = Observable(1 to 10) 13 | val ys = xs.map(x => x+1) 14 | 15 | assertEquals(List(2,3,4,5,6,7,8,9,10,11), ys.toBlockingObservable.toList) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/scala/coursera/rx/Schedulers.scala: -------------------------------------------------------------------------------- 1 | package coursera.rx 2 | 3 | import rx.lang.scala.{Observable, Scheduler, Subscription} 4 | import scala.language.postfixOps 5 | import org.junit.Test 6 | import org.scalatest.junit.JUnitSuite 7 | 8 | /** 9 | * Since several of the sample function below run on their own thread, 10 | * or run forever, add @Test by hand in front of the functions you want study. 11 | * It is best to only have one test enabled. 12 | */ 13 | class Schedulers extends JUnitSuite { 14 | 15 | def fromIterableBad[T](seq: Iterable[T]) : Observable[T] = { 16 | 17 | Observable(observer => { 18 | try { 19 | seq.foreach(observer.onNext(_)) 20 | } catch { 21 | case e: Throwable => observer.onError(e) 22 | } 23 | 24 | Subscription() 25 | }) 26 | } 27 | 28 | def from(start: Integer): Iterable[Integer] = { 29 | new Iterable[Integer]() { 30 | def iterator: Iterator[Integer] = { 31 | var n = start-1; 32 | new Iterator[Integer] () { 33 | def hasNext: Boolean = { n += 1; true } 34 | def next(): Integer = n 35 | } 36 | } 37 | } 38 | } 39 | 40 | def attemptI(): Unit = { 41 | val nats: Observable[Integer] = fromIterableBad(from(0)) 42 | val subscription = nats.subscribe(println(_)) 43 | subscription.unsubscribe() 44 | } 45 | 46 | def helloScheduler(): Unit = { 47 | val scheduler: Scheduler = rx.lang.scala.concurrency.Schedulers.newThread 48 | println(s"running on ${Thread.currentThread().getName}") 49 | val subscription = scheduler.schedule{ 50 | println(s"running on ${Thread.currentThread().getName}") 51 | } 52 | subscription.unsubscribe() 53 | } 54 | 55 | /** 56 | * Don't count on this to work! 57 | * The only guarantee you get is that you can cancel the work 58 | * between it gets scheduled and before it gets run. 59 | * (RxJava goes out of the way to make it stop) 60 | */ 61 | def attemptII(): Unit = { 62 | val scheduler: Scheduler = rx.lang.scala.concurrency.Schedulers.newThread 63 | val nats: Observable[Integer] = Observable(observer => { 64 | scheduler.schedule{ from(0).foreach(observer.onNext(_)) } 65 | }) 66 | 67 | val subscription = nats.subscribe(println(_)) 68 | subscription.unsubscribe() 69 | println("You won the lottery") 70 | } 71 | 72 | // warning: does not catch exceptions and send to onError 73 | def attemptIII(): Unit = { 74 | val scheduler: Scheduler = rx.lang.scala.concurrency.Schedulers.newThread 75 | val nats: Observable[Integer] = Observable(observer => { 76 | val iterator = from(0).iterator 77 | scheduler.scheduleRec(self => { 78 | 79 | if(iterator.hasNext) { 80 | observer.onNext(iterator.next()); self 81 | } else { 82 | observer.onCompleted() 83 | } 84 | 85 | }) 86 | }) 87 | val subscription = nats.subscribe(println(_)) 88 | subscription.unsubscribe() 89 | println("we made it work!") 90 | } 91 | 92 | /** 93 | * In the slides this is defined as factory method on Observable in the quiz. 94 | * Perhaps the easiest way to use schedulers in scenarios like the above, 95 | * since you can directly subscribe to the scheduler. 96 | */ 97 | def SchedulerToObservable()(implicit scheduler: Scheduler): Observable[Unit] = { 98 | Observable(observer => { 99 | scheduler.scheduleRec(self => { 100 | observer.onNext() 101 | self 102 | }) 103 | }) 104 | } 105 | 106 | @Test def unitObservable() { 107 | implicit val scheduler: Scheduler = rx.lang.scala.concurrency.Schedulers.newThread 108 | val observable = SchedulerToObservable() 109 | observable.subscribe(u => println("unit")) 110 | println("unitObservable out") 111 | } 112 | 113 | def scheduleRec(outer: Scheduler, work: (=>Unit)=>Unit): Subscription = { 114 | val subscription = rx.lang.scala.subscriptions.MultipleAssignmentSubscription() 115 | outer.schedule(s => { 116 | def loop(): Unit = { 117 | subscription.subscription = s.schedule { 118 | work { loop() } } 119 | } 120 | loop() 121 | subscription 122 | }) 123 | subscription 124 | } 125 | 126 | @Test def comeOnBabyOneMoreTime(): Unit = { 127 | 128 | val ns = (Observable(observer => { 129 | var n = 0 130 | scheduleRec(rx.lang.scala.concurrency.Schedulers.newThread, self => { 131 | observer.onNext(n); n += 1 132 | self 133 | })}): Observable[Integer]).take(5) 134 | 135 | ns.subscribe(println(_)) 136 | 137 | Thread.sleep(1000) 138 | 139 | } 140 | 141 | def range(start: Int, count: Int)(implicit s: Scheduler): Observable[Int] = { 142 | Observable(observer => { 143 | var i = 0 144 | SchedulerToObservable().subscribe(u => { 145 | if (i < count) { observer.onNext(start + i); i += 1 } 146 | else { observer.onCompleted() } 147 | }) 148 | }) 149 | } 150 | 151 | @Test def range() { 152 | implicit val scheduler: Scheduler = rx.lang.scala.concurrency.Schedulers.newThread 153 | val xs = range(1, 10) 154 | xs.subscribe(x => println(x)) 155 | println("range out") 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /src/test/scala/coursera/rx/Subjects.scala: -------------------------------------------------------------------------------- 1 | package coursera.rx 2 | 3 | import org.junit.Test 4 | import org.scalatest.junit.JUnitSuite 5 | import org.junit.Assert._ 6 | import rx.lang.scala.subjects.{BehaviorSubject, AsyncSubject, ReplaySubject, PublishSubject} 7 | 8 | 9 | class Subjects extends JUnitSuite { 10 | 11 | /* 12 | Banana: 42 13 | Apple: 42 14 | 15 | Banana: 4711 16 | Banana. 17 | 18 | Cranberry. 19 | */ 20 | @Test def PublishSubjectIsAChannel() { 21 | 22 | val channel = PublishSubject[Int]() 23 | val a = channel.subscribe(x => println(s"Apple: $x"), e => println(s"Apple~ $e"), () => println(s"Apple.")) 24 | val b = channel.subscribe(x => println(s"Banana: $x"), e => println(s"Banana~ $e"), () => println(s"Banana.")) 25 | 26 | channel.onNext(42) 27 | 28 | a.unsubscribe() 29 | 30 | channel.onNext(4711) 31 | channel.onCompleted() 32 | 33 | val c = channel.subscribe(x => println(s"Cranberry: $x"), e => println(s"Cranberry~ $e"), () => println(s"Cranberry.")) 34 | 35 | channel.onNext(13) 36 | } 37 | 38 | /* 39 | Banana: 42 40 | Apple: 42 41 | 42 | Banana: 4711 43 | Banana. 44 | 45 | Cranberry: 42 46 | Cranberry: 4711 47 | Cranberry. 48 | */ 49 | @Test def ReplaySubjectIsAChannel() { 50 | 51 | val channel = ReplaySubject[Int]() 52 | val a = channel.subscribe(x => println(s"Apple: $x"), e => println(s"Apple~ $e"), () => println(s"Apple.")) 53 | val b = channel.subscribe(x => println(s"Banana: $x"), e => println(s"Banana~ $e"), () => println(s"Banana.")) 54 | 55 | channel.onNext(42) 56 | 57 | a.unsubscribe() 58 | 59 | channel.onNext(4711) 60 | channel.onCompleted() 61 | 62 | val c = channel.subscribe(x => println(s"Cranberry: $x"), e => println(s"Cranberry~ $e"), () => println(s"Cranberry.")) 63 | 64 | channel.onNext(13) 65 | } 66 | 67 | /* 68 | Apple: 2013 69 | Banana: 2013 70 | 71 | Banana: 42 72 | Apple: 42 73 | 74 | Banana: 4711 75 | Banana. 76 | 77 | Cranberry. 78 | */ 79 | @Test def BehaviorSubjectIsACache() { 80 | 81 | val channel = BehaviorSubject(2013) 82 | val a = channel.subscribe(x => println(s"Apple: $x"), e => println(s"Apple~ $e"), () => println(s"Apple.")) 83 | val b = channel.subscribe(x => println(s"Banana: $x"), e => println(s"Banana~ $e"), () => println(s"Banana.")) 84 | 85 | channel.onNext(42) 86 | 87 | a.unsubscribe() 88 | 89 | channel.onNext(4711) 90 | channel.onCompleted() 91 | 92 | val c = channel.subscribe(x => println(s"Cranberry: $x"), e => println(s"Cranberry~ $e"), () => println(s"Cranberry.")) 93 | 94 | channel.onNext(13) 95 | } 96 | 97 | /* 98 | Banana: 4711 99 | Banana. 100 | Cranberry: 4711 101 | Cranberry. 102 | */ 103 | @Test def AsyncSubjectIsAFuture() { 104 | 105 | val channel = AsyncSubject[Int]() 106 | val a = channel.subscribe(x => println(s"Apple: $x"), e => println(s"Apple~ $e"), () => println(s"Apple.")) 107 | val b = channel.subscribe(x => println(s"Banana: $x"), e => println(s"Banana~ $e"), () => println(s"Banana.")) 108 | 109 | channel.onNext(42) 110 | 111 | a.unsubscribe() 112 | 113 | channel.onNext(4711) 114 | channel.onCompleted() 115 | 116 | val c = channel.subscribe(x => println(s"Cranberry: $x"), e => println(s"Cranberry~ $e"), () => println(s"Cranberry.")) 117 | 118 | channel.onNext(13) 119 | } 120 | } 121 | 122 | 123 | -------------------------------------------------------------------------------- /src/test/scala/coursera/rx/Subscriptions.scala: -------------------------------------------------------------------------------- 1 | package coursera.rx 2 | 3 | import rx.lang.scala.subscriptions._ 4 | import org.scalatest.junit.JUnitSuite 5 | import org.junit.Assert._ 6 | import org.junit.Test 7 | import rx.lang.scala.Subscription 8 | 9 | class Subscriptions extends JUnitSuite { 10 | 11 | @Test def FishingI(): Unit = { 12 | 13 | val subscription = Subscription{ 14 | println("Bye, bye, I'm out fishing") 15 | } 16 | 17 | subscription.unsubscribe() 18 | println(subscription.isUnsubscribed) 19 | // bug! 20 | subscription.unsubscribe() 21 | } 22 | 23 | @Test def FishingII(): Unit = { 24 | 25 | val subscription = BooleanSubscription{ 26 | println("Bye, bye, I'm out fishing as well") 27 | } 28 | 29 | subscription.unsubscribe() 30 | println(subscription.isUnsubscribed) 31 | // bug! 32 | subscription.unsubscribe() 33 | } 34 | 35 | @Test def Composite(): Unit = { 36 | val a = BooleanSubscription { println("Apple") } 37 | val b = Subscription { println("Banana") } 38 | val c = Subscription{ println ("Cranberry") } 39 | 40 | val composite = CompositeSubscription(a,b) 41 | 42 | println(s"composite.isUnsubscribed=${composite.isUnsubscribed}") 43 | 44 | composite.unsubscribe() 45 | 46 | println(s"a.isUnsubscribed=${a.isUnsubscribed}") 47 | println(s"b.isUnsubscribed=${b.isUnsubscribed}") 48 | println(s"composite.isUnsubscribed=${composite.isUnsubscribed}") 49 | 50 | println(s"c.isUnsubscribed=${c.isUnsubscribed}") 51 | composite += c 52 | println(s"c.isUnsubscribed=${c.isUnsubscribed}") 53 | 54 | } 55 | 56 | @Test def Multi(): Unit = { 57 | val a = BooleanSubscription { println("Apple") } 58 | val b = Subscription { println("Banana") } 59 | val c = Subscription{ println ("Cranberry") } 60 | 61 | val multiple = MultipleAssignmentSubscription() 62 | 63 | println(s"multiple.isUnsubscribed=${multiple.isUnsubscribed}") 64 | 65 | multiple.subscription = a 66 | multiple.subscription = b 67 | 68 | multiple.unsubscribe() 69 | 70 | println(s"a.isUnsubscribed=${a.isUnsubscribed}") 71 | println(s"b.isUnsubscribed=${b.isUnsubscribed}") 72 | println(s"multiple.isUnsubscribed=${multiple.isUnsubscribed}") 73 | 74 | println(s"c.isUnsubscribed=${c.isUnsubscribed}") 75 | multiple.subscription = c 76 | println(s"c.isUnsubscribed=${c.isUnsubscribed}") 77 | 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/test/scala/coursera/rx/ToObservable.scala: -------------------------------------------------------------------------------- 1 | package coursera.rx 2 | 3 | import rx.lang.scala.subjects.AsyncSubject 4 | import rx.lang.scala.Observable 5 | import scala.concurrent.{ExecutionContext, Future} 6 | import ExecutionContext.Implicits.global 7 | 8 | object ToObservable { 9 | 10 | def apply[T](future: Future[T]): Observable[T] = { 11 | val subject = AsyncSubject[T]() 12 | 13 | future.onSuccess{ 14 | case s => { 15 | subject.onNext(s); 16 | subject.onCompleted() 17 | } 18 | } 19 | 20 | future.onFailure{ 21 | case e => { 22 | subject.onError(e) 23 | } 24 | } 25 | 26 | subject 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/scala/coursera/socket/EmailMessage.scala: -------------------------------------------------------------------------------- 1 | package coursera.socket 2 | 3 | object EmailMessage { 4 | def apply(from: String, to: String): EmailMessage = { 5 | new EmailMessage(from, to) 6 | } 7 | } 8 | 9 | class EmailMessage(val from: String, val to: String){} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/test/scala/coursera/socket/safe/Http.scala: -------------------------------------------------------------------------------- 1 | package coursera.socket.safe { 2 | 3 | import scala.concurrent.Future 4 | 5 | object Request { 6 | def apply(bytes: Array[Byte]): Request = ??? 7 | } 8 | 9 | trait Request{} 10 | 11 | object Response { 12 | def apply(bytes: Array[Byte]): Response = ??? 13 | } 14 | 15 | trait Response{ 16 | def isOK: Boolean 17 | def body: Array[Byte] 18 | } 19 | 20 | object Http { 21 | def apply(url: String, request: Request): Future[Response] = ??? 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/test/scala/coursera/socket/safe/Socket.scala: -------------------------------------------------------------------------------- 1 | package coursera.socket.safe 2 | 3 | import scala.util.{Failure, Success} 4 | import scala.concurrent.{ExecutionContext, Future} 5 | import ExecutionContext.Implicits.global 6 | import scala.collection.immutable.Queue 7 | import akka.serialization._ 8 | import coursera.socket.EmailMessage 9 | 10 | object Socket { 11 | def apply(): Socket = new Socket(){} 12 | } 13 | 14 | trait Socket { 15 | 16 | val serialization: Serialization = ??? 17 | 18 | val memory = Queue[EmailMessage]( 19 | EmailMessage(from = "Erik", to = "Roland"), 20 | EmailMessage(from = "Martin", to = "Erik"), 21 | EmailMessage(from = "Roland", to = "Martin") 22 | ) 23 | 24 | def readFromMemory(): Future[Array[Byte]] = { 25 | Future { 26 | val email = memory.dequeue 27 | val serializer = serialization.findSerializerFor(email) 28 | serializer.toBinary(email) 29 | } 30 | } 31 | 32 | def sendToEurope(packet: Array[Byte]): Future[Array[Byte]] = { 33 | Http("mail.server.eu", Request(packet)).filter(_.isOK).map(_.body) 34 | } 35 | 36 | def sendTo(url: String, packet: Array[Byte]): Future[Array[Byte]] = 37 | Http(url, Request(packet)).filter(_.isOK).map(_.body) 38 | 39 | def sendToSafeI(packet: Array[Byte]): Future[Array[Byte]] = 40 | sendTo("...europe...", packet) recoverWith { 41 | case europeError => sendTo("...usa...", packet) recover { 42 | case usaError => usaError.getMessage.getBytes 43 | } 44 | } 45 | 46 | def sendToSafeII(packet: Array[Byte]): Future[Array[Byte]] = 47 | sendTo("...europe...", packet) fallbackTo { sendTo("...usa...", packet) } recover { 48 | case europeError => europeError.getMessage.getBytes 49 | } 50 | 51 | def sendPacketToEuropeAndBackI(): Unit = { 52 | 53 | val socket = Socket() 54 | 55 | val packet: Future[Array[Byte]] = socket.readFromMemory() 56 | 57 | val confirmation: Unit /* Future[Array[Byte]] */ = 58 | packet onComplete { 59 | case Success(p) => socket.sendToEurope(p) 60 | case Failure(t) => ??? 61 | } 62 | 63 | } 64 | 65 | def sendPacketToEuropeAndBackII(): Unit = { 66 | val socket = Socket() 67 | 68 | val packet: Future[Array[Byte]] = 69 | socket.readFromMemory() 70 | 71 | packet onComplete { 72 | case Success(p) => { 73 | val confirmation: Future[Array[Byte]] = socket.sendToEurope(p) 74 | ??? 75 | } 76 | case Failure(t) => ??? 77 | } 78 | } 79 | 80 | def sendPacketToEuropeAndBackIII(): Unit = { 81 | val socket = Socket() 82 | val packet: Future[Array[Byte]] = socket.readFromMemory() 83 | val confirmation: Future[Array[Byte]] = packet.flatMap(socket.sendToEurope(_)) 84 | } 85 | 86 | def sendPacketToEuropeAndBackIV(): Unit = { 87 | val socket = Socket() 88 | val confirmation: Future[Array[Byte]] = for { 89 | packet <- socket.readFromMemory() 90 | confirmation <- socket.sendToSafeII(packet) 91 | } yield confirmation 92 | } 93 | 94 | def sendToAndBackUp(packet: Array[Byte]):Future[(Array[Byte], Array[Byte])] = { 95 | val europeConfirm = sendTo("...europe...", packet) 96 | val usaConfirm = sendTo("...usa...", packet) 97 | europeConfirm.zip(usaConfirm) 98 | } 99 | 100 | } 101 | 102 | -------------------------------------------------------------------------------- /src/test/scala/coursera/socket/unsafe/Socket.scala: -------------------------------------------------------------------------------- 1 | package coursera.socket.unsafe 2 | 3 | object Socket { 4 | def apply(): Socket = new Socket(){ 5 | } 6 | } 7 | 8 | trait Socket { 9 | 10 | def readFromMemory(): Array[Byte] = ??? 11 | def sendToEurope(packet: Array[Byte]): Array[Byte] = ??? 12 | 13 | def sendPacketToEuropeAndBack(): Unit = { 14 | val socket = Socket() 15 | val packet = socket.readFromMemory() 16 | val confirmation = socket.sendToEurope(packet) 17 | } 18 | 19 | } 20 | 21 | -------------------------------------------------------------------------------- /src/test/scala/coursera/usgs/Feature.scala: -------------------------------------------------------------------------------- 1 | package coursera.usgs 2 | 3 | import retrofit.http.GET 4 | import retrofit.{RestAdapter, RetrofitError, Callback} 5 | import rx.lang.scala.Observable 6 | import rx.lang.scala.subjects.AsyncSubject 7 | import retrofit.client.Response 8 | 9 | class Feature { 10 | 11 | val properties : Properties = null 12 | val geometry: Point = null 13 | 14 | override def toString() = s"{ 'properties':'${properties}', 'geometry':'${geometry}' }"; 15 | } 16 | -------------------------------------------------------------------------------- /src/test/scala/coursera/usgs/FeatureCollection.scala: -------------------------------------------------------------------------------- 1 | package coursera.usgs 2 | 3 | /* 4 | * Bindings for http://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php 5 | * using Square retrofit 6 | * */ 7 | 8 | class FeatureCollection { 9 | val metadata : MetaData = null 10 | val features : Array[Feature] = null 11 | 12 | // yes, @headinthebox sometimes uses folds ;-) 13 | override def toString() = s"{ 'metadata':'${metadata}', 'features':[${features.map(_.toString()).reduceLeft((x,s)=> s"$x,\n $s")}] }"; 14 | } 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/test/scala/coursera/usgs/Magnitude.scala: -------------------------------------------------------------------------------- 1 | package coursera.usgs 2 | 3 | object Magnitude extends Enumeration { 4 | 5 | type Magnitude = Value 6 | val Micro, Minor, Light, Moderate, Strong, Major, Great = Value 7 | 8 | def apply(magnitude: Double): Magnitude = { 9 | 10 | if(magnitude >= 8.0) return Great 11 | if(magnitude >= 7.0) return Major 12 | if(magnitude >= 6.0) return Strong 13 | if(magnitude >= 5.0) return Moderate 14 | if(magnitude >= 4.0) return Light 15 | if(magnitude >= 3.0) return Minor 16 | return Micro 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test/scala/coursera/usgs/MetaData.scala: -------------------------------------------------------------------------------- 1 | package coursera.usgs 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import java.util.Date 5 | import scala.transient 6 | 7 | class MetaData { 8 | 9 | @SerializedName("generated") 10 | val _generated: Long = 0L 11 | @transient 12 | lazy val generated: Date = new Date(_generated) 13 | val url: String = null 14 | val title: String = null 15 | val api: String = null 16 | val count: Int = 0 17 | val status: Int = 0 18 | 19 | override def toString() = s"{ 'generated': '$generated', 'url':'$url', 'title':'$title', 'api': '$api', 'count': '$count', 'status': '$status' }"; 20 | } 21 | -------------------------------------------------------------------------------- /src/test/scala/coursera/usgs/Point.scala: -------------------------------------------------------------------------------- 1 | package coursera.usgs 2 | 3 | class Point { 4 | private val coordinates: Array[Double] = null 5 | 6 | lazy val latitude: Double = coordinates(1) 7 | lazy val longitude : Double = coordinates(0) 8 | lazy val altitude : Double = coordinates(2) 9 | 10 | override def toString() = s"{ 'longitude':'${longitude}', 'latitude':'${latitude}', 'altitude':'${altitude}' }"; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/test/scala/coursera/usgs/Properties.scala: -------------------------------------------------------------------------------- 1 | package coursera.usgs 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import java.util.Date 5 | import scala.transient 6 | 7 | class Properties { 8 | val place: String = null 9 | @SerializedName("time") 10 | private val _time: Long = 0L 11 | @transient 12 | lazy val time: Date = new Date(_time) 13 | @SerializedName("updated") 14 | private val _updated: Long = 0L 15 | @transient 16 | lazy val updated: Date = new Date(_updated) 17 | @SerializedName("mag") 18 | val magnitude: Double = 0D 19 | val detail: String = null 20 | val felt: Int = 0 21 | val cdi: Double = 0D 22 | val mmi: Double = 0D 23 | val alert: String = null 24 | val status: String = null 25 | val tsunami: Int = 0 26 | val sig: Int = 0 27 | val net: String = null 28 | val code: String = null 29 | val ids: String = null 30 | val sources: String = null 31 | val types: String = null 32 | val nst: Int = 0 33 | val dmin: Double = 0D 34 | val rms: Double = 0D 35 | val gap: Double = 0D 36 | val magType: String = null 37 | val `type`: String = null 38 | 39 | // add fields that you want to see. 40 | override def toString() = s"{ 'time':'${time}', 'place':'${place}', 'magnitude':'${magnitude}' }"; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/test/scala/coursera/usgs/Usgs.scala: -------------------------------------------------------------------------------- 1 | package coursera.usgs 2 | 3 | import retrofit.http.GET 4 | import retrofit.{RetrofitError, RestAdapter, Callback} 5 | import rx.lang.scala.Observable 6 | import rx.lang.scala.subjects.AsyncSubject 7 | import retrofit.client.Response 8 | 9 | object Usgs { 10 | 11 | private val restAdapter = new RestAdapter.Builder().setServer("http://earthquake.usgs.gov").build() 12 | 13 | def apply(): Observable[Feature] = { 14 | 15 | val subject = AsyncSubject[FeatureCollection]() 16 | 17 | restAdapter.create(classOf[Usgs]).get(new Callback[FeatureCollection] { 18 | 19 | def failure(error: RetrofitError): Unit = { 20 | subject.onError(error.getCause) 21 | } 22 | 23 | def success(t: FeatureCollection, response: Response): Unit = { 24 | subject.onNext(t) 25 | subject.onCompleted() 26 | } 27 | 28 | }) 29 | 30 | subject.flatMap(collection => Observable(collection.features : _*)) 31 | } 32 | } 33 | 34 | private trait Usgs { 35 | @GET("/earthquakes/feed/geojson/all/day") 36 | def get(callback: Callback[FeatureCollection]) 37 | } 38 | 39 | --------------------------------------------------------------------------------