Skip to content

Commit

Permalink
Merge pull request #56 from PyreStudios/steward-context
Browse files Browse the repository at this point in the history
feat(context): establish steward context
  • Loading branch information
bradcypert committed Oct 21, 2023
2 parents 7c6e198 + 6e411d0 commit e0f0782
Show file tree
Hide file tree
Showing 30 changed files with 204 additions and 178 deletions.
14 changes: 7 additions & 7 deletions README.md
Expand Up @@ -36,8 +36,8 @@ Here's an example of how you can use Steward!
import 'package:steward/steward.dart';
Future main() async {
var router = Router();
var container = CacheContainer();
final router = Router();
final container = Container();
// Setup a DI binding for UserService
container.bind('UserService', (_) => UserService());
Expand All @@ -51,14 +51,14 @@ Future main() async {
});
// Plucking things out of the container example
router.get('/config', (Request request) {
print(request.container.make('@config.app.name'));
return Response.Ok(request.container.make('@config.app.name'));
router.get('/config', (Context context) {
print(context.make('@config.app.name'));
return Response.Ok(context.make('@config.app.name'));
});
// Path Params example
router.get('/:name', (Request request) {
return Response.Ok(request.pathParams['name']);
router.get('/:name', (Context context) {
return Response.Ok(context.request.pathParams['name']);
});
var app = App(router: router);
Expand Down
12 changes: 6 additions & 6 deletions bin/doctor.dart
Expand Up @@ -12,12 +12,12 @@ class DoctorCommand extends Command {

@override
void run(List<String> args, Map<String, dynamic> flags) {
var configExists = File('./config.yml').existsSync();
var viewsExist = Directory('./views').existsSync();
var pubspec = File('./pubspec.yaml');
var pubspecExists = pubspec.existsSync();
var pubspecContents = pubspecExists ? pubspec.readAsLinesSync() : [];
var stewardVersion = pubspecContents.firstWhere(
final configExists = File('./config.yml').existsSync();
final viewsExist = Directory('./views').existsSync();
final pubspec = File('./pubspec.yaml');
final pubspecExists = pubspec.existsSync();
final pubspecContents = pubspecExists ? pubspec.readAsLinesSync() : [];
final stewardVersion = pubspecContents.firstWhere(
(element) => element.contains('steward:'),
orElse: () => 'NOT FOUND');

Expand Down
26 changes: 13 additions & 13 deletions bin/new.dart
Expand Up @@ -4,13 +4,13 @@ import 'package:bosun/bosun.dart';
import 'new_middleware.dart';
import 'new_view.dart';

var configTemplate = '''---
const configTemplate = '''---
app:
name: My Steward App
port: 4040
''';

var viewTemplate = '''<!DOCTYPE html>
const viewTemplate = '''<!DOCTYPE html>
<html lang="en">
<head>
Expand Down Expand Up @@ -77,27 +77,27 @@ var viewTemplate = '''<!DOCTYPE html>
</html>
''';

var appTemplate = '''
const appTemplate = '''
import 'package:steward/steward.dart';
Future main() async {
var router = Router();
router.get('/hello', (Request request) async {
final router = Router();
router.get('/hello', (Context context) async {
return Response.Ok('Hello World!');
});
router.get('/config', (Request request) async {
print(request.container.make('@config.app.name'));
return Response.Ok(request.container.make('@config.app.name'));
router.get('/config', (Context context) async {
print(context.read('@config.app.name'));
return Response.Ok(context.read('@config.app.name'));
});
router.get('/:name', (Request request) async {
return Response.Ok(request.pathParams['name']);
router.get('/:name', (Context context) async {
return Response.Ok(context.request.pathParams['name']);
});
// Add your own DI objects to the container here
var app = App(router: router);
final app = App(router: router);
return app.start();
Expand All @@ -114,11 +114,11 @@ class NewCommand extends Command {

@override
void run(List<String> args, Map<String, dynamic> flags) {
var name = args[0];
final name = args[0];

Process.runSync('dart', ['create', name, '--template', 'console-full']);

var config = File('./$name/config.yml');
final config = File('./$name/config.yml');
config.writeAsStringSync(configTemplate);
Directory('./$name/views').createSync();
Directory('./$name/assets').createSync();
Expand Down
12 changes: 6 additions & 6 deletions bin/new_middleware.dart
Expand Up @@ -3,15 +3,15 @@ import 'dart:io';
import 'package:bosun/bosun.dart';
import 'package:recase/recase.dart';

var middlwareTemplate = '''import 'package:steward/steward.dart';
const middlwareTemplate = '''import 'package:steward/steward.dart';
Future<Response> Function(Request) {{{name}}}Middleware(
Future<Response> Function(Request) next) {
return (Request request) {
Future<Response> Function(Context) {{{name}}}Middleware(
Future<Response> Function(Context) next) {
return (Context context) {
// Do something here!
return next(request);
return next(context);
};
}
''';
Expand All @@ -25,7 +25,7 @@ class NewMiddlewareCommand extends Command {

@override
void run(List<String> args, Map<String, dynamic> flags) {
var name = args[0];
final name = args[0];
// write initial files
final middleware = Directory('./lib/middleware');
if (!middleware.existsSync()) {
Expand Down
4 changes: 2 additions & 2 deletions bin/new_view.dart
Expand Up @@ -3,7 +3,7 @@ import 'dart:io';
import 'package:bosun/bosun.dart';
import 'package:recase/recase.dart';

var viewTemplate = '''<!DOCTYPE html>
const viewTemplate = '''<!DOCTYPE html>
<html lang="en">
<head>
Expand Down Expand Up @@ -75,7 +75,7 @@ class NewViewCommand extends Command {

@override
void run(List<String> args, Map<String, dynamic> flags) {
var name = args[0];
final name = args[0];
// write initial files
final views = Directory('./lib/views');
if (!views.existsSync()) {
Expand Down
8 changes: 4 additions & 4 deletions doc/site/docs/container/what_is_a_container.md
Expand Up @@ -15,7 +15,7 @@ The intent behind DI is to provide a separation of concerns when it comes to con
Let's start by binding a "Service" to the DI container.

```dart
var container = CacheContainer();
final container = Container();
container.bind<String>('sample', (Container container) {
return 'ABC123';
});
Expand All @@ -28,7 +28,7 @@ This simple snippet shows how we can new up an implementer of Container (CacheCo
Let's bind the same value from above:

```dart
var container = CacheContainer();
final container = StewardContainer();
container.bind<String>('sample', (Container container) {
return 'ABC123';
});
Expand All @@ -37,7 +37,7 @@ container.bind<String>('sample', (Container container) {
Now we'll likely want to retrieve this String from our container at some point. Doing so is fairly simple!

```dart
var key = container.make<String>('sample');
final key = container.make<String>('sample');
// key is now 'ABC123'
```

Expand All @@ -53,7 +53,7 @@ class UserService {
}
var container = CacheContainer();
final container = StewardContainer();
container.bind<UserService>(UserService.Key, (Container container) {
return UserService('1');
});
Expand Down
2 changes: 1 addition & 1 deletion doc/site/docs/router/middleware.md
Expand Up @@ -19,7 +19,7 @@ While this may look complex, the actual implementation of a custom middleware is
Future<Response> Function(Request) RequestLogger(
Future<Response> Function(Request) next) {
return (Request request) {
print('Incoming Request: ${request.request.uri}');
print('Incoming Request: ${request.uri}');
return next(request);
};
}
Expand Down
2 changes: 1 addition & 1 deletion doc/site/docs/router/routers.md
Expand Up @@ -27,7 +27,7 @@ The 2nd parameter to each of the Router methods is the handler. The Handler is a
import 'package:steward/steward.dart';
Future main() async {
var router = Router();
final router = Router();
router.get('/hello', (Request request) async {
return Response.Ok("Hello World!");
});
Expand Down
17 changes: 8 additions & 9 deletions example/main.dart
Expand Up @@ -2,24 +2,23 @@ import 'package:steward/app/app.dart';
import 'package:steward/router/router.dart';

Future main() async {
var router = Router();
final router = Router();

router.staticFiles('/assets');
router.get('/hello', (Request request) async {
router.get('/hello', (Context context) async {
return Response.Ok('Hello World!');
});

router.get('/config', (Request request) async {
print(request.container);
print(request.container.make('@config'));
return Response.Ok(request.container.make('@config'));
router.get('/config', (Context context) async {
print(context.read('@config'));
return Response.Ok(context.read('@config'));
});

router.get('/:name', (Request request) async {
return Response.Ok(request.pathParams['name']);
router.get('/:name', (Context context) async {
return Response.Ok(context.request.pathParams['name']);
});

var app = App(router: router);
final app = App(router: router);

return app.start();
}
14 changes: 7 additions & 7 deletions lib/app/app.dart
Expand Up @@ -12,7 +12,7 @@ enum Environment { production, other }
class App {
Router router;
Environment environment;
late Container _container;
late StewardContainer _container;

App({required this.router, this.environment = Environment.other}) {
_container = router.container;
Expand Down Expand Up @@ -40,25 +40,25 @@ class App {
/// and ultimately preprends "@config." to the key before adding that
/// config item to the DI container.
void _loadConfigIntoContainer() {
var configFile = File('config.yml');
var configReader = ConfigReader(file: configFile)..read();
var config = configReader.parsed;
var flat = flatten(config);
final configFile = File('config.yml');
final configReader = ConfigReader(file: configFile)..read();
final config = configReader.parsed;
final flat = flatten(config);
flat.entries.forEach((element) {
_container.bind('@config.' + element.key, (_) => element.value);
});
}

void _loadViewsIntoContainer() {
try {
var files = Directory('./views')
final files = Directory('./views')
.listSync(recursive: true, followLinks: true)
.whereType<File>()
.where((file) => file.path.endsWith('.mustache'))
.map((file) =>
{'path': file.path, 'contents': file.readAsStringSync()});
files.forEach((file) {
var key = file['path']
final key = file['path']
?.replaceAll('/', '.')
.replaceFirst('..views.', '')
.replaceFirst('.mustache', '');
Expand Down
2 changes: 1 addition & 1 deletion lib/config/config_reader.dart
Expand Up @@ -12,7 +12,7 @@ class ConfigReader {

/// Reads the config file and parses it into a Map.
void read() {
var data = file.readAsStringSync();
final data = file.readAsStringSync();
YamlMap yamlMap = loadYaml(data);
// hacky way to go from yaml map to normal map
parsed = jsonDecode(jsonEncode(yamlMap));
Expand Down
30 changes: 18 additions & 12 deletions lib/container/container.dart
Expand Up @@ -5,31 +5,37 @@ class ViewNotFoundError extends Error {
ViewNotFoundError(this.fileName);
}

abstract class Container {
void bind<T>(String key, T Function(Container) fn);
T? make<T>(String key);
abstract interface class Cloneable<T> {
T clone();
}

abstract interface class ContainerReable {
T? read<T>(String key);
}

/// Clones a container so that a new container can have values bound to it
/// without updating this current container.
Container clone();
abstract interface class ContainerWriteable {
void bind<T>(String key, T Function(Container) fn);
}

abstract interface class Container
implements ContainerReable, ContainerWriteable {}

/// A rudimentary DI container implementation
/// container bindings are created as needed.
class CacheContainer implements Container {
class StewardContainer implements Container, Cloneable<StewardContainer> {
Map<String, dynamic> bindings = {};

/// Binds a new DI item into the container
/// The function bound to the provided key will only be called when the container
/// receives a request for the item at that key.
@override
void bind<T>(String key, T Function(Container) fn) {
void bind<T>(String key, T Function(Container container) fn) {
bindings[key] = fn;
}

/// Generate an item for a given key
@override
T? make<T>(String key) {
T? read<T>(String key) {
if (bindings.containsKey(key)) {
return bindings[key](this);
}
Expand All @@ -41,7 +47,7 @@ class CacheContainer implements Container {
String? view(String filename) {
String templateString;
try {
templateString = make('@views.$filename');
templateString = read('@views.$filename');
} catch (e) {
throw ViewNotFoundError(filename);
}
Expand All @@ -50,8 +56,8 @@ class CacheContainer implements Container {
}

@override
Container clone() {
return CacheContainer()..bindings.addAll(bindings);
StewardContainer clone() {
return StewardContainer()..bindings.addAll(bindings);
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/forms/forms.dart
Expand Up @@ -22,7 +22,7 @@ abstract class Form {
/// Returns a list of form errors, or an empty list if there are no errors - in which case isValid will be true.
/// Validation is handled lazily so you'll need to call this function to trigger validation.
List<FormError> validate() {
var errors = validator();
final errors = validator();

if (errors.isNotEmpty) {
_isValid = false;
Expand Down
6 changes: 3 additions & 3 deletions lib/middleware/cors_middleware.dart
Expand Up @@ -9,9 +9,9 @@ MiddlewareFunc CorsMiddleware(
{List<String>? allowOrigin,
List<String>? allowMethods,
List<String>? allowHeaders}) {
return (Future<Response> Function(Request) next) {
return (Request request) async {
var resp = await next(request);
return (Future<Response> Function(Context) next) {
return (Context context) async {
final resp = await next(context);

if (allowOrigin != null) {
resp.headers.add(_originKey, allowOrigin);
Expand Down

0 comments on commit e0f0782

Please sign in to comment.