zhu2688
7/22/2016 - 9:22 AM

tail in ruby

tail in ruby

class Tail
  class << self
    def tail(path, n)
      file = File.open(path, "r")
      buffer_s = 512
      line_count = 0
      file.seek(0, IO::SEEK_END)

      offset = file.pos # we start at the end

      while line_count <= n && offset > 0
        to_read = if (offset - buffer_s) < 0
                    offset
                  else
                    buffer_s
                  end

        file.seek(offset-to_read)
        data = file.read(to_read)

        data.reverse.each_char do |c|
          if line_count > n
            offset += 1
            break
          end
          offset -= 1
          if c == "\n"
            line_count += 1
          end
        end
      end

      file.seek(offset)
      data = file.read
    end
  end
end


require "minitest/autorun"

class TestTail < Minitest::Test

  TEST_PATH = "/tmp/test-tail"

  def setup
    @file = File.open(TEST_PATH, "w")
  end

  def text_from(i,j)
    out = ""
    i.upto(j) do |x|
      out << "line #{x}\n"
    end
    out
  end

  def test_empty_file
    @file.write("") && @file.close
    assert_equal "", Tail.tail(TEST_PATH, 10)
  end

  def test_smaller_line_count
    @file.write(text_from(0,3)) && @file.close
    assert_equal text_from(0,3), Tail.tail(TEST_PATH, 10)
  end

  def test_same_line_count
    @file.write(text_from(0,9)) && @file.close
    assert_equal text_from(0,9), Tail.tail(TEST_PATH, 10)
  end

  def test_larger_line_count
    @file.write(text_from(0,12)) && @file.close
    assert_equal text_from(3,12), Tail.tail(TEST_PATH, 10)
  end

  def test_very_long_lines
    long_line = ("apples and oranges" * 1000) + "\n"
    bunch_of_long_lines = long_line * 12

    @file.write(bunch_of_long_lines) && @file.close
    assert_equal (long_line * 10), Tail.tail(TEST_PATH, 10)
  end

end