Really, Your still using Internet Explorer? Get a real browser and comeback later. Or keep looking, you're used to things being ugly anyway.

Readme.txt

Hardly ever read, but often useful information

Creating good documentation

As a developer that primarily learns by reading documentation, I have an great appreciation for it. In fact I probably wouldn't know anything about Django, Python, or Javascript except there are an abundance of people writing about them.

So you would think that my appreciation would convert into active documentation of my own code. Unfortunately, it hasn't.

Recently, I've been working to provide documentation for much of the code I've written during the last two years. It's been a challenge to say the least.

The first challenge has been to determine the style of documentation. A friend has consistently complained about Django documentation, because it reads more like a how-to manual instead of function-by-function code review. He thinks good documentation should explain each function, what it accepts as args and kwargs, and what it returns. Python documentation basically follows this standard as does most PHP docs.

What's I've determined so far is that both things have a purpose. A good explanation of how to use an application, setup, and examples of use are really useful. What is great about Django documentation is the readability. You can read through an application's documentation and get a good idea of the application's purpose, best uses and some example of functionality. However, there are lots of functions that are not even mentioned. When trying to leverage the tools available you are left reading code and attempting to figure out what each function adds to the whole

In contrast a function by function method often doesn't have enough examples of use. These brief notes on each function are also not necessarily easy for beginners to understand.

There's some middle ground. So I've begun my documentation adventure by taking each application and writing a Django style instructional. One addition is an explanation of "why" things are done in a particular way. In our custom code, sometime feature creep leads to complicated additions. It's best to tell other developers why and how things were added so they can go into future discussions prepared with a history of the application.

After getting a baseline of explanation, I plan to go back and take a function by function approach. This is especially important for those functions that provide special information or are expensive on the servers.

Maybe by Christmas I'll be satisfied that an other developer can come in an take my code to the next project or even the next level. I'm almost as proud of that as I am that my code provides a service to our employees and customers today.

>> More

Code Words

What we really want instead of all the blather

if_in if_notin tags

class IfInNode(Node):
    child_nodelists = ('nodelist_true', 'nodelist_false')

    def __init__(self, var1, var2, lower, using, nodelist_true, nodelist_false, negate):
        self.var1, self.var2, self.lower, self.using = var1, var2, lower, using
        self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
        self.negate = negate

    def __repr__(self):
        return "<IfInNode>"

    def render(self, context):
        if self.lower:
            val1 = self.var1.resolve(context, True)
            if val1:
                val1 = val1.lower()
            val2 = self.var2.resolve(context, True)
            if val2:
                val2 = val2.lower()
        else:
            val1 = self.var1.resolve(context, True)
            val2 = self.var2.resolve(context, True)
        if self.using == 're.search':
            match = re.search(val1, val2)
            if match:
                return self.nodelist_true.render(context)
        elif self.using == 're.match':
            match = re.match(val1, val2)
            if match:
                return self.nodelist_true.render(context)
        elif self.negate:
            if val1 not in val2:
                return self.nodelist_true.render(context)
        else:
            if val1 in val2:
                return self.nodelist_true.render(context)
        return self.nodelist_false.render(context)

def do_ifin(parser, token, negate):
    bits = list(token.split_contents())
    lower = False
    using = None
    if len(bits) < 3:
        raise template.TemplateSyntaxError, "'%s' tag requires two items to compare" % bits[0]
    elif len(bits) == 3:
        var1 = bits[1]
        var2 = bits[2]
    elif len(bits) == 4 and bits[3] == 'ignore_case':
        var1 = bits[1]
        var2 = bits[2]
        lower = True
    elif len(bits) == 5 and bits[3] == 'using':
        var1 = bits[1]
        var2 = bits[2]
        using = bits[4]
    elif len(bits) == 6 and bits[3] == 'ignore_case' and bits[4] == 'using':
        var1 = bits[1]
        var2 = bits[2]
        using = bits[4]
        lower = True
    else:
        raise template.TemplateSyntaxError, "'%s' tag takes two arguements followed by the optional keyword 'ignore_case' and a 'using' method" % bits[0]
    end_tag = 'end' + bits[0]
    nodelist_true = parser.parse(('else', end_tag))
    token = parser.next_token()
    if token.contents == 'else':
        nodelist_false = parser.parse((end_tag,))
        parser.delete_first_token()
    else:
        nodelist_false = NodeList()
    val1 = parser.compile_filter(bits[1])
    val2 = parser.compile_filter(bits[2])
    return IfInNode(val1, val2, lower, using, nodelist_true, nodelist_false, negate)


@register.tag()
def if_in(parser, token):
    """
    Checks to see if string arguement 1 is contained in string arguement 2. If the optional 'ignore_case' keyword is present then the search will be case insensitive.
    There's an optional 'use' arguement if you want to use re.search or re.match for the matching.

    Syntax::

        {% if_in string1 string2 [lower] [using re.match] %}

    Example::

        {% if_in 'ipad' request.META.HTTP_USER_AGENT ignore_case using re.match %}
    """
    return do_ifin(parser, token, False)
if_in = register.tag(if_in)

@register.tag()
def if_notin(parser, token):
    """
    Checks to see if string arguement 1 is not contained in string arguement 2. If the optional 'ignore_case' keyword is present then the search will be case insensitive.
    There's an optional 'use' arguement if you want to use re.search or re.match for the matching.

    Syntax::

        {% if_in string1 string2 [lower] [using re.match] %}

    Example::

        {% if_in 'ipad' request.META.HTTP_USER_AGENT ignore_case using re.match %}
    """
    return do_ifin(parser, token, True)
if_notin = register.tag(if_notin)

>> More