/** * Create a {@link MobiusLoop.Builder} to help you configure a MobiusLoop before starting it. * * <p>This is an alternative to {@link Mobius#loop(Update, Connectable)} if you want to set up a * simple loop without effects. The primary use of this is to explain how to setup a Mobius loop. * * <p>Once done configuring the loop you can start the loop using {@link * MobiusLoop.Factory#startFrom(Object)}. * * @param update the update function of the loop. * @return a {@link MobiusLoop.Builder} instance that you can further configure before starting * the loop */ @Nonnull public static <M, E> MobiusLoop.Builder<M, E, ?> beginnerLoop(final BiFunction<M, E, M> update) { //noinspection unchecked return loop( new Update<M, E, Object>() { @Nonnull @Override public Next<M, Object> update(M model, E event) { return Next.next(update.apply(model, event)); } }, (Connectable<Object, E>) NOOP_CONNECTABLE); } }
/** * 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 Transformer} 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, Transformer<F, E> effectHandler) { return Mobius.loop(update, RxConnectables.fromTransformer(effectHandler)); }
/** * 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)); }
@Before public void setUp() throws Exception { MobiusLoop.Factory<String, Integer, Boolean> factory = Mobius.loop( new Update<String, Integer, Boolean>() { @Nonnull @Override public Next<String, Boolean> update(String model, Integer event) { return Next.next(model + event.toString()); } }, new Connectable<Boolean, Integer>() { @Nonnull @Override public Connection<Boolean> connect(Consumer<Integer> output) throws ConnectionLimitExceededException { return new SimpleConnection<Boolean>() { @Override public void accept(Boolean value) { // no implementation, no effects will happen } }; } }); loop = new RxMobiusLoop<>(factory, ""); }
@Before public void setUp() throws Exception { MobiusLoop.Factory<String, Integer, Boolean> factory = Mobius.loop( new Update<String, Integer, Boolean>() { @Nonnull @Override public Next<String, Boolean> update(String model, Integer event) { return Next.next(model + event.toString()); } }, new Connectable<Boolean, Integer>() { @Nonnull @Override public Connection<Boolean> connect(Consumer<Integer> output) throws ConnectionLimitExceededException { return new SimpleConnection<Boolean>() { @Override public void accept(Boolean value) { // no implementation, no effects will happen } }; } }); loop = new RxMobiusLoop<>(factory, ""); }
private MobiusLoopController<String, String, String> createWithWorkRunner( WorkRunner mainThreadRunner) { return new MobiusLoopController<>( Mobius.<String, String, String>loop( (model, event) -> Next.next(model + event), effectHandler) .eventRunner(WorkRunners::immediate) .effectRunner(WorkRunners::immediate), "init", mainThreadRunner); }
/** @return a {@link MobiusLoop.Builder} based on this program */ public MobiusLoop.Builder<M, E, F> createLoop() { MobiusLoop.Builder<M, E, F> builder = Mobius.loop(update(), effectHandler()); Init<M, F> init = init(); if (init != null) { builder = builder.init(init); } EventSource<E> eventSource = eventSource(); if (eventSource != null) { builder = builder.eventSource(eventSource); } String loggingTag = loggingTag(); if (loggingTag != null) { builder = builder.logger(SLF4JLogger.<M, E, F>withTag(loggingTag)); } return builder; }
@Test public void shouldInstantiateWithMinimumParams() throws Exception { loop = Mobius.loop(UPDATE, HANDLER).startFrom(MY_MODEL); loop.dispatchEvent(8); await().atMost(Duration.ONE_SECOND).until(() -> loop.getMostRecentModel(), is("start83")); }
@Test public void shouldSupportCreatingFactory() throws Exception { MobiusLoop.Factory<String, Integer, Boolean> factory = Mobius.loop(UPDATE, HANDLER); loop = factory.startFrom("resume"); loop.dispatchEvent(97); await().atMost(Duration.ONE_SECOND).until(() -> loop.getMostRecentModel(), is("resume97")); }
@Test public void shouldPermitUsingCustomEventRunner() throws Exception { TestableWorkRunner runner = new TestableWorkRunner(); loop = Mobius.loop(UPDATE, HANDLER).eventRunner(() -> runner).startFrom(MY_MODEL); loop.dispatchEvent(3); // 2 because the initial model dispatch is run on the event runner await().atMost(Duration.ONE_SECOND).until(() -> runner.runCounter.get() == 2); }
@Test public void shouldPermitUsingCustomEffectRunner() throws Exception { TestableWorkRunner runner = new TestableWorkRunner(); loop = Mobius.loop(UPDATE, HANDLER).effectRunner(() -> runner).startFrom(MY_MODEL); loop.dispatchEvent(3); await().atMost(Duration.ONE_SECOND).until(() -> runner.runCounter.get() == 1); }
@Test public void shouldPermitUsingCustomInit() throws Exception { Init<String, Boolean> init = new Init<String, Boolean>() { @Nonnull @Override public First<String, Boolean> init(String model) { return First.first(model, effects(true)); } }; loop = Mobius.loop(UPDATE, HANDLER).init(init).startFrom(MY_MODEL); loop.dispatchEvent(3); await().atMost(Duration.ONE_SECOND).until(() -> "start33".equals(loop.getMostRecentModel())); }
@Test public void shouldPermitUsingEventSource() throws Exception { TestEventSource eventSource = new TestEventSource(); loop = Mobius.loop(UPDATE, HANDLER).eventSource(eventSource).startFrom(MY_MODEL); eventSource.consumer.accept(7); await().atMost(Duration.ONE_SECOND).until(() -> loop.getMostRecentModel(), is("start7")); }
@Test public void shouldPermitUsingCustomLogger() throws Exception { TestLogger logger = new TestLogger(); loop = Mobius.loop(UPDATE, HANDLER) .logger(logger) .eventRunner(ImmediateWorkRunner::new) .effectRunner(ImmediateWorkRunner::new) .startFrom(MY_MODEL); loop.dispatchEvent(7); assertThat( logger.history, contains( "before init: start", "after init: start, First{model=start, effects=[]}", "before update: start, 7", "after update: start, 7, Next{model=start7, effects=[false]}")); }
@Test public void shouldSupportCreatingMultipleLoops() throws Exception { MobiusLoop.Factory<String, Integer, Boolean> factory = Mobius.loop(UPDATE, HANDLER); // one loop = factory.startFrom("first"); loop.dispatchEvent(97); await().atMost(Duration.ONE_SECOND).until(() -> loop.getMostRecentModel(), is("first97")); loop.dispose(); // then another one loop = factory.startFrom("second"); loop.dispatchEvent(97); await().atMost(Duration.ONE_SECOND).until(() -> loop.getMostRecentModel(), is("second97")); }