1 module vibrant.routes; 2 3 mixin template Routes(string ResourceName = "") { 4 5 import std.typetuple; 6 import dquery.d; 7 8 private { 9 10 /++ 11 + The current request parameters. 12 ++/ 13 Parameter params; 14 15 /++ 16 + The current HTTP Request object. 17 ++/ 18 HTTPServerRequest request; 19 20 /++ 21 + The current HTTP Response object. 22 ++/ 23 HTTPServerResponse response; 24 25 /++ 26 + Content type definitions for render!(). 27 ++/ 28 enum { 29 TEXT = "text/plain", 30 JS = "application/javascript", 31 JSON = "application/json", 32 XML = "application/xml", 33 XHTML = "application/xhtml+xml", 34 HTML = "text/html", 35 CSS = "text/css", 36 EMPTY = "@empty" 37 } 38 39 @property 40 static string resourceName(this This)() { 41 static if (ResourceName == "") { 42 // Prefix from type name. 43 return "/" ~ This 44 .stringof 45 .toSnakeCase 46 .stripSuffix!"_controller"; 47 } else { 48 return ResourceName; 49 } 50 } 51 52 @property 53 template hasFunction(This, string name) { 54 enum hasFunction = Alias!( 55 !query!This() 56 .functions 57 .arity!(0) 58 .name!name 59 .empty 60 ); 61 } 62 63 @property 64 template getFunction(This, string name) if (hasFunction!(This, name)) { 65 alias getFunction = Alias!( 66 query!This() 67 .functions 68 .arity!(0) 69 .name!name 70 .first 71 .value 72 ); 73 } 74 75 static VoidCallback createCallback(This, string name)() { 76 return delegate void(HTTPServerRequest req, HTTPServerResponse res) { 77 // Create the controller. 78 This controller = new This; 79 80 // Setup request and response. 81 controller.request = req; 82 controller.response = res; 83 controller.params = req.createParameters; 84 85 // Call the controller action. 86 controller.getFunction!(This, name)(); 87 }; 88 } 89 90 } 91 92 /++ 93 + Renders a plaintext response. 94 ++/ 95 @property 96 public void render(Body)(Body content) { 97 return render!(Text, Body)(content); 98 } 99 100 /++ 101 + Renders a response with a given content type. 102 ++/ 103 @property 104 public void render(string contentType, Body)(Body content) 105 if (contentType.length > 0 && contentType[0] != '@') { 106 import std.conv : to; 107 108 response.writeBody(to!string(content), contentType); 109 } 110 111 /++ 112 + Renders a response with a given content type and status code. 113 ++/ 114 @property 115 public void render(string contentType, Body)(Body content, int code) 116 if (contentType.length > 0 && contentType[0] != '@') { 117 response.statusCode = code; 118 render!(contentType, Body)(content); 119 } 120 121 /++ 122 + Renders an empty response. 123 ++/ 124 @property 125 public void render(string contentType)() if (contentType == EMPTY) { 126 response.writeBody(""); // Write an empty body. 127 } 128 129 /++ 130 + Renders an empty response and the given status code. 131 ++/ 132 @property 133 public void render(string contentType)(int code) if (contentType == EMPTY) { 134 response.statusCode = code; 135 render!contentType; 136 } 137 138 /++ 139 + Installs the controller routes into a Vibrant router. 140 ++/ 141 public static void install(bool Bool)(VibrantRouter!Bool router) { 142 alias This = Alias!(typeof(this)); 143 144 // 'index' route; display all. 145 static if (hasFunction!(This, "index")) { 146 router.Get(resourceName!This, createCallback!(This, "index")); 147 } 148 149 // 'me' route; display my object. 150 static if (hasFunction!(This, "me")) { 151 router.Get(resourceName!This, createCallback!(This, "me")); 152 } 153 154 // 'new' route; show new form. 155 static if (hasFunction!(This, "new")) { 156 router.Get(resourceName!This ~ "/me", createCallback!(This, "new")); 157 } 158 159 // 'create' route; create object. 160 static if (hasFunction!(This, "create")) { 161 router.Post(resourceName!This, createCallback!(This, "create")); 162 } 163 164 // 'show' route; display an object. 165 static if (hasFunction!(This, "show")) { 166 router.Get(resourceName!This ~ "/:id", createCallback!(This, "show")); 167 } 168 169 // 'edit' route; display edit form. 170 static if (hasFunction!(This, "edit")) { 171 router.Get(resourceName!This ~ "/:id/edit", createCallback!(This, "edit")); 172 } 173 174 // 'update' route; edit an object. 175 static if (hasFunction!(This, "update")) { 176 router.Put(resourceName!This ~ "/:id", createCallback!(This, "update")); 177 178 router.Patch(resourceName!This ~ "/:id", createCallback!(This, "update")); 179 } 180 181 // 'destroy' route; delete an object. 182 static if (hasFunction!(This, "destroy")) { 183 router.Delete(resourceName!This ~ "/:id", createCallback!(This, "destroy")); 184 } 185 } 186 187 }