class Chef::Util::Diff
Public Instance Methods
diff(old_file, new_file)
click to toggle source
# File lib/chef/util/diff.rb, line 78 def diff(old_file, new_file) use_tempfile_if_missing(old_file) do |old_file| use_tempfile_if_missing(new_file) do |new_file| @error = do_diff(old_file, new_file) end end end
for_output()
click to toggle source
@todo: to_a, to_s, to_json, inspect defs, accessors for @diff and @error @todo: move coercion to UTF-8 into to_json @todo: replace shellout to diff -u with diff-lcs gem
# File lib/chef/util/diff.rb, line 53 def for_output # formatted output to a terminal uses arrays of strings and returns error strings @diff.nil? ? [ @error ] : @diff end
for_reporting()
click to toggle source
# File lib/chef/util/diff.rb, line 58 def for_reporting # caller needs to ensure that new files aren't posted to resource reporting return nil if @diff.nil? @diff.join("\\n") end
udiff(old_file, new_file)
click to toggle source
produces a unified-output-format diff with 3 lines of context ChefFS uses udiff() directly
# File lib/chef/util/diff.rb, line 88 def udiff(old_file, new_file) diff_str = "" file_length_difference = 0 old_data = IO.readlines(old_file).map { |e| e.chomp } new_data = IO.readlines(new_file).map { |e| e.chomp } diff_data = ::Diff::LCS.diff(old_data, new_data) return diff_str if old_data.empty? && new_data.empty? return "No differences encountered\n" if diff_data.empty? # write diff header (standard unified format) ft = File.stat(old_file).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.%N %z') diff_str << "--- #{old_file}\t#{ft}\n" ft = File.stat(new_file).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.%N %z') diff_str << "+++ #{new_file}\t#{ft}\n" # loop over diff hunks. if a hunk overlaps with the last hunk, # join them. otherwise, print out the old one. old_hunk = hunk = nil diff_data.each do |piece| begin hunk = ::Diff::LCS::Hunk.new(old_data, new_data, piece, 3, file_length_difference) file_length_difference = hunk.file_length_difference next unless old_hunk next if hunk.merge(old_hunk) diff_str << old_hunk.diff(:unified) << "\n" ensure old_hunk = hunk end end diff_str << old_hunk.diff(:unified) << "\n" return diff_str end
use_tempfile_if_missing(file) { |file| ... }
click to toggle source
# File lib/chef/util/diff.rb, line 64 def use_tempfile_if_missing(file) tempfile = nil unless File.exists?(file) Chef::Log.debug("file #{file} does not exist to diff against, using empty tempfile") tempfile = Tempfile.new("chef-diff") file = tempfile.path end yield file unless tempfile.nil? tempfile.close tempfile.unlink end end
Private Instance Methods
do_diff(old_file, new_file)
click to toggle source
# File lib/chef/util/diff.rb, line 125 def do_diff(old_file, new_file) if Chef::Config[:diff_disabled] return "(diff output suppressed by config)" end diff_filesize_threshold = Chef::Config[:diff_filesize_threshold] diff_output_threshold = Chef::Config[:diff_output_threshold] if ::File.size(old_file) > diff_filesize_threshold || ::File.size(new_file) > diff_filesize_threshold return "(file sizes exceed #{diff_filesize_threshold} bytes, diff output suppressed)" end # MacOSX(BSD?) diff will *sometimes* happily spit out nasty binary diffs return "(current file is binary, diff output suppressed)" if is_binary?(old_file) return "(new content is binary, diff output suppressed)" if is_binary?(new_file) begin Chef::Log.debug("running: diff -u #{old_file} #{new_file}") diff_str = udiff(old_file, new_file) rescue Exception => e # Should *not* receive this, but in some circumstances it seems that # an exception can be thrown even using shell_out instead of shell_out! return "Could not determine diff. Error: #{e.message}" end if !diff_str.empty? && diff_str != "No differences encountered\n" if diff_str.length > diff_output_threshold return "(long diff of over #{diff_output_threshold} characters, diff output suppressed)" else diff_str = encode_diff_for_json(diff_str) @diff = diff_str.split("\n") return "(diff available)" end else return "(no diff)" end end
encode_diff_for_json(diff_str)
click to toggle source
# File lib/chef/util/diff.rb, line 178 def encode_diff_for_json(diff_str) if Object.const_defined? :Encoding diff_str.encode!('UTF-8', :invalid => :replace, :undef => :replace, :replace => '?') end return diff_str end
is_binary?(path)
click to toggle source
# File lib/chef/util/diff.rb, line 164 def is_binary?(path) File.open(path) do |file| # XXX: this slurps into RAM, but we should have already checked our diff has a reasonable size buff = file.read buff = "" if buff.nil? begin return buff !~ /\A[\s[:print:]]*\z/m rescue ArgumentError => e return true if e.message =~ /invalid byte sequence/ raise end end end