2007-03-20 18:25:19 +00:00
|
|
|
#
|
|
|
|
# A file with general support functions used by most files in the project.
|
|
|
|
#
|
2009-05-05 20:31:41 +00:00
|
|
|
# These are the only methods added to other classes.
|
|
|
|
#
|
2007-03-20 18:25:19 +00:00
|
|
|
|
|
|
|
require 'logger'
|
2009-05-05 20:31:41 +00:00
|
|
|
require 'stringio'
|
|
|
|
require 'enumerator'
|
|
|
|
|
|
|
|
class String # :nodoc:
|
|
|
|
# plural of String#index. returns all offsets of +string+. rename to indices?
|
|
|
|
#
|
|
|
|
# note that it doesn't check for overlapping values.
|
|
|
|
def indexes string
|
|
|
|
# in some ways i'm surprised that $~ works properly in this case...
|
|
|
|
to_enum(:scan, /#{Regexp.quote string}/m).map { $~.begin 0 }
|
|
|
|
end
|
|
|
|
|
|
|
|
def each_chunk size
|
|
|
|
(length / size.to_f).ceil.times { |i| yield self[i * size, size] }
|
|
|
|
end
|
|
|
|
end
|
2007-03-20 18:25:19 +00:00
|
|
|
|
|
|
|
class File # :nodoc:
|
2009-05-05 20:31:41 +00:00
|
|
|
# for interface consistency with StringIO etc (rather than adding #stat
|
|
|
|
# to them). used by RangesIO.
|
2007-03-20 18:25:19 +00:00
|
|
|
def size
|
|
|
|
stat.size
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Symbol # :nodoc:
|
2009-05-05 20:31:41 +00:00
|
|
|
unless :x.respond_to? :to_proc
|
|
|
|
def to_proc
|
|
|
|
proc { |a| a.send self }
|
|
|
|
end
|
2007-03-20 18:25:19 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module Enumerable # :nodoc:
|
2009-05-05 20:31:41 +00:00
|
|
|
unless [].respond_to? :group_by
|
|
|
|
# 1.9 backport
|
|
|
|
def group_by
|
|
|
|
hash = Hash.new { |h, key| h[key] = [] }
|
|
|
|
each { |item| hash[yield(item)] << item }
|
|
|
|
hash
|
|
|
|
end
|
2007-03-20 18:25:19 +00:00
|
|
|
end
|
|
|
|
|
2009-05-05 20:31:41 +00:00
|
|
|
unless [].respond_to? :sum
|
|
|
|
def sum initial=0
|
|
|
|
inject(initial) { |a, b| a + b }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# move to support?
|
|
|
|
class IO # :nodoc:
|
|
|
|
# Copy data from IO-like object +src+, to +dst+
|
|
|
|
def self.copy src, dst
|
|
|
|
until src.eof?
|
|
|
|
buf = src.read(4096)
|
|
|
|
dst.write buf
|
|
|
|
end
|
2007-03-20 18:25:19 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Logger # :nodoc:
|
2009-05-05 20:31:41 +00:00
|
|
|
# A helper method for creating a +Logger+ which produce call stack
|
2007-03-20 18:25:19 +00:00
|
|
|
# in their output
|
|
|
|
def self.new_with_callstack logdev=STDERR
|
2009-05-05 21:34:51 +00:00
|
|
|
log = Logger.new
|
2007-03-20 18:25:19 +00:00
|
|
|
log.level = WARN
|
|
|
|
log.formatter = proc do |severity, time, progname, msg|
|
|
|
|
# find where we were called from, in our code
|
|
|
|
callstack = caller.dup
|
|
|
|
callstack.shift while callstack.first =~ /\/logger\.rb:\d+:in/
|
|
|
|
from = callstack.first.sub(/:in `(.*?)'/, ":\\1")
|
|
|
|
"[%s %s]\n%-7s%s\n" % [time.strftime('%H:%M:%S'), from, severity, msg.to_s]
|
|
|
|
end
|
|
|
|
log
|
|
|
|
end
|
2009-05-05 20:31:41 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# Include this module into a class that defines #each_child. It should
|
|
|
|
# maybe use #each instead, but its easier to be more specific, and use
|
|
|
|
# an alias.
|
|
|
|
#
|
|
|
|
# I don't want to force the class to cache children (eg where children
|
|
|
|
# are loaded on request in pst), because that forces the whole tree to
|
|
|
|
# be loaded. So, the methods should only call #each_child once, and
|
|
|
|
# breadth first iteration holds its own copy of the children around.
|
|
|
|
#
|
|
|
|
# Main methods are #recursive, and #to_tree
|
|
|
|
module RecursivelyEnumerable # :nodoc:
|
|
|
|
def each_recursive_depth_first(&block)
|
|
|
|
each_child do |child|
|
|
|
|
yield child
|
|
|
|
if child.respond_to? :each_recursive_depth_first
|
|
|
|
child.each_recursive_depth_first(&block)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# don't think this is actually a proper breadth first recursion. only first
|
|
|
|
# level is breadth first.
|
|
|
|
def each_recursive_breadth_first(&block)
|
|
|
|
children = []
|
|
|
|
each_child do |child|
|
|
|
|
children << child if child.respond_to? :each_recursive_breadth_first
|
|
|
|
yield child
|
|
|
|
end
|
|
|
|
children.each { |child| child.each_recursive_breadth_first(&block) }
|
|
|
|
end
|
|
|
|
|
|
|
|
def each_recursive mode=:depth_first, &block
|
|
|
|
# we always actually yield ourself (the tree root) before recursing
|
|
|
|
yield self
|
|
|
|
send "each_recursive_#{mode}", &block
|
|
|
|
end
|
|
|
|
|
|
|
|
# the idea of this function, is to allow use of regular Enumerable methods
|
|
|
|
# in a recursive fashion. eg:
|
|
|
|
#
|
|
|
|
# # just looks at top level children
|
|
|
|
# root.find { |child| child.some_condition? }
|
|
|
|
# # recurse into all children getting non-folders, breadth first
|
|
|
|
# root.recursive(:breadth_first).select { |child| !child.folder? }
|
|
|
|
# # just get everything
|
|
|
|
# items = root.recursive.to_a
|
|
|
|
#
|
|
|
|
def recursive mode=:depth_first
|
|
|
|
to_enum(:each_recursive, mode)
|
|
|
|
end
|
|
|
|
|
|
|
|
# streams a "tree" form of the recursively enumerable structure to +io+, or
|
|
|
|
# return a string form instead if +io+ is not specified.
|
|
|
|
#
|
|
|
|
# mostly a debugging aid. can specify a different block which will be called
|
|
|
|
# to provide the string form for each node.
|
|
|
|
def to_tree io='', &inspect
|
|
|
|
inspect ||= :inspect.to_proc
|
|
|
|
io << "- #{inspect[self]}\n"
|
|
|
|
recurse = proc do |node, prefix|
|
|
|
|
child = nil
|
|
|
|
node.each_child do |next_child|
|
|
|
|
if child
|
|
|
|
io << "#{prefix}|- #{inspect[child]}\n"
|
|
|
|
recurse.call child, prefix + '| '
|
|
|
|
end
|
|
|
|
child = next_child
|
|
|
|
end if node.respond_to?(:each_child)
|
|
|
|
if child
|
|
|
|
io << "#{prefix}\\- #{inspect[child]}\n"
|
|
|
|
recurse.call child, prefix + ' '
|
|
|
|
end
|
|
|
|
end
|
|
|
|
recurse.call self, ' '
|
|
|
|
io
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# can include File::Constants
|
|
|
|
class IO
|
|
|
|
# this is for jruby
|
|
|
|
include File::Constants unless defined?(RDONLY)
|
|
|
|
|
|
|
|
# nabbed from rubinius, and modified
|
|
|
|
def self.parse_mode mode
|
|
|
|
ret = 0
|
|
|
|
|
|
|
|
case mode[0, 1]
|
|
|
|
when 'r'; ret |= RDONLY
|
|
|
|
when 'w'; ret |= WRONLY | CREAT | TRUNC
|
|
|
|
when 'a'; ret |= WRONLY | CREAT | APPEND
|
|
|
|
else raise ArgumentError, "illegal access mode #{mode}"
|
|
|
|
end
|
|
|
|
|
|
|
|
(1...mode.length).each do |i|
|
|
|
|
case mode[i, 1]
|
|
|
|
when '+'; ret = (ret & ~(RDONLY | WRONLY)) | RDWR
|
|
|
|
when 'b'; ret |= Mode::BINARY
|
|
|
|
else raise ArgumentError, "illegal access mode #{mode}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
ret
|
|
|
|
end
|
|
|
|
|
|
|
|
class Mode
|
|
|
|
# ruby 1.9 defines binary as 0, which isn't very helpful.
|
|
|
|
# its 4 in rubinius. no longer using
|
|
|
|
#
|
|
|
|
# BINARY = 0x4 unless defined?(BINARY)
|
|
|
|
#
|
|
|
|
# for that reason, have my own constants module here
|
|
|
|
module Constants
|
|
|
|
include File::Constants
|
|
|
|
BINARY = 0x4
|
|
|
|
end
|
|
|
|
|
|
|
|
include Constants
|
|
|
|
NAMES = %w[rdonly wronly rdwr creat trunc append binary]
|
|
|
|
|
|
|
|
attr_reader :flags
|
|
|
|
def initialize flags
|
|
|
|
flags = IO.parse_mode flags.to_str if flags.respond_to? :to_str
|
|
|
|
raise ArgumentError, "invalid flags - #{flags.inspect}" unless Fixnum === flags
|
|
|
|
@flags = flags
|
|
|
|
end
|
|
|
|
|
|
|
|
def writeable?
|
|
|
|
#(@flags & RDONLY) == 0
|
|
|
|
(@flags & 0x3) != RDONLY
|
|
|
|
end
|
|
|
|
|
|
|
|
def readable?
|
|
|
|
(@flags & WRONLY) == 0
|
|
|
|
end
|
|
|
|
|
|
|
|
def truncate?
|
|
|
|
(@flags & TRUNC) != 0
|
|
|
|
end
|
|
|
|
|
|
|
|
def append?
|
|
|
|
(@flags & APPEND) != 0
|
|
|
|
end
|
|
|
|
|
|
|
|
def create?
|
|
|
|
(@flags & CREAT) != 0
|
|
|
|
end
|
|
|
|
|
|
|
|
def binary?
|
|
|
|
(@flags & BINARY) != 0
|
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
# revisit this
|
|
|
|
def apply io
|
|
|
|
if truncate?
|
|
|
|
io.truncate 0
|
|
|
|
elsif append?
|
|
|
|
io.seek IO::SEEK_END, 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
=end
|
|
|
|
|
|
|
|
def inspect
|
|
|
|
names = NAMES.map { |name| name if (flags & Mode.const_get(name.upcase)) != 0 }
|
|
|
|
names.unshift 'rdonly' if (flags & 0x3) == 0
|
|
|
|
"#<#{self.class} #{names.compact * '|'}>"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|