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; 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 // context variables 32 Variant[string] vars; 33 34 35 package: 36 const(CompiledTemple)* partial; 37 38 /// package 39 // TODO: This needs to be marked as a "safe" string 40 static Appender!string __templeRenderWith(in CompiledTemple temple, TempleContext ctx) 41 body 42 { 43 // Allocate a buffer and call the render func with it 44 auto buff = appender!string; 45 temple.render(buff, ctx); 46 return buff; 47 } 48 49 // sink for rendering templates to 50 TempleOutputStream sink; 51 52 // called by generated temple function 53 void put(string s) { 54 sink.put(s); 55 } 56 57 public: 58 string capture(T...)(void delegate(T) block, T args) 59 { 60 auto saved = this.sink; 61 scope(exit) this.sink = saved; 62 63 auto buffer = appender!string; 64 this.sink = TempleOutputStream(buffer); 65 66 // Call the block (which resides inside the template, and will 67 // now write to `buffer`) 68 block(args); 69 70 return buffer.data; 71 } 72 73 bool isSet(string name) 74 { 75 return (name in vars && vars[name] != Variant()); 76 } 77 78 ref Variant var(string name) 79 { 80 if(name !in vars) 81 vars[name] = Variant(); 82 83 return vars[name]; 84 } 85 86 void opIndexAssign(T)(string name, T val) { 87 if(name !in vars) 88 vars[name] = Variant(); 89 90 vars[name] = val; 91 } 92 93 VarDispatcher var() 94 { 95 return VarDispatcher(this); 96 } 97 98 Variant opDispatch(string op)() @property 99 if(op != "__ctor") // seems a scoped!T bug requires this 100 in { 101 assert(op in vars, "variant does not have key: " ~ op); 102 } 103 body { 104 return vars[op]; 105 } 106 107 void opDispatch(string op, T)(T other) @property 108 { 109 vars[op] = other; 110 } 111 112 TempleInputStream yield() @property 113 { 114 auto noop = TempleInputStream(delegate(ref TempleOutputStream) { 115 //debug debug_writeln("yielded input stream called (was a noop)"); 116 }); 117 118 if(partial !is null) 119 { 120 return TempleInputStream(delegate(ref TempleOutputStream os) { 121 partial.render(os, this); 122 }); 123 } 124 125 return noop; 126 } 127 } 128 129 private struct VarDispatcher 130 { 131 private: 132 TempleContext context; 133 134 public: 135 this(TempleContext context) 136 { 137 this.context = context; 138 } 139 140 ref Variant opDispatch(string op)() @property 141 { 142 return context.var(op); 143 } 144 145 void opDispatch(string op, T)(T other) @property 146 { 147 context.var(op) = other; 148 } 149 } 150 151 unittest 152 { 153 auto context = new TempleContext(); 154 context.foo = "bar"; 155 context.bar = 10; 156 157 with(context) 158 { 159 assert(var.foo == "bar"); 160 assert(var.bar == 10); 161 162 var.baz = true; 163 assert(var.baz == true); 164 } 165 }