Welcome to GeekCMS’s documentation!¶
GeekCMS is a lightweight framework for static site development, with properties as follow:
- Well defined protocols to facilitate the process of development and deployment.
- Plugin-base and pipe-and-filter architecture.
- Strict rules of files organization.
- Implemented in Python3.
Execution Model¶
From the view of theme(in short, theme = theme settings + plugins) developers, GeekCMS is a framework with protocols and helpful libraries, to:
- control the behavior of each individual plugin.
- customize the calling sequence of multiple plugins.
For users, GeekCMS is a theme driven tool, means that without plugins, GeekCMS can do nothing!
There are two kinds of plugin execution procedures defined in GeekCMS:
- Default procedure: Procedure consists of several runtime components. A runtime component is an area to place plugins to be executed.
- Extended procedure: Besides, there are kinds of behaviors can not be classified into components, for instance, automatically pushing static pages to a remote git repo. Such behaviors could be implemented as independent extended procedures, and triggered by CLI command by user.
Default Procedure¶
Default procedure is divided into nine runtime components:
- pre_load, in_load, post_load
- pre_process, in_process, post_process
- pre_write, in_write, post_write
which would be sequentially executed by GeekCMS. Each runtime component could contain zero or more plugins, details of that would be covered later.
The components can be classified into three layers, load/process/write. Layers are exectured in order, load –> process –> write, which is simple and intuitive. Notice that the distinction of components is vague, for instance, a plugin transforming markdown to html can be placed in in_load, post_load or even post_write, depending on developers understanding of components’ semantics. By dividing a layer into three components, theme developer could well control the sequence of plugin execution. Plugin execution order within a layer is a little bit complicated, and would be introduce later.
Extended Procedure¶
GeekCMS allow developer to define extended procedure for special usage. For more information: Extended Procedure Related.
Utilities Classes¶
All following classes are defined in geekcms.utils.
Path Resolve¶
-
class
PathResolver
¶ -
project_path
¶ The path of project.
-
classmethod
set_project_path
(cls, path)¶ Set the project_path with path.
-
classmethod
inputs
(cls, *, ensure_exist=False)¶ Return the path of inputs directory of project. If ensure_exist is
True
and the directory of inputs do not exist, then an empty directory would be created.
-
classmethod
outputs
(cls, *, ensure_exist=False)¶ Return the path of outputs directory of project. If ensure_exist is
True
and the directory of outputs do not exist, then an empty directory would be created.
-
classmethod
themes
(cls, *, ensure_exist=False)¶ Return the path of themes directory of project. If ensure_exist is
True
and the directory of themes do not exist, then an empty directory would be created.
-
classmethod
states
(cls, *, ensure_exist=False)¶ Return the path of states directory of project. If ensure_exist is
True
and the directory of states do not exist, then an empty directory would be created.
-
classmethod
theme_state
(cls, theme_name, *, ensure_exist=False)¶ Return the path of directory contains state of theme. Such path is generated by joining
cls.states()
and theme_name. If ensure_exist isTrue
and the directory of theme’s state do not exist, then an empty directory would be created.
-
classmethod
theme_dir
(cls, theme_name, *, ensure_exist=False)¶ Return the path of directory contains code of theme. Such path is generated by joining
cls.themes()
and theme_name. If ensure_exist isTrue
and the directory of theme’s dir do not exist, then an empty directory would be created.
-
PathResolver
can be helpful for development,
with which developer could easily get the path of specific directory,
and create specific directory if such directory does not exist.
Project Organization¶
Structure Of Project¶
GeekCMS would maintained a directory containing all files required to generate a website, such directory is organized as a projcet.
Sturcture of a project is as follow:
example_project/
themes/
...
states/
...
inputs/
...
outputs/
...
settings
Brief explanations of above file and direcroties:
themes
A directory where all the code of theme exists.states
A directory for themes to place its intermediate data.inputs
A direcroty contains all input files.outputs
A directory contains all generated files.settings
A text file named project settings, in which defines registered themes and global shared data.
The names of above file and direcroties is hardcoded in GeekCMS.
Structure Of Theme¶
A theme should be organized as a python package, structure is as follow:
example_project/
themes/
theme_A/
__init__.py
settings # theme settings
...
theme_B/
__init__.py
settings # theme settings
...
states/
...
inputs/
...
outputs/
...
settings # project settings
All themes should be placed in themes directory. As you can see, there is settings file exists in each theme package. Such settings file is named theme settings.
Settings File¶
GeekCMS’s behavior is guided by project settings and theme settings. Format of settings is described in configparser [1].
projcet settings should defines a RegisterTheme(case-sensitive) section. Names of themes(the name of theme’s directory) to be loaded by GeekCMS should be seperated by whitespaces and set as the value of themes key(case-insensitive). Example is as follow:
# project settings.
[RegisterTheme]
themes: simple git_upload
where directories simple and git_upload are registered.
themes settings should defines a RegisterPlugin(case-sensitive) section. Keys in the section should be one of [pre_load, in_load, post_load, pre_process, in_process, post_process, pre_write, in_write, post_write] and [cli_extend]. All avaliable keys except cli_extend is discussed in Default Procedure, and cli_extend is a key for registering extended procedure. An example for demonstration:
# settings of simple.
[RegisterPlugin]
in_load:
load_inputs_static
load_article
load_about
load_index
load_theme_static
in_process:
md_to_html << gen_article_page
post_process:
gen_about_page
gen_index_page
gen_time_line_page
gen_archive_page
pre_write:
clean
in_write:
write_static
write_page
post_write:
cname
# settings of git_upload
[RegisterPlugin]
cli_extend: GitUploader
Both projcet settings and theme settings can define a Share section.
Key-value pairs defined in Share section can be retrived by ShareData
.
An example for demonstration:
# settings of simple.
[Share]
# special pages
index_page: index.html
time_line_page: speical/time_line.html
about_page: speical/about.html
archive_page: speical/archive.html
where the value of index_page can be retrived by ShareData.get('simple.index_page')
.
Plugin Registration¶
Execution order of plugins within the same runtime component is defined by plugin registration syntax. The syntax is:
runtime_component ::= component_name (':' | '=') [NEWLINE] plugin_relation* plugin_relation ::= binary_relation_expr | unary_relation_expr NEWLINE binary_relation_expr ::= plugin_name (left_relation | right_relation) plugin_name unary_plugin_expr ::= plugin_name [left_relation] | [right_relation] plugin_name left_relation ::= '<<' [decimalinteger] right_relation ::= [decimalinteger] '>>' component_name ::= identifier plugin_name ::= identifier
where identifier, decimalinteger and NEWLINE are corresponding to the definitions in Python Lexical Analysis [2].
Semantics:
pre_load: my_loader
register plugin my_loader to component pre_load.
pre_load: my_loader << my_filter
register plugins my_loader and my_filter to component pre_load, with my_loader being executed before my_filter.
pre_load: my_filter >> my_loader
has the same meaning as pre_load: my_loader << my_filter.
pre_load: loader_a <<0 loader_b NEWLINE loader_c <<1 loader_b
the execution order would be loader_c –> loader_a –> loader_b. << is equivalent to <<0, and << decimalinteger is equivalent to decimalinteger >>.
pre_load: my_loader <<
means my_loader would be executed before the other plugins within a component, unless another relation such as anther_loader <<1 is established.
pre_load: >> my_filter
reverse meaning of pre_load: my_loader <<.
Notice that the plugin_name should be presented in the pattern of ‘theme_name.plugin_name’. ‘theme_name.’ can be omitted, as presented in above example, if plugin_name points to a plugin exists in current theme directory.
GeekCMS would automatically import the __init__ module of registered theme packages. Besides writing a theme settings, developer should import the module(s) that defines plugin(s) in __init__. An example is given for demonstration:
# ../git_upload/__init__.py
# necessary!
from . import plugin
# ../git_upload/plugin.py
"""
Usage:
geekcms gitupload
"""
from datetime import datetime
import subprocess
import os
from geekcms.protocol import BaseExtendedProcedure
from geekcms.utils import PathResolver
class CWDContextManager:
def __enter__(self):
os.chdir(PathResolver.outputs())
def __exit__(self, *args, **kwargs):
os.chdir(PathResolver.project_path)
class GitUploader(BaseExtendedProcedure):
def get_command_and_explanation(self):
return ('gitupload',
'Automatically commit and push all files of outputs.')
def get_doc(self):
return __doc__
def run(self, args):
commit_text = 'GeekCMS Update, {}'.format(
datetime.now().strftime('%c'),
)
commands = [
['git', 'add', '--all', '.'],
['git', 'commit', '-m', commit_text],
['git', 'push'],
]
with CWDContextManager():
for command in commands:
subprocess.check_call(command)
GeekCMS would automatically loaded GitUploader
in above example.
[1] | http://docs.python.org/3/library/configparser.html |
[2] | http://docs.python.org/3/reference/lexical_analysis.html |
Deploy And Download Themes¶
Command Line Interface¶
GeekCMS provides a friendly CLI interface of usage. CLI of GeekCMS is implemented by using docopt [1].
For code sharing, developer could package their codes as a template. A template is organized in as a project.
Download¶
If you type geekcms
in your prompt and current working directory is not a project,
then the shell would presents:
$ geekcms
Usage:
geekcms startproject <template_name>
Entering startproject
option with <template_name> would automatically download a
directory with the name of <template_name>, which should be an empty project,
from GeekCMS-Themes [2] to current working directory:
$ ls
$ geekcms startproject simple
A simple/inputs
A simple/inputs/about
A simple/inputs/about/about.md
A simple/inputs/article
A simple/inputs/article/test
A simple/inputs/article/test/codetest.md
A simple/inputs/article/test/longlong.md
A simple/inputs/article/test/top-level.md
A simple/inputs/index
A simple/inputs/index/welcome.md
A simple/inputs/static
A simple/inputs/static/delete it.
A simple/settings
A simple/themes
A simple/themes/git_upload
A simple/themes/git_upload/__init__.py
A simple/themes/git_upload/plugin.py
A simple/themes/git_upload/settings
A simple/themes/simple
A simple/themes/simple/__init__.py
A simple/themes/simple/assets.py
A simple/themes/simple/load.py
A simple/themes/simple/process.py
A simple/themes/simple/settings
A simple/themes/simple/static
A simple/themes/simple/static/css
A simple/themes/simple/static/css/github.css
A simple/themes/simple/templates
A simple/themes/simple/templates/archive.html
A simple/themes/simple/templates/article.html
A simple/themes/simple/templates/base.html
A simple/themes/simple/templates/time_line.html
A simple/themes/simple/utils.py
A simple/themes/simple/write.py
Checked out revision 15.
$ ls
simple
After downloading such directory, GeekCMS would ensure project settings and themes, states, inputs, outputs exists, so developer should not consider pushing an empty directory to git repo.
Deploy¶
If you want to share your code with the others, just push your code to GeekCMS-Themes [2].
[1] | https://github.com/docopt/docopt |
[2] | (1, 2) https://github.com/haoxun/GeekCMS-Themes |
Example Of Development¶
A simple [1] template is developed for demonstraction. The template simply renders markdown files and generates a static site.
[1] | https://github.com/haoxun/GeekCMS-Themes/tree/master/simple |