inspecstyle 0.1.1 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/Gemfile +1 -0
  4. data/Gemfile.lock +12 -7
  5. data/README.md +36 -1
  6. data/Rakefile +3 -0
  7. data/config/default.yml +18 -0
  8. data/doc/RuboCop.html +128 -0
  9. data/doc/RuboCop/Cop.html +117 -0
  10. data/doc/RuboCop/Cop/InSpecStyle.html +117 -0
  11. data/doc/RuboCop/Cop/InSpecStyle/AzureGenericResource.html +249 -0
  12. data/doc/RuboCop/Cop/InSpecStyle/DeprecatedAttributes.html +310 -0
  13. data/doc/RuboCop/Cop/InSpecStyle/FirstCop.html +345 -0
  14. data/doc/RuboCop/InSpecStyle.html +140 -0
  15. data/doc/RuboCop/InSpecStyle/Error.html +124 -0
  16. data/doc/RuboCop/InSpecStyle/Inject.html +195 -0
  17. data/doc/_index.html +211 -0
  18. data/doc/class_list.html +51 -0
  19. data/doc/css/common.css +1 -0
  20. data/doc/css/full_list.css +58 -0
  21. data/doc/css/style.css +496 -0
  22. data/doc/file.README.html +131 -0
  23. data/doc/file_list.html +56 -0
  24. data/doc/frames.html +17 -0
  25. data/doc/index.html +131 -0
  26. data/doc/js/app.js +314 -0
  27. data/doc/js/full_list.js +216 -0
  28. data/doc/js/jquery.js +4 -0
  29. data/doc/method_list.html +99 -0
  30. data/doc/top-level-namespace.html +110 -0
  31. data/lib/rubocop/cop/inspecstyle/azure_generic_resource.rb +35 -0
  32. data/lib/rubocop/cop/inspecstyle/deprecated_attributes.rb +17 -15
  33. data/lib/rubocop/cop/inspecstyle/first_cop.rb +0 -1
  34. data/lib/rubocop/cop/inspecstyle/oracle_db_session_pass.rb +38 -0
  35. data/lib/rubocop/cop/inspecstyle/shadow_properties.rb +61 -0
  36. data/lib/rubocop/cop/inspecstyle_cops.rb +3 -0
  37. data/lib/rubocop/inspecstyle/version.rb +1 -1
  38. data/notes-for-development.md +3 -0
  39. data/tasks/cops_documentation.rake +300 -0
  40. metadata +29 -2
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # TODO: when finished, run `rake generate_cops_documentation` to update the docs
4
3
  module RuboCop
5
4
  module Cop
6
5
  module InSpecStyle
6
+ # Checks if deprecated method attribute is used.
7
7
  #
8
- # @example EnforcedStyle: input (default)
8
+ # @example EnforcedStyle: InSpecStyle (default)
9
9
  # # Attributes have been deprecated for inputs
10
10
  # # https://github.com/inspec/inspec/issues/3802
11
11
  #
@@ -18,7 +18,8 @@ module RuboCop
18
18
  class DeprecatedAttributes < Cop
19
19
  include RangeHelp
20
20
 
21
- MSG = 'Use `#input` instead of `#attribute`.'
21
+ MSG = 'Use `#input` instead of `#attribute`. This will be removed in '\
22
+ 'InSpec 5'
22
23
 
23
24
  def_node_matcher :attribute?, <<~PATTERN
24
25
  (send nil? :attribute ...)
@@ -26,24 +27,25 @@ module RuboCop
26
27
 
27
28
  def on_send(node)
28
29
  return unless attribute?(node)
29
- add_offense(node, location: range(node))
30
+
31
+ add_offense(node, location: node.loc.selector)
32
+ end
33
+
34
+ def autocorrect(node)
35
+ lambda do |corrector|
36
+ corrector.replace(offense_range(node), preferred_replacement)
37
+ end
30
38
  end
31
39
 
32
40
  private
33
41
 
34
- def range(node)
35
- # Only highlights the method 'attribute'
36
- range_between(node.source_range.begin_pos,
37
- node.source_range.begin_pos+9
38
- )
42
+ def offense_range(node)
43
+ node.loc.selector
39
44
  end
40
45
 
41
- # def autocorrect
42
- # ->(corrector) do
43
- # corrector.insert_before(node.source_range, 'input')
44
- # corrector.remove(node.source_range, 'attribute')
45
- # end
46
- # end
46
+ def preferred_replacement
47
+ cop_config.fetch('PreferredReplacement')
48
+ end
47
49
  end
48
50
  end
49
51
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # TODO: when finished, run `rake generate_cops_documentation` to update the docs
4
3
  module RuboCop
5
4
  module Cop
6
5
  module InSpecStyle
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InSpecStyle
6
+ #
7
+ # @example EnforcedStyle: InSpecStyle (default)
8
+ # # Description of the `bar` style.
9
+ #
10
+ # # bad
11
+ # sql = oracledb_session(user: 'my_user', pass: 'password')
12
+ #
13
+ # # good
14
+ # sql = oracledb_session(user: 'my_user', password: 'password')
15
+ class OracleDbSessionPass < Cop
16
+ include MatchRange
17
+ MSG = 'Use `:password` instead of `:pass`. This will be removed in '\
18
+ 'InSpec 5'
19
+
20
+ def_node_matcher :oracledb_session_pass?, <<~PATTERN
21
+ (send _ :oracledb_session
22
+ (hash
23
+ ...
24
+ (pair
25
+ (sym $:pass)
26
+ ...)))
27
+ PATTERN
28
+
29
+ # Getting location was a bit tricky on this one, looking at docs perhaps
30
+ # convention does allow highlighting an entire line.
31
+ def on_send(node)
32
+ return unless result = oracledb_session_pass?(node)
33
+ add_offense(node, message: MSG)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # NOTE TO SELF - this one works BUT not if other its statements are defined. Needs
4
+ # to work in any arrangement. This is a powerful one to crack as this pattern
5
+ # will be used in a LOT of other cops.
6
+
7
+ module RuboCop
8
+ module Cop
9
+ module InSpecStyle
10
+ # Shadow resource property user is deprecated in favor of `users`
11
+ #
12
+ # @example EnforcedStyle: InSpecStyle (default)
13
+ # # Use users instead
14
+ #
15
+ # # bad
16
+ # describe shadow('/etc/my-custom-place/shadow') do
17
+ # its('user') { should eq 'user' }
18
+ # end
19
+ #
20
+ # # good
21
+ # describe shadow('/etc/my-custom-place/shadow') do
22
+ # its('users') { should eq 'user' }
23
+ # end
24
+ #
25
+ class ShadowProperties < Cop
26
+ # TODO: Implement the cop in here.
27
+ #
28
+ # In many cases, you can use a node matcher for matching node pattern.
29
+ # See https://github.com/rubocop-hq/rubocop-ast/blob/master/lib/rubocop/node_pattern.rb
30
+ #
31
+ # For example
32
+ MSG = 'Use `:%<modifier>ss` instead of `:%<modifier>s` as a property ' \
33
+ 'for the `shadow` resource. This property will be removed in InSpec 5'
34
+
35
+ def_node_matcher :shadow_resource_user_property?, <<~PATTERN
36
+ (block
37
+ (send _ :describe
38
+ (send _ :shadow ...) ...)
39
+ (args ...)
40
+ (block
41
+ (send _ :its
42
+ (str ${"user" "password" "last_change" "expiry_date" "line"} ...) ...) ...) ...)
43
+ PATTERN
44
+
45
+ def on_block(node)
46
+ return unless shadow_resource_user_property?(node) do |modifier|
47
+ message = format(MSG, modifier: modifier)
48
+ range = locate_range(modifier, node)
49
+ add_offense(node, message: message, location: range)
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def locate_range(modifier, node)
56
+ node.children.find { |child| child.type == :block }.children.first.children.find{|x| x == s(:str, modifier)}.source_range
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -1,3 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'inspecstyle/first_cop'
3
3
  require_relative 'inspecstyle/deprecated_attributes'
4
+ require_relative 'inspecstyle/azure_generic_resource'
5
+ require_relative 'inspecstyle/shadow_properties'
6
+ require_relative 'inspecstyle/oracle_db_session_pass'
@@ -1,5 +1,5 @@
1
1
  module RuboCop
2
2
  module InSpecStyle
3
- VERSION = "0.1.1"
3
+ VERSION = "0.1.6"
4
4
  end
5
5
  end
@@ -42,4 +42,7 @@ node = source.ast
42
42
  node.type
43
43
  node.children
44
44
  node.source
45
+ NodePattern.new('(send ...)').match(node) # => true
45
46
  ```
47
+
48
+ Correction docs at rubocop's: lib/rubocop/cop/corrector.rb
@@ -0,0 +1,300 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yard'
4
+ require 'rubocop'
5
+
6
+ YARD::Rake::YardocTask.new(:yard_for_generate_documentation) do |task|
7
+ task.files = ['lib/rubocop/cop/*/*.rb']
8
+ task.options = ['--no-output']
9
+ end
10
+
11
+ desc 'Generate docs of all cops departments'
12
+ task generate_cops_documentation: :yard_for_generate_documentation do
13
+ def cops_of_department(cops, department)
14
+ cops.with_department(department).sort!
15
+ end
16
+
17
+ # rubocop:disable Metrics/AbcSize
18
+ def cops_body(config, cop, description, examples_objects, pars)
19
+ content = h2(cop.cop_name)
20
+ content << required_ruby_version(cop)
21
+ content << properties(cop.new(config))
22
+ content << "#{description}\n"
23
+ content << examples(examples_objects) if examples_objects.count.positive?
24
+ content << configurations(pars)
25
+ content << references(config, cop)
26
+ content
27
+ end
28
+ # rubocop:enable Metrics/AbcSize
29
+
30
+ def examples(examples_object)
31
+ examples_object.each_with_object(h3('Examples').dup) do |example, content|
32
+ content << "\n" unless content.end_with?("\n\n")
33
+ content << h4(example.name) unless example.name == ''
34
+ content << code_example(example)
35
+ end
36
+ end
37
+
38
+ def required_ruby_version(cop)
39
+ return '' unless cop.respond_to?(:required_minimum_ruby_version)
40
+
41
+ "NOTE: Required Ruby version: #{cop.required_minimum_ruby_version}\n\n"
42
+ end
43
+
44
+ # rubocop:disable Metrics/MethodLength
45
+ def properties(cop_instance)
46
+ header = [
47
+ 'Enabled by default', 'Safe', 'Supports autocorrection', 'VersionAdded',
48
+ 'VersionChanged'
49
+ ]
50
+ autocorrect = if cop_instance.support_autocorrect?
51
+ "Yes#{' (Unsafe)' unless cop_instance.safe_autocorrect?}"
52
+ else
53
+ 'No'
54
+ end
55
+ cop_config = cop_instance.cop_config
56
+ content = [[
57
+ cop_status(cop_config.fetch('Enabled')),
58
+ cop_config.fetch('Safe', true) ? 'Yes' : 'No',
59
+ autocorrect,
60
+ cop_config.fetch('VersionAdded', '-'),
61
+ cop_config.fetch('VersionChanged', '-')
62
+ ]]
63
+ to_table(header, content) + "\n"
64
+ end
65
+ # rubocop:enable Metrics/MethodLength
66
+
67
+ def h2(title)
68
+ content = +"\n"
69
+ content << "== #{title}\n"
70
+ content << "\n"
71
+ content
72
+ end
73
+
74
+ def h3(title)
75
+ content = +"\n"
76
+ content << "=== #{title}\n"
77
+ content << "\n"
78
+ content
79
+ end
80
+
81
+ def h4(title)
82
+ content = +"==== #{title}\n"
83
+ content << "\n"
84
+ content
85
+ end
86
+
87
+ def code_example(ruby_code)
88
+ content = +"[source,ruby]\n----\n"
89
+ content << ruby_code.text.gsub('@good', '# good')
90
+ .gsub('@bad', '# bad').strip
91
+ content << "\n----\n"
92
+ content
93
+ end
94
+
95
+ def configurations(pars)
96
+ return '' if pars.empty?
97
+
98
+ header = ['Name', 'Default value', 'Configurable values']
99
+ configs = pars
100
+ .each_key
101
+ .reject { |key| key.start_with?('Supported') }
102
+ .reject { |key| key.start_with?('AllowMultipleStyles') }
103
+ content = configs.map do |name|
104
+ configurable = configurable_values(pars, name)
105
+ default = format_table_value(pars[name])
106
+ [name, default, configurable]
107
+ end
108
+
109
+ h3('Configurable attributes') + to_table(header, content)
110
+ end
111
+
112
+ # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
113
+ def configurable_values(pars, name)
114
+ case name
115
+ when /^Enforced/
116
+ supported_style_name = RuboCop::Cop::Util.to_supported_styles(name)
117
+ format_table_value(pars[supported_style_name])
118
+ when 'IndentationWidth'
119
+ 'Integer'
120
+ when 'Database'
121
+ format_table_value(pars['SupportedDatabases'])
122
+ else
123
+ case pars[name]
124
+ when String
125
+ 'String'
126
+ when Integer
127
+ 'Integer'
128
+ when Float
129
+ 'Float'
130
+ when true, false
131
+ 'Boolean'
132
+ when Array
133
+ 'Array'
134
+ else
135
+ ''
136
+ end
137
+ end
138
+ end
139
+ # rubocop:enable Metrics/CyclomaticComplexity,Metrics/MethodLength
140
+
141
+ def to_table(header, content)
142
+ table = [
143
+ '|===',
144
+ "| #{header.join(' | ')}\n\n"
145
+ ].join("\n")
146
+ marked_contents = content.map do |plain_content|
147
+ plain_content.map { |c| "| #{c}" }.join("\n")
148
+ end
149
+ table << marked_contents.join("\n\n")
150
+ table << "\n|===\n"
151
+ end
152
+
153
+ def format_table_value(val)
154
+ value =
155
+ case val
156
+ when Array
157
+ if val.empty?
158
+ '`[]`'
159
+ else
160
+ val.map { |config| format_table_value(config) }.join(', ')
161
+ end
162
+ else
163
+ wrap_backtick(val.nil? ? '<none>' : val)
164
+ end
165
+ value.gsub("#{Dir.pwd}/", '').rstrip
166
+ end
167
+
168
+ def wrap_backtick(value)
169
+ if value.is_a?(String)
170
+ # Use `+` to prevent text like `**/*.gemspec` from being bold.
171
+ value.start_with?('*') ? "`+#{value}+`" : "`#{value}`"
172
+ else
173
+ "`#{value}`"
174
+ end
175
+ end
176
+
177
+ def references(config, cop)
178
+ cop_config = config.for_cop(cop)
179
+ urls = RuboCop::Cop::MessageAnnotator.new(
180
+ config, cop.name, cop_config, {}
181
+ ).urls
182
+ return '' if urls.empty?
183
+
184
+ content = h3('References')
185
+ content << urls.map { |url| "* #{url}" }.join("\n")
186
+ content << "\n"
187
+ content
188
+ end
189
+
190
+ def print_cops_of_department(cops, department, config)
191
+ selected_cops = cops_of_department(cops, department)
192
+ content = +"= #{department}\n"
193
+ selected_cops.each do |cop|
194
+ content << print_cop_with_doc(cop, config)
195
+ end
196
+ file_name = "#{Dir.pwd}/docs/modules/ROOT/pages/cops_#{department.downcase}.adoc"
197
+ File.open(file_name, 'w') do |file|
198
+ puts "* generated #{file_name}"
199
+ file.write(content.strip + "\n")
200
+ end
201
+ end
202
+
203
+ def print_cop_with_doc(cop, config)
204
+ t = config.for_cop(cop)
205
+ non_display_keys = %w[
206
+ Description Enabled StyleGuide Reference Safe SafeAutoCorrect VersionAdded
207
+ VersionChanged
208
+ ]
209
+ pars = t.reject { |k| non_display_keys.include? k }
210
+ description = 'No documentation'
211
+ examples_object = []
212
+ cop_code(cop) do |code_object|
213
+ description = code_object.docstring unless code_object.docstring.blank?
214
+ examples_object = code_object.tags('example')
215
+ end
216
+ cops_body(config, cop, description, examples_object, pars)
217
+ end
218
+
219
+ def cop_code(cop)
220
+ YARD::Registry.all(:class).detect do |code_object|
221
+ next unless RuboCop::Cop::Badge.for(code_object.to_s) == cop.badge
222
+
223
+ yield code_object
224
+ end
225
+ end
226
+
227
+ def table_of_content_for_department(cops, department)
228
+ type_title = department[0].upcase + department[1..-1]
229
+ filename = "cops_#{department.downcase}.adoc"
230
+ content = +"=== Department xref:#{filename}[#{type_title}]\n\n"
231
+ cops_of_department(cops, department.to_sym).each do |cop|
232
+ anchor = cop.cop_name.sub('/', '').downcase
233
+ content << "* xref:#{filename}##{anchor}[#{cop.cop_name}]\n"
234
+ end
235
+
236
+ content
237
+ end
238
+
239
+ def print_table_of_contents(cops)
240
+ path = "#{Dir.pwd}/docs/modules/ROOT/pages/cops.adoc"
241
+ original = File.read(path)
242
+ content = +"// START_COP_LIST\n\n"
243
+
244
+ content << table_contents(cops)
245
+
246
+ content << "\n// END_COP_LIST"
247
+
248
+ content = original.sub(
249
+ %r{// START_COP_LIST.+// END_COP_LIST}m, content
250
+ )
251
+ File.write(path, content)
252
+ end
253
+
254
+ def table_contents(cops)
255
+ cops
256
+ .departments
257
+ .map(&:to_s)
258
+ .sort
259
+ .map { |department| table_of_content_for_department(cops, department) }
260
+ .join("\n")
261
+ end
262
+
263
+ def cop_status(status)
264
+ return 'Disabled' unless status
265
+
266
+ status == 'pending' ? 'Pending' : 'Enabled'
267
+ end
268
+
269
+ def assert_docs_synchronized
270
+ # Do not print diff and yield whether exit code was zero
271
+ sh('git diff --quiet docs') do |outcome, _|
272
+ return if outcome
273
+
274
+ # Output diff before raising error
275
+ sh('GIT_PAGER=cat git diff docs')
276
+
277
+ warn 'The docs directory is out of sync. ' \
278
+ 'Run `rake generate_cops_documentation` and commit the results.'
279
+ exit!
280
+ end
281
+ end
282
+
283
+ def main
284
+ cops = RuboCop::Cop::Cop.registry
285
+ config = RuboCop::ConfigLoader.default_configuration
286
+
287
+ YARD::Registry.load!
288
+ cops.departments.sort!.each do |department|
289
+ print_cops_of_department(cops, department, config)
290
+ end
291
+
292
+ print_table_of_contents(cops)
293
+
294
+ assert_docs_synchronized if ENV['CI'] == 'true'
295
+ ensure
296
+ RuboCop::ConfigLoader.default_configuration = nil
297
+ end
298
+
299
+ main
300
+ end