Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

« Newer Snippets
Older Snippets »
Showing 1-2 of 2 total  RSS 

Pattern Matching based WSGI-enabled URL routing tool.

Actual version on http://pypi.python.org/pypi/decoroute

   1  
   2  easy_install decoroute


Example:

   1  
   2  import decoroute
   3      
   4  app = decoroute.App()
   5      
   6  def render_response(status = '200 OK', **kw):
   7      # try your favorite templating engine here
   8      return status, [('Content-Type', 'text/plain')], [str(kw)]
   9  
  10  def redirect_to(env, endpoint, **kw):
  11      return '302 FOUND', [('Content-Type', 'text/plain'), \
  12      ('Location', '%s://%s%s' % (env['wsgi.url_scheme'], env['HTTP_HOST'], \
  13      env['decoroute.app'].url_for(endpoint, **kw)))], ['']
  14      
  15  @app.expose('/node', id = '1')
  16  @app.expose('/node/<id:\d+>')
  17  def node(env, id):
  18      return render_response(id = id)
  19      
  20  @app.expose('/url_for')
  21  def url_for(env):
  22      return render_response( \
  23          url = env['decoroute.app'].url_for(node, id = 666))
  24      
  25  @app.expose('/302')
  26  def found(env):
  27      return redirect_to(env, not_found)
  28      
  29  @app.expose('/404')
  30  def not_found(env):
  31      raise decoroute.NotFound()
  32      
  33  @app.not_found()
  34  def not_found_handler(env):
  35      return render_response('404 NF', url = env['PATH_INFO'])
  36      
  37  from wsgiref.simple_server import make_server
  38      
  39  make_server('', 5555, app).serve_forever()


Complete source < 100 lines of code:

   1  
   2  #!/usr/bin/env python
   3  # vim:ts=4:sw=4:et
   4  # -*- coding: utf-8 -*-
   5  # $Id: decoroute.py,v 6eb37aeee5cd 2008/06/09 07:38:26 vsevolod $
   6  #
   7  # Pattern Matching based WSGI-enabled URL routing tool.
   8  # Actual version on http://pypi.python.org/pypi/decoroute
   9  # (C) 2008 by Vsevolod S. Balashov <vsevolod@balashov.name>
  10  # under terms of GNU LGPL v2.1 http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
  11  
  12  __author__ = "Vsevolod Balashov"
  13  __email__ = "vsevolod at balashov dot name"
  14  
  15  import re
  16  from sets import ImmutableSet
  17  import wsgistraw
  18  
  19  __all__ = ['NotFound', 'App']
  20  
  21  class NotFound(Exception):
  22      pass
  23  
  24  _pattern_re = re.compile(r'''
  25      ([^<]+)                     # static rule data
  26      (?:<
  27          ([a-zA-Z][a-zA-Z0-9_]*) # variable name
  28          \:
  29          ([^>]+)                 # regexp constraint
  30      >)?
  31  ''', re.VERBOSE)
  32  
  33  def pattern2regexp(pattern, f, s = lambda x: re.escape(x)):
  34      def parser():
  35          for t in _pattern_re.findall(pattern):
  36              yield s(t[0])
  37              if t[1] != '':
  38                  yield f(t[1], t[2])
  39      return parser()
  40  
  41  make_url_for = lambda p: ''.join(pattern2regexp(p, lambda v, r: '%%(%s)s' % v, lambda s: s))
  42  make_variables = lambda p: filter(lambda x: x, pattern2regexp(p, lambda v, r: v, lambda s: None))
  43  make_pattern = lambda p: r''.join(pattern2regexp(p, lambda v, r: r'(?P<%s>%s)' % (v, r)))
  44  make_selector_fragment = lambda p: r''.join(pattern2regexp(p, lambda v, r: r'(?:%s)' % r))
  45  make_selector = lambda i: re.compile(r'(^%s$)' % r'$)|(^'.join(map(make_selector_fragment, i)))
  46  
  47  class UrlMap(object):
  48      def __init__(self):
  49          self._endpoints = {}
  50          self._patterns = {}
  51          self._pattern_selector = make_selector(self._patterns.iterkeys())
  52      
  53      def add(self, pattern, endpoint, **kw):
  54          if self._patterns.has_key(pattern):
  55              raise Exception('duplicate pattern', pattern)
  56          self._endpoints[(endpoint, ImmutableSet(make_variables(pattern)))] = make_url_for(pattern)
  57          self._patterns[pattern] = (re.compile(make_pattern(pattern)), endpoint, kw)
  58          self._pattern_selector = make_selector(self._patterns.iterkeys())
  59      
  60      def route(self, url):
  61          try:
  62              p = self._patterns.values()[re.match(self._pattern_selector, url).lastindex - 1]
  63              d = re.match(p[0], url).groupdict().copy()
  64              d.update(p[2])
  65              return p[1], d
  66          except:
  67              raise NotFound('route not found', url)
  68      
  69      def url_for(self, endpoint, **kw):
  70          return self._endpoints[(endpoint, ImmutableSet(kw.keys()))] % kw
  71  
  72  class App(object):
  73      def __init__(self, key = 'decoroute.app'):
  74          self._key = key
  75          self._map = UrlMap()
  76          self._not_found = lambda e: ('404 NOT FOUND', [("Content-Type", "text/plain")], [''])
  77      
  78      @wsgistraw.app
  79      def __call__(self, env):
  80          try:
  81              env[self._key] = self
  82              endpoint, kw = self._map.route(env['PATH_INFO'])
  83              return endpoint(env, **kw)
  84          except NotFound:
  85              return self._not_found(env)
  86      
  87      def expose(self, pattern, **kw):
  88          def decorate(f):
  89              self._map.add(pattern, f, **kw)
  90              return f
  91          return decorate
  92      
  93      def not_found(self):
  94          def decorate(f):
  95              self._not_found = f
  96              return f
  97          return decorate
  98      
  99      def url_for(self, endpoint, **kw):
 100          return self._map.url_for(endpoint, **kw)

Helper for testing default routes generated by a resource in Ruby on Rails

These methods test that the routes for resources defined in routes.rb are working as expected. Call them from your functional (controller) tests.

Add the following 3 methods to test/test_helper.rb (updated for Rails 1.2.5 which no longer uses semicolons as a separator for the edit action):
   1  
   2  # Test for routes generated by map.resource (singular).
   3  def assert_routing_for_resource(controller, skip=[], nesting=[])
   4    routes = [
   5      ["new",'/new',{},:get], ["create",'',{},:post],
   6      ["show",'',{},:get], ["edit",'/edit',{},:get],
   7      ["update",'',{},:put], ["destroy",'',{},:delete]
   8      ]
   9    check_resource_routing(controller, routes, skip, nesting)
  10  end
  11  # Test for routes generated by map.resources (plural).
  12  def assert_routing_for_resources(controller, skip=[], nesting=[])
  13    routes = [
  14      ["index",'',{},:get], ["new",'/new',{},:get], ["create",'',{},:post],
  15      ["show",'/1',{:id=>'1'},:get], ["edit",'/1/edit',{:id=>'1'},:get],
  16      ["update",'/1',{:id=>'1'},:put], ["destroy",'/1',{:id=>'1'},:delete]
  17      ]
  18    check_resource_routing(controller, routes, skip, nesting)
  19  end
  20  
  21  # Check that the expected paths will be generated by a resource, and that
  22  # the expected params will be generated by paths defined by a resource.
  23  # routes is array of [action, url string after controller, extra params].
  24  def check_resource_routing(controller, routes, skip=[], nesting=[])
  25    # set a prefix for nested resources
  26    prefix = nesting.join('s/1/')
  27    unless prefix.blank?
  28      prefix += "s/1/"
  29    end
  30    # Add params for nested resources.
  31    # For each 'nest', include a ":nest_id=>'1'" param.
  32    params = {}
  33    nesting.each do |param|
  34      params["#{param}_id".to_sym] = '1'
  35    end
  36    # Test each of the standard resource routes.
  37    routes.each do |pair|
  38      unless skip.include? pair[0]
  39        assert_generates("/#{prefix}#{controller}#{pair[1]}",
  40          {:controller=>controller,
  41          :action=>pair[0]}.merge(pair[2]).merge(params), {}, {},
  42          "Failed generation of resource route for action #{pair[0]} /#{prefix}#{controller}#{pair[1]}")
  43        assert_recognizes(
  44          {:controller=>controller,
  45            :action=>pair[0]}.merge(pair[2]).merge(params),
  46          {:path=>"/#{prefix}#{controller}#{pair[1]}", :method=>pair[3]},
  47          {}, "Failed to recognize resource route for path #{pair[3]}:/#{prefix}#{controller}#{pair[1]}")
  48      end
  49    end
  50  end

EXAMPLES

You can specify actions to 'skip' (if you have a special route for that action).
If using nested resources, set the nesting array (use singular strings).

So, if you have the following in routes.rb:
   1  
   2  map.make_thing '/make', :controller=>'things', :action=>'new'
   3  map.resources :nests do |nest|
   4    nest.resources :things
   5  end
   6  map.resource :foo

then you can use the following in things_controller_test.rb:
   1  
   2  def test_resource_routing
   3    assert_routing_for_resources 'things', ['new'], ['nest']
   4    assert_routing_for_resource 'foo'
   5  end
« Newer Snippets
Older Snippets »
Showing 1-2 of 2 total  RSS