yard-junk 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/yard-junk.svg)](http://badge.fury.io/rb/yard-junk)
|
4
|
+
[![Build Status](https://travis-ci.org/zverok/yard-junk.svg?branch=master)](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: []
|