yard-junk 0.0.1
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
- data/.rubocop_todo.yml +1 -0
- data/.yardopts +2 -0
- data/README.md +297 -0
- data/bin/yard-junk +11 -0
- data/examples/bare_log.rb +3 -0
- data/examples/formatted_log.rb +5 -0
- data/examples/input/circular_ref.rb +6 -0
- data/examples/input/lot_of_errors.rb +42 -0
- data/examples/input/unparseable.rb +7 -0
- data/lib/yard-junk/command_line.rb +23 -0
- data/lib/yard-junk/janitor/base_reporter.rb +72 -0
- data/lib/yard-junk/janitor/resolver.rb +51 -0
- data/lib/yard-junk/janitor/text_reporter.rb +29 -0
- data/lib/yard-junk/janitor.rb +66 -0
- data/lib/yard-junk/logger/message.rb +253 -0
- data/lib/yard-junk/logger.rb +78 -0
- data/lib/yard-junk/rake.rb +16 -0
- data/lib/yard-junk.rb +8 -0
- data/yard-junk.gemspec +48 -0
- metadata +218 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e2e9b802aa1bb3391b0962165574b75f10e05cad
|
4
|
+
data.tar.gz: 6b279f93f06feb8ea1c10595d10cc8661f7bcc8d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 381f8c03346812eef369dabc6548c675aef0c309f7cd08b767c0e3603ef1267f08b589acb994789f410039601108aa7cd3de46b42f3e8fc539d683a28b50975b
|
7
|
+
data.tar.gz: d9c65f218d51d23bb573671f9e89c62497bbed28063487a06ede45a1e1d97abd54dd5311e095d2944936d665ef86471dd0f1b385279906a17e5d7be9316a874a
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
|
data/.yardopts
ADDED
data/README.md
ADDED
@@ -0,0 +1,297 @@
|
|
1
|
+
# Yard-Junk: get rid of junk in your YARD docs!
|
2
|
+
|
3
|
+
[](http://badge.fury.io/rb/yard-junk)
|
4
|
+
[](https://travis-ci.org/zverok/yard-junk)
|
5
|
+
|
6
|
+
Yard-Junk is [yard](https://github.com/lsegal/yard) plugin/patch, that provides:
|
7
|
+
|
8
|
+
* structured documentation error logging;
|
9
|
+
* documentation errors validator, ready to be integrated into CI pipeline.
|
10
|
+
|
11
|
+
## Showcase
|
12
|
+
|
13
|
+
Let's generate the docs for the [rom](https://github.com/rom-rb/rom) library.
|
14
|
+
|
15
|
+
Output of `yard doc` without JunkYard: [too huge to embed in README](https://gist.github.com/zverok/6dcf946d674e63545cee9f8a74e08728).
|
16
|
+
|
17
|
+
Things to notice:
|
18
|
+
|
19
|
+
* irregular and frequently approximate addresses (`in file 'core/lib/rom/types.rb':9`,
|
20
|
+
`in file 'core/lib/rom/global.rb' near line 41`, sometimes in separate line, sometimes inline),
|
21
|
+
hard to jump-to with any tool;
|
22
|
+
* a lot of ununderstood metaprogramming (grep for "Undocumentable mixin") -- nothing to fix here,
|
23
|
+
but YARD still notifies you;
|
24
|
+
* verbose and not very informative errors (look at that "Undocumentable mixin" -- and then grep
|
25
|
+
for "The proxy Coercible has not yet been recognized." and compare).
|
26
|
+
|
27
|
+
Output of `yard doc` with Yard-Junk:
|
28
|
+
|
29
|
+
```
|
30
|
+
core/lib/rom/global.rb:40: [InvalidTagFormat] Invalid tag format for @example
|
31
|
+
core/lib/rom/schema.rb:144: [MissingParamName] @param tag has empty parameter name
|
32
|
+
core/lib/rom/schema.rb:300: [MissingParamName] @param tag has empty parameter name
|
33
|
+
core/lib/rom/schema.rb:311: [MissingParamName] @param tag has empty parameter name
|
34
|
+
core/lib/rom/gateway.rb:171: [UnknownParam] @param tag has unknown parameter name: Transaction
|
35
|
+
core/lib/rom/relation.rb:297: [UnknownParam] @param tag has unknown parameter name: options
|
36
|
+
core/lib/rom/relation.rb:406: [UnknownParam] @param tag has unknown parameter name: new_options
|
37
|
+
core/lib/rom/relation.rb:524: [UnknownParam] @param tag has unknown parameter name: klass
|
38
|
+
core/lib/rom/attribute.rb:339: [MissingParamName] @param tag has empty parameter name
|
39
|
+
core/lib/rom/plugin_base.rb:38: [UnknownParam] @param tag has unknown parameter name: base. Did you mean `_base`?
|
40
|
+
core/lib/rom/configuration.rb:46: [UnknownParam] @param tag has unknown parameter name: The
|
41
|
+
core/lib/rom/configuration.rb:47: [UnknownParam] @param tag has unknown parameter name: Plugin. Did you mean `plugin`?
|
42
|
+
core/lib/rom/memory/dataset.rb:54: [UnknownParam] @param tag has unknown parameter name: names
|
43
|
+
core/lib/rom/plugin_registry.rb:140: [UnknownTag] Unknown tag @raises. Did you mean @raise?
|
44
|
+
core/lib/rom/plugin_registry.rb:187: [UnknownTag] Unknown tag @raises. Did you mean @raise?
|
45
|
+
core/lib/rom/relation/loaded.rb:91: [UnknownTag] Unknown tag @raises. Did you mean @raise?
|
46
|
+
core/lib/rom/command_registry.rb:52: [UnknownParam] @param tag has unknown parameter name: name
|
47
|
+
core/lib/rom/relation/curried.rb:69: [UnknownTag] Unknown tag @raises. Did you mean @raise?
|
48
|
+
core/lib/rom/global/plugin_dsl.rb:41: [UnknownParam] @param tag has unknown parameter name: adapter
|
49
|
+
core/lib/rom/relation/combined.rb:28: [MissingParamName] @param tag has empty parameter name
|
50
|
+
core/lib/rom/commands/class_interface.rb:78: [UnknownParam] @param tag has unknown parameter name: command
|
51
|
+
core/lib/rom/commands/class_interface.rb:79: [UnknownParam] @param tag has unknown parameter name: parent
|
52
|
+
core/lib/rom/commands/class_interface.rb:108: [UnknownParam] @param tag has unknown parameter name: options. Did you mean `_options`?
|
53
|
+
core/lib/rom/commands/class_interface.rb:118: [MissingParamName] @param tag has empty parameter name
|
54
|
+
core/lib/rom/associations/definitions/abstract.rb:66: [UnknownParam] @param tag has unknown parameter name: options
|
55
|
+
changeset/lib/rom/changeset.rb:79: [UnknownParam] @param tag has unknown parameter name: options. Did you mean `new_options`?
|
56
|
+
changeset/lib/rom/changeset/stateful.rb:219: [UnknownParam] @param tag has unknown parameter name: assoc
|
57
|
+
mapper/lib/rom/header.rb:47: [UnknownParam] @param tag has unknown parameter name: model
|
58
|
+
mapper/lib/rom/processor/transproc.rb:212: [MissingParamName] @param tag has empty parameter name
|
59
|
+
core/lib/rom/types.rb:1: [UnknownNamespace] namespace Coercible is not recognized
|
60
|
+
core/lib/rom/types.rb:1: [UnknownNamespace] namespace Coercible is not recognized
|
61
|
+
core/lib/rom/types.rb:1: [UnknownNamespace] namespace Coercible is not recognized
|
62
|
+
```
|
63
|
+
|
64
|
+
Things to notice:
|
65
|
+
|
66
|
+
* Regular output style with clearly recognizable addresses (and fixed to point at actual line with
|
67
|
+
the problematic tag, not the method which tag is related for);
|
68
|
+
* Error classes, allowing grouping, grepping, and configuring (notice no "Undocumentable xxx" errors:
|
69
|
+
I've just configured `yard-junk` to drop them for this repo);
|
70
|
+
* Usage of Ruby's bundled `did_you_mean` gem to show reasonable suggestions:
|
71
|
+
```
|
72
|
+
Unknown tag @raises. Did you mean @raise?
|
73
|
+
@param tag has unknown parameter name: options. Did you mean `new_options`?
|
74
|
+
```
|
75
|
+
* Rephrased and cleaned up messages.
|
76
|
+
|
77
|
+
`yard-junk` tool output:
|
78
|
+
|
79
|
+
```
|
80
|
+
Problems
|
81
|
+
--------
|
82
|
+
mistyped tags or other typos in documentation
|
83
|
+
|
84
|
+
changeset/lib/rom/changeset.rb:79: [UnknownParam] @param tag has unknown parameter name: options. Did you mean `new_options`?
|
85
|
+
changeset/lib/rom/changeset/stateful.rb:219: [UnknownParam] @param tag has unknown parameter name: assoc
|
86
|
+
core/lib/rom/associations/definitions/abstract.rb:66: [UnknownParam] @param tag has unknown parameter name: options
|
87
|
+
core/lib/rom/attribute.rb:339: [MissingParamName] @param tag has empty parameter name
|
88
|
+
core/lib/rom/command_registry.rb:52: [UnknownParam] @param tag has unknown parameter name: name
|
89
|
+
core/lib/rom/commands/class_interface.rb:78: [UnknownParam] @param tag has unknown parameter name: command
|
90
|
+
core/lib/rom/commands/class_interface.rb:79: [UnknownParam] @param tag has unknown parameter name: parent
|
91
|
+
core/lib/rom/commands/class_interface.rb:108: [UnknownParam] @param tag has unknown parameter name: options. Did you mean `_options`?
|
92
|
+
core/lib/rom/commands/class_interface.rb:118: [MissingParamName] @param tag has empty parameter name
|
93
|
+
core/lib/rom/configuration.rb:46: [UnknownParam] @param tag has unknown parameter name: The
|
94
|
+
core/lib/rom/configuration.rb:47: [UnknownParam] @param tag has unknown parameter name: Plugin. Did you mean `plugin`?
|
95
|
+
core/lib/rom/gateway.rb:171: [UnknownParam] @param tag has unknown parameter name: Transaction
|
96
|
+
core/lib/rom/global.rb:40: [InvalidTagFormat] Invalid tag format for @example
|
97
|
+
core/lib/rom/global/plugin_dsl.rb:41: [UnknownParam] @param tag has unknown parameter name: adapter
|
98
|
+
core/lib/rom/memory/dataset.rb:54: [UnknownParam] @param tag has unknown parameter name: names
|
99
|
+
core/lib/rom/plugin_base.rb:38: [UnknownParam] @param tag has unknown parameter name: base. Did you mean `_base`?
|
100
|
+
core/lib/rom/plugin_registry.rb:140: [UnknownTag] Unknown tag @raises. Did you mean @raise?
|
101
|
+
core/lib/rom/plugin_registry.rb:187: [UnknownTag] Unknown tag @raises. Did you mean @raise?
|
102
|
+
core/lib/rom/relation.rb:297: [UnknownParam] @param tag has unknown parameter name: options
|
103
|
+
core/lib/rom/relation.rb:406: [UnknownParam] @param tag has unknown parameter name: new_options
|
104
|
+
core/lib/rom/relation.rb:524: [UnknownParam] @param tag has unknown parameter name: klass
|
105
|
+
core/lib/rom/relation/combined.rb:28: [MissingParamName] @param tag has empty parameter name
|
106
|
+
core/lib/rom/relation/curried.rb:69: [UnknownTag] Unknown tag @raises. Did you mean @raise?
|
107
|
+
core/lib/rom/relation/loaded.rb:91: [UnknownTag] Unknown tag @raises. Did you mean @raise?
|
108
|
+
core/lib/rom/schema.rb:144: [MissingParamName] @param tag has empty parameter name
|
109
|
+
core/lib/rom/schema.rb:300: [MissingParamName] @param tag has empty parameter name
|
110
|
+
core/lib/rom/schema.rb:311: [MissingParamName] @param tag has empty parameter name
|
111
|
+
core/lib/rom/types.rb:1: [UnknownNamespace] namespace Coercible is not recognized
|
112
|
+
core/lib/rom/types.rb:1: [UnknownNamespace] namespace Coercible is not recognized
|
113
|
+
core/lib/rom/types.rb:1: [UnknownNamespace] namespace Coercible is not recognized
|
114
|
+
mapper/lib/rom/header.rb:47: [UnknownParam] @param tag has unknown parameter name: model
|
115
|
+
mapper/lib/rom/processor/transproc.rb:212: [MissingParamName] @param tag has empty parameter name
|
116
|
+
|
117
|
+
0 failures, 32 problems (2 seconds to run)
|
118
|
+
```
|
119
|
+
|
120
|
+
It is basically the same as above, and:
|
121
|
+
|
122
|
+
* sorted by files/lines instead of "reported when found" approach;
|
123
|
+
* with short stats at the end;
|
124
|
+
* returning proper exit code (0 if no problems/parsing errors, non-0 otherwise), which allows `yard-junk`
|
125
|
+
to be integrated into CI pipeline, and control that new PRs will not screw docs (forgetting to
|
126
|
+
rename parameters in docs when they are renamed in code, for example).
|
127
|
+
|
128
|
+
As a nice addition, `yard-junk` command uses its own links to code objects resolver, which is 10x
|
129
|
+
faster (and, eventually, more correct) than YARD's own approach to resolve links when rendering docs.
|
130
|
+
|
131
|
+
## Usage
|
132
|
+
|
133
|
+
It is a `yard-junk` gem, install it as usual, or add to your `Gemfile`.
|
134
|
+
|
135
|
+
### Better logs
|
136
|
+
|
137
|
+
Add this to your `.yardopts` file:
|
138
|
+
```
|
139
|
+
--plugin junk
|
140
|
+
```
|
141
|
+
|
142
|
+
After that, just run `yard` or `yard doc` as usual, and enjoy better logs! You can also setup JunkYard
|
143
|
+
logs by passing options (in the same `.yardopts`):
|
144
|
+
```
|
145
|
+
--junk-log-format FORMAT_STR
|
146
|
+
--junk-log-ignore ERROR_TYPE1,ERROR_TYPE2,...
|
147
|
+
```
|
148
|
+
|
149
|
+
Format is usual Ruby's [#format](https://ruby-doc.org/core-2.2.3/Kernel.html#method-i-format) method
|
150
|
+
with named fields:
|
151
|
+
* `message` -- error message;
|
152
|
+
* `file` -- error file;
|
153
|
+
* `line` -- error line;
|
154
|
+
* `type` -- error type.
|
155
|
+
|
156
|
+
Default format is `%{file}:%{line}: [%{type}] %{message}`, as shown above.
|
157
|
+
|
158
|
+
`--junk-log-ignore` option allows to ingore error classes by their type names (shown in logs in `[]`).
|
159
|
+
By default, `Undocumentable` error is ignored: it is produced as metaprogramming pieces of code like
|
160
|
+
```ruby
|
161
|
+
attr_reader *OPTIONS
|
162
|
+
```
|
163
|
+
or
|
164
|
+
```ruby
|
165
|
+
include Rails.routes
|
166
|
+
```
|
167
|
+
...and typically have no way to fix, while polluting logs with a lot of, well, junk.
|
168
|
+
|
169
|
+
### Standalone docs check
|
170
|
+
|
171
|
+
Just run `yard-junk` command after gem is installed. It should work :)
|
172
|
+
|
173
|
+
### Rake task (integrating in CI)
|
174
|
+
|
175
|
+
Add this to your `Rakefile`:
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
require 'yard-junk/rake'
|
179
|
+
YardJunk::Rake.task
|
180
|
+
```
|
181
|
+
|
182
|
+
and then run it (or add to your `.travis.yml`) as
|
183
|
+
```
|
184
|
+
rake yard:junk
|
185
|
+
```
|
186
|
+
|
187
|
+
## Reasons
|
188
|
+
|
189
|
+
Small problems in docs lead to decrease of readability and usability. But it is hard to check for
|
190
|
+
all those problems manually due to YARD's cumbersome output, and lack of CI-ready doc checking tools.
|
191
|
+
|
192
|
+
Idea of regularly structured logger was initially [proposed](https://github.com/lsegal/yard/issues/1007)
|
193
|
+
as an enchancement for YARD itself, and even some steps were maid by YARD's author in that direction,
|
194
|
+
but the idea was abandoned since.
|
195
|
+
|
196
|
+
Therefore, this independent tool was made.
|
197
|
+
|
198
|
+
## Caveats
|
199
|
+
|
200
|
+
* Sometimes YARD doesn't provide enough information to guess in which line of code the problem is;
|
201
|
+
in those cases `junk-yard` just writes something like `file.rb:1` (to stay consistent and not break
|
202
|
+
go-to-file tools);
|
203
|
+
* Checking of links to files and URLs proven to be incomplete.
|
204
|
+
|
205
|
+
## Roadmap
|
206
|
+
|
207
|
+
* Docs for usage as a system-wide YARD plugin;
|
208
|
+
* Docs for internals;
|
209
|
+
* Colorized output for text reporter;
|
210
|
+
* HTML reporter for CIs allowing to store build artifacts;
|
211
|
+
* Documentation quality checks as a next level of YARD checker;
|
212
|
+
* Option to check new/updated code only (integration with git history)?
|
213
|
+
|
214
|
+
## Some examples of problems found in popular gems:
|
215
|
+
|
216
|
+
**NB: All of those are excellent libs! The showcase is of "how hard it is to maintain docs quality",
|
217
|
+
not of "how ignorant other programmers are".**
|
218
|
+
|
219
|
+
httparty:
|
220
|
+
```
|
221
|
+
lib/httparty/exceptions.rb:2: [UnknownTag] Unknown tag @abstact. Did you mean @abstract?
|
222
|
+
lib/httparty/exceptions.rb:20: [MissingParamName] @param tag has empty parameter name
|
223
|
+
```
|
224
|
+
|
225
|
+
vcr:
|
226
|
+
```
|
227
|
+
lib/vcr/deprecations.rb:71: [UnknownParam] @param tag has unknown parameter name: name
|
228
|
+
lib/vcr/deprecations.rb:73: [UnknownParam] @param tag has unknown parameter name: options
|
229
|
+
lib/vcr/linked_cassette.rb:12: [UnknownParam] @param tag has unknown parameter name: context-owned
|
230
|
+
lib/vcr/linked_cassette.rb:13: [UnknownParam] @param tag has unknown parameter name: context-unowned
|
231
|
+
lib/vcr/linked_cassette.rb:55: [UnknownParam] @param tag has unknown parameter name: context-owned
|
232
|
+
lib/vcr/linked_cassette.rb:56: [UnknownParam] @param tag has unknown parameter name: context-unowned
|
233
|
+
lib/vcr/test_frameworks/cucumber.rb:27: [UnknownParam] @param tag has unknown parameter name: options
|
234
|
+
```
|
235
|
+
|
236
|
+
eventmachine:
|
237
|
+
```
|
238
|
+
lib/em/channel.rb:39: [UnknownParam] @param tag has unknown parameter name: Subscriber
|
239
|
+
lib/em/connection.rb:603: [InvalidLink] Cannot resolve link to Socket.unpack_sockaddr_in from text: {Socket.unpack_sockaddr_in}
|
240
|
+
lib/em/connection.rb:726: [InvalidLink] Cannot resolve link to EventMachine.notify_readable from text: {EventMachine.notify_readable}
|
241
|
+
lib/em/connection.rb:726: [InvalidLink] Cannot resolve link to EventMachine.notify_writable from text: {EventMachine.notify_writable}
|
242
|
+
lib/em/connection.rb:739: [InvalidLink] Cannot resolve link to EventMachine.notify_readable from text: {EventMachine.notify_readable}
|
243
|
+
lib/em/connection.rb:739: [InvalidLink] Cannot resolve link to EventMachine.notify_writable from text: {EventMachine.notify_writable}
|
244
|
+
lib/em/protocols/httpclient2.rb:263: [InvalidLink] Cannot resolve link to |response| from text: {|response| puts response.content }
|
245
|
+
lib/em/protocols/httpclient2.rb:276: [InvalidLink] Cannot resolve link to |response| from text: {|response| puts response.content }
|
246
|
+
lib/em/protocols/line_protocol.rb:9: [InvalidLink] Cannot resolve link to line from text: {line}
|
247
|
+
lib/em/protocols/object_protocol.rb:9: [InvalidLink] Cannot resolve link to 'you from text: {'you said' => obj}
|
248
|
+
lib/em/protocols/smtpclient.rb:138: [InvalidLink] Cannot resolve link to "Subject" from text: {"Subject" => "Bogus", "CC" => "<a href="mailto:myboss@example.com">myboss@example.com</a>"}
|
249
|
+
lib/em/protocols/smtpclient.rb:138: [InvalidLink] Cannot resolve link to :type=>:plain, from text: {:type=>:plain, :username=>"<a href="mailto:mickey@disney.com">mickey@disney.com</a>", :password=>"mouse"}
|
250
|
+
lib/em/protocols/smtpserver.rb:435: [InvalidLink] Cannot resolve link to :cert_chain_file from text: {:cert_chain_file => "/etc/ssl/cert.pem", :private_key_file => "/etc/ssl/private/cert.key"}
|
251
|
+
lib/em/protocols/socks4.rb:13: [InvalidLink] Cannot resolve link to data from text: {data}
|
252
|
+
lib/em/spawnable.rb:47: [InvalidLink] Cannot resolve link to xxx from text: {xxx}
|
253
|
+
lib/eventmachine.rb:215: [InvalidLink] Cannot resolve link to EventMachine.stop from text: {EventMachine.stop}
|
254
|
+
lib/eventmachine.rb:231: [InvalidLink] Cannot resolve link to EventMachine::Callback from text: {EventMachine::Callback}
|
255
|
+
lib/eventmachine.rb:319: [UnknownParam] @param tag has unknown parameter name: delay
|
256
|
+
lib/eventmachine.rb:345: [UnknownParam] @param tag has unknown parameter name: delay
|
257
|
+
```
|
258
|
+
|
259
|
+
addressable:
|
260
|
+
```
|
261
|
+
lib/addressable/template.rb:197: [UnknownParam] @param tag has unknown parameter name: *indexes. Did you mean `indexes`?
|
262
|
+
lib/addressable/uri.rb:296: [UnknownParam] @param tag has unknown parameter name: *uris. Did you mean `uris`?
|
263
|
+
lib/addressable/uri.rb:1842: [UnknownParam] @param tag has unknown parameter name: The
|
264
|
+
lib/addressable/uri.rb:1943: [UnknownParam] @param tag has unknown parameter name: The
|
265
|
+
lib/addressable/uri.rb:1958: [UnknownParam] @param tag has unknown parameter name: The
|
266
|
+
lib/addressable/uri.rb:2023: [UnknownParam] @param tag has unknown parameter name: The
|
267
|
+
lib/addressable/uri.rb:2244: [UnknownParam] @param tag has unknown parameter name: *components. Did you mean `components`?
|
268
|
+
lib/addressable/uri.rb:2275: [UnknownParam] @param tag has unknown parameter name: *components. Did you mean `components`?
|
269
|
+
```
|
270
|
+
|
271
|
+
hashie:
|
272
|
+
```
|
273
|
+
lib/hashie/extensions/coercion.rb:68: [UnknownParam] @param tag has unknown parameter name: key
|
274
|
+
lib/hashie/extensions/coercion.rb:69: [UnknownParam] @param tag has unknown parameter name: into
|
275
|
+
lib/hashie/extensions/deep_find.rb:7: [InvalidLink] Cannot resolve link to user: from text: {user: {location: {address: '123 Street'}
|
276
|
+
lib/hashie/extensions/deep_find.rb:7: [InvalidLink] Cannot resolve link to user: from text: {user: {location: {address: '123 Street'}
|
277
|
+
lib/hashie/extensions/deep_find.rb:16: [InvalidLink] Cannot resolve link to location: from text: {location: {address: '123 Street'}
|
278
|
+
lib/hashie/extensions/deep_find.rb:16: [InvalidLink] Cannot resolve link to location: from text: {location: {address: '123 Street'}
|
279
|
+
lib/hashie/extensions/deep_find.rb:27: [InvalidLink] Cannot resolve link to users: from text: {users: [{location: {address: '123 Street'}
|
280
|
+
lib/hashie/extensions/deep_find.rb:27: [InvalidLink] Cannot resolve link to users: from text: {users: [{location: {address: '123 Street'}
|
281
|
+
lib/hashie/extensions/deep_find.rb:36: [InvalidLink] Cannot resolve link to location: from text: {location: {address: '123 Street'}
|
282
|
+
lib/hashie/extensions/deep_find.rb:36: [InvalidLink] Cannot resolve link to location: from text: {location: {address: '123 Street'}
|
283
|
+
lib/hashie/extensions/deep_find.rb:36: [InvalidLink] Cannot resolve link to location: from text: {location: {address: '234 Street'}
|
284
|
+
lib/hashie/extensions/deep_find.rb:36: [InvalidLink] Cannot resolve link to location: from text: {location: {address: '234 Street'}
|
285
|
+
lib/hashie/extensions/deep_find.rb:36: [InvalidLink] Cannot resolve link to location: from text: {location: {address: '234 Street'}
|
286
|
+
lib/hashie/extensions/deep_find.rb:36: [InvalidLink] Cannot resolve link to location: from text: {location: {address: '234 Street'}
|
287
|
+
lib/hashie/mash.rb:32: [InvalidLink] Cannot resolve link to :a from text: {:a => {:b => 23, :d => {:e => "abc"}
|
288
|
+
lib/hashie/mash.rb:32: [InvalidLink] Cannot resolve link to :g from text: {:g => 44, :h => 29}
|
289
|
+
```
|
290
|
+
|
291
|
+
## Authors
|
292
|
+
|
293
|
+
[Victor Shepelev](https://github.com/zverok)
|
294
|
+
|
295
|
+
## License
|
296
|
+
|
297
|
+
MIT
|
data/bin/yard-junk
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.join(File.dirname(File.expand_path(__FILE__)), '..', 'lib'))
|
5
|
+
|
6
|
+
require 'yard'
|
7
|
+
require 'yard-junk'
|
8
|
+
|
9
|
+
janitor = YardJunk::Janitor.new
|
10
|
+
janitor.run
|
11
|
+
exit janitor.report(YardJunk::Janitor::TextReporter.new(STDOUT))
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# This file tries to show as much errors in documentation as possible
|
2
|
+
|
3
|
+
# Macros attached to class
|
4
|
+
# @!macro [attach] attached4
|
5
|
+
# $1 $2 $3
|
6
|
+
class A
|
7
|
+
# Wrong macro directive format:
|
8
|
+
#
|
9
|
+
# @!macro
|
10
|
+
#
|
11
|
+
# Unknown directive:
|
12
|
+
# @!wtf
|
13
|
+
|
14
|
+
|
15
|
+
# @wrong Free hanging unknown tag
|
16
|
+
|
17
|
+
# Unknown macro name:
|
18
|
+
# @!macro wtf
|
19
|
+
# Points to unknown class: {B}
|
20
|
+
#
|
21
|
+
# @wrong This is unknown tag
|
22
|
+
#
|
23
|
+
# @param arg1 [C] Link to unknown class.
|
24
|
+
# @param arg3 This is unknown argument.
|
25
|
+
#
|
26
|
+
def foo(arg1, arg2)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param para
|
30
|
+
# @param para
|
31
|
+
# @example
|
32
|
+
# @see {invalid}
|
33
|
+
def bar(para)
|
34
|
+
end
|
35
|
+
|
36
|
+
OPTIONS = %i[foo bar baz]
|
37
|
+
# undocumentable attr_reader
|
38
|
+
attr_reader *OPTIONS
|
39
|
+
end
|
40
|
+
|
41
|
+
# not recognize namespace
|
42
|
+
Bar::BOOKS = 5
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module YardJunk
|
4
|
+
module CommandLineOptions
|
5
|
+
def common_options(opts) # rubocop:disable Metrics/MethodLength
|
6
|
+
super
|
7
|
+
|
8
|
+
opts.separator ''
|
9
|
+
opts.separator 'YardJunk plugin options'
|
10
|
+
|
11
|
+
opts.on('--junk-log-format [FMT]', "YardJunk::Logger format string, by default #{Logger::Message::DEFAULT_FORMAT.inspect}") do |format|
|
12
|
+
Logger.instance.format = format
|
13
|
+
end
|
14
|
+
|
15
|
+
opts.on('--junk-log-ignore [TYPE1,TYPE2,...]', "YardJunk::Logger message types to ignore, by default #{Logger::DEFAULT_IGNORE.map(&:inspect).join(', ')}") do |ignore|
|
16
|
+
Logger.instance.ignore = ignore.to_s.split(',')
|
17
|
+
end
|
18
|
+
|
19
|
+
opts.separator ''
|
20
|
+
opts.separator 'Generic options'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module YardJunk
|
4
|
+
class Janitor
|
5
|
+
# This class is a base for reporters that could be passed to
|
6
|
+
# {Janitor#report}.
|
7
|
+
#
|
8
|
+
# Basically, the reporter should define methods:
|
9
|
+
# * `header(title, explanation)` for printing problems section header;
|
10
|
+
# * `row(msg)` for printing instance of {Logger::Message};
|
11
|
+
# * `_stats(**statistics)` for printing statistics.
|
12
|
+
#
|
13
|
+
# Reporter also could redefine `finalize()` method, if it wants to do
|
14
|
+
# something at the end of a report (like "add footer and save to file").
|
15
|
+
#
|
16
|
+
class BaseReporter
|
17
|
+
# @overload initialize(io)
|
18
|
+
# @param io [#puts] Any IO-alike object that defines `puts` method.
|
19
|
+
#
|
20
|
+
# @overload initialize(filename)
|
21
|
+
# @param filename [String] Name of file to save the output.
|
22
|
+
def initialize(io_or_filename)
|
23
|
+
@io =
|
24
|
+
case io_or_filename
|
25
|
+
when ->(i) { i.respond_to?(:puts) } # quacks!
|
26
|
+
io_or_filename
|
27
|
+
when String
|
28
|
+
File.open(io_or_filename, 'w')
|
29
|
+
else
|
30
|
+
fail ArgumentError, "Can't create reporter with #{io_or_filename.class}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def finalize; end
|
35
|
+
|
36
|
+
def section(title, explanation, messages)
|
37
|
+
return if messages.empty?
|
38
|
+
header(title, explanation)
|
39
|
+
|
40
|
+
messages
|
41
|
+
.sort_by { |m| [m.file || '\uFFFF', m.line || 1000, m.message] }
|
42
|
+
.each(&method(:row))
|
43
|
+
end
|
44
|
+
|
45
|
+
def stats(**stat)
|
46
|
+
_stats(stat.merge(duration: humanize_duration(stat[:duration])))
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def _stats
|
52
|
+
fail NotImplementedError
|
53
|
+
end
|
54
|
+
|
55
|
+
def header(_title, _explanation)
|
56
|
+
fail NotImplementedError
|
57
|
+
end
|
58
|
+
|
59
|
+
def row(_message)
|
60
|
+
fail NotImplementedError
|
61
|
+
end
|
62
|
+
|
63
|
+
def humanize_duration(duration)
|
64
|
+
if duration < 60
|
65
|
+
'%i seconds' % duration
|
66
|
+
else
|
67
|
+
'%.1f minutes' % (duration / 60)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module YardJunk
|
4
|
+
class Janitor
|
5
|
+
# TODO: Tests
|
6
|
+
class Resolver
|
7
|
+
include YARD::Templates::Helpers::HtmlHelper
|
8
|
+
|
9
|
+
MESSAGE_PATTERN = 'In file `%{file}\':%{line}: Cannot resolve link to %{name} from text: %{link}'
|
10
|
+
|
11
|
+
def self.resolve_all(yard_options)
|
12
|
+
YARD::Registry.all.map(&:base_docstring).each { |ds| new(ds, yard_options).resolve }
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(docstring, yard_options)
|
16
|
+
@docstring = docstring
|
17
|
+
@options = yard_options
|
18
|
+
end
|
19
|
+
|
20
|
+
def resolve
|
21
|
+
markup_meth = "html_markup_#{options.markup}"
|
22
|
+
return unless respond_to?(markup_meth)
|
23
|
+
send(markup_meth, @docstring)
|
24
|
+
.gsub(%r{<(code|tt|pre)[^>]*>(.*?)</\1>}im, '')
|
25
|
+
.scan(/{[^}]+}/).flatten
|
26
|
+
.map(&CGI.method(:unescapeHTML))
|
27
|
+
.each(&method(:try_resolve))
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :options
|
33
|
+
|
34
|
+
def try_resolve(link)
|
35
|
+
name, _comment = link.tr('{}', '').split(/\s+/, 2)
|
36
|
+
resolved = YARD::Registry.resolve(@docstring.object, name, true, true)
|
37
|
+
return unless resolved.is_a?(YARD::CodeObjects::Proxy)
|
38
|
+
Logger.instance.register(MESSAGE_PATTERN % {file: object.file, line: object.line, name: name, link: link})
|
39
|
+
end
|
40
|
+
|
41
|
+
def object
|
42
|
+
@docstring.object
|
43
|
+
end
|
44
|
+
|
45
|
+
# required by HtmlHelper
|
46
|
+
def serializer
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module YardJunk
|
4
|
+
class Janitor
|
5
|
+
# Reporter that just outputs everything in plaintext format. Useful
|
6
|
+
# for commandline usage. See {BaseReporter} for details about reporters.
|
7
|
+
#
|
8
|
+
class TextReporter < BaseReporter
|
9
|
+
private
|
10
|
+
|
11
|
+
def _stats(**stat)
|
12
|
+
line =
|
13
|
+
'%<errors>i failures, %<problems>i problems (%<duration>s to run)' % stat
|
14
|
+
@io.puts "\n#{line}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def header(title, explanation)
|
18
|
+
@io.puts
|
19
|
+
@io.puts title
|
20
|
+
@io.puts '-' * title.length
|
21
|
+
@io.puts explanation + "\n\n"
|
22
|
+
end
|
23
|
+
|
24
|
+
def row(msg)
|
25
|
+
@io.puts msg.to_s # default Message#to_s is good enough
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'benchmark'
|
4
|
+
|
5
|
+
module YardJunk
|
6
|
+
class Janitor
|
7
|
+
def run(*opts)
|
8
|
+
YARD::Registry.clear # Somehow loads all Ruby stdlib classes before Rake task started...
|
9
|
+
Logger.instance.format = nil
|
10
|
+
|
11
|
+
puts "Running YardJunk janitor...\n\n"
|
12
|
+
|
13
|
+
@duration = Benchmark.realtime do
|
14
|
+
command = YARD::CLI::Yardoc.new
|
15
|
+
command.run('--no-save', '--no-progress', '--no-stats', '--no-output', *opts)
|
16
|
+
Resolver.resolve_all(command.options)
|
17
|
+
end
|
18
|
+
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def stats
|
23
|
+
{
|
24
|
+
errors: errors.count,
|
25
|
+
problems: problems.count,
|
26
|
+
duration: @duration || 0
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def report(*reporters)
|
31
|
+
reporters.each do |reporter|
|
32
|
+
reporter.section('Errors', 'severe code or formatting problems', errors)
|
33
|
+
reporter.section('Problems', 'mistyped tags or other typos in documentation', problems)
|
34
|
+
|
35
|
+
reporter.stats(stats)
|
36
|
+
reporter.finalize
|
37
|
+
end
|
38
|
+
|
39
|
+
exit_code
|
40
|
+
end
|
41
|
+
|
42
|
+
def exit_code
|
43
|
+
return 2 unless errors.empty?
|
44
|
+
return 1 unless problems.empty?
|
45
|
+
0
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def messages
|
51
|
+
YardJunk::Logger.instance.messages.grep_v(Logger::Undocumentable) # FIXME: Not DRY
|
52
|
+
end
|
53
|
+
|
54
|
+
def errors
|
55
|
+
messages.select(&:error?)
|
56
|
+
end
|
57
|
+
|
58
|
+
def problems
|
59
|
+
messages.select(&:warn?)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
require_relative 'janitor/base_reporter'
|
65
|
+
require_relative 'janitor/text_reporter'
|
66
|
+
require_relative 'janitor/resolver'
|
@@ -0,0 +1,253 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'did_you_mean'
|
4
|
+
|
5
|
+
module YardJunk
|
6
|
+
class Logger
|
7
|
+
class Message
|
8
|
+
attr_reader :message, :severity, :file, :line, :extra
|
9
|
+
|
10
|
+
def initialize(message:, severity: :warn, code_object: nil, file: nil, line: nil, **extra)
|
11
|
+
@message = message.gsub(/\s{2,}/, ' ')
|
12
|
+
@file = file
|
13
|
+
@line = line&.to_i
|
14
|
+
@code_object = code_object
|
15
|
+
@severity = severity
|
16
|
+
@extra = extra
|
17
|
+
end
|
18
|
+
|
19
|
+
%i[error warn].each do |sev|
|
20
|
+
define_method("#{sev}?") { severity == sev }
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_h
|
24
|
+
{
|
25
|
+
type: type,
|
26
|
+
message: message,
|
27
|
+
file: file,
|
28
|
+
line: line&.to_i || 1
|
29
|
+
}.merge(extra)
|
30
|
+
end
|
31
|
+
|
32
|
+
def ==(other)
|
33
|
+
other.is_a?(self.class) && to_h == other.to_h
|
34
|
+
end
|
35
|
+
|
36
|
+
DEFAULT_FORMAT = '%{file}:%{line}: [%{type}] %{message}'
|
37
|
+
|
38
|
+
def to_s(format = DEFAULT_FORMAT)
|
39
|
+
format % to_h
|
40
|
+
end
|
41
|
+
|
42
|
+
def type
|
43
|
+
self.class.type
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# DidYouMean changed API dramatically between 1.0 and 1.1, and different rubies have different
|
49
|
+
# versions of it bundled.
|
50
|
+
if DidYouMean.const_defined?(:SpellCheckable)
|
51
|
+
class SpellChecker < Struct.new(:error, :dictionary) # rubocop:disable Style/StructInheritance
|
52
|
+
include DidYouMean::SpellCheckable
|
53
|
+
|
54
|
+
def candidates
|
55
|
+
{error => dictionary}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def spell_check(error, dictionary)
|
60
|
+
SpellChecker.new(error, dictionary).corrections
|
61
|
+
end
|
62
|
+
elsif DidYouMean.const_defined?(:SpellChecker)
|
63
|
+
def spell_check(error, dictionary)
|
64
|
+
DidYouMean::SpellChecker.new(dictionary: dictionary).correct(error)
|
65
|
+
end
|
66
|
+
else
|
67
|
+
def spell_check(*)
|
68
|
+
[]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class << self
|
73
|
+
def registry
|
74
|
+
@registry ||= []
|
75
|
+
end
|
76
|
+
|
77
|
+
def pattern(regexp)
|
78
|
+
@pattern = regexp
|
79
|
+
Message.registry << self
|
80
|
+
end
|
81
|
+
|
82
|
+
def search_up(pattern) # rubocop:disable Style/TrivialAccessors
|
83
|
+
@search_up = pattern
|
84
|
+
end
|
85
|
+
|
86
|
+
def try_parse(line, **context)
|
87
|
+
@pattern or fail StandardError, "Pattern is not defined for #{self}"
|
88
|
+
match = @pattern.match(line) or return nil
|
89
|
+
data = context.reject { |_, v| v.nil? }
|
90
|
+
.merge(match.names.map(&:to_sym).zip(match.captures).to_h.reject { |_, v| v.nil? })
|
91
|
+
data = guard_line(data)
|
92
|
+
new(**data)
|
93
|
+
end
|
94
|
+
|
95
|
+
def type
|
96
|
+
name&.end_with?('::Message') ? 'UnknownError' : name&.sub(/^.+::/, '')
|
97
|
+
end
|
98
|
+
|
99
|
+
def valid_type?(type)
|
100
|
+
type == 'UnknownError' || registry.any? { |m| m.type == type }
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def guard_line(data) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
106
|
+
# FIXME: Ugly, huh?
|
107
|
+
data[:file] && data[:line] && @search_up or return data
|
108
|
+
data = data.merge(line: data[:line].to_i)
|
109
|
+
data = data.merge(code_object: find_object(data[:file], data[:line]))
|
110
|
+
lines = File.readlines(data[:file]) rescue (return data) # rubocop:disable Style/RescueModifier
|
111
|
+
pattern = Regexp.new(@search_up % data.map { |k, v| [k, Regexp.escape(v.to_s)] }.to_h)
|
112
|
+
_, num = lines.map
|
113
|
+
.with_index { |ln, i| [ln, i + 1] }
|
114
|
+
.first(data[:line]).reverse
|
115
|
+
.detect { |ln, _| pattern.match(ln) }
|
116
|
+
num or return data
|
117
|
+
|
118
|
+
data.merge(line: num)
|
119
|
+
end
|
120
|
+
|
121
|
+
def find_object(file, line)
|
122
|
+
YARD::Registry.detect { |o| o.file == file && o.line == line }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class UnknownTag < Message
|
128
|
+
pattern %r{^(?<message>Unknown tag (?<tag>@\S+))( in file `(?<file>[^`]+)` near line (?<line>\d+))?$}
|
129
|
+
search_up '%{tag}(\W|$)'
|
130
|
+
|
131
|
+
def message
|
132
|
+
corrections.empty? ? super : "#{super}. Did you mean #{corrections.map { |c| "@#{c}" }.join(', ')}?"
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def corrections
|
138
|
+
spell_check(extra[:tag], YARD::Tags::Library.labels.keys.map(&:to_s))
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class InvalidTagFormat < Message
|
143
|
+
pattern %r{^(?<message>Invalid tag format for (?<tag>@\S+))( in file `(?<file>[^`]+)` near line (?<line>\d+))?$}
|
144
|
+
search_up '%{tag}(\W|$)'
|
145
|
+
end
|
146
|
+
|
147
|
+
class UnknownDirective < Message
|
148
|
+
pattern %r{^(?<message>Unknown directive (?<directive>@!\S+))( in file `(?<file>[^`]+)` near line (?<line>\d+))?$}
|
149
|
+
search_up '%{directive}(\W|$)'
|
150
|
+
|
151
|
+
# TODO: did_you_mean?
|
152
|
+
end
|
153
|
+
|
154
|
+
class InvalidDirectiveFormat < Message
|
155
|
+
pattern %r{^(?<message>Invalid directive format for (?<directive>@!\S+))( in file `(?<file>[^`]+)` near line (?<line>\d+))?$}
|
156
|
+
search_up '%{directive}(\W|$)'
|
157
|
+
end
|
158
|
+
|
159
|
+
class UnknownParam < Message
|
160
|
+
pattern %r{^(?<message>@param tag has unknown parameter name: (?<param_name>\S+))\s+ in file `(?<file>[^']+)' near line (?<line>\d+)$}
|
161
|
+
search_up '@param(\s+\[.+?\])?\s+?%{param_name}(\W|$)'
|
162
|
+
|
163
|
+
def message
|
164
|
+
corrections.empty? ? super : "#{super}. Did you mean #{corrections.map { |c| "`#{c}`" }.join(', ')}?"
|
165
|
+
end
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
def corrections
|
170
|
+
spell_check(extra[:param_name], known_params)
|
171
|
+
end
|
172
|
+
|
173
|
+
def known_params
|
174
|
+
@code_object.is_a?(YARD::CodeObjects::MethodObject) or return []
|
175
|
+
@code_object.parameters.map(&:first).map { |p| p.tr('*&:', '') }
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
class MissingParamName < Message
|
180
|
+
pattern %r{^(?<message>@param tag has unknown parameter name):\s+in file `(?<file>[^']+)' near line (?<line>\d+)$}
|
181
|
+
search_up '@param(\s+\[.+?\])?\s*$'
|
182
|
+
|
183
|
+
def message
|
184
|
+
'@param tag has empty parameter name'
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
class DuplicateParam < Message
|
189
|
+
pattern %r{^(?<message>@param tag has duplicate parameter name: (?<param_name>\S+))\s+ in file `(?<file>[^']+)' near line (?<line>\d+)$}
|
190
|
+
search_up '@param\s+(\[.+?\]\s+)?%{param_name}(\W|$)'
|
191
|
+
end
|
192
|
+
|
193
|
+
class RedundantBraces < Message
|
194
|
+
pattern %r{^(?<message>@see tag \(\#\d+\) should not be wrapped in \{\} \(causes rendering issues\)):\s+in file `(?<file>[^']+)' near line (?<line>\d+)$}
|
195
|
+
search_up '@see.*{.*}'
|
196
|
+
|
197
|
+
def message
|
198
|
+
super.sub(/\s+\(\#\d+\)\s+/, ' ')
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
class SyntaxError < Message
|
203
|
+
pattern %r{^Syntax error in `(?<file>[^`]+)`:\((?<line>\d+),(?:\d+)\): (?<message>.+)$}
|
204
|
+
|
205
|
+
# Honestly, IDK why YARD considers it "warning"... So, rewriting
|
206
|
+
def severity
|
207
|
+
:error
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
class CircularReference < Message
|
212
|
+
pattern %r{^(?<file>.+?):(?<line>\d+): (?<message>Detected circular reference tag in `(?<object>[^']+)', ignoring all reference tags for this object \((?<context>[^)]+)\)\.)$}
|
213
|
+
end
|
214
|
+
|
215
|
+
class Undocumentable < Message
|
216
|
+
pattern %r{^in (?:\S+): (?<message>Undocumentable (?<object>.+?))\n\s*in file '(?<file>[^']+)':(?<line>\d+):\s+(?:\d+):\s*(?<quote>.+?)\s*$}
|
217
|
+
|
218
|
+
def message
|
219
|
+
super + ": `#{quote}`"
|
220
|
+
end
|
221
|
+
|
222
|
+
def quote
|
223
|
+
extra[:quote]
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
class UnknownNamespace < Message
|
228
|
+
pattern %r{^(?<message>The proxy (?<namespace>\S+?) has not yet been recognized).\nIf this class/method is part of your source tree, this will affect your documentation results.\nYou can correct this issue by loading the source file for this object before `(?<file>[^']+)'\n$}
|
229
|
+
|
230
|
+
def namespace
|
231
|
+
extra[:namespace]
|
232
|
+
end
|
233
|
+
|
234
|
+
def message
|
235
|
+
"namespace #{namespace} is not recognized"
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
class MacroAttachError < Message
|
240
|
+
pattern %r{^(?<message>Attaching macros to non-methods is unsupported, ignoring: (?<object>\S+)) \((?<file>.+?):(?<line>\d+)\)$}
|
241
|
+
search_up '@!macro \[attach\]'
|
242
|
+
end
|
243
|
+
|
244
|
+
class MacroNameError < Message
|
245
|
+
pattern %r{^(?<message>Invalid/missing macro name for (?<object>\S+)) \((?<file>.+?):(?<line>\d+)\)$}
|
246
|
+
end
|
247
|
+
|
248
|
+
class InvalidLink < Message
|
249
|
+
pattern %r{^In file `(?<file>[^']+)':(?<line>\d+): (?<message>Cannot resolve link to (?<object>\S+) from text:\s+(?<quote>.+))$}
|
250
|
+
search_up '%{quote}'
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
require 'pp'
|
5
|
+
|
6
|
+
module YardJunk
|
7
|
+
class Logger
|
8
|
+
require_relative 'logger/message'
|
9
|
+
|
10
|
+
include Singleton
|
11
|
+
|
12
|
+
DEFAULT_IGNORE = %w[Undocumentable].freeze
|
13
|
+
|
14
|
+
def messages
|
15
|
+
@messages ||= []
|
16
|
+
end
|
17
|
+
|
18
|
+
def register(msg, severity = :warn)
|
19
|
+
message = Message.registry
|
20
|
+
.map { |t| t.try_parse(msg, severity: severity, file: @current_parsed_file) }
|
21
|
+
.compact.first || Message.new(message: msg, file: @current_parsed_file)
|
22
|
+
messages << message
|
23
|
+
puts message.to_s(@format) if output?(message)
|
24
|
+
end
|
25
|
+
|
26
|
+
def notify(msg)
|
27
|
+
case msg
|
28
|
+
when /Parsing (\w\S+)$/
|
29
|
+
# TODO: fragile regexp; cleanup it after everything is parsed.
|
30
|
+
@current_parsed_file = Regexp.last_match(1)
|
31
|
+
when /^Generating/ # end of parsing of any file
|
32
|
+
@current_parsed_file = nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def clear
|
37
|
+
messages.clear
|
38
|
+
@format = Message::DEFAULT_FORMAT
|
39
|
+
@ignore = DEFAULT_IGNORE
|
40
|
+
end
|
41
|
+
|
42
|
+
def format=(fmt)
|
43
|
+
@format = fmt.to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
def ignore=(list)
|
47
|
+
@ignore = Array(list).map(&:to_s)
|
48
|
+
.each { |type| Message.valid_type?(type) or fail(ArgumentError, "Unrecognized message type to ignore: #{type}") }
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def output?(message)
|
54
|
+
!@format.empty? && !@ignore.include?(message.type)
|
55
|
+
end
|
56
|
+
|
57
|
+
module Mixin
|
58
|
+
def debug(msg)
|
59
|
+
YardJunk::Logger.instance.notify(msg)
|
60
|
+
super
|
61
|
+
end
|
62
|
+
|
63
|
+
def warn(msg)
|
64
|
+
YardJunk::Logger.instance.register(msg, :warn)
|
65
|
+
end
|
66
|
+
|
67
|
+
def error(msg)
|
68
|
+
YardJunk::Logger.instance.register(msg, :error)
|
69
|
+
end
|
70
|
+
|
71
|
+
def backtrace(exception, level_meth = :error)
|
72
|
+
super if %i[error fatal].include?(level_meth)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
YardJunk::Logger.instance.clear
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module YardJunk
|
4
|
+
module Rake
|
5
|
+
extend ::Rake::DSL
|
6
|
+
|
7
|
+
def self.define_task
|
8
|
+
desc 'Check th junk in your YARD Documentation'
|
9
|
+
task('yard:junk') do
|
10
|
+
require 'yard'
|
11
|
+
require_relative '../yard-junk'
|
12
|
+
exit Janitor.new.run.report(Janitor::TextReporter.new(STDOUT))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/yard-junk.rb
ADDED
data/yard-junk.gemspec
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'yard-junk'
|
3
|
+
s.version = '0.0.1'
|
4
|
+
s.authors = ['Victor Shepelev']
|
5
|
+
s.email = 'zverok.offline@gmail.com'
|
6
|
+
s.homepage = 'https://github.com/zverok/junk_yard'
|
7
|
+
|
8
|
+
s.summary = 'Get rid of the junk in your YARD docs'
|
9
|
+
s.description = <<-EOF
|
10
|
+
YardJunk is structured logger/error validator plugin for YARD documentation gem.
|
11
|
+
EOF
|
12
|
+
s.licenses = ['MIT']
|
13
|
+
|
14
|
+
s.required_ruby_version = '>= 2.3.0'
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split($RS).reject do |file|
|
17
|
+
file =~ /^(?:
|
18
|
+
spec\/.*
|
19
|
+
|Gemfile
|
20
|
+
|Rakefile
|
21
|
+
|\.rspec
|
22
|
+
|\.gitignore
|
23
|
+
|\.rubocop.yml
|
24
|
+
|\.travis.yml
|
25
|
+
)$/x
|
26
|
+
end
|
27
|
+
s.require_paths = ["lib"]
|
28
|
+
s.bindir = 'bin'
|
29
|
+
s.executables << 'yard-junk'
|
30
|
+
|
31
|
+
s.add_dependency 'yard'
|
32
|
+
if RUBY_VERSION < '2.4'
|
33
|
+
s.add_dependency 'did_you_mean', '~> 1.0'
|
34
|
+
else
|
35
|
+
s.add_dependency 'did_you_mean', '~> 1.1'
|
36
|
+
end
|
37
|
+
|
38
|
+
s.add_development_dependency 'rubocop', '>= 0.30'
|
39
|
+
s.add_development_dependency 'rspec', '>= 3'
|
40
|
+
s.add_development_dependency 'rubocop-rspec'
|
41
|
+
s.add_development_dependency 'rspec-its', '~> 1'
|
42
|
+
#s.add_development_dependency 'saharspec'
|
43
|
+
s.add_development_dependency 'fakefs'
|
44
|
+
s.add_development_dependency 'simplecov', '~> 0.9'
|
45
|
+
s.add_development_dependency 'rake'
|
46
|
+
s.add_development_dependency 'rubygems-tasks'
|
47
|
+
s.add_development_dependency 'yard'
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,218 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yard-junk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Victor Shepelev
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-08-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: yard
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: did_you_mean
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.30'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.30'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop-rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec-its
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: fakefs
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: simplecov
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.9'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.9'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rake
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rubygems-tasks
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: yard
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
description: " YardJunk is structured logger/error validator plugin for YARD documentation
|
168
|
+
gem.\n"
|
169
|
+
email: zverok.offline@gmail.com
|
170
|
+
executables:
|
171
|
+
- yard-junk
|
172
|
+
extensions: []
|
173
|
+
extra_rdoc_files: []
|
174
|
+
files:
|
175
|
+
- ".rubocop_todo.yml"
|
176
|
+
- ".yardopts"
|
177
|
+
- README.md
|
178
|
+
- bin/yard-junk
|
179
|
+
- examples/bare_log.rb
|
180
|
+
- examples/formatted_log.rb
|
181
|
+
- examples/input/circular_ref.rb
|
182
|
+
- examples/input/lot_of_errors.rb
|
183
|
+
- examples/input/unparseable.rb
|
184
|
+
- lib/yard-junk.rb
|
185
|
+
- lib/yard-junk/command_line.rb
|
186
|
+
- lib/yard-junk/janitor.rb
|
187
|
+
- lib/yard-junk/janitor/base_reporter.rb
|
188
|
+
- lib/yard-junk/janitor/resolver.rb
|
189
|
+
- lib/yard-junk/janitor/text_reporter.rb
|
190
|
+
- lib/yard-junk/logger.rb
|
191
|
+
- lib/yard-junk/logger/message.rb
|
192
|
+
- lib/yard-junk/rake.rb
|
193
|
+
- yard-junk.gemspec
|
194
|
+
homepage: https://github.com/zverok/junk_yard
|
195
|
+
licenses:
|
196
|
+
- MIT
|
197
|
+
metadata: {}
|
198
|
+
post_install_message:
|
199
|
+
rdoc_options: []
|
200
|
+
require_paths:
|
201
|
+
- lib
|
202
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
203
|
+
requirements:
|
204
|
+
- - ">="
|
205
|
+
- !ruby/object:Gem::Version
|
206
|
+
version: 2.3.0
|
207
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
208
|
+
requirements:
|
209
|
+
- - ">="
|
210
|
+
- !ruby/object:Gem::Version
|
211
|
+
version: '0'
|
212
|
+
requirements: []
|
213
|
+
rubyforge_project:
|
214
|
+
rubygems_version: 2.6.10
|
215
|
+
signing_key:
|
216
|
+
specification_version: 4
|
217
|
+
summary: Get rid of the junk in your YARD docs
|
218
|
+
test_files: []
|