class CheckPlease::Comparison
Attributes
diffs[R]
flags[R]
Public Class Methods
perform(reference, candidate, flags = {})
click to toggle source
# File lib/check_please/comparison.rb, line 4 def self.perform(reference, candidate, flags = {}) new.perform(reference, candidate, flags) end
Public Instance Methods
perform(reference, candidate, flags = {})
click to toggle source
# File lib/check_please/comparison.rb, line 8 def perform(reference, candidate, flags = {}) @flags = Flags.reify(flags) @diffs = Diffs.new(flags: @flags) catch(:max_diffs_reached) do compare reference, candidate, CheckPlease::Path.root end diffs.filter_by_flags(@flags) end
Private Instance Methods
assert_can_match_by_value!(array)
click to toggle source
# File lib/check_please/comparison.rb, line 144 def assert_can_match_by_value!(array) if array.any? { |e| Array === e || Hash === e } raise CheckPlease::BehaviorUndefined, "match_by_value behavior is not defined for collections!" end end
compare(ref, can, path)
click to toggle source
# File lib/check_please/comparison.rb, line 21 def compare(ref, can, path) return if path.excluded?(flags) case types_for_compare(ref, can) when [ :array, :array ] ; compare_arrays ref, can, path when [ :hash, :hash ] ; compare_hashes ref, can, path when [ :other, :other ] ; compare_others ref, can, path else record_diff ref, can, path, :type_mismatch end end
compare_arrays(ref_array, can_array, path)
click to toggle source
# File lib/check_please/comparison.rb, line 43 def compare_arrays(ref_array, can_array, path) case when ( key = path.key_to_match_by(flags) ) compare_arrays_by_key ref_array, can_array, path, key when path.match_by_value?(flags) compare_arrays_by_value ref_array, can_array, path else compare_arrays_by_index ref_array, can_array, path end end
compare_arrays_by_index(ref_array, can_array, path)
click to toggle source
# File lib/check_please/comparison.rb, line 151 def compare_arrays_by_index(ref_array, can_array, path) max_len = [ ref_array, can_array ].map(&:length).max (0...max_len).each do |i| n = i + 1 # count in human pls new_path = path + n ref = ref_array[i] can = can_array[i] case when ref_array.length < n ; record_diff ref, can, new_path, :extra when can_array.length < n ; record_diff ref, can, new_path, :missing else compare ref, can, new_path end end end
compare_arrays_by_key(ref_array, can_array, path, key_name)
click to toggle source
# File lib/check_please/comparison.rb, line 54 def compare_arrays_by_key(ref_array, can_array, path, key_name) refs_by_key = index_array!(ref_array, path, key_name, "reference") cans_by_key = index_array!(can_array, path, key_name, "candidate") key_values = (refs_by_key.keys | cans_by_key.keys) key_values.compact! # NOTE: will break if nil is ever used as a key (but WHO WOULD DO THAT?!) key_values.sort! key_values.each do |key_value| new_path = path + "#{key_name}=#{key_value}" ref = refs_by_key[key_value] can = cans_by_key[key_value] case when ref.nil? ; record_diff ref, can, new_path, :extra when can.nil? ; record_diff ref, can, new_path, :missing else ; compare ref, can, new_path end end end
compare_arrays_by_value(ref_array, can_array, path)
click to toggle source
FIXME: this can generate duplicate paths. Time to introduce lft_path, rgt_path ?
# File lib/check_please/comparison.rb, line 114 def compare_arrays_by_value(ref_array, can_array, path) assert_can_match_by_value! ref_array assert_can_match_by_value! can_array matches = can_array.map { false } # Look for missing values ref_array.each.with_index do |ref, i| new_path = path + (i+1) # count in human pls # Weird, but necessary to handle duplicates properly j = can_array.index.with_index { |can, j| matches[j] == false && can == ref } if j matches[j] = true else record_diff ref, nil, new_path, :missing end end # Look for extra values can_array.zip(matches).each.with_index do |(can, match), i| next if match new_path = path + (i+1) # count in human pls record_diff nil, can, new_path, :extra end end
compare_common_keys(ref_hash, can_hash, path)
click to toggle source
# File lib/check_please/comparison.rb, line 198 def compare_common_keys(ref_hash, can_hash, path) keys = ref_hash.keys & can_hash.keys keys.each do |k| compare ref_hash[k], can_hash[k], path + k end end
compare_hashes(ref_hash, can_hash, path)
click to toggle source
# File lib/check_please/comparison.rb, line 169 def compare_hashes(ref_hash, can_hash, path) if flags.indifferent_keys ref_hash = stringify_symbol_keys(ref_hash) can_hash = stringify_symbol_keys(can_hash) end record_missing_keys ref_hash, can_hash, path compare_common_keys ref_hash, can_hash, path record_extra_keys ref_hash, can_hash, path end
compare_others(ref, can, path)
click to toggle source
# File lib/check_please/comparison.rb, line 212 def compare_others(ref, can, path) ref_prime = normalize_value(path, ref) can_prime = normalize_value(path, can) return if ref_prime == can_prime record_diff ref, can, path, :mismatch end
index_array!(array_of_hashes, path, key_name, ref_or_can)
click to toggle source
# File lib/check_please/comparison.rb, line 75 def index_array!(array_of_hashes, path, key_name, ref_or_can) elements_by_key = {} array_of_hashes.each.with_index do |h, i| # make sure we have a hash unless h.is_a?(Hash) raise CheckPlease::TypeMismatchError, \ "The element at position #{i} in the #{ref_or_can} array is not a hash." end if flags.indifferent_keys h = stringify_symbol_keys(h) end # try to get the value of the attribute identified by key_name key_value = h.fetch(key_name) { raise CheckPlease::NoSuchKeyError, \ <<~EOF The #{ref_or_can} hash at position #{i} has no #{key_name.inspect} key. Keys it does have: #{h.keys.inspect} EOF } # complain about dupes if elements_by_key.has_key?(key_value) key_val_expr = "#{key_name}=#{key_value}" raise CheckPlease::DuplicateKeyError, \ "Duplicate #{ref_or_can} element found at path '#{path + key_val_expr}'." end # ok, now we can proceed elements_by_key[key_value] = h end elements_by_key end
normalize_value(path, value)
click to toggle source
# File lib/check_please/comparison.rb, line 220 def normalize_value(path, value) if flags.indifferent_values value = stringify_symbol(value) end if flags.normalize_values # We assume that normalize_values is a hash of path expression strings to a proc, string, or symbol that will be used to normalize the value _, normalizer = flags.normalize_values.detect { |path_string, a_proc| path.match?(path_string) } case normalizer when nil ; value when Proc ; normalizer.call(value) when String, Symbol ; value.send(normalizer) else ; raise ArgumentError, "Not sure how to use #{normalizer.inspect} to normalize #{value.inspect}" end else return value end end
record_diff(ref, can, path, type)
click to toggle source
# File lib/check_please/comparison.rb, line 243 def record_diff(ref, can, path, type) diff = Diff.new(type, path, ref, can) diffs << diff end
record_extra_keys(ref_hash, can_hash, path)
click to toggle source
# File lib/check_please/comparison.rb, line 205 def record_extra_keys(ref_hash, can_hash, path) keys = can_hash.keys - ref_hash.keys keys.each do |k| record_diff nil, can_hash[k], path + k, :extra end end
record_missing_keys(ref_hash, can_hash, path)
click to toggle source
# File lib/check_please/comparison.rb, line 191 def record_missing_keys(ref_hash, can_hash, path) keys = ref_hash.keys - can_hash.keys keys.each do |k| record_diff ref_hash[k], nil, path + k, :missing end end
stringify_symbol(x)
click to toggle source
# File lib/check_please/comparison.rb, line 187 def stringify_symbol(x) Symbol === x ? x.to_s : x end
stringify_symbol_keys(h)
click to toggle source
# File lib/check_please/comparison.rb, line 179 def stringify_symbol_keys(h) Hash[ h.map { |k,v| [ stringify_symbol(k), v ] } ] end
types_for_compare(*list)
click to toggle source
# File lib/check_please/comparison.rb, line 33 def types_for_compare(*list) list.map { |e| case e when Array ; :array when Hash ; :hash else ; :other end } end