covered 0.28.1 → 0.28.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/covered/files.rb CHANGED
@@ -9,35 +9,57 @@ require_relative "wrapper"
9
9
  require "set"
10
10
 
11
11
  module Covered
12
+ # Collects coverage information keyed by source path.
12
13
  class Files < Base
14
+ # Initialize an empty coverage collection.
13
15
  def initialize(*)
14
16
  super
15
17
 
16
18
  @paths = {}
17
19
  end
18
20
 
21
+ # @attribute [Hash(String, Covered::Coverage)] Coverage indexed by expanded source path.
19
22
  attr_accessor :paths
20
23
 
24
+ # Get or create coverage for the given path.
25
+ # @parameter path [String] The source path.
26
+ # @returns [Covered::Coverage] The coverage object for the path.
21
27
  def [](path)
22
28
  @paths[path] ||= Coverage.for(path)
23
29
  end
24
30
 
31
+ # Whether there are no tracked paths.
32
+ # @returns [Boolean] Whether no coverage paths are tracked.
25
33
  def empty?
26
34
  @paths.empty?
27
35
  end
28
36
 
37
+ # Mark a line in the given path as executed.
38
+ # @parameter path [String] The source path.
39
+ # @parameter line_number [Integer] The line number to mark.
40
+ # @parameter value [Integer | Array(Integer)] The execution count or counts to add.
29
41
  def mark(path, line_number, value)
30
42
  self[path].mark(line_number, value)
31
43
  end
32
44
 
45
+ # Add an annotation to a line in the given path.
46
+ # @parameter path [String] The source path.
47
+ # @parameter line_number [Integer] The line number to annotate.
48
+ # @parameter value [String] The annotation text.
33
49
  def annotate(path, line_number, value)
34
50
  self[path].annotate(line_number, value)
35
51
  end
36
52
 
53
+ # Merge coverage for the given path into this collection.
54
+ # @parameter coverage [Covered::Coverage] The coverage object to merge.
37
55
  def add(coverage)
38
56
  self[coverage.path].merge!(coverage)
39
57
  end
40
58
 
59
+ # Enumerate tracked coverage objects.
60
+ # @yields {|coverage| ...} Each tracked coverage object.
61
+ # @parameter coverage [Covered::Coverage] The current coverage object.
62
+ # @returns [Enumerator | Nil] An enumerator without a block.
41
63
  def each
42
64
  return to_enum unless block_given?
43
65
 
@@ -46,12 +68,19 @@ module Covered
46
68
  end
47
69
  end
48
70
 
71
+ # Remove all tracked coverage data.
72
+ # @returns [Hash] The cleared path map.
49
73
  def clear
50
74
  @paths.clear
51
75
  end
52
76
  end
53
77
 
78
+ # Includes coverage for files matching a glob pattern.
54
79
  class Include < Wrapper
80
+ # Initialize an include filter for the given glob pattern.
81
+ # @parameter output [Covered::Base] The output to wrap.
82
+ # @parameter pattern [String] The glob pattern to include.
83
+ # @parameter base [String] The base path used to expand the glob.
55
84
  def initialize(output, pattern, base = "")
56
85
  super(output)
57
86
 
@@ -59,8 +88,11 @@ module Covered
59
88
  @base = base
60
89
  end
61
90
 
91
+ # @attribute [String] The glob pattern to include.
62
92
  attr :pattern
63
93
 
94
+ # Resolve the include pattern to real file paths.
95
+ # @returns [Set(String)] The real paths matched by the include pattern.
64
96
  def glob
65
97
  paths = Set.new
66
98
  root = self.expand_path(@base)
@@ -75,6 +107,9 @@ module Covered
75
107
  return paths
76
108
  end
77
109
 
110
+ # Enumerate existing coverage and synthesize empty coverage for unmatched included files.
111
+ # @yields {|coverage| ...} Each existing or synthesized coverage object.
112
+ # @parameter coverage [Covered::Coverage] The current coverage object.
78
113
  def each(&block)
79
114
  paths = glob
80
115
 
@@ -90,17 +125,22 @@ module Covered
90
125
  end
91
126
  end
92
127
 
128
+ # Excludes coverage for paths matching a pattern.
93
129
  class Skip < Filter
130
+ # Initialize a skip filter for the given pattern.
131
+ # @parameter output [Covered::Base] The output to wrap.
132
+ # @parameter pattern [Regexp] The pattern to exclude.
94
133
  def initialize(output, pattern)
95
134
  super(output)
96
135
 
97
136
  @pattern = pattern
98
137
  end
99
138
 
139
+ # @attribute [Regexp] The pattern to exclude.
100
140
  attr :pattern
101
141
 
102
142
  if Regexp.instance_methods.include? :match?
103
- # This is better as it doesn't allocate a MatchData instance which is essentially useless.
143
+ # This is better as it doesn't allocate a MatchData instance which is essentially useless:
104
144
  def match? path
105
145
  !@pattern.match?(path)
106
146
  end
@@ -111,33 +151,52 @@ module Covered
111
151
  end
112
152
  end
113
153
 
154
+ # Only includes coverage for paths matching a pattern.
114
155
  class Only < Filter
156
+ # Initialize an only filter for the given pattern.
157
+ # @parameter output [Covered::Base] The output to wrap.
158
+ # @parameter pattern [Object] The pattern matched with `===`.
115
159
  def initialize(output, pattern)
116
160
  super(output)
117
161
 
118
162
  @pattern = pattern
119
163
  end
120
164
 
165
+ # @attribute [Object] The pattern matched with `===`.
121
166
  attr :pattern
122
167
 
168
+ # Whether the given path matches the only pattern.
169
+ # @parameter path [String] The source path.
170
+ # @returns [Boolean] Whether the path matches the pattern.
123
171
  def match?(path)
124
172
  @pattern === path
125
173
  end
126
174
  end
127
175
 
176
+ # Restricts coverage to a project root and converts paths relative to it.
128
177
  class Root < Filter
178
+ # Initialize a root filter for the given path.
179
+ # @parameter output [Covered::Base] The output to wrap.
180
+ # @parameter path [String] The root path.
129
181
  def initialize(output, path)
130
182
  super(output)
131
183
 
132
184
  @path = path
133
185
  end
134
186
 
187
+ # @attribute [String] The root path.
135
188
  attr :path
136
189
 
190
+ # Expand a path relative to this root.
191
+ # @parameter path [String] The path to expand.
192
+ # @returns [String] The expanded path.
137
193
  def expand_path(path)
138
194
  File.expand_path(super, @path)
139
195
  end
140
196
 
197
+ # Convert a path under this root to a relative path.
198
+ # @parameter path [String] The path to relativize.
199
+ # @returns [String] The relative path when under this root, otherwise the wrapped output result.
141
200
  def relative_path(path)
142
201
  if path.start_with?(@path)
143
202
  path.slice(@path.size+1, path.size)
@@ -146,6 +205,9 @@ module Covered
146
205
  end
147
206
  end
148
207
 
208
+ # Whether the given path is under this root.
209
+ # @parameter path [String] The source path.
210
+ # @returns [Boolean] Whether the path starts with this root.
149
211
  def match?(path)
150
212
  path.start_with?(@path)
151
213
  end
data/lib/covered/forks.rb CHANGED
@@ -6,25 +6,33 @@
6
6
  require_relative "wrapper"
7
7
 
8
8
  module Covered
9
+ # Tracks coverage across forked child processes.
9
10
  class Forks < Wrapper
11
+ # Start tracking coverage and install the fork handler.
10
12
  def start
11
13
  super
12
14
 
13
15
  Handler.start(self)
14
16
  end
15
17
 
18
+ # Stop tracking coverage and remove the fork handler state.
16
19
  def finish
17
20
  Handler.finish
18
21
 
19
22
  super
20
23
  end
21
24
 
25
+ # Handles `Process.fork` so child processes record coverage independently.
22
26
  module Handler
23
27
  LOCK = Mutex.new
24
28
 
25
29
  class << self
30
+ # @attribute [Covered::Forks | Nil] The currently registered coverage wrapper.
26
31
  attr :coverage
27
32
 
33
+ # Register coverage for fork handling.
34
+ # @parameter coverage [Covered::Forks] The coverage wrapper to use in forked children.
35
+ # @raises [ArgumentError] If coverage is already registered.
28
36
  def start(coverage)
29
37
  LOCK.synchronize do
30
38
  if @coverage
@@ -35,12 +43,14 @@ module Covered
35
43
  end
36
44
  end
37
45
 
46
+ # Clear the registered coverage.
38
47
  def finish
39
48
  LOCK.synchronize do
40
49
  @coverage = nil
41
50
  end
42
51
  end
43
52
 
53
+ # Reset coverage in a forked child and save it at exit.
44
54
  def after_fork
45
55
  return unless coverage = Handler.coverage
46
56
  pid = Process.pid
@@ -57,6 +67,8 @@ module Covered
57
67
  end
58
68
  end
59
69
 
70
+ # Intercept `Process.fork` and initialize coverage in the child.
71
+ # @returns [Integer | Nil] The process ID in the parent, and `0` or `nil` in the child depending on Ruby's fork semantics.
60
72
  def _fork
61
73
  pid = super
62
74
 
@@ -9,11 +9,19 @@ require_relative "wrapper"
9
9
  require "console/output"
10
10
 
11
11
  module Covered
12
+ # Generates a Markdown coverage summary.
12
13
  class MarkdownSummary
14
+ # Initialize the report with an optional coverage threshold.
15
+ # @parameter threshold [Numeric | Nil] The minimum ratio a file must meet to be omitted from the detailed output.
13
16
  def initialize(threshold: 1.0)
14
17
  @threshold = threshold
15
18
  end
16
19
 
20
+ # Enumerate coverage below the threshold and return aggregate statistics.
21
+ # @parameter wrapper [Covered::Base] The coverage wrapper to enumerate.
22
+ # @yields {|coverage| ...} Coverage whose ratio is below the configured threshold.
23
+ # @parameter coverage [Covered::Coverage] The coverage object below the threshold.
24
+ # @returns [Covered::Statistics] Statistics for all coverage objects, including omitted ones.
17
25
  def each(wrapper)
18
26
  statistics = Statistics.new
19
27
 
@@ -28,6 +36,11 @@ module Covered
28
36
  return statistics
29
37
  end
30
38
 
39
+ # Print any annotations for the given line.
40
+ # @parameter output [IO] The output stream.
41
+ # @parameter coverage [Covered::Coverage] The coverage being rendered.
42
+ # @parameter line [String] The source line.
43
+ # @parameter line_offset [Integer] The current line number.
31
44
  def print_annotations(output, coverage, line, line_offset)
32
45
  if annotations = coverage.annotations[line_offset]
33
46
  prefix = "#{line_offset}|".rjust(8) + "*|".rjust(8)
@@ -38,10 +51,17 @@ module Covered
38
51
  end
39
52
  end
40
53
 
54
+ # Print the line and hit-count header.
55
+ # @parameter output [IO] The output stream.
41
56
  def print_line_header(output)
42
57
  output.puts "Line|".rjust(8) + "Hits|".rjust(8)
43
58
  end
44
59
 
60
+ # Print a single source line.
61
+ # @parameter output [IO] The output stream.
62
+ # @parameter line [String] The source line.
63
+ # @parameter line_offset [Integer] The current line number.
64
+ # @parameter count [Integer | Nil] The execution count for the line.
45
65
  def print_line(output, line, line_offset, count)
46
66
  prefix = "#{line_offset}|".rjust(8) + "#{count}|".rjust(8)
47
67
 
@@ -54,7 +74,9 @@ module Covered
54
74
  end
55
75
  end
56
76
 
57
- # A coverage array gives, for each line, the number of line execution by the interpreter. A nil value means coverage is finishd for this line (lines like else and end).
77
+ # A coverage array gives, for each line, the number of line executions by the interpreter. A `nil` value means coverage is finished for this line (lines like `else` and `end`).
78
+ # @parameter wrapper [Covered::Base] The coverage wrapper to report.
79
+ # @parameter output [IO] The output stream.
58
80
  def call(wrapper, output = $stdout)
59
81
  output.puts "# Coverage Report"
60
82
  output.puts
@@ -11,7 +11,10 @@ require "minitest"
11
11
  $covered = Covered::Config.load
12
12
 
13
13
  module Covered
14
+ # Integrates coverage tracking with Minitest.
14
15
  module Minitest
16
+ # Start coverage before running the Minitest suite.
17
+ # @returns [Object] The result of Minitest's original `run` method.
15
18
  def run(*)
16
19
  $covered.start
17
20
 
@@ -6,7 +6,13 @@
6
6
  require_relative "summary"
7
7
 
8
8
  module Covered
9
+ # Generates a report containing only lines near missing coverage.
9
10
  class PartialSummary < Summary
11
+ # Print coverage snippets around uncovered lines.
12
+ # @parameter terminal [Console::Terminal] The terminal to write to.
13
+ # @parameter coverage [Covered::Coverage] The coverage to render.
14
+ # @parameter before [Integer] The number of lines before an uncovered line to show.
15
+ # @parameter after [Integer] The number of lines after an uncovered line to show.
10
16
  def print_coverage(terminal, coverage, before: 4, after: 4)
11
17
  return if coverage.zero?
12
18
 
@@ -38,6 +44,10 @@ module Covered
38
44
  end
39
45
  end
40
46
 
47
+ # Print the partial coverage report.
48
+ # @parameter wrapper [Covered::Base] The coverage wrapper to report.
49
+ # @parameter output [IO] The output stream.
50
+ # @parameter options [Hash] Options forwarded to {print_coverage}.
41
51
  def call(wrapper, output = $stdout, **options)
42
52
  terminal = self.terminal(output)
43
53
  complete_files = []
@@ -59,7 +69,7 @@ module Covered
59
69
  coverage.print(output)
60
70
  end
61
71
 
62
- # Collect files with 100% coverage that were not shown
72
+ # Collect files with 100% coverage that were not shown:
63
73
  wrapper.each do |coverage|
64
74
  if coverage.ratio >= 1.0
65
75
  complete_files << wrapper.relative_path(coverage.path)
@@ -69,7 +79,7 @@ module Covered
69
79
  terminal.puts
70
80
  statistics.print(output)
71
81
 
72
- # Only show information about files with 100% coverage if there were files with partial coverage shown above
82
+ # Only show information about files with 100% coverage if there were files with partial coverage shown above:
73
83
  if complete_files.any? && partial_files_count > 0
74
84
  terminal.puts ""
75
85
  if complete_files.size == 1
@@ -10,15 +10,24 @@ require "msgpack"
10
10
  require "time"
11
11
 
12
12
  module Covered
13
+ # Persists coverage records to a MessagePack database.
13
14
  class Persist < Wrapper
14
15
  DEFAULT_PATH = ".covered.db"
15
16
 
17
+ # Initialize persistence for the given output and database path.
18
+ # @parameter output [Covered::Base] The output to wrap.
19
+ # @parameter path [String] The coverage database path.
16
20
  def initialize(output, path = DEFAULT_PATH)
17
21
  super(output)
18
22
 
19
23
  @path = self.expand_path(path)
20
24
  end
21
25
 
26
+ # Apply a persisted record to the output.
27
+ # Records with stale source modification times are ignored unless `ignore_mtime` is true.
28
+ # @parameter record [Hash] The persisted coverage record.
29
+ # @parameter ignore_mtime [Boolean] Whether to apply records even if their source appears stale.
30
+ # @returns [Boolean] Whether the record was applied.
22
31
  def apply(record, ignore_mtime: false)
23
32
  if coverage = record[:coverage]
24
33
  if path = record[:path]
@@ -35,6 +44,9 @@ module Covered
35
44
  return false
36
45
  end
37
46
 
47
+ # Convert coverage into a database record.
48
+ # @parameter coverage [Covered::Coverage] The coverage object to serialize.
49
+ # @returns [Hash] A MessagePack-compatible record.
38
50
  def serialize(coverage)
39
51
  {
40
52
  # We want to use relative paths so that moving the repo won't break everything:
@@ -45,6 +57,9 @@ module Covered
45
57
  }
46
58
  end
47
59
 
60
+ # Load persisted coverage records into the output.
61
+ # @parameter options [Hash] Options forwarded to {apply}.
62
+ # @raises [LoadError] If the database exists but cannot be decoded.
48
63
  def load!(**options)
49
64
  return unless File.exist?(@path)
50
65
 
@@ -61,6 +76,7 @@ module Covered
61
76
  raise LoadError, "Failed to load coverage from #{@path}, maybe old format or corrupt!"
62
77
  end
63
78
 
79
+ # Save all output coverage records to the database.
64
80
  def save!
65
81
  # Dump all coverage:
66
82
  File.open(@path, "ab") do |file|
@@ -77,12 +93,17 @@ module Covered
77
93
  end
78
94
  end
79
95
 
96
+ # Finish the wrapped output and save the coverage database.
80
97
  def finish
81
98
  super
82
99
 
83
100
  self.save!
84
101
  end
85
102
 
103
+ # Reload persisted coverage and enumerate the wrapped output.
104
+ # @yields {|coverage| ...} Each coverage object from the reloaded output.
105
+ # @parameter coverage [Covered::Coverage] The current coverage object.
106
+ # @returns [Enumerator | Nil] An enumerator without a block.
86
107
  def each(&block)
87
108
  return to_enum unless block_given?
88
109
 
@@ -92,6 +113,8 @@ module Covered
92
113
  super
93
114
  end
94
115
 
116
+ # Build the MessagePack factory used for coverage records.
117
+ # @returns [MessagePack::Factory] The configured MessagePack factory.
95
118
  def make_factory
96
119
  factory = MessagePack::Factory.new
97
120
 
@@ -117,10 +140,16 @@ module Covered
117
140
  return factory
118
141
  end
119
142
 
143
+ # Build a MessagePack packer for the given IO.
144
+ # @parameter io [IO] The IO to write packed records to.
145
+ # @returns [MessagePack::Packer] A packer configured for coverage records.
120
146
  def make_packer(io)
121
147
  return make_factory.packer(io)
122
148
  end
123
149
 
150
+ # Build a MessagePack unpacker for the given IO.
151
+ # @parameter io [IO] The IO to read packed records from.
152
+ # @returns [MessagePack::Unpacker] An unpacker configured for coverage records.
124
153
  def make_unpacker(io)
125
154
  return make_factory.unpacker(io)
126
155
  end
@@ -10,7 +10,9 @@ require_relative "persist"
10
10
  require_relative "forks"
11
11
 
12
12
  module Covered
13
+ # Configures coverage collection, filtering, persistence and reports.
13
14
  class Policy < Wrapper
15
+ # Initialize a policy with an empty file collection.
14
16
  def initialize
15
17
  super(Files.new)
16
18
 
@@ -18,8 +20,11 @@ module Covered
18
20
  @capture = nil
19
21
  end
20
22
 
23
+ # @attribute [Covered::Base] The current output pipeline.
21
24
  attr :output
22
25
 
26
+ # Freeze the policy and eagerly build the capture pipeline.
27
+ # @returns [Covered::Policy] This frozen policy.
23
28
  def freeze
24
29
  return self if frozen?
25
30
 
@@ -29,47 +34,67 @@ module Covered
29
34
  super
30
35
  end
31
36
 
37
+ # Include files matching the given pattern in coverage results.
38
+ # Arguments are forwarded to {Covered::Include#initialize}.
32
39
  def include(...)
33
40
  @output = Include.new(@output, ...)
34
41
  end
35
42
 
43
+ # Exclude files matching the given pattern from coverage results.
44
+ # Arguments are forwarded to {Covered::Skip#initialize}.
36
45
  def skip(...)
37
46
  @output = Skip.new(@output, ...)
38
47
  end
39
48
 
49
+ # Restrict coverage results to files matching the given pattern.
50
+ # Arguments are forwarded to {Covered::Only#initialize}.
40
51
  def only(...)
41
52
  @output = Only.new(@output, ...)
42
53
  end
43
54
 
55
+ # Restrict coverage results to the given project root.
56
+ # Arguments are forwarded to {Covered::Root#initialize}.
44
57
  def root(...)
45
58
  @output = Root.new(@output, ...)
46
59
  end
47
60
 
61
+ # Persist coverage results to a database.
62
+ # Arguments are forwarded to {Covered::Persist#initialize}.
48
63
  def persist!(...)
49
64
  @output = Persist.new(@output, ...)
50
65
  end
51
66
 
67
+ # The runtime capture pipeline for this policy.
68
+ # @returns [Covered::Forks] The memoized capture pipeline.
52
69
  def capture
53
70
  @capture ||= Forks.new(
54
71
  Capture.new(@output)
55
72
  )
56
73
  end
57
74
 
75
+ # Start collecting coverage.
58
76
  def start
59
77
  capture.start
60
78
  end
61
79
 
80
+ # Finish collecting coverage.
62
81
  def finish
63
82
  capture.finish
64
83
  end
65
84
 
85
+ # @attribute [Array] The configured report objects or autoloaders.
66
86
  attr :reports
67
87
 
88
+ # Lazily loads a report class when it is first used.
68
89
  class Autoload
90
+ # Initialize an autoloaded report with the given constant name.
91
+ # @parameter name [String] The report class name under {Covered}.
69
92
  def initialize(name)
70
93
  @name = name
71
94
  end
72
95
 
96
+ # Instantiate the report class.
97
+ # @returns [Object] A new report instance.
73
98
  def new
74
99
  begin
75
100
  klass = Covered.const_get(@name)
@@ -82,10 +107,14 @@ module Covered
82
107
  return klass.new
83
108
  end
84
109
 
110
+ # Instantiate the report and call it.
111
+ # Arguments are forwarded to the report.
85
112
  def call(...)
86
113
  self.new.call(...)
87
114
  end
88
115
 
116
+ # A human-readable representation of this autoloaded report.
117
+ # @returns [String] A debug representation of the autoloader.
89
118
  def to_s
90
119
  "\#<#{self.class} loading #{@name}>"
91
120
  end
@@ -101,6 +130,8 @@ module Covered
101
130
  end
102
131
  end
103
132
 
133
+ # Configure reports from names, booleans, arrays or report objects.
134
+ # @parameter reports [String | Boolean | Array | Object | Nil] The reports to configure.
104
135
  def reports!(reports)
105
136
  if reports.is_a?(String)
106
137
  names = reports.split(",")
@@ -124,6 +155,8 @@ module Covered
124
155
  end
125
156
  end
126
157
 
158
+ # Generate all configured reports.
159
+ # Arguments are forwarded to each report.
127
160
  def call(...)
128
161
  @reports.each do |report|
129
162
  report.call(self, ...)
data/lib/covered/rspec.rb CHANGED
@@ -9,18 +9,26 @@ require "rspec/core/formatters"
9
9
  $covered = Covered::Config.load
10
10
 
11
11
  module Covered
12
+ # Integrates coverage tracking with RSpec.
12
13
  module RSpec
14
+ # Extends RSpec configuration with coverage tracking.
13
15
  module Policy
16
+ # Start coverage before loading spec files.
17
+ # @returns [Object] The result of RSpec's original `load_spec_files` method.
14
18
  def load_spec_files
15
19
  $covered.start
16
20
 
17
21
  super
18
22
  end
19
23
 
24
+ # The active coverage configuration.
25
+ # @returns [Covered::Config] The active coverage configuration.
20
26
  def covered
21
27
  $covered
22
28
  end
23
29
 
30
+ # Assign the active coverage configuration.
31
+ # @parameter policy [Covered::Config] The replacement coverage configuration.
24
32
  def covered= policy
25
33
  $covered = policy
26
34
  end
@@ -4,7 +4,13 @@
4
4
  # Copyright, 2018-2023, by Samuel Williams.
5
5
 
6
6
  module Covered
7
+ # Source code metadata for a covered file or generated template.
7
8
  class Source
9
+ # Build source metadata for the given path.
10
+ # Records the current file modification time when the path exists.
11
+ # @parameter path [String] The source path.
12
+ # @parameter options [Hash] Options forwarded to {initialize}.
13
+ # @returns [Covered::Source] The source metadata.
8
14
  def self.for(path, **options)
9
15
  if File.exist?(path)
10
16
  # options[:code] ||= File.read(path)
@@ -14,6 +20,11 @@ module Covered
14
20
  self.new(path, **options)
15
21
  end
16
22
 
23
+ # Initialize source metadata.
24
+ # @parameter path [String] The source path.
25
+ # @parameter code [String | Nil] Optional generated source code.
26
+ # @parameter line_offset [Integer] The starting line offset.
27
+ # @parameter modified_time [Time | Nil] The source modification time.
17
28
  def initialize(path, code: nil, line_offset: 1, modified_time: nil)
18
29
  @path = path
19
30
  @code = code
@@ -21,15 +32,28 @@ module Covered
21
32
  @modified_time = modified_time
22
33
  end
23
34
 
35
+ # @attribute [String] The source path.
24
36
  attr_accessor :path
37
+
38
+ # @attribute [String | Nil] Optional generated source code.
25
39
  attr :code
40
+
41
+ # @attribute [Integer] The starting line offset for generated source code.
26
42
  attr :line_offset
43
+
44
+ # @attribute [Time | Nil] The recorded source modification time.
27
45
  attr :modified_time
28
46
 
47
+ # A human-readable representation of this source.
48
+ # @returns [String] A summary containing the source path.
29
49
  def to_s
30
50
  "\#<#{self.class} path=#{path}>"
31
51
  end
32
52
 
53
+ # Read the source code from disk.
54
+ # @yields {|file| ...} If a block is given, yields an open source file.
55
+ # @parameter file [File] The open source file.
56
+ # @returns [String | Object] The source contents without a block, or the block result with a block.
33
57
  def read(&block)
34
58
  if block_given?
35
59
  File.open(self.path, "r", &block)
@@ -39,14 +63,19 @@ module Covered
39
63
  end
40
64
 
41
65
  # The actual code which is being covered. If a template generates the source, this is the generated code, while the path refers to the template itself.
66
+ # @returns [String] The generated code when present, otherwise the file contents.
42
67
  def code!
43
68
  self.code || self.read
44
69
  end
45
70
 
71
+ # Whether generated source code is present.
72
+ # @returns [Boolean] Whether this source has generated code.
46
73
  def code?
47
74
  !!self.code
48
75
  end
49
76
 
77
+ # Serialize this source with the given packer.
78
+ # @parameter packer [Object] The MessagePack-compatible packer.
50
79
  def serialize(packer)
51
80
  packer.write(self.path)
52
81
  packer.write(self.code)
@@ -54,6 +83,9 @@ module Covered
54
83
  packer.write(self.modified_time)
55
84
  end
56
85
 
86
+ # Deserialize a source from the given unpacker.
87
+ # @parameter unpacker [Object] The MessagePack-compatible unpacker.
88
+ # @returns [Covered::Source] The deserialized source metadata.
57
89
  def self.deserialize(unpacker)
58
90
  path = unpacker.read
59
91
  code = unpacker.read