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:
- Change -
c
- Append -
a
- 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:
-
--filter
to filter content of xml. This is usually applied to perform diff on a subset of input xml.
Defaults to "*"
.
-
--parser
to parse input xml. Defaults to :ox
.
-
--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:
--filter
: -f
--parser
: -p
--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