Posts Tagged ‘ins’

Ruby on Rails Diff Text to HTML <ins> and <del>

Tuesday, November 4th, 2008

This 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.

  1. #set up some variables to reference later
  2. temporary_directory = File.join(Rails.root, "tmp")
  3. max_lines = 9999999 #needs to be larger than the most lines you'll consider
  4. diff_header_length = 3
  5.  
  6. # text_old and text_new should be the values of the string objects to compare
  7. # these are just example strings to show it works
  8. text_old      = "line1\ndeleted line2\nline3\n\nline4\nline5"
  9. text_new      = "line1\ninserted line2\nline3\n\nline4\nline5"
  10.  
  11. # since we're using diff on the file system, we'll save the text we want to compare
  12. # and then run diff against the two files
  13. file_old_name = File.join(temporary_directory,"file_old"+rand(1000000).to_s)
  14. file_new_name = File.join(temporary_directory,"file_new"+rand(1000000).to_s)
  15. file_old      = File.new(file_old_name, "w+")
  16. file_new      = File.new(file_new_name, "w+")
  17. file_old.write(text_old+"\n")
  18. file_new.write(text_new+"\n")
  19. file_old.close
  20. file_new.close
  21.  
  22. # diff will give provide a string showing insertions and deletions.  We will
  23. # split this string out by newlines if there are difference, and mark it up
  24. # accordingly with html
  25. lines = %x(diff -­-­­­­­­unified=#{max_lines} #{file_old_name} #{file_new_name})
  26. if lines.empty?
  27.   lines = text_new.split(/\n/)
  28. else
  29.   lines = lines.split(/\n/)[diff_header_length..max_lines].
  30.   collect do |i|
  31.     if i.empty?  
  32.       ""
  33.     else
  34.       case i[0,1]
  35.       when "+"; then "<ins>"+i[1..i.length-1]+"</ins>"
  36.       when "-"; then "<del>"+i[1..i.length-1]+"</del>"
  37.       else; i[1..i.length-1]
  38.       end
  39.     end
  40.   end
  41. end
  42.  
  43. #clean up the temporary diff files we created
  44. File.delete(file_new_name)
  45. File.delete(file_old_name)
  46.  
  47. #return marked up text
  48. 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:

  1. line1
  2. <del>deleted line2</del>
  3. <ins>inserted line2</ins>
  4. line3
  5.  
  6. line4
  7. line5

Use CSS to make your ins and del tags render however you like.