Merge pull request #33 from dukefirehawk/feature/update-jael
Added JAEL template preload
This commit is contained in:
commit
97fb2edf87
6 changed files with 107 additions and 32 deletions
|
@ -1,5 +1,9 @@
|
|||
# Change Log
|
||||
|
||||
## 4.3.0
|
||||
|
||||
* Added `jaelTemplatePreload` to preload all JAEL templates into a cache
|
||||
|
||||
## 4.2.3
|
||||
|
||||
* Turned on generated HTML minification by default
|
||||
|
|
|
@ -77,3 +77,23 @@ void main() async {
|
|||
```
|
||||
|
||||
To apply additional transforms to parsed documents, provide a set of `patch` functions, like in `package:jael3_preprocessor`.
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
For handling large volume of initial requests, consider using `jaelTemplatePreload` to preload all the JAEL templates
|
||||
into an external cache.
|
||||
|
||||
```dart
|
||||
|
||||
var templateDir = fileSystem.directory('views');
|
||||
|
||||
// Preload JAEL view templates into cache
|
||||
var viewCache = <String, Document>{};
|
||||
jaelTemplatePreload(templateDir, viewCache);
|
||||
|
||||
// Inject cache into JAEL renderer
|
||||
await app.configure(
|
||||
jael(fileSystem.directory('views'), cache: viewCache),
|
||||
);
|
||||
|
||||
```
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:angel3_framework/angel3_framework.dart';
|
|||
import 'package:angel3_framework/http.dart';
|
||||
import 'package:angel3_jael/angel3_jael.dart';
|
||||
import 'package:file/local.dart';
|
||||
import 'package:jael3/jael3.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
main() async {
|
||||
|
|
|
@ -17,11 +17,12 @@ AngelConfigurer jael(Directory viewsDirectory,
|
|||
{String fileExtension = '.jael',
|
||||
bool strictResolution = false,
|
||||
bool cacheViews = true,
|
||||
Map<String, Document>? cache,
|
||||
Iterable<Patcher> patch = const [],
|
||||
bool asDSX = false,
|
||||
bool minified = true,
|
||||
CodeBuffer Function()? createBuffer}) {
|
||||
var cache = <String, Document>{};
|
||||
var _cache = cache ?? <String, Document>{};
|
||||
|
||||
var bufferFunc = createBuffer ?? () => CodeBuffer();
|
||||
|
||||
|
@ -34,31 +35,20 @@ AngelConfigurer jael(Directory viewsDirectory,
|
|||
var errors = <JaelError>[];
|
||||
Document? processed;
|
||||
|
||||
if (cacheViews && cache.containsKey(name)) {
|
||||
processed = cache[name];
|
||||
} else {
|
||||
var file = viewsDirectory.childFile(name + fileExtension);
|
||||
var contents = await file.readAsString();
|
||||
var doc = parseDocument(contents,
|
||||
sourceUrl: file.uri, asDSX: asDSX, onError: errors.add);
|
||||
if (doc == null) {
|
||||
throw ArgumentError(name + fileExtension + " does not exists");
|
||||
}
|
||||
//var stopwatch = Stopwatch()..start();
|
||||
|
||||
try {
|
||||
processed = await (resolve(doc, viewsDirectory,
|
||||
patch: patch, onError: errors.add));
|
||||
} catch (e) {
|
||||
// Ignore these errors, so that we can show syntax errors.
|
||||
}
|
||||
if (processed == null) {
|
||||
throw ArgumentError(name + fileExtension + " does not exists");
|
||||
}
|
||||
if (cacheViews && _cache.containsKey(name)) {
|
||||
processed = _cache[name];
|
||||
} else {
|
||||
processed = await _loadViewTemplate(viewsDirectory, name,
|
||||
fileExtension: fileExtension, asDSX: asDSX, patch: patch);
|
||||
|
||||
if (cacheViews) {
|
||||
cache[name] = processed;
|
||||
_cache[name] = processed!;
|
||||
}
|
||||
}
|
||||
//print('Time executed: ${stopwatch.elapsed.inMilliseconds}');
|
||||
//stopwatch.stop();
|
||||
|
||||
var buf = bufferFunc();
|
||||
var scope = SymbolTable(
|
||||
|
@ -69,7 +59,7 @@ AngelConfigurer jael(Directory viewsDirectory,
|
|||
if (errors.isEmpty) {
|
||||
try {
|
||||
const Renderer().render(processed!, buf, scope,
|
||||
strictResolution: strictResolution == true);
|
||||
strictResolution: strictResolution);
|
||||
return buf.toString();
|
||||
} on JaelError catch (e) {
|
||||
errors.add(e);
|
||||
|
@ -81,3 +71,54 @@ AngelConfigurer jael(Directory viewsDirectory,
|
|||
};
|
||||
};
|
||||
}
|
||||
|
||||
/// Preload all of Jael templates into a cache
|
||||
///
|
||||
///
|
||||
/// To apply additional transforms to parsed documents, provide a set of [patch] functions.
|
||||
Future<void> jaelTemplatePreload(
|
||||
Directory viewsDirectory, Map<String, Document> cache,
|
||||
{String fileExtension = '.jael',
|
||||
bool asDSX = false,
|
||||
Iterable<Patcher> patch = const []}) async {
|
||||
await viewsDirectory.list(recursive: true).forEach((f) async {
|
||||
if (f.basename.endsWith(fileExtension)) {
|
||||
var name = f.basename.split(".");
|
||||
if (name.length > 1) {
|
||||
print("View: ${name[0]}");
|
||||
Document? processed = await _loadViewTemplate(viewsDirectory, name[0]);
|
||||
if (processed != null) {
|
||||
cache[name[0]] = processed;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<Document?> _loadViewTemplate(Directory viewsDirectory, String name,
|
||||
{String fileExtension = '.jael',
|
||||
bool asDSX = false,
|
||||
Iterable<Patcher> patch = const []}) async {
|
||||
var errors = <JaelError>[];
|
||||
Document? processed;
|
||||
|
||||
var file = viewsDirectory.childFile(name + fileExtension);
|
||||
var contents = await file.readAsString();
|
||||
var doc = parseDocument(contents,
|
||||
sourceUrl: file.uri, asDSX: asDSX, onError: errors.add);
|
||||
|
||||
if (doc == null) {
|
||||
throw ArgumentError(file.basename + " does not exists");
|
||||
}
|
||||
|
||||
try {
|
||||
processed =
|
||||
await (resolve(doc, viewsDirectory, patch: patch, onError: errors.add));
|
||||
} catch (e) {
|
||||
// Ignore these errors, so that we can show syntax errors.
|
||||
}
|
||||
if (processed == null) {
|
||||
throw ArgumentError(file.basename + " does not exists");
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: angel3_jael
|
||||
version: 4.2.3
|
||||
version: 4.3.0
|
||||
description: Angel support for the Jael templating engine, similar to Blade or Liquid.
|
||||
homepage: https://angel3-framework.web.app/
|
||||
repository: https://github.com/dukefirehawk/angel/tree/master/packages/jael/angel_jael
|
||||
|
@ -13,6 +13,7 @@ dependencies:
|
|||
jael3_preprocessor: ^4.2.0
|
||||
file: ^6.0.0
|
||||
logging: ^1.0.1
|
||||
|
||||
dev_dependencies:
|
||||
angel3_test: ^4.0.0
|
||||
html: ^0.15.0
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:angel3_jael/angel3_jael.dart';
|
|||
import 'package:angel3_test/angel3_test.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:html/parser.dart' as html;
|
||||
import 'package:jael3/jael3.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
@ -40,13 +41,17 @@ void main() {
|
|||
</extend>
|
||||
''');
|
||||
|
||||
app.get('/github/:username', (req, res) {
|
||||
app.get('/github/:username', (req, res) async {
|
||||
var username = req.params['username'];
|
||||
return res.render('github', {'username': username});
|
||||
});
|
||||
|
||||
//Preload the view template
|
||||
var viewCache = <String, Document>{};
|
||||
jaelTemplatePreload(viewsDirectory, viewCache);
|
||||
|
||||
await app.configure(
|
||||
jael(viewsDirectory, cacheViews: true),
|
||||
jael(viewsDirectory, cache: viewCache),
|
||||
);
|
||||
|
||||
app.fallback((req, res) => throw AngelHttpException.notFound());
|
||||
|
@ -73,16 +78,19 @@ void main() {
|
|||
.outerHtml);
|
||||
});
|
||||
|
||||
test('can render multiples', () async {
|
||||
// Load the view template and wait for it to be cached
|
||||
var response1 = await client.get(Uri.parse('/github/thosakwe'));
|
||||
|
||||
for (var i = 0; i < 100; i++) {
|
||||
test('initial load concurreny', () async {
|
||||
// Concurrently hit the same JAEL page
|
||||
for (var i = 0; i < 512; i++) {
|
||||
client.get(Uri.parse('/github/thosakwe'));
|
||||
}
|
||||
var response = await client.get(Uri.parse('/github/thosakwe'));
|
||||
|
||||
//print('Body:\n${response.body}');
|
||||
Stopwatch stopwatch = Stopwatch()..start();
|
||||
var response = await client.get(Uri.parse('/github/thosakwe'));
|
||||
var elapsedTime = stopwatch.elapsed.inMilliseconds;
|
||||
|
||||
print('Latency is $elapsedTime');
|
||||
|
||||
print('Body:\n${response.body}');
|
||||
expect(
|
||||
html.parse(response.body).outerHtml,
|
||||
html
|
||||
|
|
Loading…
Reference in a new issue