require 'pact/shared/jruby_support' require 'pact/matchers/differ' require 'pact/matchers/extract_diff_messages' module Pact module Matchers class UnixDiffFormatter include JRubySupport MESSAGES_TITLE = "\n\nDescription of differences\n--------------------------------------" def initialize diff, options = {} @diff = diff @colour = options.fetch(:colour, false) @actual = options.fetch(:actual, {}) @include_explanation = options.fetch(:include_explanation, true) @differ = Pact::Matchers::Differ.new(@colour) @messages = Pact::Matchers::ExtractDiffMessages.call(diff).collect{ | message| "* #{message}" }.join("\n") end def self.call diff, options = {} # require stops circular dependency from pact/configuration <-> pact/matchers/unix_diff_formatter require 'pact/configuration' default_options = {colour: Pact.configuration.color_enabled} new(diff, default_options.merge(options)).call end def call to_s end def to_s expected = generate_string(diff, :expected) actual = generate_string(diff, :actual) suffix = @include_explanation ? key + "\n" : '' messages = @include_explanation ? "#{MESSAGES_TITLE}\n#{@messages}\n" : '' string_diff = @differ.diff_as_string(actual, expected).lstrip string_diff = remove_first_line(string_diff) string_diff = remove_comma_from_end_of_arrays(string_diff) suffix + string_diff + messages end private def handle thing, target case thing when Hash then copy_hash(thing, target) when Array then copy_array(thing, target) when Difference then copy_diff(thing, target) when TypeDifference then copy_diff(thing, target) when RegexpDifference then copy_diff(thing, target) when NoDiffAtIndex then copy_no_diff(thing, target) else copy_object(thing, target) end end def generate_string diff, target comparable = handle(diff, target) begin # Can't think of an elegant way to check if we can pretty generate other than to try it and maybe fail json = fix_blank_lines_in_empty_hashes JSON.pretty_generate(comparable) add_comma_to_end_of_arrays json rescue JSON::GeneratorError comparable.to_s end end def copy_hash hash, target hash.keys.each_with_object({}) do | key, new_hash | value = handle hash[key], target new_hash[key] = value unless (KeyNotFound === value || UnexpectedKey === value) end end def copy_array array, target array.each_index.each_with_object([]) do | index, new_array | value = handle array[index], target new_array[index] = value unless (UnexpectedIndex === value || IndexNotFound === value) end end def copy_no_diff(thing, target) NoDifferenceDecorator.new end def copy_diff difference, target if target == :actual handle difference.actual, target else handle difference.expected, target end end def copy_object object, target if Regexp === object RegexpDecorator.new(object) else object end end def key "Diff\n--------------------------------------\n" + "Key: " + @differ.red("-") + @differ.red(" is expected \n") + @differ.green(" +") + @differ.green(" is actual \n") + "Matching keys and values are not shown\n" end def remove_first_line string_diff lines = string_diff.split("\n") if lines[0] =~ /@@/ lines[1..-1].join("\n") else string_diff end end def add_comma_to_end_of_arrays string string.gsub(/(\n\s*\])/, ',\1') end def remove_comma_from_end_of_arrays string string.gsub(/,(\n\s*\])/, '\1') end class NoDifferenceDecorator def to_json options = {} "... " end end class RegexpDecorator def initialize regexp @regexp = regexp end def to_json options = {} @regexp.inspect end def as_json @regexp.inspect end end attr_reader :diff end end end