« January 2005 | Main | March 2005 »

February 15, 2005

Subversion for CodeWarrior

Stephen Davis has written a Subversion plugin for MetroWerks CodeWarrior. If you are a CoreWarrior user, you may want to check it out.

February 11, 2005

Spiffy HTML Templating

Yes, it's been done a thousand times, and I've gone and reinvented the wheel by creating yet another templating system for HTML. You might wonder what might posess me to do that, and the answer is that, basically, they all suck. It's entirely possible that mine sucks as well, but it works for my needs, and that's what I wrote it for. Actually, they don't all suck. WebObjects, for example, is pretty good, despite being a Java platform, which is a major reason why I can't use it. It's also not free (but then it's a lot more than a templating engine and includes EOF, which is well worth paying for).

My requirements:

  • No code in HTML.
    Instead, you insert tags, which are replaced with dynamic content. HTML is for composing content, not programming.
  • Components.
    This is something that WebObjects does very well. I want to be able to define nestable containers that implement widgets and whatnot.
    Components are not necessarily embedded in code. They can live in independent files that can be managed entirely apart from code. They may be composed entirely in HTML, or implemented entirely in code, or a combination thereof.
    WebObjects does this by using three files: HTML, bindings, and code. I put the bindings in the HTML, so I use two files: HTML and code.
  • Stream-based.
    Some toolkits (WebObjects included) render a page in memory, then spit it out to the server all at once. That model means that a page doesn't start to render in the user's browser until after all of the page has been rendered to HTML on the server, which may take a while. It is sometimes possible to send a reasonable amount of data to the user before blocking on an expensive operation (eg. a database query), and it should be possible to send data as soon as it becomes available.
  • Python.
    An awful lot of web tools are built for Java. Unfortunately, Java sucks for many reasons, not the least of which is that Java is an island, and doesn't play well with any other language/environment.
    I like Python, so I wrote it in Python, which is easily bridged to Objective-C, which means I can get to the entire Cocoa stack in Mac OS X. (Yeah, that's bridged to Java also, but not as well, and Java still sucks.) Not that AppKit is useful to a typical HTML application, but Core Data (in Tiger) sure might be.
  • Only one special tag.
    The <component> tag is the only tag that is special to the templating system.

Anyway, a first draft is checked into my Subversion tree at http://svn.red-bean.com/wsanchez/trunk/Spiffy/. Docs are sparse, but there is an example set of templates there, which I use to test the code. Spiffy doesn't do form handling (use the cgi module), nor is it an app server. It's just a templating engine, at least for now.

February 07, 2005

Enforcer

I've written a script called enforcer that can be used in a Subversion repository as a pre-commit hook in order to validate code before it is committed into the repository. This allows the repository administrator to enforce all sorts of evil rules. Doing this from scratch is pretty painful, so the script allows you to write hooks into specific commit events such as "this line was added/removed," "this file was modified/added/removed," etc.

From the doc string:

Enforcer is a utility which can be used in a Subversion pre-commit hook script to enforce various requirements which a repository administrator would like to impose on data coming into the repository.

A couple of example scenarios:

  • In a Java project I work on, we use log4j extensively. Use of System.out.println() bypasses the control that we get from log4j, so we would like to discourage the addition of println calls in our code.

    We want to deny any commits that add a println into the code. The world being full of exceptions, we do need a way to allow some uses of println, so we will allow it if the line of code that calls println ends in a comment that says it is ok:

    System.out.println("No log4j here"); // (authorized)

    We also do not (presently) want to refuse a commit to a file which already has a println in it. There are too many already in the code and a given developer may not have time to fix them up before commiting an unrelated change to a file.

  • The above project uses WebObjects, and you can enable debugging in a WebObjects component by turning on the WODebug flag in the component WOD file. That is great for debugging, but massively bloats the log files when the application is deployed.

    We want to disable any commit of a file enabling WODebug, regardless of whether the committer made the change or not; these have to be cleaned up before any successful commit.

What this script does is it uses svnlook to peek into the transaction is progress. As it sifts through the transaction, it calls out to a set of hooks which allow the repository administrator to examine what is going on and decide whether it is acceptable. Hooks may be written (in Python) into a configuration file. If the hook raises an exception, enforcer will exit with an error status (and presumably the commit will be denied by th pre-commit hook). The following hooks are available:

verify_file_added(filename)
called when a file is added.
verify_file_removed(filename)
called when a file is removed.
verify_file_copied(destination_filename, source_filename)
called when a file is copied.
verify_file_modified(filename)
called when a file is modified.
verify_line_added(filename, line)
called for each line that is added to a file.
(verify_file_modified() will have been called on the file beforehand)
verify_line_removed(filename, line)
called for each line that is removed from a file.
(verify_file_modified() will have been called on the file beforehand)
verify_property_line_added(filename, property, line)
called for each line that is added to a property on a file.
verify_property_line_removed(filename, property, line)
called for each line that is removed from a property on a file.

In addition, these functions are available to be called from within a hook routine:

open_file(filename)
Returns an open file-like object from which the data of the given file (as available in the transaction being processed) can be read.

In our example scenarios, we can deny the addition of println calls by hooking into verify_line_added(): if the file is a Java file, and the added line calls println, raise an exception.

Similarly, we can deny the commit of any WOD file enabling WODebug by hooking into verify_file_modified(): open the file using open_file(), then raise if WODebug is enabled anywhere in the file.

Note that verify_file_modified() is called once per modified file, whereas verify_line_added() and verify_line_removed() may each be called zero or many times for each modified file, depending on the change. This makes verify_file_modified() appropriate for checking the entire file and the other two appropriate for checking specific changes to files.

These example scenarios are implemented in the provided example configuration file enforcer.conf.

When writing hooks, it is usually easier to test the hooks on commited transactions already in the repository, rather than installing the hook and making commits to test the them. Enforcer allows you to specify either a transaction ID (for use in a hook script) or a revision number (for testing). You can then, for example, find a revision that you would like to have blocked (or not) and test your hooks against that revision.