Class: Covered::Persist

Inherits:
Wrapper show all
Defined in:
lib/covered/persist.rb

Overview

Persists coverage records to a MessagePack database.

Constant Summary collapse

DEFAULT_PATH =
".covered.db"

Instance Attribute Summary

Attributes inherited from Wrapper

#The wrapped output., #output

Instance Method Summary collapse

Methods inherited from Wrapper

#accept?, #add, #clear, #expand_path, #mark, #relative_path, #start, #to_h

Methods inherited from Base

#accept?, #add, #clear, #expand_path, #mark, #relative_path, #start

Constructor Details

#initialize(output, path = DEFAULT_PATH) ⇒ Persist

Initialize persistence for the given output and database path.



20
21
22
23
24
# File 'lib/covered/persist.rb', line 20

def initialize(output, path = DEFAULT_PATH)
	super(output)
	
	@path = self.expand_path(path)
end

Instance Method Details

#apply(record, ignore_mtime: false) ⇒ Object

Apply a persisted record to the output. Records with stale source modification times are ignored unless ‘ignore_mtime` is true.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/covered/persist.rb', line 31

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

#each(&block) ⇒ Object

Reload persisted coverage and enumerate the wrapped output.



107
108
109
110
111
112
113
114
# File 'lib/covered/persist.rb', line 107

def each(&block)
	return to_enum unless block_given?
	
	@output.clear
	self.load!
	
	super
end

#finishObject

Finish the wrapped output and save the coverage database.



97
98
99
100
101
# File 'lib/covered/persist.rb', line 97

def finish
	super
	
	self.save!
end

#load!(**options) ⇒ Object

Load persisted coverage records into the output.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/covered/persist.rb', line 63

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

#make_factoryObject

Build the MessagePack factory used for coverage records.



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/covered/persist.rb', line 118

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

#make_packer(io) ⇒ Object

Build a MessagePack packer for the given IO.



146
147
148
# File 'lib/covered/persist.rb', line 146

def make_packer(io)
	return make_factory.packer(io)
end

#make_unpacker(io) ⇒ Object

Build a MessagePack unpacker for the given IO.



153
154
155
# File 'lib/covered/persist.rb', line 153

def make_unpacker(io)
	return make_factory.unpacker(io)
end

#save!Object

Save all output coverage records to the database.



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/covered/persist.rb', line 80

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

#serialize(coverage) ⇒ Object

Convert coverage into a database record.



50
51
52
53
54
55
56
57
58
# File 'lib/covered/persist.rb', line 50

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