Class: File

Inherits:
Object
  • Object
show all
Defined in:
lib/upm/core_ext.rb,
lib/upm/core_ext/file.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.which(*bins) ⇒ Object

Overly clever which(), which returns an array if more than one argument was supplied, or string/nil if only one argument was supplied.



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/upm/core_ext.rb', line 13

def self.which(*bins)
  results = []
  bins    = bins.flatten
  paths   = ENV["PATH"].split(":").map { |path| File.realpath(path) rescue nil }.compact.uniq

  paths.each do |dir|
    bins.each do |bin|

      full_path = File.join(dir, bin)
      
      if File.exists?(full_path)
        if bins.size == 1
          return full_path
        else 
          results << full_path 
        end
      end

    end
  end

  bins.size == 1 ? nil : results
end

.which_is_best?(*bins) ⇒ Boolean

Returns:

  • (Boolean)


37
38
39
40
# File 'lib/upm/core_ext.rb', line 37

def self.which_is_best?(*bins)
  result = which(*bins.flatten)
  result.is_a?(Array) ? result.first : result
end

Instance Method Details

#each_line_with_offsetObject

Iterate over each line of the file, yielding the line and the byte offset of the start of the line in the file



148
149
150
151
152
153
154
155
156
157
# File 'lib/upm/core_ext/file.rb', line 148

def each_line_with_offset
  return to_enum(:each_line_with_offset) unless block_given?

  offset = 0

  each_line do |line|
    yield line, offset
    offset = tell
  end
end

#reverse_each(&block) ⇒ Object Also known as: reverse_each_line

A streaming ‘reverse_each` implementation. (For large files, it’s faster and uses less memory.)



11
12
13
14
15
16
# File 'lib/upm/core_ext/file.rb', line 11

def reverse_each(&block)
  return to_enum(:reverse_each) unless block_given?

  seek_end
  reverse_each_from_current_pos(&block)
end

#reverse_each_from_current_pos {|fragment| ... } ⇒ Object

Read each line of file backwards (from the current position.)

Yields:

  • (fragment)


53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/upm/core_ext/file.rb', line 53

def reverse_each_from_current_pos
  return to_enum(:reverse_each_from_current_pos) unless block_given?

  # read the rest of the current line, in case we started in the middle of a line
  start_pos = pos
  fragment = readline rescue ""
  seek(start_pos)

  while data = reverse_read(4096)
    lines = data.each_line.to_a
    lines.last << fragment unless lines.last[-1] == "\n"

    fragment = lines.first

    lines[1..-1].reverse_each { |line| yield line }
  end

  yield fragment
end

#reverse_read(length, block_aligned = false) ⇒ Object

Read the previous ‘length` bytes. After the read, `pos` will be at the beginning of the region that you just read. Returns `nil` when the beginning of the file is reached.

If the ‘block_aligned` argument is `true`, reads will always be aligned to file positions which are multiples of 512 bytes. (This should increase performance slightly.)



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/upm/core_ext/file.rb', line 26

def reverse_read(length, block_aligned=false)
  raise "length must be a multiple of 512" if block_aligned and length % 512 != 0

  end_pos = pos
  return nil if end_pos == 0

  if block_aligned
    misalignment = end_pos % length
    length      += misalignment
  end

  if length >= end_pos # this read will take us to the beginning of the file
    seek(0)
  else
    seek(-length, IO::SEEK_CUR)
  end

  start_pos = pos
  data      = read(end_pos - start_pos)
  seek(start_pos)

  data
end

#reverse_readlineObject

Read the previous line (leaving ‘pos` at the beginning of the string that was read.)

Raises:



90
91
92
93
94
95
96
97
98
# File 'lib/upm/core_ext/file.rb', line 90

def reverse_readline
  raise BOFError.new("beginning of file reached") if pos == 0

  seek_backwards_to("\n", 512, -2)
  new_pos = pos
  data = readline
  seek(new_pos)
  data
end

#seek_backwards_to(string, blocksize = 512, rindex_end = -1)) ⇒ Object Also known as: reverse_seek_to

Scan backwards in the file until ‘string` is found, and set the IO’s pos to the first character after the matched string.



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/upm/core_ext/file.rb', line 125

def seek_backwards_to(string, blocksize=512, rindex_end=-1)
  raise "Error: blocksize must be at least as large as the string" if blocksize < string.size

  loop do
    data = reverse_read(blocksize)

    if index = data.rindex(string, rindex_end)
      seek(index+string.size, IO::SEEK_CUR)
      break
    elsif pos == 0
      return nil
    else
      seek(string.size - 1, IO::SEEK_CUR)
    end
  end

  pos
end

#seek_endObject

Seek to ‘EOF`



76
77
78
# File 'lib/upm/core_ext/file.rb', line 76

def seek_end
  seek(0, IO::SEEK_END)
end

#seek_startObject

Seek to ‘BOF`



83
84
85
# File 'lib/upm/core_ext/file.rb', line 83

def seek_start
  seek(0)
end

#seek_to(string, blocksize = 512) ⇒ Object

Scan through the file until ‘string` is found, and set the IO’s pos to the first character of the matched string.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/upm/core_ext/file.rb', line 103

def seek_to(string, blocksize=512)
  raise "Error: blocksize must be at least as large as the string" if blocksize < string.size

  loop do
    data = read(blocksize)

    if index = data.index(string)
      seek(-(data.size - index), IO::SEEK_CUR)
      break
    elsif eof?
      return nil
    else
      seek(-(string.size - 1), IO::SEEK_CUR)
    end
  end

  pos
end