Templatetags

Since my super-long discussion with @dmeehan in #78, I've been mulling over the idea of template tags in IK, whether they make sense, and how we would implement them.

Well, I decided to take a crack at it in the hopes of getting some review, feedback, gauging interest, and determining what we're up against. 

I think examples are the clearest, so here's how the template tag works:


	# myapp/imagespecs.py
	
	from imagekit.templatetags import spec
	from imagekit.processors import resize

	@spec.register('my_spec_identifier')
	class Thumbnail(object):
		processors = [resize.Fit(100, 100)]
		format = 'JPEG'
		options = {'quality': 60}

	class AnotherSpec(object):
		processors = [resize.Fill(300, 500)]

	# For oldschoolers, you don't have to use decorators
	spec.register('my_other_spec', AnotherSpec)

.

	{# mytemplate.html #}

	{% load imagekit_tags %}

    {% spec 'my_spec_identifier' mymodel.profile_image as th %}
    <img src="{{ th.url }}" width="{{ th.width }}" height="{{ th.height }}" />


# Design Decisions

## Tag

The design of the tag itself is pretty straightforward; it's taken from @dmeehan's suggestion in #78. Look up a spec, apply it to an image, store the result in a context var.

## Registry

The tag would be pretty pointless if your view had to look up an object to pass to your template, so somehow we need to associate an id with a spec. Currently, I'm using a registry. Another option would be just defining the class in the template:

    {% spec 'myapp.imagespecs.Thumbnail' mymodel.profile_image as th %}

Or, the imagespecs module could be implied (a la the models module in model ids):

    {% spec 'myapp.Thumbnail' mymodel.profile_image as th %}

The problem with these approaches is that it makes it very difficult to override specs. For example, if a third-party app defines some specs that you don't like, what would you do? You'd have to hope that the author exposed the spec ids as settings (which places an additional burden on the author), or rewrite the templates. Neither is acceptable. With the registry approach, you can unregister their spec and register your own:

	# myapp/imagespecs.py
	
	from imagekit.templatetags import spec
	from imagekit.processors import resize
	from thirdparty.imagespecs import Thumbnail

	class MyThumbnail(object):
		processors = [resize.Fit(35, 35)]

	spec.unregister('my_spec_identifier', Thumbnail)
	spec.register('my_spec_identifier', MyThumbnail)

So specs are now overridable from other apps which I think solves the distribution problem. The only thing is that spec ids have to be unique, so we can't have everybody registering specs with the id "thumbnail". I'm not really sure how we can enforce this. Right now I'm thinking we'll just have to rely on the convention set in our documentation; we can use something like "myapp:specname". For example,

	# myapp/imagespecs.py
	
	from imagekit.templatetags import spec
	from imagekit.processors import resize

	@spec.register('myapp:my_spec_identifier')
	class Thumbnail(object):
		processors = [resize.Fit(100, 100)]
		format = 'JPEG'
		options = {'quality': 60}

.

	{# mytemplate.html #}

	{% load imagekit_tags %}

    {% spec 'myapp:my_spec_identifier' mymodel.profile_image as th %}
    <img src="{{ th.url }}" width="{{ th.width }}" height="{{ th.height }}" />

If anybody can think of a better solution, let me know. At first I thought the app could be implied (like with the url templatetag) but I'm not quite sure where that value comes from. (It doesn't seem to get populated for me.)


# Remaining Hurdles


We have no mechanism for template tag spec images to be invalidated. With the ones created by `ImageSpecField`, we know when the model is saved, and can use that to invalidate the spec image.

What are the ramifications of this?

* We have to rely on filenames more heavily. More specifically, we need to be certain that if the source image changes, so will the spec image filename. This is not guaranteed when we allow the user to select a spec image filename. So it seems like we can't provide the `cache_to` functionality that spec fields enjoy.
* If the user changes a spec, the image will not be regenerated. One possible workaround for this would be to create a hash of the spec (which would in turn require processors to implement `__hash__`). We could then use that value in the filename.

Spec images generated with template tags also can't be invalidated or revalidated using the management commands. That's because they work by iterating over each model's `ImageSpecField`s. With the template tags, we have no store of the spec images that have been generated, and therefore no way of knowing what files to validate or invalidate.

# So?

So what do you think? Is it worth it? Have any clever ideas for getting around these issues?