=======================================
Developing bobtemplates.plone templates
=======================================

Setup dev environment
=====================

In the package folder create a virtualenv and install the package:

.. code-block:: shell

   python3 -m venv venv --clear
   ./venv/bin/pip install -e .


Intro
=====

We can have standalone templates and sub-templates for bobtemplates.plone.
By convention we will have a python module and a template folder for every template and use some generic functions from the base module.

All templates are living inside the ``bobtemplates/plone`` folder, in their own template folder.
All module files are placed inside the ``bobtemplates/plone`` folder and are referenced from hook commands in the ``.mrbob.ini`` file in the template folders.
They get called by different ``mrbob hooks``, like ``pre_render``, ``post_render`` or ``pre_question hook`` and so on.


Standalone templates
====================

Standalone templates are normal templates for mrbob, which are meant to live standalone and are not depending on any other template.

Examples are the ``buildout`` and ``addon`` templates. For details see the documentation of the mrbob package.


Sub-templates
=============

Sub-templates are templates which are living inside an existing package created by a standalone template like the ``addon`` template.
These templates extend the existing standalone template structure by new features like a ``theme`` or a ``content_type``.

Sub-templates are searching first for the ``setup.py`` and a ``bobtemplate.cfg`` file inside the package and configure all needed parameters with this information.
See also :doc:`upgrade-packages` to see how to upgrade an existing package to be compatible with sub-templates.
Every sub-template should define a ``pre_renderer`` and a ``post_renderer`` hook in their ``.mrbob.ini`` which points to a method in their sub-templates module.

.. code-block:: ini

   [template]
   pre_render = bobtemplates.plone.<YOURTEMPLATE_MODULE>:pre_renderer
   post_render = bobtemplates.plone.<YOURTEMPLATE_MODULE>:post_renderer


.. code-block:: python

   from bobtemplates.plone.base import base_prepare_renderer


   def pre_renderer(configurator):
       configurator = base_prepare_renderer(configurator)
       configurator.variables['template_id'] = 'content_type'


   def post_renderer(configurator):
       """
       """


As you can see, by convention we define a template_id here. We also call the base_prepare_renderer method from the base module first to setup some generic variables. The ``pre_renderer`` hook is a good place to set template specific variables. If you need you can override or extend the variables, like we do in the ``content_type`` module with the ``target_folder``.

.. code-block:: python

   configurator.target_directory = configurator.variables['package_folder']


The ``post_renderer`` method is a good place to update configuration files, like we do for example in the ``theme`` and ``content_type`` sub-templates.

You can also print some useful advice for the developer here, as we do in the ``vocabulary`` sub-template for example.


Template Registration
=====================

Even though you can use bobtemplates without registration, you should register the template to allow plonecli and future mrbob versions to query for it.
The registration is done by adding a Python entry point into the ``setup.py`` of ``bobtemplates.plone`` and by adding a short method to the ``bobregistry.py`` file.
You can of course create your own custom package, analog to bobtemplates.plone and register your templates plonecli the same way.
This could be used for example for your agency or client specific code structures. If you need help by creating such custom bobtemplates and plonecli integration's, give us a sign on Gitter: https://gitter.im/plone/plonecli.

Let's look first on the entry point:

.. code-block:: python

    entry_points={
        'mrbob_templates': [
            'plone_addon = bobtemplates.plone.bobregistry:plone_addon',
            'plone_content_type = bobtemplates.plone.bobregistry:plone_content_type',
            'plone_vocabulary = bobtemplates.plone.bobregistry:plone_vocabulary',
        ],

This registers every template globally for mrbob and tools like plonecli. The first part is the global template name and the second part points to a method in the bobregistry module. This method gives back some details for the template.

.. code-block:: python

    def plone_vocabulary():
        reg = RegEntry()
        reg.template = 'bobtemplates.plone:vocabulary'
        reg.plonecli_alias = 'vocabulary'
        reg.depend_on = 'plone_addon'
        return reg

The method defines the following things:

- ``template``: the mrbob template to use
- ``plonecli_alias``: a short name alias which will be used by plonecli
- ``depend_on``: an optional global parent template

We use here globally unique template names which have the ``plone_`` prefix.
That is because other ``bobtemplate`` packages might register templates too and we want to avoid name clashes.

Testing
=======

All templates and sub-templates should have tests for the structure they provide.

These tests will give developers a good starting point to write tests for their own code.
Also these tests will be called by Tox and on Travis to make sure that all the structures created by ``bobtemplates.plone`` are working and tested.

We run tests for both all the templates with every combination and inside the generated packages.

For example tests could be run only on ``addon``.
Alternately, for a package with Dexterity content types, tests could be run first for the ``add-on`` template, then inside the package created by the ``content_type`` sub-template.

The tests are running after all templates for a case are applied.

To run all tests locally, just run ``tox`` without any parameter.
You can also run individual tests for a specific environment. To get a list of all environments run ``tox -l``.

.. code-block:: shell

    $ tox -l
    py37-lint
    black-check
    docs
    py39-packagetests
    py38-packagetests
    py37-packagetests
    py39-skeletontests-Plone52-template-addon
    py39-skeletontests-Plone60-template-addon
    py38-skeletontests-Plone52-template-addon
    py38-skeletontests-Plone60-template-addon
    py37-skeletontests-Plone52-template-addon
    py37-skeletontests-Plone60-template-addon
    py39-skeletontests-Plone52-template-addon_all
    py39-skeletontests-Plone60-template-addon_all
    py38-skeletontests-Plone52-template-addon_all
    py38-skeletontests-Plone60-template-addon_all
    py37-skeletontests-Plone52-template-addon_all
    py37-skeletontests-Plone60-template-addon_all
    py37-skeletontests-Plone52-template-addon_theme_barceoneta
    py37-skeletontests-Plone60-template-addon_theme_barceoneta
    coverage-report

You can run just one of them:

.. code-block:: sh

   tox -e py38-skeletontests-Plone52-template-addon

or call all of the same template but for different Plone versions:

.. code-block:: shell

   tox -e py38-skeletontests-Plone43-template-addon_content_type,py27-skeletontests-Plone51-template-add-on_content_type,py27-skeletontests-Plone52-template-add-on_content_type

.. note::

   There is no empty space between the list elements!

Running a specific test
-----------------------

The actual tests are written with the pytest module, therefor you can always run them with pytest directly.

To run a specific pytest with Tox, you can pass additional arguments to pytest, buy putting them after the ``--`` parameter.

.. code-block:: shell

    $ tox -e py36-packagetests -- -k test_set_global_vars

Increase verbosity of Tox/Pytest
................................

.. code-block:: shell

    tox -e py37-packagetests -vv -- -s

Package tests
.............

Package tests are for testing the code of bobtemplates.plone it self. These code is used to generate and update the structures of the generated packages.

You can find these test in the ``package-test`` folder.
This is a good place to test everything related to the generation process.

Skeleton tests
..............

Skeleton tests are for testing, that the generated packages are actually work. We generate the packages, with different combinations of sub-templates, build and run the tests inside.

The tests are defined in the directory ``skeleton-tests`` and are called by ``tox`` as defined in ``tox.ini``.

If you add new test cases (files), make sure that they are in the ``tox.ini`` and also included int the Travis matrix, see below!

Skeleton tests it self are using pytest too, but the tests inside the generated packages are Zope tests running by zc.testrunner.
Starting from version 4.x, packages generated by bobtemplates.plone are containing also a tox setup by them self. This allows you to easily test your package against multiple Python and Plone versions.


