-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added the Database Protocol with Drift and RxDB implementations (#30)
- Loading branch information
Showing
50 changed files
with
52,005 additions
and
167 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
# Databases | ||
|
||
Global state in re-dash is stored in some database. | ||
|
||
Usually this database is in-memory only and represented as some data structure, like a map - this is the default behavior. | ||
|
||
Other times we might require more capability like persistence, relational queries, replication etc. and want to opt for using a full featured database engine on the client instead, while keeping with the core concepts of the [data loop](https://day8.github.io/re-frame/a-loop/) - events, subscriptions etc. | ||
|
||
This is achieved by implementing the `Database` protocol. Out the box, re-dash ships with support for 3 database implementations of this protocol: | ||
|
||
- AppDB (the default path based Map Atom) | ||
- [Drift](https://drift.simonbinder.eu/) (a relational persistence library over sqlite3) | ||
- [RxDB](https://rxdb.info/articles/flutter-database.html) (a popular reactive NoSQL database for JavaScript, usable from Flutter) | ||
|
||
> Multiple databases may be registered and used in the same app simultaneously. Note however, that subscription signals of different database implementations cannot be used with a layer 3 subscription. | ||
## AppDB | ||
|
||
The default path based Map Atom. | ||
|
||
This `Database` is registered by default, always available to query via subscriptions or update via events using the built-in `:db` effect. | ||
|
||
## Drift | ||
|
||
[Drift](https://drift.simonbinder.eu/) is the relational persistence library for your Dart and Flutter apps - it is an abstraction over sqlite3. | ||
|
||
### Schema | ||
|
||
The simplest way (currently) is to maintain the Drift schema in raw `.dart` files in the `/.lib` folder directly and have the code generator watch it with `dart run build_runner watch`. | ||
|
||
For more info see the `samples/drift/lib/database.dart` sample and the [drift documentation](https://drift.simonbinder.eu/docs/getting-started/#database-class). | ||
|
||
### Database registration | ||
|
||
Register it early on in the app startup (like in `main`) | ||
|
||
```clojure | ||
(await | ||
(rd/reg-database | ||
:drift ;; <== Choose any `database-id` | ||
{:impl (drift/Drift.) ;; <== The `Database` protocol implementation | ||
:instance (database/AppDatabase.)})) ;; <== The database instance | ||
``` | ||
|
||
### Subscription | ||
|
||
Use the chosen database-id in `reg-sub` that should target Drift for example | ||
|
||
```clojure | ||
(rd/reg-sub | ||
::app-state | ||
:drift ;; <== The registered database-id | ||
(fn [^db/AppDatabase db _] | ||
(.select db (.-appState db)))) ;; <== Query that returns a subsribable (stream) | ||
|
||
(rd/reg-sub | ||
::width | ||
:drift | ||
(fn [_] | ||
(rd/subscribe [::app-state])) ;; <== Layer 3 signal(s) supported | ||
(fn [[^db/AppStateData data] _] | ||
(.-width data))) | ||
``` | ||
|
||
### Events | ||
|
||
Use the chosen database-id as the effect-id in events. | ||
|
||
Note that the event itself does not mutate the database directly, but instead returns a function being passed the database instance. This is keeping with re-dash (& re-frame) methodology of writing pure event handlers - the mutation is applied later when re-dash execute effects. | ||
|
||
There is no need to register the `:drift` (database-id) effect as this is automatically done for you. | ||
|
||
```clojure | ||
(rd/reg-event-fx | ||
::increment-width | ||
(fn [_ [_ width]] | ||
{:drift #(let [db ^db/AppDatabase %] | ||
(.write (.update db (.-appState db)) | ||
(db/AppStateCompanion | ||
.width (-> width inc int d/Value))))})) | ||
``` | ||
|
||
### Sample app | ||
|
||
There's a working sample app in the `samples/drift` folder using the Drift database as app state | ||
|
||
|
||
## RxDB | ||
|
||
A popular reactive NoSQL database for JavaScript, [somewhat usable from Flutter](https://rxdb.info/articles/flutter-database.html). It has various storage layer plugins including an in-memory only option. | ||
|
||
### Schema | ||
|
||
Maintain the schema in raw JavaScript, and compile it with node. | ||
|
||
For more info see the `samples/rxdb/javascript/src/index.js` sample and the [rxdb documentation](https://github.com/pubkey/rxdb/tree/master/examples/flutter#in-javascript) | ||
|
||
### Database registration | ||
|
||
Register it early on in the app startup (like in `main`) | ||
|
||
```clojure | ||
(await | ||
(rd/reg-database | ||
:rxdb ;; <== Choose any `database-id` | ||
{:impl (rx-db/RxDB.) ;; <== The `Database` protocol implementation | ||
:instance (database/getRxDatabase "javascript/dist/index.js" ;; <== The database instance | ||
"AppDatabase")})) | ||
``` | ||
|
||
### Subscription | ||
|
||
Use the chosen database-id in `reg-sub` that should target RxDB for example | ||
|
||
```clojure | ||
(rd/reg-sub | ||
::app-state | ||
:rxdb ;; <== The registered database-id | ||
(fn [db [_ selector]] | ||
(-> (.getCollection db coll-name) | ||
(.find selector)))) ;; <== Query that returns a subsribable (stream) | ||
|
||
(rd/reg-sub | ||
::width | ||
:rxdb | ||
(fn [_] | ||
(rd/subscribe [::app-state])) ;; <== Layer 3 signal(s) supported | ||
(fn [[doc] _] | ||
(get (->> (.-data ^rxdb/RxDocument doc) | ||
(into {})) | ||
"width"))) | ||
``` | ||
|
||
### Events | ||
|
||
Use the chosen database-id as the effect-id in events. | ||
|
||
Note that the event itself does not mutate the database directly, but instead returns a function being passed the database instance. This is keeping with re-dash (& re-frame) methodology of writing pure event handlers - the mutation is applied later when re-dash execute effects. | ||
|
||
There is no need to register the `:rxdb` (database-id) effect as this is automatically done for you. | ||
|
||
```clojure | ||
(rd/reg-event-fx | ||
::increment-width | ||
(fn [_ _] | ||
{:rxdb #(.insert (.getCollection % coll-name) | ||
{"id" (str (DateTime/now)) | ||
"width" ((fnil f 0) val)})})) | ||
``` | ||
|
||
### Status | ||
|
||
RxDB for Flutter is experimental, and many features are missing. | ||
|
||
Some of the known limitations include: | ||
|
||
- Documents can only be inserted, queried and observed. [Update is not yet supported](https://pub.dev/documentation/rxdb/latest/rxdb/RxCollection-class.html) | ||
- Only collection queries can be observed (subscribed) not documents, or individual fields in a document. | ||
- RxDB for Flutter is not supported in the web platform. | ||
|
||
|
||
### Sample app | ||
|
||
There's a working sample app in the `samples/rxdb` folder using the RxDB database as app state | ||
|
||
## Custom implementation | ||
|
||
Adding support for another database is a matter of implementing the `Database` protocol and registering it early on in the app startup, like in main: | ||
|
||
```clojure | ||
(await | ||
(rd/reg-database | ||
:my-database-id ;; <== Choose any `database-id` | ||
{:impl (my/DatabaseImpl.) ;; <== Instantiate the `Database` protocol implementation | ||
:instance (some/MyDatabase.)})) ;; <== Instantiate the database instance | ||
``` | ||
|
||
To support subscriptions, the database engine must support some way of watching / observing queries. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# Miscellaneous | ||
*.class | ||
*.log | ||
*.pyc | ||
*.swp | ||
.DS_Store | ||
.atom/ | ||
.buildlog/ | ||
.history | ||
.svn/ | ||
migrate_working_dir/ | ||
|
||
# IntelliJ related | ||
*.iml | ||
*.ipr | ||
*.iws | ||
.idea/ | ||
|
||
# The .vscode folder contains launch configuration and tasks you configure in | ||
# VS Code which you may wish to be included in version control, so this line | ||
# is commented out by default. | ||
#.vscode/ | ||
|
||
# Flutter/Dart/Pub related | ||
**/doc/api/ | ||
**/ios/Flutter/.last_build_id | ||
.dart_tool/ | ||
.flutter-plugins | ||
.flutter-plugins-dependencies | ||
.packages | ||
.pub-cache/ | ||
.pub/ | ||
/build/ | ||
|
||
# Symbolication related | ||
app.*.symbols | ||
|
||
# Obfuscation related | ||
app.*.map.json | ||
|
||
# Android Studio will place build artifacts here | ||
/android/app/debug | ||
/android/app/profile | ||
/android/app/release |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# This file tracks properties of this Flutter project. | ||
# Used by Flutter tool to assess capabilities and perform upgrades etc. | ||
# | ||
# This file should be version controlled and should not be manually edited. | ||
|
||
version: | ||
revision: "d211f42860350d914a5ad8102f9ec32764dc6d06" | ||
channel: "stable" | ||
|
||
project_type: app | ||
|
||
# Tracks metadata for the flutter migrate command | ||
migration: | ||
platforms: | ||
- platform: root | ||
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 | ||
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 | ||
- platform: android | ||
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 | ||
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 | ||
- platform: ios | ||
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 | ||
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 | ||
- platform: linux | ||
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 | ||
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 | ||
- platform: macos | ||
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 | ||
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 | ||
- platform: web | ||
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 | ||
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 | ||
- platform: windows | ||
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 | ||
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 | ||
|
||
# User provided section | ||
|
||
# List of Local paths (relative to this file) that should be | ||
# ignored by the migrate tool. | ||
# | ||
# Files that are not part of the templates will be ignored by default. | ||
unmanaged_files: | ||
- 'lib/main.dart' | ||
- 'ios/Runner.xcodeproj/project.pbxproj' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# flow_drift | ||
|
||
> This sample tries to demonstrate the Flow sample using Drift as the back-end for app state as opposed to a Clojure map. | ||
Shows an example of using Flows to calculate a derived result of some calculation, in addition to Flow life-cycle controls. | ||
|
||
## Run the sample | ||
|
||
### Create the platform folders | ||
|
||
```bash | ||
flutter create . | ||
``` | ||
|
||
### Run it | ||
|
||
```bash | ||
clj -M:cljd flutter | ||
``` | ||
|
||
## Updating the Drift database schema | ||
|
||
> Ignore this section if you only intend to run this sample as is. Read on if you made some changes that require a scheme update. | ||
Drift requires a schema to be persisted prior to any rows being inserted, see [Database class](https://drift.simonbinder.eu/docs/getting-started/#database-class) | ||
|
||
This sample includes a pre-built schema with source in `lib/database.dart`. | ||
|
||
Any changes to the schema will need to be recompiled: | ||
|
||
|
||
```bash | ||
|
||
# Either once off | ||
|
||
dart run build_runner build | ||
|
||
# Or watched for changes | ||
|
||
dart run build_runner watch | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# This file configures the analyzer, which statically analyzes Dart code to | ||
# check for errors, warnings, and lints. | ||
# | ||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled | ||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be | ||
# invoked from the command line by running `flutter analyze`. | ||
|
||
# The following line activates a set of recommended lints for Flutter apps, | ||
# packages, and plugins designed to encourage good coding practices. | ||
include: package:flutter_lints/flutter.yaml | ||
|
||
linter: | ||
# The lint rules applied to this project can be customized in the | ||
# section below to disable rules from the `package:flutter_lints/flutter.yaml` | ||
# included above or to enable additional rules. A list of all available lints | ||
# and their documentation is published at https://dart.dev/lints. | ||
# | ||
# Instead of disabling a lint rule for the entire project in the | ||
# section below, it can also be suppressed for a single line of code | ||
# or a specific dart file by using the `// ignore: name_of_lint` and | ||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file | ||
# producing the lint. | ||
rules: | ||
# avoid_print: false # Uncomment to disable the `avoid_print` rule | ||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule | ||
|
||
# Additional information about this file can be found at | ||
# https://dart.dev/guides/language/analysis-options |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{:paths ["script"] | ||
:deps {} | ||
:tasks | ||
{compile {:doc "Compile app" | ||
:task (shell "clojure -M:cljd compile")} | ||
|
||
build {:doc "Builds the deployable assets" | ||
:task (shell "flutter build web --base-href=/re-dash/drift/build/web/") | ||
:depends [compile]}}} |
Oops, something went wrong.