/** * Creates an {@link ObservableTransformer} that will flatten the provided {@link Function} into * the stream as an {@link Observable} every time it receives an effect from the upstream effects * observable. This will result in calling the function on the immediate scheduler, and passing it * the requested effect object then emitting its returned value. * * @param function {@link Function} to be invoked every time the effect is requested * @param <F> the type of Effect this transformer handles * @param <E> the type of Event this transformer emits * @return an {@link ObservableTransformer} that can be used with a {@link * SubtypeEffectHandlerBuilder}. */ static <F, E> ObservableTransformer<F, E> fromFunction(final Function<F, E> function) { return fromFunction(function, null); } }
/** * Add a {@link Function} for handling effects of a given type. The function will be invoked * once for every received effect object that extends the given class. The returned event will * be forwarded to the Mobius loop. * * <p>Adding handlers for two effect classes where one is a super-class of the other is * considered a collision and is not allowed. Registering the same class twice is also * considered a collision. * * @param effectClass the class to handle * @param function the function that should be invoked for the effect * @param <G> the effect class as a type parameter * @return this builder * @throws IllegalArgumentException if there is a handler collision */ public <G extends F> SubtypeEffectHandlerBuilder<F, E> addFunction( final Class<G> effectClass, final Function<G, E> function) { //noinspection ResultOfMethodCallIgnored checkNotNull(effectClass); //noinspection ResultOfMethodCallIgnored checkNotNull(function); return addTransformer(effectClass, Transformers.fromFunction(function)); }
/** * Add a {@link Function} for handling effects of a given type. The function will be invoked * once for every received effect object that extends the given class. The returned event will * be forwarded to the Mobius loop. * * <p>Adding handlers for two effect classes where one is a super-class of the other is * considered a collision and is not allowed. Registering the same class twice is also * considered a collision. * * @param effectClass the class to handle * @param function the function that should be invoked for the effect * @param scheduler the scheduler that should be used when invoking the function * @param <G> the effect class as a type parameter * @return this builder * @throws IllegalArgumentException if there is a handler collision */ public <G extends F> SubtypeEffectHandlerBuilder<F, E> addFunction( final Class<G> effectClass, final Function<G, E> function, Scheduler scheduler) { //noinspection ResultOfMethodCallIgnored checkNotNull(effectClass); //noinspection ResultOfMethodCallIgnored checkNotNull(function); return addTransformer(effectClass, Transformers.fromFunction(function, scheduler)); }
@Test public void processingLongEffectsDoesNotBlockProcessingShorterEffects() { final List<String> effects = Arrays.asList("Hello", "Rx2"); PublishSubject<String> upstream = PublishSubject.create(); Function<String, Integer> sleepyFunction = s -> { try { Thread.sleep(duration(s)); } catch (InterruptedException ie) { } return s.length(); }; final List<Integer> results = new ArrayList<>(); upstream .compose(Transformers.fromFunction(sleepyFunction, Schedulers.io())) .subscribe(results::add); Observable.fromIterable(effects).subscribe(upstream); await().atMost(durationForEffects(effects)).until(() -> results.equals(expected(effects))); }
@Test public void effectPerformerInvokesFunctionWithReceivedEffectAndEmitsReturnedEvents() { PublishSubject<String> upstream = PublishSubject.create(); TestScheduler scheduler = new TestScheduler(); Function<String, Integer> function = s -> s.length(); TestObserver<Integer> observer = upstream.compose(Transformers.fromFunction(function, scheduler)).test(); upstream.onNext("Hello"); scheduler.triggerActions(); observer.assertValue(5); }
@Test public void effectPerformerInvokesFunctionWithReceivedEffectAndErrorsForUnhandledExceptions() { PublishSubject<String> upstream = PublishSubject.create(); TestScheduler scheduler = new TestScheduler(); Function<String, Integer> function = s -> { throw new RuntimeException("Something bad happened"); }; TestObserver<Integer> observer = upstream.compose(Transformers.fromFunction(function, scheduler)).test(); upstream.onNext("Hello"); scheduler.triggerActions(); observer.assertError(RuntimeException.class); }