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 :wiki:`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
            mywiki.py
        setup.py
        static/
            __init__.py

These files are:

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

Register your wiki application
------------------------------

Change your current working directory to the wiki 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 ``mywiki.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
    
    app = lambda: 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 clickCallBack(self):
            self.title = "%s has been clicked" % self.title
    
    @presentation.render_for(Page)
    def render(self, h, binding, *args):
        return ('This is page *',
                h.a(self.title).action(self.clickCallBack),
                '*')
    
    app = lambda: Page(u'FrontPage')

The `a` tag is generated by the h renderer, by default it uses html namespace.
As you can see a method can be hooked to this tag through the use of `action`.

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

Use different views on the same page
------------------------------------

Actually, we did not intend to modify the title by a single but to change the
informations shown on the page. To do it we just modify the callback associated
with the link by one which change the model. A model, is a parameter passed to
`@presentation.render_for` decoration to qualifiy it. Let's create a "clicked"
view:

.. 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 *',
                h.a(self.title).action(lambda : binding.becomes(self, model = 'clicked')),
                '*')
    
    
    @presentation.render_for(Page, model = 'clicked')
    def render(self, h, binding, *args):
    
        return ('Page *',
                h.a(self.title).action(lambda : binding.becomes(self)),
                '* has been clicked')
    
    app = lambda: Page(u'FrontPage')

The model change is achieved by replacing the object planned for rendering
`binding` through the use of `becomes` method which accepts an optional `model`
keyword.

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):
        pagename = Field(Unicode(40), primary_key=True)
        pagecontent = 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 `[database]` section like this::

    [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():
        page = PageData()
        page.pagename = u'FrontPage'
        page.data = u'Welcome on my *WikiWiki* !'
        session.save(page)

        page = PageData()
        page.pagename = u'WikiWiki'
        page.data = u'A wiki allows users to create and edit web pages easily.'
        session.save(page)

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

::

    [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, binding, *args):
        page = PageData.get_by(pagename=self.title)
        return ('This is page *',
                h.a(self.title).action(lambda : binding.becomes(self, model = 'clicked')),
                '*',
                h.hr, page.pagecontent)
    ...

Handle wikiwords properly
-------------------------

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

Open ``mywiki.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 getWikiTaggedContent(self):
            page = PageData.get_by(pagename=self.title)
            if page is None:
                page = PageData(pagename=self.title, pagecontent='')
            content = docutils.core.publish_parts(page.pagecontent,
                                                  writer_name='html')['html_body']
            return wikiwords.sub(r'<wiki>\1</wiki>', content)
        ...
    ...
    @presentation.render_for(Page)
    def render(self, h, binding, *args):
        pageContent = h.parse_htmlstring(self.getWikiTaggedContent(), fragment=True)[0]
        for node in pageContent.getiterator():
            if node.tag == 'wiki':
                node.replace(h.a(node.text).action(
                        lambda title=unicode(node.text): binding.becomes(Page(title)))
                        )
                
        return ('This is page *',
                h.a(self.title).action(lambda : binding.becomes(self, model = 'clicked')),
                '*',
                h.hr,
                pageContent)
    ...

Use form to modify pages
------------------------

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

.. code-block:: python

    ...
    from nagare.var import Var
    ...
    
    class Page(object):
        ...
        def edit(self, binding):
            content = binding.call(PageEditor(self))
    
            if content is not None:
                page = PageData.get_by(pagename=self.title)
                if page is None:
                    page = PageData(pagename=self.title, pagecontent='')
                page.pagecontent = content
    
    ...
    @presentation.render_for(Page)
    def render(self, h, binding, *args):
        pageContent = h.parse_htmlstring(self.getWikiTaggedContent(), fragment=True)[0]
        for node in pageContent.getiterator():
            if node.tag == 'wiki':
                node.replace(h.a(node.text).action(lambda title=unicode(node.text): binding.becomes(Page(title))))
                
        return (h.p(h.a('Edit *', 
                        self.title,
                        '* page').action(lambda: self.edit(binding))),
                h.hr,
                pageContent)
    ...
    ...
    class PageEditor(object):
        def __init__(self, page):
            self.page = page
            self.content = Var()
    
    
    @presentation.render_for(PageEditor)
    def render(self, h, binding, *args):
        page = PageData.get_by(pagename=self.page.title)
        if page is None:
            page = PageData(pagename=self.page.title, pagecontent='')
    
        return h.form(h.textarea(page.pagecontent, rows='10', cols='60').action(self.content),
                    h.br,
                    h.input(type='submit', value='Save').action(lambda: binding.answer(self.content())),
                    ' ',
                    h.input(type='submit', value='Cancel').action(binding.answer))
    ...

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
`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 ``mywiki.py`` and replace ``app = ...`` content by the following:

.. code-block:: python

    class Wiki(object):
        pass
    
    
    @presentation.render_for(Wiki)
    def render(self, h, binding, *args):
        page = component.ComponentBinding(Page(u'FrontPage'))
        return (h.div( page ),
                h.hr,
                'View the ',
                h.a('complete list of pages').action(lambda : binding.becomes(self,
                                                                              model = 'all')),)
    
    
    @presentation.render_for(Wiki, model = 'all')
    def render(self, h, binding, *args):
        return h.div(h.ul([h.li(h.a(page.pagename).action(lambda title=page.pagename: binding.becomes(Page(title),
                                                          model = 'all'))) for page in PageData.query.order_by(PageData.pagename)]),
                    h.hr,
                    h.a('Back').action(lambda : binding.becomes(self)))
    
    app = Wiki

Here, the navigation is done between the wiki and the page, 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 ``mywiki.py`` and replace the previously inserted code by:

.. code-block:: python
    
    class Wiki(object):
        def __init__(self):
            self.current_page = component.ComponentBinding(Page(u'FrontPage'))
    
    
    @presentation.render_for(Wiki)
    def render(self, h, binding, *args):
        return (h.div( self.current_page ),
                h.hr,
                'View the ',
                h.a('complete list of pages').action(lambda : binding.becomes(self, model = 'all')),)
    
    
    @presentation.render_for(Wiki, model = 'all')
    def render(self, h, binding, *args):
        return h.div(h.ul([h.li(h.a(page.pagename).action(lambda title=page.pagename: self.current_page.becomes(Page(title)))) for page in PageData.query.order_by(PageData.pagename)]),
                    h.hr,
                    h.a('Back').action(lambda : binding.becomes(self)))
    
    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.

.. wikiname: WikiTutorial
