Skip to content

Commit d837002

Browse files
committed
chore: Polish the api and implementation.
1 parent 97eb4e7 commit d837002

File tree

8 files changed

+162
-66
lines changed

8 files changed

+162
-66
lines changed
Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# Sink.forall
22

3-
Apply a predicate function to assert each element received, it returns true if all elements satisfy the assertion, otherwise it returns false.
3+
A `Sink` that will test the given predicate `p` for every received element and completes with the result.
44

55
@ref[Sink operators](../index.md#sink-operators)
66

77
## Signature
88

9-
@apidoc[Sink.forall](Sink$) { scala="#forall(predicate:T=>U):org.apache.pekko.stream.scaladsl.Sink[T,scala.concurrent.Future[Boolean]]" java="#forall(org.apache.pekko.japi.function.Predicate)" }
9+
@apidoc[Sink.forall](Sink$) { scala="#forall%5BT%5D(p%3A%20T%20%3D%3E%20Boolean):org.apache.pekko.stream.scaladsl.Sink[T,scala.concurrent.Future[Boolean]]" java="#forall(org.apache.pekko.japi.function.Predicate)" }
1010

1111
## Description
1212
forall applies a predicate function to assert each element received, it returns true if all elements satisfy the assertion, otherwise it returns false.
@@ -15,22 +15,33 @@ It materializes into a `Future` (in Scala) or a `CompletionStage` (in Java) that
1515

1616
Notes that if source is empty, it will return true
1717

18+
A `Sink` that will test the given predicate `p` for every received element and
19+
20+
- completes and returns @scala[`Future`] @java[`CompletionStage`] of `true` if the predicate is true for all elements;
21+
- completes and returns @scala[`Future`] @java[`CompletionStage`] of `true` if the stream is empty (i.e. completes before signalling any elements);
22+
- completes and returns @scala[`Future`] @java[`CompletionStage`] of `false` if the predicate is false for any element.
23+
24+
The materialized value @scala[`Future`] @java[`CompletionStage`] will be completed with the value `true` or `false`
25+
when the input stream ends, or completed with `Failure` if there is a failure signaled in the stream.
26+
1827
## Example
1928

20-
This example reads a stream of positive integers, asserts that all numbers are greater than 0, and finally prints the assertion result.
29+
This example tests all elements in the stream is `<=` 100.
2130

2231
Scala
23-
: @@snip [snip](/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/SinkSpec.scala) { #forall }
32+
: @@snip [ForAll.scala](/docs/src/test/scala/docs/stream/operators/sink/ForAll.scala) { #forall }
2433

2534
Java
26-
: @@snip [snip](/stream-tests/src/test/java/org/apache/pekko/stream/javadsl/SinkTest.java) { #forall }
35+
: @@snip [ForAll.java](/docs/src/test/java/jdocs/stream/operators/sink/ForAll.java) { #forall }
2736

2837
## Reactive Streams Semantics
2938

3039
@@@div { .callout }
3140

32-
**cancels** never
41+
***Completes*** when upstream completes or the predicate `p` returns `false`
42+
43+
**cancels** when predicate `p` returns `false`
3344

34-
**backpressures** when the predicate function invocation has not yet completed
45+
**backpressures** when the invocation of predicate `p` has not yet completed
3546

3647
@@@

docs/src/main/paradox/stream/operators/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ These built-in sinks are available from @scala[`org.apache.pekko.stream.scaladsl
6262
|Sink|<a name="completionstagesink"></a>@ref[completionStageSink](Sink/completionStageSink.md)|Streams the elements to the given future sink once it successfully completes. |
6363
|Sink|<a name="fold"></a>@ref[fold](Sink/fold.md)|Fold over emitted elements with a function, where each invocation will get the new element and the result from the previous fold invocation.|
6464
|Sink|<a name="foldwhile"></a>@ref[foldWhile](Sink/foldWhile.md)|Fold over emitted elements with a function, where each invocation will get the new element and the result from the previous fold invocation.|
65-
|Sink|<a name="forall"></a>@ref[forall](Sink/forall.md)|Apply a predicate function to assert each element received, it returns true if all elements satisfy the assertion, otherwise it returns false.|
65+
|Sink|<a name="forall"></a>@ref[forall](Sink/forall.md)|A `Sink` that will test the given predicate `p` for every received element and completes with the result.|
6666
|Sink|<a name="foreach"></a>@ref[foreach](Sink/foreach.md)|Invoke a given procedure for each element received.|
6767
|Sink|<a name="foreachasync"></a>@ref[foreachAsync](Sink/foreachAsync.md)|Invoke a given procedure asynchronously for each element received.|
6868
|Sink|<a name="foreachparallel"></a>@ref[foreachParallel](Sink/foreachParallel.md)|Like `foreach` but allows up to `parallellism` procedure calls to happen in parallel.|
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package jdocs.stream.operators.sink;
19+
20+
import org.apache.pekko.actor.ActorSystem;
21+
import org.apache.pekko.stream.javadsl.Sink;
22+
import org.apache.pekko.stream.javadsl.Source;
23+
24+
import java.util.concurrent.TimeUnit;
25+
26+
public class ForAll {
27+
private ActorSystem system = null;
28+
29+
public void forAllUsage() throws Exception {
30+
// #forall
31+
final boolean allMatch =
32+
Source.range(1, 100)
33+
.runWith(Sink.forall(elem -> elem <= 100), system)
34+
.toCompletableFuture()
35+
.get(3, TimeUnit.SECONDS);
36+
System.out.println(allMatch);
37+
// Expect prints:
38+
// true
39+
// #forall
40+
}
41+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package docs.stream.operators.sink
19+
20+
import org.apache.pekko.actor.ActorSystem
21+
import org.apache.pekko.stream.scaladsl.{ Sink, Source }
22+
23+
import scala.concurrent.duration.DurationInt
24+
import scala.concurrent.{ Await, ExecutionContextExecutor, Future }
25+
26+
object ForAll {
27+
implicit val system: ActorSystem = ???
28+
implicit val ec: ExecutionContextExecutor = system.dispatcher
29+
def foldExample: Unit = {
30+
// #forall
31+
val result: Future[Boolean] =
32+
Source(1 to 100)
33+
.runWith(Sink.forall(_ <= 100))
34+
val allMatch = Await.result(result, 3.seconds)
35+
println(allMatch)
36+
// Expect prints:
37+
// true
38+
// #forall
39+
}
40+
}

stream-tests/src/test/java/org/apache/pekko/stream/javadsl/SinkTest.java

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -238,16 +238,11 @@ public void sinkForeachMustBeDocumented()
238238
}
239239

240240
@Test
241-
public void sinkForallMustBeDocumented()
241+
public void sinkMustBeAbleToUseForall()
242242
throws InterruptedException, ExecutionException, TimeoutException {
243-
// #forall
244-
Sink<Integer, CompletionStage<Boolean>> forallSink = Sink.forall(param -> param > 0);
245243
CompletionStage<Boolean> cs =
246-
Source.from(Arrays.asList(1, 2, 3, 4)).runWith(forallSink, system);
247-
Boolean predicate = cs.toCompletableFuture().get(100, TimeUnit.MILLISECONDS);
248-
// will print
249-
// true
250-
// #forall
251-
assertEquals(predicate, true);
244+
Source.from(Arrays.asList(1, 2, 3, 4)).runWith(Sink.forall(param -> param > 0), system);
245+
boolean allMatch = cs.toCompletableFuture().get(100, TimeUnit.MILLISECONDS);
246+
assertTrue(allMatch);
252247
}
253248
}

stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/SinkSpec.scala

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@ package org.apache.pekko.stream.scaladsl
1616
import scala.annotation.nowarn
1717
import scala.concurrent.{ Await, Future }
1818
import scala.concurrent.duration._
19+
1920
import org.apache.pekko
2021
import pekko.Done
2122
import pekko.stream._
2223
import pekko.stream.testkit._
2324
import pekko.stream.testkit.scaladsl.{ TestSink, TestSource }
2425
import pekko.testkit.DefaultTimeout
26+
2527
import org.reactivestreams.Publisher
28+
2629
import org.scalatest.concurrent.ScalaFutures
2730

2831
class SinkSpec extends StreamSpec with DefaultTimeout with ScalaFutures {
@@ -338,37 +341,30 @@ class SinkSpec extends StreamSpec with DefaultTimeout with ScalaFutures {
338341

339342
"The forall sink" must {
340343

341-
"always true forall = true" in {
342-
// #forall
343-
val forallSink: Sink[Int, Future[Boolean]] = Sink.forall[Int](_ > 0)
344-
val f = Source(1 to 4).runWith(forallSink)
345-
val result = Await.result(f, 100.millis)
346-
// will print
347-
// true
348-
// #forall
349-
result shouldBe true
344+
"completes with `ture` when all elements match" in {
345+
Source(1 to 4)
346+
.runWith(Sink.forall(_ > 0))
347+
.futureValue shouldBe true
350348
}
351349

352-
"has false forall = false" in {
353-
val forallSink: Sink[Int, Future[Boolean]] = Sink.forall[Int](_ > 2)
354-
val f = Source(1 to 4).runWith(forallSink)
355-
val result = Await.result(f, 100.millis)
356-
result shouldBe false
350+
"completes with `false` when any element match" in {
351+
Source(1 to 4)
352+
.runWith(Sink.forall(_ > 2))
353+
.futureValue shouldBe false
357354
}
358355

359-
"always false forall = false" in {
360-
val forallSink: Sink[Int, Future[Boolean]] = Sink.forall[Int](_ < 0)
361-
val f = Source(1 to 4).runWith(forallSink)
362-
val result = Await.result(f, 100.millis)
363-
result shouldBe false
356+
"completes with `true` if the stream is empty" in {
357+
Source.empty[Int]
358+
.runWith(Sink.forall(_ > 2))
359+
.futureValue shouldBe true
364360
}
365361

366-
"empty forall = true" in {
367-
val forallSink: Sink[Int, Future[Boolean]] = Sink.forall[Int](_ < 0)
368-
val f = Source.empty.runWith(forallSink)
369-
val result = Await.result(f, 100.millis)
370-
result shouldBe true
362+
"completes with `Failure` if the stream failed" in {
363+
Source.failed[Int](new RuntimeException("Oops"))
364+
.runWith(Sink.forall(_ > 2))
365+
.failed.futureValue shouldBe a[RuntimeException]
371366
}
367+
372368
}
373369

374370
"Sink pre-materialization" must {

stream/src/main/scala/org/apache/pekko/stream/javadsl/Sink.scala

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414
package org.apache.pekko.stream.javadsl
1515

1616
import java.util.Optional
17-
import java.util.concurrent.CompletableFuture
18-
import java.util.concurrent.CompletionStage
17+
import java.util.concurrent.{ CompletableFuture, CompletionStage }
1918
import java.util.function.BiFunction
2019
import java.util.stream.Collector
2120

@@ -27,22 +26,17 @@ import scala.util.Try
2726

2827
import org.apache.pekko
2928
import pekko._
30-
import pekko.actor.ActorRef
31-
import pekko.actor.ClassicActorSystemProvider
32-
import pekko.actor.Status
29+
import pekko.actor.{ ActorRef, ClassicActorSystemProvider, Status }
3330
import pekko.dispatch.ExecutionContexts
3431
import pekko.japi.{ function, Util }
3532
import pekko.japi.function.Creator
3633
import pekko.stream._
3734
import pekko.stream.impl.LinearTraversalBuilder
38-
import pekko.stream.javadsl
39-
import pekko.stream.scaladsl
4035
import pekko.stream.scaladsl.SinkToCompletionStage
4136
import pekko.util.FutureConverters._
4237
import pekko.util.OptionConverters._
4338

44-
import org.reactivestreams.Publisher
45-
import org.reactivestreams.Subscriber
39+
import org.reactivestreams.{ Publisher, Subscriber }
4640

4741
/** Java API */
4842
object Sink {
@@ -81,17 +75,27 @@ object Sink {
8175
new Sink(scaladsl.Sink.foldAsync[U, In](zero)(f(_, _).asScala).toCompletionStage())
8276

8377
/**
84-
* A `Sink` that will invoke the given predicate for every received element, giving it its previous
85-
* output (or the given `false`) and the element as input.
78+
* A `Sink` that will test the given predicate `p` for every received element and
79+
* 1. completes and returns [[java.util.concurrent.CompletionStage]] of `true` if the predicate is true for all elements;
80+
* 2. completes and returns [[java.util.concurrent.CompletionStage]] of `true` if the stream is empty (i.e. completes before signalling any elements);
81+
* 3. completes and returns [[java.util.concurrent.CompletionStage]] of `false` if the predicate is false for any element.
8682
*
87-
* The returned [[java.util.concurrent.CompletionStage]] will be completed with the predicate is false, or the
88-
* the final predicate evaluation when the input stream ends, or completed with `Failure` if
89-
* there is a failure signaled in the stream.
83+
* The materialized value [[java.util.concurrent.CompletionStage]] will be completed with the value `true` or `false`
84+
* when the input stream ends, or completed with `Failure` if there is a failure signaled in the stream.
85+
*
86+
* Adheres to the [[ActorAttributes.SupervisionStrategy]] attribute.
87+
*
88+
* '''Completes when''' upstream completes or the predicate `p` returns `false`
89+
*
90+
* '''Backpressures when''' the invocation of predicate `p` has not yet completed
91+
*
92+
* '''Cancels when''' predicate `p` returns `false`
9093
*/
91-
def forall[T](predicate: function.Predicate[T]): javadsl.Sink[T, CompletionStage[java.lang.Boolean]] =
92-
scaladsl.Sink.forall(predicate.test)
93-
.mapMaterializedValue(_.map(Boolean.box)(ExecutionContexts.parasitic))
94-
.toCompletionStage().asJava
94+
def forall[In](p: function.Predicate[In]): javadsl.Sink[In, CompletionStage[java.lang.Boolean]] = {
95+
import pekko.util.FutureConverters._
96+
new Sink(scaladsl.Sink.forall[In](p.test)
97+
.mapMaterializedValue(_.map(Boolean.box)(ExecutionContexts.parasitic).asJava))
98+
}
9599

96100
/**
97101
* Creates a sink which materializes into a ``CompletionStage`` which will be completed with a result of the Java ``Collector``

stream/src/main/scala/org/apache/pekko/stream/scaladsl/Sink.scala

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -441,15 +441,24 @@ object Sink {
441441
Flow[T].foldAsync(zero)(f).toMat(Sink.head)(Keep.right).named("foldAsyncSink")
442442

443443
/**
444-
* A `Sink` that will invoke the given predicate for every received element, giving it its previous
445-
* output (or the given `false`) and the element as input.
444+
* A `Sink` that will test the given predicate `p` for every received element and
445+
* 1. completes and returns [[scala.concurrent.Future]] of `true` if the predicate is true for all elements;
446+
* 2. completes and returns [[scala.concurrent.Future]] of `true` if the stream is empty (i.e. completes before signalling any elements);
447+
* 3. completes and returns [[scala.concurrent.Future]] of `false` if the predicate is false for any element.
446448
*
447-
* The returned [[scala.concurrent.Future]] will be completed with the predicate is false, or the
448-
* the final predicate evaluation when the input stream ends, or completed with `Failure` if
449-
* there is a failure signaled in the stream.
449+
* The materialized value [[scala.concurrent.Future]] will be completed with the value `true` or `false`
450+
* when the input stream ends, or completed with `Failure` if there is a failure signaled in the stream.
451+
*
452+
* Adheres to the [[ActorAttributes.SupervisionStrategy]] attribute.
453+
*
454+
* '''Completes when''' upstream completes or the predicate `p` returns `false`
455+
*
456+
* '''Backpressures when''' the invocation of predicate `p` has not yet completed
457+
*
458+
* '''Cancels when''' predicate `p` returns `false`
450459
*/
451-
def forall[T](predicate: T => Boolean): Sink[T, Future[Boolean]] =
452-
Flow[T].foldWhile(true)(util.ConstantFun.scalaIdentityFunction)(_ && predicate(_))
460+
def forall[T](p: T => Boolean): Sink[T, Future[Boolean]] =
461+
Flow[T].foldWhile(true)(util.ConstantFun.scalaIdentityFunction)(_ && p(_))
453462
.toMat(Sink.head)(Keep.right)
454463
.named("forallSink")
455464

0 commit comments

Comments
 (0)