A Quick Tour

This tour takes just a few minutes to cover the full cycle of application persistence - from application data object to file storage and back to the application.

Object Declaration

The first step is to declare a collection of values that will be stored and then recovered at some later point. A simple example appears below:

import uuid
import ansar as ar

class ReceivedJob(ar.Message):
    def __init__(self, unique_id=None, title='watchdog', priority=10, service='noop', body=b''):
        ar.Message.__init__(self)
        self.unique_id = unique_id or uuid.uuid4()
        self.title = title
        self.priority = priority
        self.service = service
        self.body = body

ar.bind(ReceivedJob)

The ReceivedJob class inherits from the Message base class, all class members are given default values using named arguments and lastly, the class is registered using the bind function.

After default creation - j = ReceivedJob() - all members of j contain valid data. The library relies on this behaviour during recovery of data and also at registration time, to determine the data types of individual members.

Note

Classes can be declared with much more than a few int and str members. Refer to More About Types for an introduction into the deeper topic of types.

Write An Object To A File

Writing an object into file storage is most conveniently carried out using the File class:

f = ar.File('job', ReceivedJob)
j = ReceivedJob()

f.store(j)

The call to store() creates or overwrites the job.json file in the current folder. The contents of the file look like this:

{
    "value": {
        "body": "",
        "priority": 10,
        "service": "noop",
        "title": "watchdog",
        "unique_id": "8eab5e5d-6b74-49ad-994f-d436dc4cbf39"
    }
}

The file contains an instance of a JSON object. The values for the ReceivedJob appear as the member value.

Note

The ReceivedJob class being passed to the File object is an example of a type expression. Refer to Type Expressions for the full scope of what that parameter can be.

XML is supported as an alternative encoding. The major motivation for providing this alternative is for those projects with significant commitment to XML in their literature and tooling. There may also be relevant external requirements. Adopting XML for storing and recovering of jobs requires a single additional parameter:

f = ar.File('job', ReceivedJob, encoding=ar.CodecXml)
j = ReceivedJob()

f.store(j)

The contents of the new file look like:

<?xml version="1.0" ?>
<message>
  <message name="value">
    <string name="unique_id">1d719661-af98-47be-9af0-a550b85770fe</string>
    <string name="title">watchdog</string>
    <integer name="priority" value="10"/>
    <string name="service">noop</string>
    <string name="body"/>
  </message>
</message>

Use of the XML encoding comes at the cost of increased consumption of resources. XML can consume 2-4 times as much file space as JSON and the increase in consumption of CPU cycles is typically worse. For these reasons the JSON encoding is defined as the default.

Warning

The XML encoding does not deliver the same capabilities as the JSON encoding, specifically in the area of representation of non-printing values within byte values. Look here for details.

Note

By default the store() method - and its sibling recover() - auto-append an extension to the supplied file name. This behaviour is consistent throughout the library and especially significant when dealing with collections of files inside folders. The behaviour can be disabled using decorate_names=False which passes complete responsibility for file names back to the caller.

Reading An Object From A File

Reading an object from file storage is also carried out using the File class. In fact, we can re-use the same instance from the previous sample:

j, _ = f.recover()

This results in assignment of a fully formed instance of the ReceivedJob class, to the j variable. Details like the filename and expected object type were retained in the f variable and re-applied here.

The recover() method specifically returns a 2-tuple. The first element is the recovered instance and the second element (i.e. the underscore) is a version tag. Use of the underscore above obviously discards whatever that version information might have been. This is fine in prototyping code and is also fine in the first version of any software - everything initially defaults to a “no-op” or “nothing to see here”.

Note

Support for version handling is hard-wired into the library. An entire section is devoted to the proper use of the version information (Versions, Upgrading And Migration) returned by the recover() method. For the moment it is enough to know that nothing in the above code fragments is “wrong” or destined to be discarded when the “proper” code is written.

A Few Details

The operational behaviour of the File class can be influenced by passing additional named parameters. The full set of parameters are:

  • name

  • te

  • encoding

  • create_default

  • pretty_format

  • decorate_names

The first two are required and often all that is needed - the remainder have default values satisfying most expectations. The create_default parameter affects the behaviour of the recover() method, where a named file does not exist. If set to True the method will return a default instance of the expected type, rather than raising an exception. By default, file contents are “pretty-printed” for readability and to assist direct editing. Efficiency can be improved by setting this parameter to False. Lastly, setting the decorate_names parameter to False disables the auto-append of an encoding-dependent file extension, e.g. .xml.

Summary

That is a complete introduction into how the library should be used to implement application persistence. The signficant steps were:

  • registration,

  • storing

  • and recovering.

From a developer’s point of view this represents a low intellectual overhead. It also quietly delivers:

  • type sophistication,

  • reading of fully-formed application types

  • and production-ready version support.