mirror of
https://github.com/datafolklabs/cement.git
synced 2026-02-06 13:56:49 +00:00
Resolves Issue #270
This commit is contained in:
parent
b4e3e18b7a
commit
ce5a4a8b6a
@ -205,7 +205,7 @@ class CementApp(meta.MetaMixin):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
['/usr/lib/<app_label>/plugins', '~/.<app_label>/plugins']
|
||||
['~/.<app_label>/plugins', '/usr/lib/<app_label>/plugins']
|
||||
|
||||
|
||||
Modules are attempted to be loaded in order, and will stop loading
|
||||
@ -414,11 +414,11 @@ class CementApp(meta.MetaMixin):
|
||||
template_module = None
|
||||
"""
|
||||
A python package (dotted import path) where template files can be
|
||||
loaded from. This is generally something like 'myapp.templates'
|
||||
loaded from. This is generally something like ``myapp.templates``
|
||||
where a plugin file would live at ``myapp/templates/mytemplate.txt``.
|
||||
Templates are first loaded from ``CementApp.Meta.template_dir``, and
|
||||
Templates are first loaded from ``CementApp.Meta.template_dirs``, and
|
||||
and secondly from ``CementApp.Meta.template_module``. The
|
||||
``template_dir`` has presedence.
|
||||
``template_dirs`` setting has presedence.
|
||||
"""
|
||||
|
||||
template_dirs = None
|
||||
@ -426,25 +426,29 @@ class CementApp(meta.MetaMixin):
|
||||
A list of directory paths where template files can be loaded
|
||||
from.
|
||||
|
||||
Note: Though ``Meta.template_dirs`` defaults to None, Cement will set
|
||||
this to a default list based on Meta.label (or in other words, the
|
||||
name of the application). This will equate to:
|
||||
Note: Though ``CementApp.Meta.template_dirs`` defaults to ``None``,
|
||||
Cement will set this to a default list based on
|
||||
``CementApp.Meta.label``. This will equate to:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
['/usr/lib/<app_label>/templates', '~/.<app_label>/templates']
|
||||
['~/.<app_label>/templates', '/usr/lib/<app_label>/templates']
|
||||
|
||||
|
||||
Templates are attempted to be loaded in order, and will stop loading
|
||||
once a template is successfully loaded from a directory.
|
||||
"""
|
||||
|
||||
template_dir = None
|
||||
"""
|
||||
A directory path where template files can be loaded from. By default,
|
||||
this setting is also overridden by the '[base] -> template_dir' config
|
||||
setting parsed in any of the application configuration files (where
|
||||
[base] is the base configuration section of the application which is
|
||||
determinedby ``Meta.config_section`` but defaults to Meta.label).
|
||||
this setting is also overridden by the
|
||||
``[<app_label>] -> template_dir`` config setting parsed in any of the
|
||||
application configuration files .
|
||||
|
||||
If set, this item will be appended to ``Meta.template_dirs``.
|
||||
If set, this item will be **prepended** to
|
||||
``CementApp.Meta.template_dirs`` (giving it precedence over other
|
||||
``template_dirs``.
|
||||
"""
|
||||
|
||||
def __init__(self, label=None, **kw):
|
||||
@ -858,8 +862,8 @@ class CementApp(meta.MetaMixin):
|
||||
# plugin dirs
|
||||
if self._meta.plugin_dirs is None:
|
||||
self._meta.plugin_dirs = [
|
||||
'/usr/lib/%s/plugins' % self._meta.label,
|
||||
os.path.join(fs.HOME_DIR, '.%s' % label, 'plugins'),
|
||||
'/usr/lib/%s/plugins' % self._meta.label,
|
||||
]
|
||||
plugin_dir = self._meta.plugin_dir
|
||||
if plugin_dir is not None:
|
||||
@ -881,15 +885,26 @@ class CementApp(meta.MetaMixin):
|
||||
LOG.debug("no output handler defined, skipping.")
|
||||
return
|
||||
|
||||
label = self._meta.label
|
||||
LOG.debug("setting up %s.output handler" % self._meta.label)
|
||||
self.output = self._resolve_handler('output',
|
||||
self._meta.output_handler,
|
||||
raise_error=False)
|
||||
# template module
|
||||
if self._meta.template_module is None:
|
||||
self._meta.template_module = '%s.templates' % self._meta.label
|
||||
if self._meta.template_dir is None:
|
||||
self._meta.template_dir = '/usr/lib/%s/templates' % \
|
||||
self._meta.label
|
||||
self._meta.template_module = '%s.templates' % label
|
||||
|
||||
# template dirs
|
||||
if self._meta.template_dirs is None:
|
||||
self._meta.template_dirs = [
|
||||
os.path.join(fs.HOME_DIR, '.%s' % label, 'templates'),
|
||||
'/usr/lib/%s/templates' % label,
|
||||
]
|
||||
template_dir = self._meta.template_dir
|
||||
if template_dir is not None:
|
||||
if template_dir not in self._meta.template_dirs:
|
||||
# insert so that this dir has precedence
|
||||
self._meta.template_dirs.insert(0, template_dir)
|
||||
|
||||
def _setup_cache_handler(self):
|
||||
if self._meta.cache_handler is None:
|
||||
|
||||
@ -108,20 +108,24 @@ class TemplateOutputHandler(CementOutputHandler):
|
||||
"""
|
||||
|
||||
def _load_template_from_file(self, template_path):
|
||||
template_prefix = self.app._meta.template_dir.rstrip('/')
|
||||
template_path = template_path.lstrip('/')
|
||||
full_path = fs.abspath(os.path.join(template_prefix, template_path))
|
||||
LOG.debug("attemping to load output template from file %s" %
|
||||
full_path)
|
||||
if os.path.exists(full_path):
|
||||
content = open(full_path, 'r').read()
|
||||
LOG.debug("loaded output template from file %s" %
|
||||
for template_dir in self.app._meta.template_dirs:
|
||||
template_prefix = template_dir.rstrip('/')
|
||||
template_path = template_path.lstrip('/')
|
||||
full_path = fs.abspath(os.path.join(template_prefix,
|
||||
template_path))
|
||||
LOG.debug("attemping to load output template from file %s" %
|
||||
full_path)
|
||||
return content
|
||||
else:
|
||||
LOG.debug("output template file %s does not exist" %
|
||||
full_path)
|
||||
return None
|
||||
if os.path.exists(full_path):
|
||||
content = open(full_path, 'r').read()
|
||||
LOG.debug("loaded output template from file %s" %
|
||||
full_path)
|
||||
return content
|
||||
else:
|
||||
LOG.debug("output template file %s does not exist" %
|
||||
full_path)
|
||||
continue
|
||||
|
||||
return None
|
||||
|
||||
def _load_template_from_module(self, template_path):
|
||||
template_module = self.app._meta.template_module
|
||||
@ -152,16 +156,16 @@ class TemplateOutputHandler(CementOutputHandler):
|
||||
|
||||
def load_template(self, template_path):
|
||||
"""
|
||||
Loads a template file first from ``self.app._meta.template_dir`` and
|
||||
Loads a template file first from ``self.app._meta.template_dirs`` and
|
||||
secondly from ``self.app._meta.template_module``. The
|
||||
``template_dir`` has presedence.
|
||||
``template_dirs`` have presedence.
|
||||
|
||||
:param template_path: The secondary path of the template *after*
|
||||
either ``template_module`` or ``template_dir`` prefix (set via
|
||||
CementApp.Meta)
|
||||
:param template_path: The secondary path of the template **after**
|
||||
either ``template_module`` or ``template_dirs`` prefix (set via
|
||||
``CementApp.Meta``)
|
||||
:returns: The content of the template (str)
|
||||
:raises: FrameworkError if the template does not exist in either the
|
||||
``template_module`` or ``template_dir``.
|
||||
``template_module`` or ``template_dirs``.
|
||||
"""
|
||||
if not template_path:
|
||||
raise exc.FrameworkError("Invalid template path '%s'." %
|
||||
|
||||
@ -14,6 +14,9 @@ class GenshiOutputHandler(output.TemplateOutputHandler):
|
||||
interface. It provides text output from template and uses the
|
||||
`Genshi Text Templating Language
|
||||
<http://genshi.edgewall.org/wiki/Documentation/text-templates.html>`_.
|
||||
Please see the developer documentation on
|
||||
:ref:`Output Handling <dev_output_handling>`.
|
||||
|
||||
**Note** This extension has an external dependency on ``genshi``. You
|
||||
must include ``genshi`` in your applications dependencies as Cement
|
||||
explicitly does *not* include external dependencies for optional
|
||||
@ -31,10 +34,15 @@ class GenshiOutputHandler(output.TemplateOutputHandler):
|
||||
extensions = ['genshi']
|
||||
output_handler = 'genshi'
|
||||
template_module = 'myapp.templates'
|
||||
template_dir = '/usr/lib/myapp/templates'
|
||||
template_dirs = [
|
||||
'~/.myapp/templates',
|
||||
'/usr/lib/myapp/templates',
|
||||
]
|
||||
# ...
|
||||
|
||||
From here, you would then put a Genshi template file in
|
||||
Note that the above ``template_module`` and ``template_dirs`` are the
|
||||
auto-defined defaults but are added here for clarity. From here, you
|
||||
would then put a Genshi template file in
|
||||
``myapp/templates/my_template.genshi`` and then render a data dictionary
|
||||
with it:
|
||||
|
||||
@ -50,10 +58,15 @@ class GenshiOutputHandler(output.TemplateOutputHandler):
|
||||
|
||||
Configuration:
|
||||
|
||||
This extension honors the ``template_dir`` configuration option under the
|
||||
base configuration section of any application configuration file. It
|
||||
also honors the ``template_module`` and ``template_dir`` meta options
|
||||
under the main application object.
|
||||
To **prepend** a directory to the ``template_dirs`` list defined by the
|
||||
application/developer, an end-user can add the configuration option
|
||||
``template_dir`` to their application configuration file under the main
|
||||
config section:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
[myapp]
|
||||
template_dir = /path/to/my/templates
|
||||
|
||||
"""
|
||||
|
||||
@ -69,8 +82,8 @@ class GenshiOutputHandler(output.TemplateOutputHandler):
|
||||
|
||||
:param data_dict: The data dictionary to render.
|
||||
:param template: The path to the template, after the
|
||||
``template_module`` or ``template_dir`` prefix as defined in the
|
||||
application.
|
||||
``template_module`` or ``template_dirs`` prefix as defined in the
|
||||
application.
|
||||
:returns: str (the rendered template text)
|
||||
|
||||
"""
|
||||
|
||||
@ -37,12 +37,12 @@ def set_output_handler(app):
|
||||
|
||||
|
||||
class JsonOutputHandler(output.CementOutputHandler):
|
||||
|
||||
"""
|
||||
This class implements the :ref:`IOutput <cement.core.output>`
|
||||
interface. It provides JSON output from a data dictionary using the
|
||||
`json <http://docs.python.org/library/json.html>`_ module of the standard
|
||||
library.
|
||||
library. Please see the developer documentation on
|
||||
:ref:`Output Handling <dev_output_handling>`.
|
||||
|
||||
Note: The cement framework detects the '--json' option and suppresses
|
||||
output (same as if passing --quiet). Therefore, if debugging or
|
||||
|
||||
@ -12,11 +12,13 @@ class MustacheOutputHandler(output.TemplateOutputHandler):
|
||||
"""
|
||||
This class implements the :ref:`IOutput <cement.core.output>`
|
||||
interface. It provides text output from template and uses the
|
||||
`Mustache Templating Language <http://mustache.github.com>`_.
|
||||
`Mustache Templating Language <http://mustache.github.com>`_. Please
|
||||
see the developer documentation on
|
||||
:ref:`Output Handling <dev_output_handling>`.
|
||||
|
||||
**Note** This extension has an external dependency on ``pystache``. You
|
||||
must include ``pystache`` in your applications dependencies as Cement
|
||||
explicitly does *not* include external dependencies for optional
|
||||
explicitly does **not** include external dependencies for optional
|
||||
extensions.
|
||||
|
||||
Usage:
|
||||
@ -31,11 +33,16 @@ class MustacheOutputHandler(output.TemplateOutputHandler):
|
||||
extensions = ['mustache']
|
||||
output_handler = 'mustache'
|
||||
template_module = 'myapp.templates'
|
||||
template_dir = '/usr/lib/myapp/templates'
|
||||
template_dirs = [
|
||||
'~/.myapp/templates',
|
||||
'/usr/lib/myapp/templates',
|
||||
]
|
||||
# ...
|
||||
|
||||
From here, you would then put a Mustache template file in
|
||||
`myapp/templates/my_template.mustache` and then render a data dictionary
|
||||
Note that the above ``template_module`` and ``template_dirs`` are the
|
||||
auto-defined defaults but are added here for clarity. From here, you
|
||||
would then put a Mustache template file in
|
||||
``myapp/templates/my_template.mustache`` and then render a data dictionary
|
||||
with it:
|
||||
|
||||
.. code-block:: python
|
||||
@ -50,10 +57,16 @@ class MustacheOutputHandler(output.TemplateOutputHandler):
|
||||
|
||||
Configuration:
|
||||
|
||||
This extension honors the ``template_dir`` configuration option under the
|
||||
base configuration section of any application configuration file. It
|
||||
also honors the ``template_module`` and ``template_dir`` meta options
|
||||
under the main application object.
|
||||
To **prepend** a directory to the ``template_dirs`` list defined by the
|
||||
application/developer, an end-user can add the configuration option
|
||||
``template_dir`` to their application configuration file under the main
|
||||
config section:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
[myapp]
|
||||
template_dir = /path/to/my/templates
|
||||
|
||||
|
||||
"""
|
||||
|
||||
@ -69,8 +82,8 @@ class MustacheOutputHandler(output.TemplateOutputHandler):
|
||||
|
||||
:param data_dict: The data dictionary to render.
|
||||
:param template: The path to the template, after the
|
||||
``template_module`` or ``template_dir`` prefix as defined in the
|
||||
application.
|
||||
``template_module`` or ``template_dirs`` prefix as defined in the
|
||||
application.
|
||||
:returns: str (the rendered template text)
|
||||
|
||||
"""
|
||||
|
||||
@ -15,7 +15,8 @@ class YamlOutputHandler(output.CementOutputHandler):
|
||||
This class implements the :ref:`IOutput <cement.core.output>`
|
||||
interface. It provides YAML output from a data dictionary and uses
|
||||
`pyYAML <http://pyyaml.org/wiki/PyYAMLDocumentation>`_ to dump it to
|
||||
STDOUT.
|
||||
STDOUT. Please see the developer documentation on
|
||||
:ref:`Output Handling <dev_output_handling>`.
|
||||
|
||||
**Note** The cement framework detects the '--yaml' option and suppresses
|
||||
output (same as if passing --quiet). Therefore, if debugging or
|
||||
|
||||
@ -86,7 +86,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Cement'
|
||||
copyright = u'2009-2012, BJ Dierkes'
|
||||
copyright = u'2009-2014, Data Folk Labs, LLC'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
@ -224,8 +224,8 @@ htmlhelp_basename = 'Cementdoc'
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'Cement.tex', u'Cement Documentation',
|
||||
u'BJ Dierkes', 'manual'),
|
||||
('index', 'Cement.tex', u'Cement Framework',
|
||||
u'Data Folk Labs, LLC', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
@ -257,6 +257,6 @@ latex_documents = [
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'cement', u'Cement Documentation',
|
||||
[u'BJ Dierkes'], 1)
|
||||
('index', 'cement', u'Cement Framework',
|
||||
[u'Data Folk Labs, LLC'], 1)
|
||||
]
|
||||
|
||||
@ -147,6 +147,11 @@ they do, Cement will honor them (overriding built-in defaults).
|
||||
If set, this item will be appended to
|
||||
``CementApp.Meta.template_dirs``.
|
||||
|
||||
In general, this setting should not be defined by the developer, as it
|
||||
is primarily used to allow the end-user to define a ``template_dir``
|
||||
without completely trumping the hard-coded list of default
|
||||
``template_dirs`` defined by the app/developer.
|
||||
|
||||
|
||||
Application Configuration Defaults vs Handler Configuration Defaults
|
||||
--------------------------------------------------------------------
|
||||
|
||||
@ -1,40 +1,45 @@
|
||||
.. _dev_output_handling:
|
||||
|
||||
Output Handling
|
||||
===============
|
||||
|
||||
Cement defines an output interface called :ref:`IOutput <cement.core.output>`,
|
||||
as well as the default :ref:`NullOutputHandler <cement.ext.ext_nulloutput>`
|
||||
that implements the interface. This handler is part of Cement, and actually
|
||||
Cement defines an output interface called :ref:`IOutput <cement.core.output>`,
|
||||
as well as the default :ref:`NullOutputHandler <cement.ext.ext_nulloutput>`
|
||||
that implements the interface. This handler is part of Cement, and actually
|
||||
does nothing to produce output. Therefore it can be said that by default
|
||||
a Cement application does not handle rendering output to the console, but
|
||||
can should another output handler be used.
|
||||
a Cement application does not handle rendering output to the console, but
|
||||
can if another output handler be used.
|
||||
|
||||
Please note that there may be other handler's that implement the IOutput
|
||||
interface. The documentation below only references usage based on the
|
||||
Please note that there may be other handler's that implement the ``IOutput``
|
||||
interface. The documentation below only references usage based on the
|
||||
interface and not the full capabilities of the implementation.
|
||||
|
||||
The following output handlers are included and maintained with Cement:
|
||||
|
||||
* :ref:`NullOutputHandler <cement.ext.ext_nulloutput>`
|
||||
* :ref:`JsonOutputHandler <cement.ext.ext_json>`
|
||||
* :ref:`YamlOutputHandler <cement.ext.ext_yaml>`
|
||||
* :ref:`GenshiOutputHandler <cement.ext.ext_genshi>`
|
||||
* :ref:`MustacheOutputHandler <cement.ext.ext_mustache>`
|
||||
|
||||
Please reference the :ref:`IOutput <cement.core.output>` interface
|
||||
Please reference the :ref:`IOutput <cement.core.output>` interface
|
||||
documentation for writing your own output handler.
|
||||
|
||||
Rending Output
|
||||
--------------
|
||||
|
||||
Cement applications do not need to use an output handler by any means. Most
|
||||
small applications can get away with print() statements. However, anyone
|
||||
who has ever built a bigger application that produces a lot of output will
|
||||
know that this can get ugly very quickly in your code.
|
||||
small applications can get away with simple ``print()`` statements. However,
|
||||
anyone who has ever built a bigger application that produces a lot of output
|
||||
will know that this can get ugly very quickly in your code.
|
||||
|
||||
Using an output handler allows the developer to keep their logic clean, and
|
||||
offload the display of relevant data to an output handler, possibly by
|
||||
Using an output handler allows the developer to keep their logic clean, and
|
||||
offload the display of relevant data to an output handler, possibly by
|
||||
templates or other means (GUI?).
|
||||
|
||||
An output handler has a 'render()' function that takes a data dictionary that
|
||||
it uses to produce output. Some output handlers may also accept a 'template'
|
||||
or other parameters that define how output is rendered. This is easily
|
||||
An output handler has a ``render()`` function that takes a data dictionary
|
||||
to produce output. Some output handlers may also accept a ``template``
|
||||
or other parameters that define how output is rendered. This is easily
|
||||
accessible by the application object.
|
||||
|
||||
.. code-block:: python
|
||||
@ -57,8 +62,9 @@ accessible by the application object.
|
||||
# Close the application
|
||||
app.close()
|
||||
|
||||
The above example uses the default output handler, therefore nothing is
|
||||
displayed on screen. That said, if we write our own quickly we can see
|
||||
|
||||
The above example uses the default output handler, therefore nothing is
|
||||
displayed on screen. That said, if we write our own quickly we can see
|
||||
something happen:
|
||||
|
||||
.. code-block:: python
|
||||
@ -70,16 +76,95 @@ something happen:
|
||||
class Meta:
|
||||
label = 'myoutput'
|
||||
|
||||
def render(self, data, template=None):
|
||||
def render(self, data):
|
||||
for key in data:
|
||||
print "%s => %s" % (key, data[key])
|
||||
|
||||
app = foundation.CementApp('myapp', output_handler=MyOutputHandler)
|
||||
...
|
||||
|
||||
|
||||
Which looks like:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ python test.py
|
||||
foo => bar
|
||||
|
||||
|
||||
Rendering Output Via Templates
|
||||
------------------------------
|
||||
|
||||
An extremely powerful feature of Cement is the ability to offload console
|
||||
output to a template output handler. Several are inluded with Cement but not
|
||||
enabled by default (listed above). The following example shows the use of
|
||||
the Mustache templating langugage, as well as Json output handling.
|
||||
|
||||
**myapp.py**
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from cement.core.foundation import CementApp
|
||||
from cement.core.controller import CementBaseController, expose
|
||||
|
||||
|
||||
class MyBaseController(CementBaseController):
|
||||
class Meta:
|
||||
label = 'base'
|
||||
description = 'MyApp Does Amazing Things'
|
||||
|
||||
@expose(hide=True)
|
||||
def default(self):
|
||||
data = dict(foo='bar')
|
||||
print self.app.render(data, 'default.m')
|
||||
|
||||
# always return the data, some output handlers require this
|
||||
# such as Json/Yaml (which don't use templates)
|
||||
return data
|
||||
|
||||
|
||||
class MyApp(CementApp):
|
||||
class Meta:
|
||||
label = 'myapp'
|
||||
base_controller = MyBaseController
|
||||
extensions = ['mustache', 'json']
|
||||
|
||||
# default output handler
|
||||
output_handler = 'mustache'
|
||||
|
||||
|
||||
app = MyApp()
|
||||
try:
|
||||
app.setup()
|
||||
app.run()
|
||||
finally:
|
||||
app.close()
|
||||
|
||||
|
||||
**/usr/lib/myapp/templates/default.m**
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
This is the output of the MyBaseController.default() command.
|
||||
|
||||
The value of the 'foo' variable is => '{{foo}}'
|
||||
|
||||
|
||||
And this looks like:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ python myapp.py
|
||||
|
||||
This is the output of the MyBaseController.default() command.
|
||||
|
||||
The value of the 'foo' variable is => 'bar'
|
||||
|
||||
|
||||
Optionally, we can use the ``JsonOutputHandler`` via ``--json`` to trigger
|
||||
just Json output (supressing all other output) using our return dictionary:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ python myapp.py --json
|
||||
{"foo": "bar"}
|
||||
|
||||
|
||||
@ -80,7 +80,7 @@ plugin_dirs = ``None``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
['/usr/lib/<app_label>/plugins', '~/.<app_label>/plugins']
|
||||
['~/.<app_label>/plugins', '/usr/lib/<app_label>/plugins']
|
||||
|
||||
|
||||
Modules are attempted to be loaded in order, and will stop loading
|
||||
|
||||
Loading…
Reference in New Issue
Block a user