1 module handlebars.components.all; 2 3 public import handlebars.components.base; 4 public import handlebars.components.each; 5 public import handlebars.components.scope_; 6 public import handlebars.components.if_; 7 8 9 import handlebars.helper; 10 11 import std.algorithm; 12 import std.traits; 13 import std.exception; 14 import std.conv; 15 16 /// 17 struct ComponentGroup(Components...) { 18 19 static: 20 /// 21 bool exists(string componentName) { 22 static foreach(Component; Components) {{ 23 24 static if (__traits(hasMember, Component, "ComponentName")) { 25 immutable name = Component.ComponentName; 26 } else { 27 immutable name = Component.stringof; 28 } 29 30 if(componentName == name) { 31 return true; 32 } 33 }} 34 35 return false; 36 } 37 38 /// 39 string get(T)(T controller, Token token, Token[] content, Lifecycle lifecycle) { 40 auto instance = getInstance(controller, token); 41 instance.lifecycle = lifecycle; 42 instance.content = content; 43 44 enforce!RenderComponentException(instance !is null, 45 "Can't initilize component `" ~ token.value ~ "`."); 46 47 static foreach(Component; Components) { 48 if(Component.stringof == token.value) { 49 setupFields(controller, cast(Component) instance, token.properties); 50 return (cast(Component)instance).render!(Component, Components)(); 51 } 52 53 static if(__traits(hasMember, Component, "ComponentName")) { 54 if(Component.ComponentName == token.value) { 55 setupFields(controller, cast(Component) instance, token.properties); 56 return (cast(Component)instance).render!(Component, Components)(); 57 } 58 } 59 } 60 61 throw new RenderComponentException("Can't render component `" ~ token.value ~ "`"); 62 } 63 64 /// 65 string get(Token[] tokens, T)(T controller, Lifecycle lifecycle) { 66 enum token = tokens[0]; 67 68 static if(tokens.length >= 1) { 69 enum content = tokens[1..$-1]; 70 } else { 71 enum content = Token.empty; 72 } 73 74 auto instance = getInstance!(token, content)(controller); 75 instance.lifecycle = lifecycle; 76 77 enforce!RenderComponentException(instance !is null, 78 "Can't initilize component `" ~ token.value ~ "`."); 79 80 return instance.render!(T, Components)(controller); 81 } 82 83 /// 84 IHbsComponent getInstance(T)(T controller, Token token) { 85 static foreach(Component; Components) {{ 86 static if (__traits(hasMember, Component, "ComponentName")) { 87 immutable name = Component.ComponentName; 88 } else { 89 immutable name = Component.stringof; 90 } 91 92 if(token.value == name) { 93 static if(__traits(hasMember, Component, "__ctor")) { 94 static foreach (t; __traits(getOverloads, Component, "__ctor")) { 95 96 static if(arity!t != 1 || (arity!t == 1 && !is(Parameters!t[0] == Properties))) { 97 if(arity!t == token.properties.list.length) { 98 mixin(genHelperValues!("ctor", Parameters!t)); 99 mixin("return new " ~ Component.stringof ~ "(" ~ genHelperParams!("ctor", Parameters!t) ~ ");"); 100 } 101 } 102 } 103 } else { 104 return new Component(); 105 } 106 } 107 }} 108 109 static foreach(Component; Components) {{ 110 static if (__traits(hasMember, Component, "ComponentName")) { 111 immutable name = Component.ComponentName; 112 } else { 113 immutable name = Component.stringof; 114 } 115 116 if(token.value == name) { 117 static if(__traits(hasMember, Component, "__ctor")) { 118 static foreach (t; __traits(getOverloads, Component, "__ctor")) { 119 static if(arity!t == 1 && is(Parameters!t[0] == Properties)) { 120 mixin("return new " ~ Component.stringof ~ "(token.properties);"); 121 } 122 } 123 } 124 } 125 }} 126 127 throw new RenderComponentException("The `"~token.value~"` component can't be rendered with the provided fields."); 128 } 129 130 /// 131 auto getInstance(Token token, Token[] content, T)(T controller) { 132 static foreach(Component; Components) { 133 static if (__traits(hasMember, Component, "ComponentName") && token.value == Component.ComponentName) { 134 alias SelectedComponent = Component; 135 } else static if(token.value == Component.stringof) { 136 alias SelectedComponent = Component; 137 } 138 } 139 140 return getInstance!(SelectedComponent, token.properties, content)(controller); 141 } 142 143 /// 144 auto getInstance(ClassName, Properties properties, Token[] content, T)(T controller) { 145 static if(content.length > 0) { 146 enum init = "new " ~ ClassName.stringof ~ "Ct!(content, properties)"; 147 } else { 148 enum init = "new " ~ ClassName.stringof; 149 } 150 151 static if(properties.list.length > 0 && __traits(hasMember, ClassName, "__ctor")) { 152 alias t = ClassName.__ctor; 153 154 mixin(genHelperPropertiesValues!("ctor", Parameters!t)); 155 enum ctorParams = `(` ~ genHelperParams!("ctor", Parameters!t) ~ `)`; 156 } else { 157 enum ctorParams = "()"; 158 } 159 160 mixin("return " ~ init ~ ctorParams ~ ";"); 161 } 162 163 /// 164 private void setupFields(T, U)(T controller, U instance, Properties properties) { 165 static immutable ignoredMembers = [ __traits(allMembers, Object), "ComponentName", "lifecycle" ]; 166 167 static foreach (memberName; __traits(allMembers, U)) {{ 168 enum protection = __traits(getProtection, __traits(getMember, U, memberName)); 169 170 static if(protection == "public" && memberName != "this" && memberName != "content" && !ignoredMembers.canFind(memberName)) { 171 mixin(`alias field = U.` ~ memberName ~ `;`); 172 173 static if(!isCallable!field && !is(typeof(field) == void)) { 174 enum property = `properties.hash["` ~ memberName ~ `"]`; 175 176 if(memberName in properties.hash) { 177 mixin(`if(!`~property~`.isEvaluated) { 178 instance.` ~ memberName ~ ` = evaluate!(` ~ typeof(field).stringof ~ `)(controller, ` ~ property ~ `.value); 179 }`); 180 181 static if(isSomeString!(typeof(field)) || isBuiltinType!(typeof(field))) { 182 mixin(`if(`~property~`.isEvaluated) { 183 instance.` ~ memberName ~ ` = `~property~`.value.to!(` ~ typeof(field).stringof ~ `); 184 }`); 185 } else { 186 mixin(`if(`~property~`.isEvaluated) { 187 throw new RenderComponentException("Can't pass evaluated property to ` ~ memberName ~ `"); 188 }`); 189 } 190 } 191 } 192 } 193 }} 194 } 195 }