1 /** 2 * Temple (C) Dylan Knutson, 2013, distributed under the: 3 * Boost Software License - Version 1.0 - August 17th, 2003 4 * 5 * Permission is hereby granted, free of charge, to any person or organization 6 * obtaining a copy of the software and accompanying documentation covered by 7 * this license (the "Software") to use, reproduce, display, distribute, 8 * execute, and transmit the Software, and to prepare derivative works of the 9 * Software, and to permit third-parties to whom the Software is furnished to 10 * do so, all subject to the following: 11 * 12 * The copyright notices in the Software and this entire statement, including 13 * the above license grant, this restriction and the following disclaimer, 14 * must be included in all copies of the Software, in whole or in part, and 15 * all derivative works of the Software, unless such copies or derivative 16 * works are solely in the form of machine-executable object code generated by 17 * a source language processor. 18 */ 19 20 module temple.temple_context; 21 22 import temple.temple; 23 import temple.output_stream; 24 25 public import std.variant : Variant; 26 private import std.array, std.string, std.typetuple; 27 28 final class TempleContext 29 { 30 private: 31 // First hook is called to set the output buffer, the second is to unset it. 32 alias TemplateHooks = TypeTuple!(void delegate(OutputStream), void delegate()); 33 TemplateHooks[0][] pushBuffHooks; 34 TemplateHooks[1][] popBuffHooks; 35 36 Variant[string] vars; 37 38 TempleFuncType* yielded_template; 39 40 41 TemplateHooks[0] getPushBuffHook() 42 { 43 return pushBuffHooks[$-1]; 44 } 45 46 TemplateHooks[1] getPopBuffHook() 47 { 48 return popBuffHooks[$-1]; 49 } 50 51 package: 52 /// package 53 void __templePopHooks() 54 { 55 pushBuffHooks.length--; 56 popBuffHooks.length--; 57 } 58 59 /// package 60 void __templePushHooks(TemplateHooks h) 61 { 62 this.pushBuffHooks ~= h[0]; 63 this.popBuffHooks ~= h[1]; 64 } 65 66 /// package 67 static AppenderOutputStream __templeRenderWith(TempleFuncType* render_func, TempleContext ctx = null) 68 in { assert(render_func !is null); } 69 body 70 { 71 // Ensure a context is always present 72 if(ctx is null) 73 { 74 ctx = new TempleContext(); 75 } 76 // Allocate a buffer and call the render func with it 77 auto buff = new AppenderOutputStream(); 78 render_func(buff, ctx); 79 80 return buff; 81 } 82 83 public: 84 string capture(T...)(void delegate(T) block, T args) 85 { 86 auto buffer = new AppenderOutputStream(); 87 scope(exit) { buffer.clear(); } 88 89 // Make the template push to a new, blank buffer 90 this.getPushBuffHook()(buffer); 91 92 // Call the block (which resides inside the template, and will 93 // now write to `buffer`) 94 block(args); 95 96 // Pop `buffer` out of the template 97 this.getPopBuffHook()(); 98 99 return buffer.data; 100 } 101 102 bool isSet(string name) 103 { 104 return (name in vars && vars[name] != Variant()); 105 } 106 107 ref Variant var(string name) 108 { 109 if(name !in vars) 110 vars[name] = Variant(); 111 112 return vars[name]; 113 } 114 115 void opIndexAssign(T)(string name, T val) { 116 if(name !in vars) 117 vars[name] = Variant(); 118 119 vars[name] = val; 120 } 121 122 VarDispatcher var() 123 { 124 return VarDispatcher(this); 125 } 126 127 Variant opDispatch(string op)() @property 128 { 129 return vars[op]; 130 } 131 132 void opDispatch(string op, T)(T other) @property 133 { 134 vars[op] = other; 135 } 136 137 /** 138 * Methods/properties relating to layout support 139 */ 140 void partial(TempleFuncType* temple_func) @property 141 { 142 yielded_template = temple_func; 143 } 144 145 auto partial() @property 146 { 147 return yielded_template; 148 } 149 150 AppenderOutputStream yield() @property 151 { 152 auto buff = new AppenderOutputStream(); 153 154 if(yielded_template !is null) 155 { 156 (*yielded_template)(buff, this); 157 } 158 159 return buff; 160 } 161 } 162 163 private struct VarDispatcher 164 { 165 private: 166 TempleContext context; 167 168 public: 169 this(TempleContext context) 170 { 171 this.context = context; 172 } 173 174 ref Variant opDispatch(string op)() @property 175 { 176 return context.var(op); 177 } 178 179 void opDispatch(string op, T)(T other) @property 180 { 181 context.var(op) = other; 182 } 183 } 184 185 unittest 186 { 187 auto context = new TempleContext(); 188 context.foo = "bar"; 189 context.bar = 10; 190 191 with(context) 192 { 193 assert(var.foo == "bar"); 194 assert(var.bar == 10); 195 196 var.baz = true; 197 assert(var.baz == true); 198 } 199 }