\chapter{Writing file handler classes}
\label{section: files2}

The chapter before we have learnt about file handlers and how to use them,
now we are going to learn how to write our own handler classes, what by the
way will help to solidify the concepts seen before.

The explanation will be driven by an example: we are going to write a task
tracker. The code can be found in the directory {\tt examples/chapter5}.

\section{Functional scope}

Lets start by defining the functional scope of our task tracker. It is
going to be very simple, it will be a collection of tasks where every
task will have three fields:

\begin{itemize}
  \item {\em title}, a short sentence describing the task.

  \item {\em description}, a longer description detailing the task.

  \item {\em state}, it may be {\em open} (if the task has not been
    finished yet), or {\em closed} (if the task has been finished).
\end{itemize}

The task tracker will provide an API to manipulate the collection of
tasks: create a new task, see either the open or the closed tasks,
and close a task.


\section{The file format}

Now that we know what we want to do, we have to decide where and how
the information will be stored.

We will keep the tasks in a single text file, with a format somewhat
similar to the one used by the standards {\em vCard} and {\em iCal},
for example:

\begin{code}
    title:Re-write the chapter about writing handler classes.
    description:A new chapter that explains how to write file handler
     classes must be written, it should go inmediately after the chapter
     that introduces file handlers.
    state:closed

    title:Finish the chapter about folder handlers.
    description:The chapter about folder handlers needs much more work.
     For example the skeleton of folder handlers must be explained.
    state:open
\end{code}

Each task is separated from the next one by a blank line. Every field
starts by the field name follewed by the field value, both separated by
a colon. If a field value is very long it can be written in multiple lines,
where the second and next lines start by an space.

This very same file can be found in the examples directory with the name
{\tt itools.tt}. Using our own filename extension ({\tt tt}) will prove
useful, as we will see later.


\section{De-serialization}

The first draft of our handler class will be able to load (de-serialize)
the resource into a data structure on memory.

\begin{code}
    from itools.handlers.Text import Text


    class Task(object):
        def __init__(self, title, description, state='open'):
            self.title = title
            self.description = description
            self.state = state


    class TaskTracker(Text):

        def _load(self, resource=None):
            # Load the resource as a unicode string
            Text._load(self, resource)
            # Split the raw data in lines.
            lines = self._data.splitlines()
            # Append None to signal the end of the data.
            lines.append(None)
            # Free the un-needed data structure, 'self._data'
            del self._data

            # Initialize the internal data structure
            self.tasks = []
            # Parse and load the tasks
            fields = {}
            for line in lines:
                if line is None or line.strip() == '':
                    if fields:
                        task = Task(fields['title'],
                                    fields['description'],
                                    fields['state'])
                        self.tasks.append(task)
                        fields = {}
                else:
                    if line.startswith(' '):
                        fields[field_name] += line
                    else:
                        field_name, field_value = line.split(':', 1)
                        fields[field_name] = field_value
\end{code}

First, our handler class {\tt TaskTracker} inherits from the handler class
{\tt Text}, because it is intended to manage a text file.

The method {\tt \_load} is the one to implement, it is responsible to
de-serialize the resource and build a data structure on memory that
represents it.

The first thing it does is to call the parent's {\tt \_load} method, which
will read the resource's data and get a unicode string from it, which will
be stored in the {\tt self.\_data} variable. This is an intermediary
representation, which will be discarded.

The rest of the method process the raw data and builds a list of instances
of the class {\tt Task} into the variable {\tt self.tasks}.

Lets try it:

\begin{code}
    >>> from pprint import pprint
    >>> import textwrap
    >>> from itools.resources import get_resource
    >>> from TaskTracker import TaskTracker
    >>>
    >>> resource = get_resource('itools.tt')
    >>> task_tracker = TaskTracker(resource)
    >>>
    >>> pprint(task_tracker.tasks)
    [<TaskTracker.Task object at 0xb7aebd4c>,
     <TaskTracker.Task object at 0xb7aebe6c>]
    >>>
    >>> task = task_tracker.tasks[0]
    >>> print task.title
    Re-write the chapter about writing handler classes.
    >>> print textwrap.fill(task.description)
    A new chapter that explains how to write file handler classes must be
    written, it should go inmediately after the chapter that introduces
    file handlers.
    >>> print task.state
    closed
\end{code}


\section{Serialization}

Now we are going to write the other half, the serialization process,
just adding the {\tt to\_unicode} method to the {\tt TaskTracker} class:

\begin{code}
        def to_unicode(self, encoding='UTF-8'):
            lines = []
            for task in self.tasks:
                lines.append(u'title:%s' % task.title)
                description = u'description:%s' % task.description
                description = textwrap.wrap(description)
                lines.append(description[0])
                for line in description[1:]:
                    lines.append(u' %s' % line)
                lines.append(u'state:%s' % task.state)
                lines.append('')
            return u'\n'.join(lines)
\end{code}


Note that we implement the {\tt to\_unicode} method instead of {\tt to\_str}
because this is a text handler. The {\tt encoding} parameter is not used in
our example because the file format does not specifiy the encoding within its
content, but it must be declared anyway.

Lets try our new code:

\begin{code}
    >>> print task_tracker.to_str()
    title:Re-write the chapter about writing handler classes.
    description:A new chapter that explains how to write file handler
     classes must be written, it should go inmediately after the chapter
     that introduces file handlers.
    state:closed

    title:Finish the chapter about folder handlers.
    description:The chapter about folder handlers needs much more work.
     For example the skeleton of folder handlers must be explained.
    state:open
\end{code}

We could have tried {\tt to\_unicode} to get a similar output, except
that then the result would be a unicode string instead of a byte string.


\section{The API}

Now it is time to write the API to manage the tasks, here is an excerpt:

\begin{code}
    def add_task(self, title, description):
        task = Task(title, description)
        self.tasks.append(task)


    def show_open_tasks(self):
        for id, task in enumerate(self.tasks):
            if task.state == 'open':
                print 'Task #%d: %s' % (id, task.title)
                print
                print textwrap.fill(task.description)
                print
                print


    def close_task(self, id):
        task = self.tasks[id]
        task.state = u'closed'
\end{code}


The first method, {\tt add\_task} creates a new task, whose state will be
{\em open}. The method {\tt show\_open\_tasks} prints the list of open tasks
with a human readable format (we could write a method that returns HTML
instead, to use our task tracker on the web). Finally, the method
{\tt close\_task} closes the task.


\section{The skeleton}

The skeleton for our task tracker should be empty, but to illustrate this
feature we are going to implement an skeleton with one dummy task:

\begin{code}
        def get_skeleton(self):
            return 'title:Read the docs!\n' \
                   'description:Read the itools documentation, it is\n' \
                   ' so gooood.\n' \
                   'state:open\n'
\end{code}


To exercise the whole thing we are going to create a new task tracker, we
will close the first task, add a new one, and look what we have.

\begin{code}
    >>> from TaskTracker import TaskTracker
    >>>
    >>> task_tracker = TaskTracker()
    >>> task_tracker.show_open_tasks()
    Task #0: Read the docs!

    Read the itools documentation, it is so gooood.


    >>> task_tracker.close_task(0)
    >>> task_tracker.add_task('Join itools!',
    ...   'Subscribe to the itools mailing list.')
    >>> task_tracker.show_open_tasks()
    Task #1: Join itools!

    Subscribe to the itools mailing list.
\end{code}

Now, don't forget to save the task tracker in the file system, so you can
come back to it later:

\begin{code}
    >>> from itools.handlers import get_handler
    >>>
    >>> tmp = get_handler('/tmp')
    >>> tmp.set_handler('most_important_things_in_live.tt', task_tracker)
\end{code}


\section{Register}

However:

\begin{code}
    >>> task_tracker = tmp.get_handler('most_important_things_in_live.tt')
    >>> print task_tracker
    <itools.handlers.File.File object at 0xb7c00f0c>
\end{code}

It would be nice if the code above worked. To achieve it we will associate
the new mimetype {\tt text/x-task-tracker} to the file extension {\tt tt},
we will tell our handler class is able to manage that mimetype with the
variable class {\tt class\_mimetypes}, and we will register our handler
class to its parent:

\begin{code}
    import mimetypes
    mimetypes.add_type('text/x-task-tracker', '.tt')


    class TaskTracker(Text):

        class_mimetypes = ['text/x-task-tracker']

        [...]


    Text.register_handler_class(TaskTracker)
\end{code}

And {\em voilà}:

\begin{code}
    >>> task_tracker = tmp.get_handler('most_important_things_in_live.tt')
    >>> print task_tracker
    <TaskTracker.TaskTracker object at 0xb7af084c>
\end{code}


The full code can be found in {\tt examples/chapter5/TaskTracker.py}.