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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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