Ruby on Rails Diff Text to HTML <ins> and <del>
Tuesday, November 4th, 2008This code is perfect if you have 2 text objects in your Rails application and you want to compare their differences in one of your HTML views. It’s 99% pure Ruby too, so if you alter the first line, you can use it for other purposes.
Only one thing to note: you must have diff installed. I’m using: diff (GNU diffutils) 2.8.1.
-
#set up some variables to reference later
-
temporary_directory = File.join(Rails.root, "tmp")
-
max_lines = 9999999 #needs to be larger than the most lines you'll consider
-
diff_header_length = 3
-
-
# text_old and text_new should be the values of the string objects to compare
-
# these are just example strings to show it works
-
text_old = "line1\ndeleted line2\nline3\n\nline4\nline5"
-
text_new = "line1\ninserted line2\nline3\n\nline4\nline5"
-
-
# since we're using diff on the file system, we'll save the text we want to compare
-
# and then run diff against the two files
-
file_old_name = File.join(temporary_directory,"file_old"+rand(1000000).to_s)
-
file_new_name = File.join(temporary_directory,"file_new"+rand(1000000).to_s)
-
file_old = File.new(file_old_name, "w+")
-
file_new = File.new(file_new_name, "w+")
-
file_old.write(text_old+"\n")
-
file_new.write(text_new+"\n")
-
file_old.close
-
file_new.close
-
-
# diff will give provide a string showing insertions and deletions. We will
-
# split this string out by newlines if there are difference, and mark it up
-
# accordingly with html
-
lines = %x(diff --unified=#{max_lines} #{file_old_name} #{file_new_name})
-
if lines.empty?
-
lines = text_new.split(/\n/)
-
else
-
lines = lines.split(/\n/)[diff_header_length..max_lines].
-
collect do |i|
-
if i.empty?
-
""
-
else
-
case i[0,1]
-
when "+"; then "<ins>"+i[1..i.length-1]+"</ins>"
-
when "-"; then "<del>"+i[1..i.length-1]+"</del>"
-
else; i[1..i.length-1]
-
end
-
end
-
end
-
end
-
-
#clean up the temporary diff files we created
-
File.delete(file_new_name)
-
File.delete(file_old_name)
-
-
#return marked up text
-
lines.join("\n")
If you fire up RAILS_ROOT/script/console and paste that code in, it will return a nicely marked up string like this:
-
line1
-
<del>deleted line2</del>
-
<ins>inserted line2</ins>
-
line3
-
-
line4
-
line5
Use CSS to make your ins and del tags render however you like.