class Fned::EditList
Public Class Methods
new(options = {})
click to toggle source
# File lib/fned/edit_list.rb, line 37 def initialize(options = {}) @options = { :separator => ' ', }.merge(options) # Do not use characters in lower and upper case, the line numbers # are case insensitive. @digits = ('0'..'9').to_a + ('A'..'Z').to_a @digits_upcase = @digits.map { |s| s.upcase } @escape = { "\r" => "\\r", "\n" => "\\n", "\\" => "\\\\", } end
Public Instance Methods
bin_dup(str)
click to toggle source
return dup of string with binary encoding
# File lib/fned/edit_list.rb, line 174 def bin_dup(str) str = str.to_s.dup str.force_encoding "binary" str end
edit(items, comments)
click to toggle source
start editor to edit items, returns new list of items
# File lib/fned/edit_list.rb, line 181 def edit(items, comments) # Ensure all strings are in binary encoding as filenames may have # invalid encodings. items = items.map { |item| bin_dup(item) } comments = comments.map { |comment| bin_dup(comment) if comment } Tempfile.open(File.basename($0), :encoding => "binary") do |fh| write_file(fh, items, comments) fh.close begin # TODO: return code of editor meaningful? system(editor, fh.path) File.open(fh.path, "r", :encoding => "binary") do |io| return read_file(io, items.length) end rescue InvalidLine => e warn e.message if retry? retry else raise UserAbort end end end end
editor()
click to toggle source
editor to run from environment or
# File lib/fned/edit_list.rb, line 55 def editor # TODO: check for existence of editor, vim, emacs? ENV["VISUAL"] || ENV["EDITOR"] || "vi" end
escape(str)
click to toggle source
escape string using @escape
# File lib/fned/edit_list.rb, line 67 def escape(str) replace(@escape, str) end
number_decode(str)
click to toggle source
decode number using @digits
# File lib/fned/edit_list.rb, line 90 def number_decode(str) str.upcase.chars.map do |char| n = @digits_upcase.index(char) return nil unless n n end.inject(0) do |m, e| m * @digits.length + e end end
number_encode(n, padding = 1)
click to toggle source
encode number using @digits, padding is minimum number of digits
# File lib/fned/edit_list.rb, line 77 def number_encode(n, padding = 1) result = [] raise ArgumentError if n < 0 raise ArgumentError if padding < 1 until n == 0 n, k = n.divmod(@digits.length) result << @digits[k] end result.fill(@digits[0], result.length, padding - result.length) result.reverse.join end
read_file(io, count)
click to toggle source
read from io and parse content
# File lib/fned/edit_list.rb, line 124 def read_file(io, count) @result = Array.new(count) line_number = 0 io.each do |line| line_number += 1 line = line.chomp if line =~ /\A\s*(?:#|\z)/ next end key, value = line.split(@options[:separator], 2) index = number_decode(key) value = unescape(value.to_s) if index.nil? raise InvalidLine.new("index #{key.inspect} contains invalid " + "characters", line_number) end if index >= count raise InvalidLine.new("index #{key.inspect} too large", line_number) end if @result[index] raise InvalidLine.new("index #{key.inspect} used multiple times", line_number) end if value.empty? raise InvalidLine.new("value for #{key.inspect} empty", line_number) end @result[index] = value end @result end
replace(replacements, str)
click to toggle source
replace according to a hash
# File lib/fned/edit_list.rb, line 61 def replace(replacements, str) r = Regexp.new(replacements.keys.map { |s| Regexp.quote(s) }.join("|")) str.gsub(r) { |s| replacements[s] } end
retry?()
click to toggle source
ask user for retry
# File lib/fned/edit_list.rb, line 160 def retry? loop do $stderr.print "Edit / Abort? [Ea] " $stderr.flush case $stdin.readline.strip when "", /\Ae/i return true when /\Aa/i return false end end end
unescape(str)
click to toggle source
unescape string using @escape
# File lib/fned/edit_list.rb, line 72 def unescape(str) replace(@escape.invert, str) end
write_comment(io, comments)
click to toggle source
write comment to io
# File lib/fned/edit_list.rb, line 101 def write_comment(io, comments) comments.lines.map(&:chomp).each do |s| io.puts "# #{escape(s.to_s)}" end end
write_file(io, items, comments)
click to toggle source
write items and comments to io
# File lib/fned/edit_list.rb, line 114 def write_file(io, items, comments) padding = number_encode([items.length - 1, 0].max).length items.each_with_index do |item, index| write_comment(io, comments[index]) if comments[index] write_item(io, index, padding, item) end write_comment(io, comments[items.length]) if comments[items.length] end
write_item(io, index, padding, str)
click to toggle source
write number and item to io
# File lib/fned/edit_list.rb, line 108 def write_item(io, index, padding, str) io.puts number_encode(index, padding) + @options[:separator] + escape(str.to_s) end