3
\$\begingroup\$

I've written a tool to store a files history more efficiently by only generating diffs and storing them whereas whole file would otherwise have to be stored. I would like to to hear about ways to improve readability and otherwise make this code more "ruby" or suggestions for test-cases to improve robustness. I also feel like the swap case could be expressed as a insertion/deletion combinations, but maybe it's more readable to leave it as is. Here is the Diff class:

class Diff
  attr_reader :a, :b, :diff

  def initialize(a,b)
    @a = a
    @b = b
    @diff = {} # {line_number => {type: +/-/~, content: text}}
    a = a.split("");
    b = b.split("");
    a_pos = 0
    b_pos = 0
    while (a+b).length > 0
      if a[0] == b[0]
        a.shift
        a_pos += 1
        b.shift
      else
        i = 1
        loop do
          if a[i] == b[0]
            @diff[a_pos] = {type: "-", content: a[0..i-1].join}
            a = a[i..-1]
            a_pos += a[0..i].length
            break
          elsif b[i] == a[0]
            @diff[a_pos] = {type: "+", content: b[0..i-1].join}
            b = b[i..-1]
            break
          elsif a[i] == b[i]
            a_pos += i
            @diff[a_pos] = {type: "~", content: [a.shift(i), b.shift(i)]}
          end
          i = i+1
          break if a[i] == b[0] || b[i] == a[0] || a[i]==b[i]
        end
      end
      if a.empty? && !b.empty?
        @diff[a_pos] = {type: "+", content: b.join}
        break
      elsif b.empty? && !a.empty?
        @diff[a_pos] = {type: "-", content: a.join}
        break
      end
    end
  end

  def self.apply(a, diff_hash)
    a = a.split("")
    Hash[diff_hash.diff.sort.reverse].each do |line, diff|
      case diff[:type]
      when "-"
        a = a[0..line-1]+a[line+diff[:content].length..-1]
      when "+"
        a.insert(line-1, diff[:content]) # line-2
      when "~"
        a = a[0..line-2]+a[line+diff[:content][0].length-1..-1]
        a.insert(line-1, diff[:content][1])
      end
    end
    return a.join
  end
end
\$\endgroup\$
  • \$\begingroup\$ test input and resulting output? for someone unfamiliar with this code its near impossible to understand....more explanation would help immensely \$\endgroup\$ – BKSpurgeon Apr 13 '17 at 23:24
  • \$\begingroup\$ Does this work? These lines a = a[i..-1]; a_pos += a[0..i].length look awfully suspicious to me. \$\endgroup\$ – David Stanley Apr 18 '17 at 16:15
  • \$\begingroup\$ There's something wrong with the code. Trying Diff.new("dddddabc", "abc") goes into an infinite loop. \$\endgroup\$ – David Stanley Apr 19 '17 at 0:45
  • \$\begingroup\$ Curiousity question: is there a reason you didn't start with an existing diff gem like diffy? \$\endgroup\$ – Mark Thomas Apr 27 '17 at 1:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.