yardcheck 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +214 -0
  5. data/.rubocop_todo.yml +69 -0
  6. data/.ruby-version +1 -0
  7. data/Gemfile +22 -0
  8. data/Guardfile +5 -0
  9. data/README.md +83 -0
  10. data/bin/yardcheck +6 -0
  11. data/lib/yardcheck.rb +26 -0
  12. data/lib/yardcheck/color.rb +27 -0
  13. data/lib/yardcheck/const.rb +39 -0
  14. data/lib/yardcheck/documentation.rb +29 -0
  15. data/lib/yardcheck/documentation/method_object.rb +111 -0
  16. data/lib/yardcheck/method_call.rb +38 -0
  17. data/lib/yardcheck/method_tracer.rb +86 -0
  18. data/lib/yardcheck/observation.rb +72 -0
  19. data/lib/yardcheck/proxy.rb +33 -0
  20. data/lib/yardcheck/runner.rb +93 -0
  21. data/lib/yardcheck/source_lines.rb +29 -0
  22. data/lib/yardcheck/spec_observer.rb +28 -0
  23. data/lib/yardcheck/test_runner.rb +27 -0
  24. data/lib/yardcheck/test_value.rb +82 -0
  25. data/lib/yardcheck/typedef.rb +122 -0
  26. data/lib/yardcheck/typedef/parser.rb +82 -0
  27. data/lib/yardcheck/version.rb +5 -0
  28. data/lib/yardcheck/violation.rb +156 -0
  29. data/lib/yardcheck/warning.rb +14 -0
  30. data/spec/integration/yardcheck_spec.rb +57 -0
  31. data/spec/spec_helper.rb +51 -0
  32. data/spec/unit/yardcheck/const_spec.rb +48 -0
  33. data/spec/unit/yardcheck/documentation_spec.rb +148 -0
  34. data/spec/unit/yardcheck/method_tracer_spec.rb +59 -0
  35. data/spec/unit/yardcheck/runner_spec.rb +183 -0
  36. data/spec/unit/yardcheck/test_value_spec.rb +25 -0
  37. data/spec/unit/yardcheck/typedef/parser_spec.rb +37 -0
  38. data/spec/unit/yardcheck/typedef_spec.rb +32 -0
  39. data/test_app/.rspec +1 -0
  40. data/test_app/Gemfile +7 -0
  41. data/test_app/Gemfile.lock +66 -0
  42. data/test_app/lib/test_app.rb +119 -0
  43. data/test_app/spec/test_app_spec.rb +49 -0
  44. data/yardcheck.gemspec +24 -0
  45. metadata +158 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 88750f901e803de9b757d1699df2284e811e1b46
4
+ data.tar.gz: 47016f5393d602d75bbaf3a736735b9d93680eb9
5
+ SHA512:
6
+ metadata.gz: 688559a7d4caebc0218a1193ed67add77977e4c191e1f58767fa85e0120503a333d4146fe148d9f205c6940972f4d2451883e91fc21d244f3cc7575a3ac49b85
7
+ data.tar.gz: 3ae9fe9623004ec459faf4a1cfb475c7f60e7e4f2f47a177ae160360d92a5ef6c44a24fe3acd00c067fb09e0d9eb072be1dcae284dc7949fcb117731b760cdc6
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /test_app/.bundle/*
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --require yardcheck
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,214 @@
1
+ ---
2
+ inherit_from: .rubocop_todo.yml
3
+ require:
4
+ - rubocop-rspec
5
+ - rubocop/devtools
6
+ AllCops:
7
+ Exclude:
8
+ - 'test_app/**/*'
9
+ - 'tmp/**/*'
10
+ - vendor/**/*
11
+ DisplayCopNames: true
12
+ TargetRubyVersion: 2.3
13
+ # This ends up being too spammy
14
+ Style/Documentation:
15
+ Enabled: false
16
+ Style/ExtraSpacing:
17
+ AllowForAlignment: true
18
+ Metrics/LineLength:
19
+ Max: 100
20
+ Metrics/BlockLength:
21
+ Exclude:
22
+ # Ignore RSpec DSL
23
+ - spec/**/*
24
+ # Ignore Rake task DSL
25
+ - Rakefile
26
+ Style/IfUnlessModifier:
27
+ MaxLineLength: 100
28
+ Style/Next:
29
+ EnforcedStyle: always
30
+ Style/PercentLiteralDelimiters:
31
+ PreferredDelimiters:
32
+ '%i': '[]'
33
+ '%I': '[]'
34
+ '%q': '{}'
35
+ '%Q': '{}'
36
+ '%r': '{}'
37
+ '%s': ()
38
+ '%w': '[]'
39
+ '%W': '[]'
40
+ '%x': ()
41
+ Style/TrivialAccessors:
42
+ ExactNameMatch: false
43
+ Style/SymbolArray:
44
+ Enabled: true
45
+ Style/BarePercentLiterals:
46
+ EnforcedStyle: percent_q
47
+ Style/CollectionMethods:
48
+ Enabled: true
49
+ Style/Send:
50
+ Enabled: true
51
+ Style/AutoResourceCleanup:
52
+ Enabled: true
53
+ Style/FirstArrayElementLineBreak:
54
+ Enabled: true
55
+ Style/FirstHashElementLineBreak:
56
+ Enabled: true
57
+ Style/FirstMethodArgumentLineBreak:
58
+ Enabled: true
59
+ Style/FirstMethodParameterLineBreak:
60
+ Enabled: true
61
+ Style/MultilineArrayBraceLayout:
62
+ Enabled: true
63
+ Style/MultilineAssignmentLayout:
64
+ EnforcedStyle: new_line
65
+ Enabled: true
66
+ Style/MultilineHashBraceLayout:
67
+ Enabled: true
68
+ Style/MultilineMethodCallBraceLayout:
69
+ Enabled: true
70
+ Style/MultilineMethodDefinitionBraceLayout:
71
+ Enabled: true
72
+ Style/OptionHash:
73
+ Enabled: true
74
+ Style/StringMethods:
75
+ Enabled: true
76
+ Style/IndentArray:
77
+ EnforcedStyle: consistent
78
+ Style/IndentHash:
79
+ EnforcedStyle: consistent
80
+ MultilineMethodCallIndentation:
81
+ EnforcedStyle: indented
82
+ Style/Alias:
83
+ EnforcedStyle: prefer_alias_method
84
+ Style/AlignHash:
85
+ EnforcedColonStyle: table
86
+ Style/SignalException:
87
+ EnforcedStyle: semantic
88
+ Style/SingleLineBlockParams:
89
+ Enabled: false
90
+ # We prefer being able to write
91
+ #
92
+ # foo(*%w[bar baz qux norf hello goodbye])
93
+ #
94
+ # over
95
+ #
96
+ # foo('bar', 'baz', 'qux', 'norf', 'hello', 'goodbye')
97
+ #
98
+ # because
99
+ #
100
+ # 1. the `%w` signals that all elements are strings without interpolation
101
+ # 2. the `%w` case is more compact
102
+ #
103
+ # The only exception is method invocations with a single argument. These
104
+ # cases should be `foo('bar')` and never `foo(*%w[bar])`
105
+ #
106
+ Lint/UnneededSplatExpansion:
107
+ Enabled: false
108
+ # We only use guard clauses when it guards two or more statements:
109
+ #
110
+ # # bad
111
+ # def foo
112
+ # return if bar
113
+ #
114
+ # baz
115
+ # end
116
+ #
117
+ # # good
118
+ # def foo
119
+ # baz if bar
120
+ # end
121
+ #
122
+ # This includes conditionals with an `else` branch:
123
+ #
124
+ # # bad
125
+ # def foo
126
+ # return qux if bar
127
+ #
128
+ # baz
129
+ # end
130
+ #
131
+ # # good
132
+ # def foo
133
+ # if bar
134
+ # qux
135
+ # else
136
+ # baz
137
+ # end
138
+ # end
139
+ #
140
+ # It is up to the author of the code in question if the condition concerns
141
+ # exactly two statements
142
+ #
143
+ # # ok
144
+ # def foo
145
+ # return if bar
146
+ #
147
+ # baz
148
+ # qux
149
+ # end
150
+ #
151
+ # # also ok
152
+ # def foo
153
+ # if bar
154
+ # baz
155
+ # qux
156
+ # end
157
+ # end
158
+ #
159
+ # Use a guard clause if more than two statements are being guarded by the conditional
160
+ #
161
+ # # bad
162
+ # def foo
163
+ # if bar
164
+ # baz
165
+ # qux
166
+ # norf
167
+ # end
168
+ # end
169
+ #
170
+ # # good
171
+ # def foo
172
+ # return if bar
173
+ #
174
+ # baz
175
+ # qux
176
+ # norf
177
+ # end
178
+ Style/GuardClause:
179
+ Enabled: false
180
+ # Prefer writing an empty method on two lines
181
+ #
182
+ # # good
183
+ # def foo
184
+ # end
185
+ #
186
+ # # bar
187
+ # def foo; end
188
+ #
189
+ Style/EmptyMethod:
190
+ EnforcedStyle: expanded
191
+ # RuboCop disables end alignment by default. Explanation:
192
+ #
193
+ # The end alignment cops are in the Lint category because the bad
194
+ # indentation could be something more serious than just a style issue.
195
+ # It could be a mistake in which keyword you think you're matching with the end.
196
+ # (ruby -w warns for these too, by the way.) So for this reason I don't think
197
+ # we can add auto-correct for these cops.
198
+ #
199
+ # From https://github.com/bbatsov/rubocop/pull/1789#issuecomment-92308357
200
+ #
201
+ # I think we have more than enough tools that sound the alarms if we have
202
+ # such an obvious mistake like mismatched tokens.
203
+ #
204
+ # - Our specs are likely to fail
205
+ # - RuboCop will flag it regardless, it just doesn't autocorrect it by default
206
+ # - Ruby will emit a warning which we configure to fail our specs
207
+ #
208
+ # So I think it is safe to enable autocorrect for end alignment cops because it
209
+ # does not seem unsafe and it improves workflow to be able to autocorrect alignment
210
+ Lint/DefEndAlignment:
211
+ AutoCorrect: true
212
+ # See explanation for `Lint/DefEndAlignment`
213
+ Lint/EndAlignment:
214
+ AutoCorrect: true
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,69 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2017-03-22 20:59:41 -0700 using RuboCop version 0.47.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ Lint/HandleExceptions:
11
+ Exclude:
12
+ - 'spec/spec_helper.rb'
13
+
14
+ # Offense count: 4
15
+ Metrics/AbcSize:
16
+ Max: 22
17
+
18
+ # Offense count: 1
19
+ Metrics/CyclomaticComplexity:
20
+ Max: 8
21
+
22
+ # Offense count: 5
23
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
24
+ # URISchemes: http, https
25
+ Metrics/LineLength:
26
+ Max: 138
27
+
28
+ # Offense count: 3
29
+ # Configuration parameters: CountComments.
30
+ Metrics/MethodLength:
31
+ Max: 27
32
+
33
+ # Offense count: 1
34
+ RSpec/DescribeClass:
35
+ Exclude:
36
+ - 'spec/integration/yardcheck_spec.rb'
37
+
38
+ # Offense count: 6
39
+ # Configuration parameters: Max.
40
+ RSpec/ExampleLength:
41
+ Exclude:
42
+ - 'spec/integration/yardcheck_spec.rb'
43
+ - 'spec/unit/yardcheck/documentation_spec.rb'
44
+ - 'spec/unit/yardcheck/method_tracer_spec.rb'
45
+ - 'spec/unit/yardcheck/typedef/parser_spec.rb'
46
+ - 'spec/unit/yardcheck/typedef_spec.rb'
47
+
48
+ # Offense count: 1
49
+ # Configuration parameters: IgnoreSymbolicNames.
50
+ RSpec/VerifiedDoubles:
51
+ Exclude:
52
+ - 'spec/unit/yardcheck/test_value_spec.rb'
53
+
54
+ # Offense count: 1
55
+ Style/MethodMissing:
56
+ Exclude:
57
+ - 'lib/yardcheck/proxy.rb'
58
+
59
+ # Offense count: 6
60
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
61
+ # SupportedStyles: separated, grouped
62
+ Style/MixinGrouping:
63
+ Exclude:
64
+ - 'lib/yardcheck/documentation.rb'
65
+ - 'lib/yardcheck/documentation/method_object.rb'
66
+ - 'lib/yardcheck/method_tracer.rb'
67
+ - 'lib/yardcheck/runner.rb'
68
+ - 'lib/yardcheck/spec_observer.rb'
69
+ - 'lib/yardcheck/typedef/parser.rb'
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.3.3
data/Gemfile ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ ruby File.read('.ruby-version').chomp
6
+
7
+ gemspec
8
+
9
+ group :test do
10
+ gem 'rspec', '~> 3.5'
11
+ end
12
+
13
+ group :lint do
14
+ gem 'rubocop', git: 'https://github.com/bbatsov/rubocop.git'
15
+ gem 'rubocop-devtools', git: 'https://github.com/backus/rubocop-devtools.git'
16
+ gem 'rubocop-rspec', git: 'https://github.com/backus/rubocop-rspec.git'
17
+ end
18
+
19
+ gem 'guard'
20
+ gem 'guard-rspec'
21
+ gem 'mutest', '0.0.6'
22
+ gem 'mutest-rspec'
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ guard :rspec, cmd: 'bundle exec rspec' do
4
+ watch(/.+\.rb/) { %w[spec/unit] }
5
+ end
data/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # Yardcheck
2
+
3
+ Check whether your YARD types are correct by running your test suite. Take a look!
4
+
5
+ ![yardcheck](https://cloud.githubusercontent.com/assets/2085622/24262402/211ecfbe-0fb7-11e7-86f7-1b287298339f.gif)
6
+
7
+ ## What is this?
8
+
9
+ When you write documentation like this
10
+
11
+ ```ruby
12
+ # Validates the user
13
+ #
14
+ # @param user [User]
15
+ #
16
+ # @return [true,false]
17
+ # def valid?(user)
18
+ # ...
19
+ # end
20
+ ```
21
+
22
+ You are saying that you are always going to be passing in a `User` instance and the method will always returns `true` or `false`.
23
+
24
+ `yardcheck` traces method invocations to observe the parameters and return values in your application while running your test suite. When your test suite finishes running we compare the observed types found while running your tests against the types in your documentation.
25
+
26
+ For example, here is part of the output from the demo gif at the top of the README:
27
+
28
+ > Expected `Dry::Types::Array::Member#try` to receive `an object responding to #call` for `block` but observed `NilClass`
29
+ >
30
+ > ```
31
+ > source: lib/dry/types/array/member.rb:35
32
+ > tests:
33
+ > - ./spec/dry/types/compiler_spec.rb:184
34
+ > - ./spec/dry/types/sum_spec.rb:47
35
+ > - ./spec/dry/types/sum_spec.rb:60
36
+ > - ./spec/dry/types/types/form_spec.rb:235
37
+ > - ./spec/dry/types/types/form_spec.rb:240
38
+ > - ./spec/dry/types/types/form_spec.rb:245
39
+ > - ./spec/dry/types/types/form_spec.rb:250
40
+ > - ./spec/dry/types/types/json_spec.rb:69
41
+ > ```
42
+ >
43
+ >
44
+ > ```ruby
45
+ > # @param [Array, Object] input
46
+ > # @param [#call] block
47
+ > # @yieldparam [Failure] failure
48
+ > # @yieldreturn [Result]
49
+ > # @return [Result]
50
+ > def try(input, &block)
51
+ > if input.is_a?(::Array)
52
+ > result = call(input, :try)
53
+ > output = result.map(&:input)
54
+ >
55
+ > if result.all?(&:success?)
56
+ > success(output)
57
+ > else
58
+ > failure = failure(output, result.select(&:failure?))
59
+ > block ? yield(failure) : failure
60
+ > end
61
+ > else
62
+ > failure = failure(input, "#{input} is not an array")
63
+ > block ? yield(failure) : failure
64
+ > end
65
+ > end
66
+ > ```
67
+
68
+ Yardcheck is doing some cool things here:
69
+
70
+ 1. It outputs the offending method and documentation to give you immediate context. It also gives you the path and line number if you want to open up that file.
71
+ 2. It understands that the YARD documentation `@param [#call]` means a duck typed object that responds to the method `#call`.
72
+ 3. It tells you that it actually observed cases where the param was `nil` which does not respond to `#call`.
73
+ 4. It lists all of the tests that observed a `nil` block param.
74
+
75
+ In this case I would update the documentation to be `@param [#call, nil] block`
76
+
77
+ # Is this ready?
78
+
79
+ Kind of.
80
+
81
+ It is not ready to be run in CI to check your documentation and it may never be since tracing method calls is fairly slow. We also sometimes mess up. For example, if another method raises an error then all of the methods that bubble up that error without rescuing it will be marked as returning `nil`. This seems like a limitation of ruby's `TracePoint` right now.
82
+
83
+ It is very helpful though. It will find a lot of cases where your documentation isn't quite right and the output is clear. Install it and give it a try.
data/bin/yardcheck ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'yardcheck'
5
+
6
+ Yardcheck::Runner.run(ARGV).check
data/lib/yardcheck.rb ADDED
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'anima'
4
+ require 'concord'
5
+ require 'yard'
6
+ require 'rspec'
7
+ require 'coderay'
8
+
9
+ require 'yardcheck/version'
10
+ require 'yardcheck/runner'
11
+ require 'yardcheck/method_tracer'
12
+ require 'yardcheck/method_call'
13
+ require 'yardcheck/proxy'
14
+ require 'yardcheck/test_value'
15
+ require 'yardcheck/test_runner'
16
+ require 'yardcheck/const'
17
+ require 'yardcheck/documentation'
18
+ require 'yardcheck/documentation/method_object'
19
+ require 'yardcheck/typedef'
20
+ require 'yardcheck/typedef/parser'
21
+ require 'yardcheck/observation'
22
+ require 'yardcheck/spec_observer'
23
+ require 'yardcheck/color'
24
+ require 'yardcheck/violation'
25
+ require 'yardcheck/warning'
26
+ require 'yardcheck/source_lines'