Yadtfp

yadtfp is a diff tool that takes two well formed xml documents and generates the differences in an easy to read, concise output. The difference algorithm operates on element values producing a very precise diff possible.

The name is an acronym for "Yet Another Diff Tool For Param". The original motivation of yadtfp was to make it a CLI tool to compare two xml files containing application configuration parameters, which is how it got it's name, we think.

yadtfp parses each input XML into an array of flat hashes, applies filter and compares the two. The result is an array of difference hashes. The result array is then passed to the selected outputter which prints the output to console.

Installation

$ gem install yadtfp

Documentation

The code base documentation is available here.

Parsers

Currently yadtfp supports Ox parser which is the default.

Outputters

Currently yadtfp supports Yadtfp::Outputters::Pretty outputter which is the default.

Pretty Outputter

Pretty outputter generates the output in the following format:

Changes (Replace left value with right value)
---------------------------------------------

Appends (Add values to left)
----------------------------

Deletes (Remove values from left)
---------------------------------

Summary of differences
----------------------
Number of differences: x
Changes 'c': x
Appends 'a': x
Deletes 'd': x

where x is an integer value for each difference.

Note: In "Summary of differences", Changes, Appends and Deletes are included only if the diff contains respective difference, i.e. if a diff does not contain "Deletes" then "Summary of differences" excludes "Deletes", and likewise for "Appends" and "Changes".

Other topics of interest

Path generation

Path to each XML component, e.g. attribute, comment, cdata, text are based on the following:

1. Attribute

Attributes are prefixed with @ sign.

# Input XML
<xml id='foo' />

# Path to attribute `id`
/xml/@id

2. Comment

Comments are denoted by comment().

# Input XML
<xml>
  <!-- Root node -->
</xml>

# Path to comment
/xml/comment()

3. CData

CData are denoted by cdata().

# Input XML
<xml>
  <![CDATA[ Foo ]]>
</xml>

# Path to cdata
/xml/cdata()

4. Text

Texts are denoted by path to the node containing the text.

# Input XML
<xml>Foo</xml>

# Path to text
/xml

Flat hash

Each input xml document is parsed into an array of hashes. This hash's key is the path to the element from the path specified in the filter and it's value is the text or value in the xml. If no filter is specified, the default filter is used.

Example of flat hash data structure, given an input xml:

# Input XML
<xml id='root'>
  <!-- Root node -->
  <child id='child'>Foo</child>
  <![CDATA[ Bar ]]>
</xml>

# Output Array of hashes
[
  { '/xml/@id' => 'root' },
  { '/xml/comment()' => [ 'Root node' ] }, 
  { '/xml/cdata()' => [ 'Bar' ] }, 
  { '/xml/child/@id' => 'child' }, 
  { '/xml/child' => 'Foo' }
]

Difference hash

There are altogether three types of differences:

  1. Change - c
  2. Append - a
  3. Delete - d

Example difference hash for each difference type is as follows:

# Change 
{ type: `c`, path: '', lvalue: '', rvalue: '' }

# Append
{ type: 'a', path: '', lvalue: '', rvalue: '' }

# Delete
{ type: 'd', path: '', lvalue: '', rvalue: '' }

Configuration options

yadtfp supports the following three parameters:

  1. --filter to filter content of xml. This is usually applied to perform diff on a subset of input xml. Defaults to "*".

  2. --parser to parse input xml. Defaults to :ox.

  3. --outputter to output result diff. Defaults to :pretty.

Each filter option has a shorthand to save a little typing! but only in the CLI. The shorthand mapping of each options are:

  1. --filter: -f
  2. --parser: -p
  3. --outputter: -o

Each configuration option is prefixed with double dash -- except for shorthand notations which are prefixed by a single dash -.

Filters

Filters are based on ::Ox::Element#locate()

Examples sourced from documentation for #Ox::Element#locate():

Family/Pete/*       - All children of Pete element
Family/?[1]         - First element in Family element
Family/?[<3]*       - First 3 elements in Family element
Family/?/@age       - Age attribute for each child in Family element
Family/*/@type      - Type attribute value for decendents of Family element
Family/^Comment     - Comment children of Family element

CLI Usage

1. yadtfp two xml strings using default options

$ yadtfp -- "<xml id='root' />" "<xml name='root_node' />"
Changes (Replace left value with right value)
---------------------------------------------

Appends (Add values to left)
----------------------------

1. Path: /xml/@name
   Left: 
   Right: root_node

Deletes (Remove values from left)
---------------------------------

1. Path: /xml/@id
   Left: root
   Right: 


Summary of differences
----------------------
Number of differences: 2
 Appends 'a': 1
 Deletes 'd': 1

The above command can be substituted with:

$ yadtfp --filter "*" --parser "ox" --outputter "pretty" "<xml id='root' />" "<xml name='root_node' />"

or using shorthand options:

$ yadtfp -f "*" -p "ox" -o "pretty" "<xml id='root' />" "<xml name='root_node' />"

2. yadtfp two xml files using default options

Given two xml files file1.xml and file2.xml:

# file1.xml
<?xml version='1.0' encoding='utf-8' ?>
<xml>Foo</xml>

# file2.xml
<?xml version='1.0' encoding='utf-8' ?>
<xml>Bar</xml>

Execute yadtfp diff:

$ yadtfp -- file1.xml file2.xml
Changes (Replace left value with right value)
---------------------------------------------

1. Path: /xml
   Left: Foo
   Right: Bar

Appends (Add values to left)
----------------------------

Deletes (Remove values from left)
---------------------------------


Summary of differences
----------------------
Number of differences: 1
 Changes 'c': 1

The above command can be substituted with:

$ yadtfp --filter "*" -parser "ox" -outputter "pretty" file1.xml file2.xml

or using shorthand options:

$ yadtfp -f "*" -p "ox" -o "pretty" file1.xml file2.xml

Note: File paths may be absolute or relative from the present working directory.

Contributing

Please see CONTRIBUTING.md