Module: Yard::Timekeeper
- Defined in:
- lib/yard/timekeeper.rb,
lib/yard/timekeeper/version.rb
Defined Under Namespace
Modules: Version
Classes: Error
Constant Summary
collapse
- RAKE_INTEGRATIONS =
{}
- RAKE_INTEGRATIONS_MUTEX =
Mutex.new
- TITLE_GENERATOR_DIFF_LINE_RE =
/\A[+-].*Documentation by YARD \d+(?:\.\d+)+(?:[.\w-][.\w-]*)?\s*\z/
- TIMESTAMP_DIFF_LINE_RE =
/\A[+-]\s{2}Generated on .+ by\s*\z/
/
\A[+-]\s{2}
\d+(?:\.\d+)+(?:[.\w-][.\w-]*)?
\s\(ruby-[^)]+\)\.\s*
\z
/x
- TITLE_GENERATOR_LINE_RE =
/Documentation by YARD \d+(?:\.\d+)+(?:[.\w-][.\w-]*)?/
- TIMESTAMP_LINE_RE =
/\A\s{2}Generated on .+ by\s*\z/
/
\A\s{2}
\d+(?:\.\d+)+(?:[.\w-][.\w-]*)?
\s\(ruby-[^)]+\)\.\s*
\z
/x
- DIFF_METADATA_PREFIXES =
[
"diff --git ",
"index ",
"--- ",
"+++ ",
"@@ "
].freeze
- VERSION =
Current gem version exposed at the traditional constant location.
Version::VERSION
Class Method Summary
collapse
-
.__reset_rake_integrations__ ⇒ Object
-
.balanced_footer_churn?(change_lines) ⇒ Boolean
-
.changed_docs_files(root, docs_path = "docs") ⇒ Object
-
.enabled? ⇒ Boolean
-
.footer_churn_kind(line) ⇒ Object
-
.footer_churn_line?(line) ⇒ Boolean
-
.generated_metadata_kind(line) ⇒ Object
-
.generated_metadata_lines(content) ⇒ Object
-
.generated_metadata_value(line, kind) ⇒ Object
-
.git_diff(path, root) ⇒ Object
-
.git_root ⇒ Object
-
.install_rake_tasks!(yard_task_name = :yard) ⇒ Object
-
.normalize_generated_metadata(path, root) ⇒ Object
-
.normalize_generated_metadata_line(line, kind, replacement) ⇒ Object
-
.postprocess_html_docs ⇒ Object
-
.restore_file(path, root) ⇒ Object
-
.root_relative_path(docs_dir, root) ⇒ Object
Path of docs_dir relative to the git repository root, or nil when the docs dir is not inside the repository (git pathspecs must be root-relative here).
-
.run_at_exit ⇒ Object
-
.timestamp_only_diff?(diff_text) ⇒ Boolean
-
.tracked_file_content(path, root) ⇒ Object
Class Method Details
.__reset_rake_integrations__ ⇒ Object
162
163
164
165
166
167
168
169
170
|
# File 'lib/yard/timekeeper.rb', line 162
def (change_lines)
removed_lines = change_lines.select { |line| line.start_with?("-") }
added_lines = change_lines.select { |line| line.start_with?("+") }
removed_lines.size == added_lines.size &&
removed_lines.size.between?(1, 2) &&
removed_lines.map { |line| (line) }.sort ==
added_lines.map { |line| (line) }.sort
end
|
.changed_docs_files(root, docs_path = "docs") ⇒ Object
103
104
105
106
107
108
109
110
|
# File 'lib/yard/timekeeper.rb', line 103
def changed_docs_files(root, docs_path = "docs")
stdout, status = Open3.capture2("git", "diff", "--name-only", "--relative", "--", docs_path, chdir: root)
return [] unless status.success?
stdout.lines.map(&:strip).reject(&:empty?)
rescue Errno::ENOENT
[]
end
|
.enabled? ⇒ Boolean
90
91
92
|
# File 'lib/yard/timekeeper.rb', line 90
def enabled?
!ENV.fetch("YARD_TIMEKEEPER_DISABLE", "false").casecmp?("true")
end
|
219
220
221
222
223
224
|
# File 'lib/yard/timekeeper.rb', line 219
def generated_metadata_lines(content)
content.lines.each_with_object({}) do |line, replacements|
kind = generated_metadata_kind(line)
replacements[kind] ||= generated_metadata_value(line, kind) if kind
end
end
|
226
227
228
229
230
231
232
233
|
# File 'lib/yard/timekeeper.rb', line 226
def generated_metadata_value(line, kind)
case kind
when :title_generator
line[TITLE_GENERATOR_LINE_RE]
else
line
end
end
|
.git_diff(path, root) ⇒ Object
125
126
127
128
129
130
131
132
133
134
135
136
137
|
# File 'lib/yard/timekeeper.rb', line 125
def git_diff(path, root)
stdout, _status = Open3.capture2(
"git",
"diff",
"--no-ext-diff",
"--no-color",
"--unified=0",
"--",
path,
chdir: root
)
stdout
end
|
.git_root ⇒ Object
94
95
96
97
98
99
100
101
|
# File 'lib/yard/timekeeper.rb', line 94
def git_root
stdout, status = Open3.capture2("git", "rev-parse", "--show-toplevel", chdir: Dir.pwd)
return unless status.success?
stdout.strip
rescue Errno::ENOENT
nil
end
|
.install_rake_tasks!(yard_task_name = :yard) ⇒ Object
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
|
# File 'lib/yard/timekeeper.rb', line 180
def normalize_generated_metadata(path, root)
original = tracked_file_content(path, root)
return false unless original
absolute_path = File.join(root, path)
return false unless File.file?(absolute_path)
replacement_lines = generated_metadata_lines(original)
return false if replacement_lines.empty?
current = File.read(absolute_path)
changed = false
normalized_lines = current.lines.map do |line|
kind = generated_metadata_kind(line)
replacement = replacement_lines[kind]
normalized_line = replacement ? normalize_generated_metadata_line(line, kind, replacement) : line
if replacement && line != normalized_line
changed = true
normalized_line
else
line
end
end
return false unless changed
File.write(absolute_path, normalized_lines.join)
true
end
|
235
236
237
238
239
240
241
242
|
# File 'lib/yard/timekeeper.rb', line 235
def normalize_generated_metadata_line(line, kind, replacement)
case kind
when :title_generator
line.sub(TITLE_GENERATOR_LINE_RE, replacement.to_s)
else
replacement
end
end
|
.postprocess_html_docs ⇒ Object
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
# File 'lib/yard/timekeeper.rb', line 38
def postprocess_html_docs
return unless enabled?
docs_dir = File.join(Dir.pwd, "docs")
return unless Dir.exist?(docs_dir)
root = git_root
return unless root
docs_path = root_relative_path(docs_dir, root)
return unless docs_path
changed_docs_files(root, docs_path).each do |relative_path|
next unless relative_path.end_with?(".html")
diff = git_diff(relative_path, root)
if timestamp_only_diff?(diff)
restore_file(relative_path, root)
else
normalize_generated_metadata(relative_path, root)
end
end
rescue => e
warn("Yard::Timekeeper.postprocess_html_docs failed: #{e.class}: #{e.message}")
end
|
.restore_file(path, root) ⇒ Object
253
254
255
256
257
258
|
# File 'lib/yard/timekeeper.rb', line 253
def restore_file(path, root)
_stdout, _stderr, status = Open3.capture3("git", "checkout", "--", path, chdir: root)
status.success?
rescue Errno::ENOENT
false
end
|
.root_relative_path(docs_dir, root) ⇒ Object
Path of docs_dir relative to the git repository root, or nil when the
docs dir is not inside the repository (git pathspecs must be
root-relative here).
115
116
117
118
119
120
121
122
123
|
# File 'lib/yard/timekeeper.rb', line 115
def root_relative_path(docs_dir, root)
expanded_docs = File.expand_path(docs_dir)
expanded_root = File.expand_path(root)
prefix = "#{expanded_root}/"
return "docs" if expanded_docs == expanded_root
return unless expanded_docs.start_with?(prefix)
expanded_docs.delete_prefix(prefix)
end
|
.run_at_exit ⇒ Object
67
68
69
|
# File 'lib/yard/timekeeper.rb', line 67
def run_at_exit
postprocess_html_docs
end
|
.timestamp_only_diff?(diff_text) ⇒ Boolean
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
# File 'lib/yard/timekeeper.rb', line 139
def timestamp_only_diff?(diff_text)
return false if diff_text.to_s.strip.empty?
change_lines = []
diff_text.each_line do |line|
next if DIFF_METADATA_PREFIXES.any? { |prefix| line.start_with?(prefix) }
next if line.strip.empty?
return false unless (line)
change_lines << line
end
(change_lines)
end
|
.tracked_file_content(path, root) ⇒ Object
210
211
212
213
214
215
216
217
|
# File 'lib/yard/timekeeper.rb', line 210
def tracked_file_content(path, root)
stdout, status = Open3.capture2("git", "show", "HEAD:#{path}", chdir: root)
return unless status.success?
stdout
rescue Errno::ENOENT
nil
end
|