NOTE: This readme hasn't been updated since the start of the project, so some parts are probably outdated.
SuperDiff is a utility that helps you diff two complex data structures in Ruby, and gives you helpful output to show you exactly how the two data structures differ.
Let's say we have two arrays. Array A looks like this:
[
"foo",
["bar", ["baz", "quux"]],
"ying",
["blargh", "zing", "fooz", ["raz", ["vermouth", "eee", "ffff"]]]
]
Array B looks like this:
[
"foz",
"bar",
"ying",
["blargh", "gragh", 1, ["raz", ["ralston"]], ["foreal", ["zap"]]]
]
We want to know what the difference is between them, so we say:
differ = SuperDiff::Differ.new
data = differ.diff(a, b)
stdout = StringIO.new
reporter = SuperDiff::Reporter.new(stdout)
reporter.report(data)
Differ#data takes two values and figures out the difference between them, returning a hash of data that represents this difference. If the two values are both arrays or hashes, this means descending into the values recursively and figuring out the difference between elements too. Reporter#report then takes the data generated by the Differ and formats it into a message, sending to the stream we've given. So if we read the StringIO here, we'll get this:
Error: Arrays of same size but with differing elements.
Expected: ["foo", ["bar", ["baz", "quux"]], "ying", ["blargh", "zing", "fooz", ["raz", ["vermouth", "eee", "ffff"]]]]
Got: ["foz", "bar", "ying", ["blargh", "gragh", 1, ["raz", ["ralston"]], ["foreal", ["zap"]]]]
Breakdown:
- *[0]: Differing strings.
- Expected: "foo"
- Got: "foz"
- *[1]: Values of differing type.
- Expected: ["bar", ["baz", "quux"]]
- Got: "bar"
- *[3]: Arrays of same size but with differing elements.
- *[1]: Differing strings.
- Expected: "zing"
- Got: "gragh"
- *[2]: Values of differing type.
- Expected: "fooz"
- Got: 1
- *[3]: Arrays of same size but with differing elements.
- *[1]: Arrays of differing size and elements.
- *[0]: Differing strings.
- Expected: "vermouth"
- Got: "ralston"
- *[1]: Expected to be present, but missing "eee".
- *[2]: Expected to be present, but missing "ffff".
- *[4]: Expected to not be present, but found ["foreal", ["zap"]].
For testing, specifically with RSpec. For instance, say you're doing a simple equality test, but between two complex data structures:
complex_data_structure.should == another_complex_data_structure
Now, RSpec (at least RSpec 2) does give you a difference between the two data
structures, but all it really does is call
complex_data_structure.pretty_inspect
and
another_complex_data_structure.pretty_inspect
and then uses Diff::LCS to take
the unified diff between the two strings (as though they were text files). It's
not a terrible solution, but it's naive -- it doesn't know anything about the
data structures involved, so you have to do a bit of work to read the unified
diff output and then apply it to the data. I think this process could be made
easier.
Sure. Be aware that this is kind of proof-of-concept at the moment, so things will probably change in the future, like the API or even the name of the project. If that's okay with you, feel free to try it out.
Check out the issue tracker. If you have an idea, add an issue and I'll take a look.
Totally! All you'll need to do is install Ruby 1.9, then run bundle install
.
(Neither Ruby 1.9 nor Bundler will be required to use SuperDiff in production
code; 1.9 is only necessary for testing, since hashes are defined in a
guaranteed order, and there are a few development dependencies so I'm merely
using Bundler for convenience.)
If you're contributing an idea or fixing a bug:
- Add tests. I'm using RSpec, so you can run all tests with
rake spec
. - Make a branch, send me a pull request, and I'll take a look at it.
© 2011 Elliot Winkler. The code here is under no license; you're free to do whatever you want with it. If you do end up using it, an attached courtesy would be appreciated.
- Email: elliot.winkler@gmail.com
- Twitter: @mcmire