/genesis

An input-agnostic code generator written in .Net Core / Standard. (v3 preview at the moment)

Primary LanguageC#MIT LicenseMIT

Genesis .Net


A framework for generating "things" based on a schema from a "source". It's designed to make starting a project a lot faster. It's entirely configurable; even scriptable.

How does it work?


Genesis is centered around a group of ObjectGraph objects and pieces of code that manipulate them, called Executors.

There are currently two types of executors, though more could certainly be added.

Input executors deal with a "source". They're responsible for interrogating some data store (or weburl, or text file, or...) and populating a group of ObjectGraphs. They're available to all other executors at any point. (It's currently serial execution)

Output executors do the other half of the work. They can use the data in the ObjectGraphs to write out classes, services, interfaces, clients etc. Anything really. They don't even have to write code.

  • They each have their own configuration.json as well as an auto-mapped & Typed Configuration object.

  • Generators can have dependencies, or support files required to make the generated code work.

  • Configuration of executors may also be done by commands at the Genesis Prompt.

genesis>


Genesis is a console application based on Microsoft's .Net standard/core. The commands are fairly simple to implement and use. Its usage and syntax are common to a shell.

Here is a basic syntax of console commands:

commandName [argument1] [anotherArg="Some Value"] 

The '?' command will list all of the commands that have been discovered. (They're extensible) alt text

Commands


An example of the scan command. This loads configurations and initializes new executors. There's probably a case for it to automatically scan on startup, but for now you have to trigger it. alt text

This will let you know what the commands they expose actually are. Once you know that you're able to manipulate their configurations and / or execute them.

scan
Loads a default configuration and initializes executors that have been discovered.
status
Displays information about the current state of "things". You can see what's been discovered, what the names of executors are etc.
add
Add an executor to the current Chain. The chain is a linked list of executors. It's not quite a pipeline in that they don't feed into each other. They do share the same context though.
exec
Tells an executor to run immediately, or tells the chain to execute sequentially

There are quite a few more commands, but those should produce some results.

Configuration


In addition to a .json file for each executor, they're able to be configured during execution from the prompt.

Say I wanted to set the connection string for an sql input executor:

scan

To prepare things and list out what executors are available. One of them is 'mssql'.

Use the config command to modify a configuration.

config mssql ConnectionString="Server=test;Database=db"

This changes the value of the ConnectionString property that lives on that executor's configuration class. Defaults are loaded at scan time and may be overwritten. alt text

  • non-string types don't use quotes

Executing Executors


Once things are configured the way you want them, you would execute a generator.

Here is the mssql executor being used to populate a few ObjectGraphs for the other executors: alt text

At this point, any other executor (Generator / Output) would have ObjectGraphs that contained some schema.

Lets create some Plain Old C# Objects. (Pocos)

  • If you'll notice, there is an executor listed from the scan called pocos.

They can use this schema to write out / act upon / completeley ignore the schema that was identified.

Here is the 'poco' generator being executed after the 'mssql' populator has been executed. The result is a class file with properties for each column in the source (mssql database table) alt text Evidently it doesn't output anything right now. That'll be fixed ;)

Templates & Dependencies


Each output executor has a [GeneratingExecutorClassName].gen file that contains a simple "template" for it to use during its Execute procedure. In the case of the poco executor, it's just a class file that all the generated code will look like.

There are search and replace tokens that the generator can use.

They have a [GeneratingExecutorClassName].deps file that contains all of the dependencies that the specific piece of code it generates will need to run properly. (Base classes, interfaceses, abstractions etc.)

This sounds and looks like a pain in the ass


Agreed. It's not really though.

Fortunately, you can script it. Its yet another tool for your development toolbox. We all end up writing boilerplate code repetitively and often. When you realize you have code that will need typed a ton, write a Generator for them. More than likely they come from or are dictated-by some other source or authority.

This is obviously not perfect. It's a simple scriptable prompt that feeds a REPL line-by-line. It can read a .genesis ('script') file and pass along each line to the interpreter as if it were typed to the console.

It's activated by a `--script [path]' option:

genesis-cli --script "\path\to\script.genesis"

You could have a script to write out classes based on one source/input, then turn around and switch the input to something completely different, then write out different support classes with a seperate Generator.

What if?

  • Pretty odd to think about, but you could integrate it into a build chain.
  • Genesis doesn't HAVE to write out code. It could act on data that was within a table, rather than on its schema.
  • The sources could literally come from anywhere. There's a ton of "schema definition" out there on the web.
  • Generate a .Net Standard HttpClient wrapper for a specific Endpoint/Yaml.

There are tools to do pretty much anything this could do. Though, if they had executors for Genesis..., it would be pretty slick!