diff options
| author | Malthe Borch <mborch@gmail.com> | 2009-08-24 17:21:10 +0000 |
|---|---|---|
| committer | Malthe Borch <mborch@gmail.com> | 2009-08-24 17:21:10 +0000 |
| commit | 241390d911639cb658bbbbbf1bf3d8c21e0c0270 (patch) | |
| tree | 7ebcc33709c8c715debf9b94e5157a1464df260c | |
| parent | 45b545c377945812a83d926d11f1635d279069a6 (diff) | |
| download | pyramid-241390d911639cb658bbbbbf1bf3d8c21e0c0270.tar.gz pyramid-241390d911639cb658bbbbbf1bf3d8c21e0c0270.tar.bz2 pyramid-241390d911639cb658bbbbbf1bf3d8c21e0c0270.zip | |
Added ZCML directive to serve up static files from a directory.
| -rw-r--r-- | CHANGES.txt | 3 | ||||
| -rw-r--r-- | docs/narr/MyProject/myproject/configure.zcml | 5 | ||||
| -rw-r--r-- | docs/narr/MyProject/myproject/views.py | 3 | ||||
| -rw-r--r-- | docs/narr/project.rst | 19 | ||||
| -rw-r--r-- | docs/narr/views.rst | 84 | ||||
| -rw-r--r-- | repoze/bfg/includes/meta.zcml | 6 | ||||
| -rw-r--r-- | repoze/bfg/paster_templates/starter/+package+/configure.zcml | 5 | ||||
| -rw-r--r-- | repoze/bfg/paster_templates/starter/+package+/views.py_tmpl | 3 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_zcml.py | 70 | ||||
| -rw-r--r-- | repoze/bfg/zcml.py | 34 |
10 files changed, 177 insertions, 55 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index fca8c5f0d..50b9758f4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,9 @@ Next release ============ +- Added ``static`` ZCML directive which registers a route for a static + view that serves up files in a directory. + - "tests" module removed from the bfg_alchemy paster template; these tests didn't work. diff --git a/docs/narr/MyProject/myproject/configure.zcml b/docs/narr/MyProject/myproject/configure.zcml index 89bf74525..aa98296c5 100644 --- a/docs/narr/MyProject/myproject/configure.zcml +++ b/docs/narr/MyProject/myproject/configure.zcml @@ -8,10 +8,9 @@ view=".views.my_view" /> - <view - for=".models.MyModel" - view=".views.static_view" + <static name="static" + path="templates/static" /> </configure> diff --git a/docs/narr/MyProject/myproject/views.py b/docs/narr/MyProject/myproject/views.py index d4de33b6d..486019bd7 100644 --- a/docs/narr/MyProject/myproject/views.py +++ b/docs/narr/MyProject/myproject/views.py @@ -1,7 +1,4 @@ from repoze.bfg.chameleon_zpt import render_template_to_response -from repoze.bfg.view import static - -static_view = static('templates/static') def my_view(context, request): return render_template_to_response('templates/mytemplate.pt', diff --git a/docs/narr/project.rst b/docs/narr/project.rst index e2fc69435..131a8ff48 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -565,10 +565,8 @@ registry`. It looks like so: ``.views.my_view`` could be replaced with ``myproject.views.my_view``. -#. Lines 11-15 register a view named ``static``. This view - declaration points at the ``static_view``, which is a view - implementation that serves static files from the filesystem for the - default application. +#. Lines 11-14 register a static view, which will register a view + which serves up the files from the static directory in the package. ``views.py`` ~~~~~~~~~~~~ @@ -582,13 +580,7 @@ in the model, and the response given back to a browser. #. Lines 1-2 import required functions. -#. Line 4 sets up a ``static_view`` which will be consulted when - visitors visit ``/static/<something>``. This view will serve up - CSS and images in our default application. This view is registered - in ``configure.zcml`` as the ``static`` view name for the class - ``MyModel`` (the root). - -#. Lines 6-9 provide the ``my_view`` that was registered as the view. +#. Lines 3-6 provide the ``my_view`` that was registered as the view. ``configure.zcml`` said that the default URL for instances that are of the class ``MyModel`` should run this ``my_view`` function. @@ -622,11 +614,6 @@ in the model, and the response given back to a browser. retrieve the template object without rendering it at all, for additional control. -.. note:: - - For more information about the ``static`` view helper function see - :ref:`static_resources_section`. - .. _modelspy_project_section: ``models.py`` diff --git a/docs/narr/views.rst b/docs/narr/views.rst index b5db0ca63..795fa61a6 100644 --- a/docs/narr/views.rst +++ b/docs/narr/views.rst @@ -630,18 +630,66 @@ includes other response types for Unauthorized, etc. .. _static_resources_section: +Serving Static Resources Using a ZCML directive +----------------------------------------------- + +Using the ``static`` ZCML directive is the preferred way to serve +static resources (like JavaScript and CSS files) within +:mod:`repoze.bfg`. This directive makes static files available at a +name relative to the application root URL, e.g. ``/static``. + +You can either use a package-relative path to a directory or a path +relative to the ZCML file (or an absolute path). + +.. code-block:: xml + :linenos: + + <static + name="static" + path="some_package:static" + /> + + <static + name="static" + path="static" + /> + + <static + name="static" + path="/var/www/static" + /> + +Now put your static files (JS, etc) on your filesystem in the +directory represented as ``/path/to/static/dir``. After this is done, +you should be able to view the static files in this directory via a +browser at URLs prefixed with ``/static/``, for instance +``/static/foo.js`` will return the file +``/path/to/static/dir/foo.js``. The static directory may contain +subdirectories recursively, and any subdirectories may hold files; +these will be resolved by the static view as you would expect. + +.. note:: To ensure that model objects contained in the root don't + "shadow" your static view (model objects take precedence during + traversal), or to ensure that your root object's ``__getitem__`` is + never called when a static resource is requested, you can refer to + your static resources as registered above in URLs as, + e.g. ``/@@static/foo.js``. This is completely equivalent to + ``/static/foo.js``. See :ref:`traversal_chapter` for information + about "goggles" (``@@``). + Serving Static Resources Using a View ------------------------------------- -Using the :mod:`repoze.bfg.view` ``static`` helper class is the -preferred way to serve static resources (like JavaScript and CSS -files) within :mod:`repoze.bfg`. This class creates a callable that -is capable acting as a :mod:`repoze.bfg` view which serves static -resources from a directory. For instance, to serve files within a -directory located on your filesystem at ``/path/to/static/dir`` -mounted at the URL path ``/static`` in your application, create an -instance of :mod:`repoze.bfg.view` 's ``static`` class inside a -``static.py`` file in your application root as below. +For more flexibility, static resources can be served by a view which +you register manually. The :mod:`repoze.bfg.view` ``static`` helper +class is the preferred way to go about this. This class creates a +callable that is capable acting as a :mod:`repoze.bfg` view which +serves static resources from a directory. For instance, to serve +files within a directory located on your filesystem at +``/path/to/static/dir`` mounted at the URL path ``/static`` in your +application, create an instance of :mod:`repoze.bfg.view` 's +``static`` class inside a ``static.py`` file in your application root +as below. .. code-block:: python :linenos: @@ -671,24 +719,6 @@ In this case, ``.models.Root`` refers to the class of which your for ``/anything/static/foo.js`` too, as long as ``anything`` itself is resolveable. -Now put your static files (JS, etc) on your filesystem in the -directory represented as ``/path/to/static/dir``. After this is done, -you should be able to view the static files in this directory via a -browser at URLs prefixed with ``/static/``, for instance -``/static/foo.js`` will return the file -``/path/to/static/dir/foo.js``. The static directory may contain -subdirectories recursively, and any subdirectories may hold files; -these will be resolved by the static view as you would expect. - -.. note:: To ensure that model objects contained in the root don't - "shadow" your static view (model objects take precedence during - traversal), or to ensure that your root object's ``__getitem__`` is - never called when a static resource is requested, you can refer to - your static resources as registered above in URLs as, - e.g. ``/@@static/foo.js``. This is completely equivalent to - ``/static/foo.js``. See :ref:`traversal_chapter` for information - about "goggles" (``@@``). - Using Views to Handle Form Submissions (Unicode and Character Set Issues) ------------------------------------------------------------------------- diff --git a/repoze/bfg/includes/meta.zcml b/repoze/bfg/includes/meta.zcml index 89a540098..69e2eabc0 100644 --- a/repoze/bfg/includes/meta.zcml +++ b/repoze/bfg/includes/meta.zcml @@ -41,6 +41,12 @@ /> <meta:directive + name="static" + schema="repoze.bfg.zcml.IStaticDirective" + handler="repoze.bfg.zcml.static" + /> + + <meta:directive name="repozewho1authenticationpolicy" schema="repoze.bfg.zcml.IRepozeWho1AuthenticationPolicyDirective" handler="repoze.bfg.zcml.repozewho1authenticationpolicy" diff --git a/repoze/bfg/paster_templates/starter/+package+/configure.zcml b/repoze/bfg/paster_templates/starter/+package+/configure.zcml index 89bf74525..aa98296c5 100644 --- a/repoze/bfg/paster_templates/starter/+package+/configure.zcml +++ b/repoze/bfg/paster_templates/starter/+package+/configure.zcml @@ -8,10 +8,9 @@ view=".views.my_view" /> - <view - for=".models.MyModel" - view=".views.static_view" + <static name="static" + path="templates/static" /> </configure> diff --git a/repoze/bfg/paster_templates/starter/+package+/views.py_tmpl b/repoze/bfg/paster_templates/starter/+package+/views.py_tmpl index 9c5fe7a67..d9f0df378 100644 --- a/repoze/bfg/paster_templates/starter/+package+/views.py_tmpl +++ b/repoze/bfg/paster_templates/starter/+package+/views.py_tmpl @@ -1,7 +1,4 @@ from repoze.bfg.chameleon_zpt import render_template_to_response -from repoze.bfg.view import static - -static_view = static('templates/static') def my_view(context, request): return render_template_to_response('templates/mytemplate.pt', diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py index 556f22983..d17babaf3 100644 --- a/repoze/bfg/tests/test_zcml.py +++ b/repoze/bfg/tests/test_zcml.py @@ -1037,6 +1037,76 @@ class TestRouteDirective(unittest.TestCase): self.assertEqual(route_discriminator[3], 'GET') self.assertEqual(route_args, ('path', 'name', None)) +class TestStaticDirective(unittest.TestCase): + def setUp(self): + cleanUp() + import os + here = os.path.dirname(__file__) + self.static_path = os.path.join(here, 'fixtures', 'static') + + def tearDown(self): + cleanUp() + + def _callFUT(self, *arg, **kw): + from repoze.bfg.zcml import static + return static(*arg, **kw) + + def test_absolute(self): + from repoze.bfg.zcml import handler + from repoze.bfg.zcml import connect_route + from repoze.bfg.interfaces import IView + context = DummyContext() + self._callFUT(context, 'name', self.static_path) + actions = context.actions + self.assertEqual(len(actions), 2) + + route_action = actions[0] + route_callable = route_action['callable'] + route_discriminator = route_action['discriminator'] + route_args = route_action['args'] + self.assertEqual(route_callable, handler) + self.assertEqual(route_discriminator[:3], ( + 'view', None, '')) + self.assertEqual(route_discriminator[4], IView) + + route_action = actions[1] + route_callable = route_action['callable'] + route_discriminator = route_action['discriminator'] + route_args = route_action['args'] + self.assertEqual(route_callable, connect_route) + self.assertEqual(route_discriminator, ( + 'route', 'name', None, None)) + self.assertEqual(route_args, ( + 'name*subpath', 'name', None)) + + def test_package_relative(self): + from repoze.bfg.zcml import handler + from repoze.bfg.zcml import connect_route + from repoze.bfg.interfaces import IView + context = DummyContext() + self._callFUT(context, 'name', 'repoze.bfg.tests:fixtures/static') + actions = context.actions + self.assertEqual(len(actions), 2) + + route_action = actions[0] + route_callable = route_action['callable'] + route_discriminator = route_action['discriminator'] + route_args = route_action['args'] + self.assertEqual(route_callable, handler) + self.assertEqual(route_discriminator[:3], ( + 'view', None, '')) + self.assertEqual(route_discriminator[4], IView) + + route_action = actions[1] + route_callable = route_action['callable'] + route_discriminator = route_action['discriminator'] + route_args = route_action['args'] + self.assertEqual(route_callable, connect_route) + self.assertEqual(route_discriminator, ( + 'route', 'name', None, None)) + self.assertEqual(route_args, ( + 'name*subpath', 'name', None)) + class TestResourceDirective(unittest.TestCase): def setUp(self): cleanUp() diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py index 7c1a74ec2..e38fd3449 100644 --- a/repoze/bfg/zcml.py +++ b/repoze/bfg/zcml.py @@ -46,6 +46,7 @@ from repoze.bfg.security import ViewPermissionFactory from repoze.bfg.secpols import registerBBBAuthn +from repoze.bfg.view import static as static_view import martian @@ -338,6 +339,39 @@ def connect_route(path, name, factory): mapper = getUtility(IRoutesMapper) mapper.connect(path, name, factory) +class IStaticDirective(Interface): + name = TextLine( + title=u"The URL prefix of the static view", + description=u""" + The directory will be served up for the route that starts with + this prefix.""", + required=True) + + path = TextLine( + title=u'Path to the directory which contains resources', + description=u'May be package-relative by using a colon to ' + 'seperate package name and path relative to the package directory.', + required=True) + + cache_max_age = Int( + title=u"Cache maximum age", + required=False, + default=None) + +def static(_context, name, path, cache_max_age=3600): + """ Handle ``static`` ZCML directives + """ + + if ':' in path: + package_name, path = path.split(':') + else: + package_name = _context.resolve('.').__name__ + + view = static_view( + path, cache_max_age=cache_max_age, package_name=package_name) + + route(_context, name, "%s*subpath" % name, view=view) + class IViewDirective(Interface): for_ = GlobalObject( title=u"The interface or class this view is for.", |
