During the setup phase I have to make decisions about my blogging process: I have to come up with blogging workflow on the user level, choose core features I want and then decide which technology to use.
Static site generator: I’ll stick to this format as it is convenient for me. Taking into account that there are lots similar projects on github — this approach had been proven to work for other people well.
Main interface as CLI application: console apps have universal interface (i.e. POSIX), are portable and can be integrated into other applications. This means that Stagosaurus should be machine-friendly.
Graphic user interface atop CLI application: I am a human being so I need user-friendly interface. But there is a problem here: different people have different opinions on what is user friendly, so my requirement should be stated as: to have web-interface from the box. If it doesn’t work for you — adapt machine-friendly solution for your needs. Also I won’t be stating the obvious why user interface for me should be web. answer is simple here: I am Frontend Web Developer
Extensibility: I need steps
0⃣ Setup 1⃣ Write 2⃣ Render 3⃣ Deploy 4⃣ Promoteto be customizable (to some reasonable extent, of course). Most of existing solution rely on conventions made by their respectable authors and I want to have them stated explicitly.
Library: Stagosaurus would be shipped as a library, but there will be some ready ‘starter kits’ for easy start, so anyone could build up his/her own generator with conventions he/she likes. Maybe some of these will be pre-build, so these apps will be usable out of the box.
As it was stated before — I’ll be using Go programming language. It is fast, it is cross-platform and it is a good fit for building cli applications and doing system programming. Many of parts needed are already implemented: markdown to html parser, even several site/blog generators exist (gostatic, Goblog, trofaf and other), including my current generator.
However, there is one big limitation – go is a compiled language, so it would be impossible to have dynamic plug-in system, like the one in Sublime Text (which has one of the best extension systems I’ve ever seen). Still, go has built-in support for using github/bitbucket go libraries, so we can have extensibility, the only thing that you would have to recompile your generator. On the other hand, this will allow to have custom builds of site generators suited for some specific needs: blog, documentation site, etc.
The main thing that happens at the setup stage is configuration. Global configuration should be accessible to all stages of site generation and you can’t predict what params should be specified. This leads to conclusion that configuration should be generic.
I’ve did some analysis of the config implementation in various existing blog engines:
- almost all have configuration as static/global class (kirby, anchorcms, picocms), but some pass it via constructor (gostatic)
- some use hardcoded consts for configuration (begoon), some have hardcoded fields in config class (picocms, gostatic)
- many implement config as dictionary/associative array (anchorcms, nikola), which later could be meged with pre-configured defaults. Some allow only keys of value-type (strings mostly)
- some mix i/o with configuration, so it config should always be provided via some file, also gostatic has nice format for posts’ processing configuration
The solution for configuration I’ll be using is a dictionary (I prefer no to use word ‘map’ due to connotations it has in functional programming). Speaking strictly, dictionary is a set of triples: [key, value, default-value].
Currently it is implemented in struct
Config and has an optional
*Config with defaults.
There is no limitation of types of keys, most of the type value-types as keys will work great, but site generator can store runtime data in config too.
Dictionary values can be anything, but in practice it’s inconvenient to guess type by yourself (it’s in the example below), so for a some limited set of basic types (like strings, numbers, dates, etc.) I’ve added convenience functions similar to the type casting functions in go’s reflect package.
Also there is configuration validation: depending on workflow, different keys will be needed — so there should be a way of checking whether configuration has all needed data, both in user-friendly and machine-friendly way.
Another broad topic is serializing/deserializing. generic abstractions doesn’t play well with concrete tasks: serializing any object is a tricky (if not impossible task) in general sense, but if there is a way of specializing abstraction to a concrete type/interface — the problem will be gone. More on that will be in next posts.
But here is a quick example: there is in-memory dictionary that is needed to be serialized/deserialized. There are many ways of doing it: serializing to xml/json/edn/yaml/etc. The process will look like the following
in-mem cfg ⇆ config.xml
Note, that some part of data will be inevitably lost in these conversion. We could add step in the middle, like
in-mem-cfg ⇆ serializable-cfg ⇔ config.xml
This means, if we want to serialize config to JSON, for example, we just need to convert config to some smaller subset of types, which will be serializable. This also has other benefits, consider this example from the future: I want to render content (html for example) with some meta-data (like title/author and so on) to a web-page using some fancy templating engine — guess what, templating engine would be designed to work only with some limited subset of types/ways of representing data, this is where specialization will be handy.
Another point of view on the problem is that there always will be in-memory config, and only sometimes config in file, so in-memory config is more general than stored config.
And if we decide to store configuration, not all of it should be stored, some part of configuration would have semantic sense only for the current session, like passed command-line flags, environmental vars, etc. So the this topic is still open, more serialization/deserialization will be covered when I get to posts’ processing.
git clone https://github.com/ndrew/stagosaurus.git cd stagosaurus git checkout iteration_1 go test # i assume you have go installed go run example/example.go
If you run the code the results should be like
->: Desktop ndrw$ git clone https://github.com/ndrew/stagosaurus.git Cloning into 'stagosaurus'... bla-bla-bla, done. ->: Desktop ndrw$ cd stagosaurus ->: stagosaurus ndrw$ git checkout iteration_1 ... bla-bla-bla ... HEAD is now at b9521f5... added configuration and first example ->: stagosaurus ndrw$ go test PASS ok _/Users/ndrw/Desktop/stagosaurus 0.022s ->: stagosaurus ndrw$ ->: stagosaurus ndrw$ go run example/example.go ╔========================╗ ╟ Rhoaarrrr Stagosaurus! ╢ ╚========================╝ Congratulation, you did it!
Of course, configuration is not final at this point: it should be refined during further stages of development.
The next post will be on posts and there will be coding in it. Stay tuned.