1 2 //////////////////////////////////////////////////////////////////////////////////////////// 3 // Copyright (c) 2012 Christopher Nicholson-Sauls // 4 // // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy of this // 6 // software and associated documentation files (the "Software"), to deal in the // 7 // Software without restriction, including without limitation the rights to use, copy, // 8 // modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, // 9 // and to permit persons to whom the Software is furnished to do so, subject to the // 10 // following conditions: // 11 // // 12 // The above copyright notice and this permission notice shall be included in all // 13 // copies or substantial portions of the Software. // 14 // // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // 16 // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // 17 // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // 18 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // 19 // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // 20 // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // 21 //////////////////////////////////////////////////////////////////////////////////////////// 22 23 /** 24 * 25 */ 26 module zeal.http.router; 27 28 import std.metastrings; 29 import std.string; 30 31 import vibe.core.log; 32 33 import vibe.http.fileserver; 34 import vibe.http.router; 35 import vibe.http.server; 36 37 import zeal.config; 38 import zeal.inflector; 39 40 import zeal.base.controller; 41 42 import zeal.utils.singleton; 43 import zeal.utils.tuple; 44 45 import sass; 46 47 mixin ConfigImports; 48 49 50 /** 51 * 52 */ 53 class ZealRouter : UrlRouter { 54 mixin Singleton; 55 56 protected this () { 57 foreach ( _R; ArrayTuple!( ZealConfig!`resources` ) ) { 58 resource!( _R ); 59 } 60 routeAssets(); 61 } 62 63 /** 64 * 65 */ 66 final @property typeof( this ) match ( string _Path, string _C_A, string _Via_ = "any" ) () 67 if ( _Via_ == "delete" || _Via_ == "delete_" || _Via_ == "get" || _Via_ == "post" || _Via_ == "put" || _Via_ == "any" ) { 68 enum _Module = _C_A.parentize(); 69 enum _Controller = _Module.controllerize(); 70 enum _Action = _C_A.childize(); 71 enum _Via = _Via_ ~ ( _Via_ == "delete" ? "_" : "" ); 72 73 mixin(Format!( 74 q{ 75 import controllers.%s; 76 auto cb = %s().action!`%s`; 77 if ( cb == null ) { 78 throw new Exception( "Attempted to add route to nonexistant action: %s -> %s" ); 79 } 80 %s( "%s", cb ); 81 }, 82 _Module, 83 _Controller, _Action, 84 _Path, _C_A, 85 _Via, _Path 86 )); 87 return this; 88 } 89 90 /** 91 * 92 */ 93 final @property typeof( this ) resource ( _C : Controller ) ( _C c ) { 94 enum _Base = "/" ~ _C.stringof[ 0 .. $ - 10 ].decamelize(); 95 enum _New = _Base ~ "/new"; 96 enum _ID = _Base ~ "/:id"; 97 enum _Edit = _ID ~ "/edit"; 98 99 if ( auto a = c.action!`new` ) get ( _New , a ); 100 if ( auto a = c.action!`create` ) post ( _Base, a ); 101 if ( auto a = c.action!`index` ) get ( _Base, a ); 102 if ( auto a = c.action!`show` ) get ( _ID , a ); 103 if ( auto a = c.action!`edit` ) get ( _Edit, a ); 104 if ( auto a = c.action!`update` ) put ( _ID , a ); 105 if ( auto a = c.action!`destroy` ) delete_( _ID , a ); 106 107 static if ( is( typeof( c.route( this ) ) ) ) { 108 c.route( this ); 109 } 110 111 logInfo( "ZealRouter: added resource: %s.", _Base[ 1 .. $ ] ); 112 return this; 113 } 114 115 ///ditto 116 final @property typeof( this ) resource ( string _R ) () { 117 enum _Module = "controllers." ~ _R; 118 enum _Controller = _R.controllerize(); 119 120 mixin(Format!( 121 q{ 122 import %s; 123 resource!%s = %s(); 124 }, 125 _Module, 126 _Controller, _Controller 127 )); 128 return this; 129 } 130 131 /** 132 * 133 */ 134 final @property typeof( this ) root ( string _Via = "any", _Dummy = void ) ( Controller.Action cb ) { 135 mixin(Format!( 136 q{ 137 %s( "/", cb ); 138 }, 139 _Via 140 )); 141 return this; 142 } 143 144 ///ditto 145 final @property typeof( this ) root ( string _C_A, string _Via = "any" ) () { 146 match!( "/", _C_A, _Via ); 147 return this; 148 } 149 150 /** 151 * 152 */ 153 final typeof( this ) routeAssets () { 154 static done = false; 155 156 if ( !done ) { 157 auto assets = ZealConfig!`assets`; 158 159 auto dir = assets ~ `styles/`; 160 foreach ( style; ZealConfig!`styles` ) { 161 compileSass( dir, style ); 162 } 163 164 get( `*`, serveStaticFiles( ZealConfig!`assets` ) ); 165 166 done = true; 167 } 168 return this; 169 } 170 171 } // end class ZealRouter 172 173 174 /** 175 * 176 */ 177 private mixin template ConfigImports ( alias _List ) { 178 static if ( _List.length > 0 ) { 179 mixin(Format!( 180 `import controllers.%s;`, 181 _List[ 0 ] 182 )); 183 mixin ConfigImports!( _List[ 1 .. $ ] ); 184 } 185 } 186 187 private mixin template ConfigImports () { 188 mixin ConfigImports!( ZealConfig!`resources` ); 189 }