diff options
Diffstat (limited to 'docs/tutorials/wiki2')
17 files changed, 193 insertions, 101 deletions
diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst index 9aca94fe5..01fdd351e 100644 --- a/docs/tutorials/wiki2/basiclayout.rst +++ b/docs/tutorials/wiki2/basiclayout.rst @@ -32,30 +32,21 @@ entry point happens to be the ``app`` function within the file named #. *Lines 1-4*. Imports to support later code. -#. *Lines 9-11*. Get the database configuration string from the - ``development.ini`` file's ``[app:sqlalchemy]`` section. This will be a - URI (something like ``sqlite://``). +#. *Line 9*. Create a SQLAlchemy database engine from the ``sqlalchemy.`` + prefixed settings in the ``development.ini`` file's ``[app:tutorial]`` + section. This will be a URI (something like ``sqlite://``). -#. *Line 12*. Get the database echo setting from ``development.ini`` - file's ``[app:sqlalchemy]`` section. This will either be ``true`` - or ``false``. If ``true``, the application will print SQL to the - console as it is generated and run by SQLAlchemy. By default, it - is false. +#. *Line 10*. We initialize our SQL database using SQLAlchemy, passing + it the engine -#. Line *13*. We initialize our SQL database using SQLAlchemy, passing - it the db string and a variant of the db_echo value. - -#. *Line 14*. We construct a :term:`Configurator`. ``settings`` is +#. *Line 11*. We construct a :term:`Configurator`. ``settings`` is passed as a keyword argument with the dictionary values passed by PasteDeploy as the ``settings`` argument. This will be a dictionary of settings parsed by PasteDeploy, which contains deployment-related values such as ``reload_templates``, ``db_string``, etc. -#. *Line 15*. We call :meth:`pyramid.configuration.Configurator.begin` which - tells the configuration machinery we are starting configuration. - -#. *Line 16*. We call +#. *Line 12*. We call :meth:`pyramid.configuration.Configurator.add_static_view` with the arguments ``static`` (the name), and ``tutorial:static`` (the path). This registers a static resource view which will match any URL that starts with @@ -67,7 +58,7 @@ entry point happens to be the ``app`` function within the file named ``/static/foo``) will be used to compose a path to a static file resource, such as a CSS file. -#. *Lines 17-18*. Register a :term:`route configuration` via the +#. *Lines 13-14*. Register a :term:`route configuration` via the :meth:`pyramid.configuration.Configurator.add_route` method that will be used when the URL is ``/``. Since this route has an ``pattern`` equalling ``/`` it is the "default" route. The argument named ``view`` with the @@ -81,10 +72,7 @@ entry point happens to be the ``app`` function within the file named ``tutorial.views.my_view`` view returns a dictionary, a :term:`renderer` will use this template to create a response. -#. *Line 19*. We call :meth:`pyramid.configuration.Configurator.end` which - tells the configuration machinery we are ending configuration. - -#. *Line 20*. We use the +#. *Line 15*. We use the :meth:`pyramid.configuration.Configurator.make_wsgi_app` method to return a :term:`WSGI` application. @@ -103,29 +91,28 @@ Here is the source for ``models.py``: :linenos: :language: py -#. *Lines 1-14*. Imports to support later code. +#. *Lines 1-13*. Imports to support later code. -#. *Line 16*. We set up a SQLAlchemy "DBSession" object here. We +#. *Line 15*. We set up a SQLAlchemy "DBSession" object here. We specify that we'd like to use the "ZopeTransactionExtension". This extension is an extension which allows us to use a *transaction manager* instead of controlling commits and aborts to database operations by hand. -#. *Line 17*. We create a declarative ``Base`` object to use as a +#. *Line 16*. We create a declarative ``Base`` object to use as a base class for our model. -#. *Lines 19-27*. A model class named ``MyModel``. It has an +#. *Lines 18-26*. A model class named ``MyModel``. It has an ``__init__`` that takes a two arguments (``name``, and ``value``). It stores these values as ``self.name`` and ``self.value`` within the ``__init__`` function itself. The ``MyModel`` class also has a ``__tablename__`` attribute. This informs SQLAlchemy which table to use to store the data representing instances of this class. -#. *Lines 29-34*. A function named ``populate`` which adds a single +#. *Lines 28-33*. A function named ``populate`` which adds a single model instance into our SQL storage and commits a transaction. -#. *Lines 36-44*. A function named ``initialize_sql`` which sets up - an actual SQL database and binds it to our SQLAlchemy DBSession - object. It also calls the ``populate`` function, to do initial - database population. +#. *Lines 35-42*. A function named ``initialize_sql`` which receives a SQL + database engine and binds it to our SQLAlchemy DBSession object. It also + calls the ``populate`` function, to do initial database population. diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst index b87cd6a64..0f446bb4e 100644 --- a/docs/tutorials/wiki2/definingviews.rst +++ b/docs/tutorials/wiki2/definingviews.rst @@ -24,7 +24,7 @@ The request passed to every view that is called as the result of a route match has an attribute named ``matchdict`` that contains the elements placed into the URL by the ``pattern`` of a ``route`` statement. For instance, if a call to :meth:`pyramid.configuration.Configurator.add_route` in -``__init__.py`` had the pattern ``:one/:two``, and the URL at +``__init__.py`` had the pattern ``{one}/{two}``, and the URL at ``http://example.com/foo/bar`` was invoked, matching this pattern, the matchdict dictionary attached to the request passed to the view would have a ``one`` key with the value ``foo`` and a ``two`` key with the value ``bar``. @@ -277,16 +277,16 @@ the order they're found in the ``__init__.py`` file. to the view named ``view_wiki`` in our ``views.py`` file with the name ``view_wiki``. This is the :term:`default view` for the wiki. -#. Add a declaration which maps the pattern ``/:pagename`` to the view named +#. Add a declaration which maps the pattern ``/{pagename}`` to the view named ``view_page`` in our ``views.py`` file with the view name ``view_page``. This is the regular view for a page. #. Add a declaration which maps the pattern - ``/add_page/:pagename`` to the view named ``add_page`` in our + ``/add_page/{pagename}`` to the view named ``add_page`` in our ``views.py`` file with the name ``add_page``. This is the add view for a new page. -#. Add a declaration which maps the pattern ``/:pagename/edit_page`` to the +#. Add a declaration which maps the pattern ``/{pagename}/edit_page`` to the view named ``edit_page`` in our ``views.py`` file with the name ``edit_page``. This is the edit view for a page. diff --git a/docs/tutorials/wiki2/src/authorization/development.ini b/docs/tutorials/wiki2/src/authorization/development.ini index e1d0ab598..23b01a338 100644 --- a/docs/tutorials/wiki2/src/authorization/development.ini +++ b/docs/tutorials/wiki2/src/authorization/development.ini @@ -5,8 +5,7 @@ debug_authorization = false debug_notfound = false debug_templates = true default_locale_name = en -db_string = sqlite:///%(here)s/tutorial.db -db_echo = false +sqlalchemy.url = sqlite:///%(here)s/tutorial.db [pipeline:main] pipeline = @@ -18,3 +17,37 @@ pipeline = use = egg:Paste#http host = 0.0.0.0 port = 6543 + +# Begin logging configuration + +[loggers] +keys = root, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_sqlalchemy] +level = INFO +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s + +# End logging configuration diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py index 4bddea74c..dbac349b9 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py @@ -2,7 +2,7 @@ from pyramid.configuration import Configurator from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy -from pyramid.settings import asbool +from sqlalchemy import engine_from_config from tutorial.models import initialize_sql from tutorial.security import groupfinder @@ -10,11 +10,8 @@ from tutorial.security import groupfinder def main(global_config, **settings): """ This function returns a WSGI application. """ - db_string = settings.get('db_string') - if db_string is None: - raise ValueError("No 'db_string' value in application configuration.") - db_echo = settings.get('db_echo', 'false') - initialize_sql(db_string, asbool(db_echo)) + engine = engine_from_config(settings, 'sqlalchemy.') + initialize_sql(engine) authn_policy = AuthTktAuthenticationPolicy( 'sosecret', callback=groupfinder) authz_policy = ACLAuthorizationPolicy() @@ -22,7 +19,6 @@ def main(global_config, **settings): root_factory='tutorial.models.RootFactory', authentication_policy=authn_policy, authorization_policy=authz_policy) - config.begin() config.add_static_view('static', 'tutorial:static') config.add_route('view_wiki', '/', view='tutorial.views.view_wiki') config.add_route('login', '/login', @@ -30,20 +26,19 @@ def main(global_config, **settings): view_renderer='tutorial:templates/login.pt') config.add_route('logout', '/logout', view='tutorial.login.logout') - config.add_route('view_page', '/:pagename', + config.add_route('view_page', '/{pagename}', view='tutorial.views.view_page', view_renderer='tutorial:templates/view.pt') - config.add_route('add_page', '/add_page/:pagename', + config.add_route('add_page', '/add_page/{pagename}', view='tutorial.views.add_page', view_renderer='tutorial:templates/edit.pt', view_permission='edit') - config.add_route('edit_page', '/:pagename/edit_page', + config.add_route('edit_page', '/{pagename}/edit_page', view='tutorial.views.edit_page', view_renderer='tutorial:templates/edit.pt', view_permission='edit') config.add_view('tutorial.login.login', renderer='tutorial:templates/login.pt', context='pyramid.exceptions.Forbidden') - config.end() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models.py b/docs/tutorials/wiki2/src/authorization/tutorial/models.py index 7580220b6..487299c4c 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/models.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models.py @@ -3,7 +3,6 @@ import transaction from pyramid.security import Allow from pyramid.security import Everyone -from sqlalchemy import create_engine from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import Text @@ -30,8 +29,7 @@ class Page(Base): self.name = name self.data = data -def initialize_sql(db_string, echo=False): - engine = create_engine(db_string, echo=echo) +def initialize_sql(engine): DBSession.configure(bind=engine) Base.metadata.bind = engine Base.metadata.create_all(engine) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py index 65330ce17..c78899797 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py @@ -14,9 +14,9 @@ def _initTestingDB(): return DBSession def _registerRoutes(config): - config.add_route('view_page', ':pagename') - config.add_route('edit_page', ':pagename/edit_page') - config.add_route('add_page', 'add_page/:pagename') + config.add_route('view_page', '{pagename}') + config.add_route('edit_page', '{pagename}/edit_page') + config.add_route('add_page', 'add_page/{pagename}') class ViewWikiTests(unittest.TestCase): def setUp(self): @@ -28,7 +28,7 @@ class ViewWikiTests(unittest.TestCase): def test_it(self): from tutorial.views import view_wiki - self.config.add_route('view_page', ':pagename') + self.config.add_route('view_page', '{pagename}') request = testing.DummyRequest() response = view_wiki(request) self.assertEqual(response.location, 'http://example.com/FrontPage') diff --git a/docs/tutorials/wiki2/src/basiclayout/development.ini b/docs/tutorials/wiki2/src/basiclayout/development.ini index e1d0ab598..23b01a338 100644 --- a/docs/tutorials/wiki2/src/basiclayout/development.ini +++ b/docs/tutorials/wiki2/src/basiclayout/development.ini @@ -5,8 +5,7 @@ debug_authorization = false debug_notfound = false debug_templates = true default_locale_name = en -db_string = sqlite:///%(here)s/tutorial.db -db_echo = false +sqlalchemy.url = sqlite:///%(here)s/tutorial.db [pipeline:main] pipeline = @@ -18,3 +17,37 @@ pipeline = use = egg:Paste#http host = 0.0.0.0 port = 6543 + +# Begin logging configuration + +[loggers] +keys = root, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_sqlalchemy] +level = INFO +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s + +# End logging configuration diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py index 7a701fc02..5236a538d 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py @@ -1,22 +1,17 @@ from pyramid.configuration import Configurator -from pyramid.settings import asbool +from sqlalchemy import engine_from_config from tutorial.models import initialize_sql def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - db_string = settings.get('db_string') - if db_string is None: - raise ValueError("No 'db_string' value in application configuration.") - db_echo = settings.get('db_echo', 'false') - initialize_sql(db_string, asbool(db_echo)) + engine = engine_from_config(settings, 'sqlalchemy.') + initialize_sql(engine) config = Configurator(settings=settings) - config.begin() config.add_static_view('static', 'tutorial:static') config.add_route('home', '/', view='tutorial.views.my_view', view_renderer='templates/mytemplate.pt') - config.end() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py index a1726ebf4..9da906752 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py @@ -1,6 +1,5 @@ import transaction -from sqlalchemy import create_engine from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import Unicode @@ -33,8 +32,7 @@ def populate(): session.flush() transaction.commit() -def initialize_sql(db_string, db_echo=False): - engine = create_engine(db_string, echo=db_echo) +def initialize_sql(engine): DBSession.configure(bind=engine) Base.metadata.bind = engine Base.metadata.create_all(engine) diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py index 72f0c89d8..2db1bc5b6 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py @@ -3,8 +3,9 @@ from pyramid.configuration import Configurator from pyramid import testing def _initTestingDB(): + from sqlalchemy import create_engine from tutorial.models import initialize_sql - session = initialize_sql('sqlite://') + session = initialize_sql(create_engine('sqlite://')) return session class TestMyView(unittest.TestCase): diff --git a/docs/tutorials/wiki2/src/models/development.ini b/docs/tutorials/wiki2/src/models/development.ini index e1d0ab598..23b01a338 100644 --- a/docs/tutorials/wiki2/src/models/development.ini +++ b/docs/tutorials/wiki2/src/models/development.ini @@ -5,8 +5,7 @@ debug_authorization = false debug_notfound = false debug_templates = true default_locale_name = en -db_string = sqlite:///%(here)s/tutorial.db -db_echo = false +sqlalchemy.url = sqlite:///%(here)s/tutorial.db [pipeline:main] pipeline = @@ -18,3 +17,37 @@ pipeline = use = egg:Paste#http host = 0.0.0.0 port = 6543 + +# Begin logging configuration + +[loggers] +keys = root, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_sqlalchemy] +level = INFO +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s + +# End logging configuration diff --git a/docs/tutorials/wiki2/src/models/tutorial/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/__init__.py index 10fcd0cbc..e1baa2d64 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/models/tutorial/__init__.py @@ -1,20 +1,15 @@ from pyramid.configuration import Configurator -from pyramid.settings import asbool +from sqlalchemy import engine_from_config from tutorial.models import initialize_sql def main(global_config, **settings): """ This function returns a WSGI application. """ - db_string = settings.get('db_string') - if db_string is None: - raise ValueError("No 'db_string' value in application configuration.") - db_echo = settings.get('db_echo', 'false') - initialize_sql(db_string, asbool(db_echo)) + engine = engine_from_config(settings, 'sqlalchemy.') + initialize_sql(engine) config = Configurator(settings=settings) - config.begin() config.add_static_view('static', 'tutorial:static') config.add_route('home', '/', view='tutorial.views.my_view', view_renderer='templates/mytemplate.pt') - config.end() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/models/tutorial/models.py b/docs/tutorials/wiki2/src/models/tutorial/models.py index ec9d2b25c..23b8afab8 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models.py +++ b/docs/tutorials/wiki2/src/models/tutorial/models.py @@ -1,6 +1,5 @@ import transaction -from sqlalchemy import create_engine from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import Text @@ -27,8 +26,7 @@ class Page(Base): self.name = name self.data = data -def initialize_sql(db_string, echo=False): - engine = create_engine(db_string, echo=echo) +def initialize_sql(engine): DBSession.configure(bind=engine) Base.metadata.bind = engine Base.metadata.create_all(engine) diff --git a/docs/tutorials/wiki2/src/views/development.ini b/docs/tutorials/wiki2/src/views/development.ini index e1d0ab598..23b01a338 100644 --- a/docs/tutorials/wiki2/src/views/development.ini +++ b/docs/tutorials/wiki2/src/views/development.ini @@ -5,8 +5,7 @@ debug_authorization = false debug_notfound = false debug_templates = true default_locale_name = en -db_string = sqlite:///%(here)s/tutorial.db -db_echo = false +sqlalchemy.url = sqlite:///%(here)s/tutorial.db [pipeline:main] pipeline = @@ -18,3 +17,37 @@ pipeline = use = egg:Paste#http host = 0.0.0.0 port = 6543 + +# Begin logging configuration + +[loggers] +keys = root, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_sqlalchemy] +level = INFO +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s + +# End logging configuration diff --git a/docs/tutorials/wiki2/src/views/tutorial/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/__init__.py index 9ef923e0b..91c299e24 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/views/tutorial/__init__.py @@ -1,29 +1,24 @@ from pyramid.configuration import Configurator -from pyramid.settings import asbool +from sqlalchemy import engine_from_config from tutorial.models import initialize_sql def main(global_config, **settings): """ This function returns a WSGI application. """ - db_string = settings.get('db_string') - if db_string is None: - raise ValueError("No 'db_string' value in application configuration.") - db_echo = settings.get('db_echo', 'false') - initialize_sql(db_string, asbool(db_echo)) + engine = engine_from_config(settings, 'sqlalchemy.') + initialize_sql(engine) config = Configurator(settings=settings) - config.begin() config.add_static_view('static', 'tutorial:static') config.add_route('home', '/', view='tutorial.views.view_wiki') - config.add_route('view_page', '/:pagename', + config.add_route('view_page', '/{pagename}', view='tutorial.views.view_page', view_renderer='tutorial:templates/view.pt') - config.add_route('add_page', '/add_page/:pagename', + config.add_route('add_page', '/add_page/{pagename}', view='tutorial.views.add_page', view_renderer='tutorial:templates/edit.pt') - config.add_route('edit_page', '/:pagename/edit_page', + config.add_route('edit_page', '/{pagename}/edit_page', view='tutorial.views.edit_page', view_renderer='tutorial:templates/edit.pt') - config.end() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/views/tutorial/models.py b/docs/tutorials/wiki2/src/views/tutorial/models.py index ec9d2b25c..23b8afab8 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/models.py +++ b/docs/tutorials/wiki2/src/views/tutorial/models.py @@ -1,6 +1,5 @@ import transaction -from sqlalchemy import create_engine from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import Text @@ -27,8 +26,7 @@ class Page(Base): self.name = name self.data = data -def initialize_sql(db_string, echo=False): - engine = create_engine(db_string, echo=echo) +def initialize_sql(engine): DBSession.configure(bind=engine) Base.metadata.bind = engine Base.metadata.create_all(engine) diff --git a/docs/tutorials/wiki2/src/views/tutorial/tests.py b/docs/tutorials/wiki2/src/views/tutorial/tests.py index 40336fca4..435e4b588 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/views/tutorial/tests.py @@ -14,9 +14,9 @@ def _initTestingDB(): return DBSession def _registerRoutes(config): - config.add_route('view_page', ':pagename') - config.add_route('edit_page', ':pagename/edit_page') - config.add_route('add_page', 'add_page/:pagename') + config.add_route('view_page', '{pagename}') + config.add_route('edit_page', '{pagename}/edit_page') + config.add_route('add_page', 'add_page/{pagename}') class ViewWikiTests(unittest.TestCase): def setUp(self): @@ -28,7 +28,7 @@ class ViewWikiTests(unittest.TestCase): def test_it(self): from tutorial.views import view_wiki - self.config.add_route('view_page', ':pagename') + self.config.add_route('view_page', '{pagename}') request = testing.DummyRequest() response = view_wiki(request) self.assertEqual(response.location, 'http://example.com/FrontPage') |
