пятница, 22 июля 2016 г.

ObjectTravel. A tool to traverse object references

ObjectTravel is a tool to deeply traverse "native" references of objects through instance variables and "array contents".

     Metacello new
         baseline: 'ObjectTravel';
         repository: 'github://dionisiydk/ObjectTravel';
         load.

Usage is quite simple: just create traveler instance on your object and send #referencesDo:

      traveler := ObjectTraveler on: (1@2 corner: 3@4).
      traveler referencesDo: [:eachRef | eachRef logCr].

It will print on transcript:

      (1@2)
      (3@4)
      1
      2
      3
      4

There is convenience method to collect all references:

      traveler collectReferences. "==> an OrderedCollection((1@2) (3@4) 1 2 3 4))

Traveler enumerates each reference in breadth-first direction and visit every reference only once:

      point := 1@2.
      traveler := ObjectTraveler on: {point . #middle. point}.
      traveler collectReferences "==> an OrderedCollection((1@2) #middle (1@2) 1 2)"

"point" is referenced two times. But traveler will go deeply only once. So circles are supported:

      array := Array new: 2.
      array at: 1 put: 1@3.
      array at: 2 put: array.
      traveler := ObjectTraveler on: array.
      traveler collectReferences asArray = { 1@3. array. 1. 3 } "==> true"

You could control traveler to skip particular objects:

      rect := 1@2 corner: 3@4.
      traveler := ObjectTraveler on: rect.
      traveler skip: rect origin.
      traveler collectReferences "==> an OrderedCollection((1@2) (3@4) 3 4)"

(it can be done during enumeration too).

Also you could apply predicate for objects which you want traverse:

      rect :=
      traveler := ObjectTraveler on: {1@2 corner: 3@4. 5@6}. 
      traveler traverseOnly: [:each | each isKindOf: Point]. 
      traveler collectReferences "==> an OrderedCollection((1@2) corner: (3@4) (5@6) 5 6)"

There are another convenient methods:

      traveler countReferences

It will count all references in object graph.

      traveler copyObject

It will make deep copy of original object which is similar to #veryDeepCopy: cyclic references are supported. 

Also traveler could find all paths to given object in object graph:

      root :=  {#one -> #target. {#emptyArray}. {#two -> #target}}.
      traveler := ObjectTraveler on: root.
      traveler findAllPathsTo: #target "==>
           {#one -> #target. root}
            {#two -> #target. {#two -> #target}. root} "

During enumeration traveler could replace visited references:

      rect := 1@2 corner: 3@4.
      traveler := ObjectTraveler on: rect.
      traveler referencesDo: [:each | 
           each isInteger ifTrue: [traveler replaceCurrentReferenceWith: each + 1]].
      rect "==> 2@3 corner: 4@5"

It would be fun project to connect ObjectTraveler to XStreams to make any object "streamable":

      (1@2 corner: 3@4) traveling reading count: [:each | each isInteger].
      (1@2 corner: 3@4) traveling reading get; get; read: 2; rest.
      (1@2 corner: 3@4) traveling writing write: 100@500

Update: project was moved to github

Комментариев нет:

Отправить комментарий