PageRenderTime 21ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/README.md

https://bitbucket.org/obensonne/poole/
Markdown | 393 lines | 282 code | 111 blank | 0 comment | 0 complexity | 5e91d8c597fd1372181541e2ff145305 MD5 | raw file
  1. # Poole
  2. **Create simple websites fast, now.**
  3. Poole is an easy to use [Markdown][] driven static website generator. You write
  4. the content of your pages in Markdown and Poole creates a nice and simple site
  5. with a navigation menu. You don't need to learn a template or preprocessing
  6. engine.
  7. Though Poole is made for simple sites, it has basic support for content
  8. generation by embedding Python code in page source files. This is a dirty merge
  9. of content and logic but for simple sites it's a pragmatic way to get things
  10. done fast and easy -- if you know Python you're ready to start.
  11. Check the list of [sites built with Poole][examples] (and feel free to add
  12. yours).
  13. [examples]: https://bitbucket.org/obensonne/poole/wiki/Home
  14. [![Flattr this][flattr-img]][flattr-url]
  15. [![Build status][travis-img]][travis-url]
  16. [flattr-url]: http://flattr.com/thing/142425/Poole
  17. [flattr-img]: http://api.flattr.com/button/flattr-badge-large.png "Flattr this"
  18. [travis-url]: https://travis-ci.org/obensonne/poole
  19. [travis-img]: https://travis-ci.org/obensonne/poole.png?branch=master "Build status"
  20. ## Requirements
  21. **You** should know Markdown and optionally Python if you want to use Poole's
  22. dirty content generation capability.
  23. **Your system** should have installed Python ≥ 2.7 and [python-markdown][pymd].
  24. Poole has been tested on Linux but should also work on other Unix systems and
  25. Windows (in theory, [report an issue][issues] if it fails).
  26. [markdown]: http://daringfireball.net/projects/markdown/
  27. [pymd]: https://pypi.python.org/pypi/Markdown
  28. ## Getting Started
  29. Clone or download ([zip][zip], [tgz][tgz]) the repository and then put
  30. *poole.py* to your *PATH*:
  31. $ hg clone http://bitbucket.org/obensonne/poole/ /some/where/poole
  32. $ export PATH=$PATH:/some/where/poole
  33. **TIP**: You might want to add the last command to your `~/.bashrc`.
  34. **Python3**: Download the packages from the *py3* branch ([zip][zip3],
  35. [tgz][tgz3]) or check out the *py3* branch when cloned.
  36. Create and build a site project:
  37. $ mkdir /path/to/site/project
  38. $ cd /path/to/site/project
  39. $ poole.py --init --theme minimal
  40. $ poole.py --build
  41. $ poole.py --serve
  42. Done. You've just created a website! Browse <http://localhost:8080/> and watch
  43. the example pages which have been created during initialization. To write your
  44. own pages, use the example pages in the *input* folder as a starting point.
  45. Next to the *miniaml* theme, there are some other [choices available][themes].
  46. Run `poole.py --build` whenever you've made some changes in the *input* folder.
  47. [zip]: http://bitbucket.org/obensonne/poole/get/default.zip
  48. [tgz]: http://bitbucket.org/obensonne/poole/get/default.tar.gz
  49. [zip3]: https://bitbucket.org/obensonne/poole/get/py3.zip
  50. [tgz3]: http://bitbucket.org/obensonne/poole/get/py3.tar.gz
  51. [themes]: https://bitbucket.org/obensonne/poole/wiki/Themes
  52. ## How It Works
  53. Poole takes files from a project's `input` directory and copies them to the
  54. `output` directory. In this process files ending with *md*, *mkd*, *mdown* or
  55. *markdown* get converted to HTML using the project's `page.html` as a template
  56. (unless a custom template is set on an individual page).
  57. Additionally Poole expands any macros used in a page. Don't care about that for
  58. now ..
  59. When running `poole.py --build` in a Poole project, an input directory like
  60. this:
  61. |- input
  62. |- index.md
  63. |- news.mkd
  64. |- foo.mdown
  65. |- images
  66. |- bar.png
  67. will result in an output folder like that:
  68. |- output
  69. |- index.html
  70. |- news.html
  71. |- foo.html
  72. |- images
  73. |- bar.png
  74. ## Page Layout
  75. Every Poole page is based on the skeleton file `page.html`. Hence adjusting the
  76. site layout means adjusting `page.html` and extending or replacing its CSS file
  77. `input/poole.css`.
  78. The only thing you should keep in `page.html` are the embedded
  79. {{\_\_content\_\_}} and {{\_\_encoding\_\_}} expressions. Below is an almost
  80. minimal `page.html` file. It does not look nice but it's a clean starting point
  81. to build your own layout from scratch.
  82. Minimal `page.html`:
  83. <html>
  84. <head>
  85. <meta http-equiv="Content-Type" content="text/html; charset={{ __encoding__ }}" />
  86. </head>
  87. <body>
  88. {{ __content__ }}
  89. </body>
  90. </html>
  91. It's easy to apply one of the numerous free CSS templates out there to a Poole
  92. site. For more information read [this blog post with step-by-step
  93. instructions][pimp].
  94. In case you need special templates for individual pages, you can add the
  95. property `tempalte` in the front matter of each page:
  96. title: This looks different
  97. template: a-special-page-template.html
  98. ---
  99. In that case the given file is used as the page template instead of the default
  100. `page.html` file.
  101. [pimp]: http://obensonne.bitbucket.org/blog/20091122-using-a-free-css-templates-in-poole.html
  102. ## Content Generation
  103. Poole allows you to embed Python code in your pages to *generate* content:
  104. `input/some-page.md`:
  105. Here is normal text in *markdown* flavor.
  106. {%
  107. print "hello poole"
  108. %}
  109. Did you know? The sum of 2 and 2 is {{ 2 + 2 }}.
  110. This example demonstrates two ways to embed Python code, either as statements or
  111. as expressions:
  112. 1. Everything between `{%` and `%}` are *statements* and whatever is printed
  113. to *stdout* during their execution is going to be part of the final HTML
  114. page.
  115. 2. Everything between `{{` and `}}` are *expressions* and their evaluation is
  116. going to be part of the final page.
  117. **TIP**: Instead of the outer curly brackets `{` and `}` you can also use
  118. `<!--` and `-->` to prevent syntax highlighting markdown editors from getting
  119. confused by the Python code.
  120. **TIP:** To print the code surrounding tags literally, simply escape the
  121. opening tag with a backslash.
  122. [hyde]: http://ringce.com/hyde
  123. ### Outsource complex or frequently used code
  124. To keep embedded code short and compact or to reuse it in several pages, it can
  125. be outsourced into a file called `macros.py` in a project's root folder (where
  126. the `page.html` file is located). Every public attribute in `macros.py` is
  127. available within embedded Python code blocks:
  128. `macros.py`:
  129. from datetime import date
  130. def today():
  131. return date.today().strftime("%B %d, %Y")
  132. author = "Popeye"
  133. `input/some-page.md`:
  134. This site has been updated on {{ today() }} by {{ author }}.
  135. #### Builtin macros
  136. Builtin macros can be used from the macros module as well as from python
  137. code in your pages and templates (just as if they are defined within
  138. your macros.py).
  139. Currently, there is only one builtin macro available.
  140. `hx(s)`
  141. > Replace the characters that are special within HTML (`&`, `<`, `>` and `"`)
  142. > with their equivalent character entity (e.g., `&amp;`). This should be
  143. > called whenever an arbitrary string is inserted into HTML (i.e. use
  144. > `{{ hx(variable) }}` instead of `{{ variable }}`). You do not need this
  145. > within a markdown context.
  146. >
  147. > Note that `"` is not special in most HTML, only within attributes.
  148. > However, since escaping it does not hurt within normal HTML, it is
  149. > just escaped unconditionally.
  150. ### Working with pages
  151. Next to stuff defined in `macros.py` the objects `page` and `pages` are
  152. available in embedded Python code. The first one is a dictionary describing the
  153. page in which the code is embedded. The second one is a list of *all* pages in
  154. the project.
  155. The following attributes are always set in a page dictionary:
  156. * **title:** The page's title, by default its filename without extension
  157. (setting alternatives is described in the next section).
  158. * **fname:** Path to the page's source file, e.g.
  159. `/path/to/project/input/stuff/news.md`.
  160. * **url:** The page's relative URL, e.g. for a source page
  161. `input/stuff/news.md` this is `stuff/news.html`.
  162. The example `page.html` file in a freshly initialized site project uses a
  163. page's *title* attribute:
  164. ...
  165. <div id="header">
  166. <h1>a poole site</h1>
  167. <h2>{{ page["title"] }}</h2>
  168. </div>
  169. ...
  170. **TIP:** All items in a page dictionary are exposed as attributes, i.e.
  171. `page["foobar"]` is identical to `page.foobar`. Dictionary access is useful if
  172. an item may not be set, e.g.: `page.get("foobar", "...")`.
  173. #### Setting page attributes
  174. Page attributes can be set at the top of a page's source file, in [Python's
  175. configuration file style][pyconf]. They are delimited from the page's content
  176. by a line with 3 or more dashes.
  177. `input/stuff/news.md`:
  178. title: Hot News
  179. foobar: King Kong
  180. ---
  181. Here are some news about {{ page.foobar }}.
  182. Did I say {% print(page.foobar) %}?
  183. That way you can also set a page's title explicitly, instead of using the file
  184. name. Other useful attributes to set are *description* and *keywords*, which
  185. get used by the default `page.html` file to set HTML meta tags. Here it comes
  186. in handy to set *default* page attributes in the `macros.py` file:
  187. `macros.py`:
  188. page = { "description": "some stuff", "keywords": "stuff" }
  189. That way you can safely use the *description* and *keywords* attributes without
  190. bothering if they are really defined in every page.
  191. [pyconf]: http://docs.python.org/library/configparser.html
  192. #### Accessing page objects in the macros module
  193. The objects `pages` and `page` are also available within `macros.py`. That
  194. means you can define them as dummys and reference them in `macros.py`. Poole
  195. updates them when loading the `macros` module.
  196. `macros.py`:
  197. page = {} # you can also set defaults here, see previous section
  198. pages = []
  199. def something():
  200. # when executing this, the page and pages objects above are up-to-date
  201. print page["title"]
  202. ### Options and paths
  203. Similarly to `page` and `pages` the following objects are available within
  204. embedded Python code and within the *macros* module:
  205. * **options:** The command line arguments given to Poole as parsed by
  206. [Python's optparse module][pyopts]. For instance the base URL can be
  207. retrieved by `options.base_url`.
  208. * **project:** Path to the project's root directory.
  209. * **input:** Path to the project's input directory.
  210. * **output:** Path to the project's output directory.
  211. [pyopts]: http://docs.python.org/library/optparse.html
  212. ### Character encodings
  213. In case you use non-ASCII characters, check the *encoding* options of Poole. In
  214. most cases working with non-ASCII strings should work straight forward if the
  215. options are set properly (default is *UTF-8*).
  216. However, be aware that page variables defined within page source files and
  217. derived from a page's file name internally are handled as Python *unicode*
  218. objects. That means if you want to refer to non-ASCII page variable names and
  219. values form within embedded Python code or from `macros.py`, make sure to use
  220. *unicode* strings to reference them.
  221. ### Custom file converters
  222. If you use [LESS][] or [CleverCSS][] you'll be happy about the possibility to
  223. define custom converters Poole applies to selected files in its building
  224. process. Custom converters may be defined in `macros.py` using a dictionary
  225. named 'converter' with file name patterns as keys and a converter function as
  226. well as a target file name extension as values:
  227. converter = {
  228. r'\.ccss': (ccss_to_css, 'css'),
  229. ...
  230. }
  231. The converter function `ccss_to_css` must accept the source file name and the
  232. destination file name as arguments. The destination file name is a suggestion
  233. (the source filename mapped to the project's output directory with the
  234. extension given in the converter dictionary) - you are free to choose another
  235. one:
  236. import clevercss
  237. def ccss_to_css(src, dst):
  238. # when `src` is '/path/to/project/input/foo.ccss'
  239. # then `dst` is '/path/to/project/output/foo.css'
  240. ccss = open(src).read()
  241. css = clevercss.convert(ccss)
  242. open(dst, 'w').write(css)
  243. [clevercss]: http://sandbox.pocoo.org/clevercss/
  244. [less]: http://lesscss.org/
  245. ### Pre- and post-convert hooks
  246. All pages converted by Poole may be processed by custom code in `macros.py`
  247. using *hook* functions. In particular, any function whose name starts with
  248. `hook_preconvert_` is run after source markdown files have been parsed but
  249. not yet converted. Similarly, any function whose name starts with
  250. `hook_postconvert_` is run after the content of pages has been converted to
  251. HTML (but still without the skeleton HTML given in the project's `page.html`
  252. file).
  253. Pre-convert hooks are useful to preprocess the markdown source and/or to
  254. generate new virtual pages based on existing real pages:
  255. def hook_preconvert_foo():
  256. # important: replace all foos by bars in every page
  257. for p in pages:
  258. p.source = p.source.replace("foo", "bar")
  259. # create a new virtual page which still has a foo
  260. p = Page("foo.md", virtual="The only page with a *foo*.", title="Foony")
  261. pages.append(p)
  262. Virtual pages can be created by providing a virtual source filename relative
  263. to the project's input folder and corresponding markdown content. Page
  264. attributes (e.g. `title`) may be given as additional keyword arguments but
  265. may also be encoded in the markdown source as in real markdown input files.
  266. A common use case for post-convert hooks is to generate full content RSS feeds:
  267. def hook_postconvert_rss():
  268. # this is kind of pseudo code
  269. rss = ...
  270. for p in pages:
  271. rss.add_item(..., r.html)
  272. rss.save(".../rss.xml")
  273. More practical and detailed usage examples of hooks and virtual pages can be
  274. found in the recipes.
  275. ### Recipes
  276. You can do some pretty fancy and useful things with inlined Python code and
  277. the macros module, for instance generate a list of blog posts or create an RSS
  278. file. Check out the [example recipes][recipes].
  279. [recipes]: https://bitbucket.org/obensonne/poole/wiki/Recipes
  280. ## Feedback
  281. Please use the [issue tracker][issues].
  282. [issues]: http://bitbucket.org/obensonne/poole/issues/