Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Template engine #163

Merged
merged 8 commits into from
Jan 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions lib/src/http/controller/controller_handler.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:vania/src/exception/validation_exception.dart';
import 'package:vania/src/route/route_data.dart';
import 'package:vania/src/route/route_history.dart';
import 'package:vania/vania.dart';

class ControllerHandler {
Expand Down Expand Up @@ -28,9 +29,14 @@ class ControllerHandler {

response.makeResponse(request.response);
} on ValidationException catch (error) {
error
.response(request.headers['accept'].toString().contains('html'))
.makeResponse(request.response);
bool isHtml =
request.request.headers.value('accept').toString().contains('html');
if (isHtml) {
Response.redirect(RouteHistory().previousRoute)
.makeResponse(request.response);
} else {
error.response(false).makeResponse(request.response);
}
} catch (error) {
_response(request, error.toString());
}
Expand Down
10 changes: 10 additions & 0 deletions lib/src/http/request/request.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:vania/src/exception/validation_exception.dart';
import 'package:vania/src/http/request/request_body.dart';
import 'package:vania/src/http/validation/validator.dart';
import 'package:vania/src/route/route_data.dart';
import 'package:vania/src/view_engine/template_engine.dart';
import 'package:vania/vania.dart';

class Request {
Expand Down Expand Up @@ -321,6 +322,7 @@ class Request {
[Map<String, String> messages = const <String, String>{}]) {
assert(rules is Map<String, String> || rules is List<Validation>,
'Rules must be either Map<String, String> or List<Validation>.');
TemplateEngine().sessionErrors.clear();
if (rules is Map<String, String>) {
_validate(rules, messages);
} else {
Expand All @@ -336,6 +338,10 @@ class Request {
}
validator.validate(rules);
if (validator.hasError) {
bool isHtml = request.headers.value('accept').toString().contains('html');
if (isHtml) {
TemplateEngine().sessionErrors.addAll(validator.errors);
}
throw ValidationException(message: validator.errors);
}
}
Expand All @@ -354,6 +360,10 @@ class Request {
}
}
if (errors.isNotEmpty) {
bool isHtml = request.headers.value('accept').toString().contains('html');
if (isHtml) {
TemplateEngine().sessionErrors.addAll(errors);
}
throw ValidationException(message: errors);
}
}
Expand Down
11 changes: 10 additions & 1 deletion lib/src/http/request/request_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import 'package:vania/src/http/controller/controller_handler.dart';
import 'package:vania/src/http/middleware/middleware_handler.dart';
import 'package:vania/src/route/route_data.dart';
import 'package:vania/src/route/route_handler.dart';
import 'package:vania/src/route/route_history.dart';
import 'package:vania/src/view_engine/template_engine.dart';
import 'package:vania/src/websocket/web_socket_handler.dart';
import 'package:vania/vania.dart';

Expand Down Expand Up @@ -36,6 +38,8 @@ Future httpRequestHandler(HttpRequest req) async {
String requestUri = req.uri.path;
String starteRequest = startTime.format();

bool isHtml = req.headers.value('accept').toString().contains('html');

try {
/// Check if cors is enabled
HttpCors(req);
Expand All @@ -44,6 +48,12 @@ Future httpRequestHandler(HttpRequest req) async {
await request.extractBody();
if (route == null) return;

RouteHistory().updateRouteHistory(req);

if (isHtml) {
TemplateEngine().formData.addAll(request.all());
}

/// check if pre middleware exist and call it
if (route.preMiddleware.isNotEmpty) {
await middlewareHandler(route.preMiddleware, request);
Expand All @@ -55,7 +65,6 @@ Future httpRequestHandler(HttpRequest req) async {
request: request,
);
} on BaseHttpResponseException catch (error) {
bool isHtml = req.headers.value('accept').toString().contains('html');
if (error is NotFoundException && isHtml) {
if (File('lib/view/template/errors/404.html').existsSync()) {
return view('errors/404').makeResponse(req.response);
Expand Down
21 changes: 21 additions & 0 deletions lib/src/http/response/response.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import 'dart:io';
import 'dart:typed_data';
import 'package:meta/meta.dart';
import 'package:vania/src/http/response/stream_file.dart';
import 'package:vania/src/route/route_history.dart';
import 'package:vania/src/view_engine/template_engine.dart';

enum ResponseType {
json,
Expand Down Expand Up @@ -191,4 +193,23 @@ class Response {
responseType: ResponseType.download,
headers: headers,
);

static back([String? key, String? message]) {
String previousRoute = RouteHistory().previousRoute;
if (key != null && message != null) {
TemplateEngine().sessions[key] = message;
}
if (previousRoute.isNotEmpty) {
return Response(
responseType: ResponseType.redirect,
data: previousRoute,
httpStatusCode: HttpStatus.found,
);
}
return Response(
responseType: ResponseType.redirect,
data: RouteHistory().currentRoute,
httpStatusCode: HttpStatus.found,
);
}
}
17 changes: 14 additions & 3 deletions lib/src/route/route_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ import 'package:vania/src/route/set_static_path.dart';
import 'package:vania/src/utils/functions.dart';
import 'package:vania/vania.dart';

/// Find the matched route from the given request and return the
/// [RouteData] for the matched route.
///
/// The function first checks if the request is an OPTIONS request. If it is,
/// the request is closed and null is returned. If the request is not an
/// OPTIONS request, the function checks if the request path matches a static
/// file. If it does, the function returns null. If it doesn't, the function
/// throws a [NotFoundException].
///
/// If the request is not an OPTIONS request and the request path doesn't match
/// a static file, the function returns the matched [RouteData].
///
/// Throws a [NotFoundException] if the request is not an OPTIONS request and
/// the request path doesn't match a static file.
RouteData? httpRouteHandler(HttpRequest req) {
final route = _getMatchRoute(
Uri.decodeComponent(
Expand Down Expand Up @@ -36,7 +50,6 @@ RouteData? httpRouteHandler(HttpRequest req) {
return route;
}

/// Exctract the domain from the url
String _extractDomain(String domain, String path) {
String firstPart = domain.split('.').first.toLowerCase();
final RegExp domainRegex = RegExp(r'\{[^}]*\}');
Expand All @@ -48,8 +61,6 @@ String _extractDomain(String domain, String path) {
return domainUri;
}

/// Exctarct username from {username}
/// Or any string between {}
String? _extractDomainPlaceholder(String input) {
final RegExp regex = RegExp(r'\{([^}]*)\}');
final match = regex.firstMatch(input);
Expand Down
24 changes: 24 additions & 0 deletions lib/src/route/route_history.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'dart:io';

class RouteHistory {
static final RouteHistory _instance = RouteHistory._internal();
factory RouteHistory() => _instance;
RouteHistory._internal();

String _currentRoute = '';
String _previousRoute = '';

String get currentRoute => _currentRoute;
String get previousRoute => _previousRoute;

Future<void> updateRouteHistory(HttpRequest req) async {
if (req.headers.value('accept').toString().contains('html')) {
if (_currentRoute.isEmpty) {
_currentRoute = req.uri.path;
} else {
_previousRoute = _currentRoute;
_currentRoute = req.uri.path;
}
}
}
}
2 changes: 1 addition & 1 deletion lib/src/utils/helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Response view(String template, [Map<String, dynamic>? context]) =>
Future<void> setSession(String key, dynamic value) async =>
await SessionManager().setSession(key, value);
Future<T> getSession<T>(String key) async =>
await SessionManager().getSession<T>(key);
TemplateEngine().sessions[key] ?? await SessionManager().getSession<T>(key);
Future<Map<String, dynamic>?> allSessions() async =>
await SessionManager().allSessions();
Future<void> deleteSession(String key) async =>
Expand Down
29 changes: 29 additions & 0 deletions lib/src/view_engine/processor_engine/error_processor.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import 'package:vania/src/view_engine/template_engine.dart';

import 'abs_processor.dart';

class ErrorProcessor extends AbsProcessor {
@override
String parse(String content, [Map<String, dynamic>? context]) {
final hasErrorPattern = RegExp(
r"hasError\(\s*'([^']*)'\s*\)",
dotAll: true,
);
content = content.replaceAllMapped(hasErrorPattern, (match) {
final errorKey = match.group(1);
return TemplateEngine().sessionErrors.containsKey(errorKey).toString();
});

final errorPattern = RegExp(
r"\{@\s*error\(\s*'([^']*)'\s*\)\s*@\}",
dotAll: true,
);

content = content.replaceAllMapped(errorPattern, (error) {
final errorKey = error.group(1);
return TemplateEngine().sessionErrors[errorKey] ?? '';
});

return content;
}
}
20 changes: 20 additions & 0 deletions lib/src/view_engine/processor_engine/old_processor.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'package:vania/src/view_engine/template_engine.dart';

import 'abs_processor.dart';

class OldProcessor extends AbsProcessor {
@override
String parse(String content, [Map<String, dynamic>? context]) {
final oldPattern = RegExp(
r"\{@\s*old\(\s*'([^']*)'\s*\)\s*@\}",
dotAll: true,
);

content = content.replaceAllMapped(oldPattern, (oldMatch) {
final oldKey = oldMatch.group(1);
return TemplateEngine().formData[oldKey] ?? '';
});

return content;
}
}
29 changes: 29 additions & 0 deletions lib/src/view_engine/processor_engine/session_processor.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import 'package:vania/src/view_engine/processor_engine/abs_processor.dart';
import 'package:vania/src/view_engine/template_engine.dart';

class SessionProcessor implements AbsProcessor {
@override
String parse(String content, [Map<String, dynamic>? context]) {
final hasSessionPattern = RegExp(
r"hasSession\(\s*'([^']*)'\s*\)",
dotAll: true,
);

content = content.replaceAllMapped(hasSessionPattern, (match) {
final sessionKey = match.group(1);
return TemplateEngine().sessions.containsKey(sessionKey).toString();
});

final sessionPattern = RegExp(
r"\{@\s*session\(\s*'([^']*)'\s*\)\s*@\}",
dotAll: true,
);

content = content.replaceAllMapped(sessionPattern, (math) {
final sessionKey = math.group(1);
return TemplateEngine().sessions[sessionKey] ?? '';
});

return content;
}
}
20 changes: 19 additions & 1 deletion lib/src/view_engine/template_engine.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ import 'package:vania/src/view_engine/processor_engine/variables_processor.dart'

import 'processor_engine/csrf_processor.dart';
import 'processor_engine/csrf_token_processor.dart';
import 'processor_engine/error_processor.dart';
import 'processor_engine/if_statement_processor.dart';
import 'processor_engine/extends_processor.dart';
import 'processor_engine/for_loop_processor.dart';
import 'processor_engine/include_processor.dart';
import 'processor_engine/old_processor.dart';
import 'processor_engine/section_processor.dart';
import 'processor_engine/session_processor.dart';
import 'processor_engine/switch_cases_processor.dart';
import 'template_reader.dart';

Expand All @@ -28,6 +31,7 @@ class TemplateEngine {
static final TemplateEngine _singleton = TemplateEngine._internal();
factory TemplateEngine() => _singleton;
TemplateEngine._internal();

final VariablesProcessor _variablesProcess = VariablesProcessor();
final IfStatementProcessor _conditionalProcess = IfStatementProcessor();
final SwitchCasesProcessor _switchCaseProcess = SwitchCasesProcessor();
Expand All @@ -36,11 +40,22 @@ class TemplateEngine {
final ExtendsProcessor _extendsProcessor = ExtendsProcessor();
final CsrfProcessor _csrfProcessor = CsrfProcessor();
final CsrfTokenProcessor _csrfTokenProcessor = CsrfTokenProcessor();
final ErrorProcessor _errorProcessor = ErrorProcessor();
final SectionProcessor _sectionProcessor = SectionProcessor();
final OldProcessor _oldProcessor = OldProcessor();
final SessionProcessor _sessionProcessor = SessionProcessor();

final Map<String, dynamic> sessionErrors = {};
final Map<String, dynamic> formData = {};
final Map<String, dynamic> sessions = {};

String render(String template, [Map<String, dynamic>? data]) {
String templateContent = FileTemplateReader().read(template);
return renderString(templateContent, data);
String renderedTemplate = renderString(templateContent, data);
sessionErrors.clear();
formData.clear();
sessions.clear();
return renderedTemplate;
}

/// Renders a template string with the provided data context.
Expand Down Expand Up @@ -69,12 +84,15 @@ class TemplateEngine {
_extendsProcessor,
_includeProcessor,
_sectionProcessor,
_errorProcessor,
_sessionProcessor,
_forLoopProcessor,
_switchCaseProcess,
_conditionalProcess,
_variablesProcess,
_csrfProcessor,
_csrfTokenProcessor,
_oldProcessor
]);

final renderedContent = pipeline.run(templateContent, data);
Expand Down
Loading