Metadata-Version: 1.0
Name: ao.shorturl
Version: 1.1.6
Summary: Reusable url shortener and lookup library.
Home-page: http://github.com/aatiis/ao.shorturl
Author: Attila Olah
Author-email: attilaolah@gmail.com
License: GNU GPL
Description: In a nutshell
        =============
        
        `ao.shorturl` is a library for integrating short URLs to a web application.
        Its front-end configuration is not specific to any web application framework,
        instead it uses various back-ends for different frameworks.
        
        For example, `ao.shorturl.appengine` implements a Datastore backand for Google
        / Typhoon App Engine. If installed as a Django application, `ao.shorturl` also
        provides a template tag for easily displaying short URLs for any object that
        supports them.
        
        
        Using the short URL library without any framework
        =================================================
        
        
        Registering and getting handlers
        --------------------------------
        
        To use the library, you need to register a _handler_ first, using the
        `ao.shorturl.registerHandler()` function. To get back the handler, use the
        `getHandler()` function.
        
        >>> from ao import shorturl
        >>> shorturl.getHandler()
        Traceback (most recent call last):
        ...
        ImproperlyConfigured: The requested handler is not initialized.
        
        >>> handler = shorturl.registerHandler()
        >>> shorturl.getHandler() is handler
        True
        >>> handler
        <ao.shorturl.BaseShortUrlHandler object at ...>
        
        Note that if you intend to use multiple handlers, you need to give them
        _names_, as the default handler is stored as a module global. However, to
        utilize named handlers, you need to make the `zope.component` and
        `zope.interface` packages available. Each handler is stored in the local site,
        meaning that if you use multiple sites, you can have different handlers with
        the same name on a per-site basis. However, the unnamed handler is still a
        _module_ _global_, so take thet in consideration when using multiple handlers
        and sites.
        
        >>> foo = shorturl.registerHandler(name='foo')
        >>> shorturl.getHandler(name='foo') is shorturl.getHandler('foo') is foo
        True
        
        If you don't have the `zope.component` and `zope.interface` packages
        available, you won't be able to use named handlers.
        
        Let's pretend we don't have `zope.component` and `zope.interface`:
        
        >>> import sys
        
        >>> class _():
        ...     def __init__(self, modules):
        ...         self.modules = modules
        ...
        ...     def find_module(self, fullname, path=None):
        ...         if fullname in self.modules:
        ...             raise ImportError('Debug import failure for %s' % fullname)
        ...
        
        >>> fail_loader = _(['zope.component', 'zope.interface'])
        >>> sys.meta_path.append(fail_loader)
        
        >>> for elem in ('zope.component', 'zope.interface'):
        ...     del sys.modules[elem]
        ...
        
        >>> reload(shorturl)
        <module 'ao.shorturl' from '...'>
        
        >>> del shorturl.zc  # delete the leftover zope.component module
        
        >>> shorturl.registerHandler(name='bar')
        Traceback (most recent call last):
        ...
        ImproperlyConfigured: To use named handlers, you need to make the ...
        
        >>> shorturl.getHandler('bar')
        Traceback (most recent call last):
        ...
        ImproperlyConfigured: To use named handlers, you need to make the ...
        
        Remove our import hook:
        
        >>> del sys.meta_path[0]
        
        
        Configuring the handler
        -----------------------
        
        To overwrite any default handler configuration, just pass the apropriate
        keyword argument to the `ao.shorturl.registerHandler()` function.
        
        >>> len(shorturl.registerHandler().generate_url())
        6
        
        >>> len(shorturl.registerHandler(url_length=10).generate_url())
        10
        
        >>> shorturl.registerHandler(url_length=10, url_elems='x').generate_url()
        'xxxxxxxxxx'
        
        
        Using custom handlers
        ---------------------
        
        When calling `ao.shorturl.registerHandler()` without a `handler` argument, it
        will not have any real functionality.
        
        >>> shorturl.registerHandler().assign_url(None)
        Traceback (most recent call last):
        ...
        NotImplementedError: You must overload `assign_url`.
        
        >>> shorturl.registerHandler().construct_url(None)
        Traceback (most recent call last):
        ...
        NotImplementedError: You must overload `construct_url`.
        
        Registering a custom handler is easy, just subclass
        `ao.shorturl.BaseShortUrlHandler`.
        
        >>> class FancyShortUrlHandler(shorturl.BaseShortUrlHandler):
        ...     def assign_url(self, context):
        ...         context['shorturl'] = self.generate_url()
        ...     def get_context_from_cache(self, url):
        ...         if context['shorturl'] == url:
        ...             return context
        ...         raise LookupError
        ...
        >>> handler = shorturl.registerHandler(handler=FancyShortUrlHandler, url_length=20)
        >>> handler
        <FancyShortUrlHandler object at ...>
        
        >>> context = {'foo': 'bar'}
        >>> handler.assign_url(context)
        >>> len(context['shorturl']) == 20
        True
        
        As for now, there's one custom handler provided for App Engine:
        `ao.shorturl.appengine.AppEngineShortUrlHandler`. It uses the datastore API to
        store the short url associations and the memcache API to cache the keys for
        better performance.
        
        
        Getting the context from the handler
        ------------------------------------
        
        In your view (if you're using an MCV framework), you can call the handler's
        `get_context()` method to query the context for a given short url.
        
        >>> handler.get_context('xxx')
        Traceback (most recent call last):
        ...
        ShortUrlNotFound: Short URL could not be found: xxx
        
        >>> handler.get_context(context['shorturl']) is context
        True
        
        Note that `ao.shorturl.get_context()` will be called at least once each time a
        new short url is created, to check for duplicates.
        
        >>> fired = False
        >>> def get_context(name):
        ...     global fired
        ...     if not fired:
        ...         print 'This URL already exists!'
        ...         fired = True
        ...         return 'Dummy context'
        ...     raise LookupError
        ...
        
        >>> handler.get_context = get_context
        
        >>> handler.generate_url()
        This URL already exists!
        '...'
        
        Clean up after the tests:
        
        >>> from zope.testing import cleanup
        >>> cleanup.cleanUp()
        
        
        Using with Django and template tags
        -----------------------------------
        
        If you use Django, you can access an object's short URL from a template with
        the `shorturl` template tag. To use it, add `ao.shorturl` to your
        `INSTALLED_APPS`. Then in the template you can do something like this:
        
        {% load shorturl %}
        <a href="{ shorturl city }">{{ city.name }}</a>
        
        Note that this will create an _absolute_ url.
        
        Test the template tag:
        
        >>> from ao.shorturl.templatetags import shorturl
        
        >>> class Parser(object):
        ...     def split_contents(self):
        ...         return (None, 'xxx')
        ...
        
        >>> node = shorturl.shorturl(None, Parser())
        >>> node
        <ao.shorturl.templatetags.shorturl.URL object at ...>
        
        >>> node.render({'xxx': None})
        Traceback (most recent call last):
        ...
        NotImplementedError: You must overload `construct_url`.
        
        Clean up after the tests:
        
        >>> from zope.testing import cleanup
        >>> cleanup.cleanUp()
        
        
        TODO
        ====
        
        * Improve documentation
        * Get 100% test coverage
        * Add backends for Django Models and SQLAlchemy/Elixir
        
        
        Changelog
        =========
        
        
        1.1.6 (2010-03-21)
        ==================
        
        * Fixed a few typos
        * More documentation
        * Better test coverage
        
        
        1.1.4 (2010-03-19)
        ==================
        
        * Added some unit tests
        * Updated the documentation
        * Changed the way registering and getting handlers work
        
        
        1.0.0 (2010-03-19)
        ==================
        
        * First public release
        * Added Django template tag
        * Added App Engine backend
        
        
        
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Framework :: Buildout
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved
Classifier: License :: OSI Approved :: GNU General Public License (GPL)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.5
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Topic :: Internet
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
