Alice in Deadland (Alice in Deadland, #1)
Review:Not fun and rather shallow. Requires a suspension of disbelief I could not achieve. |
Review:Not fun and rather shallow. Requires a suspension of disbelief I could not achieve. |
A simple yet powerful and flexible static website and blog generator, based on doit, mako, docutils and bootstrap.
I built this to power this very site you are reading, but decided it may be useful to others. The main goals of Nikola are:
Small codebase: because I don't want to maintain a big thing for my blog
Fast page generation: Adding a post should not take more that 5 seconds to build.
Static output: Deployment using rsync is smooth.
Flexible page generation: you can decide where everything goes in the final site.
Powerful templates: Uses Mako
Clean markup for posts: Uses Docutils
Don't do stupid builds: Uses doit
Clean HTML output by default: Uses bootstrap
Comments out of the box: Uses Disqus
Tags, with their own RSS feeds
Easy way to do a blog
Static pages outside the blog
Multilingual blog support (my own blog is english + spanish)
I think this initial version achieves all of those goals, but of course, it can be improved. Feedback is very welcome!
Nikola's home page is currently http://nikola-generator.googlecode.com
As I hope you know, if you get a string of bytes, and want the text in it, and that text may be non-ascii, what you need to do is decode the string using the correct encoding name:
However, there is a gotcha there. You have to be absolutely sure that the thing you are decoding is a string of bytes, and not a unicode object. Because unicode objects also have a decode method but it's an incredibly useless one, whose only purpose in life is causing this peculiar error:
>>> u'á'.decode('utf8') Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python2.7/encodings/utf_8.py", line 16, in decode return codecs.utf_8_decode(input, errors, True) UnicodeEncodeError: 'ascii' codec can't encode character u'\xe1' in position 0: ordinal not in range(128)
Why peculiar? Because it's an Encode error. Caused by calling decode. You see, on unicode objects, decode does something like this:
The user wants a unicode object. He has a unicode object. By definition, there is no such thing as a way to utf-8-decode a unicode object. It just makes NO SENSE. It's like asking for a way to comb a fish, or climb a lake.
What it should return is self
! Also, it's annoying as all hell in that
the only way to avoid it is to check for type, which is totally unpythonic.
Or even better, let's just not have a decode method on unicode objects, which I think is the case in python 3, and I know we will never get on python 2.
So, be aware of it, and good luck!
I added a very minor feature to the site. Up here ^ you should be able to see a link that says "reSt". If you click on it, it will show you the "source code" for the page.
I did this for a few reasons:
Because a comment seemed to suggest it ;-)
Because it seems like a nice thing to do. Since I so like reSt, I would like others to use it, too. And showing how easy it is to write using it, is cool.
It's the "free software-y" thing to do. I am providing you the preferred way to modify my posts.
It was ridiculously easy to add.
Also, if you see something missing, or something you would like to have on the site, please comment, I will try to add it.
I managed to do some minor work today on Nikola, the static website generator used to generate ... well, this static website.
Implemented tags (including per-tag RSS feeds)
Simplified templates
Separated code and configuration.
The last one was the trickiest. And as a teaser, here is the full configuration file to create this site, except HTML bits for analytics, google custom search and whatever that would make no sense on other sites. I hope it's somewhat clear.
# -*- coding: utf-8 -*- # post_pages contains (wildcard, destination, template) tuples. # # The wildcard is used to generate a list of reSt source files (whatever/thing.txt) # That fragment must have an associated metadata file (whatever/thing.meta), # and opcionally translated files (example for spanish, with code "es"): # whatever/thing.txt.es and whatever/thing.meta.es # # From those files, a set of HTML fragment files will be generated: # whatever/thing.html (and maybe whatever/thing.html.es) # # These files are combinated with the template to produce rendered # pages, which will be placed at # output / TRANSLATIONS[lang] / destination / pagename.html # # where "pagename" is specified in the metadata file. # post_pages = ( ("posts/*.txt", "weblog/posts", "post.tmpl"), ("stories/*.txt", "stories", "post.tmpl"), ) # What is the default language? DEFAULT_LANG = "en" # What languages do you have? # If a specific post is not translated to a language, then the version # in the default language will be shown instead. # The format is {"translationcode" : "path/to/translation" } # the path will be used as a prefix for the generated pages location TRANSLATIONS = { "en": "", "es": "tr/es", } # Data about this site BLOG_TITLE = "Lateral Opinion" BLOG_URL = "//ralsina.me" BLOG_EMAIL = "ralsina@kde.org" BLOG_DESCRIPTION = "I write free software. I have an opinion on almost "\ "everything. I write quickly. A weblog was inevitable." # Paths for different autogenerated bits. These are combined with the translation # paths. # Final locations are: # output / TRANSLATION[lang] / TAG_PATH / index.html (list of tags) # output / TRANSLATION[lang] / TAG_PATH / tag.html (list of posts for a tag) # output / TRANSLATION[lang] / TAG_PATH / tag.xml (RSS feed for a tag) TAG_PATH = "categories" # Final location is output / TRANSLATION[lang] / INDEX_PATH / index-*.html INDEX_PATH = "weblog" # Final locations for the archives are: # output / TRANSLATION[lang] / ARCHIVE_PATH / archive.html # output / TRANSLATION[lang] / ARCHIVE_PATH / YEAR / index.html ARCHIVE_PATH = "weblog" # Final locations are: # output / TRANSLATION[lang] / RSS_PATH / rss.xml RSS_PATH = "weblog" # A HTML fragment describing the license, for the sidebar. LICENSE = """ <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/2.5/ar/"> <img alt="Creative Commons License" style="border-width:0; margin-bottom:12px;" src="http://i.creativecommons.org/l/by-nc-sa/2.5/ar/88x31.png"></a> """ # A search form to search this site, for the sidebar. Has to be a <li> # for the default template (base.tmpl). SEARCH_FORM = """ <!-- google custom search --> <!-- End of google custom search --> """ # Google analytics or whatever else you use. Added to the bottom of <body> # in the default template (base.tmpl). ANALYTICS = """ <!-- Start of StatCounter Code --> <!-- End of StatCounter Code --> <!-- Start of Google Analytics --> <!-- End of Google Analytics --> """ # Put in global_context things you want available on all your templates. # It can be anything, data, functions, modules, etc. GLOBAL_CONTEXT = { 'analytics': ANALYTICS, 'blog_title': BLOG_TITLE, 'blog_url': BLOG_URL, 'translations': TRANSLATIONS, 'license': LICENSE, 'search_form': SEARCH_FORM, # Locale-dependent links 'archives_link': { 'es': '<a href="/tr/es/weblog/archive.html">Archivo</a>', 'en': '<a href="/weblog/archive.html">Archives</a>', }, 'tags_link': { 'es': '<a href="/tr/es/categories/index.html">Tags</a>', 'en': '<a href="/categories/index.html">Tags</a>', }, } execfile("nikola/nikola.py")