yard-fence 0.1.0
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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +1 -0
- data/CHANGELOG.md +35 -0
- data/CITATION.cff +20 -0
- data/CODE_OF_CONDUCT.md +134 -0
- data/CONTRIBUTING.md +218 -0
- data/FUNDING.md +77 -0
- data/LICENSE.txt +21 -0
- data/README.md +579 -0
- data/REEK +0 -0
- data/RUBOCOP.md +71 -0
- data/SECURITY.md +21 -0
- data/lib/yard/fence/kramdown_gfm_document.rb +25 -0
- data/lib/yard/fence/version.rb +10 -0
- data/lib/yard/fence.rb +169 -0
- data/lib/yard-fence.rb +6 -0
- data/sig/kramdown.rbs +6 -0
- data/sig/yard/fence.rbs +37 -0
- data.tar.gz.sig +0 -0
- metadata +353 -0
- metadata.gz.sig +0 -0
data/RUBOCOP.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# RuboCop Usage Guide
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
A tale of two RuboCop plugin gems.
|
|
6
|
+
|
|
7
|
+
### RuboCop Gradual
|
|
8
|
+
|
|
9
|
+
This project uses `rubocop_gradual` instead of vanilla RuboCop for code style checking. The `rubocop_gradual` tool allows for gradual adoption of RuboCop rules by tracking violations in a lock file.
|
|
10
|
+
|
|
11
|
+
### RuboCop LTS
|
|
12
|
+
|
|
13
|
+
This project uses `rubocop-lts` to ensure, on a best-effort basis, compatibility with Ruby >= 1.9.2.
|
|
14
|
+
RuboCop rules are meticulously configured by the `rubocop-lts` family of gems to ensure that a project is compatible with a specific version of Ruby. See: https://rubocop-lts.gitlab.io for more.
|
|
15
|
+
|
|
16
|
+
## Checking RuboCop Violations
|
|
17
|
+
|
|
18
|
+
To check for RuboCop violations in this project, always use:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
bundle exec rake rubocop_gradual:check
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Do not use** the standard RuboCop commands like:
|
|
25
|
+
- `bundle exec rubocop`
|
|
26
|
+
- `rubocop`
|
|
27
|
+
|
|
28
|
+
## Understanding the Lock File
|
|
29
|
+
|
|
30
|
+
The `.rubocop_gradual.lock` file tracks all current RuboCop violations in the project. This allows the team to:
|
|
31
|
+
|
|
32
|
+
1. Prevent new violations while gradually fixing existing ones
|
|
33
|
+
2. Track progress on code style improvements
|
|
34
|
+
3. Ensure CI builds don't fail due to pre-existing violations
|
|
35
|
+
|
|
36
|
+
## Common Commands
|
|
37
|
+
|
|
38
|
+
- **Check violations**
|
|
39
|
+
- `bundle exec rake rubocop_gradual`
|
|
40
|
+
- `bundle exec rake rubocop_gradual:check`
|
|
41
|
+
- **(Safe) Autocorrect violations, and update lockfile if no new violations**
|
|
42
|
+
- `bundle exec rake rubocop_gradual:autocorrect`
|
|
43
|
+
- **Force update the lock file (w/o autocorrect) to match violations present in code**
|
|
44
|
+
- `bundle exec rake rubocop_gradual:force_update`
|
|
45
|
+
|
|
46
|
+
## Workflow
|
|
47
|
+
|
|
48
|
+
1. Before submitting a PR, run `bundle exec rake rubocop_gradual:autocorrect`
|
|
49
|
+
a. or just the default `bundle exec rake`, as autocorrection is a pre-requisite of the default task.
|
|
50
|
+
2. If there are new violations, either:
|
|
51
|
+
- Fix them in your code
|
|
52
|
+
- Run `bundle exec rake rubocop_gradual:force_update` to update the lock file (only for violations you can't fix immediately)
|
|
53
|
+
3. Commit the updated `.rubocop_gradual.lock` file along with your changes
|
|
54
|
+
|
|
55
|
+
## Never add inline RuboCop disables
|
|
56
|
+
|
|
57
|
+
Do not add inline `rubocop:disable` / `rubocop:enable` comments anywhere in the codebase (including specs, except when following the few existing `rubocop:disable` patterns for a rule already being disabled elsewhere in the code). We handle exceptions in two supported ways:
|
|
58
|
+
|
|
59
|
+
- Permanent/structural exceptions: prefer adjusting the RuboCop configuration (e.g., in `.rubocop.yml`) to exclude a rule for a path or file pattern when it makes sense project-wide.
|
|
60
|
+
- Temporary exceptions while improving code: record the current violations in `.rubocop_gradual.lock` via the gradual workflow:
|
|
61
|
+
- `bundle exec rake rubocop_gradual:autocorrect` (preferred; will autocorrect what it can and update the lock only if no new violations were introduced)
|
|
62
|
+
- If needed, `bundle exec rake rubocop_gradual:force_update` (as a last resort when you cannot fix the newly reported violations immediately)
|
|
63
|
+
|
|
64
|
+
In general, treat the rules as guidance to follow; fix violations rather than ignore them. For example, RSpec conventions in this project expect `described_class` to be used in specs that target a specific class under test.
|
|
65
|
+
|
|
66
|
+
## Benefits of rubocop_gradual
|
|
67
|
+
|
|
68
|
+
- Allows incremental adoption of code style rules
|
|
69
|
+
- Prevents CI failures due to pre-existing violations
|
|
70
|
+
- Provides a clear record of code style debt
|
|
71
|
+
- Enables focused efforts on improving code quality over time
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
|----------|-----------|
|
|
7
|
+
| 1.latest | ✅ |
|
|
8
|
+
|
|
9
|
+
## Security contact information
|
|
10
|
+
|
|
11
|
+
To report a security vulnerability, please use the
|
|
12
|
+
[Tidelift security contact](https://tidelift.com/security).
|
|
13
|
+
Tidelift will coordinate the fix and disclosure.
|
|
14
|
+
|
|
15
|
+
## Additional Support
|
|
16
|
+
|
|
17
|
+
If you are interested in support for versions older than the latest release,
|
|
18
|
+
please consider sponsoring the project / maintainer @ https://liberapay.com/pboling/donate,
|
|
19
|
+
or find other sponsorship links in the [README].
|
|
20
|
+
|
|
21
|
+
[README]: README.md
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# YARD GFM support shim: ensure Kramdown GFM provider is first in the provider list
|
|
2
|
+
# so markdown code fences and GitHub-style markdown render as expected. This does not
|
|
3
|
+
# alter content; it influences the order in which YARD tries markdown engines.
|
|
4
|
+
# See test_gfm.rb for a quick sanity check of provider order and <pre><code> rendering.
|
|
5
|
+
|
|
6
|
+
# Gratefully and liberally taken from the MIT-licensed https://github.com/bensheldon/good_job/pull/113/files
|
|
7
|
+
require "kramdown"
|
|
8
|
+
require "kramdown-parser-gfm"
|
|
9
|
+
|
|
10
|
+
# Custom markup provider class that always renders Kramdown using GFM (Github Flavored Markdown).
|
|
11
|
+
# GFM is needed to render markdown tables and fenced code blocks in the README.
|
|
12
|
+
module Yard
|
|
13
|
+
module Fence
|
|
14
|
+
class KramdownGfmDocument < Kramdown::Document
|
|
15
|
+
def initialize(source, options = {})
|
|
16
|
+
options[:input] = "GFM" unless options.key?(:input)
|
|
17
|
+
super(source, options)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Note:
|
|
24
|
+
# We intentionally do NOT auto-register here to avoid circular require warnings when
|
|
25
|
+
# YARD is loading. Prefer calling Yard::Fence.use_kramdown_gfm! from your .yardopts.
|
data/lib/yard/fence.rb
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Prepare sanitized copies of Markdown/TXT files in tmp/ for YARD consumption.
|
|
4
|
+
#
|
|
5
|
+
# Why this exists
|
|
6
|
+
# - YARD treats any `{…}` sequence in extra files (like README.md) as a potential link
|
|
7
|
+
# to a code object (its "reference tag" syntax). This scanning happens outside of
|
|
8
|
+
# the Markdown renderer and does not reliably respect fenced code blocks.
|
|
9
|
+
# - As a result, normal hash literals in code examples, e.g. {get: :query} or
|
|
10
|
+
# {"Authorization" => "Basic ..."}, trigger lots of `[InvalidLink] Cannot resolve link` warnings.
|
|
11
|
+
#
|
|
12
|
+
# Our constraints
|
|
13
|
+
# - We want to keep the source Markdown clean and readable on GitHub (no HTML entities,
|
|
14
|
+
# no backslash escapes sprinkled throughout examples).
|
|
15
|
+
# - We need YARD to stop interpreting code-fenced braces as links.
|
|
16
|
+
#
|
|
17
|
+
# Strategy (tmp/ preprocessor)
|
|
18
|
+
# - At doc build time, copy top-level *.md/*.txt into tmp/ and transform only the risky
|
|
19
|
+
# brace pairs in contexts YARD misinterprets:
|
|
20
|
+
# * inside triple‑backtick fenced code blocks
|
|
21
|
+
# * inside single‑backtick inline code spans
|
|
22
|
+
# * simple brace placeholders in prose like {issuer} or {{TEMPLATE}}
|
|
23
|
+
# - The transformation uses Unicode "fullwidth" braces: { and }.
|
|
24
|
+
# These look like normal ASCII braces when rendered, but importantly they do not match
|
|
25
|
+
# YARD's `{…}` reference tag regex, so they are ignored by the link resolver.
|
|
26
|
+
# - After YARD finishes generating HTML into docs/, we post‑process the HTML and convert
|
|
27
|
+
# fullwidth braces back to ASCII braces so copying code from the docs works as expected.
|
|
28
|
+
#
|
|
29
|
+
# Key lines to see:
|
|
30
|
+
# - Transform to fullwidth: str.tr('{}', '{}')
|
|
31
|
+
# This replaces ASCII { and } with their fullwidth Unicode counterparts in sanitized tmp files.
|
|
32
|
+
# - Restore to ASCII (HTML): out = src.tr('{}', '{}')
|
|
33
|
+
# This runs after docs are generated and rewrites the HTML contents back to normal { and }.
|
|
34
|
+
#
|
|
35
|
+
# This keeps README.md pristine while eliminating YARD InvalidLink noise and ensuring
|
|
36
|
+
# the published docs remain copy‑pastable.
|
|
37
|
+
|
|
38
|
+
# includes modules from stdlib
|
|
39
|
+
require "fileutils"
|
|
40
|
+
|
|
41
|
+
# third party gems
|
|
42
|
+
require "version_gem"
|
|
43
|
+
|
|
44
|
+
# includes gem files
|
|
45
|
+
require_relative "fence/version"
|
|
46
|
+
require_relative "fence/kramdown_gfm_document"
|
|
47
|
+
|
|
48
|
+
module Yard
|
|
49
|
+
module Fence
|
|
50
|
+
ASCII_BRACES = "{}"
|
|
51
|
+
FULLWIDTH_BRACES = "{}"
|
|
52
|
+
KRAMDOWN_PROVIDER = {lib: :kramdown, const: "KramdownGfmDocument"}
|
|
53
|
+
GLOB_PATTERN = "*.{md,MD,txt,TXT}"
|
|
54
|
+
TRIPLE_TICK_FENCE = /^\s*```/
|
|
55
|
+
INLINE_TICK_FENCE = /`([^`]+)`/
|
|
56
|
+
DOUBLE_BRACE_PLACEHOLDER_REGEX = /{{([^{}]+)}}/
|
|
57
|
+
SINGLE_BRACE_PLACEHOLDER_REGEX = /{([A-Za-z0-9_:\-]+)}/
|
|
58
|
+
|
|
59
|
+
class Error < StandardError; end
|
|
60
|
+
raise Error, "ASCII braces are not the same as Unicode Fullwidth braces" if ASCII_BRACES == FULLWIDTH_BRACES
|
|
61
|
+
|
|
62
|
+
module_function
|
|
63
|
+
|
|
64
|
+
# Replace ASCII { } with Unicode fullwidth { }.
|
|
65
|
+
# Visual effect in browsers is the same, but YARD's link matcher won't recognize them.
|
|
66
|
+
def fullwidth_braces(str)
|
|
67
|
+
str.tr(ASCII_BRACES, FULLWIDTH_BRACES)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Escape braces inside inline `code` spans only.
|
|
71
|
+
def sanitize_inline_code(line)
|
|
72
|
+
line.gsub(INLINE_TICK_FENCE) { |_| "`#{fullwidth_braces($1)}`" }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Walk the text, toggling a simple in_fence state on ``` lines.
|
|
76
|
+
# While inside a fence, convert braces to fullwidth; outside, also sanitize inline code
|
|
77
|
+
# and disarm simple prose placeholders like {issuer} or {{something}}.
|
|
78
|
+
def sanitize_fenced_blocks(text)
|
|
79
|
+
in_fence = false
|
|
80
|
+
|
|
81
|
+
text.each_line.map do |line|
|
|
82
|
+
if line.match?(TRIPLE_TICK_FENCE)
|
|
83
|
+
in_fence = !in_fence
|
|
84
|
+
line
|
|
85
|
+
elsif in_fence
|
|
86
|
+
fullwidth_braces(line)
|
|
87
|
+
else
|
|
88
|
+
ln = sanitize_inline_code(line)
|
|
89
|
+
# IMPORTANT: handle double-brace placeholders first so we don't partially
|
|
90
|
+
# convert the inner {TOKEN} and leave outer ASCII braces from `{{TOKEN}}`.
|
|
91
|
+
ln = ln.gsub(DOUBLE_BRACE_PLACEHOLDER_REGEX) { |m| fullwidth_braces(m) }
|
|
92
|
+
ln.gsub(SINGLE_BRACE_PLACEHOLDER_REGEX) { |m| fullwidth_braces(m) }
|
|
93
|
+
end
|
|
94
|
+
end.join
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def sanitize_text(text)
|
|
98
|
+
return text unless text.is_a?(String)
|
|
99
|
+
sanitize_fenced_blocks(text)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Copy top-level *.md/*.txt into tmp/ with the above sanitization applied.
|
|
103
|
+
def prepare_tmp_files
|
|
104
|
+
root = Dir.pwd
|
|
105
|
+
outdir = File.join(root, "tmp")
|
|
106
|
+
FileUtils.mkdir_p(outdir)
|
|
107
|
+
|
|
108
|
+
candidates = Dir.glob(File.join(root, GLOB_PATTERN))
|
|
109
|
+
candidates.each do |src|
|
|
110
|
+
next unless File.file?(src)
|
|
111
|
+
content = File.read(src)
|
|
112
|
+
sanitized = sanitize_text(content)
|
|
113
|
+
dst = File.join(outdir, File.basename(src))
|
|
114
|
+
File.write(dst, sanitized)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def restore_ascii_braces_in_html_file(html_filepath)
|
|
119
|
+
return unless File.file?(html_filepath)
|
|
120
|
+
content = File.read(html_filepath)
|
|
121
|
+
restored = content.tr(FULLWIDTH_BRACES, ASCII_BRACES)
|
|
122
|
+
File.write(html_filepath, restored)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def postprocess_html_docs
|
|
126
|
+
docs = File.join(Dir.pwd, "docs")
|
|
127
|
+
return unless Dir.exist?(docs)
|
|
128
|
+
Dir.glob(File.join(docs, "**", "*.html")).each do |html|
|
|
129
|
+
restore_ascii_braces_in_html_file(html)
|
|
130
|
+
end
|
|
131
|
+
rescue => e
|
|
132
|
+
warn("Yard::Fence.postprocess_html_docs failed: #{e.class}: #{e.message}")
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Register Kramdown GFM as the highest priority Markdown provider in YARD.
|
|
136
|
+
# Returns true if registration succeeded, false otherwise.
|
|
137
|
+
#
|
|
138
|
+
# This method is intentionally not executed at file load to avoid circular
|
|
139
|
+
# require warnings when YARD is in the middle of loading itself. Call this
|
|
140
|
+
# from a file loaded via .yardopts (e.g. `-e 'require "yard/fence/kramdown_gfm"; Yard::Fence.use_kramdown_gfm!'`).
|
|
141
|
+
def use_kramdown_gfm!
|
|
142
|
+
providers = ::YARD::Templates::Helpers::MarkupHelper::MARKUP_PROVIDERS[:markdown]
|
|
143
|
+
providers.unshift(KRAMDOWN_PROVIDER)
|
|
144
|
+
providers.uniq! { |p| [p[:lib].to_s, p[:const].to_s] }
|
|
145
|
+
true
|
|
146
|
+
rescue NameError => e
|
|
147
|
+
warn("Yard::Fence.use_kramdown_gfm!: failed to load YARD helper: #{e.class}: #{e.message}")
|
|
148
|
+
false
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Execute at load-time so files exist before YARD scans tmp/*.md
|
|
153
|
+
begin
|
|
154
|
+
Yard::Fence.prepare_tmp_files
|
|
155
|
+
rescue => e
|
|
156
|
+
warn("Yard::Fence: failed to prepare tmp files: #{e.class}: #{e.message}")
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Extend the Version with VersionGem::Basic to provide semantic version helpers.
|
|
161
|
+
Yard::Fence::Version.class_eval do
|
|
162
|
+
extend VersionGem::Basic
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# After YARD completes, restore ASCII braces in generated HTML docs.
|
|
166
|
+
# This guarantees the published docs (docs/*.html) show and copy normal { }.
|
|
167
|
+
at_exit do
|
|
168
|
+
Yard::Fence.postprocess_html_docs
|
|
169
|
+
end
|
data/lib/yard-fence.rb
ADDED
data/sig/kramdown.rbs
ADDED
data/sig/yard/fence.rbs
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Yard
|
|
2
|
+
module Fence
|
|
3
|
+
# Constants
|
|
4
|
+
VERSION: String
|
|
5
|
+
ASCII_BRACES: String
|
|
6
|
+
FULLWIDTH_BRACES: String
|
|
7
|
+
KRAMDOWN_PROVIDER: Hash[Symbol, untyped]
|
|
8
|
+
GLOB_PATTERN: String
|
|
9
|
+
TRIPLE_TICK_FENCE: ::Regexp
|
|
10
|
+
INLINE_TICK_FENCE: ::Regexp
|
|
11
|
+
DOUBLE_BRACE_PLACEHOLDER_REGEX: ::Regexp
|
|
12
|
+
SINGLE_BRACE_PLACEHOLDER_REGEX: ::Regexp
|
|
13
|
+
|
|
14
|
+
# Error class
|
|
15
|
+
class Error < ::StandardError
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Module functions
|
|
19
|
+
def self.fullwidth_braces: (String str) -> String
|
|
20
|
+
def self.sanitize_inline_code: (String line) -> String
|
|
21
|
+
def self.sanitize_fenced_blocks: (String text) -> String
|
|
22
|
+
def self.sanitize_text: (String text) -> String
|
|
23
|
+
| (untyped text) -> untyped
|
|
24
|
+
def self.prepare_tmp_files: () -> void
|
|
25
|
+
def self.restore_ascii_braces_in_html_file: (String html_filepath) -> void
|
|
26
|
+
def self.postprocess_html_docs: () -> void
|
|
27
|
+
def self.use_kramdown_gfm!: () -> bool
|
|
28
|
+
|
|
29
|
+
module Version
|
|
30
|
+
VERSION: String
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class KramdownGfmDocument < ::Kramdown::Document
|
|
34
|
+
def initialize: (String source, ?Hash[Symbol, untyped] options) -> void
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
data.tar.gz.sig
ADDED
|
Binary file
|