1.0.0
This commit is contained in:
parent
4fdae1ace7
commit
bde453963b
7 changed files with 227 additions and 1 deletions
44
.gitignore
vendored
44
.gitignore
vendored
|
@ -10,3 +10,47 @@ pubspec.lock
|
|||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.xml
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
|
17
.idea/file_service.iml
Normal file
17
.idea/file_service.iml
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||
<orderEntry type="library" name="Dart Packages" level="project" />
|
||||
</component>
|
||||
</module>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/file_service.iml" filepath="$PROJECT_DIR$/.idea/file_service.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
23
README.md
23
README.md
|
@ -1,2 +1,23 @@
|
|||
# file_service
|
||||
Angel service that persists data to a file on disk.
|
||||
Angel service that persists data to a file on disk, stored as a JSON list. It uses a simple
|
||||
mutex to prevent race conditions, and caches contents in memory until changes
|
||||
are made.
|
||||
|
||||
The file will be created on read/write, if it does not already exist.
|
||||
|
||||
This package is useful in development, as it prevents you from having to install
|
||||
an external database to run your server.
|
||||
|
||||
When running a multi-threaded server, there is no guarantee that file operations
|
||||
will be mutually excluded. Thus, try to only use this one a single-threaded server
|
||||
if possible, or one with very low load.
|
||||
|
||||
While not necessarily *slow*, this package makes no promises about performance.
|
||||
|
||||
# Usage
|
||||
```dart
|
||||
configureServer(Angel app) async {
|
||||
// Just like a normal service
|
||||
app.use('/api/todos', new JsonFileService(new File('todos_db.json')));
|
||||
}
|
||||
```
|
120
lib/file_service.dart
Normal file
120
lib/file_service.dart
Normal file
|
@ -0,0 +1,120 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:pool/pool.dart';
|
||||
|
||||
/// Persists in-memory changes to a file on disk.
|
||||
class JsonFileService extends Service {
|
||||
FileStat _lastStat;
|
||||
final Pool _mutex = new Pool(1);
|
||||
MapService _store;
|
||||
final File file;
|
||||
|
||||
JsonFileService(this.file,
|
||||
{bool allowRemoveAll: false, bool allowQuery: true}) {
|
||||
_store = new MapService(
|
||||
allowRemoveAll: allowRemoveAll == true,
|
||||
allowQuery: allowQuery != false);
|
||||
}
|
||||
|
||||
_load() async {
|
||||
if (!await file.exists()) await file.writeAsString(JSON.encode([]));
|
||||
var stat = await file.stat();
|
||||
//
|
||||
|
||||
if (_lastStat == null ||
|
||||
stat.modified.millisecondsSinceEpoch >
|
||||
_lastStat.modified.millisecondsSinceEpoch) {
|
||||
_lastStat = stat;
|
||||
|
||||
var contents = await file.readAsString();
|
||||
|
||||
try {
|
||||
var list = JSON.decode(contents) as List;
|
||||
_store.items.clear(); // Clear exist in-memory copy
|
||||
_store.items.addAll(list.map(_revive)); // Insert all new entries
|
||||
} catch (e) {
|
||||
print('WARNING: Failed to reload contents of ${file.path}.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_save() async {
|
||||
var r = await _mutex.request();
|
||||
await file.writeAsString(JSON.encode(_store.items.map(_jsonify).toList()));
|
||||
r.release();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List> index([Map params]) async =>
|
||||
_load().then((_) => _store.index(params));
|
||||
|
||||
@override
|
||||
Future<Map> read(id, [Map params]) =>
|
||||
_load().then((_) => _store.read(id, params));
|
||||
|
||||
@override
|
||||
Future<Map> create(data, [Map params]) async {
|
||||
await _load();
|
||||
_store.items.add(data);
|
||||
await _save();
|
||||
return data;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map> remove(id, [Map params]) async {
|
||||
await _load();
|
||||
var r = await _store.remove(id, params);
|
||||
await _save();
|
||||
return r;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map> update(id, data, [Map params]) async {
|
||||
await _load();
|
||||
var r = await _store.update(id, data, params);
|
||||
await _save();
|
||||
return r;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map> modify(id, data, [Map params]) async {
|
||||
await _load();
|
||||
var r = await _store.update(id, data, params);
|
||||
await _save();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
_safeForJson(x) {
|
||||
if (x is DateTime)
|
||||
return x.toIso8601String();
|
||||
else if (x is Map)
|
||||
return _jsonify(x);
|
||||
else if (x is num || x is String || x is bool || x == null)
|
||||
return x;
|
||||
else if (x is Iterable)
|
||||
return x.map(_safeForJson).toList();
|
||||
else
|
||||
return x.toString();
|
||||
}
|
||||
|
||||
Map _jsonify(Map map) {
|
||||
return map.keys.fold<Map>({}, (out, k) => out..[k] = _safeForJson(map[k]));
|
||||
}
|
||||
|
||||
_revive(x) {
|
||||
if (x is Map) {
|
||||
return x.keys.fold<Map>({}, (out, k) => out..[k] = _revive(x[k]));
|
||||
} else if (x is Iterable)
|
||||
return x.map(_revive).toList();
|
||||
else if (x is String) {
|
||||
try {
|
||||
return DateTime.parse(x);
|
||||
} catch (e) {
|
||||
return x;
|
||||
}
|
||||
} else
|
||||
return x;
|
||||
}
|
10
pubspec.yaml
Normal file
10
pubspec.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
name: angel_file_service
|
||||
version: 1.0.0
|
||||
description: Angel service that persists data to a file on disk.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/file_service
|
||||
environment:
|
||||
sdk: ">=1.19.0"
|
||||
dependencies:
|
||||
angel_framework: ^1.0.0-dev
|
||||
pool: ^1.0.0
|
Loading…
Reference in a new issue