Revisionair: Persistence-agnostic version tracking library

I required something akin to the Rubygem ‘papertrail’ in Elixir. However, in large part because of the ‘do you really need a database’ and ‘how to separate your contexts’ discussions that are going on, I did not want to use a package that was tightly coupled to Ecto or any other single thing (The two existing versioning packages, paper_trail and whatwasit both are).


So, enter Revisionair. At its core it is just a simple behaviour together with a public easy-to-use wrapper, the result of an evening of coding.
An implementation of the Revisionair.Storage behaviour can easily be created for any kind of persistence layer, including Ecto, ETS, Mnesia, plain old GenServer/Agent type of stores, flat file reading/writing, or wrapping remote API communications. (Or at least I hope so, because that was the idea).

The behaviour thus only explicitly states what it needs from ‘a data storage provider’. A storage provider can be configured application-wide, or explicitly stated in the options parameter of each function call.

As for the public API, you can store a new revision for a structure, read all prior-stored revisions for a certain structure, read the most recent revision for a certain structure, and finally clean everything again. It’s simple, but this is all that I need for my application, so KISS.

To keep it as agnostic as possible, it also does not (necessarily) assume anything from the structures you pass in. That is, by default it assumes that the maps you pass it are structs (maps containing a __struct__ field) who can be uniquely identified by an id field, but this is also completely configurable when calling the functions of the module.

So, version 0.9 is now released! it is simple, but then again it now is 01:30, so I might also miss some crucial details. The perils of midnight programming :sunglasses:

Any and all questions and feedback is greatly appreciated.


Is this inspired on Datomic? I’m eager to give a try! :slight_smile:

Revisionair is not based on Datomic (I had never heard about it before, thank you from bringing Datomic to my attention). Revisionair is a lot simpler; the idea is that it is used alongside (and not in place of) your primary business logic, to ensure that e. g. the staff of your application can roll back bad edits to openly editable resources. This means that relational querying is something that is (by design) outside of Revisionair’s scope.

1 Like

Ah, you use an agent!
Updated an hour ago! I love that. :wink:

Yes, I did, exactly because I never used an Agent to do anything before and I wanted to find out how I felt about using them.

Using an Agent for this example seems okay as it only keeps track of state and the code is relatively simple so people can see what is going on. The reason this implementation exists is so there is a reference implementation that can be used in the (doc)tests, as well as to show people how another storage adapter could be written.

Just poking. :slight_smile:

1 Like

The first (1.0) version of RevisionairEcto was just released!

This was the main adapter I require myself for my current application, so I wrote it as first external adapter. Now Revisionair is useful for any application that already uses Ecto :-).

Of course, just with Revisionair itself, everything is configurable both in your config.exs as well as in the options passed to the individual function calls. You can alter what Ecto repository and which revisions table is used. Also, using either numerical IDs and UUIDs is possible.

One other thing I am considering for RevisionairEcto but which is slightly lower on my TODO-list, because it would need a little extra work, is to also create a variant built on top of Ecto.Multi, but that is zukunftsmusik right now

I’d love it if you’d give it a whirl and check it out!


I really liked the project, a shame I didn’t found it before. :confused:

I implemented something like it myself for a production app here with ElasticSearch as my data layer, but your solution is so much more flexible and decent than mine!

Maybe, when I get time to refactor my code, I will make a Revisionair.Storage.ElasticSearch for it. :slight_smile:

1 Like

That would be wonderful! :smiley:

Glad you like it :slight_smile:

Just to let you know, with thanks to @tfwright, version 1.1.0 of RevisionairEcto is now released, which works with Ecto 3 :grin:.