Commits

Ollie Rutherfurd committed bbeb39b

calling easyurl wraps calling 'url(regex(...), ...)'

It's what I find myself doing, so why not make it the default. Tried to
make the docs a little less mystical, too.

Comments (0)

Files changed (1)

 r"""
 Making it easier to read and write Django URL patterns.
 
-By making assumptions, like ``year`` is usually a 4-digit number and
-``id`` 1 or more digits, `django-easyurls` takes much of the repetition
-out of defining URLs, using a syntax that's shorter and easier to read.
+By assuming defaults, such as `year` is a 4-digit number and `id` is
+one or more digits, `easyurls` removes much of the repetition from
+defining URLs in Django.  As a result, URLs are shorter, easier to read,
+and easier to write.
 
 Compare the following:
 
         url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 'archive_month', info_dict),
         url(r'^(?P<year>\d{4})/$', 'archive_year',  info_dict),
     )
-    
-    # use easyurls, and let it generate the regex for you
+
+    # using easyurls: let it generate the regex
     from easyurls import regex as p
     urlpatterns += patterns('django.views.generic.date_based',
         url(p('<year>/<month:mon>/<day>/<slug>'), 'object_detail', info_dict),
         url(p('<year>'),                          'archive_year',  info_dict),
     )
 
-These two sets of URL patterns are functionally equivalent -- the same
-regex is passed to ``url()`` in both cases, but the second is shorter
-and clearer.  Why keep repeating that year is a 4-digit number, month is
-3 letters, and day a 1 or 2 digit number?  Also, since 99% of the
-time one wants the pattern to start with '^', and end with '/$', why
-keep repeating it?  If you don't want them, you can say so.
+    # using easyurls: calling easyurl is equivalent to url(regex(...), ...)
+    from easyurls import easyurl
+    urlpatterns += patterns('django.views.generic.date_based',
+        easyurl('<year>/<month:mon>/<day>/<slug>', 'object_detail', info_dict),
+        easyurl('<year>/<month:mon>/<day>',        'archive_day',   info_dict),
+        easyurl('<year>/<month:mon>',              'archive_month', info_dict),
+        easyurl('<year>',                          'archive_year',  info_dict),
+    )
 
-django-easyurls works by defining names for patterns and generating
+
+These three sets of URL patterns are equivalent.  In the second and
+third sets, ``easyurl`` generated the same regular expressions as in the
+first set.  The difference is the patterns in second and third set are
+easier to read, write, and so less likely to contain errors.  Why keep
+repeating that `year` is a 4-digit number or that `id` is an integer?
+Why not have reasonable defaults, that can be overridden when deviating
+from them.  For example, by default `month` is a 1- or 2-digit year, but
+above we're overriding the default, expecting instead a 3-letter
+abbreviation.  Lastly, continuing the trend of less repetition: by
+default, if missing, '^' is prepended to patterns and '/' and '$' are
+appended.
+
+`easyurls` works by defining names for patterns and generating
 regular expressions for you.  By default, the name of the captured
-variable is the name of the pattern.  This can be overriden, as is done
-above where the "mon" pattern is used for "month", instead of the
+variable is the name of the pattern.  This can be overridden, as is done
+above where the `mon` pattern is used for `month`, instead of the
 default ``\d{1,2}``.
 
-Here's a list of the default patterns:
+Let's dive in and explore easyurls.
 
 .. sourcecode:: pycon
 
-    >>> from easyurls import regex as p
-    >>> for name in sorted(p.patterns):
-    ...     print '%5s: %s' % (name,p.patterns[name])
+    >>> from easyurls import easyurl, regex
+
+``easyurl`` is an automatically created instance of
+``URLPatternGenerator``.
+
+.. sourcecode:: pycon
+
+    >>> type(easyurl)
+    <class 'easyurls.URLPatternGenerator'>
+
+``regex`` is just an alias for ``easyurl.regex``, which generates the
+regular expressions.
+
+.. sourcecode:: pycon
+
+    >>> easyurl.regex == regex
+    True
+    >>> print regex('article/<id>/edit')
+    ^article/(?P<id>\d+)/edit/$
+
+Here are the default patterns:
+
+.. sourcecode:: pycon
+
+    >>> for name in sorted(easyurl.patterns):
+    ...     print '%5s: %s' % (name, easyurl.patterns[name])
       day: \d{1,2}
        id: \d+
       mon: [a-z]{3}
 .. sourcecode:: pycon
 
     # default for month is \d{1,2}
-    >>> print p('<month>')
+    >>> print easyurl.regex('<month>')
     ^(?P<month>\d{1,2})/$
 
     # using [a-z]{3} for month
-    >>> print p('<month:mon>')
+    >>> print easyurl.regex('<month:mon>')
     ^(?P<month>[a-z]{3})/$
 
     # using [a-z]{3} for mmm
-    >>> print p('<mmm:mon>')
+    >>> print easyurl.regex('<mmm:mon>')
     ^(?P<mmm>[a-z]{3})/$
 
 It's easy to add new or override existing patterns:
 
 .. sourcecode:: pycon
 
-    >>> p['yy'] = r'\d{2}'
-    >>> p['mm'] = r'\d{2}'
-    >>> p['dd'] = r'\d{2}'
+    >>> easyurl['yy'] = r'\d{2}'
+    >>> easyurl['mm'] = r'\d{2}'
+    >>> easyurl['dd'] = r'\d{2}'
 
-    >>> print p('<year:yy>/<month:mm>/<day:dd>')
+    >>> print easyurl.regex('<year:yy>/<month:mm>/<day:dd>')
     ^(?P<year>\d{2})/(?P<month>\d{2})/(?P<day>\d{2})/$
 
 By default, if no pattern is found, ``\d+`` is assumed.
 
 .. sourcecode:: pycon
 
-    >>> print p('releases/<project_id>')
+    >>> print easyurl.regex('releases/<project_id>')
     ^releases/(?P<project_id>\d+)/$
 
 For flexibility, you can always use a regular expression.
 .. sourcecode:: pycon
 
     # regex for unknown "zip_code"
-    >>> print p('zip/<zip_code:\d{5}>')
+    >>> print easyurl.regex('zip/<zip_code:\d{5}>')
     ^zip/(?P<zip_code>\d{5})/$
 
     # override slug, allowing "."
-    >>> print p('<slug:[\w-.]+>')
+    >>> print easyurl.regex('<slug:[\w-.]+>')
     ^(?P<slug>[\w-.]+)/$
 
-For demonstration, and testing, purposes here's how prepending and
+For demonstration and testing purposes, here's how prepending and
 appending or '^', '/', and '$' is handled:
 
 .. sourcecode:: pycon
 
-    >>> print p('')
+    >>> print easyurl.regex('')
     ^$
-    >>> print p('foo$')
+    >>> print easyurl.regex('foo$')
     ^foo$
-    >>> print p('foo/')
+    >>> print easyurl.regex('foo/')
     ^foo/$
-    >>> print p('/')
+    >>> print easyurl.regex('/')
     ^/$
 
 Prepending of '^' and appending of '/' and '$' can be disabled.
 
 .. sourcecode:: pycon
 
-    >>> p('foo', anchor=False, terminate=False, append_slash=False)
+    >>> easyurl.regex('foo', anchor=False, terminate=False, append_slash=False)
     'foo'
 """
-__version__ = '0.1'
+__version__ = '0.2'
+__all__ = ['easyurl', 'regex', 'URLPatternGenerator']
 
 import functools,re
 
         self.patterns[name] = pattern
     __setitem__ = add
     
-    def replace(self, match, url):
+    def _replace(self, match, url):
         regexp = None
         name = match.group('name')
         pattern = match.group('pattern')
         segment = '(?P<%s>%s)' % (name, regexp)
         return segment
     
-    def __call__(self, url, **kw):
-        r = VARIABLE.sub(functools.partial(self.replace, url=url), url)
+    def url(self, path, *args, **kw):
+        # replacement for django.conf.urls.defaults.url
+        from django.conf.urls.defaults import url as _url
+        return _url(self.regex(url), *args, **kw)
+    __call__ = url
+    
+    def regex(self, url, **kw):
+        r = VARIABLE.sub(functools.partial(self._replace, url=url), url)
         if kw.get('anchor', self.anchor) and r[:1] != '^':
             r = '^' + r
         # special-case so '^$' doesn't end up with a '/' in it
 
 
 # don't require creating an instance of the class
-regex = URLPatternGenerator()
-
-try:
-    from django.conf.urls.defaults import url as _url
-    def easyurl(url, *args, **kw):
-        return _url(regex(url), *args, **kw)
-except ImportError:
-    pass
+easyurl = URLPatternGenerator()
+regex = easyurl.regex
 
 
 if __name__ == '__main__':