Removed deprecated packages

This commit is contained in:
thomashii 2021-12-22 07:51:13 +08:00
parent 70e6f1b6c1
commit 9b000c768e
147 changed files with 3 additions and 7167 deletions

View file

@ -1,64 +0,0 @@
# Created by .ignore support plugin (hsz.mobi)
### 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
# CMake
cmake-build-debug/
# 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
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### Dart template
# See https://www.dartlang.org/tools/private-files.html
# Files and directories created by pub
.packages
.pub/
build/
# If you're building an application, you may want to check-in your pubspec.lock
pubspec.lock
# Directory created by dartdoc
# If you don't generate documentation locally you can remove this line.
doc/api/

View file

@ -1,4 +0,0 @@
language: dart
dart:
- dev
- stable

View file

@ -1,12 +0,0 @@
Primary Authors
===============
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
Thomas is the current maintainer of the code base. He has refactored and migrated the
code base to support NNBD.
* __[Tobe O](thosakwe@gmail.com)__
Tobe has written much of the original code prior to NNBD migration. He has moved on and
is no longer involved with the project.

View file

@ -1,30 +0,0 @@
# Change Log
## 2.1.2
* Final release. Replaced by `belatuk_body_parser` package.
## 2.1.1
* Fixed calling deprecated methods in unit test
## 2.1.0
* Replaced `http_server` with `belatuk_http_server`
## 2.0.1
* Fixed source code formating warning
* Updated README
## 2.0.0
* Migrated to support Dart SDK 2.12.x NNBD
## 1.1.1
* Dart 2 updates; should fix Angel in Travis.
## 1.1.0
* Add `parseBodyFromStream`

View file

@ -1,21 +0,0 @@
MIT License (MIT)
Copyright (c) 2021 dukefirehawk.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,77 +0,0 @@
# Angel3 Body Parser
[![version](https://img.shields.io/badge/pub-v2.1.2-brightgreen)](https://pub.dartlang.org/packages/angel3_body_parser)
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/body_parser/LICENSE)
**DEPRECATED: Replaced by [`belatuk_body_parser`](https://pub.dartlang.org/packages/belatuk_body_parser) package**
Parse request bodies and query strings in Dart, as well multipart/form-data uploads. No external dependencies required.
This is the request body parser powering the [Angel3 framework](https://github.com/dukefirehawk/angel). If you are looking for a server-side solution with dependency injection, WebSockets, and more, then I highly recommend it as your first choice. Bam!
## Contents
- [Angel3 Body Parser](#angel3-body-parser)
- [Contents](#contents)
- [About](#about)
- [Installation](#installation)
- [Usage](#usage)
- [Custom Body Parsing](#custom-body-parsing)
### About
I needed something like Express.js's `body-parser` module, so I made it here. It fully supports JSON requests. x-www-form-urlencoded fully supported, as well as query strings. You can also include arrays in your query, in the same way you would for a PHP application. Full file upload support will also be present by the production 1.0.0 release.
A benefit of this is that primitive types are automatically deserialized correctly. As in, if you have a `hello=1.5` request, then `body['hello']` will equal `1.5` and not `'1.5'`. A very semantic difference, yes, but it relieves stress in my head.
### Installation
To install Body Parser for your Dart project, simply add body_parser to your pub dependencies.
dependencies:
angel3_body_parser: ^2.1.0
### Usage
Body Parser exposes a simple class called `BodyParseResult`. You can easily parse the query string and request body for a request by calling `Future<BodyParseResult> parseBody`.
```dart
import 'dart:convert';
import 'package:angel3_body_parser/angel3_body_parser.dart';
main() async {
// ...
await for (HttpRequest request in server) {
request.response.write(JSON.encode(await parseBody(request).body));
await request.response.close();
}
}
```
You can also use `buildMapFromUri(Map, String)` to populate a map from a URL encoded string.
This can easily be used with a library like [Angel3 JSON God](https://pub.dev/packages/angel3_json_god) to build structured JSON/REST APIs. Add validation and you've got an instant backend.
```dart
MyClass create(HttpRequest request) async {
return god.deserialize(await parseBody(request).body, MyClass);
}
```
### Custom Body Parsing
In cases where you need to parse unrecognized content types, `body_parser` won't be of any help to you on its own. However, you can use the `originalBuffer` property of a `BodyParseResult` to see the original request buffer. To get this functionality, pass `storeOriginalBuffer` as `true` when calling `parseBody`.
For example, if you wanted to [parse GraphQL queries within your server](https://github.com/dukefirehawk/graphql_dart)...
```dart
app.get('/graphql', (req, res) async {
if (req.headers.contentType.mimeType == 'application/graphql') {
var graphQlString = String.fromCharCodes(req.originalBuffer);
// ...
}
});
```

View file

@ -1,4 +0,0 @@
include: package:pedantic/analysis_options.yaml
analyzer:
strong-mode:
implicit-casts: false

View file

@ -1,61 +0,0 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'package:http_parser/http_parser.dart';
import 'package:angel3_body_parser/angel3_body_parser.dart';
void main() async {
var address = '127.0.0.1';
var port = 3000;
var futures = <Future>[];
for (var i = 1; i < Platform.numberOfProcessors; i++) {
futures.add(Isolate.spawn(start, [address, port, i]));
}
await Future.wait(futures).then((_) {
print('All instances started.');
print(
'Test with "wrk -t12 -c400 -d30s -s ./example/post.lua http://localhost:3000" or similar');
start([address, port, 0]);
});
}
void start(List args) {
var address = InternetAddress(args[0] as String);
var port = 8080;
if (args[1] is int) {
args[1];
}
var id = 0;
if (args[2] is int) {
args[2];
}
HttpServer.bind(address, port, shared: true).then((server) {
server.listen((request) async {
// ignore: deprecated_member_use
var body = await defaultParseBody(request);
request.response
..headers.contentType = ContentType('application', 'json')
..write(json.encode(body.body));
await request.response.close();
});
print(
'Server #$id listening at http://${server.address.address}:${server.port}');
});
}
Future<BodyParseResult> defaultParseBody(HttpRequest request,
{bool storeOriginalBuffer = false}) {
return parseBodyFromStream(
request,
request.headers.contentType != null
? MediaType.parse(request.headers.contentType.toString())
: null,
request.uri,
storeOriginalBuffer: storeOriginalBuffer);
}

View file

@ -1,6 +0,0 @@
-- example HTTP POST script which demonstrates setting the
-- HTTP method, body, and adding a header
wrk.method = "POST"
wrk.body = "foo=bar&baz=quux"
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"

View file

@ -1,6 +0,0 @@
/// A library for parsing HTTP request bodies and queries.
library angel3_body_parser;
export 'src/body_parse_result.dart';
export 'src/file_upload_info.dart';
export 'src/parse_body.dart';

View file

@ -1,28 +0,0 @@
import 'file_upload_info.dart';
/// A representation of data from an incoming request.
abstract class BodyParseResult {
/// The parsed body.
Map<String?, dynamic> get body;
/// The parsed query string.
Map<String?, dynamic> get query;
/// All files uploaded within this request.
List<FileUploadInfo> get files;
/// The original body bytes sent with this request.
///
/// You must set [storeOriginalBuffer] to `true` to see this.
List<int>? get originalBuffer;
/// If an error was encountered while parsing the body, it will appear here.
///
/// Otherwise, this is `null`.
dynamic get error;
/// If an error was encountered while parsing the body, the call stack will appear here.
///
/// Otherwise, this is `null`.
StackTrace? get stack;
}

View file

@ -1,7 +0,0 @@
import 'file_upload_info.dart';
List<FileUploadInfo> getFileDataFromChunk(
String chunk, String boundary, String fileUploadName, Map body) {
var result = <FileUploadInfo>[];
return result;
}

View file

@ -1,17 +0,0 @@
/// Represents a file uploaded to the server.
class FileUploadInfo {
/// The MIME type of the uploaded file.
String? mimeType;
/// The name of the file field from the request.
String? name;
/// The filename of the file.
String? filename;
/// The bytes that make up this file.
List<int> data;
FileUploadInfo(
{this.mimeType, this.name, this.filename, this.data = const []});
}

View file

@ -1,22 +0,0 @@
import 'dart:convert';
dynamic getValue(String value) {
try {
var numValue = num.parse(value);
if (!numValue.isNaN) {
return numValue;
} else {
return value;
}
} on FormatException {
if (value.startsWith('[') && value.endsWith(']')) {
return json.decode(value);
} else if (value.startsWith('{') && value.endsWith('}')) {
return json.decode(value);
} else if (value.trim().toLowerCase() == 'null') {
return null;
} else {
return value;
}
}
}

View file

@ -1,44 +0,0 @@
import 'get_value.dart';
/// Parses a URI-encoded string into real data! **Wow!**
///
/// Whichever map you provide will be automatically populated from the urlencoded body string you provide.
void buildMapFromUri(Map map, String body) {
var parseArrayRgx = RegExp(r'^(.+)\[\]$');
for (var keyValuePair in body.split('&')) {
if (keyValuePair.contains('=')) {
var equals = keyValuePair.indexOf('=');
var key = Uri.decodeQueryComponent(keyValuePair.substring(0, equals));
var value = Uri.decodeQueryComponent(keyValuePair.substring(equals + 1));
if (parseArrayRgx.hasMatch(key)) {
Match queryMatch = parseArrayRgx.firstMatch(key)!;
key = queryMatch.group(1)!;
if (!(map[key] is List)) {
map[key] = [];
}
map[key].add(getValue(value));
} else if (key.contains('.')) {
// i.e. map.foo.bar => [map, foo, bar]
var keys = key.split('.');
var targetMap = map[keys[0]] != null ? map[keys[0]] as Map? : {};
map[keys[0]] = targetMap;
for (var i = 1; i < keys.length; i++) {
if (i < keys.length - 1) {
targetMap![keys[i]] = targetMap[keys[i]] ?? {};
targetMap = targetMap[keys[i]] as Map?;
} else {
targetMap![keys[i]] = getValue(value);
}
}
} else {
map[key] = getValue(value);
}
} else {
map[Uri.decodeQueryComponent(keyValuePair)] = true;
}
}
}

View file

@ -1,149 +0,0 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:http_parser/http_parser.dart';
import 'package:belatuk_http_server/belatuk_http_server.dart';
import 'package:mime/mime.dart';
import 'body_parse_result.dart';
import 'file_upload_info.dart';
import 'map_from_uri.dart';
/// Forwards to [parseBodyFromStream].
@deprecated
Future<BodyParseResult> parseBody(HttpRequest request,
{bool storeOriginalBuffer = false}) {
return parseBodyFromStream(
request,
request.headers.contentType != null
? MediaType.parse(request.headers.contentType.toString())
: null,
request.uri,
storeOriginalBuffer: storeOriginalBuffer);
}
/// Grabs data from an incoming request.
///
/// Supports URL-encoded and JSON, as well as multipart/* forms.
/// On a file upload request, only fields with the name **'file'** are processed
/// as files. Anything else is put in the body. You can change the upload file name
/// via the *fileUploadName* parameter. :)
///
/// Use [storeOriginalBuffer] to add the original request bytes to the result.
Future<BodyParseResult> parseBodyFromStream(
Stream<Uint8List> data, MediaType? contentType, Uri requestUri,
{bool storeOriginalBuffer = false}) async {
var result = _BodyParseResultImpl();
Future<Uint8List> getBytes() {
return data
.fold<BytesBuilder>(BytesBuilder(copy: false), (a, b) => a..add(b))
.then((b) => b.takeBytes());
}
Future<String> getBody() {
if (storeOriginalBuffer) {
return getBytes().then((bytes) {
result.originalBuffer = bytes;
return utf8.decode(bytes);
});
} else {
return utf8.decoder.bind(data).join();
}
}
try {
if (contentType != null) {
if (contentType.type == 'multipart' &&
contentType.parameters.containsKey('boundary')) {
Stream<Uint8List> stream;
if (storeOriginalBuffer) {
var bytes = result.originalBuffer = await getBytes();
var ctrl = StreamController<Uint8List>()..add(bytes);
await ctrl.close();
stream = ctrl.stream;
} else {
stream = data;
}
var parts =
MimeMultipartTransformer(contentType.parameters['boundary']!)
.bind(stream)
.map((part) =>
HttpMultipartFormData.parse(part, defaultEncoding: utf8));
await for (HttpMultipartFormData part in parts) {
if (part.isBinary ||
part.contentDisposition.parameters.containsKey('filename')) {
var builder = await part.fold(
BytesBuilder(copy: false),
(BytesBuilder b, d) =>
b..add(d is! String ? (d as List<int>?)! : d.codeUnits));
var upload = FileUploadInfo(
mimeType: part.contentType!.mimeType,
name: part.contentDisposition.parameters['name'],
filename:
part.contentDisposition.parameters['filename'] ?? 'file',
data: builder.takeBytes());
result.files.add(upload);
} else if (part.isText) {
var text = await part.join();
buildMapFromUri(result.body,
'${part.contentDisposition.parameters["name"]}=$text');
}
}
} else if (contentType.mimeType == 'application/json') {
result.body.addAll(
_foldToStringDynamic(json.decode(await getBody()) as Map?)!);
} else if (contentType.mimeType == 'application/x-www-form-urlencoded') {
var body = await getBody();
buildMapFromUri(result.body, body);
} else if (storeOriginalBuffer == true) {
result.originalBuffer = await getBytes();
}
} else {
if (requestUri.hasQuery) {
buildMapFromUri(result.query, requestUri.query);
}
if (storeOriginalBuffer == true) {
result.originalBuffer = await getBytes();
}
}
} catch (e, st) {
result.error = e;
result.stack = st;
}
return result;
}
class _BodyParseResultImpl implements BodyParseResult {
@override
Map<String?, dynamic> body = {};
@override
List<FileUploadInfo> files = [];
@override
List<int>? originalBuffer;
@override
Map<String?, dynamic> query = {};
@override
var error;
@override
StackTrace? stack;
}
Map<String, dynamic>? _foldToStringDynamic(Map? map) {
return map == null
? null
: map.keys.fold<Map<String, dynamic>>(
<String, dynamic>{}, (out, k) => out..[k.toString()] = map[k]);
}

View file

@ -1,15 +0,0 @@
name: angel3_body_parser
version: 2.1.2
description: Parse request bodies and query strings in Dart. Supports JSON, URL-encoded, and multi-part bodies.
homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/body_parser
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
http_parser: ^4.0.0
belatuk_http_server: ^2.0.0
mime: ^1.0.0
dev_dependencies:
http: ^0.13.0
test: ^1.17.0
pedantic: ^1.11.0

View file

@ -1,156 +0,0 @@
import 'dart:io';
import 'dart:convert';
import 'package:angel3_body_parser/angel3_body_parser.dart';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';
import 'package:test/test.dart';
import 'server_test.dart';
Future<BodyParseResult> _parseBody(HttpRequest request) {
return parseBodyFromStream(
request,
request.headers.contentType != null
? MediaType.parse(request.headers.contentType.toString())
: null,
request.uri,
storeOriginalBuffer: false);
}
void main() {
HttpServer? server;
String? url;
http.Client? client;
setUp(() async {
server = await HttpServer.bind('127.0.0.1', 0);
server!.listen((HttpRequest request) async {
//Server will simply return a JSON representation of the parsed body
// ignore: deprecated_member_use
request.response.write(jsonEncodeBody(await _parseBody(request)));
await request.response.close();
});
url = 'http://localhost:${server!.port}';
print('Test server listening on $url');
client = http.Client();
});
tearDown(() async {
await server!.close(force: true);
client!.close();
server = null;
url = null;
client = null;
});
test('No upload', () async {
var boundary = 'myBoundary';
var headers = <String, String>{
'content-type': 'multipart/form-data; boundary=$boundary'
};
var postData = '''
--$boundary
Content-Disposition: form-data; name="hello"
world
--$boundary--
'''
.replaceAll('\n', '\r\n');
print(
'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}');
var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}');
var jsons = json.decode(response.body);
var files = jsons['files'].map((map) {
return map == null
? null
: map.keys.fold<Map<String, dynamic>>(
<String, dynamic>{}, (out, k) => out..[k.toString()] = map[k]);
});
expect(files.length, equals(0));
expect(jsons['body']['hello'], equals('world'));
});
test('Single upload', () async {
var boundary = 'myBoundary';
var headers = <String, String>{
'content-type': ContentType('multipart', 'form-data',
parameters: {'boundary': boundary}).toString()
};
var postData = '''
--$boundary
Content-Disposition: form-data; name="hello"
world
--$boundary
Content-Disposition: form-data; name="file"; filename="app.dart"
Content-Type: application/dart
Hello world
--$boundary--
'''
.replaceAll('\n', '\r\n');
print(
'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}');
var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}');
var jsons = json.decode(response.body);
var files = jsons['files'];
expect(files.length, equals(1));
expect(files[0]['name'], equals('file'));
expect(files[0]['mimeType'], equals('application/dart'));
expect(files[0]['data'].length, equals(11));
expect(files[0]['filename'], equals('app.dart'));
expect(jsons['body']['hello'], equals('world'));
});
test('Multiple upload', () async {
var boundary = 'myBoundary';
var headers = <String, String>{
'content-type': 'multipart/form-data; boundary=$boundary'
};
var postData = '''
--$boundary
Content-Disposition: form-data; name="json"
god
--$boundary
Content-Disposition: form-data; name="num"
14.50000
--$boundary
Content-Disposition: form-data; name="file"; filename="app.dart"
Content-Type: text/plain
Hello world
--$boundary
Content-Disposition: form-data; name="entry-point"; filename="main.js"
Content-Type: text/javascript
function main() {
console.log("Hello, world!");
}
--$boundary--
'''
.replaceAll('\n', '\r\n');
print(
'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}');
var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}');
var jsons = json.decode(response.body);
var files = jsons['files'];
expect(files.length, equals(2));
expect(files[0]['name'], equals('file'));
expect(files[0]['mimeType'], equals('text/plain'));
expect(files[0]['data'].length, equals(11));
expect(files[1]['name'], equals('entry-point'));
expect(files[1]['mimeType'], equals('text/javascript'));
expect(jsons['body']['json'], equals('god'));
expect(jsons['body']['num'], equals(14.5));
});
}

View file

@ -1,174 +0,0 @@
import 'dart:convert';
import 'dart:io' show HttpRequest, HttpServer;
import 'package:angel3_body_parser/angel3_body_parser.dart';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';
import 'package:test/test.dart';
const TOKEN =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIxMjcuMC4wLjEiLCJleHAiOi0xLCJpYXQiOiIyMDE2LTEyLTIyVDEyOjQ5OjUwLjM2MTQ0NiIsImlzcyI6ImFuZ2VsX2F1dGgiLCJzdWIiOiIxMDY2OTQ4Mzk2MDIwMjg5ODM2NTYifQ==.PYw7yUb-cFWD7N0sSLztP7eeRvO44nu1J2OgDNyT060=';
String jsonEncodeBody(BodyParseResult result) {
return json.encode({
'query': result.query,
'body': result.body,
'error': result.error?.toString(),
'files': result.files.map((f) {
return {
'name': f.name,
'mimeType': f.mimeType,
'filename': f.filename,
'data': f.data,
};
}).toList(),
'originalBuffer': result.originalBuffer,
'stack': null, //result.stack.toString(),
});
}
Future<BodyParseResult> _parseBody(HttpRequest request) {
return parseBodyFromStream(
request,
request.headers.contentType != null
? MediaType.parse(request.headers.contentType.toString())
: null,
request.uri,
storeOriginalBuffer: true);
}
void main() {
HttpServer? server;
String? url;
http.Client? client;
setUp(() async {
server = await HttpServer.bind('127.0.0.1', 0);
server!.listen((HttpRequest request) async {
//Server will simply return a JSON representation of the parsed body
request.response.write(
// ignore: deprecated_member_use
jsonEncodeBody(await _parseBody(request)));
await request.response.close();
});
url = 'http://localhost:${server!.port}';
print('Test server listening on $url');
client = http.Client();
});
tearDown(() async {
await server!.close(force: true);
client!.close();
server = null;
url = null;
client = null;
});
group('query string', () {
test('GET Simple', () async {
print('GET $url/?hello=world');
var response = await client!.get(Uri.parse('$url/?hello=world'));
print('Response: ${response.body}');
var result = json.decode(response.body);
expect(result['body'], equals({}));
expect(result['query'], equals({'hello': 'world'}));
expect(result['files'], equals([]));
//expect(result['originalBuffer'], isNull);
});
test('GET Complex', () async {
var postData =
'hello=world&nums%5B%5D=1&nums%5B%5D=2.0&nums%5B%5D=${3 - 1}&map.foo.bar=baz';
print('Body: $postData');
var response = await client!.get(Uri.parse('$url/?$postData'));
print('Response: ${response.body}');
var query = json.decode(response.body)['query'];
expect(query['hello'], equals('world'));
expect(query['nums'][2], equals(2));
expect(query['map'] is Map, equals(true));
expect(query['map']['foo'], equals({'bar': 'baz'}));
});
test('JWT', () async {
var postData = 'token=$TOKEN';
print('Body: $postData');
var response = await client!.get(Uri.parse('$url/?$postData'));
print('Response: ${response.body}');
var query = json.decode(response.body)['query'];
expect(query['token'], equals(TOKEN));
});
});
group('urlencoded', () {
var headers = <String, String>{
'content-type': 'application/x-www-form-urlencoded'
};
test('POST Simple', () async {
print('Body: hello=world');
var response = await client!
.post(Uri.parse(url!), headers: headers, body: 'hello=world');
print('Response: ${response.body}');
var result = json.decode(response.body);
expect(result['query'], equals({}));
expect(result['body'], equals({'hello': 'world'}));
expect(result['files'], equals([]));
expect(result['originalBuffer'], isList);
expect(result['originalBuffer'], isNotEmpty);
});
test('Post Complex', () async {
var postData =
'hello=world&nums%5B%5D=1&nums%5B%5D=2.0&nums%5B%5D=${3 - 1}&map.foo.bar=baz';
var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}');
var body = json.decode(response.body)['body'];
expect(body['hello'], equals('world'));
expect(body['nums'][2], equals(2));
expect(body['map'] is Map, equals(true));
expect(body['map']['foo'], equals({'bar': 'baz'}));
});
test('JWT', () async {
var postData = 'token=$TOKEN';
var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
var body = json.decode(response.body)['body'];
expect(body['token'], equals(TOKEN));
});
});
group('json', () {
var headers = <String, String>{'content-type': 'application/json'};
test('Post Simple', () async {
var postData = json.encode({'hello': 'world'});
print('Body: $postData');
var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}');
var result = json.decode(response.body);
expect(result['body'], equals({'hello': 'world'}));
expect(result['query'], equals({}));
expect(result['files'], equals([]));
expect(result['originalBuffer'], allOf(isList, isNotEmpty));
});
test('Post Complex', () async {
var postData = json.encode({
'hello': 'world',
'nums': [1, 2.0, 3 - 1],
'map': {
'foo': {'bar': 'baz'}
}
});
print('Body: $postData');
var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}');
var body = json.decode(response.body)['body'];
expect(body['hello'], equals('world'));
expect(body['nums'][2], equals(2));
expect(body['map'] is Map, equals(true));
expect(body['map']['foo'], equals({'bar': 'baz'}));
});
});
}

View file

@ -1,3 +0,0 @@
# GraphQL
Moved to [`GraphQL Repo`](https://github.com/dukefirehawk/graphql_dart)

View file

@ -1,3 +0,0 @@
# Angel3 Html Builder
`angel3_html_builder` package has been deprecated and replaced by `belatuk_html_builder`package at [`Belatuk Common Utilities`](<https://github.com/dart-backend/belatuk-common-utilities/tree/main/packages/html_builder>)

View file

@ -1,3 +0,0 @@
George Moschovitis <george.moschovitis@gmail.com>
Tobe O <thosakwe@gmail.com>
Thomas Hii <thomashii@dukefirehawk.com>

View file

@ -1,38 +0,0 @@
## 1.0.0
* Migrated to support Dart SDK 2.12.x NNBD
## 0.4.3
- Add past tense support (thanks @phodonou !)
## 0.4.2
- Dart 2 compatibility.
- Hard fork.
## 0.4.1
- Cleanup, follow latest conventions and developments in the Dartiverse.
- Added past inflections.
## 0.3.2
- Cleanup.
## 0.3.1
- Case conversion handles sentences delimited with whitespace.
## 0.3.0
- Handle irregular plural nouns.
- Added shortcut functions.
## 0.2.0
- Handle uncountable nouns.
- Added singular inflection.
- Added plural inflection.
- Refactored to use a dart:convert based API.
## 0.1.0
- Initial version, convertToSnakeCase / convertToSpinalCase.

View file

@ -1,21 +0,0 @@
MIT License (MIT)
Copyright (c) 2021 dukefirehawk.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,44 +0,0 @@
# Inflection3
[![version](https://img.shields.io/badge/pub-v1.0.0-brightgreen)](https://pub.dartlang.org/packages/inflection3)
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
In grammar, inflection or inflexion is the modification of a word to express
different grammatical categories such as tense, mood, voice, aspect, person,
number, gender and case.
A port of the Rails/ActiveSupport inflector library to Dart.
**IMPORTANT NOTE**: This is a *hard fork* of the original `inflection2` package,
as the former is now archived, and abandoned.
## Usage
A simple usage example:
```dart
import 'package:inflection3/inflection3.dart';
void main() {
// Using 'shortcut' functions.
print(pluralize("house")); // => "houses"
print(convertToPlural("house")); // => "houses", alias for pluralize
print(pluralizeVerb("goes")); // => "go"
print(singularize("axes")); // => "axis"
print(convertToSingular("axes")); // => "axis", alias for pluralize
print(singularizeVerb("write")); // => "writes"
print(convertToSnakeCase("CamelCaseName")); // => "camel_case_name"
print(convertToSpinalCase("CamelCaseName")); // => "camel-case-name"
print(past("forgo")); // => "forwent"
// Using default encoders.
print(PLURAL.convert("virus")); // => "viri"
print(SINGULAR.convert("Matrices")); // => "Matrix"
print(SINGULAR.convert("species")); // => "species"
print(SNAKE_CASE.convert("CamelCaseName")); // => "camel_case_name"
print(SPINAL_CASE.convert("CamelCaseName")); // => "camel-case-name"
print(PAST.convert("miss")); // => "missed"
}
```

View file

@ -1,5 +0,0 @@
include: package:pedantic/analysis_options.yaml
analyzer:
strong-mode:
implicit-casts: false

View file

@ -1,24 +0,0 @@
import 'package:inflection3/inflection3.dart';
void main() {
// Using 'shortcut' functions.
print(pluralize('house')); // => 'houses'
print(convertToPlural('house')); // => 'houses', alias for pluralize
print(pluralizeVerb('goes')); // => 'go'
print(singularize('axes')); // => 'axis'
print(convertToSingular('axes')); // => 'axis', alias for pluralize
print(singularizeVerb('write')); // => 'writes'
print(convertToSnakeCase('CamelCaseName')); // => 'camel_case_name'
print(convertToSpinalCase('CamelCaseName')); // => 'camel-case-name'
print(past('forgo')); // => 'forwent'
// Using default encoders.
print(PLURAL.convert('virus')); // => 'viri'
print(SINGULAR.convert('Matrices')); // => 'Matrix'
print(SINGULAR.convert('species')); // => 'species'
print(SNAKE_CASE.convert('CamelCaseName')); // => 'camel_case_name'
print(SPINAL_CASE.convert('CamelCaseName')); // => 'camel-case-name'
print(PAST.convert('miss')); // => 'missed'
}

View file

@ -1,43 +0,0 @@
/// In grammar, inflection or inflexion is the modification of a word to express
/// different grammatical categories such as tense, mood, voice, aspect, person,
/// number, gender and case.
///
/// [ActiveSupport Inflector](https://github.com/rails/rails/tree/master/activesupport/lib/active_support/inflector)
/// [Letter case](http://en.wikipedia.org/wiki/Letter_case#Special_case_styles)
library inflection3;
import 'src/past.dart';
import 'src/plural.dart';
import 'src/plural_verb.dart';
import 'src/singular.dart';
import 'src/singular_verb.dart';
import 'src/snake_case.dart';
import 'src/spinal_case.dart';
export 'src/past.dart';
export 'src/plural.dart';
export 'src/plural_verb.dart';
export 'src/singular.dart';
export 'src/singular_verb.dart';
export 'src/snake_case.dart';
export 'src/spinal_case.dart';
export 'src/is_tense.dart';
String convertToPlural(String word) => PLURAL.convert(word);
String pluralize(String word) => PLURAL.convert(word);
String convertToPluralVerb(String word) => PLURALVERB.convert(word);
String pluralizeVerb(String word) => PLURALVERB.convert(word);
String convertToSingular(String word) => SINGULAR.convert(word);
String singularize(String word) => SINGULAR.convert(word);
String convertToSingularVerb(String word) => SINGULARVERB.convert(word);
String singularizeVerb(String word) => SINGULARVERB.convert(word);
String convertToSnakeCase(String word) => SNAKE_CASE.convert(word);
String convertToSpinalCase(String word) => SPINAL_CASE.convert(word);
String convertToPast(String word) => PAST.convert(word);
String past(String word) => PAST.convert(word);

View file

@ -1,323 +0,0 @@
//library inflection2.irregular_past_verbs;
/// A collection of verbs with irregular past.
final Map<String, String> irregularPastVerbs = const {
'backslidden': 'backslid',
'forbidden': 'forbade',
'stridden': 'strode',
'stricken': 'struck',
'brought': 'brought',
'striven': 'strove',
'swollen': 'swelled',
'thought': 'thought',
'trodden': 'trod',
'forbade': 'forbade',
'arisen': 'arose',
'awoken': 'awoke',
'beaten': 'beat',
'became': 'became',
'become': 'became',
'bidden': 'bid',
'bitten': 'bit',
'broken': 'broke',
'bought': 'bought',
'caught': 'caught',
'choose': 'chose',
'chosen': 'chose',
'forbid': 'forbade',
'dreamt': 'dreamt',
'driven': 'drove',
'fallen': 'fell',
'fought': 'fought',
'freeze': 'froze',
'frozen': 'froze',
'gotten': 'got',
'ground': 'ground',
'hidden': 'hid',
'learnt': 'learnt',
'proven': 'proved',
'sought': 'sought',
'shaken': 'shook',
'shaven': 'shaved',
'shrank': 'shrank',
'shrink': 'shrank',
'shrunk': 'shrank',
'spoken': 'spoke',
'spoilt': 'spoilt',
'spread': 'spread',
'sprang': 'sprang',
'spring': 'sprang',
'sprung': 'sprang',
'stolen': 'stole',
'strewn': 'strewed',
'strode': 'strode',
'stride': 'strode',
'strike': 'struck',
'strove': 'strove',
'struck': 'struck',
'string': 'strung',
'strung': 'strung',
'taught': 'taught',
'thrown': 'threw',
'thrust': 'thrust',
'arise': 'arose',
'arose': 'arose',
'awake': 'awoke',
'awoke': 'awoke',
'borne': 'bore',
'began': 'began',
'begin': 'began',
'begun': 'began',
'bound': 'bound',
'bleed': 'bled',
'blown': 'blew',
'break': 'broke',
'breed': 'bred',
'bring': 'brought',
'broke': 'broke',
'build': 'built',
'built': 'built',
'burnt': 'burnt',
'catch': 'caught',
'chose': 'chose',
'cling': 'clung',
'clung': 'clung',
'creep': 'crept',
'crept': 'crept',
'dealt': 'dealt',
'wound': 'wound',
'wring': 'wrung',
'wrung': 'wrung',
'write': 'wrote',
'wrote': 'wrote',
'drawn': 'drew',
'drank': 'drank',
'drink': 'drank',
'drunk': 'drank',
'drive': 'drove',
'drove': 'drove',
'dwelt': 'dwelt',
'eaten': 'ate',
'fight': 'fought',
'found': 'found',
'fling': 'flung',
'flung': 'flung',
'flown': 'flew',
'froze': 'froze',
'given': 'gave',
'grind': 'ground',
'grown': 'grew',
'heard': 'heard',
'knelt': 'knelt',
'known': 'knew',
'leant': 'leant',
'leapt': 'leapt',
'leave': 'left',
'meant': 'meant',
'risen': 'rose',
'shake': 'shook',
'shorn': 'sheared',
'shone': 'shone',
'shook': 'shook',
'shoot': 'shot',
'shown': 'showed',
'slain': 'slew',
'sleep': 'slept',
'slept': 'slept',
'slide': 'slid',
'sling': 'slung',
'slung': 'slung',
'slunk': 'slunk',
'smelt': 'smelt',
'snuck': 'snuck',
'speak': 'spoke',
'spelt': 'spelt',
'spend': 'spent',
'spent': 'spent',
'spilt': 'spilt',
'split': 'split',
'spoke': 'spoke',
'stand': 'stood',
'stood': 'stood',
'steal': 'stole',
'stick': 'stuck',
'stole': 'stole',
'stuck': 'stuck',
'sting': 'stung',
'stung': 'stung',
'stank': 'stank',
'stink': 'stank',
'stunk': 'stank',
'swear': 'swore',
'swore': 'swore',
'sworn': 'swore',
'sweep': 'swept',
'swept': 'swept',
'swing': 'swung',
'swung': 'swung',
'taken': 'took',
'teach': 'taught',
'think': 'thought',
'threw': 'threw',
'throw': 'threw',
'tread': 'trod',
'wake': 'woke',
'woken': 'woke',
'woven': 'wove',
'bear': 'bore',
'bore': 'bore',
'born': 'bore',
'beat': 'beat',
'bend': 'bent',
'bent': 'bent',
'bind': 'bound',
'bite': 'bit',
'bled': 'bled',
'blew': 'blew',
'blow': 'blew',
'bred': 'bred',
'cast': 'cast',
'clad': 'clad',
'come': 'came',
'cost': 'cost',
'deal': 'dealt',
'does': 'did',
'done': 'did',
'draw': 'drew',
'drew': 'drew',
'fall': 'fell',
'feed': 'fed',
'feel': 'felt',
'fell': 'fell',
'felt': 'felt',
'find': 'found',
'flee': 'fled',
'fled': 'fled',
'flew': 'flew',
'gave': 'gave',
'give': 'gave',
'gone': 'went',
'grew': 'grew',
'grow': 'grew',
'hang': 'hung',
'hung': 'hung',
'have': 'had',
'hear': 'heard',
'hewn': 'hewed',
'hide': 'hid',
'hold': 'held',
'held': 'held',
'hurt': 'hurt',
'keep': 'kept',
'kept': 'kept',
'knew': 'knew',
'know': 'knew',
'laid': 'laid',
'lead': 'led',
'left': 'left',
'lend': 'lent',
'lent': 'lent',
'lain': 'lay',
'lose': 'lost',
'lost': 'lost',
'make': 'made',
'made': 'made',
'mean': 'meant',
'meet': 'met',
'mown': 'mowed',
'paid': 'paid',
'pled': 'pled',
'read': 'read',
'ride': 'rode',
'rode': 'rode',
'ring': 'rang',
'rung': 'rang',
'rise': 'rose',
'rose': 'rose',
'sang': 'sang',
'sawn': 'sawed',
'said': 'said',
'seen': 'saw',
'seek': 'sought',
'sell': 'sold',
'slew': 'slew',
'sold': 'sold',
'send': 'sent',
'sent': 'sent',
'sewn': 'sewed',
'shed': 'shed',
'shot': 'shot',
'shut': 'shut',
'sing': 'sang',
'sung': 'sang',
'slid': 'slid',
'slit': 'slit',
'sown': 'sowed',
'sped': 'sped',
'spin': 'spun',
'spun': 'spun',
'spit': 'spit',
'spat': 'spat',
'swam': 'swam',
'swim': 'swam',
'swum': 'swam',
'take': 'took',
'tear': 'tore',
'tore': 'tore',
'torn': 'tore',
'tell': 'told',
'told': 'told',
'took': 'took',
'trod': 'trod',
'wear': 'wore',
'wore': 'wore',
'worn': 'wore',
'weep': 'wept',
'went': 'went',
'wept': 'wept',
'were': 'were',
'wind': 'wound',
'woke': 'woke',
'wove': 'wove',
'are': 'were',
'ate': 'ate',
'bet': 'bet',
'bid': 'bid',
'bit': 'bit',
'buy': 'bought',
'cut': 'cut',
'did': 'did',
'dig': 'dug',
'dug': 'dug',
'eat': 'ate',
'fed': 'fed',
'fly': 'flew',
'get': 'got',
'got': 'got',
'had': 'had',
'has': 'had',
'hid': 'hid',
'hit': 'hit',
'lay': 'laid',
'led': 'led',
'let': 'let',
'lit': 'lit',
'met': 'met',
'pay': 'paid',
'put': 'put',
'ran': 'ran',
'rid': 'rid',
'run': 'ran',
'saw': 'saw',
'say': 'said',
'see': 'saw',
'sit': 'sat',
'sat': 'sat',
'set': 'set',
'was': 'was',
'win': 'won',
'won': 'won',
'do': 'did',
'go': 'went',
'is': 'was',
};

View file

@ -1,11 +0,0 @@
//library inflection2.irregular_plural_nouns;
/// A collection of nouns with irregular plurals.
///
/// [A List of 100 Irregular Plural Nouns in English](http://grammar.about.com/od/words/a/A-List-Of-Irregular-Plural-Nouns-In-English.htm)
final Map<String, String> irregularPluralNouns = const {
'person': 'people',
'man': 'men',
'child': 'children',
'sex': 'sexes'
};

View file

@ -1,9 +0,0 @@
//library inflection2.irregular_plural_verbs;
/// A collection of verbs with irregular plurals.
final Map<String, String> irregularPluralVerbs = const {
'is': 'are',
'am': 'are',
'was': 'were',
'has': 'have'
};

View file

@ -1,6 +0,0 @@
import '../inflection3.dart';
/// returns true if this word is in the past tense
bool isPastTense(String word) {
return word.toLowerCase().trim() == past(word).toLowerCase().trim();
}

View file

@ -1,69 +0,0 @@
//library inflection2.past;
import 'dart:convert';
import 'irregular_past_verbs.dart';
import 'verbs_ending_with_ed.dart';
import 'util.dart';
class PastEncoder extends Converter<String, String> {
final List<List> _inflectionRules = [];
PastEncoder() {
irregularPastVerbs.forEach((String presentOrParticiple, String past) {
addIrregularInflectionRule(presentOrParticiple, past);
});
[
[r'.+', (Match m) => '${m[0]}ed'],
[r'([^aeiou])y$', (Match m) => '${m[1]}ied'],
[r'([aeiou]e)$', (Match m) => '${m[1]}d'],
[r'[aeiou][^aeiou]e$', (Match m) => '${m[0]}d']
]
.reversed
.forEach((rule) => addInflectionRule(rule.first as String, rule.last));
}
void addInflectionRule(String presentOrParticiple, dynamic past) {
_inflectionRules
.add([RegExp(presentOrParticiple, caseSensitive: false), past]);
}
void addIrregularInflectionRule(String presentOrParticiple, String past) {
_inflectionRules.add([
RegExp(
r'^(back|dis|for|fore|in|inter|mis|off|over|out|par|pre|re|type|un|under|up)?' +
presentOrParticiple +
r'$',
caseSensitive: false),
(Match m) => (m[1] == null) ? past : m[1]! + past
]);
}
@override
String convert(String word) {
if (word.isNotEmpty) {
if (word.contains('ed', word.length - 2)) {
var reg = RegExp(
r'^(back|dis|for|fore|in|inter|mis|off|over|out|par|pre|re|type|un|under|up)(.+)$');
if (reg.hasMatch(word)) {
if (!verbsEndingWithEd.contains(reg.firstMatch(word)!.group(2))) {
return word;
}
} else if (!verbsEndingWithEd.contains(word)) {
return word;
}
}
for (var r in _inflectionRules) {
var pattern = r.first as RegExp;
if (pattern.hasMatch(word)) {
return word.replaceAllMapped(pattern, r.last as MatchToString);
}
}
}
return word;
}
}
final Converter<String, String> PAST = PastEncoder();

View file

@ -1,87 +0,0 @@
//library inflection2.plural;
import 'dart:convert';
import 'uncountable_nouns.dart';
import 'irregular_plural_nouns.dart';
import 'util.dart';
class PluralEncoder extends Converter<String, String> {
final List<List> _inflectionRules = [];
PluralEncoder() {
irregularPluralNouns.forEach((singular, plural) {
addIrregularInflectionRule(singular, plural);
});
[
[r'$', (Match m) => 's'],
[r's$', (Match m) => 's'],
[r'^(ax|test)is$', (Match m) => '${m[1]}es'],
[r'(octop|vir)us$', (Match m) => '${m[1]}i'],
[r'(octop|vir)i$', (Match m) => m[0]],
[r'(alias|status)$', (Match m) => '${m[1]}es'],
[r'(bu)s$', (Match m) => '${m[1]}ses'],
[r'(buffal|tomat)o$', (Match m) => '${m[1]}oes'],
[r'([ti])um$', (Match m) => '${m[1]}a'],
[r'([ti])a$', (Match m) => m[0]],
[r'sis$', (Match m) => 'ses'],
[r'(?:([^f])fe|([lr])f)$', (Match m) => '${m[1]}${m[2]}ves'],
[r'([^aeiouy]|qu)y$', (Match m) => '${m[1]}ies'],
[r'(x|ch|ss|sh)$', (Match m) => '${m[1]}es'],
[r'(matr|vert|ind)(?:ix|ex)$', (Match m) => '${m[1]}ices'],
[r'^(m|l)ouse$', (Match m) => '${m[1]}ice'],
[r'^(m|l)ice$', (Match m) => m[0]],
[r'^(ox)$', (Match m) => '${m[1]}en'],
[r'^(oxen)$', (Match m) => m[1]],
[r'(quiz)$', (Match m) => '${m[1]}zes']
]
.reversed
.forEach((rule) => addInflectionRule(rule.first as String, rule.last));
}
void addInflectionRule(String singular, dynamic plural) {
_inflectionRules.add([RegExp(singular, caseSensitive: false), plural]);
}
void addIrregularInflectionRule(String singular, String plural) {
final s0 = singular.substring(0, 1);
final srest = singular.substring(1);
final p0 = plural.substring(0, 1);
final prest = plural.substring(1);
if (s0.toUpperCase() == p0.toUpperCase()) {
addInflectionRule('($s0)$srest\$', (Match m) => '${m[1]}$prest');
addInflectionRule('($p0)$prest\$', (Match m) => '${m[1]}$prest');
} else {
addInflectionRule('${s0.toUpperCase()}(?i)$srest\$',
(Match m) => '${p0.toUpperCase()}$prest');
addInflectionRule('${s0.toLowerCase()}(?i)$srest\$',
(Match m) => '${p0.toUpperCase()}$prest');
addInflectionRule('${p0.toUpperCase()}(?i)$prest\$',
(Match m) => '${p0.toUpperCase()}$prest');
addInflectionRule('${p0.toLowerCase()}(?i)$prest\$',
(Match m) => '${p0.toLowerCase()}$prest');
}
}
@override
String convert(String word) {
if (word.isNotEmpty) {
if (uncountableNouns.contains(word.toLowerCase())) {
return word;
} else {
for (var r in _inflectionRules) {
var pattern = r.first as RegExp;
if (pattern.hasMatch(word)) {
return word.replaceAllMapped(pattern, r.last as MatchToString);
}
}
}
}
return word;
}
}
final Converter<String, String> PLURAL = PluralEncoder();

View file

@ -1,49 +0,0 @@
//library inflection2.plural_verb;
import 'dart:convert';
import 'irregular_plural_verbs.dart';
import 'util.dart';
class PluralVerbEncoder extends Converter<String, String> {
final List<List> _inflectionRules = [];
PluralVerbEncoder() {
irregularPluralVerbs.forEach((singular, plural) {
addInflectionRule(singular, (Match m) => plural);
});
[
[r'e?s$', (Match m) => ''],
[r'ies$', (Match m) => 'y'],
[r'([^h|z|o|i])es$', (Match m) => '${m[1]}e'],
[r'ses$', (Match m) => 's'],
[r'zzes$', (Match m) => 'zz'],
[r'([cs])hes$', (Match m) => '${m[1]}h'],
[r'xes$', (Match m) => 'x'],
[r'sses$', (Match m) => 'ss']
]
.reversed
.forEach((rule) => addInflectionRule(rule.first as String, rule.last));
}
void addInflectionRule(String singular, dynamic plural) {
_inflectionRules.add([RegExp(singular, caseSensitive: false), plural]);
}
@override
String convert(String word) {
if (word.isNotEmpty) {
for (var r in _inflectionRules) {
var pattern = r.first as RegExp;
if (pattern.hasMatch(word)) {
return word.replaceAllMapped(pattern, r.last as MatchToString);
}
}
}
return word;
}
}
final Converter<String, String> PLURALVERB = PluralVerbEncoder();

View file

@ -1,95 +0,0 @@
//library inflection2.singular;
import 'dart:convert';
import 'uncountable_nouns.dart';
import 'irregular_plural_nouns.dart';
import 'util.dart';
class SingularEncoder extends Converter<String, String> {
final List<List> _inflectionRules = [];
SingularEncoder() {
irregularPluralNouns.forEach((singular, plural) {
addIrregularInflectionRule(singular, plural);
});
[
[r's$', (Match m) => ''],
[r'(ss)$', (Match m) => m[1]],
[r'(n)ews$', (Match m) => '${m[1]}ews'], // TODO: uncountable?
[r'([ti])a$', (Match m) => '${m[1]}um'],
[
r'((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$',
(Match m) => '${m[1]}sis'
],
[r'(^analy)(sis|ses)$', (Match m) => '${m[1]}sis'], // TODO: not needed?
[r'([^f])ves$', (Match m) => '${m[1]}fe'],
[r'(hive|tive)s$', (Match m) => m[1]],
[r'([lr])ves$', (Match m) => '${m[1]}f'],
[r'([^aeiouy]|qu)ies$', (Match m) => '${m[1]}y'],
[r'(s)eries$', (Match m) => '${m[1]}eries'], // TODO: uncountable
[r'(m)ovies$', (Match m) => '${m[1]}ovie'],
[r'(x|ch|ss|sh)es$', (Match m) => m[1]],
[r'^(m|l)ice$', (Match m) => '${m[1]}ouse'],
[r'(bus)(es)?$', (Match m) => m[1]],
[r'(shoe)s$', (Match m) => m[1]],
[r'(cris|test)(is|es)$', (Match m) => '${m[1]}is'],
[r'^(a)x[ie]s$', (Match m) => '${m[1]}xis'],
[r'(octop|vir)(us|i)$', (Match m) => '${m[1]}us'],
[r'(alias|status)(es)?$', (Match m) => m[1]],
[r'^(ox)en', (Match m) => m[1]],
[r'(vert|ind)ices$', (Match m) => '${m[1]}ex'],
[r'(matr)ices$', (Match m) => '${m[1]}ix'],
[r'(quiz)zes$', (Match m) => m[1]],
[r'(database)s$', (Match m) => m[1]]
]
.reversed
.forEach((rule) => addInflectionRule(rule.first as String, rule.last));
}
void addInflectionRule(String plural, dynamic singular) {
_inflectionRules.add([RegExp(plural, caseSensitive: false), singular]);
}
void addIrregularInflectionRule(String singular, String plural) {
final s0 = singular.substring(0, 1);
final srest = singular.substring(1);
final p0 = plural.substring(0, 1);
final prest = plural.substring(1);
if (s0.toUpperCase() == p0.toUpperCase()) {
addInflectionRule('($s0)$srest\$', (Match m) => '${m[1]}$srest');
addInflectionRule('($p0)$prest\$', (Match m) => '${m[1]}$srest');
} else {
addInflectionRule('${s0.toUpperCase()}(?i)$srest\$',
(Match m) => '${s0.toUpperCase()}$srest');
addInflectionRule('${s0.toLowerCase()}(?i)$srest\$',
(Match m) => '${s0.toUpperCase()}$srest');
addInflectionRule('${p0.toUpperCase()}(?i)$prest\$',
(Match m) => '${s0.toUpperCase()}$srest');
addInflectionRule('${p0.toLowerCase()}(?i)$prest\$',
(Match m) => '${s0.toLowerCase()}$srest');
}
}
@override
String convert(String word) {
if (word.isNotEmpty) {
if (uncountableNouns.contains(word.toLowerCase())) {
return word;
} else {
for (var r in _inflectionRules) {
var pattern = r.first as RegExp;
if (pattern.hasMatch(word)) {
return word.replaceAllMapped(pattern, r.last as MatchToString);
}
}
}
}
return word;
}
}
final Converter<String, String> SINGULAR = SingularEncoder();

View file

@ -1,46 +0,0 @@
//library inflection2.singular_verb;
import 'dart:convert';
import 'irregular_plural_verbs.dart';
import 'util.dart';
class SingularVerbEncoder extends Converter<String, String> {
final List<List> _inflectionRules = [];
SingularVerbEncoder() {
irregularPluralVerbs.forEach((singular, plural) {
addInflectionRule(plural, (Match m) => singular);
});
[
[r'$', (Match m) => 's'],
[r'([^aeiou])y$', (Match m) => '${m[1]}ies'],
[r'(z)$', (Match m) => '${m[1]}es'],
[r'(ss|zz|x|h|o|us)$', (Match m) => '${m[1]}es'],
[r'(ed)$', (Match m) => '${m[1]}']
]
.reversed
.forEach((rule) => addInflectionRule(rule.first as String, rule.last));
}
void addInflectionRule(String singular, dynamic plural) {
_inflectionRules.add([RegExp(singular, caseSensitive: false), plural]);
}
@override
String convert(String word) {
if (word.isNotEmpty) {
for (var r in _inflectionRules) {
var pattern = r.first as RegExp;
if (pattern.hasMatch(word)) {
return word.replaceAllMapped(pattern, r.last as MatchToString);
}
}
}
return word;
}
}
final Converter<String, String> SINGULARVERB = SingularVerbEncoder();

View file

@ -1,24 +0,0 @@
//library inflection2.snake_case;
import 'dart:convert';
final _underscoreRE0 = RegExp(r'''([A-Z\d]+)([A-Z][a-z])''');
final _underscoreRE1 = RegExp(r'''([a-z\d])([A-Z])''');
final _underscoreRE2 = RegExp(r'[-\s]');
class SnakeCaseEncoder extends Converter<String, String> {
const SnakeCaseEncoder();
/// Converts the input [phrase] to 'spinal case', i.e. a hyphen-delimited,
/// lowercase form. Also known as 'kebab case' or 'lisp case'.
@override
String convert(String phrase) {
return phrase
.replaceAllMapped(_underscoreRE0, (match) => '${match[1]}_${match[2]}')
.replaceAllMapped(_underscoreRE1, (match) => '${match[1]}_${match[2]}')
.replaceAll(_underscoreRE2, '_')
.toLowerCase();
}
}
const Converter<String, String> SNAKE_CASE = SnakeCaseEncoder();

View file

@ -1,24 +0,0 @@
//library inflection2.spinal_case;
import 'dart:convert';
final _underscoreRE0 = RegExp(r'''([A-Z\d]+)([A-Z][a-z])''');
final _underscoreRE1 = RegExp(r'''([a-z\d])([A-Z])''');
final _underscoreRE2 = RegExp(r'[_\s]');
class SpinalCaseEncoder extends Converter<String, String> {
const SpinalCaseEncoder();
/// Converts the input [phrase] to 'spinal case', i.e. a hyphen-delimited,
/// lowercase form. Also known as 'kebab case' or 'lisp case'.
@override
String convert(String phrase) {
return phrase
.replaceAllMapped(_underscoreRE0, (match) => '${match[1]}-${match[2]}')
.replaceAllMapped(_underscoreRE1, (match) => '${match[1]}-${match[2]}')
.replaceAll(_underscoreRE2, '-')
.toLowerCase();
}
}
const Converter<String, String> SPINAL_CASE = SpinalCaseEncoder();

View file

@ -1,16 +0,0 @@
//library inflection2.uncountable;
/// Uncountable nouns are substances, concepts etc that we cannot divide into
/// separate elements. We cannot 'count' them.
final Set<String> uncountableNouns = Set.from(const [
'equipment',
'information',
'rice',
'money',
'species',
'series',
'fish',
'sheep',
'jeans',
'police'
]);

View file

@ -1 +0,0 @@
typedef MatchToString = String Function(Match m);

View file

@ -1,21 +0,0 @@
//library inflection2.verbs_ending_with_ed;
/// A collection of verbs ending with -ed.
final List<String> verbsEndingWithEd = const [
'bed',
'bleed',
'breed',
'embed',
'exceed',
'feed',
'heed',
'need',
'proceed',
'seed',
'shred',
'speed',
'succeed',
'ted',
'wed',
'weed'
];

View file

@ -1,9 +0,0 @@
name: inflection3
version: 1.0.0
description: Grammatical Inflection encoders.
homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/inflection3
environment:
sdk: '>=2.12.0 <3.0.0'
dev_dependencies:
test: ^1.17.4
pedantic: ^1.11.0

View file

@ -1,21 +0,0 @@
import 'inflection_test.dart' as inflection_test;
import 'past_test.dart' as past_test;
import 'plural_test.dart' as plural_test;
import 'plural_verb_test.dart' as plural_verb_test;
import 'singular_test.dart' as singular_test;
import 'singular_verb_test.dart' as singular_verb_test;
import 'snake_case_test.dart' as snake_case_test;
import 'spinal_case_test.dart' as spinal_case_test;
import 'is_tense_test.dart' as is_tense_test;
void main() {
inflection_test.main();
past_test.main();
plural_test.main();
plural_verb_test.main();
singular_test.main();
singular_verb_test.main();
snake_case_test.main();
spinal_case_test.main();
is_tense_test.main();
}

View file

@ -1,17 +0,0 @@
library inflection3.test;
import 'package:test/test.dart';
import 'package:inflection3/inflection3.dart';
void main() {
group('The inflection library', () {
test('provides a few convenient helper functions', () {
expect(pluralize('axis'), equals('axes'));
expect(convertToPlural('axis'), equals('axes'));
expect(singularize('Houses'), equals('House'));
expect(convertToSingular('Houses'), equals('House'));
expect(convertToSnakeCase('CamelCase'), equals('camel_case'));
expect(convertToSpinalCase('CamelCase'), equals('camel-case'));
});
});
}

View file

@ -1,15 +0,0 @@
library inflection3.is_tense.test;
import 'package:test/test.dart';
import 'package:inflection3/inflection3.dart';
void main() {
group('isTense', () {
test('correctly identifies if a word is in past tense', () {
expect(isPastTense('run'), false);
expect(isPastTense('ran'), true);
expect(isPastTense('PusHed'), true);
expect(isPastTense('PusH'), false);
});
});
}

View file

@ -1,45 +0,0 @@
library inflection3.past.test;
import 'package:inflection3/inflection3.dart';
import 'package:inflection3/src/irregular_past_verbs.dart';
import 'package:test/test.dart';
void main() {
group('The PastEncoder', () {
test('converts verbs from present or participle to past', () {
expect(PAST.convert(''), equals(''));
expect(PAST.convert('ask'), equals('asked'));
expect(PAST.convert('close'), equals('closed'));
expect(PAST.convert('die'), equals('died'));
expect(PAST.convert('phone'), equals('phoned'));
expect(PAST.convert('play'), equals('played'));
expect(PAST.convert('destroy'), equals('destroyed'));
expect(PAST.convert('show'), equals('showed'));
expect(PAST.convert('marry'), equals('married'));
expect(PAST.convert('study'), equals('studied'));
expect(PAST.convert('visit'), equals('visited'));
expect(PAST.convert('miss'), equals('missed'));
expect(PAST.convert('watch'), equals('watched'));
expect(PAST.convert('finish'), equals('finished'));
expect(PAST.convert('fix'), equals('fixed'));
expect(PAST.convert('buzz'), equals('buzzed'));
expect(PAST.convert('asked'), equals('asked'));
expect(PAST.convert('closed'), equals('closed'));
expect(PAST.convert('reopened'), equals('reopened'));
expect(PAST.convert('unseed'), equals('unseeded'));
});
test('handles irregular past verbs', () {
irregularPastVerbs.forEach((String presentOrParticiple, String past) {
expect(PAST.convert(presentOrParticiple), equals(past));
});
expect(PAST.convert('forgo'), equals('forwent'));
expect(PAST.convert('undo'), equals('undid'));
expect(PAST.convert('outsell'), equals('outsold'));
expect(PAST.convert('rebreed'), equals('rebred'));
expect(PAST.convert('arose'), equals('arose'));
expect(PAST.convert('backslid'), equals('backslid'));
expect(PAST.convert('forbade'), equals('forbade'));
});
});
}

View file

@ -1,65 +0,0 @@
library inflection3.plural.test;
import 'package:inflection3/inflection3.dart';
import 'package:inflection3/src/uncountable_nouns.dart';
import 'package:test/test.dart';
void main() {
group('The PluralEncoder', () {
test('converts nouns from singular to plural', () {
expect(PLURAL.convert(''), equals(''));
expect(PLURAL.convert('House'), equals('Houses'));
expect(PLURAL.convert('house'), equals('houses'));
expect(PLURAL.convert('dog'), equals('dogs'));
expect(PLURAL.convert('axis'), equals('axes'));
expect(PLURAL.convert('testis'), equals('testes'));
expect(PLURAL.convert('octopus'), equals('octopi'));
expect(PLURAL.convert('virus'), equals('viri'));
expect(PLURAL.convert('octopi'), equals('octopi'));
expect(PLURAL.convert('viri'), equals('viri'));
expect(PLURAL.convert('alias'), equals('aliases'));
expect(PLURAL.convert('status'), equals('statuses'));
expect(PLURAL.convert('bus'), equals('buses'));
expect(PLURAL.convert('buffalo'), equals('buffaloes'));
expect(PLURAL.convert('tomato'), equals('tomatoes'));
expect(PLURAL.convert('ultimatum'), equals('ultimata'));
expect(PLURAL.convert('pentium'), equals('pentia'));
expect(PLURAL.convert('ultimata'), equals('ultimata'));
expect(PLURAL.convert('pentia'), equals('pentia'));
expect(PLURAL.convert('nemesis'), equals('nemeses'));
expect(PLURAL.convert('hive'), equals('hives'));
expect(PLURAL.convert('fly'), equals('flies'));
expect(PLURAL.convert('dish'), equals('dishes'));
expect(PLURAL.convert('bench'), equals('benches'));
expect(PLURAL.convert('matrix'), equals('matrices'));
expect(PLURAL.convert('vertex'), equals('vertices'));
expect(PLURAL.convert('index'), equals('indices'));
expect(PLURAL.convert('mouse'), equals('mice'));
expect(PLURAL.convert('louse'), equals('lice'));
expect(PLURAL.convert('mice'), equals('mice'));
expect(PLURAL.convert('lice'), equals('lice'));
expect(PLURAL.convert('ox'), equals('oxen'));
expect(PLURAL.convert('ox'), equals('oxen'));
expect(PLURAL.convert('oxen'), equals('oxen'));
expect(PLURAL.convert('quiz'), equals('quizzes'));
});
test('handles uncountable nouns', () {
uncountableNouns.forEach((noun) {
expect(PLURAL.convert(noun), equals(noun));
});
uncountableNouns.forEach((noun) {
final upperNoun = noun.toUpperCase();
expect(PLURAL.convert(upperNoun), equals(upperNoun));
});
});
test('handles irregular plural nouns', () {
expect(PLURAL.convert('person'), equals('people'));
expect(PLURAL.convert('Child'), equals('Children'));
expect(PLURAL.convert('children'), equals('children'));
expect(PLURAL.convert('man'), equals('men'));
});
});
}

View file

@ -1,34 +0,0 @@
library inflection3.plural_verb.test;
import 'package:inflection3/inflection3.dart';
import 'package:test/test.dart';
void main() {
group('The PluralVerbEncoder', () {
test('converts verbs from singular to plural', () {
expect(PLURALVERB.convert(''), equals(''));
expect(PLURALVERB.convert('eats'), equals('eat'));
expect(PLURALVERB.convert('goes'), equals('go'));
expect(PLURALVERB.convert('boxes'), equals('box'));
expect(PLURALVERB.convert('pays'), equals('pay'));
expect(PLURALVERB.convert('rides'), equals('ride'));
expect(PLURALVERB.convert('writes'), equals('write'));
expect(PLURALVERB.convert('wears'), equals('wear'));
expect(PLURALVERB.convert('steals'), equals('steal'));
expect(PLURALVERB.convert('springs'), equals('spring'));
expect(PLURALVERB.convert('speaks'), equals('speak'));
expect(PLURALVERB.convert('sings'), equals('sing'));
expect(PLURALVERB.convert('buses'), equals('bus'));
expect(PLURALVERB.convert('knows'), equals('know'));
expect(PLURALVERB.convert('hides'), equals('hide'));
expect(PLURALVERB.convert('catches'), equals('catch'));
});
test('handles irregular plural verbs', () {
expect(PLURALVERB.convert('am'), equals('are'));
expect(PLURALVERB.convert('is'), equals('are'));
expect(PLURALVERB.convert('was'), equals('were'));
expect(PLURALVERB.convert('has'), equals('have'));
});
});
}

View file

@ -1,64 +0,0 @@
library inflection3.singular.test;
import 'package:inflection3/inflection3.dart';
import 'package:inflection3/src/uncountable_nouns.dart';
import 'package:test/test.dart';
void main() {
group('The SingularEncoder', () {
test('converts nouns from plural to singular', () {
expect(SINGULAR.convert(''), equals(''));
expect(SINGULAR.convert('Houses'), equals('House'));
expect(SINGULAR.convert('houses'), equals('house'));
expect(SINGULAR.convert('ultimata'), equals('ultimatum'));
expect(SINGULAR.convert('pentia'), equals('pentium'));
expect(SINGULAR.convert('analyses'), equals('analysis'));
expect(SINGULAR.convert('diagnoses'), equals('diagnosis'));
expect(SINGULAR.convert('Parentheses'), equals('Parenthesis'));
expect(SINGULAR.convert('lives'), equals('life'));
expect(SINGULAR.convert('hives'), equals('hive'));
expect(SINGULAR.convert('tives'), equals('tive'));
expect(SINGULAR.convert('shelves'), equals('shelf'));
expect(SINGULAR.convert('qualities'), equals('quality'));
expect(SINGULAR.convert('series'), equals('series'));
expect(SINGULAR.convert('movies'), equals('movie'));
expect(SINGULAR.convert('benches'), equals('bench'));
expect(SINGULAR.convert('fishes'), equals('fish'));
expect(SINGULAR.convert('mice'), equals('mouse'));
expect(SINGULAR.convert('lice'), equals('louse'));
expect(SINGULAR.convert('buses'), equals('bus'));
expect(SINGULAR.convert('shoes'), equals('shoe'));
expect(SINGULAR.convert('testis'), equals('testis'));
expect(SINGULAR.convert('crisis'), equals('crisis'));
expect(SINGULAR.convert('axes'), equals('axis'));
expect(SINGULAR.convert('axis'), equals('axis'));
expect(SINGULAR.convert('viri'), equals('virus'));
expect(SINGULAR.convert('octopi'), equals('octopus'));
expect(SINGULAR.convert('aliases'), equals('alias'));
expect(SINGULAR.convert('statuses'), equals('status'));
expect(SINGULAR.convert('vertices'), equals('vertex'));
expect(SINGULAR.convert('indices'), equals('index'));
expect(SINGULAR.convert('Matrices'), equals('Matrix'));
expect(SINGULAR.convert('quizzes'), equals('quiz'));
expect(SINGULAR.convert('databases'), equals('database'));
});
test('handles uncountable nouns', () {
uncountableNouns.forEach((noun) {
expect(SINGULAR.convert(noun), equals(noun));
});
uncountableNouns.forEach((noun) {
final upperNoun = noun.toUpperCase();
expect(SINGULAR.convert(upperNoun), equals(upperNoun));
});
});
test('handles irregular plural nouns', () {
expect(SINGULAR.convert('people'), equals('person'));
expect(SINGULAR.convert('Children'), equals('Child'));
expect(SINGULAR.convert('child'), equals('child'));
expect(SINGULAR.convert('men'), equals('man'));
});
});
}

View file

@ -1,33 +0,0 @@
library inflection3.singular_verb.test;
import 'package:inflection3/inflection3.dart';
import 'package:test/test.dart';
void main() {
group('The SingularVerbEncoder', () {
test('converts verbs from singular to plural', () {
expect(SINGULARVERB.convert(''), equals(''));
expect(SINGULARVERB.convert('eat'), equals('eats'));
expect(SINGULARVERB.convert('go'), equals('goes'));
expect(SINGULARVERB.convert('box'), equals('boxes'));
expect(SINGULARVERB.convert('pay'), equals('pays'));
expect(SINGULARVERB.convert('ride'), equals('rides'));
expect(SINGULARVERB.convert('write'), equals('writes'));
expect(SINGULARVERB.convert('wear'), equals('wears'));
expect(SINGULARVERB.convert('steal'), equals('steals'));
expect(SINGULARVERB.convert('spring'), equals('springs'));
expect(SINGULARVERB.convert('speak'), equals('speaks'));
expect(SINGULARVERB.convert('sing'), equals('sings'));
expect(SINGULARVERB.convert('bus'), equals('buses'));
expect(SINGULARVERB.convert('know'), equals('knows'));
expect(SINGULARVERB.convert('hide'), equals('hides'));
expect(SINGULARVERB.convert('catch'), equals('catches'));
});
test('handles irregular plural verbs', () {
expect(SINGULARVERB.convert('are'), equals('is'));
expect(SINGULARVERB.convert('were'), equals('was'));
expect(SINGULARVERB.convert('have'), equals('has'));
});
});
}

View file

@ -1,18 +0,0 @@
library inflection3.snake_case.test;
import 'package:inflection3/inflection3.dart';
import 'package:test/test.dart';
void main() {
group('The SnakeCaseEncoder', () {
test('converts phrases to "snake_case"', () {
expect(SNAKE_CASE.convert(''), equals(''));
expect(SNAKE_CASE.convert('CamelCaseName'), equals('camel_case_name'));
expect(SNAKE_CASE.convert('propertyName'), equals('property_name'));
expect(SNAKE_CASE.convert('property'), equals('property'));
expect(SNAKE_CASE.convert('lisp-case'), equals('lisp_case'));
expect(SNAKE_CASE.convert('This is a nice article'),
equals('this_is_a_nice_article'));
});
});
}

View file

@ -1,18 +0,0 @@
library inflection3.spinal_case.test;
import 'package:inflection3/inflection3.dart';
import 'package:test/test.dart';
void main() {
group('The SpinalCaseEncoder', () {
test('converts phrases to "spinal-case"', () {
expect(SPINAL_CASE.convert(''), equals(''));
expect(SPINAL_CASE.convert('CamelCaseName'), equals('camel-case-name'));
expect(SPINAL_CASE.convert('propertyName'), equals('property-name'));
expect(SPINAL_CASE.convert('property'), equals('property'));
expect(SPINAL_CASE.convert('snake_case'), equals('snake-case'));
expect(SPINAL_CASE.convert('This is a nice article'),
equals('this-is-a-nice-article'));
});
});
}

View file

@ -1,71 +0,0 @@
# See https://www.dartlang.org/tools/private-files.html
# Files and directories created by pub
.dart_tool
.packages
.pub/
build/
# If you're building an application, you may want to check-in your pubspec.lock
pubspec.lock
# Directory created by dartdoc
# If you don't generate documentation locally you can remove this line.
doc/api/
### Dart template
# See https://www.dartlang.org/tools/private-files.html
# Files and directories created by pub
# SDK 1.20 and later (no longer creates packages directories)
# Older SDK versions
# (Include if the minimum SDK version specified in pubsepc.yaml is earlier than 1.20)
.project
.buildlog
**/packages/
# Files created by dart2js
# (Most Dart developers will use pub build to compile Dart, use/modify these
# rules if you intend to use dart2js directly
# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
# differentiate from explicit Javascript files)
*.dart.js
*.part.js
*.js.deps
*.js.map
*.info.json
# Directory created by dartdoc
# Don't commit pubspec lock file
# (Library packages only! Remove pattern if developing an application package)
### 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:
## VsCode
.vscode/
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
.idea/
/out/
.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

View file

@ -1,4 +0,0 @@
language: dart
dart:
- dev
- stable

View file

@ -1,12 +0,0 @@
Primary Authors
===============
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
Thomas is the current maintainer of the code base. He has refactored and migrated the
code base to support NNBD.
* __[Tobe O](thosakwe@gmail.com)__
Tobe has written much of the original code prior to NNBD migration. He has moved on and
is no longer involved with the project.

View file

@ -1,25 +0,0 @@
# 4.0.3
* Fixed static analysis errors
# 4.0.2
* Updated pubspec description
* Fixed static analysis errors
# 4.0.1
* Added example
* Updated README
# 4.0.0
* Migrated to support Dart SDK 2.12.x NNBD
# 3.0.0
* Migrated to work with Dart SDK 2.12.x Non NNBD
# 2.0.0-beta+3
* Long-needed updates, ensured Dart 2 compatibility, fixed DDC breakages.
* Patches for reflection bugs with typing.
# 2.0.0-beta+2
* This version breaks in certain Dart versions (likely anything *after* `2.0.0-dev.59.0`)
until https://github.com/dart-lang/sdk/issues/33594 is resolved.
* Removes the reference to `Schema` class.

View file

@ -1,21 +0,0 @@
MIT License (MIT)
Copyright (c) 2021 dukefirehawk.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,112 +0,0 @@
# angel3_json_god
[![version](https://img.shields.io/badge/pub-v4.0.3-brightgreen)](https://pub.dartlang.org/packages/angel3_json_god)
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/json_god/LICENSE)
**DEPRECATED: Replaced by [`belatuk_json_serializer`](https://pub.dartlang.org/packages/belatuk_json_serializer) package**
The ***new and improved*** definitive solution for JSON in Dart. It supports synchronously transform an object into a JSON string and also deserialize a JSON string back into an instance of any type.
## Installation
dependencies:
angel3_json_god: ^4.0.0
## Usage
It is recommended to import the library under an alias, i.e., `god`.
```dart
import 'package:angel3_json_god/angel3_json_god.dart' as god;
```
## Serializing JSON
Simply call `god.serialize(x)` to synchronously transform an object into a JSON
string.
```dart
Map map = {"foo": "bar", "numbers": [1, 2, {"three": 4}]};
// Output: {"foo":"bar","numbers":[1,2,{"three":4]"}
String json = god.serialize(map);
print(json);
```
You can easily serialize classes, too. JSON God also supports classes as members.
```dart
class A {
String foo;
A(this.foo);
}
class B {
late String hello;
late A nested;
B(String hello, String foo) {
this.hello = hello;
this.nested = A(foo);
}
}
main() {
print(god.serialize( B("world", "bar")));
}
// Output: {"hello":"world","nested":{"foo":"bar"}}
```
If a class has a `toJson` method, it will be called instead.
## Deserializing JSON
Deserialization is equally easy, and is provided through `god.deserialize`.
```dart
Map map = god.deserialize('{"hello":"world"}');
int three = god.deserialize("3");
```
### Deserializing to Classes
JSON God lets you deserialize JSON into an instance of any type. Simply pass the
type as the second argument to `god.deserialize`.
If the class has a `fromJson` constructor, it will be called instead.
```dart
class Child {
String foo;
}
class Parent {
String hello;
Child child = Child();
}
main() {
God god = God();
Parent parent = god.deserialize('{"hello":"world","child":{"foo":"bar"}}', Parent);
print(parent);
}
```
**Any JSON-deserializable classes must initializable without parameters.
If `Foo()` would throw an error, then you can't use Foo with JSON.**
This allows for validation of a sort, as only fields you have declared will be
accepted.
```dart
class HasAnInt { int theInt; }
HasAnInt invalid = god.deserialize('["some invalid input"]', HasAnInt);
// Throws an error
```
An exception will be thrown if validation fails.

View file

@ -1,3 +0,0 @@
analyzer:
strong-mode:
implicit-casts: false

View file

@ -1,19 +0,0 @@
import 'package:angel3_json_god/angel3_json_god.dart' as god;
class A {
String foo;
A(this.foo);
}
class B {
late String hello;
late A nested;
B(String hello, String foo) {
this.hello = hello;
this.nested = A(foo);
}
}
void main() {
print(god.serialize(B("world", "bar")));
}

View file

@ -1,18 +0,0 @@
/// A robust library for JSON serialization and deserialization.
library angel3_json_god;
//import 'package:dart2_constant/convert.dart';
import 'dart:convert';
import 'package:logging/logging.dart';
import 'src/reflection.dart' as reflection;
part 'src/serialize.dart';
part 'src/deserialize.dart';
part 'src/validation.dart';
part 'src/util.dart';
/// Instead, listen to [logger].
@deprecated
bool debug = false;
final Logger logger = Logger('json_god');

View file

@ -1,44 +0,0 @@
part of angel3_json_god;
/// Deserializes a JSON string into a Dart datum.
///
/// You can also provide an output Type to attempt to serialize the JSON into.
deserialize(String json, {Type? outputType}) {
var deserialized = deserializeJson(json, outputType: outputType);
logger.info("Deserialization result: $deserialized");
return deserialized;
}
/// Deserializes JSON into data, without validating it.
deserializeJson(String s, {Type? outputType}) {
logger.info("Deserializing the following JSON: $s");
if (outputType == null) {
logger
.info("No output type was specified, so we are just using json.decode");
return json.decode(s);
} else {
logger.info("Now deserializing to type: $outputType");
return deserializeDatum(json.decode(s), outputType: outputType);
}
}
/// Deserializes some JSON-serializable value into a usable Dart value.
deserializeDatum(value, {Type? outputType}) {
if (outputType != null) {
return reflection.deserialize(value, outputType, deserializeDatum);
} else if (value is List) {
logger.info("Deserializing this List: $value");
return value.map(deserializeDatum).toList();
} else if (value is Map) {
logger.info("Deserializing this Map: $value");
Map result = {};
value.forEach((k, v) {
result[k] = deserializeDatum(v);
});
return result;
} else if (_isPrimitive(value)) {
logger.info("Value $value is a primitive");
return value;
}
}

View file

@ -1,189 +0,0 @@
library angel3_json_god.reflection;
import 'dart:mirrors';
import '../angel3_json_god.dart';
const Symbol hashCodeSymbol = #hashCode;
const Symbol runtimeTypeSymbol = #runtimeType;
typedef Serializer(value);
typedef Deserializer(value, {Type? outputType});
List<Symbol> _findGetters(ClassMirror classMirror) {
List<Symbol> result = [];
classMirror.instanceMembers
.forEach((Symbol symbol, MethodMirror methodMirror) {
if (methodMirror.isGetter &&
symbol != hashCodeSymbol &&
symbol != runtimeTypeSymbol) {
logger.info("Found getter on instance: $symbol");
result.add(symbol);
}
});
return result;
}
serialize(value, Serializer serializer, [@deprecated bool debug = false]) {
logger.info("Serializing this value via reflection: $value");
Map result = {};
InstanceMirror instanceMirror = reflect(value);
ClassMirror classMirror = instanceMirror.type;
// Check for toJson
for (Symbol symbol in classMirror.instanceMembers.keys) {
if (symbol == #toJson) {
logger.info("Running toJson...");
var result = instanceMirror.invoke(symbol, []).reflectee;
logger.info("Result of serialization via reflection: $result");
return result;
}
}
for (Symbol symbol in _findGetters(classMirror)) {
String name = MirrorSystem.getName(symbol);
var valueForSymbol = instanceMirror.getField(symbol).reflectee;
try {
result[name] = serializer(valueForSymbol);
logger.info("Set $name to $valueForSymbol");
} catch (e, st) {
logger.severe("Could not set $name to $valueForSymbol", e, st);
}
}
logger.info("Result of serialization via reflection: $result");
return result;
}
deserialize(value, Type outputType, Deserializer deserializer,
[@deprecated bool debug = false]) {
logger.info("About to deserialize $value to a $outputType");
try {
if (value is List) {
List<TypeMirror> typeArguments = reflectType(outputType).typeArguments;
Iterable it;
if (typeArguments.isEmpty) {
it = value.map(deserializer);
} else {
it = value.map((item) =>
deserializer(item, outputType: typeArguments[0].reflectedType));
}
if (typeArguments.isEmpty) return it.toList();
logger.info(
'Casting list elements to ${typeArguments[0].reflectedType} via List.from');
var mirror = reflectType(List, [typeArguments[0].reflectedType]);
if (mirror is ClassMirror) {
var output = mirror.newInstance(#from, [it]).reflectee;
logger.info('Casted list type: ${output.runtimeType}');
return output;
} else {
throw ArgumentError(
'${typeArguments[0].reflectedType} is not a class.');
}
} else if (value is Map) {
return _deserializeFromJsonByReflection(value, deserializer, outputType);
} else {
return deserializer(value);
}
} catch (e, st) {
logger.severe('Deserialization failed.', e, st);
rethrow;
}
}
/// Uses mirrors to deserialize an object.
_deserializeFromJsonByReflection(
data, Deserializer deserializer, Type outputType) {
// Check for fromJson
var typeMirror = reflectType(outputType);
if (typeMirror is! ClassMirror) {
throw ArgumentError('$outputType is not a class.');
}
var type = typeMirror;
var fromJson = Symbol('${MirrorSystem.getName(type.simpleName)}.fromJson');
for (Symbol symbol in type.declarations.keys) {
if (symbol == fromJson) {
var decl = type.declarations[symbol];
if (decl is MethodMirror && decl.isConstructor) {
logger.info("Running fromJson...");
var result = type.newInstance(#fromJson, [data]).reflectee;
logger.info("Result of deserialization via reflection: $result");
return result;
}
}
}
ClassMirror classMirror = type;
InstanceMirror instanceMirror = classMirror.newInstance(Symbol(""), []);
if (classMirror.isSubclassOf(reflectClass(Map))) {
var typeArguments = classMirror.typeArguments;
if (typeArguments.isEmpty ||
classMirror.typeArguments
.every((t) => t == currentMirrorSystem().dynamicType)) {
return data;
} else {
var mapType =
reflectType(Map, typeArguments.map((t) => t.reflectedType).toList())
as ClassMirror;
logger.info('Casting this map $data to Map of [$typeArguments]');
var output = mapType.newInstance(Symbol(''), []).reflectee;
for (var key in data.keys) {
output[key] = data[key];
}
logger.info('Output: $output of type ${output.runtimeType}');
return output;
}
} else {
data.keys.forEach((key) {
try {
logger.info("Now deserializing value for $key");
logger.info("data[\"$key\"] = ${data[key]}");
var deserializedValue = deserializer(data[key]);
logger.info(
"I want to set $key to the following ${deserializedValue.runtimeType}: $deserializedValue");
// Get target type of getter
Symbol searchSymbol = Symbol(key.toString());
Symbol symbolForGetter = classMirror.instanceMembers.keys
.firstWhere((x) => x == searchSymbol);
Type requiredType = classMirror
.instanceMembers[symbolForGetter]!.returnType.reflectedType;
if (data[key].runtimeType != requiredType) {
logger.info("Currently, $key is a ${data[key].runtimeType}.");
logger.info("However, $key must be a $requiredType.");
deserializedValue =
deserializer(deserializedValue, outputType: requiredType);
}
logger.info(
"Final deserialized value for $key: $deserializedValue <${deserializedValue.runtimeType}>");
instanceMirror.setField(Symbol(key.toString()), deserializedValue);
logger.info("Success! $key has been set to $deserializedValue");
} catch (e, st) {
logger.severe('Could not set value for field $key.', e, st);
}
});
}
return instanceMirror.reflectee;
}

View file

@ -1,36 +0,0 @@
part of angel3_json_god;
/// Serializes any arbitrary Dart datum to JSON. Supports schema validation.
String serialize(value) {
var serialized = serializeObject(value);
logger.info('Serialization result: $serialized');
return json.encode(serialized);
}
/// Transforms any Dart datum into a value acceptable to json.encode.
serializeObject(value) {
if (_isPrimitive(value)) {
logger.info("Serializing primitive value: $value");
return value;
} else if (value is DateTime) {
logger.info("Serializing this DateTime: $value");
return value.toIso8601String();
} else if (value is Iterable) {
logger.info("Serializing this Iterable: $value");
return value.map(serializeObject).toList();
} else if (value is Map) {
logger.info("Serializing this Map: $value");
return serializeMap(value);
} else {
return serializeObject(reflection.serialize(value, serializeObject));
}
}
/// Recursively transforms a Map and its children into JSON-serializable data.
Map serializeMap(Map value) {
Map outputMap = {};
value.forEach((key, value) {
outputMap[key] = serializeObject(value);
});
return outputMap;
}

View file

@ -1,5 +0,0 @@
part of angel3_json_god;
bool _isPrimitive(value) {
return value is num || value is bool || value is String || value == null;
}

View file

@ -1,25 +0,0 @@
part of angel3_json_god;
/// Thrown when schema validation fails.
class JsonValidationError implements Exception {
//final Schema schema;
final invalidData;
final String cause;
const JsonValidationError(
this.cause, this.invalidData); //, Schema this.schema);
}
/// Specifies a schema to validate a class with.
class WithSchema {
final Map schema;
const WithSchema(this.schema);
}
/// Specifies a schema to validate a class with.
class WithSchemaUrl {
final String schemaUrl;
const WithSchemaUrl(this.schemaUrl);
}

View file

@ -1,12 +0,0 @@
name: angel3_json_god
version: 4.0.3
description: Easy JSON to Object serialization and deserialization in Dart.
homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/json_god
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
#dart2_constant: ^1.0.0
logging: ^1.0.1
dev_dependencies:
stack_trace: ^1.10.0
test: ^1.17.4

View file

@ -1,115 +0,0 @@
import 'package:angel3_json_god/angel3_json_god.dart' as god;
import 'package:test/test.dart';
import 'shared.dart';
main() {
god.logger.onRecord.listen(printRecord);
group('deserialization', () {
test('deserialize primitives', testDeserializationOfPrimitives);
test('deserialize maps', testDeserializationOfMaps);
test('deserialize maps + reflection',
testDeserializationOfMapsWithReflection);
test('deserialize lists + reflection',
testDeserializationOfListsAsWellAsViaReflection);
test('deserialize with schema validation',
testDeserializationWithSchemaValidation);
});
}
testDeserializationOfPrimitives() {
expect(god.deserialize('1'), equals(1));
expect(god.deserialize('1.4'), equals(1.4));
expect(god.deserialize('"Hi!"'), equals("Hi!"));
expect(god.deserialize("true"), equals(true));
expect(god.deserialize("null"), equals(null));
}
testDeserializationOfMaps() {
String simpleJson =
'{"hello":"world", "one": 1, "class": {"hello": "world"}}';
String nestedJson =
'{"foo": {"bar": "baz", "funny": {"how": "life", "seems": 2, "hate": "us sometimes"}}}';
var simple = god.deserialize(simpleJson) as Map;
var nested = god.deserialize(nestedJson) as Map;
expect(simple['hello'], equals('world'));
expect(simple['one'], equals(1));
expect(simple['class']['hello'], equals('world'));
expect(nested['foo']['bar'], equals('baz'));
expect(nested['foo']['funny']['how'], equals('life'));
expect(nested['foo']['funny']['seems'], equals(2));
expect(nested['foo']['funny']['hate'], equals('us sometimes'));
}
class Pokedex {
Map<String, int>? pokemon;
}
testDeserializationOfMapsWithReflection() {
var s = '{"pokemon": {"Bulbasaur": 1, "Deoxys": 382}}';
var pokedex = god.deserialize(s, outputType: Pokedex) as Pokedex;
expect(pokedex.pokemon, hasLength(2));
expect(pokedex.pokemon!['Bulbasaur'], 1);
expect(pokedex.pokemon!['Deoxys'], 382);
}
testDeserializationOfListsAsWellAsViaReflection() {
String json = '''[
{
"hello": "world",
"nested": []
},
{
"hello": "dolly",
"nested": [
{
"bar": "baz"
},
{
"bar": "fight"
}
]
}
]
''';
var list = god.deserialize(json, outputType: (<SampleClass>[]).runtimeType)
as List<SampleClass>;
SampleClass first = list[0];
SampleClass second = list[1];
expect(list.length, equals(2));
expect(first.hello, equals("world"));
expect(first.nested.length, equals(0));
expect(second.hello, equals("dolly"));
expect(second.nested.length, equals(2));
SampleNestedClass firstNested = second.nested[0];
SampleNestedClass secondNested = second.nested[1];
expect(firstNested.bar, equals("baz"));
expect(secondNested.bar, equals("fight"));
}
testDeserializationWithSchemaValidation() async {
String babelRcJson =
'{"presets":["es2015","stage-0"],"plugins":["add-module-exports"]}';
var deserialized =
god.deserialize(babelRcJson, outputType: BabelRc) as BabelRc;
print(deserialized.presets.runtimeType);
expect(deserialized.presets is List, equals(true));
expect(deserialized.presets.length, equals(2));
expect(deserialized.presets[0], equals('es2015'));
expect(deserialized.presets[1], equals('stage-0'));
expect(deserialized.plugins is List, equals(true));
expect(deserialized.plugins.length, equals(1));
expect(deserialized.plugins[0], equals('add-module-exports'));
}

View file

@ -1,133 +0,0 @@
//import 'package:dart2_constant/convert.dart';
import 'dart:convert';
import 'package:angel3_json_god/angel3_json_god.dart' as god;
import 'package:test/test.dart';
import 'shared.dart';
main() {
god.logger.onRecord.listen(printRecord);
group('serialization', () {
test('serialize primitives', testSerializationOfPrimitives);
test('serialize dates', testSerializationOfDates);
test('serialize maps', testSerializationOfMaps);
test('serialize lists', testSerializationOfLists);
test('serialize via reflection', testSerializationViaReflection);
test('serialize with schema validation',
testSerializationWithSchemaValidation);
});
}
testSerializationOfPrimitives() {
expect(god.serialize(1), equals("1"));
expect(god.serialize(1.4), equals("1.4"));
expect(god.serialize("Hi!"), equals('"Hi!"'));
expect(god.serialize(true), equals("true"));
expect(god.serialize(null), equals("null"));
}
testSerializationOfDates() {
DateTime date = DateTime.now();
String s = god.serialize({'date': date});
print(s);
var deserialized = json.decode(s);
expect(deserialized['date'], equals(date.toIso8601String()));
}
testSerializationOfMaps() {
var simple = json.decode(god
.serialize({'hello': 'world', 'one': 1, 'class': SampleClass('world')}));
var nested = json.decode(god.serialize({
'foo': {
'bar': 'baz',
'funny': {'how': 'life', 'seems': 2, 'hate': 'us sometimes'}
}
}));
expect(simple['hello'], equals('world'));
expect(simple['one'], equals(1));
expect(simple['class']['hello'], equals('world'));
expect(nested['foo']['bar'], equals('baz'));
expect(nested['foo']['funny']['how'], equals('life'));
expect(nested['foo']['funny']['seems'], equals(2));
expect(nested['foo']['funny']['hate'], equals('us sometimes'));
}
testSerializationOfLists() {
List pandorasBox = [
1,
"2",
{"num": 3, "four": SampleClass('five')},
SampleClass('six')..nested.add(SampleNestedClass('seven'))
];
String s = god.serialize(pandorasBox);
print(s);
var deserialized = json.decode(s);
expect(deserialized is List, equals(true));
expect(deserialized.length, equals(4));
expect(deserialized[0], equals(1));
expect(deserialized[1], equals("2"));
expect(deserialized[2] is Map, equals(true));
expect(deserialized[2]['num'], equals(3));
expect(deserialized[2]['four'] is Map, equals(true));
expect(deserialized[2]['four']['hello'], equals('five'));
expect(deserialized[3] is Map, equals(true));
expect(deserialized[3]['hello'], equals('six'));
expect(deserialized[3]['nested'] is List, equals(true));
expect(deserialized[3]['nested'].length, equals(1));
expect(deserialized[3]['nested'][0] is Map, equals(true));
expect(deserialized[3]['nested'][0]['bar'], equals('seven'));
}
testSerializationViaReflection() {
SampleClass sample = SampleClass('world');
for (int i = 0; i < 3; i++) {
sample.nested.add(SampleNestedClass('baz'));
}
String s = god.serialize(sample);
print(s);
var deserialized = json.decode(s);
expect(deserialized['hello'], equals('world'));
expect(deserialized['nested'] is List, equals(true));
expect(deserialized['nested'].length == 3, equals(true));
expect(deserialized['nested'][0]['bar'], equals('baz'));
expect(deserialized['nested'][1]['bar'], equals('baz'));
expect(deserialized['nested'][2]['bar'], equals('baz'));
}
testSerializationWithSchemaValidation() async {
BabelRc babelRc =
BabelRc(presets: ['es2015', 'stage-0'], plugins: ['add-module-exports']);
String s = god.serialize(babelRc);
print(s);
var deserialized = json.decode(s);
expect(deserialized['presets'] is List, equals(true));
expect(deserialized['presets'].length, equals(2));
expect(deserialized['presets'][0], equals('es2015'));
expect(deserialized['presets'][1], equals('stage-0'));
expect(deserialized['plugins'] is List, equals(true));
expect(deserialized['plugins'].length, equals(1));
expect(deserialized['plugins'][0], equals('add-module-exports'));
//Map babelRc2 = {'presets': 'Hello, world!'};
String json2 = god.serialize(babelRc);
print(json2);
}

View file

@ -1,51 +0,0 @@
import 'package:logging/logging.dart';
import 'package:angel3_json_god/angel3_json_god.dart';
import 'package:stack_trace/stack_trace.dart';
void printRecord(LogRecord rec) {
print(rec);
if (rec.error != null) print(rec.error);
if (rec.stackTrace != null) print(Chain.forTrace(rec.stackTrace!).terse);
}
class SampleNestedClass {
String? bar;
SampleNestedClass([String? this.bar]);
}
class SampleClass {
String? hello;
List<SampleNestedClass> nested = [];
SampleClass([String? this.hello]);
}
@WithSchemaUrl(
"http://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/babelrc.json")
class BabelRc {
List<String> presets;
List<String> plugins;
BabelRc(
{List<String> this.presets: const [],
List<String> this.plugins: const []});
}
@WithSchema(const {
r"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Validated Sample Class",
"description": "Sample schema for validation via JSON God",
"type": "object",
"hello": const {"description": "A friendly greeting.", "type": "string"},
"nested": const {
"description": "A list of NestedSampleClass items within this instance.",
"type": "array",
"items": const {
"type": "object",
"bar": const {"description": "Filler text", "type": "string"}
}
},
"required": const ["hello", "nested"]
})
class ValidatedSampleClass {}

View file

@ -1,32 +0,0 @@
import 'package:angel3_json_god/angel3_json_god.dart' as god;
import 'package:test/test.dart';
import 'shared.dart';
main() {
god.logger.onRecord.listen(printRecord);
test('fromJson', () {
var foo = god.deserialize('{"bar":"baz"}', outputType: Foo) as Foo;
expect(foo is Foo, true);
expect(foo.text, equals('baz'));
});
test('toJson', () {
var foo = Foo(text: 'baz');
var data = god.serializeObject(foo);
expect(data, equals({'bar': 'baz', 'foo': 'poobaz'}));
});
}
class Foo {
String? text;
String get foo => 'poo$text';
Foo({this.text});
factory Foo.fromJson(Map json) => Foo(text: json['bar'].toString());
Map toJson() => {'bar': text, 'foo': foo};
}

View file

@ -1,71 +0,0 @@
# See https://www.dartlang.org/tools/private-files.html
# Files and directories created by pub
.dart_tool
.packages
.pub/
build/
# If you're building an application, you may want to check-in your pubspec.lock
pubspec.lock
# Directory created by dartdoc
# If you don't generate documentation locally you can remove this line.
doc/api/
### Dart template
# See https://www.dartlang.org/tools/private-files.html
# Files and directories created by pub
# SDK 1.20 and later (no longer creates packages directories)
# Older SDK versions
# (Include if the minimum SDK version specified in pubsepc.yaml is earlier than 1.20)
.project
.buildlog
**/packages/
# Files created by dart2js
# (Most Dart developers will use pub build to compile Dart, use/modify these
# rules if you intend to use dart2js directly
# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
# differentiate from explicit Javascript files)
*.dart.js
*.part.js
*.js.deps
*.js.map
*.info.json
# Directory created by dartdoc
# Don't commit pubspec lock file
# (Library packages only! Remove pattern if developing an application package)
### 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:
## VsCode
.vscode/
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
.idea/
/out/
.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

View file

@ -1,12 +0,0 @@
Primary Authors
===============
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
Thomas is the current maintainer of the code base. He has refactored and migrated the
code base to support NNBD.
* __[Tobe O](thosakwe@gmail.com)__
Tobe has written much of the original code prior to NNBD migration. He has moved on and
is no longer involved with the project.

View file

@ -1,16 +0,0 @@
# 2.0.2
* Resolve static analysis warnings
# 2.0.1
* Updated README
# 2.0.0
* Migrated to work with Dart SDK 2.12.x NNBD
# 1.0.2
* Add an example, for Pub's sake.
# 1.0.1
* Add a specific constraint on Dart versions, to prevent Pub from rejecting all packages that depend on
`merge_map` (the entire Angel framework).
* Add generic type support

View file

@ -1,21 +0,0 @@
MIT License (MIT)
Copyright (c) 2021 dukefirehawk.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,28 +0,0 @@
# angel3_merge_map
[![version](https://img.shields.io/badge/pub-v2.0.2-brightgreen)](https://pub.dartlang.org/packages/angel3_merge_map)
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/merge_map/LICENSE)
**DEPRECATED: Replaced by [`belatuk_merge_map`](https://pub.dartlang.org/packages/belatuk_merge_map) package**
Combine multiple Maps into one. Equivalent to
[Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
in JS.
# Example
```dart
import "package:angel3_merge_map/angel3_merge_map.dart";
void main() {
Map map1 = {'hello': 'world'};
Map map2 = {'foo': {'bar': 'baz', 'this': 'will be overwritten'}};
Map map3 = {'foo': {'john': 'doe', 'this': 'overrides previous maps'}};
Map merged = mergeMap(map1, map2, map3);
// {hello: world, foo: {bar: baz, john: doe, this: overrides previous maps}}
}
```

View file

@ -1,4 +0,0 @@
include: package:pedantic/analysis_options.yaml
analyzer:
strong-mode:
implicit-casts: false

View file

@ -1,20 +0,0 @@
import 'package:angel3_merge_map/angel3_merge_map.dart';
void main() {
// ignore: omit_local_variable_types
Map map1 = {'hello': 'world'};
// ignore: omit_local_variable_types
Map map2 = {
'foo': {'bar': 'baz', 'this': 'will be overwritten'}
};
// ignore: omit_local_variable_types
Map map3 = {
'foo': {'john': 'doe', 'this': 'overrides previous maps'}
};
var merged = mergeMap([map1, map2, map3]);
print(merged);
// {hello: world, foo: {bar: baz, john: doe, this: overrides previous maps}}
}

View file

@ -1,34 +0,0 @@
/// Exposes the [mergeMap] function, which... merges Maps.
library angel3_merge_map;
dynamic _copyValues<K, V>(
Map<K, V> from, Map<K, V?>? to, bool recursive, bool acceptNull) {
for (var key in from.keys) {
if (from[key] is Map<K, V> && recursive) {
if (!(to![key] is Map<K, V>)) {
to[key] = <K, V>{} as V;
}
_copyValues(from[key] as Map, to[key] as Map?, recursive, acceptNull);
} else {
if (from[key] != null || acceptNull) to![key] = from[key];
}
}
}
/// Merges the values of the given maps together.
///
/// `recursive` is set to `true` by default. If set to `true`,
/// then nested maps will also be merged. Otherwise, nested maps
/// will overwrite others.
///
/// `acceptNull` is set to `false` by default. If set to `false`,
/// then if the value on a map is `null`, it will be ignored, and
/// that `null` will not be copied.
Map<K, V> mergeMap<K, V>(Iterable<Map<K, V>> maps,
{bool recursive = true, bool acceptNull = false}) {
var result = <K, V>{};
maps.forEach((Map<K, V> map) {
_copyValues(map, result, recursive, acceptNull);
});
return result;
}

View file

@ -1,9 +0,0 @@
name: angel3_merge_map
version: 2.0.2
description: Combine multiple Maps into one. Equivalent to Object.assign in JS.
homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/merge_map
environment:
sdk: '>=2.12.0 <3.0.0'
dev_dependencies:
test: ^1.17.4
pedantic: ^1.11.0

View file

@ -1,104 +0,0 @@
import 'package:angel3_merge_map/angel3_merge_map.dart';
import 'package:test/test.dart';
void main() {
test('can merge two simple maps', () {
var merged = mergeMap([
{'hello': 'world'},
{'hello': 'dolly'}
]);
expect(merged['hello'], equals('dolly'));
});
test("the last map's values supersede those of prior", () {
var merged = mergeMap([
{'letter': 'a'},
{'letter': 'b'},
{'letter': 'c'}
]);
expect(merged['letter'], equals('c'));
});
test('can merge two once-nested maps', () {
// ignore: omit_local_variable_types
Map map1 = {
'hello': 'world',
'foo': {'nested': false}
};
// ignore: omit_local_variable_types
Map map2 = {
'goodbye': 'sad life',
'foo': {'nested': true, 'it': 'works'}
};
var merged = mergeMap([map1, map2]);
expect(merged['hello'], equals('world'));
expect(merged['goodbye'], equals('sad life'));
expect(merged['foo']['nested'], equals(true));
expect(merged['foo']['it'], equals('works'));
});
test('once-nested map supersession', () {
// ignore: omit_local_variable_types
Map map1 = {
'hello': 'world',
'foo': {'nested': false}
};
// ignore: omit_local_variable_types
Map map2 = {
'goodbye': 'sad life',
'foo': {'nested': true, 'it': 'works'}
};
// ignore: omit_local_variable_types
Map map3 = {
'foo': {'nested': 'supersession'}
};
var merged = mergeMap([map1, map2, map3]);
expect(merged['foo']['nested'], equals('supersession'));
});
test('can merge two twice-nested maps', () {
// ignore: omit_local_variable_types
Map map1 = {
'a': {
'b': {'c': 'd'}
}
};
// ignore: omit_local_variable_types
Map map2 = {
'a': {
'b': {'c': 'D', 'e': 'f'}
}
};
var merged = mergeMap([map1, map2]);
expect(merged['a']['b']['c'], equals('D'));
expect(merged['a']['b']['e'], equals('f'));
});
test('twice-nested map supersession', () {
// ignore: omit_local_variable_types
Map map1 = {
'a': {
'b': {'c': 'd'}
}
};
// ignore: omit_local_variable_types
Map map2 = {
'a': {
'b': {'c': 'D', 'e': 'f'}
}
};
// ignore: omit_local_variable_types
Map map3 = {
'a': {
'b': {'e': 'supersession'}
}
};
var merged = mergeMap([map1, map2, map3]);
expect(merged['a']['b']['c'], equals('D'));
expect(merged['a']['b']['e'], equals('supersession'));
});
}

View file

@ -82,7 +82,6 @@ class EmployeeQuery extends Query<Employee, EmployeeQueryWhere> {
if (row.every((x) => x == null)) return null;
var model = Employee(
id: row[0].toString(),
error: (row[1] as String?),
createdAt: (row[2] as DateTime?),
updatedAt: (row[3] as DateTime?),
uniqueId: (row[4] as String?),
@ -187,7 +186,6 @@ class EmployeeQueryValues extends MapQueryValues {
set salary(double? value) => values['salary'] = value.toString();
void copyFrom(Employee model) {
error = model.error;
createdAt = model.createdAt;
updatedAt = model.updatedAt;
uniqueId = model.uniqueId;
@ -205,7 +203,6 @@ class EmployeeQueryValues extends MapQueryValues {
class Employee extends _Employee {
Employee(
{this.id,
this.error,
this.createdAt,
this.updatedAt,
this.uniqueId,
@ -217,9 +214,6 @@ class Employee extends _Employee {
@override
String? id;
@override
String? error;
/// The time at which this item was created.
@override
DateTime? createdAt;
@ -251,7 +245,6 @@ class Employee extends _Employee {
double? salary}) {
return Employee(
id: id ?? this.id,
error: error ?? this.error,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
uniqueId: uniqueId ?? this.uniqueId,
@ -264,7 +257,6 @@ class Employee extends _Employee {
bool operator ==(other) {
return other is _Employee &&
other.id == id &&
other.error == error &&
other.createdAt == createdAt &&
other.updatedAt == updatedAt &&
other.uniqueId == uniqueId &&
@ -275,21 +267,13 @@ class Employee extends _Employee {
@override
int get hashCode {
return hashObjects([
id,
error,
createdAt,
updatedAt,
uniqueId,
firstName,
lastName,
salary
]);
return hashObjects(
[id, createdAt, updatedAt, uniqueId, firstName, lastName, salary]);
}
@override
String toString() {
return 'Employee(id=$id, error=$error, createdAt=$createdAt, updatedAt=$updatedAt, uniqueId=$uniqueId, firstName=$firstName, lastName=$lastName, salary=$salary)';
return 'Employee(id=$id, createdAt=$createdAt, updatedAt=$updatedAt, uniqueId=$uniqueId, firstName=$firstName, lastName=$lastName, salary=$salary)';
}
Map<String, dynamic> toJson() {
@ -327,7 +311,6 @@ class EmployeeSerializer extends Codec<Employee, Map> {
static Employee fromMap(Map map) {
return Employee(
id: map['id'] as String,
error: map['error'] as String,
createdAt: map['created_at'] != null
? (map['created_at'] is DateTime
? (map['created_at'] as DateTime)
@ -347,7 +330,6 @@ class EmployeeSerializer extends Codec<Employee, Map> {
static Map<String, dynamic> toMap(_Employee model) {
return {
'id': model.id,
'error': model.error,
'created_at': model.createdAt?.toIso8601String(),
'updated_at': model.updatedAt?.toIso8601String(),
'unique_id': model.uniqueId,

View file

@ -1,12 +0,0 @@
Primary Authors
===============
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
Thomas is the current maintainer of the code base. He has refactored and migrated the
code base to support NNBD.
* __[Tobe O](thosakwe@gmail.com)__
Tobe has written much of the original code prior to NNBD migration. He has moved on and
is no longer involved with the project.

View file

@ -1,16 +0,0 @@
# 3.0.3
* Updated README
# 3.0.2
* Updated README
# 3.0.1
* Fixed invalid homepage url in pubspec.yaml
# 3.0.0
* Migrated to support Dart SDK 2.12.x NNBD
# 2.0.0
* Migrated to work with Dart SDK 2.12.x Non NNBD
# 1.0.0
* Initial release.

View file

@ -1,21 +0,0 @@
MIT License (MIT)
Copyright (c) 2021 dukefirehawk.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,42 +0,0 @@
# Angel3 Petty Logging
[![version](https://img.shields.io/badge/pub-v3.0.3-brightgreen)](https://pub.dartlang.org/packages/angel3_pretty_logging)
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/pretty_logging/LICENSE)
**DEPRECATED: Replaced by [`belatuk_pretty_logging`](https://pub.dartlang.org/packages/belatuk_pretty_logging) package**
Standalone helper for colorful logging output, using pkg:io AnsiCode.
## Installation
In your `pubspec.yaml`:
```yaml
dependencies:
angel3_pretty_logging: ^3.0.0
```
## Usage
Basic usage is very simple:
```dart
myLogger.onRecord.listen(prettyLog);
```
However, you can conditionally pass logic to omit printing an error, provide colors, or to provide a custom print function:
```dart
var pretty = prettyLog(
logColorChooser: (_) => red,
printFunction: stderr.writeln,
omitError: (r) {
var err = r.error;
return err is AngelHttpException && err.statusCode != 500;
},
);
myLogger.onRecord.listen(pretty);
```

View file

@ -1,4 +0,0 @@
include: package:pedantic/analysis_options.yaml
analyzer:
strong-mode:
implicit-casts: false

View file

@ -1,11 +0,0 @@
import 'package:logging/logging.dart';
import 'package:angel3_pretty_logging/angel3_pretty_logging.dart';
void main() {
Logger.root
..level = Level.ALL
..onRecord.listen(prettyLog)
..info('Hey!')
..finest('Bye!')
..severe('Oops!', StateError('Wrong!'), StackTrace.current);
}

View file

@ -1,50 +0,0 @@
import 'package:logging/logging.dart';
import 'package:io/ansi.dart';
/// Prints the contents of a [LogRecord] with pretty colors.
///
/// By passing [omitError], you can omit printing the error of a given
/// [LogRecord].
///
/// You can also pass a custom [printFunction] or [logColorChooser].
void prettyLog(LogRecord record,
{bool Function(LogRecord)? omitError,
void Function(String)? printFunction,
AnsiCode Function(Level)? logColorChooser}) {
logColorChooser ??= chooseLogColor;
omitError ??= (_) => false;
printFunction ??= print;
var code = logColorChooser(record.level);
if (record.error == null) printFunction(code.wrap(record.toString())!);
if (record.error != null) {
var err = record.error;
if (omitError(record)) return;
printFunction(code.wrap(record.toString() + '\n')!);
printFunction(code.wrap(err.toString())!);
if (record.stackTrace != null) {
printFunction(code.wrap(record.stackTrace.toString())!);
}
}
}
/// Chooses a color based on the logger [level].
AnsiCode chooseLogColor(Level level) {
if (level == Level.SHOUT) {
return backgroundRed;
} else if (level == Level.SEVERE) {
return red;
} else if (level == Level.WARNING) {
return yellow;
} else if (level == Level.INFO) {
return cyan;
} else if (level == Level.CONFIG ||
level == Level.FINE ||
level == Level.FINER ||
level == Level.FINEST) {
return lightGray;
}
return resetAll;
}

View file

@ -1,11 +0,0 @@
name: angel3_pretty_logging
version: 3.0.3
description: Standalone helper for colorful logging output, using pkg:io AnsiCode.
homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/pretty_logging
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
io: ^1.0.0
logging: ^1.0.1
dev_dependencies:
pedantic: ^1.11.0

View file

@ -1 +0,0 @@
language: dart

View file

@ -1,12 +0,0 @@
Primary Authors
===============
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
Thomas is the current maintainer of the code base. He has refactored and migrated the
code base to support NNBD.
* __[Tobe O](thosakwe@gmail.com)__
Tobe has written much of the original code prior to NNBD migration. He has moved on and
is no longer involved with the project.

View file

@ -1,34 +0,0 @@
# Change Log
## 3.0.3
* Final release. Replaced by `belatuk_pub_sub` package.
## 3.0.2
* Resolved static analysis warnings
## 3.0.1
* Resolved static analysis warnings
## 3.0.0
* Migrated to work with Dart SDK 2.12.x NNBD
## 2.3.0
* Allow `2.x` versions of `stream_channel`.
* Apply `package:pedantic` lints.
## 2.2.0
* Upgrade `uuid`.
## 2.1.0
* Allow for "trusted clients," which are implicitly-registered clients. This makes using `package:pub_sub` easier, as well making it easier to scale.
## 2.0.0
* Dart 2 updates.

View file

@ -1,21 +0,0 @@
MIT License (MIT)
Copyright (c) 2021 dukefirehawk.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,234 +0,0 @@
# Angel3 Pub Sub
[![version](https://img.shields.io/badge/pub-v3.0.3-brightgreen)](https://pub.dartlang.org/packages/angel3_pub_sub)
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/pub_sub/LICENSE)
**DEPRECATED: Replaced by [`belatuk_pub_sub`](https://pub.dartlang.org/packages/belatuk_pub_sub) package**
Keep application instances in sync with a simple pub/sub API.
# Installation
Add `angel3_pub_sub` as a dependency in your `pubspec.yaml` file:
```yaml
dependencies:
angel3_pub_sub: ^3.0.0
```
Then, be sure to run `pub get` in your terminal.
# Usage
`pub_sub` is your typical pub/sub API. However, `angel3_pub_sub` enforces authentication of every
request. It is very possible that `angel3_pub_sub` will run on both servers and in the browser,
or on a platform angel3_pub_sublike Flutter. Thus, there are provisions available to limit
access.
**Be careful to not leak any `angel3_pub_sub` client ID's if operating over a network.**
If you do, you risk malicious users injecting events into your application, which
could ultimately spell *disaster*.
A `angel3_pub_sub` server can operate across multiple *adapters*, which take care of interfacing data over different
media. For example, a single server can handle pub/sub between multiple Isolates and TCP Sockets, as well as
WebSockets, simultaneously.
```dart
import 'package:angel3_pub_sub/angel3_pub_sub.dart' as pub_sub;
main() async {
var server = pub_sub.Server([
FooAdapter(...),
BarAdapter(...)
]);
server.addAdapter( BazAdapter(...));
// Call `start` to activate adapters, and begin handling requests.
server.start();
}
```
### Trusted Clients
You can use `package:angel3_pub_sub` without explicitly registering
clients, *if and only if* those clients come from trusted sources.
Clients via `Isolate` are always trusted.
Clients via `package:json_rpc_2` must be explicitly marked
as trusted (i.e. using an IP whitelist mechanism):
```dart
JsonRpc2Adapter(..., isTrusted: false);
// Pass `null` as Client ID when trusted...
pub_sub.IsolateClient(null);
```
### Access Control
The ID's of all *untrusted* clients who will connect to the server must be known at start-up time.
You may not register new clients after the server has started. This is mostly a security consideration;
if it is impossible to register new clients, then malicious users cannot grant themselves additional
privileges within the system.
```dart
import 'package:angel3_pub_sub/angel3_pub_sub.dart' as pub_sub;
main() async {
// ...
server.registerClient(const ClientInfo('<client-id>'));
// Create a user who can subscribe, but not publish.
server.registerClient(const ClientInfo('<client-id>', canPublish: false));
// Create a user who can publish, but not subscribe.
server.registerClient(const ClientInfo('<client-id>', canSubscribe: false));
// Create a user with no privileges whatsoever.
server.registerClient(const ClientInfo('<client-id>', canPublish: false, canSubscribe: false));
server.start();
}
```
## Isolates
If you are just running multiple instances of a server,
use `package:angel3_pub_sub/isolate.dart`.
You'll need one isolate to be the master. Typically this is the first isolate you create.
```dart
import 'dart:io';
import 'dart:isolate';
import 'package:angel3_pub_sub/isolate.dart' as pub_sub;
import 'package:angel3_pub_sub/angel3_pub_sub.dart' as pub_sub;
void main() async {
// Easily bring up a server.
var adapter = pub_sub.IsolateAdapter();
var server = pub_sub.Server([adapter]);
// You then need to create a client that will connect to the adapter.
// Each isolate in your application should contain a client.
for (int i = 0; i < Platform.numberOfProcessors - 1; i++) {
server.registerClient(pub_sub.ClientInfo('client$i'));
}
// Start the server.
server.start();
// Next, let's start isolates that interact with the server.
//
// Fortunately, we can send SendPorts over Isolates, so this is no hassle.
for (int i = 0; i < Platform.numberOfProcessors - 1; i++)
Isolate.spawn(isolateMain, [i, adapter.receivePort.sendPort]);
// It's possible that you're running your application in the server isolate as well:
isolateMain([0, adapter.receivePort.sendPort]);
}
void isolateMain(List args) {
var client =
pub_sub.IsolateClient('client${args[0]}', args[1] as SendPort);
// The client will connect automatically. In the meantime, we can start subscribing to events.
client.subscribe('user::logged_in').then((sub) {
// The `ClientSubscription` class extends `Stream`. Hooray for asynchrony!
sub.listen((msg) {
print('Logged in: $msg');
});
});
}
```
## JSON RPC 2.0
If you are not running on isolates, you need to import
`package:angel3_pub_sub/json_rpc_2.dart`. This library leverages `package:json_rpc_2` and
`package:stream_channel` to create clients and servers that can hypothetically run on any
medium, i.e. WebSockets, or TCP Sockets.
Check out `test/json_rpc_2_test.dart` for an example of serving `angel3_pub_sub` over TCP sockets.
# Protocol
`angel3_pub_sub` is built upon a simple RPC, and this package includes
an implementation that runs via `SendPort`s and `ReceivePort`s, as
well as one that runs on any `StreamChannel<String>`.
Data sent over the wire looks like the following:
```typescript
// Sent by a client to initiate an exchange.
interface Request {
// This is an arbitrary string, assigned by your client, but in every case,
// the client uses this to match your requests with asynchronous responses.
request_id: string,
// The ID of the client to authenticate as.
//
// As you can imagine, this should be kept secret, to prevent breaches.
client_id: string,
// Required for *every* request.
params: {
// A value to be `publish`ed.
value?: any,
// The name of an event to `publish`.
event_name?: string,
// The ID of a subscription to be cancelled.
subscription_id?: string
}
}
/// Sent by the server in response to a request.
interface Response {
// `true` for success, `false` for failures.
status: boolean,
// Only appears if `status` is `false`; explains why an operation failed.
error_message?: string,
// Matches the request_id sent by the client.
request_id: string,
result?: {
// The number of other clients to whom an event was `publish`ed.
listeners:? number,
// The ID of a created subscription.
subscription_id?: string
}
}
```
When sending via JSON_RPC 2.0, the `params` of a `Request` are simply folded into the object
itself, for simplicity's sake. In this case, a response will be sent as a notification whose
name is the `request_id`.
In the case of Isolate clients/servers, events will be simply sent as Lists:
```dart
['<event-name>', value]
```
Clients can send the following (3) methods:
* `subscribe` (`event_name`:string): Subscribe to an event.
* `unsubscribe` (`subscription_id`:string): Unsubscribe from an event you previously subscribed to.
* `publish` (`event_name`:string, `value`:any): Publish an event to all other clients who are subscribed.
The client and server in `package:angel3_pub_sub/isolate.dart` must make extra
provisions to keep track of client ID's. Since `SendPort`s and `ReceivePort`s
do not have any sort of guaranteed-unique ID's, new clients must send their
`SendPort` to the server before sending any requests. The server then responds
with an `id` that must be used to identify a `SendPort` to send a response to.

View file

@ -1,4 +0,0 @@
include: package:pedantic/analysis_options.yaml
analyzer:
strong-mode:
implicit-casts: false

View file

@ -1,44 +0,0 @@
import 'dart:io';
import 'dart:isolate';
import 'package:angel3_pub_sub/isolate.dart' as pub_sub;
import 'package:angel3_pub_sub/angel3_pub_sub.dart' as pub_sub;
void main() async {
// Easily bring up a server.
var adapter = pub_sub.IsolateAdapter();
var server = pub_sub.Server([adapter]);
// You then need to create a client that will connect to the adapter.
// Every untrusted client in your application should be pre-registered.
//
// In the case of Isolates, however, those are always implicitly trusted.
for (var i = 0; i < Platform.numberOfProcessors - 1; i++) {
server.registerClient(pub_sub.ClientInfo('client$i'));
}
// Start the server.
server.start();
// Next, let's start isolates that interact with the server.
//
// Fortunately, we can send SendPorts over Isolates, so this is no hassle.
for (var i = 0; i < Platform.numberOfProcessors - 1; i++) {
await Isolate.spawn(isolateMain, [i, adapter.receivePort.sendPort]);
}
// It's possible that you're running your application in the server isolate as well:
isolateMain([0, adapter.receivePort.sendPort]);
}
void isolateMain(List args) {
// Isolates are always trusted, so technically we don't need to pass a client iD.
var client = pub_sub.IsolateClient('client${args[0]}', args[1] as SendPort);
// The client will connect automatically. In the meantime, we can start subscribing to events.
client.subscribe('user::logged_in').then((sub) {
// The `ClientSubscription` class extends `Stream`. Hooray for asynchrony!
sub.listen((msg) {
print('Logged in: $msg');
});
});
}

View file

@ -1 +0,0 @@
export 'src/protocol/protocol.dart';

View file

@ -1,2 +0,0 @@
export 'src/isolate/client.dart';
export 'src/isolate/server.dart';

Some files were not shown because too many files have changed in this diff Show more