Skip to content

Commit

Permalink
Rename Library (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
mirland authored Aug 29, 2022
1 parent 16c34b9 commit 592d72e
Show file tree
Hide file tree
Showing 37 changed files with 444 additions and 410 deletions.
52 changes: 26 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ It is inspired by the [Store] Kotlin library.
Its main goal is to prevent excessive calls to the network and disk cache.
By utilizing it, you eliminate the possibility of flooding your network with the same request while, at the same time, adding layers of caching.

Although you can use it without a local source, the greatest benefit comes from combining Stock with a local database such as [Floor], [Drift], [sqflite], [Realm], etc.
Although you can use it without a local source, the greatest benefit comes from combining Stock with a local database such as [Floor], [Drift], [Sqflite], [Realm], etc.

## Features

Expand Down Expand Up @@ -73,7 +73,7 @@ _Note: to the proper operation, when `write` is invoked with new data, the sourc
`Stock` lets you combine the different data sources and get the data.

```dart
final store = Store<String, List<Tweet>>(
final stock = Stock<String, List<Tweet>>(
fetcher: fetcher,
sourceOfTruth: sourceOfTruth,
);
Expand All @@ -85,25 +85,25 @@ You can generate a data `Stream` using `stream()`.

You need to invoke it with a specific `key`, and an optional `refresh` value that tells Stock if a refresh is optional or mandatory.

That returns a data stream of `StoreResponse`, which has 3 possible values:
- `StoreResponseLoading` informs that a network request is in progress. It can be useful to display a loading indicator in your UI.
- `StoreResponseData` holds the response data. It has a `value` field which includes an instance of the type returned by `Stock`.
- `StoreResponseError` indicates that an error happened.
That returns a data stream of `StockResponse`, which has 3 possible values:
- `StockResponseLoading` informs that a network request is in progress. It can be useful to display a loading indicator in your UI.
- `StockResponseData` holds the response data. It has a `value` field which includes an instance of the type returned by `Stock`.
- `StockResponseError` indicates that an error happened.
When an error happens, `Stock` does not throw an exception, instead, it wraps it in this class.
It includes an `error` field that contains the exception thrown by the given `origin`.

Each `StoreResponse` includes an `origin` field which specifies where the event is coming from.
Each `StockResponse` includes an `origin` field which specifies where the event is coming from.

```dart
store
stock
.stream('key', refresh: true)
.listen((StoreResponse<List<Tweet>> storeResponse) {
if (storeResponse is StoreResponseLoading) {
.listen((StockResponse<List<Tweet>> stockResponse) {
if (stockResponse is StockResponseLoading) {
_displayLoadingIndicator();
} else if (storeResponse is StoreResponseData) {
_displayTweetsInUI((storeResponse is StoreResponseData).data);
} else if (stockResponse is StockResponseData) {
_displayTweetsInUI((stockResponse is StockResponseData).data);
} else {
_displayErrorInUi((storeResponse as StoreResponseError).error);
_displayErrorInUi((stockResponse as StockResponseError).error);
}
});
```
Expand All @@ -117,21 +117,21 @@ Stock provides a couple of methods to get data without using a data stream.

```dart
// Get fresh data
final List<Tweet> freshTweets = await store.fresh(key);
final List<Tweet> freshTweets = await stock.fresh(key);
// Get the previous cached data
final List<Tweet> cachedTweets = await store.get(key);
final List<Tweet> cachedTweets = await stock.get(key);
```


### Use different types for `Fetcher` and `SourceOfTruth`

Sometimes you need to use different entities for Network and DB. For that case `Stock` provides the `StoreTypeMapper`, a class that transforms one entity into the other.
Sometimes you need to use different entities for Network and DB. For that case `Stock` provides the `StockTypeMapper`, a class that transforms one entity into the other.

`StoreTypeMapper` is used in the `SourceOfTruth` via the method `mapToUsingMapper`
`StockTypeMapper` is used in the `SourceOfTruth` via the method `mapToUsingMapper`

```dart
class TweetMapper implements StoreTypeMapper<DbTweet, NetworkTweet> {
class TweetMapper implements StockTypeMapper<DbTweet, NetworkTweet> {
@override
NetworkTweet fromInput(DbTweet value) => NetworkTweet(value);
Expand Down Expand Up @@ -197,7 +197,7 @@ final SharedPreferencesSourceOfTruth<String, User> = SharedPreferencesSourceOfTr

For bugs please use [GitHub Issues](https://github.com/xmartlabs/stock/issues). For questions, ideas, and discussions use [GitHub Discussions](https://github.com/xmartlabs/stock/discussions).

Made with ❤️ by [Xmartlabs](http://xmartlabs.com).
Made with ❤️ by [Xmartlabs](https://xmartlabs.com).

## License

Expand All @@ -215,13 +215,13 @@ Made with ❤️ by [Xmartlabs](http://xmartlabs.com).
See the License for the specific language governing permissions and
limitations under the License.

[Store]: https://github.com/MobileNativeFoundation/Store
[Floor]: https://pub.dev/packages/floor
[Drift]: https://pub.dev/packages/drift
[sqflite]: https://pub.dev/packages/sqflite
[Floor]: https://pub.dev/packages/floor
[Realm]: https://pub.dev/packages/realm
[`Stock`]: lib/store.dart
[`Fetcher`]: lib/fetcher.dart
[`SourceOfTruth`]: lib/source_of_truth.dart
[`CachedSourceOfTruth`]: lib/source_of_truth.dart
[Store]: https://github.com/MobileNativeFoundation/Store
[`CachedSourceOfTruth`]: lib/src/source_of_truth.dart
[`Fetcher`]: lib/src/fetcher.dart
[`SourceOfTruth`]: lib/src/source_of_truth.dart
[`Stock`]: lib/src/stock.dart
[shared_preferences]: https://pub.dev/packages/shared_preferences
[Sqflite]: https://pub.dev/packages/sqflite
26 changes: 11 additions & 15 deletions example/main.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import 'package:stock/fetcher.dart';
import 'package:stock/source_of_truth.dart';
import 'package:stock/src/extensions/store_response_extensions.dart';
import 'package:stock/store.dart';
import 'package:stock/store_response.dart';
import 'package:stock/stock.dart';

late TwitterApi _api;
late TweetsLocalDatabase _database;
Expand All @@ -19,32 +15,32 @@ void main() async {
writer: (userId, tweets) => _database.writeUserTweets(userId, tweets),
);

// Create Store
final store = Store<String, List<Tweet>>(
// Create Stock
final stock = Stock<String, List<Tweet>>(
fetcher: fetcher,
sourceOfTruth: sourceOfTruth,
);

// Create a stream to listen tweet changes of the user `xmartlabs`
store
stock
.stream('xmartlabs', refresh: true)
.listen((StoreResponse<List<Tweet>> storeResponse) {
if (storeResponse is StoreResponseLoading) {
.listen((StockResponse<List<Tweet>> stockResponse) {
if (stockResponse is StockResponseLoading) {
_displayLoadingIndicator();
} else if (storeResponse is StoreResponseData) {
_displayTweetsInUI(storeResponse.data);
} else if (stockResponse is StockResponseData) {
_displayTweetsInUI(stockResponse.requireData());
} else {
_displayErrorInUi((storeResponse as StoreResponseError).error);
_displayErrorInUi((stockResponse as StockResponseError).error);
}
});

// Get Xmartlabs tweets from the network and save them in the DB.
final List<Tweet> freshTweets = await store.fresh('xmartlabs');
final List<Tweet> freshTweets = await stock.fresh('xmartlabs');

// Get Xmartlabs tweets
// It will try to use Local Database Tweets.
// However, if the DB tweets are empty, it will fetch new tweets from the fetcher, and save them in the DB.
final List<Tweet> cachedTweets = await store.get('xmartlabs');
final List<Tweet> cachedTweets = await stock.get('xmartlabs');
}

void _displayTweetsInUI(List<Tweet>? tweets) {}
Expand Down
41 changes: 0 additions & 41 deletions lib/fetcher.dart

This file was deleted.

File renamed without changes.
File renamed without changes.
20 changes: 0 additions & 20 deletions lib/src/extensions/future_stream_extensions.dart

This file was deleted.

20 changes: 20 additions & 0 deletions lib/src/extensions/future_stream_internal_extensions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'package:rxdart/rxdart.dart';
import 'package:stock/src/stock_response.dart';

extension StreamExtensions<T> on Stream<T> {
Stream<StockResponse<T>> mapToResponse(ResponseOrigin origin) =>
map((data) => StockResponse.data(origin, data))
.onErrorReturnWith((error, stacktrace) {
return StockResponse.error(origin, error, stacktrace);
});
}

extension FutureExtensions<T> on Future<T> {
Future<StockResponse<T>> mapToResponse(ResponseOrigin origin) async {
try {
return StockResponse.data(origin, await this);
} catch (error, stacktrace) {
return StockResponse.error(origin, error, stacktrace);
}
}
}
29 changes: 29 additions & 0 deletions lib/src/extensions/stock_response_internal_extensions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import 'package:stock/src/errors.dart';
import 'package:stock/src/stock_response.dart';
import 'package:stock/src/stock_response_extensions.dart';

extension StockResponseExtensions<T> on StockResponse<T> {
StockResponse<R> swapType<R>() {
if (this is StockResponseData<T>) {
return StockResponse.data(origin, requireData() as R);
} else if (isError) {
var errorResponse = this as StockResponseError<T>;
return StockResponse.error(
origin,
errorResponse.error,
errorResponse.stackTrace,
);
} else if (isLoading) {
return StockResponse.loading(origin);
} else {
throw StockError('Unknown type');
}
}
}

extension StockResponseStreamExtensions<T> on Stream<StockResponse<T?>> {
Stream<StockResponse<T>> whereDataNotNull() => where(
(event) =>
event is StockResponseData<T?> ? event.requireData() != null : true,
).map((event) => event.swapType<T>());
}
48 changes: 0 additions & 48 deletions lib/src/extensions/store_response_extensions.dart

This file was deleted.

41 changes: 41 additions & 0 deletions lib/src/fetcher.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import 'package:stock/src/implementations/factory_fetcher.dart';
import 'package:stock/src/stock.dart';
import 'package:stock/src/stock_response.dart';

/// Fetcher is used by [Stock] to fetch network records of the type [T]
/// for a given key of the type [Key]. The return type is [Stream] to
/// allow for multiple results per request.
///
/// Note: [Stock] does not catch exceptions thrown by a [Fetcher].
/// Use [StockResponseError] to communicate expected errors.
///
/// See [ofFuture] for easily translating from a regular `Future` function.
/// See [ofStream], for easily translating to [StockResponse] (and
/// automatically transforming exceptions into [StockResponseError].
abstract class Fetcher<Key, T> {
Fetcher._();

/// "Creates" a [Fetcher] from a [futureFactory] and translates the results into a [StockResponse].
///
/// Emitted values will be wrapped in [StockResponseData]. If an exception disrupts the stream then
/// it will be wrapped in [StockResponseError]
///
/// Use when creating a [Stock] that fetches objects in a single response per request
/// network protocol (e.g Http).
static Fetcher<Key, Output> ofFuture<Key, Output>(
Future<Output> Function(Key key) futureFactory,
) =>
FutureFetcher(futureFactory);

/// "Creates" a [Fetcher] from a [streamFactory] and translates the results into a [StockResponse].
///
/// Emitted values will be wrapped in [StockResponseData]. If an exception disrupts the flow then
/// it will be wrapped in [StockResponseError].
///
/// Use when creating a [Stock] that fetches objects in a multiple responses per request
/// network protocol (e.g Web Sockets).
static Fetcher<Key, Output> ofStream<Key, Output>(
Stream<Output> Function(Key key) streamFactory,
) =>
StreamFetcher(streamFactory);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:stock/fetcher.dart';
import 'package:stock/src/fetcher.dart';

class FactoryFetcher<Key, Output> implements Fetcher<Key, Output> {
Stream<Output> Function(Key key) factory;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:stock/source_of_truth.dart';
import 'package:stock/src/source_of_truth.dart';

class SourceOfTruthImpl<Key, T> implements SourceOfTruth<Key, T> {
final Stream<T?> Function(Key key) _reader;
Expand Down
Loading

0 comments on commit 592d72e

Please sign in to comment.