When the configuration is passed from the outside, it has to be validated.
While Go provides a little bit more control on the parser than Ruby, it still misses the validation part.
YAML parsing in Go looks like this:
type Configuration struct {
InstanceIP string
}func Load(path string) (Configuration, error) {
...
config := Configuration{}
err := yaml.Unmarshal(data, &config)
return config, err
}
Unfortunately, when the configuration is parsed this way, it can still be invalid. This means that with every use, the configuration has to be validated.
One of the ways to solve this problem is to validate the configuration during the parsing.
In order to do this, we need to introduce a new type which can be parsed separately and implement an interface so it can be unmarshaled from YAML.
type IP stringfunc (ip *IP) UnmarshalYAML(u func(interface{}) error) error {
return nil
}
Test-driven development is the easiest way to understand how actually implement the parsing.
Here is the simplest test I was able to implement after several iterations.
Describe("IP", func() {
It("unmarshals valid IP", func() {
ip := new(parser.IP)
err := ip.UnmarshalYAML(func(s interface{}) err {
str := s.(*string)
*str = "8.8.8.8"
return nil
})
Expect(err).ToNot(HaveOccurred())
Expect(*ip).To(Equal(parser.IP("10.1.1.1")))
})
})Let’s first analyse what is happening in the test.
In order to parse YAML special way — your object has to implement yaml.Unmarshaler interface. In has only 1 method UnmarshalYAML which has to be defined on the object. (Note: in order to save YAML, you have to implement Marshaler interface with MarshalYAML method).
This method expects another function to be passed in. What is this function? I think this is one of the hardest thing to grok with this library. YAML is getting read in one of the primitives first and then passed this value further. So when you parsing you actually converting one type to another.
Here is the naive implementation:
func (ip *IP) UnmarshalYAML(u func(interface{}) error) error {
var s string
u(&s)
*ip = IP(s)
return nil
}With that in mind, we created some simple tests that validate wrong IP addresses.
While sounds simple, it adds lots of overhead. If you don’t plan to use custom types that were used in parsing, it is much easier to parse YAML and than validate the configuration inside the Load method.
