Writing your first Nagare application
=====================================

This document explains you how to create a basic wiki app using the Nagare framework.

Prerequisites
-------------

You have Nagare installed as explained in the :doc:`quickstart`.

Creating your wiki app
----------------------

Change your current working directory to a directory where you'd like to store
your wiki code, then run the command ``<NAGARE_HOME>/bin/nagare-admin create-app mywiki``
on command line.

This will create a ``mywiki`` directory in your current directory with the following elements::

    mywiki/
        conf/
            __init__.py
            mywiki.cfg
        data/
            __init__.py
        mywiki/
            __init__.py
            models.py
            app.py
        setup.py
        static/
            __init__.py

These files are:

- ``__init__.py``: Empty files needed by Python to treat the directories as packages.
- ``models.py``: File that contains the database model description for wiki.
- ``app.py``: File that contains the wiki classes.
- ``setup.py``: File used by setuptools to package the wiki application.
- ``mywiki.cfg``: Wiki configuration file.

Register your wiki application
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Change your current working directory to ``mywiki`` directory and run the command
``<NAGARE_HOME>/bin/python setup.py develop`` on command line.

Presenting your pages
---------------------

Write a simple Wiki Page
~~~~~~~~~~~~~~~~~~~~~~~~

Let's create a simple page and view it in a browser.
Open ``app.py`` and replace its content by the following:

.. code-block:: python

    from nagare import presentation

    class Page(object):
        def __init__(self, title):
            self.title = title

    @presentation.render_for(Page)
    def render(self, h, binding, *args):
        return 'This is page *%s*' % self.title

    def app():
        return Page(u'FrontPage')

The ``@presentation.render_for(Page)`` decoration associates the view with the
python object. Let's check if it worked, run the command
``<NAGARE_HOME>/bin/nagare-admin serve mywiki --reload``, you should
see the following output::

    Application 'app mywiki' registered as '/mywiki'
    serving on http://127.0.0.1:8080

The lightweight web server is up and running, try to visit http://127.0.0.1:8080/mywiki
with your browser. You'll see your first wiki page containing
"This is page \*FrontPage\*".

Hence you launched the server with the option ``--reload``, any modification
made in source code will be automatically reloaded.

Write a page that actually does something
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To generate HTML with Nagare there is no need for templates, it can be completely
forged out of python code. The templating issue will be discussed later.
A link is inserted on the page title that will append "clicked" to the pages title:

.. code-block:: python

    from nagare import presentation

    class Page(object):
        def __init__(self, title):
            self.title = title

        def click_callback(self):
            self.title = '%s has been clicked' % self.title

    @presentation.render_for(Page)
    def render(self, h, binding, *args):
        with h.div:
            h << 'This is page *'
            h << h.a(self.title).action(self.click_callback)
            h << '*'

        return h.root

    def app():
        return Page(u'FrontPage')

The ``a`` tag is generated by the ``h`` renderer wihch is by default the HTML namespace.
As you can see a method can be hooked to this tag through the use of ``.action()``.

Once the action ``click_callBack`` is done the current object is automatically
rendered with its associated view.

Use different views on the same page
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Now, instead of changing the ``title`` when the link is clicked, we will display
the wiki component with an other view.

So, let's create a new view called "clicked":

.. code-block:: python

    from nagare import presentation

    class Page(object):
        def __init__(self, title):
            self.title = title


    @presentation.render_for(Page)
    def render(self, h, comp, model):
        with h.div:
            h << 'This is page *'
            h << h.a(self.title).action(comp.becomes, model='clicked')
            h << '*'

        return h.root


    @presentation.render_for(Page, model='clicked')
    def render(self, h, comp, model):
        with h.div:
            h << 'Page *'
            h << h.a(self.title).action(comp.becomes, model=None),
            h << '* has been clicked'

        return h.root

    def app():
        return Page(u'FrontPage')

Using a database
----------------

Setup database model
~~~~~~~~~~~~~~~~~~~~

Let's start with a wikipage with two properties:

- its name
- its content

Open ``models.py`` file and add the following lines:

.. code-block:: python

    class PageData(Entity):
        page_name = Field(Unicode(40), primary_key=True)
        page_content = Field(Unicode(10*1024))

Model declaration can use any of `Elixir`_ or `SQLAlchemy`_ syntax.

.. _Elixir: http://elixir.ematia.de
.. _SQLAlchemy: http://www.sqlalchemy.org

Setup database connection
~~~~~~~~~~~~~~~~~~~~~~~~~

We gonna use an SQLite database to store our wikipages.

Open ``mywiki.cfg`` and modify its ``[database]`` section like this:

.. code-block:: ini

    [database]
    activated = on
    uri = sqlite:///$here/../data/mywiki.db
    metadata = mywiki.models:__metadata__
    debug = off

Populate database
~~~~~~~~~~~~~~~~~

Let's prepare some data for our wiki

Open ``models.py`` file and add the following lines:

.. code-block:: python

    def populate():
        PageData(
            page_name=u'FrontPage',
            page_content=u'Welcome on my *WikiWiki* !'
        )

        PageData(
            page_name=u'WikiWiki',
            page_content=u'A wiki allows users to create and edit web pages easily.'
        )

Open ``mywiki.cfg`` and modify its ``[database]`` section like this:

.. code-block:: ini

    [database]
    activated = on
    uri = sqlite:///$here/../data/mywiki.db
    metadata = mywiki.models:__metadata__
    debug = off
    populate = mywiki.models:populate

Create database
~~~~~~~~~~~~~~~

Run the command ``<NAGARE_HOME>/bin/nagare-admin create-db mywiki``
on command line. The SQLite database now contains our two pages `FrontPage`
and `WikiWiki`.

Associate database object with our previous page
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Now we use our previous `Page` object to render our page `Frontpage` data:

.. code-block:: python

    ...
    from .models import PageData
    ...

    @presentation.render_for(Page)
    def render(self, h, comp, *args):
        page = PageData.get_by(page_name=self.title)

        with h.div:
            h << 'This is page *'
            h << h.a(self.title).action(comp.becomes, model='clicked')
            h << '*' << h.hr
            h << page.page_content

        return h.root
    ...

Handle wikiwords properly
~~~~~~~~~~~~~~~~~~~~~~~~~

In `Frontage` page there is the wiki word `WikiWiki`. This is the name of the
second page inserted in our database. Let's make this word a link
to the `WikiWiki` page.

first install the docutils package with the ``<NAGARE_HOME>/bin/easy_install docutils`` command,
then open ``app.py`` and replace its content by the following:

.. code-block:: python

    ...
    import docutils.core
    import re
    wikiwords = re.compile(r'\b([A-Z]\w+[A-Z]+\w+)')
    ...

    class Page(object):
        def __init__(self, title):
            self.title = title

            # If the page doesn't exist, create it
            page = PageData.get_by(page_name=self.title)
            if page is None:
                PageData(page_name=title, page_content='')

        def get_wiki_tagged_content(self):
            page = PageData.get_by(page_name=self.title)
            content = docutils.core.publish_parts(page.page_content,
                                                  writer_name='html')['html_body']

            return wikiwords.sub(r'<wiki>\1</wiki>', content)
        ...
    ...
    @presentation.render_for(Page)
    def render(self, h, comp, model):
        content = h.parse_htmlstring(self.get_wiki_tagged_content(), fragment=True)[0]
        for node in content.getiterator():
            if node.tag == 'wiki':
                node.replace(h.a(node.text).action(
                        lambda title=unicode(node.text): comp.becomes(Page(title)))
                        )

        with h.div:
            h << 'This is page *'
            h <<  h.a(self.title).action(comp.becomes, model='clicked')
            h <<  '*' << h.hr
            h <<  content


        return h.root
    ...

Use form to modify pages
~~~~~~~~~~~~~~~~~~~~~~~~

To modify our wiki page, we use another object that will handle the updates.
Let's call it `PageEditor`. Open ``app.py`` and add the following:

.. code-block:: python

    ...
    from nagare.var import Var
    ...

    class Page(object):
        ...
        def edit(self, comp):
            content = comp.call(PageEditor(self))

            if content is not None:
                PageData.get_by(page_name=self.title).page_content = content

    ...
    @presentation.render_for(Page)
    def render(self, h, comp, model):
        content = h.parse_htmlstring(self.get_wiki_tagged_content(), fragment=True)[0]
        for node in content.getiterator():
            if node.tag == 'wiki':
                node.replace(h.a(node.text).action(lambda title=unicode(node.text): comp.becomes(Page(title))))

        with h.div:
            with h.p:
                h << h.a('Edit *', self.title, '* page').action(self.edit, comp)

            h << h.hr
            h << content

        return h.root
    ...
    class PageEditor(object):
        def __init__(self, page):
            self.page = page
            self.content = Var()


    @presentation.render_for(PageEditor)
    def render(self, h, comp, model):
        page = PageData.get_by(page_name=self.page.title)

        with h.form:
            h << h.textarea(page.page_content, rows=10, cols=60).action(self.content)
            h << h.br
            h << h.input(type='submit', value='Save').action(lambda: comp.answer(self.content()))
            h << ' '
            h << h.input(type='submit', value='Cancel').action(comp.answer)

        return h.root

The `call/answer` mechanism is used to show up the form and handle the posted
content. When the ``call()`` method is used the form is displayed and the ``edit()``
method is blocked awaiting an answer that should be send through the use of
the ``answer()`` method. On every input of a form an action can be hooked. They are
called in the `input > submit` order.

Refactoring
-----------

Merge pages into a real wiki app
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Open ``app.py`` and replace the ``app()`` function by the following:

.. code-block:: python

    from nagare import component

    ...

    class Wiki(object):
        pass


    @presentation.render_for(Wiki)
    def render(self, h, comp, model):
        page = component.Component(Page(u'FrontPage'))

        with h.div:
            h << h.div(page) << h.hr
            h << 'View the '
            h << h.a('complete list of pages').action(comp.becomes, model='all')

        return h.root


    @presentation.render_for(Wiki, model='all')
    def render(self, h, comp, model):
        with h.div:
            with h.ul:
                for page in PageData.query.order_by(PageData.page_name):
                    with h.li:
                        h << h.a(page.page_name).action(lambda title=page.page_name: comp.becomes(Page(title)))
            h << h.hr
            h << h.a('Back').action(comp.becomes, model=None)

        return h.root

    app = Wiki

Here, the navigation is done between the Wiki and the pages when we click on
the link to a page, the current component is replaced by the selected page.
For example the bottom part with the link `complete list of pages` has
completely disapeared from the HTML page.

Compose components and views
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Open ``app.py`` and replace the previously inserted code by:

.. code-block:: python

    class Wiki(object):
        def __init__(self):
            self.current_page = component.Component(Page(u'FrontPage'))

        def navigate(self, title):
            self.current_page.becomes(Page(title))


    @presentation.render_for(Wiki)
    def render(self, h, comp, model):
        with h.div:
            h << h.div(self.current_page) << h.hr
            h << 'View the '
            h << h.a('complete list of pages').action(comp.becomes, model='all')

        return h.root

    @presentation.render_for(Wiki, model='all')
    def render(self, h, comp, model):
        with h.div:
            h << 'Select a page:'
            with h.ul:
                for page in PageData.query.order_by(PageData.page_name):
                    with h.li:
                        h << h.a(page.page_name).action(self.navigate, page.page_name)

            h << h.hr
            h << h.a('View the selected page').action(comp.becomes, model=None)

        return h.root

    app = Wiki

With this new version we only change the ``current_page`` attribute when we
click a link to a page. The Wiki application context is preserved, e.g. the
bottom part with the link `complete list of pages` is still there.

