Scrap docutils for markdown. #275
[zzz-pokedex.git] / pokedex / db / markdown.py
diff --git a/pokedex/db/markdown.py b/pokedex/db/markdown.py
new file mode 100644 (file)
index 0000000..d8ac683
--- /dev/null
@@ -0,0 +1,100 @@
+# encoding: utf8
+u"""Implements the markup used for description and effect text in the database.
+
+The language used is a variation of Markdown and Markdown Extra.  There are
+docs for each at http://daringfireball.net/projects/markdown/ and
+http://michelf.com/projects/php-markdown/extra/ respectively.
+
+Pokédex links are represented with the extended syntax `[name]{type}`, e.g.,
+`[Eevee]{pokemon}`.  The actual code that parses these is in spline-pokedex.
+"""
+
+import markdown
+import sqlalchemy.types
+
+class MarkdownString(object):
+    """Wraps a Markdown string.  Stringifies to the original text, but .as_html
+    will return an HTML rendering.
+
+    To add extensions to the rendering (which is necessary for rendering links
+    correctly, and which spline-pokedex does), you must append to this class's
+    `markdown_extensions` list.  Yep, that's gross.
+    """
+
+    markdown_extensions = ['extra']
+
+    def __init__(self, source_text):
+        self.source_text = source_text
+        self._as_html = None
+
+    def __unicode__(self):
+        return self.source_text
+
+    @property
+    def as_html(self):
+        """Returns the string as HTML4."""
+
+        if self._as_html:
+            return self._as_html
+
+        md = markdown.Markdown(
+            extensions=self.markdown_extensions,
+            safe_mode='escape',
+            output_format='xhtml1',
+        )
+
+        self._as_html = md.convert(self.source_text)
+
+        return self._as_html
+
+    @property
+    def as_text(self):
+        """Returns the string in a plaintext-friendly form.
+
+        At the moment, this is just the original source text.
+        """
+        return self.source_text
+
+
+class MoveEffectProperty(object):
+    """Property that wraps a move effect.  Used like this:
+
+        MoveClass.effect = MoveEffectProperty('effect')
+
+        some_move.effect            # returns a MarkdownString
+        some_move.effect.as_html    # returns a chunk of HTML
+
+    This class also performs simple substitution on the effect, replacing
+    `$effect_chance` with the move's actual effect chance.
+    """
+
+    def __init__(self, effect_column):
+        self.effect_column = effect_column
+
+    def __get__(self, move, move_class):
+        effect_text = getattr(move.move_effect, self.effect_column)
+        effect_text = effect_text.replace(
+            u'$effect_chance',
+            unicode(move.effect_chance),
+        )
+
+        return MarkdownString(effect_text)
+
+class MarkdownColumn(sqlalchemy.types.TypeDecorator):
+    """Generic SQLAlchemy column type for Markdown text.
+
+    Do NOT use this for move effects!  They need to know what move they belong
+    to so they can fill in, e.g., effect chances.  Use the MoveEffectProperty
+    property class above.
+    """
+    impl = sqlalchemy.types.Unicode
+
+    def process_bind_param(self, value, dialect):
+        if not isinstance(value, basestring):
+            # Can't assign, e.g., MarkdownString objects yet
+            raise NotImplementedError
+
+        return unicode(value)
+
+    def process_result_value(self, value, dialect):
+        return MarkdownString(value)