===========================================
 An Example Application Using Desktopcouch
===========================================

In this document we will outline how to design a brand new application
using Desktopcouch. As an example we use a simple recipe manager.

1  Data Model
2  Views
3  Attachments
4  Bulk Updates
5  Application Annotations
6  Mergeable Lists


Data Model
==========

When designing a new Desktopcouch application it is important to think
about what kind of data you want to store and how you want to
structure it, since this can greatly impact what kind of information
you have available to you, and how efficiently you can query for it.

First of all you will have to think of what kind(s) of data you
will want to store, and define record types for them, or reuse
existing record types if there are any available.[#]_

.. [#] Desktopcouch encourages sharing of data between different
applications, so if there are existing record types for the data you
will be storing, we urge you to reuse and extend them rather than
invent your own. We will document ways to extend existing record types
in the section `Application Annotations`_.

In this case let's assume we want to have a recipe Record type, and
that there isn't one yet. First of all we'll come up with an
identifier for the Record type, which should be a url, let's say:

  http://www.freedesktop.org/wiki/Specifications/desktopcouch/recipe

This need not be a url on freedesktop.org, but it should point to a
page that will specify the format. If you want to cooperate with other
application developers, it is probably a good idea to discuss your
proposed data format on the developers mailing list[#]_, and if a
consensus is reached, propose it to freedesktop.org.

.. [#] http://groups.google.com/group/desktop-couchdb

Since data formats are not static, we also advise that you use a
record_type_version number, which should be increased whenever changes
to the format are made.

Once you have decided on a record type, you are ready to start
building your data objects. For our recipe manager, we would create a
new recipe.py file, containing::

    >>> from desktopcouch.records import Record

    >>> RECIPE_TYPE =\
    ...     'http://www.freedesktop.org/wiki/Specifications/desktopcouch/recipe'
    >>> VERSION = '0.1'

    >>> class Recipe(Record):
    ...     """A desktopcouch recipe object."""
    ...
    ...     def __init__(self, data=None, record_id=None):
    ...         super(Recipe, self).__init__(
    ...             record_id=record_id, data=data, record_type=RECIPE_TYPE,
    ...             record_type_version=VERSION)

This is the minimal amount of work you have to do to get a new record
type. It will allow you to instantiate records of this type, modify
and read the data they contain and save them to couchdb.

    >>> my_recipe = Recipe()
    >>> my_recipe['name'] = 'Very Big Very Sour Apple Pie'
    >>> my_recipe['ingredients'] = []
    >>> my_recipe['ingredients'].append({
    ...     'ingredient': 'apples',
    ...     'quantity': {
    ...         'value': 5,
    ...         'unit': 'kg'}})

As you can see we did not (yet) specify any fields anywhere in the
code, but any Recipe object will just act like a Python dictionary,
and allow you to set values on arbitrary fields. While this works, it
is error-prone and not very developer-friendly, so we can make the
recipe class nicer, by dynamically adding the fields that are in the
schema as properties, so that we can find them easily.

    >>> from desktopcouch.records import Record, RecordField

    >>> class Recipe(Record):
    ...     """A desktopcouch recipe object."""
    ...
    ...     name = RecordField('name')
    ...     ingredients = RecordField('ingredients')
    ...     instructions = RecordField('instructions')
    ...
    ...     def __init__(self, data=None, record_id=None):
    ...         super(Recipe, self).__init__(
    ...             record_id=record_id, data=data, record_type=RECIPE_TYPE,
    ...             record_type_version=VERSION)

after which we can do:

    >>> my_recipe = Recipe()
    >>> my_recipe.name = 'Very Big Very Sour Apple Pie'
    >>> my_recipe.ingredients = []
    >>> my_recipe.ingredients.append({
    ...     'ingredient': 'apples',
    ...     'quantity': {
    ...         'value': 5,
    ...         'unit': 'kg'}})

but you can still use dictionary access as well, which makes some
kinds of dynamic coding easier:

    >>> sorted([key for key in my_recipe])
    ['ingredients', 'name']

There are more specific types of RecordField available if you want to
encourage that the value of one of the fields is of a certain
type.[#]_

.. [#] Your application should probably be tolerant of values that
violate the schema though, since users can bypass desktopcouch and
manipulate data directly in couchdb, and couchdb itself is entirely
schemaless.

    >>> from desktopcouch.records import (
    ...     Record, StringRecordField, ListRecordField)

    >>> class Recipe(Record):
    ...     """A desktopcouch recipe object."""
    ...
    ...     name = StringRecordField('name')
    ...     ingredients = ListRecordField('ingredients')
    ...     instructions = ListRecordField('instructions')
    ...
    ...     def __init__(self, data=None, record_id=None):
    ...         super(Recipe, self).__init__(
    ...             record_id=record_id, data=data, record_type=RECIPE_TYPE,
    ...             record_type_version=VERSION)

    >>> my_recipe = Recipe()
    >>> my_recipe.name = 1
    Traceback (most recent call last):
        ...
    TypeError: Expected string value, got 1.
    >>> my_recipe = Recipe()
    >>> my_recipe.ingredients = 'twelvity'
    Traceback (most recent call last):
        ...
    TypeError: Expected list value, got 'twelvity'.
    >>> my_recipe = Recipe()
    >>> my_recipe.instructions = {}
    Traceback (most recent call last):
        ...
    TypeError: Expected list value, got {}.

Views
=====



Attachments
===========



Bulk Updates
============



Application Annotations
=======================



Mergeable Lists
===============

