1 /** 2 Copyright: © 2016 Chris Barnes 3 License: The MIT License, see license file 4 Authors: Chris Barnes 5 */ 6 module runtimer.configuration; 7 8 import std.conv; 9 import std.file; 10 import std.getopt; 11 import std.json; 12 import vibe.data.json; 13 14 /** 15 Represents application configuration where a struct T maps to some JSON configuration. 16 */ 17 class Configuration(T) 18 { 19 /** 20 Creates a new configuration object wihout any command line overrides. 21 Params: 22 path = path to a JSON file containing the default path and configuration filename 23 */ 24 this(string path = "configuration.json") 25 { 26 this.path = path; 27 } 28 29 /** 30 Creates a new configuration object given the command line override args array. 31 Params: 32 path = path to a JSON file containing the default path and configuration filename 33 */ 34 this(string[] args, string path = "configuration.json") 35 { 36 getopt(args, 37 "e|environment", &environmentArgument, 38 "p|path", &pathArgument 39 ); 40 this(path); 41 } 42 43 /** 44 Reads in the current configuration into this.application 45 */ 46 void initialize() 47 { 48 JSONValue[string] configuration; 49 if(this.pathArgument == "") 50 { 51 configuration = parseJSON(to!string(read(this.path))).object; 52 } 53 string defaultEnvironment = this.environmentArgument != "" ? this.environmentArgument : configuration["default-environment"].str; 54 string environmentPath = this.pathArgument != "" ? this.pathArgument : configuration["environment-path"].str; 55 this.application = deserializeJson!T(to!(string)(read(environmentPath ~ "/" ~ defaultEnvironment ~ ".json"))); 56 } 57 58 T application; 59 60 private: 61 string path; 62 string environmentArgument; 63 string pathArgument; 64 } 65 66 version(unittest) 67 { 68 struct Host 69 { 70 string name; 71 ushort port; 72 } 73 74 struct Credentials 75 { 76 string username; 77 string password; 78 } 79 80 struct Http 81 { 82 Host host; 83 } 84 85 struct Data 86 { 87 Host host; 88 Credentials credentials; 89 string database; 90 } 91 92 struct Assets 93 { 94 string directory; 95 string files; 96 } 97 98 struct Logging 99 { 100 string level; 101 string path; 102 } 103 104 struct App 105 { 106 string name; 107 Http http; 108 Data data; 109 Assets assets; 110 Logging logging; 111 } 112 } 113 114 unittest 115 { 116 auto configuration = new Configuration!(App)("fixtures/configuration.json"); 117 configuration.initialize(); 118 assert(configuration.application.name == "some-name"); 119 assert(configuration.application.http.host.port == to!short(3000)); 120 assert(configuration.application.http.host.name == "127.0.0.1"); 121 122 assert(configuration.application.data.host.name == "127.0.0.1"); 123 assert(configuration.application.data.host.port == to!short(5432)); 124 assert(configuration.application.data.database == "PostgreSQL"); 125 assert(configuration.application.data.credentials.username == "admin"); 126 assert(configuration.application.data.credentials.password == "password"); 127 128 assert(configuration.application.assets.directory == "public"); 129 assert(configuration.application.assets.files == "*"); 130 131 assert(configuration.application.logging.level == "debug"); 132 assert(configuration.application.logging.path == "log/test.log"); 133 } 134 135 unittest 136 { 137 auto configuration = new Configuration!(App)(); 138 assert(configuration.path == "configuration.json"); //don't init, just make sure the default path is in place 139 } 140 141 version(unittest) 142 { 143 struct AnotherApp 144 { 145 string name; 146 } 147 } 148 149 unittest 150 { 151 //Make sure the terse switches work 152 auto configuration = new Configuration!(AnotherApp)(["_", "-e", "development", "-p", "fixtures/environments"]); 153 configuration.initialize(); 154 assert(configuration.application.name == "development environment"); 155 } 156 157 unittest 158 { 159 //Make sure the verbose switches work 160 auto configuration = new Configuration!(AnotherApp)(["_", "--environment", "development", "--path", "fixtures/environments"]); 161 configuration.initialize(); 162 assert(configuration.application.name == "development environment"); 163 }