init
command. Then we'll install the serve plugin so we have a web server to show our generated site. The subsequent run
command will start the web server on http://localhost:9778, watch for changes, and regenerate the website when they occur. This runs in the foreground; you can stop it any time by pressing CTRL+C
. (For the moment we'll keep it running.)source/documents
and source/files
directories, rather than source/render
and source/static
. You should remove the documents
and files
directories and create render
and static
directories in order to follow the new naming conventions.source/render/index.html
and give it the following content:out/
directory to the list of files to ignore, since that should never be committed.source/render/about.html
and give it the content:<head>
to something else, then we'll have to change it in two places! This is where layouts come in.render
files, so we can define the surrounding areas of a document only once. Let's move the wrapper stuff of our two render
files into a new layout located at source/layouts/default.html.eco
, so that we end up with the following:isPage
attribute, this is our own custom attribute and has no special meaning in DocPad. However, we will give it special meaning later on when we add our menu listings :)render
files - which is why we have to use them for layouts, as a layout is an abstraction, an abstraction that wraps around a document and outputs the document's content in a specific way :)CTRL+C
), and running:docpad run
) and then go our Homepage or About page, we'll see that the content actually contains the document content. In other words, it has rendered correctly through our eco templating engine. Woot woot!<%=
for the document's title, but <%-
for the document's content? The reason for this is that <%=
will escape the data we give it before outputting the data, which allows special characters to be interpreted as normal characters. On the other hand, <%-
will output without performing any escaping, leaving special characters intact. So, why should we escape some things but not others? Generally, whenever the data we want to output is HTML (e.g., the document's content) we want to output it "as is", without any escaping. On the other hand, if we didn't escape document titles, titles such as 3 is > 2
would end up as <title>3 is > 2</title>
, which is invalid HTML due to the superfluous closing element character, >
. Thus, using escaping would result in <title>3 is > 2</title>
, which uses the HTML entity code to represent the special character. This is valid HTML and looks exactly the same to the user :)default.html.eco
, we can render from eco
to html
. Conversely, if we tried default.eco.html
, we'd be trying to render from html
to eco
, which wouldn't actually be very useful or even possible. However, other combinations can be rendered in both directions, such as CoffeeScript to JavaScript and JavaScript to CoffeeScript, provided you have the right plugins installed. You can also mix and match extensions, such as default.html.md.eco
, which renders from eco
to md
(markdown) and then md
to html
. This allows you to apply abstractions with eco to your markdown render
files. Awesome! For now, though, it's way out of our scope, so let's get back on track!---
at the top of our render
files? That stuff is our meta data. It is where we can define extra information about our document, such as its title, layout, date, whatever. You are not limited to what you can define here; it's up to you. However, there are some pre-defined properties that serve special purposes (such as layout, which we just used).render
files, and a layout to abstract them. As we are making changes, it sure would be nice if our browser refreshed the page automatically! We can do this with the Live Reload Plugin, so let's install that now:default.html.eco
layout to become:static
directory at source/static/images/logo.gif
(binary files should always go in the static
directory). Then, we'll add it to the body of our layout, to show our logo on each page:h1
headers red, by adding a stylesheet file in our render directory at source/render/styles/style.css
that contains:default.html.eco
layout to:static
directory at source/static/vendor/jquery.js
.static
directory for vendor files is that it is extremely unlikely we'll ever want to render any vendor files, so having them there is a good choice for consistency and speed. Whereas, we will probably eventually want to render our own scripts and styles with something, so generally we'll just put them in the render directory to make the transition to rendering engines easier.source/render/scripts/script.js
that contains:default.html.eco
layout:script
tag, then it would be useful.body
element, we already have access to the body
element, and therefore can start our fadeIn
animation immediately. This avoids the page loading, then, the DOM loading after a delay, followed by our fade-in with an undesirable "popping" effect.render
files in one language (the source language), and export them to a different language (the target language). This is extremely beneficial, as it allows you to use the syntax that you enjoy, instead of the syntax that you are sometimes forced to work with. Most importantly, however, pre-processors often offer more robust and clean functionality than the target language supports out of the box, allowing you to make use of modern development tools while still working with old languages.source/render/about.html
) to (source/render/about.html.md
), to indicate that we want to render from Markdown to HTML, and open it. Writing in Markdown, update its content (leave the existing meta data section as it is) to become:source/render/styles/style.css
). CSS really hasn't come that far over the years and, thus, it has absolutely no abstractions available to us, making it incredibly verbose and painful to write. Fortunately, Stylus (one of the many CSS Pre-Processors available to us) is our saviour!source/render/styles/style.css
to source/render/styles/style.css.styl
, to indicate we want to render from Stylus to CSS, and open it. The reason why we created the style file in render
and not in static
is now obvious: if the Stylus stylesheet file were in source/static/styles/
folder, it would not have been pre-processed before copying to out
.source/render/scripts/script.js
to source/render/scripts/script.js.coffee
, to indicate we want to render from CoffeeScript to JavaScript, and open it.| My Website
. A page title of My Website
would look far better when our document doesn't have a title.default.html.eco
layout template to become:My Website
. Considering this a common requirement, it would be nice if we could abstract it out, say, into a configuration file!docpad.coffee
(in the project root) when we initialized the project. If not, let's create it, with the following contents:docpadConfig
object is written in CoffeeScript's version of JSON.# ...
is located), and the second part is a Node convention for exporting data from one file to another. Whenever we add some configuration, you'll want to add it to the docpadConfig
object we just defined.@document
is part of our template data. To be able to abstract out something that our templates will use, we will need to extend our template data. We can do this by modifying our template data configuration property in docpad.coffee
like so:default.html.eco
template:.coffee
or .js
files to define our Configuration File, we are allowed to define functions. Doing so allows us to use functions within our template data, which we call Template Helpers.docpad.coffee
would look like:default.html.eco
, would become:h1
:getCollection
template helper to fetch the html
collection, which is a pre-defined collection by DocPad that contains all the HTML documents in our website. Then, with that collection, we find everything that has a isPage
attribute set to true
. (We defined it earlier, when first applying our layout to our pages.) Then, we convert the result from a Backbone Collection / QueryEngine into a standard JavaScript Array using toJSON
.docpad.coffee
) and open it up. This time we want to add the following:default.html.eco
, we'll update the getCollection
line to become:findAllLive
instead of the findAll
method. That's because findAllLive
uses QueryEngine's Live Collections, which allows us to define our criteria once, and then continue to keep our collection up-to-date.html
collection is the parent collection and our pages
collection is the child collection.) The child collection then subscribes to the parent collection's add
, remove
, and change
events, and tests the model that the event was for against our child collection's criteria. If it passes the collection, it adds it; if not, then it removes it. This performs much better than querying everything every single time.@getCollection('html').findAllLive({isPage:true})
to add a second argument, which is the sorting argument; @getCollection('html').findAllLive({isPage:true},[{filename:1}])
that, in this case, will sort by the filename in ascending order. To sort in descending order, we would change the 1
to become -1
. Now we can sort by any attribute available on our models, which means that we could even add an order
attribute to our document meta data and then sort by that if we wanted to.layout: default
to the meta data of each page.add
event automatically fired by Backbone whenever a model (a page, file, document, whatever) is added to our collection. Then, inside our event, we say we want to set our default meta data attributes for the model; in this case, setting the layout to "default"
.docs/docpad/01-start/04-begin.html.md
, we can detect that the project is docpad
, and assign project: "docpad"
to the meta data accordingly. As the section is "start" and it is order first, we set category: "start"
and categoryOrder: 1
. We also see that our file is begin
and ordered 4th. This is just one nifty example. There's plenty more you'll discover on your own epic journey! :)post
that will use the default layout. Use it to perform custom styling for your blog post (e.g., <div class="post"><%- @content %></div>
).date
meta data attribute in the format of date: 2012-12-25
, so you can sort your blog posts in descending date order.posts
that contains all of your blog posts, and use the query relativeOutDirPath: 'posts'
for your custom collection in order to retrieve all documents in the posts
output directory (/my-new-website/out/posts
). You can refer to the Meta Data Page for more information about the attributes already available to you.posts.html.eco
that lists all your blog posts. This will be, more or less, the same as our navigation menu. If you would like to display descriptions of the blog posts, just add that as a meta data attribute for the blog posts, and then output that meta data attribute. If you want to show the rendered content of the data, you can use post.contentRenderedWithoutLayouts
. You can refer to the Meta Data Page for more information about the attributes already available to you.render
files in any language, markup, pre-processor, templating engine, whatever you wish, by installing the necessary plugin for it and changing the extensions of the document