1 module action_dispatch.controller; 2 3 import vibe.d; 4 public import dynamic_loader.dynamic_class; 5 6 import std.regex; 7 import action_dispatch.format; 8 9 class ActionController : DynamicClass { 10 mixin DynamicClassImplementation!(); 11 12 protected { 13 HTTPServerRequest _request; 14 HTTPServerResponse _response; 15 string _format; 16 } 17 18 @property { 19 HTTPServerRequest request() { 20 return _request; 21 } 22 23 HTTPServerResponse response() { 24 return _response; 25 } 26 27 void request(HTTPServerRequest request) { 28 _request = request; 29 } 30 31 void response(HTTPServerResponse response) { 32 _response = response; 33 } 34 35 void format(string format) { 36 _format = format; 37 } 38 } 39 40 void handleRequest(HTTPServerRequest req, HTTPServerResponse res, string action) { 41 try { 42 _request = req; 43 _response = res; 44 _format = req.params["format"]; 45 __send__(action); 46 } finally { 47 _request = null; 48 _response = null; 49 _format = null; 50 } 51 } 52 53 @DynamicallyAvailable 54 void assets() { 55 serveStaticFiles("./public/")(_request, _response); 56 } 57 58 protected { 59 void respondTo(string format, void delegate() yield) { 60 if (_format == format) 61 yield(); 62 } 63 64 void respondTo(void delegate(Format) yield) { 65 auto format = new Format(_format); 66 yield(format); 67 } 68 } 69 70 static { 71 ActionController loadController(string controllerName, string prefix = "action_dispatch.controller") { 72 string[] builder; 73 if (prefix.length > 0) 74 builder ~= prefix; 75 76 builder ~= controllerName; 77 78 string factory = join(builder, "."); 79 auto controller = cast(ActionController) Object.factory(factory); 80 81 if (controller is null) { 82 foreach (string cName; getAllDynamicClasses()) { 83 if (match(cName, controllerName)) { 84 return loadController(cName, ""); 85 } 86 } 87 throw new Exception("Your class " ~ prefix ~ "." ~ controllerName ~ " isn't callable dynamically. Classes are: " ~ to!string(getAllDynamicClasses())); 88 } 89 90 return controller; 91 } 92 } 93 } 94 95 unittest { 96 import dunit.toolkit; 97 98 struct F { int a; } 99 100 class FooController : ActionController { 101 mixin DynamicClassImplementation!(); 102 103 void foo(out F _foo) { 104 respondTo(delegate void(Format format) { 105 format.html(delegate void() { _foo.a = 3; }); 106 }); 107 } 108 } 109 110 // This is a super janky test; basically we're testing that the 111 // new version of #respondTo works correctly by setting up 112 // the FooController which modifies the `f` variable only 113 // when the format is "html" 114 F f = { a: 0 }; 115 auto foo = new FooController; 116 foo.format = "html"; 117 foo.foo(f); 118 f.a.assertEqual(3); 119 120 // The negative side of the test just makes sure that it doesn't 121 // modify the z struct when the format is set to "json" 122 F z = { a: 0 }; 123 foo.format = "json"; 124 foo.foo(z); 125 z.a.assertEqual(0); 126 }