rubomatic-html 1.1.0.pre.rc.4 → 1.1.0.pre.rc.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +10 -1
  3. data/config/default.yml +28 -0
  4. data/docs/cops/style/README.adoc +9 -0
  5. data/docs/cops/style/no_fields_for/README.adoc +28 -0
  6. data/docs/cops/style/no_form_for/README.adoc +28 -0
  7. data/docs/cops/style/no_form_tag/README.adoc +28 -0
  8. data/docs/cops/style/no_on_before_unload/README.adoc +29 -0
  9. data/docs/cops/style/no_on_click/README.adoc +29 -0
  10. data/docs/cops/style/no_on_drag/README.adoc +29 -0
  11. data/docs/cops/style/no_on_load/README.adoc +29 -0
  12. data/docs/cops/style/no_on_unload/README.adoc +29 -0
  13. data/docs/cops/style/no_on_wheel/README.adoc +29 -0
  14. data/exe/rubomatic-html +1 -1
  15. data/lib/rubomatic-html/cop/base.rb +58 -0
  16. data/lib/rubomatic-html/cop/cops.rb +19 -0
  17. data/lib/rubomatic-html/cop/layout/base.rb +17 -0
  18. data/lib/rubomatic-html/cop/layout/line_length.rb +28 -0
  19. data/lib/rubomatic-html/cop/layout/multiple_line_breaks.rb +42 -0
  20. data/lib/rubomatic-html/cop/layout/trailing_whitespace.rb +28 -0
  21. data/lib/rubomatic-html/cop/style/base.rb +17 -0
  22. data/lib/rubomatic-html/cop/style/no_fields_for.rb +28 -0
  23. data/lib/rubomatic-html/cop/style/no_form_for.rb +28 -0
  24. data/lib/rubomatic-html/cop/style/no_form_tag.rb +28 -0
  25. data/lib/rubomatic-html/cop/style/no_on_attribute.rb +31 -0
  26. data/lib/rubomatic-html/cop/style/no_on_before_unload.rb +27 -0
  27. data/lib/rubomatic-html/cop/style/no_on_click.rb +27 -0
  28. data/lib/rubomatic-html/cop/style/no_on_drag.rb +27 -0
  29. data/lib/rubomatic-html/cop/style/no_on_load.rb +27 -0
  30. data/lib/rubomatic-html/cop/style/no_on_unload.rb +27 -0
  31. data/lib/rubomatic-html/cop/style/no_on_wheel.rb +27 -0
  32. data/lib/rubomatic-html/cop/style/partial_instance_variable.rb +44 -0
  33. data/lib/rubomatic-html/generator/cop_readme_injector.rb +48 -0
  34. data/lib/rubomatic-html/generator/dept_readme_injector.rb +111 -0
  35. data/lib/rubomatic-html/generator.rb +330 -0
  36. data/lib/rubomatic-html/inject.rb +19 -0
  37. data/lib/rubomatic-html/runner.rb +129 -0
  38. data/lib/rubomatic-html/version.rb +5 -0
  39. data/lib/rubomatic-html.rb +11 -1
  40. metadata +47 -18
  41. data/lib/rubomatic/html/cop/base.rb +0 -42
  42. data/lib/rubomatic/html/cop/cops.rb +0 -9
  43. data/lib/rubomatic/html/cop/layout/base.rb +0 -19
  44. data/lib/rubomatic/html/cop/layout/line_length.rb +0 -26
  45. data/lib/rubomatic/html/cop/layout/multiple_line_breaks.rb +0 -40
  46. data/lib/rubomatic/html/cop/layout/trailing_whitespace.rb +0 -26
  47. data/lib/rubomatic/html/cop/style/base.rb +0 -19
  48. data/lib/rubomatic/html/cop/style/partial_instance_variable.rb +0 -28
  49. data/lib/rubomatic/html/version.rb +0 -7
  50. data/lib/rubomatic/html.rb +0 -115
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubomaticHtml
4
+ module Cop
5
+ module Style
6
+ class NoOnBeforeUnload < RubomaticHtml::Cop::Style::NoOnAttribute
7
+ class << self
8
+ # @see super
9
+ def abstract_cop?
10
+ false
11
+ end
12
+
13
+ # @see super
14
+ def name
15
+ [department, 'NoOnBeforeUnload'].join('/')
16
+ end
17
+ end
18
+
19
+ def initialize(...)
20
+ super(...)
21
+
22
+ @html_attr = 'onbeforeunload'
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubomaticHtml
4
+ module Cop
5
+ module Style
6
+ class NoOnClick < RubomaticHtml::Cop::Style::NoOnAttribute
7
+ class << self
8
+ # @see super
9
+ def abstract_cop?
10
+ false
11
+ end
12
+
13
+ # @see super
14
+ def name
15
+ [department, 'NoOnClick'].join('/')
16
+ end
17
+ end
18
+
19
+ def initialize(...)
20
+ super(...)
21
+
22
+ @html_attr = 'onclick'
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubomaticHtml
4
+ module Cop
5
+ module Style
6
+ class NoOnDrag < RubomaticHtml::Cop::Style::NoOnAttribute
7
+ class << self
8
+ # @see super
9
+ def abstract_cop?
10
+ false
11
+ end
12
+
13
+ # @see super
14
+ def name
15
+ [department, 'NoOnDrag'].join('/')
16
+ end
17
+ end
18
+
19
+ def initialize(...)
20
+ super(...)
21
+
22
+ @html_attr = 'ondrag'
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubomaticHtml
4
+ module Cop
5
+ module Style
6
+ class NoOnLoad < RubomaticHtml::Cop::Style::NoOnAttribute
7
+ class << self
8
+ # @see super
9
+ def abstract_cop?
10
+ false
11
+ end
12
+
13
+ # @see super
14
+ def name
15
+ [department, 'NoOnLoad'].join('/')
16
+ end
17
+ end
18
+
19
+ def initialize(...)
20
+ super(...)
21
+
22
+ @html_attr = 'onload'
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubomaticHtml
4
+ module Cop
5
+ module Style
6
+ class NoOnUnload < RubomaticHtml::Cop::Style::NoOnAttribute
7
+ class << self
8
+ # @see super
9
+ def abstract_cop?
10
+ false
11
+ end
12
+
13
+ # @see super
14
+ def name
15
+ [department, 'NoOnUnload'].join('/')
16
+ end
17
+ end
18
+
19
+ def initialize(...)
20
+ super(...)
21
+
22
+ @html_attr = 'onunload'
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubomaticHtml
4
+ module Cop
5
+ module Style
6
+ class NoOnWheel < RubomaticHtml::Cop::Style::NoOnAttribute
7
+ class << self
8
+ # @see super
9
+ def abstract_cop?
10
+ false
11
+ end
12
+
13
+ # @see super
14
+ def name
15
+ [department, 'NoOnWheel'].join('/')
16
+ end
17
+ end
18
+
19
+ def initialize(...)
20
+ super(...)
21
+
22
+ @html_attr = 'onwheel'
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubomaticHtml
4
+ module Cop
5
+ module Style
6
+ class PartialInstanceVariable < RubomaticHtml::Cop::Style::Base
7
+ class << self
8
+ # @see super
9
+ def abstract_cop?
10
+ false
11
+ end
12
+
13
+ # @see super
14
+ def name
15
+ [department, 'PartialInstanceVariable'].join('/')
16
+ end
17
+
18
+ # @see super
19
+ def allowed_config_transform
20
+ super.merge({ 'AllowedIdentifiers' => :allowed_identifiers }).freeze
21
+ end
22
+ end
23
+
24
+ # @see super
25
+ def run_for_line(line, index)
26
+ return unless File.basename(file).match?(/^_/i)
27
+
28
+ return unless line.match?(/@/i)
29
+
30
+ return if allowed_identifiers.any? { |ai| line[/@.+/].start_with?(ai) }
31
+
32
+ puts("#{file}:#{index}: uses an instance variable")
33
+ end
34
+
35
+ private
36
+
37
+ # @return [Array<String>]
38
+ def allowed_identifiers
39
+ @allowed_identifiers ||= Array(config.dig(:allowed_identifiers))
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubomaticHtml
4
+ class Generator
5
+ class CopReadmeInjector < DeptReadmeInjector
6
+ TEMPLATE =
7
+ '* xref:./%{cop_folder}/README.adoc[``%{department}/%{cop}``]'
8
+
9
+ # :nodoc:
10
+ def initialize(badge:, **kwargs)
11
+ super(badge: badge, **kwargs)
12
+
13
+ @cop = badge.cop_name
14
+ end
15
+
16
+ private
17
+
18
+ # @return [String]
19
+ attr_reader :cop
20
+
21
+ # @see super
22
+ def new_readme_entry
23
+ format(TEMPLATE, {
24
+ department_folder: snake_case(department),
25
+ cop_folder: snake_case(cop),
26
+ department: department,
27
+ cop: cop
28
+ })
29
+ end
30
+
31
+ # @see super
32
+ def line_is_good?(line)
33
+ return true if super
34
+
35
+ matches = line.match(target_regex)
36
+
37
+ return false if matches.nil?
38
+
39
+ department == matches[:department] && cop < matches[:cop]
40
+ end
41
+
42
+ # @see super
43
+ def target_regex
44
+ %r{\* xref:\./[a-z_]+/README\.adoc\[``(?<department>[a-zA-Z_]+)/(?<cop>[a-zA-Z_]+)``\]}
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubomaticHtml
4
+ class Generator
5
+ class DeptReadmeInjector
6
+ TEMPLATE = '* xref:./docs/cops/%{department_folder}/README.adoc[``%{department}``]'
7
+
8
+ # :nodoc:
9
+ def initialize(readme_file_path:, badge:, department:)
10
+ @readme_file_path = readme_file_path
11
+ @badge = badge
12
+ @department = department
13
+ @output = output
14
+ end
15
+
16
+ # Performs the actual string injection into the file
17
+ # modified version of `inject` from RuboCop::Cop::Generator::ConfigurationInjector
18
+ # Named `inject_string` becuase rubocop thought when called it was `Array#inject`
19
+ #
20
+ # @return [void]
21
+ #
22
+ def inject_string
23
+ if readme_entries.none?("#{new_readme_entry}\n")
24
+ target_line = find_target_line
25
+
26
+ if target_line
27
+ readme_entries.insert(target_line, "#{new_readme_entry}\n")
28
+ else
29
+ readme_entries.push(new_readme_entry)
30
+ end
31
+ end
32
+
33
+ File.write(readme_file_path, readme_entries.join(''))
34
+
35
+ yield if block_given?
36
+ end
37
+
38
+ private
39
+
40
+ # @return [String]
41
+ attr_reader :readme_file_path
42
+ # @return [RuboCop::Cop::Badge]
43
+ attr_reader :badge
44
+ # @return [String]
45
+ attr_reader :department
46
+ # @return [*] Default $stdout
47
+ attr_reader :output
48
+
49
+ # Lines in <department>/README.adoc
50
+ #
51
+ # @return [Array<String>]
52
+ #
53
+ def readme_entries
54
+ @readme_entries ||= File.readlines(readme_file_path)
55
+ end
56
+
57
+ # Modified version from Rubocop::Cop::Generator::ConfigurationInjector
58
+ #
59
+ # @return [String]
60
+ #
61
+ def new_readme_entry
62
+ format(TEMPLATE, {
63
+ department_folder: snake_case(department),
64
+ department: department
65
+ })
66
+ end
67
+
68
+ # Modified version from Rubocop::Cop::Generator::ConfigurationInjector
69
+ #
70
+ # @return [Integer, Nil]
71
+ #
72
+ def find_target_line
73
+ readme_entries.find_index do |line|
74
+ line_is_good?(line)
75
+ end
76
+ end
77
+
78
+ # Determines if the given line is the same type we're trying to add and if it's alphabetically before
79
+ #
80
+ # @return [Boolean]
81
+ #
82
+ def line_is_good?(line)
83
+ matches = line.match(target_regex)
84
+
85
+ return false if matches.nil?
86
+
87
+ department < line.match(target_regex)[:department]
88
+ end
89
+
90
+ # Regex to look for in the readme
91
+ #
92
+ # @return [Regexp]
93
+ #
94
+ def target_regex
95
+ %r{\* xref:\./docs/cops/[a-z_]+/README\.adoc\[``(?<department>[a-zA-Z_]+)``\]}
96
+ end
97
+
98
+ # Copied from Rubocop::Cop::Generator
99
+ #
100
+ # @return [String]
101
+ #
102
+ def snake_case(camel_case_string)
103
+ camel_case_string
104
+ .gsub('RSpec', 'Rspec')
105
+ .gsub(%r{([^A-Z/])([A-Z]+)}, '\1_\2')
106
+ .gsub(%r{([A-Z])([A-Z][^A-Z\d/]+)}, '\1_\2')
107
+ .downcase
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,330 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'generator/dept_readme_injector'
4
+ # require_relative 'generator/require_file_injector'
5
+
6
+ require_relative 'generator/cop_readme_injector'
7
+
8
+ module RubomaticHtml
9
+ class Generator
10
+ COP_DOC = <<~RUBY
11
+ # TODO: Write cop description and example of bad / good code. For every
12
+ # `SupportedStyle` and unique configuration, there needs to be examples.
13
+ # Examples must have valid Ruby syntax. Do not use upticks.
14
+ #
15
+ # @safety
16
+ # Delete this section if the cop is not unsafe (`Safe: false` or
17
+ # `SafeAutoCorrect: false`), or use it to explain how the cop is
18
+ # unsafe.
19
+ #
20
+ # @example EnforcedStyle: bar (default)
21
+ # # Description of the `bar` style.
22
+ #
23
+ # # bad
24
+ # bad_bar_method
25
+ #
26
+ # # bad
27
+ # bad_bar_method(args)
28
+ #
29
+ # # good
30
+ # good_bar_method
31
+ #
32
+ # # good
33
+ # good_bar_method(args)
34
+ #
35
+ # @example EnforcedStyle: foo
36
+ # # Description of the `foo` style.
37
+ #
38
+ # # bad
39
+ # bad_foo_method
40
+ #
41
+ # # bad
42
+ # bad_foo_method(args)
43
+ #
44
+ # # good
45
+ # good_foo_method
46
+ #
47
+ # # good
48
+ # good_foo_method(args)
49
+ #
50
+ RUBY
51
+ SOURCE_TEMPLATE = <<~RUBY
52
+ # frozen_string_literal: true
53
+
54
+ module RubomaticHtml
55
+ module Cop
56
+ module %{department}
57
+ class %{cop_name} < RubomaticHtml::Cop::%{department}::Base
58
+ class << self
59
+ # @see super
60
+ def abstract_cop?
61
+ false
62
+ end
63
+
64
+ # @see super
65
+ def name
66
+ [department, '%{cop_name}'].join('/')
67
+ end
68
+ end
69
+
70
+ # @see super
71
+ def run_for_line(line, index)
72
+ # TODO: Implement the cop in here.
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ RUBY
79
+ SPEC_TEMPLATE = <<~SPEC
80
+ # frozen_string_literal: true
81
+
82
+ RSpec.describe RubomaticHtml::Cop::%{department}::%{cop_name}, :config do
83
+ let(:config) { RubomaticHtml::Config.new }
84
+
85
+ # TODO: Write test code
86
+ #
87
+ # For example
88
+ it 'registers an offense when using `#bad_method`' do
89
+ expect_offense(<<~RHTML)
90
+ bad_method
91
+ ^^^^^^^^^^ Use `#good_method` instead of `#bad_method`.
92
+ RHTML
93
+ end
94
+
95
+ it 'does not register an offense when using `#good_method`' do
96
+ expect_no_offenses(<<~RHTML)
97
+ good_method
98
+ RHTML
99
+ end
100
+ end
101
+ SPEC
102
+ README_ADDED_MESSAGE = '[modify] A link for the %{dept_vs_cop} has been added into %{readme_file_path}.'
103
+
104
+ DEPT_README_TEMPLATE = <<~ADOC
105
+ = %{department}
106
+
107
+ Describe the department here
108
+
109
+ == Cops
110
+
111
+ ADOC
112
+ COP_README_TEMPLATE = <<~ADOC
113
+ = ``%{department}/%{cop_name}``
114
+
115
+ == Description
116
+
117
+ Add a description here
118
+
119
+ == Examples
120
+
121
+ [source,rhtml]
122
+ ----
123
+ <!-- Bad -->
124
+ <!-- Add a bad example here -->
125
+
126
+ <!-- Good -->
127
+ <!-- Add a good example here -->
128
+ ----
129
+
130
+ == Configurable Attributes
131
+
132
+ |===
133
+ |Name |Default value |Configurable values
134
+
135
+ |Max
136
+ |120
137
+ |Integer
138
+
139
+ |===
140
+
141
+ == References
142
+
143
+ https://github.com/BrandsInsurance/expert-chainsaw/issues
144
+ ADOC
145
+
146
+ # :nodoc:
147
+ def initialize(name, output: $stdout)
148
+ @base_gen = RuboCop::Cop::Generator.new(name, output: output)
149
+ end
150
+
151
+ # @see RuboCop::Cop::generator method
152
+ def write_source
153
+ write_unless_file_exists(source_path, generated_source)
154
+ end
155
+
156
+ # @see RuboCop::Cop::generator method
157
+ def write_spec
158
+ write_unless_file_exists(spec_path, generated_spec)
159
+ end
160
+
161
+ # @see RuboCop::Cop::generator method
162
+ def generated_source
163
+ generate(SOURCE_TEMPLATE)
164
+ end
165
+
166
+ # @see RuboCop::Cop::generator method
167
+ def generated_spec
168
+ generate(SPEC_TEMPLATE)
169
+ end
170
+
171
+ # @see RuboCop::Cop::generator method
172
+ def source_path
173
+ File.join(
174
+ 'lib',
175
+ 'rubomatic-html',
176
+ 'cop',
177
+ snake_case(badge.department.to_s),
178
+ "#{snake_case(badge.cop_name.to_s)}.rb"
179
+ )
180
+ end
181
+
182
+ # @see RuboCop::Cop::generator method
183
+ def spec_path
184
+ File.join(
185
+ 'spec',
186
+ 'rubomatic-html',
187
+ 'cop',
188
+ snake_case(badge.department.to_s),
189
+ "#{snake_case(badge.cop_name.to_s)}_spec.rb"
190
+ )
191
+ end
192
+
193
+ # @see RuboCop::Cop::generator method
194
+ def inject_require(root_file_path:)
195
+ RuboCop::Cop::Generator::RequireFileInjector.new(source_path: source_path, root_file_path: root_file_path).inject
196
+ end
197
+
198
+ # Calls methods in the base class
199
+ #
200
+ # @return [*]
201
+ #
202
+ def method_missing(...)
203
+ @base_gen.__send__(...)
204
+ end
205
+
206
+ # `self` responds to `method_name` if `@base_gen` does
207
+ #
208
+ def respond_to_missing?(method_name, include_private = false)
209
+ @base_gen.respond_to?(method_name, include_private)
210
+ end
211
+
212
+ # Creates the department readme if it doesn't exist
213
+ # Modified version of `wirte_source` from RuboCop::Cop::Generator
214
+ #
215
+ # @return [void]
216
+ #
217
+ def write_dept_readme
218
+ return if File.exist?(dept_docs_path)
219
+
220
+ write_unless_file_exists(dept_docs_path, generated_dept_docs)
221
+ end
222
+
223
+ # Creates the cop readme if it doesn't exist
224
+ # Modified version of `wirte_source` from RuboCop::Cop::Generator
225
+ #
226
+ # @return [void]
227
+ #
228
+ def write_cop_readme
229
+ write_unless_file_exists(docs_path, generated_cop_docs)
230
+ end
231
+
232
+ # Injects the, possibly new, department readme link into the base readme
233
+ # Modified version of `inject_config` from RuboCop::Cop::Generator
234
+ #
235
+ # @return [void]
236
+ #
237
+ def inject_dept_readme(readme_file_path: 'README.adoc')
238
+ # Add this dept to base readme if not already there
239
+ injector = DeptReadmeInjector.new(
240
+ readme_file_path: readme_file_path,
241
+ badge: badge,
242
+ department: department
243
+ )
244
+
245
+ injector.inject_string do
246
+ output.puts(format(README_ADDED_MESSAGE, readme_file_path: readme_file_path, dept_vs_cop: 'department'))
247
+ end
248
+ end
249
+
250
+ # Injects the new cop readme link into the department readme
251
+ # Modified version of `inject_config` from RuboCop::Cop::Generator
252
+ #
253
+ # @return [void]
254
+ #
255
+ def inject_cop_readme(readme_file_path: dept_docs_path)
256
+ # Add this cop to the dept readme
257
+ injector = CopReadmeInjector.new(
258
+ readme_file_path: readme_file_path,
259
+ badge: badge,
260
+ department: department
261
+ )
262
+
263
+ injector.inject_string do
264
+ output.puts(format(README_ADDED_MESSAGE, readme_file_path: readme_file_path, dept_vs_cop: 'cop'))
265
+ end
266
+ end
267
+
268
+ private
269
+
270
+ # @return [String]
271
+ def department
272
+ badge.department_name
273
+ end
274
+
275
+ # Modified version of `generated_source` from Rubocop::Cop::Generator
276
+ #
277
+ # @return [String]
278
+ #
279
+ def generated_dept_docs
280
+ generate_readme(DEPT_README_TEMPLATE)
281
+ end
282
+
283
+ # Modified version of `generated_source` from Rubocop::Cop::Generator
284
+ #
285
+ # @return [String]
286
+ #
287
+ def generated_cop_docs
288
+ generate_readme(COP_README_TEMPLATE)
289
+ end
290
+
291
+ # Modified version from Rubocop::Cop::Generator
292
+ #
293
+ # @return [String]
294
+ #
295
+ def generate_readme(template)
296
+ format(template, {
297
+ department: department,
298
+ cop_name: badge.cop_name,
299
+ cop_folder: snake_case(badge.cop_name.to_s)
300
+ })
301
+ end
302
+
303
+ # Path to <department>/README.adoc
304
+ #
305
+ # @return [String]
306
+ #
307
+ def dept_docs_path
308
+ File.join(
309
+ 'docs',
310
+ 'cops',
311
+ snake_case(department),
312
+ 'README.adoc'
313
+ )
314
+ end
315
+
316
+ # Path to <department>/<cop>/README.adoc
317
+ #
318
+ # @return [String]
319
+ #
320
+ def docs_path
321
+ File.join(
322
+ 'docs',
323
+ 'cops',
324
+ snake_case(department),
325
+ snake_case(badge.cop_name.to_s),
326
+ 'README.adoc'
327
+ )
328
+ end
329
+ end
330
+ end