Skip to content

Commit

Permalink
Merge pull request #163 from javad-zobeidi/dev
Browse files Browse the repository at this point in the history
Improve Template engine
  • Loading branch information
javad-zobeidi authored Jan 18, 2025
2 parents e00bde7 + 6ebd788 commit 838b363
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 9 deletions.
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

0 comments on commit 838b363

Please sign in to comment.