aboutsummaryrefslogtreecommitdiffstats
path: root/docs/customising/index.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/customising/index.md')
0 files changed, 0 insertions, 0 deletions
'>63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
# 
# A file with general support functions used by most files in the project.
# 
# These are the only methods added to other classes.
# 

require 'logger'
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

class File # :nodoc:
	# for interface consistency with StringIO etc (rather than adding #stat
	# to them). used by RangesIO.
	def size
		stat.size
	end
end

class Symbol # :nodoc:
	unless :x.respond_to? :to_proc
		def to_proc
			proc { |a| a.send self }
		end
	end
end

module Enumerable # :nodoc:
	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
	end

	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
	end
end

class Logger # :nodoc:
	# A helper method for creating a +Logger+ which produce call stack
	# in their output
	def self.new_with_callstack logdev=STDERR
		log = Logger.new logdev
		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
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