Skip to content

Commit

Permalink
Documentation for each individual module
Browse files Browse the repository at this point in the history
  • Loading branch information
cjbrooks12 committed Mar 17, 2022
1 parent 28cbf3b commit 3d7d730
Show file tree
Hide file tree
Showing 12 changed files with 520 additions and 49 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
## 0.12.0 - 2022-03-16
## 0.12.0 - 2022-03-17

- Fixes issue with `inputHandler { }` and `eventHandler { }` functions, which previously didn't actually send the input
or event to the lambda
- Move all InputHandler checks into the Guardian, so any/all of them could be thrown away by the end-user if needed
- make a dedicated `InputStrategyScope` to match the pattern of all other pluggable features

## 0.11.0 - 2022-03-15

Expand Down
20 changes: 8 additions & 12 deletions ballast-core/api/android/ballast-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ public abstract interface class com/copperleaf/ballast/InputHandlerScope {
public abstract interface class com/copperleaf/ballast/InputStrategy {
public abstract fun createQueue ()Lkotlinx/coroutines/channels/Channel;
public abstract fun getRollbackOnCancellation ()Z
public abstract fun processInputs (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun processInputs (Lcom/copperleaf/ballast/InputStrategyScope;Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public abstract interface class com/copperleaf/ballast/InputStrategy$Guardian {
Expand All @@ -277,6 +277,10 @@ public final class com/copperleaf/ballast/InputStrategy$Guardian$DefaultImpls {
public static fun close (Lcom/copperleaf/ballast/InputStrategy$Guardian;)V
}

public abstract interface class com/copperleaf/ballast/InputStrategyScope {
public abstract fun acceptQueued (Lcom/copperleaf/ballast/Queued;Lcom/copperleaf/ballast/InputStrategy$Guardian;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public abstract class com/copperleaf/ballast/Queued {
}

Expand Down Expand Up @@ -412,14 +416,14 @@ public final class com/copperleaf/ballast/core/FifoInputStrategy : com/copperlea
public fun <init> ()V
public fun createQueue ()Lkotlinx/coroutines/channels/Channel;
public fun getRollbackOnCancellation ()Z
public fun processInputs (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun processInputs (Lcom/copperleaf/ballast/InputStrategyScope;Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class com/copperleaf/ballast/core/LifoInputStrategy : com/copperleaf/ballast/InputStrategy {
public fun <init> ()V
public fun createQueue ()Lkotlinx/coroutines/channels/Channel;
public fun getRollbackOnCancellation ()Z
public fun processInputs (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun processInputs (Lcom/copperleaf/ballast/InputStrategyScope;Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class com/copperleaf/ballast/core/LoggingInterceptor : com/copperleaf/ballast/BallastInterceptor {
Expand All @@ -439,7 +443,7 @@ public final class com/copperleaf/ballast/core/ParallelInputStrategy : com/coppe
public fun <init> ()V
public fun createQueue ()Lkotlinx/coroutines/channels/Channel;
public fun getRollbackOnCancellation ()Z
public fun processInputs (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun processInputs (Lcom/copperleaf/ballast/InputStrategyScope;Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class com/copperleaf/ballast/core/ParallelInputStrategy$Guardian : com/copperleaf/ballast/core/DefaultGuardian {
Expand All @@ -455,14 +459,6 @@ public final class com/copperleaf/ballast/core/PrintlnLogger : com/copperleaf/ba
public fun info (Ljava/lang/String;)V
}

public final class com/copperleaf/ballast/internal/BallastInterceptorScopeImpl : com/copperleaf/ballast/BallastInterceptorScope, kotlinx/coroutines/CoroutineScope {
public fun <init> (Lcom/copperleaf/ballast/BallastLogger;Ljava/lang/String;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;)V
public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext;
public fun getHostViewModelName ()Ljava/lang/String;
public fun getLogger ()Lcom/copperleaf/ballast/BallastLogger;
public fun sendToQueue (Lcom/copperleaf/ballast/Queued;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class com/copperleaf/ballast/internal/BallastViewModelImpl : com/copperleaf/ballast/BallastViewModel, com/copperleaf/ballast/BallastViewModelConfiguration, kotlinx/coroutines/channels/SendChannel {
public fun <init> (Lcom/copperleaf/ballast/BallastViewModelConfiguration;Lkotlinx/coroutines/channels/Channel;)V
public synthetic fun <init> (Lcom/copperleaf/ballast/BallastViewModelConfiguration;Lkotlinx/coroutines/channels/Channel;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
Expand Down
20 changes: 8 additions & 12 deletions ballast-core/api/jvm/ballast-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ public abstract interface class com/copperleaf/ballast/InputHandlerScope {
public abstract interface class com/copperleaf/ballast/InputStrategy {
public abstract fun createQueue ()Lkotlinx/coroutines/channels/Channel;
public abstract fun getRollbackOnCancellation ()Z
public abstract fun processInputs (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun processInputs (Lcom/copperleaf/ballast/InputStrategyScope;Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public abstract interface class com/copperleaf/ballast/InputStrategy$Guardian {
Expand All @@ -277,6 +277,10 @@ public final class com/copperleaf/ballast/InputStrategy$Guardian$DefaultImpls {
public static fun close (Lcom/copperleaf/ballast/InputStrategy$Guardian;)V
}

public abstract interface class com/copperleaf/ballast/InputStrategyScope {
public abstract fun acceptQueued (Lcom/copperleaf/ballast/Queued;Lcom/copperleaf/ballast/InputStrategy$Guardian;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public abstract class com/copperleaf/ballast/Queued {
}

Expand Down Expand Up @@ -390,14 +394,14 @@ public final class com/copperleaf/ballast/core/FifoInputStrategy : com/copperlea
public fun <init> ()V
public fun createQueue ()Lkotlinx/coroutines/channels/Channel;
public fun getRollbackOnCancellation ()Z
public fun processInputs (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun processInputs (Lcom/copperleaf/ballast/InputStrategyScope;Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class com/copperleaf/ballast/core/LifoInputStrategy : com/copperleaf/ballast/InputStrategy {
public fun <init> ()V
public fun createQueue ()Lkotlinx/coroutines/channels/Channel;
public fun getRollbackOnCancellation ()Z
public fun processInputs (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun processInputs (Lcom/copperleaf/ballast/InputStrategyScope;Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class com/copperleaf/ballast/core/LoggingInterceptor : com/copperleaf/ballast/BallastInterceptor {
Expand All @@ -417,7 +421,7 @@ public final class com/copperleaf/ballast/core/ParallelInputStrategy : com/coppe
public fun <init> ()V
public fun createQueue ()Lkotlinx/coroutines/channels/Channel;
public fun getRollbackOnCancellation ()Z
public fun processInputs (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun processInputs (Lcom/copperleaf/ballast/InputStrategyScope;Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class com/copperleaf/ballast/core/ParallelInputStrategy$Guardian : com/copperleaf/ballast/core/DefaultGuardian {
Expand All @@ -433,14 +437,6 @@ public final class com/copperleaf/ballast/core/PrintlnLogger : com/copperleaf/ba
public fun info (Ljava/lang/String;)V
}

public final class com/copperleaf/ballast/internal/BallastInterceptorScopeImpl : com/copperleaf/ballast/BallastInterceptorScope, kotlinx/coroutines/CoroutineScope {
public fun <init> (Lcom/copperleaf/ballast/BallastLogger;Ljava/lang/String;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;)V
public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext;
public fun getHostViewModelName ()Ljava/lang/String;
public fun getLogger ()Lcom/copperleaf/ballast/BallastLogger;
public fun sendToQueue (Lcom/copperleaf/ballast/Queued;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class com/copperleaf/ballast/internal/BallastViewModelImpl : com/copperleaf/ballast/BallastViewModel, com/copperleaf/ballast/BallastViewModelConfiguration, kotlinx/coroutines/channels/SendChannel {
public fun <init> (Lcom/copperleaf/ballast/BallastViewModelConfiguration;Lkotlinx/coroutines/channels/Channel;)V
public synthetic fun <init> (Lcom/copperleaf/ballast/BallastViewModelConfiguration;Lkotlinx/coroutines/channels/Channel;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
Expand Down
6 changes: 3 additions & 3 deletions docs/src/orchid/resources/wiki/feature-comparison.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ See table below for a comparison of features or names in Ballast vs other popula
✅: Fully Officially supported
☑️: Fully supported by 3rd-party

[11]: {{ 'Debugger' | link }}
[12]: {{ 'Testing Utils' | link }}
[13]: {{ 'Repository' | link }}
[11]: {{ 'Ballast Debugger' | link }}
[12]: {{ 'Ballast Testing Utils' | link }}
[13]: {{ 'Ballast Repository' | link }}

[20]: https://github.com/reduxjs/redux
[21]: https://github.com/reduxjs/redux-devtools
Expand Down
4 changes: 2 additions & 2 deletions docs/src/orchid/resources/wiki/mental-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -433,8 +433,8 @@ where we do not need those resources.
But real-world applications aren't always that simple. One use-case is observing a stream of events (a Kotlin `Flow`) of
some data source, rather than a discrete suspending value. For example, rather than the respository directly delivering
the results of an API call, it may cache it, and send multiple emissions to notify of the cache status (see
{{ 'Repository' | anchor }} module). Or you connect to the phone's GPS and receive an endless stream of GPS coordinates
you need to display on a map. We need a new strategy to handle this kind of use-case: a "side effect".
{{ 'Ballast Repository' | anchor }} module). Or you connect to the phone's GPS and receive an endless stream of GPS
coordinates you need to display on a map. We need a new strategy to handle this kind of use-case: a "side effect".

Until this point, we've been working with the notion that the InputHandler will suspend until the async work completes,
and we considered what would happen if a new Input arrived while one was already suspended. But if we have a
Expand Down
63 changes: 63 additions & 0 deletions docs/src/orchid/resources/wiki/modules/ballast-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,69 @@

# Ballast Core

The Ballast Core module provides all the core capabilities of the entire Ballast MVI framework. The Core framework is
robust and opinionated, but also provides many ways to extend the functionality through Interceptors without impacting
the core MVI model. Any additional functionality outside of Core will typically be implemented as an Interceptor and
provided to the `BallastViewModelConfiguration`.

## ViewModels

The Core module provides several ViewModel base classes, so Ballast can integrate natively with a variety of platforms.

- `AndroidViewModel`: A subclass of `androidx.lifecycle.ViewModel`
- `IosViewModel`: A custom ViewModel that is tied to an iOS `ViewController`'s lifecycle
- `BasicViewModel`: A generic ViewModel which can be used in any arbitrary context, including Kotlin targets that don't
have their own platform-specific ViewModel. `BasicViewModel`'s lifecycle is controlled by a `coroutineScope` provided
to it upon creation.

## Interceptors

The Core module comes with only one Interceptor, `LoggingInterceptor`. It will print all Ballast activity to the logger
provided to the `BallastViewModelConfiguration`. Ballast Core also includes two different Logger implementations,
`NoOpLogger`, which is used by default and simply discards all logs, and `PrintlnLogger` which writes all logs to
`println()`. Be sure to only include `LoggingInterceptor()` in debug builds, as logging in production may cause
performance degradation, and risks leaking sensitive info through the logs as there is no log filtering capability
included.

```kotlin
@HiltViewModel
class ExampleViewModel
@Inject
constructor() : AndroidViewModel<
ExampleContract.Inputs,
ExampleContract.Events,
ExampleContract.State>(
config = BallastViewModelConfiguration.Builder()
.apply {
logger = PrintlnLogger()
this += LoggingInterceptor()
}
.forViewModel(
initialState = ExampleContract.State(),
inputHandler = ExampleInputHandler(),
name = "Example",
)
)
```

## Input Strategies

Ballast offers 3 different Input Strategies out-of-the-box, which each adapt Ballast's core functionality for different
applications:

- `LifoInputStrategy`: A last-in-first-out strategy for handling Inputs, and the default strategy if none is provided.
Only 1 Input will be processed at a time, and if a new Input is received while one is still working, the running Input
will be cancelled to immediately accept the new one. Corresponds to `Flow.collectLatest { }`, best for UI ViewModels.
- `FifoInputStrategy`: A first-in-first-out strategy for handling Inputs. Inputs will be processed in the same order
they were sent and only ever one-at-a-time, but instead of cancelling running Inputs, new ones are queued and will be
consumed later when the queue is free. Corresponds to the normal `Flow.collect { }`, best for non-UI ViewModels.
- `ParallelInputStrategy`: For specific edge-cases where neither of the above strategies works. Inputs are all handled
concurrently so you don't have to worry about blocking the queue or having Inputs cancelled. However, it places
additional restrictions on State reads/changes to prevent usage that might lead to race conditions.

Ballast Core also includes `DefaultGuardian`, which you can use if you need to create your own `InputStrategy` to
maintain the same level of safety as the core `InputStrategies`.

## Installation

```kotlin
Expand Down
71 changes: 63 additions & 8 deletions docs/src/orchid/resources/wiki/modules/ballast-debugger.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,66 @@

# Ballast Debugger

Ballast Debugger is a tool for inspecting the status of all components in your Ballast ViewModels in a IntelliJ plugin.
It supports features one would expect from an MVI graphical debugger:

<div id="intellij-plugin-button"></div>

- Inspecting the status and data within all ViewModel features in real-time
- Time-travel debugging
- Resending Inputs for re-processing
- Templates for creating new Ballast components (coming soon)
- Sample panel for getting a feel for the features of Ballast, which connects itself to the Debugger

The Ballast Debugger must first be installed as a plugin in IntelliJ Idea (Community or Ultimate) or Android Studio, and
then add the [`ballast-debugger`](#Installation) dependency to your project.

<div id="intellij-plugin-card"></div>

You will need to create a `BallastDebuggerClientConnection` with your choice of [Ktor client engine][1] and connect it
on an application-wide CoroutineScope. This will start a Websocket connection to the IntelliJ plugin's server over
localhost on port 9684. The connection will automatically retry the connection until it succeeds. Finally, add the
`BallastDebuggerInterceptor` which will send all its data through the websocket and be captured and displayed on the
plugin's UI.

The same connection should be shared among all ViewModels. Also, you definitely do not want to start the connection or
include this interceptor in production builds, so make sure you configure your app to only use it in debug builds (or
better yet, only include the debugger dependency in debug builds).

```kotlin
val debuggerConnection by lazy {
val applicationScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
BallastDebuggerClientConnection(
OkHttp,
applicationScope,
host = "127.0.0.1", // 10.1.1.20 on Android
) {
// OkHttp Ktor client engine configuration
}
.also { it.connect() }
}

@HiltViewModel
class ExampleViewModel
@Inject
constructor() : AndroidViewModel<
ExampleContract.Inputs,
ExampleContract.Events,
ExampleContract.State>(
config = BallastViewModelConfiguration.Builder()
.apply {
if(BuildConfig.DEBUG) {
this += BallastDebuggerInterceptor(debuggerConnection)
}
}
.forViewModel(
initialState = ExampleContract.State(),
inputHandler = ExampleInputHandler(),
name = "Example",
)
)
```

## Installation

```kotlin
Expand All @@ -27,15 +87,10 @@ kotlin {
}
```

<div id="intellij-plugin-card"></div>
<div id="intellij-plugin-button"></div>

<script src="https://plugins.jetbrains.com/assets/scripts/mp-widget.js"></script>
<script>
// Please, replace #yourelement with a real element id on your webpage
MarketplaceWidget.setupMarketplaceWidget('card', 18702, "#intellij-plugin-card");
</script>
<script>
// Please, replace #yourelement with a real element id on your webpage
MarketplaceWidget.setupMarketplaceWidget('install', 18702, "#intellij-plugin-button");
MarketplaceWidget.setupMarketplaceWidget('card', 18702, "#intellij-plugin-card");
</script>

[1]: https://ktor.io/docs/http-client-engines.html
Loading

0 comments on commit 3d7d730

Please sign in to comment.