lib/tracksperanto/accumulator.rb in tracksperanto-2.2.4 vs lib/tracksperanto/accumulator.rb in tracksperanto-2.3.0
- old
+ new
@@ -15,58 +15,89 @@
#
# a.clear # ensure that the file is deleted
class Tracksperanto::Accumulator
include Enumerable
+ DELIM = "\n"
+
# Returns the number of objects stored so far
attr_reader :size
def initialize
@store = Tracksperanto::BufferIO.new
-
@size = 0
- @byte_size = 0
super
end
+ def empty?
+ @size.zero?
+ end
+
# Store an object
def push(object_to_store)
- @store.seek(@byte_size)
-
blob = marshal_object(object_to_store)
@store.write(blob)
@size += 1
- @byte_size = @byte_size + blob.size
object_to_store
end
alias_method :<<, :push
# Retreive each stored object in succession. All other Enumerable
# methods are also available (but be careful with Enumerable#map and to_a)
def each
- @store.rewind
- @size.times { yield(recover_object) }
+ with_separate_read_io do | iterable |
+ @size.times { yield(recover_object_from(iterable)) }
+ end
end
# Calls close! on the datastore and deletes the objects in it
def clear
@store.close!
@size = 0
- @offsets = []
end
+ # Retreive a concrete object at index
+ def [](idx)
+ idx.respond_to?(:each) ? idx.map{|i| recover_at(i) } : recover_at(idx)
+ end
+
private
+ def recover_at(idx)
+ with_separate_read_io do | iterable |
+ iterable.seek(0)
+
+ # Do not unmarshal anything but wind the IO in fixed offsets
+ idx.times do
+ skip_bytes = iterable.gets("\t").to_i
+ iterable.seek(iterable.pos + skip_bytes)
+ end
+
+ recover_object_from(iterable)
+ end
+ end
+
+ # We first ensure that we have a disk-backed file, then reopen it as read-only
+ # and iterate through that (we will have one IO handle per loop nest)
+ def with_separate_read_io
+ iterable = File.open(@store.to_file.path, "r")
+ yield(iterable)
+ ensure
+ iterable.close
+ end
+
def marshal_object(object_to_store)
d = Marshal.dump(object_to_store)
- blob = [d.size, "\t", d, "\n"].join
+ blob = [d.size, "\t", d, DELIM].join
end
- def recover_object
+ def recover_object_from(io)
# Up to the tab is the amount of bytes to read
- demarshal_bytes = @store.gets("\t").strip.to_i
- Marshal.load(@store.read(demarshal_bytes))
+ demarshal_bytes = io.gets("\t").to_i
+ blob = io.read(demarshal_bytes)
+
+ Marshal.load(blob)
end
end