More CRUDGEON Updates!

Using more reflection, I made some of CRUDGEON a bit more clean, and easier to update in the future. But more work is needed!

I've been on a tear lately, updating CRUDGEON. There are a bunch of spots that could use improvement. I did basically cobble together a bunch of things and it works. I don't like much of the code, particularly around the “flags” portion, which is basically the core of it.

In CRUDGEON, you can define flags on a field or an object. This is part of the “behavior driven” core. If you are generating database scripts and want to exclude certain types of fields, like a select should only include fields that are indexed or keys, you can say for a specific generation that I only want indexes or keys. Voila. It looks like this

//+class Author
//+xmlignore,+jsonignore
string AuthorID //+key
string Name
string DateOfBirthString //+dbignore
DateTime DateOfBirth //+parsefromstring DateOfBirth time.Time{}

The flags in question for this update are the +class and +xmlignore. These are eventually handled by the templates when generating code. The template can interpret the +xmlignore or not, as it sees fit. In my C# classes, if it's not “+xmlignore”, then I will generate XML attributes on the classes, and include XML includes in the import declarations.

We'll talk about these two specifically (xmlignore and class), as they cover all of my use cases. So basically what I was doing was manually parsing these. I had a switch statement. The “class” flag is a special type in that it also has a string value.

flg := s[0] == ‘+’
strval := // get string value
switch flgname {
   case “xmlignore”:  flags.XmlIgnore = flg
   case “class”:  flags.Class = flg;  flags.ClassName = strval
}

This is in a big loop processing these, splitting on “,” as necessary, etc. It worked even though I may not have working pseudocode above :)

Now I have a struct in Go, it looks like this.

type GenFlags struct {
    XmlIgnore bool `flag:"xmlignore"`
    Class bool `flag:"class" value:"ClassName"`
    ClassName string
}

And using reflection, I'm able to parse those same values and set the appropriate fields based on the structure!

This commit covers it. Basically I have a “FlagSetter” struct which first parses the struct tags and stores them in maps (one for flag and one for flag value)

func (fs *FlagSetter) parseTags() {
    fs.m = make(map[string]string)
    fs.vm = make(map[string]string)
    for i := range fs.t.NumField() {
        fld := fs.t.Field(i)
        flagtag := fld.Tag.Get("flag")
        if flagtag != "" {
            fs.m[flagtag] = fld.Name
        }

        valtag := fld.Tag.Get("value")
        if valtag != "" {
            fs.vm[flagtag] = valtag
        }
    }
}

Setting the flags involves a bunch of Go reflect package. This can be viewed on line 51. I really like anything involving reflection and dynamic programming. It is an obvious choice this kind of thing, and also looking to architect your code to have it able to use time savers like reflection. It's not always about saving time now, but saving time in the future. Until you want to do something that doesn't fit in the same paradigm :P  But then you get to write more reflection code!

To use this code, you can send it a bunch of flags in the format “+flagname (optional value)” and it'll keep updating the internal flag object with the flags and values. I updated the parsing code to use this, ran it, and nothing changed in the output (a great sign!).  Because my script that runs this executable first removes the output folders, and then generates the code. This is important for when you actually want to delete things. But also a great sign that it worked the same exact way, since nothing changed.

Next, I want to get away from properties at all! But that will be tricky. The way the templates work, it can just access the property.

Like {{ .Flags.XmlIgnore }}. 

I have a mechanism for having custom tags, and I don't want to make everything a custom tag. Some are built into the functionality in the exe, not just the templates. And that's like 

{{ if bitflag “CustomFlag” }} Custom flag is set! {{ end }}

But to fool the templates somehow into thinking that “CustomFlag” is actually a property, so it's more like

{{ if .Flags.CustomFlag }} Custom flag is set! {{ end }}

That could be pretty nice. But that will involve using the reflect package to actually build a struct, which is possible! It would be a dynamically created struct with dynamically created fields. It's doable. And I will go down that path in my testing :)

Happy coding!