Magnanimous Gopher Magnanimous Documentation

3. Paths and Links

When writing your website, you’ll probably want to make use of several Magnanimous instructions which need to point to another source file. They do that by telling Magnanimous the path to the other file.

You’ll also likely add links to other files in your pages, so that users can easily navigate between the pages of your website.

The two concepts are somewhat similar, but definitely not the same.

A path is a compilation-time concept and doesn’t exist at runtime, while links only exist at runtime.

In the next sections, you’ll learn what that means in detail, so you never have to spend time wondering why a link does not point where you thought it ought to, or a path does not resolve correctly, giving you compile time warnings.

Paths

Components, the for and the include instructions all need to declare a reference to some file, or directory in the case of for.

They need to know where to find the information necessary to do their job: the contents of a file, in the case of the include and component instructions, and the directory containing the files to be iterated over, in the case of the for instruction.

Paths can be of three types:

Absolute paths

Absolute paths start with a /, and are always interpreted as being under the source/ directory.

Example:

{{ include /processed/_header.html }}

Relative paths

Relative paths, unlike absolute paths, do not start with a / and are resolved by the location of the file they are declared in.

Example:

{{ include _header.html }}

If the above inclusion is done from the file /processed/components/_comp1.html, then the path, _header.html, will point to the file at /processed/components/_header.html.

If the inclusion is from /processed/_comp2.html, then it points to /processed/_header.html.

Relative paths can also refer to a parent directory if they start with ../, just like in most file systems.

However, trying to access anything above the source/ directory is forbidden and will result in the ../ part being ignored.

Up paths

Up paths refer to a file located at the same location of the file currently being written by Magnanimous (not necessarily the location of the file the include statement is declared in, as with absolute and relative paths), or any parent directory up to the source directory.

Example:

{{ include .../_messages }}

Up paths are special in that the file they point to depends on the context. They are commonly used to allow overriding which file should be used, which is useful for things like internationalization.

For example, given this file structure:

source
└── processed
    ├── _header.html
    ├── index.html
    ├── _messages
    └── pt
    |   ├── _messages
    |   └── index.html
    └── sv
        └── index.html

Assuming source/processed/_header.html has the following contents:

{{ include .../_messages }}
<html>
<head>
<title>{{ eval title }}</title>
</head>
<body>

Notice that the header file uses an up path, .../_messages.

If the source/processed/index.html file includes this header, it will get messages from source/processed/_messages because that’s the nearest _messages file looking up the directory tree.

If the source/processed/pt/index.html file includes the header, its messages will come from source/processed/pt/_messages instead!

If the source/processed/sv/index.html file includes the header, because there’s no _messages file on the same directory, Magnanimous will look up the directory tree, and find the source/processed/_messages file, so it will include that.

Simple, but effective.

Expression paths

Paths can also be given by expressions if starting with " or `, or explicitly with eval <expr>.

For example, the paths in these include instructions all evaluate to /hello/world/file.html:

{{ include /hello/world/file.html }}
{{ include "/hello" + "/world" + "/file.html" }}

{{ define dir "/hello/world/" }}
{{ include eval dir + "file.html }}

Path variables

Even though most of the time, paths are declared as simple expressions or unquoted strings, it is possible to explicitly declare a path as follows:

{{ define my_path path["/processed/example/file.txt"] }}

This is useful to read the top-level variables defined by another file. For example, suppose the file at /processed/example/file.txt contained the following definition:

{{ define astronaut "Neil Armstrong" }}

Then, given my_path defined above, we can evaluate this definition in another file:

The first astronaut to step on the Moon was {{ eval my_path.astronaut }}.

It is even possible to refer to the current file:

This file is at {{ eval path["."] }}.

Path variables can also be used with date expressions:

{{ define this path["."] }}
This file `{{eval this}}` was last updated on {{ eval date[this] }}.

Here’s how the above code sample renders on this document itself!

This file website/source/processed/sections/docs/paths.md was last updated on 02 Jul 2022, 11:20 AM.

When are paths resolved?

Paths are resolved when Magnanimous is writing the generated website files.

Notice that paths are a compile-time concept, which means that they are only used by Magnanimous instructions, during compilation, to resolve the contents of the files that will be part of the website. For this reason, paths can point to no-writable files (whose file names start with _, which are not copied to the final website).

Once the website is created, instructions and paths simply do not exist anymore! All you have is a bunch of static files ready to be served by a dumb web server.

For this reason, paths should always point to other source files, not to generated files.

Links, on the other hand, are a different story, as we’ll see.

Links

Links are not really a Magnanimous concept, but a Markdown and HTML concept. But because, like paths, they point to another file (or website), they can be confused with paths, so some explanation of what’s different between them is warranted.

Example:

Markdown

My favourite site is [Wikipedia](https://www.wikipedia.org/).

HTML

<p>My favourite site is <a href="https://www.wikipedia.org/">Wikipedia</a>.</p>

The big difference between links and paths is simple: links are only ever used at runtime, while paths are used by Magnanimous at compile time to resolve Magnanimous instructions.

This has an important implication: while paths point to source files, links point to generated files (or other websites).

So, if you want to add a link to some content you wrote in a markdown file, say /processed/blog/post.md, the link to it should point to /processed/blog/post.html, the generated file, not the source file.

Notice that all processed .md files are converted to .html files by Magnanimous. See the Markdown Guide for details.

When are links resolved?

Links are resolved by the browser when users click on links on your HTML pages.

They are part of the language you’re using to write content, be it HTML or Markdown, and are not used by Magnanimous.

Consider the base path in your links

If your website is not served from the root path within your domain, you might also need to consider the base path!

For example, if your website will be served under the https://user1.github.io/my-project URL (as a project called my-project by user1, served via GitHub pages would), then your links need to take the /my-project path (the base path) into account, so an absolute link to the source file /processed/blog/post.md should actually point to /my-project/blog/post.html.

A good way to approach this is to define a basePath variable in the /processed/_global_context file, as in this example:

{{ define basePath "/my-project/" }}
source/processed/_global_context

Now, from any other processed file, a link can be safely created as follows:

<a href="{{ eval basePath + `blog/post.html` }}">A link</a>

Or, in markdown:

[A link]({{ eval basePath + "blog/post.html" }})