From 83e8f61d3fe4a14f5ecf9eb4f621125a3effef6a Mon Sep 17 00:00:00 2001 From: Jacob Jensen Date: Fri, 13 Oct 2017 06:28:09 +0200 Subject: [PATCH] Fixed compile-time AA's Compile-time AA's are now fixed and compiles with the latest version of DMD. Also implemented new functions for views to render, caching, as well redirect functionality for controllers. --- src/controllers/controller.d | 28 +++++++++ src/controllers/status.d | 2 +- src/templates/parser.d | 40 ++++++------ src/views/view.d | 116 ++++++++++++++++++++++++++++++++++- src/views/viewgeneration.d | 16 ++--- src/views/viewimports.d | 7 +-- 6 files changed, 177 insertions(+), 32 deletions(-) diff --git a/src/controllers/controller.d b/src/controllers/controller.d index aeffddb..7542720 100644 --- a/src/controllers/controller.d +++ b/src/controllers/controller.d @@ -66,6 +66,20 @@ version (WebServer) { return Status.end; } + /** + * Redirects the response to a specific url. + * Params: + * url = The url to redirect to. + * status = The status of the redirection. (Default is HTTPStatus.Found) + * Returns: + * The status required for the redirection to work properly. (Status.end) + */ + Status redirectTo(string url, HTTPStatus status = HTTPStatus.Found) { + _view.response.redirect(url, status); + + return Status.end; + } + /** * Handles the view's current controller action. * Returns: @@ -181,6 +195,20 @@ else version (WebService) { return Status.end; } + /** + * Redirects the response to a specific url. + * Params: + * url = The url to redirect to. + * status = The status of the redirection. (Default is HTTPStatus.Found) + * Returns: + * The status required for the redirection to work properly. (Status.end) + */ + Status redirectTo(string url, HTTPStatus status = HTTPStatus.Found) { + response.redirect(url, status); + + return Status.end; + } + /** * Handles the view's current controller action. * Returns: diff --git a/src/controllers/status.d b/src/controllers/status.d index 153642d..315664c 100644 --- a/src/controllers/status.d +++ b/src/controllers/status.d @@ -8,7 +8,7 @@ enum Status { notFound, /** * Indicates the response should end after executing the actions. - * This is useful if you respond with a different type of data than html such as json etc. + * This is useful if you're redirecting or responding with a different type of data than html such as json etc. */ end } diff --git a/src/templates/parser.d b/src/templates/parser.d index 983b84b..730729e 100644 --- a/src/templates/parser.d +++ b/src/templates/parser.d @@ -10,48 +10,52 @@ import diamond.templates.characterincludemode; import diamond.templates.part; private { - enum grammars = [ - '[' : new Grammar( + // HACK: to make AA's with classes work at compile-time. + @property grammars() { + Grammar[char] aa; + aa['['] = new Grammar( "metadata", '[', ']', ContentMode.metaContent, CharacterIncludeMode.none, false, false - ), + ); - '<' : new Grammar( + aa['<'] = new Grammar( "placeHolder", '<', '>', ContentMode.appendContent, CharacterIncludeMode.both, true, false - ), + ); - '{' : new Grammar( + aa['{'] = new Grammar( "code", '{', '}', ContentMode.mixinContent, CharacterIncludeMode.none, false, false - ), + ); - ':' : new Grammar( + aa[':'] = new Grammar( "expression", ':', '\n', ContentMode.mixinContent, CharacterIncludeMode.none, false, true - ), + ); - '=' : new Grammar( + aa['='] = new Grammar( "expressionValue", '=', ';', ContentMode.appendContent, CharacterIncludeMode.none, false, true - ), + ); - '(' : new Grammar( + aa['('] = new Grammar( "escapedValue", '(', ')', ContentMode.appendContent, CharacterIncludeMode.none, false, false - ), + ); - '$' : new Grammar( + aa['$'] = new Grammar( "expressionEscaped", '$', ';', ContentMode.appendContent, CharacterIncludeMode.none, false, false, '=' // Character that must follow the first character after @ - ), + ); - '*' : new Grammar( + aa['*'] = new Grammar( "comment", '*', '*', ContentMode.discardContent, CharacterIncludeMode.none, false, true - ) - ]; + ); + + return aa; + } } /** diff --git a/src/views/view.d b/src/views/view.d index 398c611..98fc5d8 100644 --- a/src/views/view.d +++ b/src/views/view.d @@ -7,6 +7,8 @@ else { version = Not_WebService; } +private size_t _pageId; + version (Not_WebService) { import std.string : format, strip; import std.array : join, replace, split, array; @@ -42,6 +44,8 @@ version (Not_WebService) { /// The routes string[] _result; + string _rootPath; + protected: version (WebServer) { /** @@ -112,6 +116,10 @@ version (Not_WebService) { * Note: Calling this property might be expensive, so caching the value is recommended. */ auto rootPath() { + if (_rootPath) { + return _rootPath; + } + // This makes sure that it's not retrieving the page's route, but the requests. // It's useful in terms of a view redirecting to another view internally. // Since the redirected view will have the route of the redirection and not the request. @@ -152,7 +160,8 @@ version (Not_WebService) { layoutResult = layoutResult.replace("@", _placeHolders["head"]); } - result = layoutResult.replace("@", result); + _pageId++; + result = layoutResult.replace("@", result.replace("@", to!string(_pageId))); } } @@ -246,6 +255,17 @@ version (Not_WebService) { * Returns: * The view. */ + auto viewSpecialized(string name)(bool checkRoute = false) { + mixin("import diamondapp : getView, view_" ~ name ~ ";"); // To retrieve views ... + + version (WebServer) { + mixin("return cast(view_" ~ name ~ ")getView(this.request, this.response, [name], checkRoute);"); + } + else { + mixin("return cast(view_" ~ name ~ ")getView(name);"); + } + } + auto view(string name, bool checkRoute = false) { import diamondapp : getView; // To retrieve views ... @@ -277,6 +297,100 @@ version (Not_WebService) { void render(string name) { append(retrieve(name)); } + + /** + * Will render a layout view of anothr view into this one. + * Params: + * name = The name of the layout view. + * id = The id of the rendered content. This uses the @ placeholder. + * placeHolder = The name of the placeholder in the layout view. + * view = The name of the view. + */ + void render(string name, string id, string placeHolder, string view) { + auto layoutView = retrieve(name); + layoutView = layoutView.replace("@", id); + auto contentView = retrieve(view); + layoutView = layoutView.replace("@<" ~ placeHolder ~ ">", contentView); + + append(layoutView); + } + + /** + * Retrieves the generated html of a view. + * This should generally only be used to render partial views into another view. + * Params: + * name = The name of the view to generate the html of. + * Returns: + * A string qeuivalent to the generated html. + */ + string retrieve(string name)() { + return viewSpecialized!name.generate(); + } + + /** + * Will render another view into this one. + * Params: + * name = The name of the view to render. + */ + void render(string name)() { + append(retrieve!name); + } + + /** + * Will render a layout view of anothr view into this one. + * Params: + * name = The name of the layout view. + * id = The id of the rendered content. This uses the @ placeholder. + * placeHolder = The name of the placeholder in the layout view. + * view = The name of the view. + */ + void render(string name)(string id, string placeHolder, string view) { + auto layoutView = retrieve!name; + layoutView = layoutView.replace("@", id); + auto contentView = retrieve(view); + layoutView = layoutView.replace("@<" ~ placeHolder ~ ">", contentView); + + append(layoutView); + } + + // + /** + * Retrieves the generated html of a view. + * This should generally only be used to render partial views into another view. + * Params: + * name = The name of the view to generate the html of. + * Returns: + * A string qeuivalent to the generated html. + */ + string retrieve(string name, TModel)(TModel model) { + return viewSpecialized!name.generate(model); + } + + /** + * Will render another view into this one. + * Params: + * name = The name of the view to render. + */ + void render(string name, TModel)(TModel model) { + append(retrieve!(name, TModel)(model)); + } + + /** + * Will render a layout view of anothr view into this one. + * Params: + * name = The name of the layout view. + * id = The id of the rendered content. This uses the @ placeholder. + * placeHolder = The name of the placeholder in the layout view. + * view = The name of the view. + */ + void render(string view, TModel)(string name, string id, string placeHolder, TModel model) { + auto layoutView = retrieve(name); + layoutView = layoutView.replace("@", id); + auto contentView = retrieve!(view,TModel)(model); + layoutView = layoutView.replace("@<" ~ placeHolder ~ ">", contentView); + + append(layoutView); + } } /** diff --git a/src/views/viewgeneration.d b/src/views/viewgeneration.d index ccab15d..1988e5e 100644 --- a/src/views/viewgeneration.d +++ b/src/views/viewgeneration.d @@ -11,8 +11,8 @@ version (Not_WebService) { /// Generates all the views into classes. mixin template ViewGeneration() { auto generateViews() { - auto routableViewsMixin = "private enum routableViews = ["; - bool hasRoutes = false; + auto routableViewsMixin = "private static __gshared string[string] _routableViews; private @property routableViews() { if (_routableViews && _routableViews.length) return _routableViews; string[string] aa;\r\n"; + // bool hasRoutes = false; version (WebServer) { enum pageClassFormat = q{ @@ -175,8 +175,8 @@ version (Not_WebService) { version (WebServer) { case "route": { auto stripped = value.strip().replace("\n", ""); - routableViewsMixin ~= format("\"%s\" : \"%s\",", stripped, pageName); - hasRoutes = true; + routableViewsMixin ~= format("aa[\"%s\"] = \"%s\";\r\n", stripped, pageName); + //hasRoutes = true; break; } } @@ -246,11 +246,11 @@ version (Not_WebService) { ); } - if (hasRoutes) { - routableViewsMixin.length -= 1; - } + // if (hasRoutes) { + // routableViewsMixin.length -= 1; + // } - routableViewsMixin ~= "];"; + routableViewsMixin ~= "_routableViews = aa; return _routableViews; }"; viewMixin ~= routableViewsMixin; return viewMixin; diff --git a/src/views/viewimports.d b/src/views/viewimports.d index c43fea9..b67b2db 100644 --- a/src/views/viewimports.d +++ b/src/views/viewimports.d @@ -11,15 +11,14 @@ version (Not_WebService) { /// Generates the collection of views and imports their content mixin template ViewImports() { auto generateViewImports() { - auto viewFormat = "\"%s\" : import(\"%s\"),"; - auto viewsResult = "private enum viewInitCollection = ["; + auto viewFormat = "aa[\"%s\"] = import(\"%s\");"; + auto viewsResult = "private @property auto viewInitCollection() { string[string] aa;"; foreach (pageName,pageContent; views) { viewsResult ~= viewFormat.format(pageName,pageContent); } - viewsResult.length -= 1; - viewsResult ~= "];"; + viewsResult ~= "return aa; }"; return viewsResult; }