Domore.Conf simplifies configuration of POCO classes using a syntax resembling a programming language, offering flexibility in setting configurations alongside whitespace and case insensitivity features.
Domore.Conf configures POCO classes with a syntax that resembles a programming language but is less strict in its use.
This is the first in a series of posts that describes how Brows leverages simple text for configuration and data.
Consider the following class:
class MyConfig {
public string Theme { get; set; }
public DayOfWeek WeekendStartsOn { get; set; }
}
A configuration file for that class might look like this:
My config . Theme = light
my config . Weekend starts on = Saturday
If that configuration file exists alongside an application’s .exe with the same name as the .exe but with different extension .conf, an instance may be configured by the following:
var obj = new MyConfig();
Conf.Configure(obj);
I’m sure you can guess what the output will be:
Console.WriteLine(obj.Theme);
Console.WriteLine(obj.WeekendStartsOn);
If the file-naming convention described above was confusing, here’s an example that should clear things up. An executable named my-process.exe would have a configuration file in the same directory with the name my-process.conf.
So Far Things Seem Pretty Simple. Let’s Make Them Simpler.
Change the configuration file to look like this:
Theme = dark
Weekend starts on = Saturday
And configure the object with a blank key.
Conf.Configure(obj, key: "");
If a key is not provided, or is null
, the name of the type will be used to prefix configuration properties. If a blank key is passed (string.Empty
), each line is a potential configuration property.
That behavior allows some interesting conveniences. Suppose the class above is subclassed.
class PartyAnimal : MyConfig { }
class CollegeStudent : PartyAnimal { }
And the configuration changes to this:
Theme = dark
Weekend starts on = Friday
college student . weekend starts on = thursday
Instances of each class may be configured by either or both a blank and null
key.
var pa = Conf.Configure(new PartyAnimal(), key: "");
var cs = Conf.Configure(new CollegeStudent(), key: "");
Conf.Configure(cs);
The college student was configured twice, first with a blank key to gather generic configuration for all instances of MyConfig
, then with a key matching the name of the type. The output is as follows:
Console.WriteLine(pa.Theme);
Console.WriteLine(pa.WeekendStartsOn);
Console.WriteLine(cs.Theme);
Console.WriteLine(cs.WeekendStartsOn);
Both the party animal and the college student got the same configuration for their theme, but the college student has more specific configuration for the starting day of his weekend.
Notice that a new instance of each type was passed to Conf.Configure
. That same instance was returned from the method.
Let’s Set Some Rules That We’ve Seen So Far
- Whitespace in configuration keys is ignored.
- Configuration keys are case-insensitive.
- Configuration key parts are separated by a dot (
.
).
Now let’s clean up that college student and send him to class.
class CollegeStudent {
public List<DayOfWeek> ClassSchedule { get; set; }
}
The class schedule could be configured with this .conf file.
student.class schedule[0] = monday
student.class schedule[1] = wednesday
student.class schedule[2] = friday
But that’s a little clunky. It’s more readable and easier to maintain like this.
student.class schedule = monday, Wednesday, Friday
In the above example, neither a blank key nor the type name was used as the key. Rather, student
may be used by calling Conf.Configure
with an arbitrary key, i.e.:
Conf.Configure(new CollegeStudent(), key: "student")
College Isn’t That Easy. It’s a Little More Complex.
class CollegeProfessor {
public string Name { get; set; }
}
class CollegeClass {
public CollegeProfessor Professor { get; set; }
public List<DayOfWeek> Schedule { get; set; }
public TimeSpan Time { get; set; }
}
class CollegeStudent {
public Dictionary<string, CollegeClass> Class { get; set; }
}
That student’s schedule can be configured with this .conf file by calling:
Conf.Configure(new CollegeStudent(), key: "")
class[Chemistry 101].time = 8:00
class[Chemistry 101].schedule = Monday, Wednesday, Friday
class[Chemistry 101].professor.name = Marie Curie
class[ Physics 200 ].time = 10:30
class[ Physics 200 ].schedule = Tuesday, Wednesday
class[ Physics 200 ].professor.name = Georges Lemaître
Dictionary keys follow a few rules different from standard keys.
- Whitespace before and after the key is ignored.
- Whitespace within the key is respected.
- Case is respected. To ignore the case of a dictionary’s keys, create it with a case-insensitive comparer.
That’s It for Now, But There’s More to Cover.
In a future post, I’ll go over custom configuration parsing, attributes for configuring your configuration, and reversing the whole process to convert a POCO class into a configuration file.