no-nonsense 0.0.5
No nonsense is a static site generator.
Requires Python: >=3.8

# No Nonsense

No nonsense is a static site generator. Essential idea is this:

Static Site Generation is a solved problem. All you need is a templating engine
and a config.

So, I am just using [wheezy.template](https://pypi.org/project/wheezy.template/) 
as a Template Engine and YAML as config.

### Features
1. Simple to use and understand (just 1 small python file)
2. You can embed arbitrary python code in the templates to create whatever you want

### Problems
1. You need to know HTML, CSS, JS to use effectively
2. Cannot connect to databases. So, things like comments, etc. are out of question
3. The template engine I am using - wheezy.template uses `@` to mark statements like:
if, else, for, python code, etc. which conflicts with emails, annotations in some 
languages like java, etc. And we need to replace it with: `@{'@'}` so the output contains
@ and is not treated like a statement.

### Motivation

I have a personal blog and I observed some repetitive code. I figured out, what I really need
is a template engine and more specifically a static site generator (SSG). But most static site
generators are way too complex and my blog is too simple to inherit all the complexity. So I 
decided to write my own. 

I knew template engines existed so most of the problem was solved. All I needed to do was to find
a good template engine. I started out with Jinja2 but it was limited in terms of the code allowed
and I could not embed arbitrary python code. Which made some computations very tricky like 
filtering a list, sorting it by some arbitrary key and then looping over the result. So, I restarted
my search and found out wheezy. While it is not perfect, the `@` being the statement character is a bad
idea in my experience at least for generating developer blogs in html, but to its credit, it is terse.
And the ability to embed arbitrary python code was a blessing.

Idea is to be very flexible with the configuration file and let the template 
handle all the logic.

### Why YAML?

It is easier to read with "Naked Eye" as it does not have a lot of syntax. 
Disadvantage is whitespace is significant in YAML files and tabs vs spaces 
can cause confusion

### Getting started
Install dependency: `pip install no-nonsense`

Create a yaml file in the root directory of the project called nn.yaml (nn = no nonsense)
```yaml
metadata:
  author: ABC Def
  title: ABC's blog
templates:
  output_file_name.html:
    input: input_file.html
    title: Abc DEF
    author: Def Abc
```

Then, create `input_file.html`

```html
@require(config, this)
@(from datetime import datetime)
<!doctype html>
<html lang="en">
<head>
  <title>@{this['title']}</title>
</head>

<body>
  <h1>@{this['title']}</h1>
  <p>- @{this['author']} @{this['created_at'].strftime('%d %B, %Y')}</p> 
</body>
</html>
```

Every template is injected with 2 variables:
1. `config` - this is the dict representing the yaml file
2. `this` - the value of the current template i.e.
   If config is:
   ```yaml
   metadata:
     author: ABC Def
     title: ABC's blog
   
   templates:
     output_file_name.html:
       input: input_file.html
       title: Abc DEF
       author: Def Abc
   ```
   
   Then `this` will be:
   ```yaml
   input: input_file.html
   title: Abc DEF
   author: Def Abc
   ```

Now, first make sure your virtualenv is activated (if any). Then run
`no_nonsense -f <path/to/yaml/file/nn.yaml>` (file name can be anything, but I use nn.yaml by 
convention)

### What just happened?

You should see a public folder under `<path/to/yaml/file/public>`. It should have the template 
with appropriate substitutions

### Template syntax

This project uses [wheezy.template](https://pypi.org/project/wheezy.template/) as the template engine.
The syntax for wheezy.template can be found [here](https://wheezytemplate.readthedocs.io/en/latest/userguide.html)

1. In the template, the first line is: `@require(config, this)`. This is optional if you don't want to use
`config` and `this` variables. If you want to use `config` and `this` variables in your template, 
you need to add it.

2. wheezy.template allows us to directly embed python code in the template. In the next line,
we import datetime from datetime module directly. This will not be added to the output file. 
But it can be used for processing as we will shortly see. @(...) basically executes the python code
directly into the template.

3. In the 6th line, we have: `<title>@{this['title']}</title>`. @{...} basically evaluates the python
code and substitutes the value in the output file. In this case, it is accessing `title` key of `this`
variable. If you remember, `this` is the current template in the yaml file which is an object (dict).
Likewise, we can access any other attribute we have defined

### YAML file contract

1. YAML file needs to have a `templates:` key.
2. Each child of `templates:` should be a key value pair. 
   1. Each key is the name/path of the output file
   2. Each value will be an object with at least one attribute: `input:`
   3. `input:` will contain the name/path of the file that contains the template
3. **Note: You SHOULD NOT rely on the order of processing of templates. 
Each template should be independent of one another**

The YAML file can contain anything else. The template can contain arbitrary python code.
And the library will inject `config` and `this` variables allowing the template to do whatever
it wants. Effectively, you can customize to your heart's delight.

#### How does wheezy work?

Essentially, it takes your code and creates a python function to generate the string with the 
required input at runtime and then calls it with the required parameters.

### Usage

1. `no_nonsense --version` - Prints version info and exits

2. `no_nonsense --help | -h` - Prints help and exits

3. `no_nonsense -f <path/to/yaml/file/nn.yaml> [-o <output_dir>]` - Use the file at 
`<path/to/yaml/file/nn.yaml>` to parse the templates. If -o is specified, use it as 
output directory. Otherwise, uses `<path/to/yaml/file>/public/` as output folder 

### (For maintainers) Uploading to pip

In the root directory, run:
1. `python3 -m build`
2. `python3 -m twine upload dist/*`