io.reactivex.rxjava3.core.ObservableTransformer Java Examples

The following examples show how to use io.reactivex.rxjava3.core.ObservableTransformer. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: MobiusEffectRouterTest.java    From mobius with Apache License 2.0 6 votes vote down vote up
@Test
public void effectHandlersShouldBeImmutable() throws Exception {
  // redo some test setup for test case specific conditions
  publishSubject = PublishSubject.create();
  testSubscriber = TestObserver.create();

  RxMobius.SubtypeEffectHandlerBuilder<TestEffect, TestEvent> builder =
      RxMobius.<TestEffect, TestEvent>subtypeEffectHandler()
          .addTransformer(A.class, (Observable<A> as) -> as.map(a -> AEvent.create(a.id())));

  ObservableTransformer<TestEffect, TestEvent> router = builder.build();

  // this should not lead to the effects router being capable of handling B effects
  builder.addTransformer(B.class, bs -> bs.map(b -> BEvent.create(b.id())));

  publishSubject.compose(router).subscribe(testSubscriber);

  B effect = B.create(84);
  publishSubject.onNext(effect);
  publishSubject.onComplete();

  testSubscriber.await();
  testSubscriber.assertError(new UnknownEffectException(effect));
}
 
Example #2
Source File: MobiusEffectRouterTest.java    From mobius with Apache License 2.0 6 votes vote down vote up
@Before
public void setUp() throws Exception {
  cConsumer = new TestConsumer<>();
  dAction = new TestAction();

  ObservableTransformer<TestEffect, TestEvent> router =
      RxMobius.<TestEffect, TestEvent>subtypeEffectHandler()
          .addTransformer(A.class, (Observable<A> as) -> as.map(a -> AEvent.create(a.id())))
          .addTransformer(B.class, (Observable<B> bs) -> bs.map(b -> BEvent.create(b.id())))
          .addConsumer(C.class, cConsumer)
          .addAction(D.class, dAction)
          .addFunction(E.class, e -> AEvent.create(e.id()))
          .build();

  publishSubject = PublishSubject.create();
  testSubscriber = TestObserver.create();

  publishSubject.compose(router).subscribe(testSubscriber);
}
 
Example #3
Source File: RxMobiusLoopTest.java    From mobius with Apache License 2.0 6 votes vote down vote up
@Test
public void shouldThrowIfStartingALoopWithInitAndStartEffects() throws Exception {
  MobiusLoop.Builder<String, Integer, Boolean> withInit =
      builder.init(
          new Init<String, Boolean>() {
            @Nonnull
            @Override
            public First<String, Boolean> init(String model) {
              return First.first(model + "-init");
            }
          });

  ObservableTransformer<Integer, String> transformer =
      RxMobius.loopFrom(withInit, "hi", effects(true));

  Observable.just(10)
      .compose(transformer)
      .test()
      .assertError(t -> t.getMessage().contains("cannot pass in start effects"));
}
 
Example #4
Source File: RxMobiusLoopTest.java    From mobius with Apache License 2.0 6 votes vote down vote up
@Test
public void shouldSupportStartingALoopWithAnInit() throws Exception {
  MobiusLoop.Builder<String, Integer, Boolean> withInit =
      builder.init(
          new Init<String, Boolean>() {
            @Nonnull
            @Override
            public First<String, Boolean> init(String model) {
              return First.first(model + "-init");
            }
          });

  ObservableTransformer<Integer, String> transformer = RxMobius.loopFrom(withInit, "hi");

  final TestObserver<String> observer = Observable.just(10).compose(transformer).test();

  observer.awaitCount(2);
  observer.assertValues("hi-init", "hi-init10");
}
 
Example #5
Source File: Transformers.java    From mobius with Apache License 2.0 6 votes vote down vote up
/**
 * 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 specified scheduler, and passing it
 * the requested effect object then emitting its returned value.
 *
 * @param function the {@link Function} to be invoked every time the effect is requested
 * @param scheduler the {@link Scheduler} to be used when invoking the function
 * @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
 *     RxMobius.SubtypeEffectHandlerBuilder}.
 */
static <F, E> ObservableTransformer<F, E> fromFunction(
    final Function<F, E> function, @Nullable final Scheduler scheduler) {
  return new ObservableTransformer<F, E>() {
    @Override
    public ObservableSource<E> apply(Observable<F> effectStream) {
      return effectStream.flatMap(
          new Function<F, ObservableSource<E>>() {
            @Override
            public ObservableSource<E> apply(@NonNull F f) {
              Observable<E> eventObservable =
                  Observable.fromSupplier(
                      new Supplier<E>() {
                        @Override
                        public E get() throws Throwable {
                          return function.apply(f);
                        }
                      });
              return scheduler == null ? eventObservable : eventObservable.subscribeOn(scheduler);
            }
          });
    }
  };
}
 
Example #6
Source File: RxMobius.java    From mobius with Apache License 2.0 6 votes vote down vote up
/**
 * Add an {@link ObservableTransformer} for handling effects of a given type. The handler will
 * receive all effect objects that extend the given class.
 *
 * <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 effectHandler the effect handler for the given effect class
 * @param <G> the effect class as a type parameter
 * @return this builder
 * @throws IllegalArgumentException if there is a handler collision
 */
public <G extends F> RxMobius.SubtypeEffectHandlerBuilder<F, E> addTransformer(
    final Class<G> effectClass, final ObservableTransformer<G, E> effectHandler) {
  checkNotNull(effectClass);
  checkNotNull(effectHandler);

  for (Class<?> cls : effectPerformerMap.keySet()) {
    if (cls.isAssignableFrom(effectClass) || effectClass.isAssignableFrom(cls)) {
      throw new IllegalArgumentException(
          "Effect classes may not be assignable to each other, collision found: "
              + effectClass.getSimpleName()
              + " <-> "
              + cls.getSimpleName());
    }
  }

  effectPerformerMap.put(
      effectClass,
      (Observable<F> effects) ->
          effects
              .ofType(effectClass)
              .compose(effectHandler)
              .doOnError(onErrorFunction.apply(effectHandler)));

  return this;
}
 
Example #7
Source File: RxMobius.java    From mobius with Apache License 2.0 6 votes vote down vote up
/**
 * Optionally set a shared error handler in case a handler throws an uncaught exception.
 *
 * <p>The default is to use {@link RxJavaPlugins#onError(Throwable)}. Note that any exception
 * thrown by a handler is a fatal error and this method doesn't enable safe error handling, only
 * configurable crash reporting.
 *
 * @param function a function that gets told which sub-transformer failed and should return an
 *     appropriate handler for exceptions thrown.
 */
public RxMobius.SubtypeEffectHandlerBuilder<F, E> withFatalErrorHandler(
    final Function<ObservableTransformer<? extends F, E>, Consumer<Throwable>> function) {
  checkNotNull(function);
  this.onErrorFunction =
      new OnErrorFunction<ObservableTransformer<? extends F, E>, Consumer<Throwable>>() {
        @Override
        public Consumer<Throwable> apply(ObservableTransformer<? extends F, E> effectHandler) {
          try {
            return function.apply(effectHandler);
          } catch (Throwable e) {
            throw new RuntimeException(
                "FATAL: fatal error handler threw exception for effect handler: "
                    + effectHandler,
                e);
          }
        }
      };

  return this;
}
 
Example #8
Source File: Transformers.java    From mobius with Apache License 2.0 6 votes vote down vote up
/**
 * Creates an {@link ObservableTransformer} that will flatten the provided {@link Consumer} into
 * the stream as a {@link Completable} every time it receives an effect from the upstream effects
 * observable. This will result in calling the consumer on the specified scheduler, and passing it
 * the requested effect object.
 *
 * @param doEffect the {@link Consumer} to be run every time the effect is requested
 * @param scheduler the {@link Scheduler} to be used when invoking the consumer
 * @param <F> the type of Effect this transformer handles
 * @param <E> these transformers are for effects that do not result in any events; however, they
 *     still need to share the same Event type
 * @return an {@link ObservableTransformer} that can be used with a {@link
 *     RxMobius.SubtypeEffectHandlerBuilder}.
 */
static <F, E> ObservableTransformer<F, E> fromConsumer(
    final Consumer<F> doEffect, @Nullable final Scheduler scheduler) {
  return new ObservableTransformer<F, E>() {
    @Override
    public ObservableSource<E> apply(Observable<F> effectStream) {
      return effectStream
          .flatMapCompletable(
              new Function<F, CompletableSource>() {
                @Override
                public CompletableSource apply(final F effect) {
                  Completable completable =
                      Completable.fromAction(
                          new Action() {
                            @Override
                            public void run() throws Throwable {
                              doEffect.accept(effect);
                            }
                          });
                  return scheduler == null ? completable : completable.subscribeOn(scheduler);
                }
              })
          .toObservable();
    }
  };
}
 
Example #9
Source File: Transformers.java    From mobius with Apache License 2.0 6 votes vote down vote up
/**
 * Creates an {@link ObservableTransformer} that will flatten the provided {@link Action} into the
 * stream as a {@link Completable} every time it receives an effect from the upstream effects
 * observable. This Completable will be subscribed on the specified {@link Scheduler}. This will
 * result in calling the provided Action on the specified scheduler every time an effect is
 * dispatched to the created effect transformer.
 *
 * @param doEffect the {@link Action} to be run every time the effect is requested
 * @param scheduler the {@link Scheduler} that the action should be run on
 * @param <F> the type of Effect this transformer handles
 * @param <E> these transformers are for effects that do not result in any events; however, they
 *     still need to share the same Event type
 * @return an {@link ObservableTransformer} that can be used with a {@link
 *     RxMobius.SubtypeEffectHandlerBuilder}.
 */
static <F, E> ObservableTransformer<F, E> fromAction(
    final Action doEffect, @Nullable final Scheduler scheduler) {
  return new ObservableTransformer<F, E>() {
    @Override
    public ObservableSource<E> apply(Observable<F> effectStream) {
      return effectStream
          .flatMapCompletable(
              new Function<F, CompletableSource>() {
                @Override
                public CompletableSource apply(F f) throws Exception {
                  return scheduler == null
                      ? Completable.fromAction(doEffect)
                      : Completable.fromAction(doEffect).subscribeOn(scheduler);
                }
              })
          .toObservable();
    }
  };
}
 
Example #10
Source File: MergedTransformer.java    From mobius with Apache License 2.0 5 votes vote down vote up
@Override
public Observable<R> apply(@NonNull Observable<T> input) {
  return input.publish(
      new Function<Observable<T>, Observable<R>>() {
        @Override
        public Observable<R> apply(Observable<T> innerInput) throws Throwable {
          final List<Observable<R>> transformed = new ArrayList<>();
          for (ObservableTransformer<T, R> transformer : transformers) {
            transformed.add(innerInput.compose(transformer));
          }
          return Observable.merge(transformed);
        }
      });
}
 
Example #11
Source File: MobiusEffectRouterTest.java    From mobius with Apache License 2.0 5 votes vote down vote up
@Test
public void shouldHandleNullRxJavaErrorHandler() throws Exception {
  // given no RxJava error handler
  RxJavaPlugins.setErrorHandler(null);

  // and a router with a broken effect handler
  publishSubject = PublishSubject.create();
  testSubscriber = TestObserver.create();

  final RuntimeException expected = new RuntimeException("expected!");
  ObservableTransformer<TestEffect, TestEvent> router =
      RxMobius.<TestEffect, TestEvent>subtypeEffectHandler()
          .addFunction(
              A.class,
              a -> {
                throw expected;
              })
          .build();

  publishSubject.compose(router).subscribe(testSubscriber);

  // when an event is sent, it doesn't crash (the exception does get printed to stderr)
  publishSubject.onNext(A.create(1));

  // and the right exception is forwarded to the test subscriber
  testSubscriber.assertError(t -> t == expected);
}
 
Example #12
Source File: RxMobius.java    From mobius with Apache License 2.0 5 votes vote down vote up
private static <F, E> Consumer<Throwable> defaultOnError(
    final ObservableTransformer<? extends F, E> effectHandler) {
  return new Consumer<Throwable>() {
    @Override
    public void accept(Throwable throwable) throws Throwable {
      RxJavaPlugins.onError(
          new ConnectionException(
              "in effect handler: " + effectHandler.getClass().toString(), throwable));
    }
  };
}
 
Example #13
Source File: RxMobius.java    From mobius with Apache License 2.0 4 votes vote down vote up
public ObservableTransformer<F, E> build() {
  return new MobiusEffectRouter<>(effectPerformerMap.keySet(), effectPerformerMap.values());
}
 
Example #14
Source File: RxConnectables.java    From mobius with Apache License 2.0 4 votes vote down vote up
@NonNull
public static <I, O> ObservableTransformer<I, O> toTransformer(
    final Connectable<I, O> connectable) {
  return new ObservableTransformer<I, O>() {
    @Override
    public @NonNull ObservableSource<O> apply(@NonNull Observable<I> upstream) {
      return Observable.create(
          new ObservableOnSubscribe<O>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<O> emitter) throws Throwable {
              com.spotify.mobius.functions.Consumer<O> output = emitter::onNext;
              final Connection<I> input = connectable.connect(output);
              final Disposable disposable =
                  upstream.subscribe(
                      new Consumer<I>() {
                        @Override
                        public void accept(I value) throws Throwable {
                          input.accept(value);
                        }
                      },
                      new Consumer<Throwable>() {
                        @Override
                        public void accept(Throwable error) throws Throwable {
                          emitter.onError(error);
                        }
                      },
                      new Action() {
                        @Override
                        public void run() throws Throwable {
                          emitter.onComplete();
                        }
                      });

              emitter.setCancellable(
                  new Cancellable() {
                    @Override
                    public void cancel() throws Throwable {
                      disposable.dispose();
                      input.dispose();
                    }
                  });
            }
          });
    }
  };
}
 
Example #15
Source File: RxConnectables.java    From mobius with Apache License 2.0 4 votes vote down vote up
public static <I, O> Connectable<I, O> fromTransformer(
    @NonNull final ObservableTransformer<I, O> transformer) {
  checkNotNull(transformer);
  return new Connectable<I, O>() {
    @Nonnull
    @Override
    public Connection<I> connect(com.spotify.mobius.functions.Consumer<O> output) {
      final PublishSubject<I> subject = PublishSubject.create();

      final Disposable disposable =
          subject
              .compose(transformer)
              .subscribe(
                  new Consumer<O>() {
                    @Override
                    public void accept(O value) throws Throwable {
                      output.accept(value);
                    }
                  },
                  new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable error) throws Throwable {
                      RxJavaPlugins.onError(error);
                    }
                  },
                  new Action() {
                    @Override
                    public void run() throws Throwable {}
                  });

      return new Connection<I>() {
        public void accept(I effect) {
          subject.onNext(effect);
        }

        @Override
        public void dispose() {
          disposable.dispose();
        }
      };
    }
  };
}
 
Example #16
Source File: MobiusEffectRouterTest.java    From mobius with Apache License 2.0 4 votes vote down vote up
@Test
public void shouldSupportCustomErrorHandler() throws Exception {
  // redo some test setup for test case specific conditions
  publishSubject = PublishSubject.create();
  testSubscriber = TestObserver.create();

  final RuntimeException expectedException = new RuntimeException("expected!");
  final AtomicBoolean gotRightException = new AtomicBoolean(false);

  RxMobius.SubtypeEffectHandlerBuilder<TestEffect, TestEvent> builder =
      RxMobius.<TestEffect, TestEvent>subtypeEffectHandler()
          .addFunction(
              A.class,
              a -> {
                throw expectedException;
              })
          .withFatalErrorHandler(
              new Function<
                  ObservableTransformer<? extends TestEffect, TestEvent>, Consumer<Throwable>>() {
                @Override
                public Consumer<Throwable> apply(
                    ObservableTransformer<? extends TestEffect, TestEvent> testEventTransformer) {
                  return new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) {
                      if (throwable.equals(expectedException)) {
                        gotRightException.set(true);
                      } else {
                        throwable.printStackTrace();
                        Assert.fail("got the wrong exception!");
                      }
                    }
                  };
                }
              });

  ObservableTransformer<TestEffect, TestEvent> router = builder.build();

  publishSubject.compose(router).subscribe(testSubscriber);

  publishSubject.onNext(A.create(1));

  assertThat(gotRightException.get(), is(true));

  testSubscriber.await();
  testSubscriber.assertError(expectedException);
}
 
Example #17
Source File: MergedTransformer.java    From mobius with Apache License 2.0 4 votes vote down vote up
MergedTransformer(@NonNull Iterable<ObservableTransformer<T, R>> transformers) {
  this.transformers = checkNotNull(transformers);
}
 
Example #18
Source File: RxMobius.java    From mobius with Apache License 2.0 3 votes vote down vote up
/**
 * Create an observable transformer that starts from a given model and given effects.
 *
 * <p>Every time the resulting observable is subscribed to, a new MobiusLoop will be started from
 * the given model and the given effects.
 *
 * @param loopFactory gets invoked for each subscription, to create a new MobiusLoop instance
 * @param startModel the starting point for each new loop
 * @param startEffects the starting effects for each new loop
 * @param <M> the model type
 * @param <E> the event type
 * @param <F> the effect type
 * @return a transformer from event to model that you can connect to your UI
 */
public static <M, E, F> ObservableTransformer<E, M> loopFrom(
    final MobiusLoop.Factory<M, E, F> loopFactory,
    final M startModel,
    final Set<F> startEffects) {
  return new RxMobiusLoop<>(loopFactory, startModel, startEffects);
}
 
Example #19
Source File: Transformers.java    From mobius with Apache License 2.0 2 votes vote down vote up
/**
 * Creates an {@link ObservableTransformer} that will flatten the provided {@link Action} into the
 * stream as a {@link Completable} every time it receives an effect from the upstream effects
 * observable. This will result in calling the provided Action every time an effect is dispatched
 * to the created effect transformer.
 *
 * @param doEffect the {@link Action} to be run every time the effect is requested
 * @param <F> the type of Effect this transformer handles
 * @param <E> these transformers are for effects that do not result in any events; however, they
 *     still need to share the same Event type
 * @return an {@link ObservableTransformer} that can be used with a {@link
 *     RxMobius.SubtypeEffectHandlerBuilder}.
 */
static <F, E> ObservableTransformer<F, E> fromAction(@NonNull final Action doEffect) {
  return fromAction(doEffect, null);
}
 
Example #20
Source File: Transformers.java    From mobius with Apache License 2.0 2 votes vote down vote up
/**
 * Creates an {@link ObservableTransformer} that will flatten the provided {@link Consumer} into
 * the stream as a {@link Completable} every time it receives an effect from the upstream effects
 * observable. This will result in calling the consumer and and passing it the requested effect
 * object.
 *
 * @param doEffect the {@link Consumer} to be run every time the effect is requested
 * @param <F> the type of Effect this transformer handles
 * @param <E> these transformers are for effects that do not result in any events; however, they
 *     still need to share the same Event type
 * @return an {@link ObservableTransformer} that can be used with a {@link
 *     RxMobius.SubtypeEffectHandlerBuilder}.
 */
static <F, E> ObservableTransformer<F, E> fromConsumer(final Consumer<F> doEffect) {
  return fromConsumer(doEffect, null);
}
 
Example #21
Source File: RxMobius.java    From mobius with Apache License 2.0 2 votes vote down vote up
/**
 * Create a {@link MobiusLoop.Builder} to help you configure a MobiusLoop before starting it.
 *
 * <p>Once done configuring the loop you can start the loop using {@link
 * MobiusLoop.Factory#startFrom(Object)}.
 *
 * @param update the {@link Update} function of the loop
 * @param effectHandler the {@link ObservableTransformer} effect handler of the loop
 * @param <M> the model type
 * @param <E> the event type
 * @param <F> the effect type
 * @return a {@link MobiusLoop.Builder} instance that you can further configure before starting
 *     the loop
 */
public static <M, E, F> MobiusLoop.Builder<M, E, F> loop(
    Update<M, E, F> update, ObservableTransformer<F, E> effectHandler) {
  return Mobius.loop(update, RxConnectables.fromTransformer(effectHandler));
}
 
Example #22
Source File: Transformers.java    From mobius with Apache License 2.0 2 votes vote down vote up
/**
 * 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
 *     RxMobius.SubtypeEffectHandlerBuilder}.
 */
static <F, E> ObservableTransformer<F, E> fromFunction(final Function<F, E> function) {
  return fromFunction(function, null);
}
 
Example #23
Source File: RxMobius.java    From mobius with Apache License 2.0 2 votes vote down vote up
/**
 * Create an observable transformer that starts from a given model.
 *
 * <p>Every time the resulting observable is subscribed to, a new MobiusLoop will be started from
 * the given model.
 *
 * @param loopFactory gets invoked for each subscription, to create a new MobiusLoop instance
 * @param startModel the starting point for each new loop
 * @param <M> the model type
 * @param <E> the event type
 * @param <F> the effect type
 * @return a transformer from event to model that you can connect to your UI
 */
public static <M, E, F> ObservableTransformer<E, M> loopFrom(
    final MobiusLoop.Factory<M, E, F> loopFactory, final M startModel) {
  return new RxMobiusLoop<>(loopFactory, startModel, null);
}