Skip to content

Commit 8bef527

Browse files
committed
Merge remote-tracking branch 'netflix/master' into android-samples
2 parents a40f8c5 + 5753a92 commit 8bef527

File tree

61 files changed

+4445
-945
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+4445
-945
lines changed

CHANGES.md

Lines changed: 734 additions & 0 deletions
Large diffs are not rendered by default.

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version=0.17.0-RC7-SNAPSHOT
1+
version=0.17.1-SNAPSHOT

language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ object Olympics {
2222
case class Medal(val year: Int, val games: String, val discipline: String, val medal: String, val athlete: String, val country: String)
2323

2424
def mountainBikeMedals: Observable[Medal] = Observable.items(
25+
duration(100 millis), // a short delay because medals are only awarded some time after the Games began
2526
Observable.items(
2627
Medal(1996, "Atlanta 1996", "cross-country men", "Gold", "Bart BRENTJENS", "Netherlands"),
2728
Medal(1996, "Atlanta 1996", "cross-country women", "Gold", "Paola PEZZO", "Italy"),
@@ -69,18 +70,33 @@ object Olympics {
6970
).concat
7071

7172
// speed it up :D
72-
val fourYears = 4000.millis
73+
val oneYear = 1000.millis
7374

74-
val neverUsedDummyMedal = Medal(3333, "?", "?", "?", "?", "?")
75+
//val neverUsedDummyMedal = Medal(3333, "?", "?", "?", "?", "?")
7576

76-
def fourYearsEmpty: Observable[Medal] = {
77+
/** runs an infinite loop, and returns Bottom type (Nothing) */
78+
def getNothing: Nothing = {
79+
println("You shouldn't have called this method ;-)")
80+
getNothing
81+
}
82+
83+
/** returns an Observable which emits no elements and completes after a duration of d */
84+
def duration(d: Duration): Observable[Nothing] = Observable.interval(d).take(1).filter(_ => false).map(_ => getNothing)
85+
86+
def fourYearsEmpty: Observable[Medal] = duration(4*oneYear)
87+
88+
def yearTicks: Observable[Int] =
89+
(Observable.from(1996 to 2014) zip (Observable.items(-1) ++ Observable.interval(oneYear))).map(_._1)
90+
91+
/*
92+
def fourYearsEmptyOld: Observable[Medal] = {
7793
// TODO this should return an observable which emits nothing during fourYears and then completes
7894
// Because of https://github.com/Netflix/RxJava/issues/388, we get non-terminating tests
7995
// And this https://github.com/Netflix/RxJava/pull/289#issuecomment-24738668 also causes problems
8096
// So we don't use this:
81-
// Observable.interval(fourYears).take(1).map(i => neverUsedDummyMedal).filter(m => false)
97+
Observable.interval(fourYears).take(1).map(i => neverUsedDummyMedal).filter(m => false)
8298
// But we just return empty, which completes immediately
83-
Observable.empty
84-
}
99+
// Observable.empty
100+
}*/
85101

86102
}

language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala

Lines changed: 75 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ import org.scalatest.junit.JUnitSuite
3232
import rx.lang.scala._
3333
import rx.lang.scala.schedulers._
3434

35+
/**
36+
* Demo how the different operators can be used. In Eclipse, you can right-click
37+
* a test and choose "Run As" > "Scala JUnit Test".
38+
*
39+
* For each operator added to Observable.java, we add a little usage demo here.
40+
* It does not need to test the functionality (that's already done by the tests in
41+
* RxJava core), but it should demonstrate how it can be used, to make sure that
42+
* the method signature makes sense.
43+
*/
3544
@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily
3645
class RxScalaDemo extends JUnitSuite {
3746

@@ -78,13 +87,9 @@ class RxScalaDemo extends JUnitSuite {
7887
val first = Observable.from(List(10, 11, 12))
7988
val second = Observable.from(List(10, 11, 12))
8089

81-
val b1 = (first zip second) map (p => p._1 == p._2) forall (b => b)
82-
83-
val equality = (a: Any, b: Any) => a == b
84-
val b2 = (first zip second) map (p => equality(p._1, p._2)) forall (b => b)
90+
val b = (first zip second) forall { case (a, b) => a == b }
8591

86-
assertTrue(b1.toBlockingObservable.single)
87-
assertTrue(b2.toBlockingObservable.single)
92+
assertTrue(b.toBlockingObservable.single)
8893
}
8994

9095
@Test def testObservableComparisonWithForComprehension() {
@@ -93,7 +98,7 @@ class RxScalaDemo extends JUnitSuite {
9398

9499
val booleans = for ((n1, n2) <- (first zip second)) yield (n1 == n2)
95100

96-
val b1 = booleans.forall(_ == true) // without `== true`, b1 is assigned the forall function
101+
val b1 = booleans.forall(identity)
97102

98103
assertTrue(b1.toBlockingObservable.single)
99104
}
@@ -216,18 +221,21 @@ class RxScalaDemo extends JUnitSuite {
216221
}).flatten.toBlockingObservable.foreach(println(_))
217222
}
218223

219-
@Ignore // TODO something's bad here
220224
@Test def timingTest1() {
221225
val numbersByModulo3 = Observable.interval(1000 millis).take(9).groupBy(_ % 3)
222226

223227
val t0 = System.currentTimeMillis
224228

225229
(for ((modulo, numbers) <- numbersByModulo3) yield {
226230
println("Observable for modulo" + modulo + " started at t = " + (System.currentTimeMillis - t0))
227-
numbers.take(1) // <- TODO very unexpected
228-
//numbers
231+
numbers.map(n => s"${n} is in the modulo-$modulo group")
229232
}).flatten.toBlockingObservable.foreach(println(_))
230233
}
234+
235+
@Test def testOlympicYearTicks() {
236+
Olympics.yearTicks.subscribe(println(_))
237+
waitFor(Olympics.yearTicks)
238+
}
231239

232240
@Test def groupByExample() {
233241
val medalsByCountry = Olympics.mountainBikeMedals.groupBy(medal => medal.country)
@@ -238,8 +246,10 @@ class RxScalaDemo extends JUnitSuite {
238246
firstMedalOfEachCountry.subscribe(medal => {
239247
println(s"${medal.country} wins its first medal in ${medal.year}")
240248
})
249+
250+
Olympics.yearTicks.subscribe(year => println(s"\nYear $year starts."))
241251

242-
waitFor(firstMedalOfEachCountry)
252+
waitFor(Olympics.yearTicks)
243253
}
244254

245255
@Test def groupByUntilExample() {
@@ -250,30 +260,36 @@ class RxScalaDemo extends JUnitSuite {
250260
}
251261

252262
@Test def combineLatestExample() {
253-
val first_counter = Observable.interval(250 millis)
254-
val second_counter = Observable.interval(550 millis)
255-
val combined_counter = first_counter.combineLatest(second_counter,
263+
val firstCounter = Observable.interval(250 millis)
264+
val secondCounter = Observable.interval(550 millis)
265+
val combinedCounter = firstCounter.combineLatest(secondCounter,
256266
(x: Long, y: Long) => List(x,y)) take 10
257267

258-
combined_counter subscribe {x => println(s"Emitted group: $x")}
268+
combinedCounter subscribe {x => println(s"Emitted group: $x")}
269+
waitFor(combinedCounter)
259270
}
260271

272+
@Test def olympicsExampleWithoutPublish() {
273+
val medals = Olympics.mountainBikeMedals.doOnEach(_ => println("onNext"))
274+
medals.subscribe(println(_)) // triggers an execution of medals Observable
275+
waitFor(medals) // triggers another execution of medals Observable
276+
}
261277

262-
@Test def olympicsExample() {
263-
val medals = Olympics.mountainBikeMedals.publish
264-
medals.subscribe(println(_))
278+
@Test def olympicsExampleWithPublish() {
279+
val medals = Olympics.mountainBikeMedals.doOnEach(_ => println("onNext")).publish
280+
medals.subscribe(println(_)) // triggers an execution of medals Observable
265281
medals.connect
266-
//waitFor(medals)
282+
waitFor(medals) // triggers another execution of medals Observable
267283
}
268284

269285
@Test def exampleWithoutPublish() {
270-
val unshared = List(1 to 4).toObservable
286+
val unshared = Observable.from(1 to 4)
271287
unshared.subscribe(n => println(s"subscriber 1 gets $n"))
272288
unshared.subscribe(n => println(s"subscriber 2 gets $n"))
273289
}
274290

275291
@Test def exampleWithPublish() {
276-
val unshared = List(1 to 4).toObservable
292+
val unshared = Observable.from(1 to 4)
277293
val shared = unshared.publish
278294
shared.subscribe(n => println(s"subscriber 1 gets $n"))
279295
shared.subscribe(n => println(s"subscriber 2 gets $n"))
@@ -402,7 +418,7 @@ class RxScalaDemo extends JUnitSuite {
402418
}
403419

404420
@Test def timestampExample() {
405-
val timestamped = Observable.interval(100 millis).take(3).timestamp.toBlockingObservable
421+
val timestamped = Observable.interval(100 millis).take(6).timestamp.toBlockingObservable
406422
for ((millis, value) <- timestamped if value > 0) {
407423
println(value + " at t = " + millis)
408424
}
@@ -441,35 +457,57 @@ class RxScalaDemo extends JUnitSuite {
441457
val oc3: rx.Notification[_ <: Int] = oc2.asJavaNotification
442458
val oc4: rx.Notification[_ <: Any] = oc2.asJavaNotification
443459
}
444-
445-
@Test def elementAtReplacement() {
446-
assertEquals("b", List("a", "b", "c").toObservable.drop(1).first.toBlockingObservable.single)
447-
}
448-
449-
@Test def elementAtOrDefaultReplacement() {
450-
assertEquals("b", List("a", "b", "c").toObservable.drop(1).firstOrElse("!").toBlockingObservable.single)
451-
assertEquals("!!", List("a", "b", "c").toObservable.drop(10).firstOrElse("!!").toBlockingObservable.single)
452-
}
453460

454461
@Test def takeWhileWithIndexAlternative {
455462
val condition = true
456463
List("a", "b").toObservable.zipWithIndex.takeWhile{case (elem, index) => condition}.map(_._1)
457464
}
458465

459-
@Test def createExample() {
466+
def calculateElement(index: Int): String = {
467+
println("omg I'm calculating so hard")
468+
index match {
469+
case 0 => "a"
470+
case 1 => "b"
471+
case _ => throw new IllegalArgumentException
472+
}
473+
}
474+
475+
/**
476+
* This is a bad way of using Observable.create, because even if the consumer unsubscribes,
477+
* all elements are calculated.
478+
*/
479+
@Test def createExampleBad() {
460480
val o = Observable.create[String](observer => {
461-
// this is bad because you cannot unsubscribe!
462-
observer.onNext("a")
463-
observer.onNext("b")
481+
observer.onNext(calculateElement(0))
482+
observer.onNext(calculateElement(1))
464483
observer.onCompleted()
465484
Subscription {}
466485
})
467-
o.subscribe(println(_))
486+
o.take(1).subscribe(println(_))
487+
}
488+
489+
/**
490+
* This is the good way of doing it: If the consumer unsubscribes, no more elements are
491+
* calculated.
492+
*/
493+
@Test def createExampleGood() {
494+
val o = Observable[String](subscriber => {
495+
var i = 0
496+
while (i < 2 && !subscriber.isUnsubscribed) {
497+
subscriber.onNext(calculateElement(i))
498+
i += 1
499+
}
500+
if (!subscriber.isUnsubscribed) subscriber.onCompleted()
501+
})
502+
o.take(1).subscribe(println(_))
468503
}
469504

470505
def output(s: String): Unit = println(s)
471506

472-
// blocks until obs has completed
507+
/** Subscribes to obs and waits until obs has completed. Note that if you subscribe to
508+
* obs yourself and also call waitFor(obs), all side-effects of subscribing to obs
509+
* will happen twice.
510+
*/
473511
def waitFor[T](obs: Observable[T]): Unit = {
474512
obs.toBlockingObservable.toIterable.last
475513
}

language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@ package rx.lang.scala
1717

1818
import java.lang.Exception
1919
import java.{ lang => jlang }
20-
2120
import scala.language.implicitConversions
2221
import scala.collection.Seq
23-
2422
import rx.functions._
2523
import rx.lang.scala.JavaConversions._
2624

@@ -56,6 +54,13 @@ object ImplicitFunctionConversions {
5654
}
5755
}
5856

57+
implicit def scalaAction1ToOnSubscribe[T](f: Subscriber[T] => Unit) =
58+
new rx.Observable.OnSubscribe[T] {
59+
def call(s: rx.Subscriber[_ >: T]): Unit = {
60+
f(s)
61+
}
62+
}
63+
5964
implicit def scalaByNameParamToFunc0[B](param: => B): Func0[B] =
6065
new Func0[B] {
6166
def call(): B = param

language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/JavaConversions.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ object JavaConversions {
3232

3333
implicit def toScalaSubscription(s: rx.Subscription): Subscription = Subscription(s)
3434

35+
implicit def toJavaSubscriber[T](s: Subscriber[T]): rx.Subscriber[_ >: T] = s.asJavaSubscriber
36+
37+
implicit def toScalaSubscriber[T](s: rx.Subscriber[_ >: T]): Subscriber[T] = Subscriber(s)
38+
3539
implicit def scalaSchedulerToJavaScheduler(s: Scheduler): rx.Scheduler = s.asJavaScheduler
3640
implicit def javaSchedulerToScalaScheduler(s: rx.Scheduler): Scheduler = Scheduler(s)
3741

language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,21 @@ trait Observable[+T]
11321132
toScalaObservable[T](asJavaObservable.sample(duration.length, duration.unit, scheduler))
11331133
}
11341134

1135+
/**
1136+
* Return an Observable that emits the results of sampling the items emitted by the source Observable
1137+
* whenever the specified sampler Observable emits an item or completes.
1138+
*
1139+
* <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/sample.o.png">
1140+
*
1141+
* @param sampler
1142+
* the Observable to use for sampling the source Observable
1143+
* @return an Observable that emits the results of sampling the items emitted by this Observable whenever
1144+
* the sampler Observable emits an item or completes
1145+
*/
1146+
def sample(sampler: Observable[Any]): Observable[T] = {
1147+
toScalaObservable[T](asJavaObservable.sample(sampler))
1148+
}
1149+
11351150
/**
11361151
* Returns an Observable that applies a function of your choosing to the first item emitted by a
11371152
* source Observable, then feeds the result of that function along with the second item emitted
@@ -2257,9 +2272,6 @@ object Observable {
22572272
* should invoke the Observer's [[rx.lang.scala.Observer.onNext onNext]], [[rx.lang.scala.Observer.onError onError]], and [[rx.lang.scala.Observer.onCompleted onCompleted]] methods
22582273
* appropriately.
22592274
*
2260-
* A well-formed Observable must invoke either the Observer's `onCompleted` method
2261-
* exactly once or its `onError` method exactly once.
2262-
*
22632275
* See <a href="http://go.microsoft.com/fwlink/?LinkID=205219">Rx Design Guidelines (PDF)</a>
22642276
* for detailed information.
22652277
*
@@ -2273,6 +2285,7 @@ object Observable {
22732285
* @return
22742286
* an Observable that, when an [[rx.lang.scala.Observer]] subscribes to it, will execute the given function.
22752287
*/
2288+
@deprecated("Use `apply[T](Subscriber[T] => Unit)` instead", "0.17.0")
22762289
def create[T](func: Observer[T] => Subscription): Observable[T] = {
22772290
toScalaObservable[T](rx.Observable.create(new OnSubscribeFunc[T] {
22782291
def onSubscribe(t1: rx.Observer[_ >: T]): rx.Subscription = {
@@ -2281,6 +2294,41 @@ object Observable {
22812294
}))
22822295
}
22832296

2297+
/*
2298+
Note: It's dangerous to have two overloads where one takes an `Observer[T] => Subscription`
2299+
function and the other takes a `Subscriber[T] => Unit` function, because expressions like
2300+
`o => Subscription{}` have both of these types.
2301+
So we call the old create method "create", and the new create method "apply".
2302+
Try it out yourself here:
2303+
def foo[T]: Unit = {
2304+
val fMeant: Observer[T] => Subscription = o => Subscription{}
2305+
val fWrong: Subscriber[T] => Unit = o => Subscription{}
2306+
}
2307+
*/
2308+
2309+
/**
2310+
* Returns an Observable that will execute the specified function when a someone subscribes to it.
2311+
*
2312+
* <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/create.png">
2313+
*
2314+
* Write the function you pass so that it behaves as an Observable: It should invoke the
2315+
* Subscriber's `onNext`, `onError`, and `onCompleted` methods appropriately.
2316+
*
2317+
* See <a href="http://go.microsoft.com/fwlink/?LinkID=205219">Rx Design Guidelines (PDF)</a> for detailed
2318+
* information.
2319+
*
2320+
* @tparam T
2321+
* the type of the items that this Observable emits
2322+
* @param f
2323+
* a function that accepts a `Subscriber[T]`, and invokes its `onNext`,
2324+
* `onError`, and `onCompleted` methods as appropriate
2325+
* @return an Observable that, when someone subscribes to it, will execute the specified
2326+
* function
2327+
*/
2328+
def apply[T](f: Subscriber[T] => Unit): Observable[T] = {
2329+
toScalaObservable(rx.Observable.create(f))
2330+
}
2331+
22842332
/**
22852333
* Returns an Observable that invokes an [[rx.lang.scala.Observer]]'s [[rx.lang.scala.Observer.onError onError]]
22862334
* method when the Observer subscribes to it.

0 commit comments

Comments
 (0)