1.0.0-dev!
This commit is contained in:
parent
267b696150
commit
80c6118544
15 changed files with 60 additions and 39 deletions
|
@ -1,2 +1,5 @@
|
|||
# angel_framework
|
||||
Documentation in the works.
|
||||
|
||||
![version 1.0.0-dev](https://img.shields.io/badge/version-1.0.0--dev-red.svg)
|
||||
|
||||
Core libraries for the Angel Framework.
|
|
@ -1,4 +1,4 @@
|
|||
/// A controller-based MVC + WebSocket framework in Dart.
|
||||
/// An easily-extensible web server framework in Dart.
|
||||
library angel_framework;
|
||||
|
||||
export 'src/http/http.dart';
|
|
@ -1,8 +1,11 @@
|
|||
part of angel_framework.http;
|
||||
|
||||
class _AngelHttpExceptionBase implements Exception {
|
||||
/// An HTTP status code this exception will throw.
|
||||
int statusCode;
|
||||
/// The cause of this exception.
|
||||
String message;
|
||||
/// A list of errors that occurred when this exception was thrown.
|
||||
List<String> errors;
|
||||
|
||||
_AngelHttpExceptionBase.base() {}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
part of angel_framework.http;
|
||||
|
||||
/// Basically an Expando whoops
|
||||
/// Supports accessing members of a Map as though they were actual members.
|
||||
class Extensible {
|
||||
/// A set of custom properties that can be assigned to the server.
|
||||
///
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
part of angel_framework.http;
|
||||
|
||||
/// Maps the given middleware(s) onto this handler.
|
||||
/// Annotation to map middleware onto a handler.
|
||||
class Middleware {
|
||||
final List handlers;
|
||||
|
||||
const Middleware(List this.handlers);
|
||||
}
|
||||
|
||||
/// This service will send an event after every action.
|
||||
/// Annotation to set a service up to release hooks on every action.
|
||||
class Hooked {
|
||||
const Hooked();
|
||||
}
|
|
@ -1,15 +1,13 @@
|
|||
part of angel_framework.http;
|
||||
|
||||
/// A function that asynchronously generates a view from the given path and data.
|
||||
typedef Future<String> ViewGenerator(String path, {Map data});
|
||||
typedef Future<String> ViewGenerator(String path, [Map data]);
|
||||
|
||||
/// A convenience wrapper around an outgoing HTTP request.
|
||||
class ResponseContext extends Extensible {
|
||||
/// The [Angel] instance that is sending a response.
|
||||
Angel app;
|
||||
|
||||
God god = new God();
|
||||
|
||||
/// Can we still write to this response?
|
||||
bool isOpen = true;
|
||||
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
part of angel_framework.http;
|
||||
|
||||
/// Represents an endpoint open for connection via the Internet.
|
||||
class Route {
|
||||
/// A regular expression used to match URI's to this route.
|
||||
RegExp matcher;
|
||||
/// The HTTP method this route responds to.
|
||||
String method;
|
||||
/// An array of functions, Futures and objects that can respond to this route.
|
||||
List handlers = [];
|
||||
/// The path this route is mounted on.
|
||||
String path;
|
||||
/// (Optional) - A name for this route.
|
||||
String name;
|
||||
|
||||
Route(String method, Pattern path, [List handlers]) {
|
||||
|
@ -35,6 +41,7 @@ class Route {
|
|||
return this;
|
||||
}
|
||||
|
||||
/// Generates a URI to this route with the given parameters.
|
||||
String makeUri([Map<String, dynamic> params]) {
|
||||
String result = path;
|
||||
if (params != null) {
|
||||
|
@ -46,6 +53,7 @@ class Route {
|
|||
return result;
|
||||
}
|
||||
|
||||
/// Enables one or more handlers to be called whenever this route is visited.
|
||||
Route middleware(handler) {
|
||||
if (handler is Iterable)
|
||||
handlers.addAll(handler);
|
||||
|
@ -53,7 +61,8 @@ class Route {
|
|||
return this;
|
||||
}
|
||||
|
||||
parseParameters(String requestPath) {
|
||||
/// Extracts route parameters from a given path.
|
||||
Map parseParameters(String requestPath) {
|
||||
Map result = {};
|
||||
|
||||
Iterable<String> values = _parseParameters(requestPath);
|
||||
|
|
|
@ -16,7 +16,7 @@ class Angel extends Routable {
|
|||
(address, port) async => await HttpServer.bind(address, port);
|
||||
|
||||
/// Default error handler, show HTML error page
|
||||
var _errorHandler = (AngelHttpException e, req, ResponseContext res) {
|
||||
AngelErrorHandler _errorHandler = (AngelHttpException e, req, ResponseContext res) {
|
||||
res.header(HttpHeaders.CONTENT_TYPE, ContentType.HTML.toString());
|
||||
res.status(e.statusCode);
|
||||
res.write("<DOCTYPE html><html><head><title>${e.message}</title>");
|
||||
|
@ -28,8 +28,10 @@ class Angel extends Routable {
|
|||
res.end();
|
||||
};
|
||||
|
||||
var viewGenerator =
|
||||
(String view, [Map data]) => "No view engine has been configured yet.";
|
||||
/// A function that renders views.
|
||||
///
|
||||
/// Called by [ResponseContext]@`render`.
|
||||
ViewGenerator viewGenerator = (String view, [Map data]) async => "No view engine has been configured yet.";
|
||||
|
||||
/// [RequestMiddleware] to be run before all requests.
|
||||
List before = [];
|
||||
|
@ -37,8 +39,12 @@ class Angel extends Routable {
|
|||
/// [RequestMiddleware] to be run after all requests.
|
||||
List after = [];
|
||||
|
||||
/// The native HttpServer running this instancce.
|
||||
HttpServer httpServer;
|
||||
|
||||
/// Starts the server.
|
||||
///
|
||||
/// Returns false on failure; otherwise, returns the HttpServer.
|
||||
startServer(InternetAddress address, int port) async {
|
||||
var server =
|
||||
await _serverGenerator(address ?? InternetAddress.LOOPBACK_IP_V4, port);
|
||||
|
@ -193,7 +199,8 @@ class Angel extends Routable {
|
|||
hooked: hooked, middlewareNamespace: middlewareNamespace);
|
||||
}
|
||||
|
||||
onError(handler) {
|
||||
/// Registers a callback to run upon errors.
|
||||
onError(AngelErrorHandler handler) {
|
||||
_errorHandler = handler;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,28 @@
|
|||
part of angel_framework.http;
|
||||
|
||||
/// Indicates how the service was accessed.
|
||||
///
|
||||
/// This will be passed to the `params` object in a service method.
|
||||
/// When requested on the server side, this will be null.
|
||||
class Providers {
|
||||
/// The transport through which the client is accessing this service.
|
||||
final String via;
|
||||
|
||||
const Providers._base(String this.via);
|
||||
const Providers(String this.via);
|
||||
|
||||
static final Providers SERVER = const Providers._base('server_side');
|
||||
static final Providers REST = const Providers._base('rest');
|
||||
static final Providers WEBSOCKET = const Providers._base('websocket');
|
||||
static const String VIA_REST = "rest";
|
||||
static const String VIA_WEBSOCKET = "websocket";
|
||||
|
||||
/// Represents a request via REST.
|
||||
static final Providers REST = const Providers(VIA_REST);
|
||||
|
||||
/// Represents a request over WebSockets.
|
||||
static final Providers WEBSOCKET = const Providers(VIA_WEBSOCKET);
|
||||
}
|
||||
|
||||
/// A data store exposed to the Internet.
|
||||
/// A front-facing interface that can present data to and operate on data on behalf of the user.
|
||||
///
|
||||
/// Heavily inspired by FeathersJS. <3
|
||||
class Service extends Routable {
|
||||
/// The [Angel] app powering this service.
|
||||
Angel app;
|
||||
|
|
|
@ -2,6 +2,7 @@ part of angel_framework.http;
|
|||
|
||||
/// Wraps another service in a service that broadcasts events on actions.
|
||||
class HookedService extends Service {
|
||||
/// Tbe service that is proxied by this hooked one.
|
||||
final Service inner;
|
||||
|
||||
HookedServiceEventDispatcher beforeIndexed =
|
||||
|
|
|
@ -2,12 +2,11 @@ part of angel_framework.http;
|
|||
|
||||
/// An in-memory [Service].
|
||||
class MemoryService<T> extends Service {
|
||||
God god = new God();
|
||||
Map <int, T> items = {};
|
||||
|
||||
_makeJson(int index, T t) {
|
||||
if (T == null || T == Map)
|
||||
return mergeMap([god.serializeToMap(t), {'id': index}]);
|
||||
return mergeMap([god.serializeObject(t), {'id': index}]);
|
||||
else
|
||||
return t;
|
||||
}
|
||||
|
@ -32,7 +31,7 @@ class MemoryService<T> extends Service {
|
|||
Future create(data, [Map params]) async {
|
||||
//try {
|
||||
print("Data: $data");
|
||||
var created = (data is Map) ? god.deserializeFromMap(data, T) : data;
|
||||
var created = (data is Map) ? god.deserializeDatum(data, outputType: T) : data;
|
||||
print("Created $created");
|
||||
items[items.length] = created;
|
||||
return _makeJson(items.length - 1, created);
|
||||
|
@ -45,10 +44,10 @@ class MemoryService<T> extends Service {
|
|||
int desiredId = int.parse(id.toString());
|
||||
if (items.containsKey(desiredId)) {
|
||||
try {
|
||||
Map existing = god.serializeToMap(items[desiredId]);
|
||||
Map existing = god.serializeObject(items[desiredId]);
|
||||
data = mergeMap([existing, data]);
|
||||
items[desiredId] =
|
||||
(data is Map) ? god.deserializeFromMap(data, T) : data;
|
||||
(data is Map) ? god.deserializeDatum(data, outputType: T) : data;
|
||||
return _makeJson(desiredId, items[desiredId]);
|
||||
} catch (e) {
|
||||
throw new AngelHttpException.BadRequest(message: 'Invalid data.');
|
||||
|
@ -61,7 +60,7 @@ class MemoryService<T> extends Service {
|
|||
if (items.containsKey(desiredId)) {
|
||||
try {
|
||||
items[desiredId] =
|
||||
(data is Map) ? god.deserializeFromMap(data, T) : data;
|
||||
(data is Map) ? god.deserializeDatum(data, outputType: T) : data;
|
||||
return _makeJson(desiredId, items[desiredId]);
|
||||
} catch (e) {
|
||||
throw new AngelHttpException.BadRequest(message: 'Invalid data.');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: angel_framework
|
||||
version: 0.0.0-dev.19
|
||||
version: 1.0.0-dev
|
||||
description: Core libraries for the Angel framework.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/angel_framework
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'dart:mirrors';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:json_god/json_god.dart';
|
||||
import 'package:json_god/json_god.dart' as god;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class Todo {
|
||||
|
@ -18,13 +17,11 @@ main() {
|
|||
Angel app;
|
||||
String url;
|
||||
http.Client client;
|
||||
God god;
|
||||
HookedService Todos;
|
||||
|
||||
setUp(() async {
|
||||
app = new Angel();
|
||||
client = new http.Client();
|
||||
god = new God();
|
||||
app.use('/todos', new MemoryService<Todo>());
|
||||
Todos = app.service("todos");
|
||||
|
||||
|
@ -37,7 +34,6 @@ main() {
|
|||
url = null;
|
||||
client.close();
|
||||
client = null;
|
||||
god = null;
|
||||
Todos = null;
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'dart:io';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:json_god/json_god.dart';
|
||||
import 'package:json_god/json_god.dart' as god;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
@Middleware(const ['interceptor'])
|
||||
|
@ -16,10 +16,8 @@ main() {
|
|||
Angel todos;
|
||||
String url;
|
||||
http.Client client;
|
||||
God god;
|
||||
|
||||
setUp(() async {
|
||||
god = new God();
|
||||
angel = new Angel();
|
||||
nested = new Angel();
|
||||
todos = new Angel();
|
||||
|
@ -58,7 +56,6 @@ main() {
|
|||
client.close();
|
||||
client = null;
|
||||
url = null;
|
||||
god = null;
|
||||
});
|
||||
|
||||
test('Can match basic url', () async {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'dart:mirrors';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:json_god/json_god.dart';
|
||||
import 'package:json_god/json_god.dart' as god;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class Todo {
|
||||
|
@ -18,12 +17,10 @@ main() {
|
|||
Angel app;
|
||||
String url;
|
||||
http.Client client;
|
||||
God god;
|
||||
|
||||
setUp(() async {
|
||||
app = new Angel();
|
||||
client = new http.Client();
|
||||
god = new God();
|
||||
Service todos = new MemoryService<Todo>();
|
||||
app.use('/todos', todos);
|
||||
print(app.service("todos"));
|
||||
|
@ -36,7 +33,6 @@ main() {
|
|||
url = null;
|
||||
client.close();
|
||||
client = null;
|
||||
god = null;
|
||||
});
|
||||
|
||||
group('memory', () {
|
||||
|
|
Loading…
Reference in a new issue