Hello community, here is the log from the commit of package rubygem-hashdiff for openSUSE:Factory checked in at 2017-09-04 12:36:51 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-hashdiff (Old) and /work/SRC/openSUSE:Factory/.rubygem-hashdiff.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-hashdiff" Mon Sep 4 12:36:51 2017 rev:5 rq:520437 version:0.3.6 Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-hashdiff/rubygem-hashdiff.changes 2017-06-08 15:01:42.198029968 +0200 +++ /work/SRC/openSUSE:Factory/.rubygem-hashdiff.new/rubygem-hashdiff.changes 2017-09-04 12:36:53.602692912 +0200 @@ -1,0 +2,14 @@ +Sun Sep 3 09:04:56 UTC 2017 - co...@suse.com + +- updated to version 0.3.6 + see installed changelog.md + + ## v0.3.6 2017-08-22 + + * add option `use_lcs` #35 + + ## v0.3.5 2017-08-06 + + * add option `array_path` #34 + +------------------------------------------------------------------- Old: ---- hashdiff-0.3.4.gem New: ---- hashdiff-0.3.6.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-hashdiff.spec ++++++ --- /var/tmp/diff_new_pack.CGFGmy/_old 2017-09-04 12:36:54.350587690 +0200 +++ /var/tmp/diff_new_pack.CGFGmy/_new 2017-09-04 12:36:54.350587690 +0200 @@ -24,7 +24,7 @@ # Name: rubygem-hashdiff -Version: 0.3.4 +Version: 0.3.6 Release: 0 %define mod_name hashdiff %define mod_full_name %{mod_name}-%{version} ++++++ hashdiff-0.3.4.gem -> hashdiff-0.3.6.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/README.md new/README.md --- old/README.md 2017-05-01 06:02:25.000000000 +0200 +++ new/README.md 2017-08-22 20:50:23.000000000 +0200 @@ -72,8 +72,8 @@ patch example: ```ruby -a = {a: 3} -b = {a: {a1: 1, a2: 2}} +a = {'a' => 3} +b = {'a' => {'a1' => 1, 'a2' => 2}} diff = HashDiff.diff(a, b) HashDiff.patch!(a, diff).should == b @@ -82,8 +82,8 @@ unpatch example: ```ruby -a = [{a: 1, b: 2, c: 3, d: 4, e: 5}, {x: 5, y: 6, z: 3}, 1] -b = [1, {a: 1, b: 2, c: 3, e: 5}] +a = [{'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5}, {'x' => 5, 'y' => 6, 'z' => 3}, 1] +b = [1, {'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5}] diff = HashDiff.diff(a, b) # diff two array is OK HashDiff.unpatch!(b, diff).should == a @@ -91,8 +91,9 @@ ### Options -There are six options available: `:delimiter`, `:similarity`, -`:strict`, `:numeric_tolerance`, `:strip` and `:case_insensitive`. +There are seven options available: `:delimiter`, `:similarity`, +`:strict`, `:numeric_tolerance`, `:strip`, `:case_insensitive` +and `:array_path`. #### `:delimiter` @@ -140,7 +141,7 @@ #### `:case_insensitive` -The :case_insensitive option makes string comparisions ignore case. +The :case_insensitive option makes string comparisons ignore case. ```ruby a = {x:5, s:'FooBar'} @@ -150,6 +151,39 @@ diff.should == [["~", "x", 5, 6]] ``` +#### `:array_path` + +The :array_path option represents the path of the diff in an array rather than +a string. This can be used to show differences in between hash key types and +is useful for `patch!` when used on hashes without string keys. + +```ruby +a = {x:5} +b = {'x'=>6} + +diff = HashDiff.diff(a, b, :array_path => true) +diff.should == [['-', [:x], 5], ['+', ['x'], 6]] +``` + +For cases where there are arrays in paths their index will be added to the path. +```ruby +a = {x:[0,1]} +b = {x:[0,2]} + +diff = HashDiff.diff(a, b, :array_path => true) +diff.should == [["-", [:x, 1], 1], ["+", [:x, 1], 2]] +``` + +This shouldn't cause problems if you are comparing an array with a hash: + +```ruby +a = {x:{0=>1}} +b = {x:[1]} + +diff = HashDiff.diff(a, b, :array_path => true) +diff.should == [["~", [:a], [1], {0=>1}]] +``` + #### Specifying a custom comparison method It's possible to specify how the values of a key should be compared. @@ -186,6 +220,8 @@ When a comparison block is given, it'll be given priority over other specified options. If the block returns value other than `true` or `false`, then the two values will be compared with other specified options. +When used in conjunction with the `array_path` option, the path passed in as an argument will be an array. When determining the ordering of an array a key of `"*"` will be used in place of the `key[*]` field. It is possible, if you have hashes with integer or `"*"` keys, to have problems distinguishing between arrays and hashes - although this shouldn't be an issue unless your data is very difficult to predict and/or your custom rules are very specific. + #### Sorting arrays before comparison An order difference alone between two arrays can create too many diffs to be useful. Consider sorting them prior to diffing. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/changelog.md new/changelog.md --- old/changelog.md 2017-05-01 06:02:25.000000000 +0200 +++ new/changelog.md 2017-08-22 20:50:23.000000000 +0200 @@ -1,5 +1,13 @@ # Change Log +## v0.3.6 2017-08-22 + +* add option `use_lcs` #35 + +## v0.3.5 2017-08-06 + +* add option `array_path` #34 + ## v0.3.4 2017-05-01 * performance improvement of HashDiff#similar? #31 Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/hashdiff/diff.rb new/lib/hashdiff/diff.rb --- old/lib/hashdiff/diff.rb 2017-05-01 06:02:25.000000000 +0200 +++ new/lib/hashdiff/diff.rb 2017-08-22 20:50:23.000000000 +0200 @@ -11,6 +11,7 @@ # * :delimiter (String) ['.'] the delimiter used when returning nested key references # * :numeric_tolerance (Numeric) [0] should be a positive numeric value. Value by which numeric differences must be greater than. By default, numeric values are compared exactly; with the :tolerance option, the difference between numeric values must be greater than the given value. # * :strip (Boolean) [false] whether or not to call #strip on strings before comparing + # * :array_path (Boolean) [false] whether to return the path references for nested values in an array, can be used for patch compatibility with non string keys. # # @yield [path, value1, value2] Optional block is used to compare each value, instead of default #==. If the block returns value other than true of false, then other specified comparison options will be used to do the comparison. # @@ -53,6 +54,7 @@ # * :delimiter (String) ['.'] the delimiter used when returning nested key references # * :numeric_tolerance (Numeric) [0] should be a positive numeric value. Value by which numeric differences must be greater than. By default, numeric values are compared exactly; with the :tolerance option, the difference between numeric values must be greater than the given value. # * :strip (Boolean) [false] whether or not to call #strip on strings before comparing + # * :array_path (Boolean) [false] whether to return the path references for nested values in an array, can be used for patch compatibility with non string keys. # # @yield [path, value1, value2] Optional block is used to compare each value, instead of default #==. If the block returns value other than true of false, then other specified comparison options will be used to do the comparison. # @@ -74,9 +76,13 @@ :delimiter => '.', :strict => true, :strip => false, - :numeric_tolerance => 0 + :numeric_tolerance => 0, + :array_path => false, + :use_lcs => true }.merge!(options) + opts[:prefix] = [] if opts[:array_path] && opts[:prefix] == '' + opts[:comparison] = block if block_given? # prefer to compare with provided block @@ -100,27 +106,26 @@ end result = [] - if obj1.is_a?(Array) - changeset = diff_array(obj1, obj2, opts) do |lcs| + if obj1.is_a?(Array) && opts[:use_lcs] + changeset = diff_array_lcs(obj1, obj2, opts) do |lcs| # use a's index for similarity lcs.each do |pair| - result.concat(diff(obj1[pair[0]], obj2[pair[1]], opts.merge(:prefix => "#{opts[:prefix]}[#{pair[0]}]"))) + prefix = prefix_append_array_index(opts[:prefix], pair[0], opts) + result.concat(diff(obj1[pair[0]], obj2[pair[1]], opts.merge(:prefix => prefix))) end end changeset.each do |change| + change_key = prefix_append_array_index(opts[:prefix], change[1], opts) if change[0] == '-' - result << ['-', "#{opts[:prefix]}[#{change[1]}]", change[2]] + result << ['-', change_key, change[2]] elsif change[0] == '+' - result << ['+', "#{opts[:prefix]}[#{change[1]}]", change[2]] + result << ['+', change_key, change[2]] end end + elsif obj1.is_a?(Array) && !opts[:use_lcs] + result.concat(LinearCompareArray.call(obj1, obj2, opts)) elsif obj1.is_a?(Hash) - if opts[:prefix].empty? - prefix = "" - else - prefix = "#{opts[:prefix]}#{opts[:delimiter]}" - end deleted_keys = obj1.keys - obj2.keys common_keys = obj1.keys & obj2.keys @@ -128,27 +133,32 @@ # add deleted properties deleted_keys.sort_by{|k,v| k.to_s }.each do |k| - custom_result = custom_compare(opts[:comparison], "#{prefix}#{k}", obj1[k], nil) + change_key = prefix_append_key(opts[:prefix], k, opts) + custom_result = custom_compare(opts[:comparison], change_key, obj1[k], nil) if custom_result result.concat(custom_result) else - result << ['-', "#{prefix}#{k}", obj1[k]] + result << ['-', change_key, obj1[k]] end end # recursive comparison for common keys - common_keys.sort_by{|k,v| k.to_s }.each {|k| result.concat(diff(obj1[k], obj2[k], opts.merge(:prefix => "#{prefix}#{k}"))) } + common_keys.sort_by{|k,v| k.to_s }.each do |k| + prefix = prefix_append_key(opts[:prefix], k, opts) + result.concat(diff(obj1[k], obj2[k], opts.merge(:prefix => prefix))) + end # added properties added_keys.sort_by{|k,v| k.to_s }.each do |k| + change_key = prefix_append_key(opts[:prefix], k, opts) unless obj1.key?(k) - custom_result = custom_compare(opts[:comparison], "#{prefix}#{k}", nil, obj2[k]) + custom_result = custom_compare(opts[:comparison], change_key, nil, obj2[k]) if custom_result result.concat(custom_result) else - result << ['+', "#{prefix}#{k}", obj2[k]] + result << ['+', change_key, obj2[k]] end end end @@ -163,7 +173,7 @@ # @private # # diff array using LCS algorithm - def self.diff_array(a, b, options = {}) + def self.diff_array_lcs(a, b, options = {}) opts = { :prefix => '', :similarity => 0.8, @@ -216,5 +226,4 @@ change_set end - end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/hashdiff/lcs.rb new/lib/hashdiff/lcs.rb --- old/lib/hashdiff/lcs.rb 2017-05-01 06:02:25.000000000 +0200 +++ new/lib/hashdiff/lcs.rb 2017-08-22 20:50:23.000000000 +0200 @@ -6,7 +6,7 @@ def self.lcs(a, b, options = {}) opts = { :similarity => 0.8 }.merge!(options) - opts[:prefix] = "#{opts[:prefix]}[*]" + opts[:prefix] = prefix_append_array_index(opts[:prefix], '*', opts) return [] if a.size == 0 or b.size == 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/hashdiff/linear_compare_array.rb new/lib/hashdiff/linear_compare_array.rb --- old/lib/hashdiff/linear_compare_array.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/lib/hashdiff/linear_compare_array.rb 2017-08-22 20:50:23.000000000 +0200 @@ -0,0 +1,155 @@ +module HashDiff + # @private + # + # Used to compare arrays in a linear complexity, which produces longer diffs + # than using the lcs algorithm but is considerably faster + class LinearCompareArray + def self.call(old_array, new_array, options = {}) + instance = self.new(old_array, new_array, options) + instance.call + end + + def call + return [] if old_array.empty? && new_array.empty? + + self.old_index = 0 + self.new_index = 0 + # by comparing the array lengths we can expect that a number of items + # are either added or removed + self.expected_additions = new_array.length - old_array.length + + loop do + if extra_items_in_old_array? + append_deletion(old_array[old_index], old_index) + elsif extra_items_in_new_array? + append_addition(new_array[new_index], new_index) + else + compare_at_index + end + + self.old_index = old_index + 1 + self.new_index = new_index + 1 + break if iterated_through_both_arrays? + end + + changes + end + + private + + attr_reader :old_array, :new_array, :options, :additions, :deletions, :differences + attr_accessor :old_index, :new_index, :expected_additions + + def initialize(old_array, new_array, options) + @old_array = old_array + @new_array = new_array + @options = { prefix: '' }.merge!(options) + + @additions = [] + @deletions = [] + @differences = [] + end + + def extra_items_in_old_array? + old_index < old_array.length && new_index >= new_array.length + end + + def extra_items_in_new_array? + new_index < new_array.length && old_index >= old_array.length + end + + def iterated_through_both_arrays? + old_index >= old_array.length && new_index >= new_array.length + end + + def compare_at_index + difference = item_difference(old_array[old_index], new_array[new_index], old_index) + return if difference.empty? + + index_after_additions = index_of_match_after_additions + append_addititions_before_match(index_after_additions) + + index_after_deletions = index_of_match_after_deletions + append_deletions_before_match(index_after_deletions) + + match = index_after_additions || index_after_deletions + + append_differences(difference) unless match + end + + def item_difference(old_item, new_item, item_index) + prefix = HashDiff.prefix_append_array_index(options[:prefix], item_index, options) + HashDiff.diff(old_item, new_item, options.merge(:prefix => prefix)) + end + + # look ahead in the new array to see if the current item appears later + # thereby having new items added + def index_of_match_after_additions + return unless expected_additions > 0 + + (1..expected_additions).each do |i| + next_difference = item_difference( + old_array[old_index], + new_array[new_index + i], + old_index + ) + + return new_index + i if next_difference.empty? + end + + nil + end + + # look ahead in the old array to see if the current item appears later + # thereby having items removed + def index_of_match_after_deletions + return unless expected_additions < 0 + + (1..(expected_additions.abs)).each do |i| + next_difference = item_difference( + old_array[old_index + i], + new_array[new_index], + old_index + ) + + return old_index + i if next_difference.empty? + end + + nil + end + + def append_addititions_before_match(match_index) + return unless match_index + (new_index...match_index).each { |i| append_addition(new_array[i], i) } + self.expected_additions = expected_additions - (match_index - new_index) + self.new_index = match_index + end + + def append_deletions_before_match(match_index) + return unless match_index + (old_index...match_index).each { |i| append_deletion(old_array[i], i) } + self.expected_additions = expected_additions + (match_index - new_index) + self.old_index = match_index + end + + def append_addition(item, index) + key = HashDiff.prefix_append_array_index(options[:prefix], index, options) + additions << ['+', key, item] + end + + def append_deletion(item, index) + key = HashDiff.prefix_append_array_index(options[:prefix], index, options) + deletions << ['-', key, item] + end + + def append_differences(difference) + differences.concat(difference) + end + + def changes + # this algorithm only allows there to be additions or deletions + # deletions are reverse so they don't change the index of earlier items + differences + additions + deletions.reverse + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/hashdiff/patch.rb new/lib/hashdiff/patch.rb --- old/lib/hashdiff/patch.rb 2017-05-01 06:02:25.000000000 +0200 +++ new/lib/hashdiff/patch.rb 2017-08-22 20:50:23.000000000 +0200 @@ -1,4 +1,4 @@ -# +# # This module provides methods to diff two hash, patch and unpatch hash # module HashDiff @@ -17,19 +17,21 @@ delimiter = options[:delimiter] || '.' changes.each do |change| - parts = decode_property_path(change[1], delimiter) + parts = change[1] + parts = decode_property_path(parts, delimiter) unless parts.is_a?(Array) + last_part = parts.last parent_node = node(obj, parts[0, parts.size-1]) if change[0] == '+' - if last_part.is_a?(Integer) + if parent_node.is_a?(Array) parent_node.insert(last_part, change[2]) else parent_node[last_part] = change[2] end elsif change[0] == '-' - if last_part.is_a?(Integer) + if parent_node.is_a?(Array) parent_node.delete_at(last_part) else parent_node.delete(last_part) @@ -56,19 +58,21 @@ delimiter = options[:delimiter] || '.' changes.reverse_each do |change| - parts = decode_property_path(change[1], delimiter) + parts = change[1] + parts = decode_property_path(parts, delimiter) unless parts.is_a?(Array) + last_part = parts.last parent_node = node(obj, parts[0, parts.size-1]) if change[0] == '+' - if last_part.is_a?(Integer) + if parent_node.is_a?(Array) parent_node.delete_at(last_part) else parent_node.delete(last_part) end elsif change[0] == '-' - if last_part.is_a?(Integer) + if parent_node.is_a?(Array) parent_node.insert(last_part, change[2]) else parent_node[last_part] = change[2] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/hashdiff/util.rb new/lib/hashdiff/util.rb --- old/lib/hashdiff/util.rb 2017-05-01 06:02:25.000000000 +0200 +++ new/lib/hashdiff/util.rb 2017-08-22 20:50:23.000000000 +0200 @@ -55,19 +55,17 @@ # # e.g. "a.b[3].c" => ['a', 'b', 3, 'c'] def self.decode_property_path(path, delimiter='.') - parts = path.split(delimiter).collect do |part| + path.split(delimiter).inject([]) do |memo, part| if part =~ /^(.*)\[(\d+)\]$/ if $1.size > 0 - [$1, $2.to_i] + memo + [$1, $2.to_i] else - $2.to_i + memo + [$2.to_i] end else - part + memo + [part] end end - - parts.flatten end # @private @@ -129,4 +127,20 @@ end end end + + def self.prefix_append_key(prefix, key, opts) + if opts[:array_path] + prefix + [key] + else + prefix.empty? ? "#{key}" : "#{prefix}#{opts[:delimiter]}#{key}" + end + end + + def self.prefix_append_array_index(prefix, array_index, opts) + if opts[:array_path] + prefix + [array_index] + else + "#{prefix}[#{array_index}]" + end + end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/hashdiff/version.rb new/lib/hashdiff/version.rb --- old/lib/hashdiff/version.rb 2017-05-01 06:02:25.000000000 +0200 +++ new/lib/hashdiff/version.rb 2017-08-22 20:50:23.000000000 +0200 @@ -1,3 +1,3 @@ module HashDiff - VERSION = '0.3.4' + VERSION = '0.3.6' end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/hashdiff.rb new/lib/hashdiff.rb --- old/lib/hashdiff.rb 2017-05-01 06:02:25.000000000 +0200 +++ new/lib/hashdiff.rb 2017-08-22 20:50:23.000000000 +0200 @@ -1,5 +1,6 @@ require 'hashdiff/util' require 'hashdiff/lcs' +require 'hashdiff/linear_compare_array' require 'hashdiff/diff' require 'hashdiff/patch' require 'hashdiff/version' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 2017-05-01 06:02:25.000000000 +0200 +++ new/metadata 2017-08-22 20:50:23.000000000 +0200 @@ -1,14 +1,14 @@ --- !ruby/object:Gem::Specification name: hashdiff version: !ruby/object:Gem::Version - version: 0.3.4 + version: 0.3.6 platform: ruby authors: - Liu Fengyun autorequire: bindir: bin cert_chain: [] -date: 2017-05-01 00:00:00.000000000 Z +date: 2017-08-22 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: rspec @@ -73,6 +73,7 @@ - lib/hashdiff.rb - lib/hashdiff/diff.rb - lib/hashdiff/lcs.rb +- lib/hashdiff/linear_compare_array.rb - lib/hashdiff/patch.rb - lib/hashdiff/util.rb - lib/hashdiff/version.rb @@ -80,6 +81,7 @@ - spec/hashdiff/diff_array_spec.rb - spec/hashdiff/diff_spec.rb - spec/hashdiff/lcs_spec.rb +- spec/hashdiff/linear_compare_array_spec.rb - spec/hashdiff/patch_spec.rb - spec/hashdiff/util_spec.rb - spec/spec_helper.rb diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/hashdiff/best_diff_spec.rb new/spec/hashdiff/best_diff_spec.rb --- old/spec/hashdiff/best_diff_spec.rb 2017-05-01 06:02:25.000000000 +0200 +++ new/spec/hashdiff/best_diff_spec.rb 2017-08-22 20:50:23.000000000 +0200 @@ -62,4 +62,13 @@ ['+', 'menu.popup.menuitem[1]', {"value" => "Open", "onclick" => "OpenDoc()"}] ] end + + it "should be able to have an array_path specified" do + a = {'x' => [{'a' => 1, 'c' => 3, 'e' => 5}, {'y' => 3}]} + b = {'x' => [{'a' => 1, 'b' => 2, 'e' => 5}] } + + diff = HashDiff.best_diff(a, b, :array_path => true) + diff.should == [["-", ["x", 0, "c"], 3], ["+", ["x", 0, "b"], 2], ["-", ["x", 1], {"y"=>3}]] + end + end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/hashdiff/diff_array_spec.rb new/spec/hashdiff/diff_array_spec.rb --- old/spec/hashdiff/diff_array_spec.rb 2017-05-01 06:02:25.000000000 +0200 +++ new/spec/hashdiff/diff_array_spec.rb 2017-08-22 20:50:23.000000000 +0200 @@ -5,7 +5,7 @@ a = [1, 2, 3] b = [1, 2, 3] - diff = HashDiff.diff_array(a, b) + diff = HashDiff.diff_array_lcs(a, b) diff.should == [] end @@ -13,7 +13,7 @@ a = [1, 2, 3] b = [1, 8, 7] - diff = HashDiff.diff_array(a, b) + diff = HashDiff.diff_array_lcs(a, b) diff.should == [['-', 2, 3], ['-', 1, 2], ['+', 1, 8], ['+', 2, 7]] end @@ -21,7 +21,7 @@ a = [1, 2] b = [] - diff = HashDiff.diff_array(a, b) + diff = HashDiff.diff_array_lcs(a, b) diff.should == [['-', 1, 2], ['-', 0, 1]] end @@ -29,7 +29,7 @@ a = [] b = [1, 2] - diff = HashDiff.diff_array(a, b) + diff = HashDiff.diff_array_lcs(a, b) diff.should == [['+', 0, 1], ['+', 1, 2]] end @@ -37,7 +37,7 @@ a = [1, 3, 5, 7] b = [2, 3, 7, 5] - diff = HashDiff.diff_array(a, b) + diff = HashDiff.diff_array_lcs(a, b) diff.should == [['-', 0, 1], ['+', 0, 2], ['+', 2, 7], ['-', 4, 7]] end @@ -45,14 +45,14 @@ a = [1, 3, 4, 7] b = [2, 3, 7, 5] - diff = HashDiff.diff_array(a, b) + diff = HashDiff.diff_array_lcs(a, b) diff.should == [['-', 0, 1], ['+', 0, 2], ['-', 2, 4], ['+', 3, 5]] end it "should be able to diff two arrays with similar elements" do a = [{'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5}, 3] b = [1, {'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5}] - diff = HashDiff.diff_array(a, b) + diff = HashDiff.diff_array_lcs(a, b) diff.should == [['+', 0, 1], ['-', 2, 3]] end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/hashdiff/diff_spec.rb new/spec/hashdiff/diff_spec.rb --- old/spec/hashdiff/diff_spec.rb 2017-05-01 06:02:25.000000000 +0200 +++ new/spec/hashdiff/diff_spec.rb 2017-08-22 20:50:23.000000000 +0200 @@ -274,4 +274,66 @@ diff.should == [['~', 'b', 'boat', 'truck'], ['~', 'c', 'plane', ' plan']] end end + + context 'when :array_path is true' do + it 'should return the diff path in an array rather than a string' do + x = { 'a' => 'foo' } + y = { 'a' => 'bar' } + diff = HashDiff.diff(x, y, :array_path => true) + + diff.should == [['~', ['a'], 'foo', 'bar']] + end + + it 'should show array indexes in paths' do + x = { 'a' => [0, 1, 2] } + y = { 'a' => [0, 1, 2, 3] } + + diff = HashDiff.diff(x, y, :array_path => true) + + diff.should == [['+', ['a', 3], 3]] + end + + it 'should show differences with string and symbol keys' do + x = { 'a' => 'foo' } + y = { :a => 'bar' } + + diff = HashDiff.diff(x, y, :array_path => true) + diff.should == [['-', ['a'], 'foo'], ['+', [:a], 'bar']] + end + + it 'should support other key types' do + time = Time.now + x = { time => 'foo' } + y = { 0 => 'bar' } + + diff = HashDiff.diff(x, y, :array_path => true) + diff.should == [['-', [time], 'foo'], ['+', [0], 'bar']] + end + end + + context 'when :use_lcs is false' do + it 'should show items in an array as changed' do + x = [:a, :b] + y = [:c, :d] + diff = HashDiff.diff(x, y, :use_lcs => false) + + diff.should == [['~', '[0]', :a, :c], ['~', '[1]', :b, :d]] + end + + it 'should show additions to arrays' do + x = { :a => [0] } + y = { :a => [0, 1] } + diff = HashDiff.diff(x, y, :use_lcs => false) + + diff.should == [['+', 'a[1]', 1]] + end + + it 'shows changes to nested arrays' do + x = { :a => [[0, 1]] } + y = { :a => [[1, 2]] } + diff = HashDiff.diff(x, y, :use_lcs => false) + + diff.should == [['~', 'a[0][0]', 0, 1], ['~', 'a[0][1]', 1, 2]] + end + end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/hashdiff/linear_compare_array_spec.rb new/spec/hashdiff/linear_compare_array_spec.rb --- old/spec/hashdiff/linear_compare_array_spec.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/spec/hashdiff/linear_compare_array_spec.rb 2017-08-22 20:50:23.000000000 +0200 @@ -0,0 +1,48 @@ +require 'spec_helper' + +describe HashDiff::LinearCompareArray do + it "should find no differences between two empty arrays" do + difference = described_class.call([], []) + difference.should == [] + end + + it "should find added items when the old array is empty" do + difference = described_class.call([], [:a, :b]) + difference.should == [['+', '[0]', :a], ['+', '[1]', :b]] + end + + it "should find removed items when the new array is empty" do + difference = described_class.call([:a, :b], []) + difference.should == [['-', '[1]', :b], ['-', '[0]', :a]] + end + + it "should find no differences between identical arrays" do + difference = described_class.call([:a, :b], [:a, :b]) + difference.should == [] + end + + it "should find added items in an array" do + difference = described_class.call([:a, :d], [:a, :b, :c, :d]) + difference.should == [['+', '[1]', :b], ['+', '[2]', :c]] + end + + it "should find removed items in an array" do + difference = described_class.call([:a, :b, :c, :d, :e, :f], [:a, :d, :f]) + difference.should == [['-', '[4]', :e], ['-', '[2]', :c], ['-', '[1]', :b]] + end + + it "should show additions and deletions as changed items" do + difference = described_class.call([:a, :b, :c], [:c, :b, :a]) + difference.should == [['~', '[0]', :a, :c], ['~', '[2]', :c, :a]] + end + + it "should show changed items in a hash" do + difference = described_class.call([{ :a => :b }], [{ :a => :c }]) + difference.should == [['~', '[0].a', :b, :c]] + end + + it "should show changed items and added items" do + difference = described_class.call([{ :a => 1, :b => 2 }], [{ :a => 2, :b => 2 }, :item]) + difference.should == [['~', '[0].a', 1, 2], ['+', '[1]', :item]] + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/hashdiff/patch_spec.rb new/spec/hashdiff/patch_spec.rb --- old/spec/hashdiff/patch_spec.rb 2017-05-01 06:02:25.000000000 +0200 +++ new/spec/hashdiff/patch_spec.rb 2017-08-22 20:50:23.000000000 +0200 @@ -157,5 +157,27 @@ HashDiff.unpatch!(b, diff, :delimiter => "\n").should == a end + it "should be able to patch when the diff is generated with an array_path" do + a = {"a" => 1, "b" => 1} + b = {"a" => 1, "b" => 2} + diff = HashDiff.diff(a, b, :array_path => true) + HashDiff.patch!(a, diff).should == b + + a = {"a" => 1, "b" => 1} + b = {"a" => 1, "b" => 2} + HashDiff.unpatch!(b, diff).should == a + end + + it "should be able to use non string keys when diff is generated with an array_path" do + a = {"a" => 1, :a => 2, 0 => 3} + b = {"a" => 5, :a => 6, 0 => 7} + diff = HashDiff.diff(a, b, :array_path => true) + + HashDiff.patch!(a, diff).should == b + + a = {"a" => 1, :a => 2, 0 => 3} + b = {"a" => 5, :a => 6, 0 => 7} + HashDiff.unpatch!(b, diff).should == a + end end