class Persist
Persists coverage records to a MessagePack database.
Definitions
def initialize(output, path = DEFAULT_PATH)
Initialize persistence for the given output and database path.
Signature
-
parameter
outputCovered::Base The output to wrap.
-
parameter
pathString The coverage database path.
Implementation
def initialize(output, path = DEFAULT_PATH)
super(output)
@path = self.expand_path(path)
end
def apply(record, ignore_mtime: false)
Apply a persisted record to the output.
Records with stale source modification times are ignored unless ignore_mtime is true.
Signature
-
parameter
recordHash The persisted coverage record.
-
parameter
ignore_mtimeBoolean Whether to apply records even if their source appears stale.
-
returns
Boolean Whether the record was applied.
Implementation
def apply(record, ignore_mtime: false)
if coverage = record[:coverage]
if path = record[:path]
path = self.expand_path(path)
coverage.path = path
end
if ignore_mtime || coverage.fresh?
add(coverage)
return true
end
end
return false
end
def serialize(coverage)
Convert coverage into a database record.
Signature
-
parameter
coverageCovered::Coverage The coverage object to serialize.
-
returns
Hash A MessagePack-compatible record.
Implementation
def serialize(coverage)
{
# We want to use relative paths so that moving the repo won't break everything:
pid: Process.pid,
path: relative_path(coverage.path),
# relative_path: relative_path(coverage.path),
coverage: coverage,
}
end
def load!(**options)
Load persisted coverage records into the output.
Signature
-
parameter
optionsHash Options forwarded to
Covered::Persist#apply.-
raises
LoadError If the database exists but cannot be decoded.
Implementation
def load!(**options)
return unless File.exist?(@path)
# Load existing coverage information and mark all files:
File.open(@path, "rb") do |file|
file.flock(File::LOCK_SH)
make_unpacker(file).each do |record|
# pp load: record
self.apply(record, **options)
end
end
rescue
raise LoadError, "Failed to load coverage from #{@path}, maybe old format or corrupt!"
end
def save!
Save all output coverage records to the database.
Implementation
def save!
# Dump all coverage:
File.open(@path, "ab") do |file|
file.flock(File::LOCK_EX)
packer = make_packer(file)
@output.each do |coverage|
# pp save: coverage
packer.write(serialize(coverage))
end
packer.flush
end
end
def finish
Finish the wrapped output and save the coverage database.
Implementation
def finish
super
self.save!
end
def each(&block)
Reload persisted coverage and enumerate the wrapped output.
Signature
-
yields
{|coverage| ...} Each coverage object from the reloaded output.
-
parameter
coverageCovered::Coverage The current coverage object.
-
parameter
-
returns
Enumerator | Nil An enumerator without a block.
Implementation
def each(&block)
return to_enum unless block_given?
@output.clear
self.load!
super
end
def make_factory
Build the MessagePack factory used for coverage records.
Signature
-
returns
MessagePack::Factory The configured MessagePack factory.
Implementation
def make_factory
factory = MessagePack::Factory.new
factory.register_type(0x00, Symbol)
factory.register_type(0x01, Time,
packer: MessagePack::Time::Packer,
unpacker: MessagePack::Time::Unpacker
)
factory.register_type(0x20, Source,
recursive: true,
packer: :serialize,
unpacker: :deserialize,
)
factory.register_type(0x21, Coverage,
recursive: true,
packer: :serialize,
unpacker: :deserialize,
)
return factory
end
def make_packer(io)
Build a MessagePack packer for the given IO.
Signature
-
parameter
ioIO The IO to write packed records to.
-
returns
MessagePack::Packer A packer configured for coverage records.
Implementation
def make_packer(io)
return make_factory.packer(io)
end
def make_unpacker(io)
Build a MessagePack unpacker for the given IO.
Signature
-
parameter
ioIO The IO to read packed records from.
-
returns
MessagePack::Unpacker An unpacker configured for coverage records.
Implementation
def make_unpacker(io)
return make_factory.unpacker(io)
end