changes package - github.com/dotchain/dot/changes - Go Packages (original) (raw)

Package changes implements the core mutation types for OT.

The three basic types are Replace, Splice and Move. Replace replaces a value altogether while Splice replaces a sub-sequence in an array-like object and Move shuffles a sub-sequence.

Both Slice and Move work on strings as well. The actual type for Slice and Move is represented by the Collection interface (while Replace uses a much more lax Value interface)

Seehttps://godoc.org/github.com/dotchain/dot/changes/types#S16 for an implementation of an OT-compatible string type.

Composition

ChangeSet allows a set of mutations to be grouped together.

PathChange allows a mutation to refer to a "path". For example, a field in a "struct type" can be thought of as having the path of "field name". An element of a collection can be thought of as having the path of the index in that array.

The type of the elements in the path is not specified but it is assumed that they are comparable for equality. Collections are required to use the index of type int for the path elements.

Custom Changes

Custom change types can be defined. They should implement the Custom interface. Seehttps://godoc.org/github.com/dotchain/dot/changes/x/rt#Run for an example custom change type.

The general asssumption underlying OT is that the Merge method produces convergence:

if: c1, c2 = changes on top of "initial" and: c1x, c2x := c1.Merge(c2) then: initial + c1 + c1x == initial + c2 + c2x

Notes

Replace and Splice change both expect the Before and After fields to be non-nil Value implementations. Replace can use changes.Nil to represent empty values for the case where a value is being deleted or created. Slices must make sure that the Before and After use the "empty" representations of the respective types.

Slices also should generally make sure that the Before and After types are compatible -- i.e. each should be able to be spliced within the other.

Value Interface

Any custom Value implementation should implement the Value interface. See https://godoc.org/github.com/dotchain/dot/changes/typesfor a set of custom value types such as string, arrays and counters.

See https://godoc.org/github.com/dotchain/dot/x/rt for a custom type that has a specific custom change associated with it.

It is common to have a value type (say *Node) that is meant as an atomic value. In that case, one can use the Atomic{} type to hold such values.

This section is empty.

Nil represents an empty value. It can be used with Replace or Splice to indicate that Before or After is empty. The only operation that can be applied is a Replace. Count and Slice cannot be called on it.

This section is empty.

type Atomic struct { Value interface{} }

Atomic is an atomic Value. It can wrap any particular value and can be used in the Before, After fields of Replace or Splice.

func (a Atomic) Apply(ctx Context, c Change) Value

Apply only accepts one type of change: one that Replace's the value.

type Change interface {

Merge(other [Change](#Change)) (otherx, cx [Change](#Change))


Revert() [Change](#Change)

}

Change represents an OT-compatible mutation

The methods provided here are the core methods. Custom changes should implement the Custom interface in addition to this. Note that it is legal for a change to be nil (meaning the value isnt change at all)

func Merge(c1, c2 Change) (c1x, c2x Change)

Merge is effectively c1.Merge(c2) except that c1 can be nil.

As with individual merge implementations, applying c1+c1x is effectively the same as applying c2+c2x.

func Simplify(c Change) Change

Simplify converts a change to a simpler form if possible

ChangeSet represents a collection of changes. It implements the Change interface thereby allowing merging groups of changes against each other.

func (c ChangeSet) ApplyTo(ctx Context, v Value) Value

ApplyTo simply walks through the individual changes and applies them to the value.

func (c ChangeSet) Merge(other Change) (otherx, cx Change)

Merge implements Change.Merge.

func (c ChangeSet) ReverseMerge(other Change) (otherx, cx Change)

ReverseMerge is like merge except with receiver and args inverted

func (c ChangeSet) Revert() Change

Revert implements Change.Revert.

func (c ChangeSet) Simplify() Change

Simplify converts an empty or single element change-set into a simpler version

type Collection interface {

[Value](#Value)


ApplyCollection(ctx [Context](#Context), c [Change](#Change)) [Collection](#Collection)


Slice(offset, count [int](/builtin#int)) [Collection](#Collection)


Count() [int](/builtin#int)

}

Collection represents an immutable array-like value

type Context interface { Value(key interface{}) interface{} }

Context defines the context in which a change is being applied. This is useful to capture data such as the "current user" or "virtual time" etc. For true convergence, the context itself should be derived from the change -- say via Meta.

Note that this interface is a subset of the standard golang "context.Context"

func MetaValue(ctx Context) (v interface{}, p Context)

MetaValue fetches the meta value and the previous context associated with the current change context.

type Custom interface { Change

ReverseMerge(c [Change](#Change)) ([Change](#Change), [Change](#Change))


ApplyTo(ctx [Context](#Context), v [Value](#Value)) [Value](#Value)

}

Custom is the interface that custom change types should implement. This allows the "known" types to interact with custom types.

Custom changes might also need to implement the refs.PathMerger interface (see https://godoc.org/github.com/dotchain/dot/refs)

type Meta struct { Data interface{} Change }

Meta wraps a change with some metadata that is maintained as the change is merged. This is useful for carrying contexts with changes. One example is the current user making the change

func (m Meta) ApplyTo(ctx Context, v Value) Value

ApplyTo implements Custom.ApplyTo

func (m Meta) Merge(other Change) (otherx, cx Change)

Merge merges the change preserving the meta data

func (m Meta) ReverseMerge(c Change) (Change, Change)

ReverseMerge implements Custom.ReverseMerge

func (m Meta) Revert() Change

Revert reverts the change preserving the meta data

type Move struct { Offset, Count, Distance int }

Move represents a shuffling of some elements (specified by Offset and Count) over to a different spot (specified by Distance, which can be negative to indicate a move over to the left).

func (m *Move) Change() Change

Change returns either nil or the underlying Move as a change.

MapIndex maps a particular index to the new location of the index after the move

func (m Move) Merge(other Change) (otherx, cx Change)

Merge implements the Change.Merge method

func (m Move) MergeMove(o Move) (ox []Move, mx []Move)

MergeMove merges a move against another Move

func (m Move) MergeReplace(other Replace) (other1 *Replace, m1 *Splice)

MergeReplace merges a move against a Replace. The replace always wins

func (m Move) MergeSplice(o Splice) (Change, Change)

MergeSplice merges a splice with a move.

func (m Move) Normalize() Move

Normalize ensures that distance is always positive

func (m Move) Revert() Change

Revert reverts the move.

type PathChange struct { Path []interface{} Change }

PathChange represents a change at the provided "path" which can consist of strings (for map-like objects) and integers for array-like objects. In particular, each element of the path should be a proper comparable value (so slices and such cannot be part of th path)

func (pc PathChange) ApplyTo(ctx Context, v Value) Value

ApplyTo is not relevant to PathChange. It only works when the path is empty. In all other cases, it panics.

func (pc PathChange) Merge(o Change) (Change, Change)

Merge implements Change.Merge

func (pc PathChange) ReverseMerge(o Change) (Change, Change)

ReverseMerge implements but with receiver and arg interchanged.

func (pc PathChange) Revert() Change

Revert implements Change.Revert

func (pc PathChange) Simplify() Change

Simplify returns a simpler version of this change removing empty paths or coalescing paths as needed

type Replace struct { Before, After Value }

Replace represents create, delete and update of a value based on whether Before is Nil, After is Nil and both are non-Nil respectively.

func (s *Replace) Change() Change

Change returns either nil or a Change

func (s Replace) IsCreate() bool

IsCreate identifies if the change is a create

func (s Replace) IsDelete() bool

IsDelete identifies if the change is a delete

func (s Replace) Merge(other Change) (otherx, cx Change)

Merge implements the Change.Merge method

func (s Replace) MergeMove(other Move) (other1 *Move, s1 *Replace)

MergeMove merges against a Move change. The replace wins

func (s Replace) MergeReplace(other Replace) (other1, s1 *Replace)

MergeReplace merges against another Replace change. The last writer wins here with the receiver assumed to be the earlier change

func (s Replace) MergeSplice(other Splice) (other1 *Splice, s1 *Replace)

MergeSplice merges against a Splice change. The replace wins

func (s Replace) Revert() Change

Revert inverts the effect of the replace

type Splice struct { Offset int Before, After Collection }

Splice represents an array edit change. A set of elements from the specified offset are removed and replaced with a new set of elements.

func (s *Splice) Change() Change

Change returns nil or the underlying Splice

MapIndex maps an index to the new location of the index after the splice. It also returns whether the item at that index has been modified by the splice change.

func (s Splice) Merge(other Change) (otherx, cx Change)

Merge implements the Change.Merge method

func (s Splice) MergeMove(o Move) (ox, sx Change)

MergeMove merges a splice against a move

func (s Splice) MergeReplace(other Replace) (other1 *Replace, s1 *Splice)

MergeReplace merges a move against a Replace. The replace always wins

func (s Splice) MergeSplice(other Splice) (other1, s1 *Splice)

MergeSplice merges a splice against another Splice.

func (s Splice) Revert() Change

Revert inverts the effect of the splice.

type Value interface {

Apply(ctx [Context](#Context), c [Change](#Change)) [Value](#Value)

}

Value represents an immutable JSON object that can apply changes. Array like values should also implement Collection