More Fun with Flip
Providing Undo/Redo Feature
Flip provides an undo/redo history system.
void run () |
{ |
Document document (Model::use (), 123456789ULL, 'acme', 'gui '); |
History history (document); (1) |
Root & root = document.root <Root> (); |
root.happiness_level = 100.0; |
document.set_label ("Change"); (2) |
auto tx = document.commit (); (3) |
history.push (tx); (4) |
history.undo ().execute (); (5) |
history.redo ().execute (); (6) |
} |
- Set up a history for document
- Set a label for the forming transaction
commit
returns a transaction, which has the label as a metadata- push the transaction to the history system
- undo the transaction
- redo the transaction
The undo/redo/history topic is studied in more details in the book Flip History Strategy Guide.
Manipulating Transactions
Transactions represent exactly the difference between to state of the document. Flip flexibility allows to store them, and replay them in any direction.
void run () |
{ |
Document document (Model::use (), 123456789ULL, 'acme', 'gui '); |
Root & root = document.root <Root> (); |
assert (root.tempo == 0.0); (1) |
root.tempo = 100.0; |
auto tx = document.commit (); |
document.execute_backward (tx); (2) |
document.execute_forward (tx); (3) |
} |
- All flip values are constructed with a zero value or an empty state
- play the transaction backward, happiness goes back to 0.0. Note that the transaction is applied, but the document is not commited
- play the transaction backward, happiness goes back to 100.0. Note that the transaction is applied, but the document is not commited. If it was commited, it would produce an empty transaction.
Since transactions can be copied and are just data without external coupling, they can be used for many usual kind of problem solving.
Reading/Writing a Document
Flip supports two formats :
- A binary format with optimal performance and secure enough for production use,
- A markup language format, human readable and editable, for debugging purposes
Those two backend formats can be read or written in an infinite number of media using a traditional Consumer/Producer architecture.
Writing a Document
void write () |
{ |
BackEndIR backend = document.write (); (1) |
std::vector <uint8_t> data; |
DataConsumerMemory consumer (data); (2) |
backend.write <BackEndBinary> (consumer); (3) |
} |
- Write the document into an intermediate representation format. This format is also used to do document revision conversion
- Set up a data consumer which will write in memory
- Write to the consumer using the binary format
See BackEndBinary or BackEndMl reference documentation for additional examples.
Reading a Document
void read () |
{ |
std::vector <uint8_t> data; (1) |
DataProviderMemory provider (data); (2) |
BackEndIR backend; |
backend.register_backend <BackEndBinary> (); (3) |
backend.register_backend <BackEndMl> (); |
backend.read (provider); (4) |
} |
- Here we suppose that data is already filled with document data
- Attach a provider to the data to read
- Register the two formats that the backend can read. The backends implementation are able to tell if the incoming data is in the right format
- Read the document
See BackEndBinary or BackEndMl reference documentation for additional examples.
The next chapter, Model Versioning will guide you through converting a document from one revision of the model to another one.