Skip to content

Commit

Permalink
- Added flash messaging, as described in the "Flash Messaging" narrative
Browse files Browse the repository at this point in the history
  documentation chapter.
  • Loading branch information
mcdonc committed Dec 22, 2010
1 parent fa3bf3d commit 4df636c
Show file tree
Hide file tree
Showing 14 changed files with 394 additions and 2 deletions.
5 changes: 5 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ Features
generate a URL, overriding the default logic. See the new "Generating The
URL Of A Resource" section within the Resources narrative chapter.

- Added flash messaging, as described in the "Flash Messaging" narrative
documentation chapter.

Documentation
-------------

Expand All @@ -33,6 +36,8 @@ Documentation
- Added "Finding a Resource With a Class or Interface in Lineage" to
Resources narrative chapter.

- Added a "Flash Messaging" narrative documentation chapter.

1.0a7 (2010-12-20)
==================

Expand Down
2 changes: 0 additions & 2 deletions TODO.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ Must-Have (before 1.0)

- Narrative docs for ``Configurator.include`` and ``Configurator.commit``.

- Provide a .flash API on session object.

- Consider deprecations for ``model`` and ``resource`` APIs.

Should-Have
Expand Down
1 change: 1 addition & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ documentation is organized alphabetically by module name.
api/config
api/events
api/exceptions
api/flash
api/httpexceptions
api/i18n
api/interfaces
Expand Down
36 changes: 36 additions & 0 deletions docs/api/flash.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
.. _flash_module:

:mod:`pyramid.flash`
--------------------

Flash Category Constants
~~~~~~~~~~~~~~~~~~~~~~~~

The following attributes represent constants for use as flash messaging
category values (see :ref:`flash_chapter`).

.. attribute:: DEBUG

An alternate spelling for the string ``debug``. Represents development
debug messages.

.. attribute:: INFO

An alternate spelling for the string ``info``. Represents messages that
are informational for user consumption.

.. attribute:: SUCCESS

An alternate spelling for the string ``success``. Represents messages that
tell the user about a successful action.

.. attribute:: WARNING

An alternate spelling for the string ``warning``. Represents messages
that tell the user about a condition that is not a success, but is neither
an error.

.. attribute:: ERROR

An alternate spelling for the string ``success``. Represents messages
that tell the user about an unsuccessful action.
1 change: 1 addition & 0 deletions docs/api/interfaces.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ Other Interfaces

.. autointerface:: ITemplateRenderer

.. autointerface:: IFlashMessages
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Narrative documentation in chapter form explaining how to use
narr/static
narr/webob
narr/sessions
narr/flash
narr/security
narr/hybrid
narr/i18n
Expand Down
2 changes: 2 additions & 0 deletions docs/latexindex.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Narrative Documentation
narr/static
narr/webob
narr/sessions
narr/flash
narr/security
narr/hybrid
narr/i18n
Expand Down Expand Up @@ -88,6 +89,7 @@ API Reference
api/config
api/events
api/exceptions
api/flash
api/httpexceptions
api/i18n
api/interfaces
Expand Down
123 changes: 123 additions & 0 deletions docs/narr/flash.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
.. _flash_chapter:

Flash Messages
==============

"Flash messages" are simply a queue of message strings stored in the
:term:`session`. To use flash messaging, you must enable a :term:`session
factory` as described in :ref:`using_the_default_session_factory` or
:ref:`using_alternate_session_factories`.

Flash messaging has two main uses: to display a status message only once to
the user after performing an internal redirect, and to allow generic code to
log messages for single-time display without having direct access to an HTML
template. The user interface consists of two methods of the :term:`session`
object.

Using the ``session.flash`` Method
----------------------------------

To add a message to a flash queue, use a session object's ``flash`` method:

.. code-block:: python
:linenos:
request.session.flash('mymessage')
The ``.flash`` method appends a message to the queue, creating the queue if
necessary. The message is not modified in any way.

The ``category`` argument names a category or level. The library defines
several default category names: ``debug``, ``info``, ``success``, ``warning``
and ``error``. The default category level is ``info``.

The ``queue_name`` argument allows you to define multiple message
queues. This can be used to display different kinds of messages in different
places on a page. You cam pass any name for your queue, but it must be a
string. The default value is the empty string, which chooses the default
queue. Each queue is independent, and can be popped by ``unflash``
separately.

Constant names for flash message category names are importable from the
:mod:`pyramid.flash` module as ``DEBUG``, ``INFO``, ``SUCCESS``, ``WARNING``
and ``ERROR``, which respectively name ``debug``, ``info``, ``success``,
``warning`` and ``error`` strings. For example you can do this:

.. code-block:: python
from pyramid import flash
request.session.flash(msg, flash.DEBUG)
Or you can use the literal name ``debug``:

.. code-block:: python
request.session.flash(msg, 'debug')
Both examples do the same thing. The meanings of flash category names are
detailed in :mod:`pyramid.flash`.

To pop a particular queue of messages from the flash object, use the session
object's ``unflash`` method.

.. code-block:: python
:linenos:
>>> request.session.flash('info message', 'info')
>>> messages = request.session.unflash()
>>> messages['info']
['info message']
Using the ``session.unflash`` Method
------------------------------------

Once one or more messages has been added to a flash queue by the
``session.flash`` API, the ``session.unflash`` API can be used to pop that
queue and return it for use.

For example some code that runs in a view callable might call the
``session.flash`` API:

.. code-block:: python
:linenos:
request.session.flash('mymessage')
A corresponding ``session.unflash`` might be called on a subsequent request:

.. code-block:: python
:linenos:
messages = request.session.unflash()
Calling ``session.unflash`` again like above without a corresponding call to
``session.flash`` will return an empty ``messages`` object, because the queue
has already been popped.

The ``messages`` object returned from ``unflash`` is a dictionary-like
object. Its keys are category names, and its values are sequences of
strings. For ease of use, the dict-like object returned by ``unflash`` isn't
a "plain" dict: it's an object which has several helper methods, each named
after a particular flash category level. These methods return all messages
related to the category name:

.. code-block:: python
:linenos:
>>> request.session.flash('debug message', 'debug')
>>> request.session.flash('info message', 'info')
>>> messages = request.session.unflash()
>>> info_messages = messages.debug()
['debug message']
>>> info_messages = messages.info()
['info message']
The full API of the ``messages`` object returned by ``unflash`` is documented
in :class:`pyramid.interfaces.IFlashMessages`.

.. The ``ignore_duplicate`` flag tells whether to suppress duplicate
.. messages. If true, and another message with identical text exists in the
.. queue, don't add the new message. But if the existing message has a
.. different category than the new message, change its category to match the
.. new message.
4 changes: 4 additions & 0 deletions docs/narr/sessions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ A :term:`session` is a namespace which is valid for some period of
continual activity that can be used to represent a user's interaction
with a web application.

.. _using_the_default_session_factory:

Using The Default Session Factory
---------------------------------

Expand Down Expand Up @@ -131,6 +133,8 @@ Some gotchas:
single: pyramid_beaker
single: Beaker

.. _using_alternate_session_factories:

Using Alternate Session Factories
---------------------------------

Expand Down
32 changes: 32 additions & 0 deletions pyramid/flash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from zope.interface import implements

from pyramid.interfaces import IFlashMessages

# flash message categories
DEBUG = 'debug' # development messages
INFO = 'info' # informational messages
SUCCESS = 'success' # a message indicating success
WARNING = 'warning' # not an error, but not a success
ERROR = 'error' # an action was unsuccessful

class FlashMessages(dict):
implements(IFlashMessages)
def custom(self, name):
messages = self.get(name, [])
return messages

def debug(self):
return self.get(DEBUG, [])

def info(self):
return self.get(INFO, [])

def success(self):
return self.get(SUCCESS, [])

def warning(self):
return self.get(WARNING, [])

def error(self):
return self.get(ERROR, [])

40 changes: 40 additions & 0 deletions pyramid/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,34 @@ def implementation():
accepts arbitrary keyword arguments and returns a string or
unicode object """

class IFlashMessages(Interface):
""" Dictionary-like object which maps flash category names to lists of
flash messages. Also supports an API for obtaining classes of flash
message lists."""
def custom(name):
""" Return a sequence of custom-category flash messages or an empty
list if no messages of this custom category existed in the queue."""

def debug():
""" Return a sequence of flash.DEBUG category flash messages or an
empty list if no flash.DEBUG messages existed in the queue."""

def info():
""" Return a sequence of flash.INFO category flash messages or an
empty list if no flash.INFO messages existed in the queue."""

def success():
""" Return a sequence of flash.SUCCESS category flash messages or an
empty list if no flash.SUCCESS messages existed in the queue."""

def warning():
""" Return a sequence of flash.WARNING category flash messages or an
empty list if no flash.WARNING messages existed in the queue."""

def error():
""" Return a sequence of flash.ERROR category flash messages or an
empty list if no flash.ERROR messages existed in the queue."""

# internal interfaces

class IRequest(Interface):
Expand Down Expand Up @@ -460,6 +488,18 @@ def changed():
the sessioning machinery to notice the mutation of the
internal dictionary."""

def flash(msg, category='info', queue_name=''):
""" Push a flash message onto the stack related to the category and
queue name. Multiple flash message queues can be managed by passing
an optional ``queue_name``. Default category names are 'debug',
'info', 'success', 'warning', and 'error' (these have constant names
importable from the ``pyramid.flash`` module). A custom category
name is also permitted."""

def unflash(queue_name=''):
""" Pop a queue from the flash message storage. This method returns
an object which implements ``pyramid.interfaces.IFlashMessages``"""

# mapping methods

def __getitem__(key):
Expand Down
13 changes: 13 additions & 0 deletions pyramid/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from zope.interface import implements

from pyramid.interfaces import ISession
from pyramid import flash

def manage_accessed(wrapped):
""" Decorator which causes a cookie to be set when a wrapped
Expand Down Expand Up @@ -167,6 +168,18 @@ def invalidate(self):
__setitem__ = manage_accessed(dict.__setitem__)
__delitem__ = manage_accessed(dict.__delitem__)

# flash API methods
@manage_accessed
def flash(self, msg, category=flash.INFO, queue_name=''):
storage = self.setdefault('_f_' + queue_name, {})
category = storage.setdefault(category, [])
category.append(msg)

@manage_accessed
def unflash(self, queue_name=''):
storage = self.pop('_f_' + queue_name, {})
return flash.FlashMessages(storage)

# non-API methods
def _set_cookie(self, response):
if not self._cookie_on_exception:
Expand Down
Loading

0 comments on commit 4df636c

Please sign in to comment.