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
|
# Change Log
|
||||||
|
|
||||||
|
## 4.3.0
|
||||||
|
|
||||||
|
* Added `jaelTemplatePreload` to preload all JAEL templates into a cache
|
||||||
|
|
||||||
## 4.2.3
|
## 4.2.3
|
||||||
|
|
||||||
* Turned on generated HTML minification by default
|
* 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`.
|
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_framework/http.dart';
|
||||||
import 'package:angel3_jael/angel3_jael.dart';
|
import 'package:angel3_jael/angel3_jael.dart';
|
||||||
import 'package:file/local.dart';
|
import 'package:file/local.dart';
|
||||||
|
import 'package:jael3/jael3.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
main() async {
|
main() async {
|
||||||
|
|
|
@ -17,11 +17,12 @@ AngelConfigurer jael(Directory viewsDirectory,
|
||||||
{String fileExtension = '.jael',
|
{String fileExtension = '.jael',
|
||||||
bool strictResolution = false,
|
bool strictResolution = false,
|
||||||
bool cacheViews = true,
|
bool cacheViews = true,
|
||||||
|
Map<String, Document>? cache,
|
||||||
Iterable<Patcher> patch = const [],
|
Iterable<Patcher> patch = const [],
|
||||||
bool asDSX = false,
|
bool asDSX = false,
|
||||||
bool minified = true,
|
bool minified = true,
|
||||||
CodeBuffer Function()? createBuffer}) {
|
CodeBuffer Function()? createBuffer}) {
|
||||||
var cache = <String, Document>{};
|
var _cache = cache ?? <String, Document>{};
|
||||||
|
|
||||||
var bufferFunc = createBuffer ?? () => CodeBuffer();
|
var bufferFunc = createBuffer ?? () => CodeBuffer();
|
||||||
|
|
||||||
|
@ -34,31 +35,20 @@ AngelConfigurer jael(Directory viewsDirectory,
|
||||||
var errors = <JaelError>[];
|
var errors = <JaelError>[];
|
||||||
Document? processed;
|
Document? processed;
|
||||||
|
|
||||||
if (cacheViews && cache.containsKey(name)) {
|
//var stopwatch = Stopwatch()..start();
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
if (cacheViews && _cache.containsKey(name)) {
|
||||||
processed = await (resolve(doc, viewsDirectory,
|
processed = _cache[name];
|
||||||
patch: patch, onError: errors.add));
|
} else {
|
||||||
} catch (e) {
|
processed = await _loadViewTemplate(viewsDirectory, name,
|
||||||
// Ignore these errors, so that we can show syntax errors.
|
fileExtension: fileExtension, asDSX: asDSX, patch: patch);
|
||||||
}
|
|
||||||
if (processed == null) {
|
|
||||||
throw ArgumentError(name + fileExtension + " does not exists");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cacheViews) {
|
if (cacheViews) {
|
||||||
cache[name] = processed;
|
_cache[name] = processed!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//print('Time executed: ${stopwatch.elapsed.inMilliseconds}');
|
||||||
|
//stopwatch.stop();
|
||||||
|
|
||||||
var buf = bufferFunc();
|
var buf = bufferFunc();
|
||||||
var scope = SymbolTable(
|
var scope = SymbolTable(
|
||||||
|
@ -69,7 +59,7 @@ AngelConfigurer jael(Directory viewsDirectory,
|
||||||
if (errors.isEmpty) {
|
if (errors.isEmpty) {
|
||||||
try {
|
try {
|
||||||
const Renderer().render(processed!, buf, scope,
|
const Renderer().render(processed!, buf, scope,
|
||||||
strictResolution: strictResolution == true);
|
strictResolution: strictResolution);
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
} on JaelError catch (e) {
|
} on JaelError catch (e) {
|
||||||
errors.add(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
|
name: angel3_jael
|
||||||
version: 4.2.3
|
version: 4.3.0
|
||||||
description: Angel support for the Jael templating engine, similar to Blade or Liquid.
|
description: Angel support for the Jael templating engine, similar to Blade or Liquid.
|
||||||
homepage: https://angel3-framework.web.app/
|
homepage: https://angel3-framework.web.app/
|
||||||
repository: https://github.com/dukefirehawk/angel/tree/master/packages/jael/angel_jael
|
repository: https://github.com/dukefirehawk/angel/tree/master/packages/jael/angel_jael
|
||||||
|
@ -13,6 +13,7 @@ dependencies:
|
||||||
jael3_preprocessor: ^4.2.0
|
jael3_preprocessor: ^4.2.0
|
||||||
file: ^6.0.0
|
file: ^6.0.0
|
||||||
logging: ^1.0.1
|
logging: ^1.0.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
angel3_test: ^4.0.0
|
angel3_test: ^4.0.0
|
||||||
html: ^0.15.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:angel3_test/angel3_test.dart';
|
||||||
import 'package:file/memory.dart';
|
import 'package:file/memory.dart';
|
||||||
import 'package:html/parser.dart' as html;
|
import 'package:html/parser.dart' as html;
|
||||||
|
import 'package:jael3/jael3.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
@ -40,13 +41,17 @@ void main() {
|
||||||
</extend>
|
</extend>
|
||||||
''');
|
''');
|
||||||
|
|
||||||
app.get('/github/:username', (req, res) {
|
app.get('/github/:username', (req, res) async {
|
||||||
var username = req.params['username'];
|
var username = req.params['username'];
|
||||||
return res.render('github', {'username': username});
|
return res.render('github', {'username': username});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Preload the view template
|
||||||
|
var viewCache = <String, Document>{};
|
||||||
|
jaelTemplatePreload(viewsDirectory, viewCache);
|
||||||
|
|
||||||
await app.configure(
|
await app.configure(
|
||||||
jael(viewsDirectory, cacheViews: true),
|
jael(viewsDirectory, cache: viewCache),
|
||||||
);
|
);
|
||||||
|
|
||||||
app.fallback((req, res) => throw AngelHttpException.notFound());
|
app.fallback((req, res) => throw AngelHttpException.notFound());
|
||||||
|
@ -73,16 +78,19 @@ void main() {
|
||||||
.outerHtml);
|
.outerHtml);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can render multiples', () async {
|
test('initial load concurreny', () async {
|
||||||
// Load the view template and wait for it to be cached
|
// Concurrently hit the same JAEL page
|
||||||
var response1 = await client.get(Uri.parse('/github/thosakwe'));
|
for (var i = 0; i < 512; i++) {
|
||||||
|
|
||||||
for (var i = 0; i < 100; i++) {
|
|
||||||
client.get(Uri.parse('/github/thosakwe'));
|
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(
|
expect(
|
||||||
html.parse(response.body).outerHtml,
|
html.parse(response.body).outerHtml,
|
||||||
html
|
html
|
||||||
|
|
Loading…
Reference in a new issue