1 module handlebars.components.each; 2 3 import handlebars.components.base; 4 import handlebars.tpl; 5 6 import std.exception; 7 import std.conv; 8 import std.traits; 9 import std.algorithm; 10 11 /// Component that will handle the if blocks 12 class EachComponentCt(Token[] tokens, Properties properties) : HbsComponent!"" { 13 enum ComponentName = "each"; 14 15 /// 16 string render(T, Components...)(T controller) { 17 string result; 18 19 static if(properties.localName != "") { 20 mixin(`alias NameType = typeof(controller.` ~ properties.name ~ `);`); 21 static if(isCallable!(NameType)) { 22 alias __Type = ForeachType!(ReturnType!NameType); 23 } else { 24 alias __Type = ForeachType!(NameType); 25 } 26 } 27 28 class TmpController { 29 private T controller; 30 31 static if(properties.localName != "") { 32 mixin(`Unqual!(__Type) ` ~ properties.localName ~ `;`); 33 } 34 35 static if(properties.indexName != "") { 36 mixin(`string ` ~ properties.indexName ~ `;`); 37 } 38 39 mixin(genControllerFields!T()); 40 } 41 42 auto tmpController = new TmpController; 43 tmpController.controller = controller; 44 45 mixin(genForeach()); 46 47 return result; 48 } 49 50 private static string genForeach() { 51 string result; 52 53 result ~= `foreach(i, value; controller.` ~ properties.name ~ `) {`; 54 55 static if(properties.localName != "") { 56 result ~= `tmpController.` ~ properties.localName ~ ` = value;`; 57 } 58 59 static if(properties.indexName != "") { 60 result ~= `tmpController.` ~ properties.indexName ~ ` = i.to!string;`; 61 } 62 63 result ~= `result ~= handlebars.tpl.render!(tokens, TmpController, Components)(tmpController);`; 64 65 result ~= `}`; 66 67 return result; 68 } 69 70 private static string genControllerFields(T)() { 71 static immutable ignoredMembers = [ __traits(allMembers, Object), "this", "ComponentName", "lifecycle", 72 "render", properties.localName, properties.indexName ]; 73 74 string result; 75 76 static foreach (memberName; __traits(allMembers, T)) {{ 77 static if(!ignoredMembers.canFind(memberName)) { 78 enum protection = __traits(getProtection, __traits(getMember, T, memberName)); 79 80 static if (protection == "public") { 81 mixin(`alias field = T.` ~ memberName ~ `;`); 82 83 static if(isCallable!(field) && arity!field > 0) { 84 result ~= `auto ` ~ memberName ~ `(` ~ genParams!(Parameters!field) ~ `) { 85 return controller.` ~ memberName ~ `(` ~ genVals!(Parameters!field) ~ `); 86 }`; 87 } else { 88 result ~= `auto ` ~ memberName ~ `() { return controller.` ~ memberName ~ `; }`; 89 } 90 } 91 } 92 }} 93 94 return result; 95 } 96 97 private static string genParams(T...)() { 98 string result; 99 100 return result; 101 } 102 103 private static string genVals(T...)() { 104 string result; 105 106 return result; 107 } 108 } 109 110 /// Component that will handle the if blocks 111 class EachComponent : HbsComponent!"" { 112 enum ComponentName = "each"; 113 114 private { 115 string name; 116 string localName; 117 string indexName; 118 } 119 120 /// 121 this(Properties properties) { 122 enforce!RenderComponentException(properties.list.length == 0, "list as |item| or list as |item index| expected"); 123 enforce!RenderComponentException(properties.name != "", "list as |item| or list as |item index| expected"); 124 enforce!RenderComponentException(properties.localName != "", "list as |item| or list as |item index| expected"); 125 126 this.name = properties.name; 127 this.localName = properties.localName; 128 this.indexName = properties.indexName; 129 } 130 131 /// 132 string render(Component, Components...)() { 133 string result; 134 135 long len = this.lifecycle.evaluateLong(this.name ~ ".length"); 136 137 foreach(i; 0..len) { 138 Token[] list; 139 string props = `"` ~ name ~ `" "` ~ localName ~ `" "` ~ i.to!string ~ `" `; 140 141 if(indexName != "") { 142 props ~= `"` ~ indexName ~ `"`; 143 } else { 144 props ~= `""`; 145 } 146 147 list ~= Token(Token.Type.openBlock, "scope", Properties(props)); 148 list ~= this.content; 149 list ~= Token(Token.Type.closeBlock, "scope"); 150 151 result ~= lifecycle.yield(list); 152 } 153 154 return result; 155 } 156 }