Flip Programming Guide

 

Writing a Converter

This chapter will describe how to convert a document when the model changes. It will show all the classes that are used to do it.

Overview

The Flip framework integrates a powerful document conversion system. Conversion occurs when the model changes, whatever the change. The model is associated a new format version. Small changes are rather easy to convert with Flip, and extreme model changes are equally easy as well to manage.

The format versions can be organised into a graph. Generally the graph is only a simple chain, but it can be more complex, for example to handle A/B testing on the model itself.

The code to convert to one format version to another is organized in the following way :

To illustrate the conversion process, we are going to use three models, respectively format version 1 , format version 2 and format version 3

ListingModel format version 1

class Root
:  public ohm::flip::Object
{
private:
   ohm::flip::Int64     _nbr_cats;
};

ListingModel format version 2

class Root
:  public ohm::flip::Object
{
private:
   ohm::flip::Int64     _nbr_cats;
   ohm::flip::Int64     _nbr_dogs;
};

ListingModel format version 3

class Root
:  public ohm::flip::Object
{
private:
   ohm::flip::Int64     _nbr_cats;
   ohm::flip::Int64     _nbr_dogs;
   ohm::flip::Int64     _nbr_fishes;
};

VersionTree

The first stage consists of making a function that will handle the conversion tree. The conversion is going to be chained : each time a version converter will try to upgrade the document to a more up-to-date version, until the current model format version is reached.

The function itself must match the ConverterProc prototype as defined in the DocumentServer class.

In our simple example, the function would look like below. It has been splitted into two functions convert and do_convert for clarity.

int   convert (flip::DataStreamOutput & dst, flip::DataStreamInput & src, bool force_flag)
{
   int err = 0;
   flip::ConvDocument document;
   
   if (err == 0)
   {
      err = document.read (src);
      assert (err == 0);
   }
   if (err == 0)
   {
      err = do_convert (document);
      assert (err == 0);
   }
   if (err == 0)
   {
      err = document.write (dst);
      assert (err == 0);
   }
   return err;
}
int   do_convert (flip::ConvDocument & document)
{
   int err = 0;
   
   bool converted_flag = false;
   
   try 
   {
      if ((err == 0) && (document._format_version == "1"))
      {
         Converter1_2 converter;
         
         err = converter.process (document);
         
         converted_flag = true;
      }
      if ((err == 0) && (document._format_version == "2"))
      {
         Converter2_3 converter;
         
         err = converter.process (document);
         
         converted_flag = true;
      }
      if (converted_flag)
      {
         document._rev_id += 1;
      }
   }
   catch (...) 
   {
      err = flip::Err_EXCEPTION;
   }
   
   return err;
}

In the code above, the convert prepare the work by loading the document in the converter object then call the version tree handled by do_convert and finally write the document back.

The document itself is loaded in a simple format : it does not need the current model to be declared to be able to load. Classes, attributes are all referenced as simple character strings that can be directly manipulated to ease the conversion process.

In the code above, the do_convert do the actual graph based conversion. If the document is in format version 1 , it will first use a converter object that will convert the document to version 2 . Then it will use another converter object that will convert the document to version 3 .

Finally, if a conversion occured, the document revision id is incremented by one. This is important for the Flip framework to know that this document has actually changed.

Converter

A canonical converter will convert from one specific format version to another specific format version. The Flip framework offers a tool class, ConvTools that assists and ease the actual conversion process.

In the following listings, we are going to expose the converter from format version 1 to format version 2 .

The first function is actually always the same : it just makes some checks to ensure that every things is in a right state, and put back the document in a correct state in the end (ensuring that Flip references do not collide).

int Converter1_2::process (flip::ConvDocument & document)
{
   int err = 0;
   
   if (err == 0)
   {
      if (document._format_version != "1")
      {
         err = flip::Err_DOC_BAD_VERSION;
      }
   }
   
   flip::ConvObject & root = document._root;
   if (err == 0)
   {
      err = ConvTools::check_recursive (root);
   }
   
   if (err == 0)
   {
      err = convert_root (root);
   }
   if (err == 0)
   {
      document.remap_refs ();
   }
   if (err == 0)
   {
      err = ConvTools::check_recursive (root);
   }
   
   if (err == 0)
   {
      document._format_version = "2";
   }
   return err;
}

The second function is the actual conversion process

int Converter68679_69859::convert_root (flip::ConvObject & root)
{
   flip::ConvInt64 & nbr_dogs = ConvTools::add_attribute <
      flip::ConvInt64
   > (root, "_nbr_dogs");
   
   nbr_dogs._val = 4;
}

All the conversion tools are exposed in details in the