rubocop-sketchup 1.2.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +20 -19
  3. data/assets/output.html.erb +301 -301
  4. data/config/default.yml +379 -379
  5. data/lib/rubocop/sketchup/config.rb +63 -63
  6. data/lib/rubocop/sketchup/cop/bugs/material_name.rb +108 -108
  7. data/lib/rubocop/sketchup/cop/bugs/render_mode.rb +72 -72
  8. data/lib/rubocop/sketchup/cop/bugs/uniform_scaling.rb +36 -36
  9. data/lib/rubocop/sketchup/cop/deprecations/add_separator_to_menu.rb +25 -25
  10. data/lib/rubocop/sketchup/cop/deprecations/operation_next_transparent.rb +30 -30
  11. data/lib/rubocop/sketchup/cop/deprecations/require_all.rb +27 -27
  12. data/lib/rubocop/sketchup/cop/deprecations/set_texture_projection.rb +26 -26
  13. data/lib/rubocop/sketchup/cop/deprecations/show_ruby_panel.rb +25 -25
  14. data/lib/rubocop/sketchup/cop/deprecations/sketchup_set.rb +30 -30
  15. data/lib/rubocop/sketchup/cop/performance/openssl.rb +41 -41
  16. data/lib/rubocop/sketchup/cop/performance/operation_disable_ui.rb +33 -33
  17. data/lib/rubocop/sketchup/cop/performance/selection_bulk.rb +79 -79
  18. data/lib/rubocop/sketchup/cop/performance/type_check.rb +63 -63
  19. data/lib/rubocop/sketchup/cop/performance/typename.rb +24 -24
  20. data/lib/rubocop/sketchup/cop/requirements/api_namespace.rb +30 -30
  21. data/lib/rubocop/sketchup/cop/requirements/exit.rb +33 -33
  22. data/lib/rubocop/sketchup/cop/requirements/extension_namespace.rb +125 -125
  23. data/lib/rubocop/sketchup/cop/requirements/file_structure.rb +97 -97
  24. data/lib/rubocop/sketchup/cop/requirements/gem_install.rb +45 -45
  25. data/lib/rubocop/sketchup/cop/requirements/get_extension_license.rb +95 -95
  26. data/lib/rubocop/sketchup/cop/requirements/global_constants.rb +38 -38
  27. data/lib/rubocop/sketchup/cop/requirements/global_include.rb +42 -42
  28. data/lib/rubocop/sketchup/cop/requirements/global_methods.rb +65 -65
  29. data/lib/rubocop/sketchup/cop/requirements/global_variables.rb +95 -95
  30. data/lib/rubocop/sketchup/cop/requirements/language_handler_globals.rb +46 -46
  31. data/lib/rubocop/sketchup/cop/requirements/load_path.rb +83 -83
  32. data/lib/rubocop/sketchup/cop/requirements/minimal_registration.rb +73 -73
  33. data/lib/rubocop/sketchup/cop/requirements/observers_start_operation.rb +161 -161
  34. data/lib/rubocop/sketchup/cop/requirements/register_extension.rb +45 -45
  35. data/lib/rubocop/sketchup/cop/requirements/ruby_core_namespace.rb +291 -291
  36. data/lib/rubocop/sketchup/cop/requirements/ruby_stdlib_namespace.rb +634 -634
  37. data/lib/rubocop/sketchup/cop/requirements/shipped_extensions_namespace.rb +61 -61
  38. data/lib/rubocop/sketchup/cop/requirements/sketchup_extension.rb +119 -119
  39. data/lib/rubocop/sketchup/cop/requirements/sketchup_require.rb +150 -149
  40. data/lib/rubocop/sketchup/cop/suggestions/add_group.rb +49 -49
  41. data/lib/rubocop/sketchup/cop/suggestions/compatibility.rb +128 -128
  42. data/lib/rubocop/sketchup/cop/suggestions/dc_internals.rb +34 -34
  43. data/lib/rubocop/sketchup/cop/suggestions/file_encoding.rb +78 -78
  44. data/lib/rubocop/sketchup/cop/suggestions/model_entities.rb +58 -58
  45. data/lib/rubocop/sketchup/cop/suggestions/monkey_patched_api.rb +45 -45
  46. data/lib/rubocop/sketchup/cop/suggestions/operation_name.rb +137 -137
  47. data/lib/rubocop/sketchup/cop/suggestions/sketchup_find_support_file.rb +39 -39
  48. data/lib/rubocop/sketchup/cop/suggestions/tool_drawing_bounds.rb +45 -45
  49. data/lib/rubocop/sketchup/cop/suggestions/tool_invalidate.rb +68 -68
  50. data/lib/rubocop/sketchup/cop/suggestions/tool_user_input.rb +41 -41
  51. data/lib/rubocop/sketchup/cop/suggestions/toolbar_timer.rb +65 -65
  52. data/lib/rubocop/sketchup/cop.rb +110 -110
  53. data/lib/rubocop/sketchup/dc_globals.rb +24 -24
  54. data/lib/rubocop/sketchup/dc_methods.rb +130 -130
  55. data/lib/rubocop/sketchup/extension_project.rb +65 -65
  56. data/lib/rubocop/sketchup/features.rb +1477 -1420
  57. data/lib/rubocop/sketchup/formatter/extension_review.rb +269 -269
  58. data/lib/rubocop/sketchup/inject.rb +19 -19
  59. data/lib/rubocop/sketchup/namespace.rb +49 -49
  60. data/lib/rubocop/sketchup/namespace_checker.rb +103 -46
  61. data/lib/rubocop/sketchup/no_comment_disable.rb +17 -17
  62. data/lib/rubocop/sketchup/range_help.rb +52 -52
  63. data/lib/rubocop/sketchup/sketchup_target_range.rb +75 -75
  64. data/lib/rubocop/sketchup/sketchup_version.rb +129 -128
  65. data/lib/rubocop/sketchup/tool_checker.rb +41 -43
  66. data/lib/rubocop/sketchup/version.rb +7 -7
  67. data/lib/rubocop/sketchup.rb +14 -14
  68. data/lib/rubocop-sketchup.rb +53 -53
  69. data/rubocop-sketchup.gemspec +29 -29
  70. metadata +8 -8
@@ -1,24 +1,24 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module SketchupPerformance
6
- # `.typename` is very slow, prefer `.is_a?` instead.
7
- #
8
- # `entity.typename == 'Face'` is slow because it performs a string
9
- # comparison. `is_a?` is much faster because it's a simple type check.
10
- class Typename < SketchUp::Cop
11
- MSG = '`.typename` is very slow, prefer `.is_a?` instead.'
12
-
13
- def on_send(node)
14
- _, method_name = *node
15
- return unless method_name == :typename
16
-
17
- # TODO(thomthom): Should we try to detect use of #typename
18
- # in context of comparing against a string?
19
- add_offense(node, location: :selector)
20
- end
21
- end
22
- end
23
- end
24
- end
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupPerformance
6
+ # `.typename` is very slow, prefer `.is_a?` instead.
7
+ #
8
+ # `entity.typename == 'Face'` is slow because it performs a string
9
+ # comparison. `is_a?` is much faster because it's a simple type check.
10
+ class Typename < SketchUp::Cop
11
+ MSG = '`.typename` is very slow, prefer `.is_a?` instead.'
12
+
13
+ def on_send(node)
14
+ _, method_name = *node
15
+ return unless method_name == :typename
16
+
17
+ # TODO(thomthom): Should we try to detect use of #typename
18
+ # in context of comparing against a string?
19
+ add_offense(node, location: :selector)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,30 +1,30 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module SketchupRequirements
6
- # Do not modify the Sketch API. This will affect other extensions and
7
- # very likely cause them to fail.
8
- #
9
- # This requirement also include adding things into the SketchUp API
10
- # namespace. The API namespace is reserved for future additions to the
11
- # API.
12
- class ApiNamespace < SketchUp::Cop
13
-
14
- include SketchUp::NoCommentDisable
15
- include SketchUp::NamespaceChecker
16
-
17
- MSG = 'Do not modify the SketchUp API.'
18
-
19
- NAMESPACES = %w[
20
- Geom Layout Sketchup SketchupExtension UI
21
- ].freeze
22
-
23
- def namespaces
24
- NAMESPACES
25
- end
26
-
27
- end
28
- end
29
- end
30
- end
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupRequirements
6
+ # Do not modify the Sketch API. This will affect other extensions and
7
+ # very likely cause them to fail.
8
+ #
9
+ # This requirement also include adding things into the SketchUp API
10
+ # namespace. The API namespace is reserved for future additions to the
11
+ # API.
12
+ class ApiNamespace < SketchUp::Cop
13
+
14
+ include SketchUp::NoCommentDisable
15
+ include SketchUp::NamespaceChecker
16
+
17
+ MSG = 'Do not modify the SketchUp API.'
18
+
19
+ NAMESPACES = %w[
20
+ Geom Layout Sketchup SketchupExtension UI
21
+ ].freeze
22
+
23
+ def namespaces
24
+ NAMESPACES
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,33 +1,33 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module SketchupRequirements
6
- # Don't attempt to kill the Ruby interpreter by calling `exit` or `exit!`.
7
- # SketchUp will trap `exit` and prevent that, with a message in the
8
- # console. But `exit!` is not trapped and will terminate SketchUp without
9
- # shutting down cleanly.
10
- #
11
- # Use `return`, `next`, `break` or `raise` instead.
12
- class Exit < SketchUp::Cop
13
-
14
- include SketchUp::NoCommentDisable
15
-
16
- MSG = '`%s` attempts to kill the Ruby interpreter. Use `return`, '\
17
- '`next`, `break` or `raise` instead.'
18
-
19
- # Reference: http://rubocop.readthedocs.io/en/latest/development/
20
- def_node_matcher :exit?, <<-PATTERN
21
- (send {(const nil? :Kernel) nil?} {:abort :exit :exit!} ...)
22
- PATTERN
23
-
24
- def on_send(node)
25
- return unless exit?(node)
26
-
27
- message = format(MSG, node.method_name)
28
- add_offense(node, location: :selector, message: message)
29
- end
30
- end
31
- end
32
- end
33
- end
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupRequirements
6
+ # Don't attempt to kill the Ruby interpreter by calling `exit` or `exit!`.
7
+ # SketchUp will trap `exit` and prevent that, with a message in the
8
+ # console. But `exit!` is not trapped and will terminate SketchUp without
9
+ # shutting down cleanly.
10
+ #
11
+ # Use `return`, `next`, `break` or `raise` instead.
12
+ class Exit < SketchUp::Cop
13
+
14
+ include SketchUp::NoCommentDisable
15
+
16
+ MSG = '`%s` attempts to kill the Ruby interpreter. Use `return`, ' \
17
+ '`next`, `break` or `raise` instead.'
18
+
19
+ # Reference: http://rubocop.readthedocs.io/en/latest/development/
20
+ def_node_matcher :exit?, <<-PATTERN
21
+ (send {(const nil? :Kernel) nil?} {:abort :exit :exit!} ...)
22
+ PATTERN
23
+
24
+ def on_send(node)
25
+ return unless exit?(node)
26
+
27
+ message = format(MSG, node.method_name)
28
+ add_offense(node, location: :selector, message: message)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,125 +1,125 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rubocop/sketchup/cop/requirements/api_namespace'
4
- require 'rubocop/sketchup/cop/requirements/ruby_core_namespace'
5
- require 'rubocop/sketchup/cop/requirements/ruby_stdlib_namespace'
6
-
7
- module RuboCop
8
- module Cop
9
- module SketchupRequirements
10
- # Extensions in SketchUp all share the same Ruby environment on the user's
11
- # machine. Because of this it's important that each extension isolate
12
- # itself to avoid clashing with other extensions.
13
- #
14
- # Extensions submitted to Extension Warehouse is expected to use only one
15
- # root module.
16
- #
17
- # @example Good - this contains everything in the extension.
18
- # module MyExtension
19
- # class Foo
20
- # end
21
- # class Bar
22
- # end
23
- # end
24
- #
25
- # @example Better - this further reduce chance of clashing.
26
- # module MyCompany
27
- # module MyExtension
28
- # class Foo
29
- # end
30
- # class Bar
31
- # end
32
- # end
33
- # end
34
- class ExtensionNamespace < SketchUp::Cop
35
-
36
- include SketchUp::NoCommentDisable
37
- include SketchUp
38
-
39
- def on_class(node)
40
- check_class_or_module(node)
41
- end
42
-
43
- def on_module(node)
44
- check_class_or_module(node)
45
- end
46
-
47
- def check_class_or_module(node)
48
- name = node.defined_module_name
49
-
50
- if node.parent_module_name
51
- parent = Namespace.new(node.parent_module_name)
52
- else
53
- # This is somewhat of an educated guess. We might end up here with
54
- # code like this:
55
- #
56
- # Example.generate do
57
- # module HelloWorld
58
- # end
59
- # end
60
- #
61
- # It might be that the module is evaluated in a different context.
62
- # But we'll accept the possible false positive and let the user
63
- # config exceptions if needed.
64
- parent = Namespace.new('Object')
65
- end
66
- namespace = parent.join(name)
67
-
68
- # Don't want to process anything that aren't top level namespaces.
69
- return unless parent.top_level?
70
- # Don't check excluded namespaces.
71
- return if exempted?(namespace)
72
-
73
- check_namespace(node, namespace)
74
- end
75
-
76
- # Class variables are normally frowned upon since they leak through all
77
- # instances. However, in this case this is exactly what we want.
78
- # The Cop picks up the first top level namespace it encounters and then
79
- # keep track of whether it detects more top level namespaces.
80
- @@namespace = nil
81
- def check_namespace(node, namespace)
82
- # Make sure the namespace isn't part of reserved namespaces that other
83
- # cops are checking.
84
- return if reserved?(namespace)
85
-
86
- # Remember the first namespace encountered and log an offence if
87
- # more top level namespaces are registered.
88
- top = namespace.first
89
- @@namespace ||= top
90
- return if @@namespace == top
91
-
92
- add_offense(node, location: :name)
93
- end
94
-
95
- def reserved?(namespace)
96
- top = namespace.first
97
- return true if RubyCoreNamespace::NAMESPACES.include?(top)
98
- return true if RubyStdLibNamespace::NAMESPACES.include?(top)
99
- return true if ApiNamespace::NAMESPACES.include?(top)
100
-
101
- false
102
- end
103
-
104
- def message(node)
105
- namespace = Namespace.new(node.defined_module_name).from_root
106
- format('Use a single root namespace. '\
107
- '(Found `%<found>s`; Previously found `%<expected>s`)',
108
- found: namespace, expected: @@namespace)
109
- end
110
-
111
- def exempted?(namespace)
112
- namespace_exceptions.include?(namespace.first)
113
- end
114
-
115
- def namespace_exceptions
116
- exceptions = cop_config['Exceptions'] || []
117
- return exceptions if exceptions.is_a?(Array)
118
-
119
- raise 'exceptions needs to be an array of strings!'
120
- end
121
-
122
- end
123
- end
124
- end
125
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop/sketchup/cop/requirements/api_namespace'
4
+ require 'rubocop/sketchup/cop/requirements/ruby_core_namespace'
5
+ require 'rubocop/sketchup/cop/requirements/ruby_stdlib_namespace'
6
+
7
+ module RuboCop
8
+ module Cop
9
+ module SketchupRequirements
10
+ # Extensions in SketchUp all share the same Ruby environment on the user's
11
+ # machine. Because of this it's important that each extension isolate
12
+ # itself to avoid clashing with other extensions.
13
+ #
14
+ # Extensions submitted to Extension Warehouse is expected to use only one
15
+ # root module.
16
+ #
17
+ # @example Good - this contains everything in the extension.
18
+ # module MyExtension
19
+ # class Foo
20
+ # end
21
+ # class Bar
22
+ # end
23
+ # end
24
+ #
25
+ # @example Better - this further reduce chance of clashing.
26
+ # module MyCompany
27
+ # module MyExtension
28
+ # class Foo
29
+ # end
30
+ # class Bar
31
+ # end
32
+ # end
33
+ # end
34
+ class ExtensionNamespace < SketchUp::Cop
35
+
36
+ include SketchUp::NoCommentDisable
37
+ include SketchUp
38
+
39
+ def on_class(node)
40
+ check_class_or_module(node)
41
+ end
42
+
43
+ def on_module(node)
44
+ check_class_or_module(node)
45
+ end
46
+
47
+ def check_class_or_module(node)
48
+ name = node.defined_module_name
49
+
50
+ if node.parent_module_name
51
+ parent = Namespace.new(node.parent_module_name)
52
+ else
53
+ # This is somewhat of an educated guess. We might end up here with
54
+ # code like this:
55
+ #
56
+ # Example.generate do
57
+ # module HelloWorld
58
+ # end
59
+ # end
60
+ #
61
+ # It might be that the module is evaluated in a different context.
62
+ # But we'll accept the possible false positive and let the user
63
+ # config exceptions if needed.
64
+ parent = Namespace.new('Object')
65
+ end
66
+ namespace = parent.join(name)
67
+
68
+ # Don't want to process anything that aren't top level namespaces.
69
+ return unless parent.top_level?
70
+ # Don't check excluded namespaces.
71
+ return if exempted?(namespace)
72
+
73
+ check_namespace(node, namespace)
74
+ end
75
+
76
+ # Class variables are normally frowned upon since they leak through all
77
+ # instances. However, in this case this is exactly what we want.
78
+ # The Cop picks up the first top level namespace it encounters and then
79
+ # keep track of whether it detects more top level namespaces.
80
+ @@namespace = nil
81
+ def check_namespace(node, namespace)
82
+ # Make sure the namespace isn't part of reserved namespaces that other
83
+ # cops are checking.
84
+ return if reserved?(namespace)
85
+
86
+ # Remember the first namespace encountered and log an offence if
87
+ # more top level namespaces are registered.
88
+ top = namespace.first
89
+ @@namespace ||= top
90
+ return if @@namespace == top
91
+
92
+ add_offense(node, location: :name)
93
+ end
94
+
95
+ def reserved?(namespace)
96
+ top = namespace.first
97
+ return true if RubyCoreNamespace::NAMESPACES.include?(top)
98
+ return true if RubyStdLibNamespace::NAMESPACES.include?(top)
99
+ return true if ApiNamespace::NAMESPACES.include?(top)
100
+
101
+ false
102
+ end
103
+
104
+ def message(node)
105
+ namespace = Namespace.new(node.defined_module_name).from_root
106
+ format('Use a single root namespace. ' \
107
+ '(Found `%<found>s`; Previously found `%<expected>s`)',
108
+ found: namespace, expected: @@namespace)
109
+ end
110
+
111
+ def exempted?(namespace)
112
+ namespace_exceptions.include?(namespace.first)
113
+ end
114
+
115
+ def namespace_exceptions
116
+ exceptions = cop_config['Exceptions'] || []
117
+ return exceptions if exceptions.is_a?(Array)
118
+
119
+ raise 'exceptions needs to be an array of strings!'
120
+ end
121
+
122
+ end
123
+ end
124
+ end
125
+ end
@@ -1,97 +1,97 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module SketchupRequirements
6
- # Check that the extension conform to expected file structure with a
7
- # single root .rb file and a support folder with matching name.
8
- #
9
- # Make sure to match upper and lower case characters between the root .rb
10
- # file and the support folder.
11
- #
12
- # @example
13
- # SketchUp/Plugins
14
- # + ex_hello_world.rb
15
- # + ex_hello_world
16
- # + main.rb
17
- # + ...
18
- class FileStructure < SketchUp::Cop
19
-
20
- include SketchUp::NoCommentDisable
21
- include SketchUp::ExtensionProject
22
- include RangeHelp
23
-
24
- IGNORED_DIRECTORIES = %w[
25
- __MACOSX
26
- ].freeze
27
-
28
- def investigate(processed_source)
29
- return if already_run?
30
-
31
- # Using range similar to RuboCop::Cop::Naming::Filename (file_name.rb)
32
- range = source_range(processed_source.buffer, 1, 0)
33
-
34
- # Find all root Ruby files in the source directory.
35
- pattern = "#{source_path}/*.rb"
36
- root_ruby_files = Dir.glob(pattern)
37
-
38
- # Ensure there is only one root Ruby file.
39
- if root_ruby_files.size != 1
40
- msg = 'Extensions must have exactly one root Ruby (.rb) file. '\
41
- 'Found: %d'
42
- add_offense(nil,
43
- location: range,
44
- message: format(msg, root_ruby_files.size))
45
- return
46
- end
47
-
48
- # Find the root file and collect the sub-directories.
49
- root_file = root_ruby_files.first
50
- extension_basename = File.basename(root_file, '.*')
51
- sub_folders = source_path.children.select(&:directory?)
52
- sub_folders.reject! { |folder|
53
- IGNORED_DIRECTORIES.include?(folder.basename.to_s)
54
- }
55
-
56
- # Ensure there is only one sub-directory.
57
- if sub_folders.size != 1
58
- msg = 'Extensions must have exactly one support directory. Found %d'
59
- add_offense(nil,
60
- location: range,
61
- message: format(msg, sub_folders.size))
62
- return
63
- end
64
-
65
- # Ensure support directory's name match the root Ruby file.
66
- support_directory = sub_folders.first
67
- unless support_directory.basename.to_s == extension_basename
68
- msg = 'Extensions must have a support directory matching the name '\
69
- 'of the root Ruby file. Expected %s, found %s'
70
- msg = format(msg, extension_basename, support_directory.basename)
71
- add_offense(nil,
72
- location: range,
73
- message: msg)
74
- end
75
- end
76
-
77
- private
78
-
79
- @@already_run = false
80
-
81
- def already_run?
82
- return true if @@already_run
83
-
84
- @@already_run = true
85
- false
86
- end
87
-
88
- # rubocop:disable Lint/IneffectiveAccessModifier
89
- def self.reset
90
- @@already_run = false
91
- end
92
- # rubocop:enable Lint/IneffectiveAccessModifier
93
-
94
- end
95
- end
96
- end
97
- end
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupRequirements
6
+ # Check that the extension conform to expected file structure with a
7
+ # single root .rb file and a support folder with matching name.
8
+ #
9
+ # Make sure to match upper and lower case characters between the root .rb
10
+ # file and the support folder.
11
+ #
12
+ # @example
13
+ # SketchUp/Plugins
14
+ # + ex_hello_world.rb
15
+ # + ex_hello_world
16
+ # + main.rb
17
+ # + ...
18
+ class FileStructure < SketchUp::Cop
19
+
20
+ include SketchUp::NoCommentDisable
21
+ include SketchUp::ExtensionProject
22
+ include RangeHelp
23
+
24
+ IGNORED_DIRECTORIES = %w[
25
+ __MACOSX
26
+ ].freeze
27
+
28
+ def investigate(processed_source)
29
+ return if already_run?
30
+
31
+ # Using range similar to RuboCop::Cop::Naming::Filename (file_name.rb)
32
+ range = source_range(processed_source.buffer, 1, 0)
33
+
34
+ # Find all root Ruby files in the source directory.
35
+ pattern = "#{source_path}/*.rb"
36
+ root_ruby_files = Dir.glob(pattern)
37
+
38
+ # Ensure there is only one root Ruby file.
39
+ if root_ruby_files.size != 1
40
+ msg = 'Extensions must have exactly one root Ruby (.rb) file. ' \
41
+ 'Found: %d'
42
+ add_offense(nil,
43
+ location: range,
44
+ message: format(msg, root_ruby_files.size))
45
+ return
46
+ end
47
+
48
+ # Find the root file and collect the sub-directories.
49
+ root_file = root_ruby_files.first
50
+ extension_basename = File.basename(root_file, '.*')
51
+ sub_folders = source_path.children.select(&:directory?)
52
+ sub_folders.reject! { |folder|
53
+ IGNORED_DIRECTORIES.include?(folder.basename.to_s)
54
+ }
55
+
56
+ # Ensure there is only one sub-directory.
57
+ if sub_folders.size != 1
58
+ msg = 'Extensions must have exactly one support directory. Found %d'
59
+ add_offense(nil,
60
+ location: range,
61
+ message: format(msg, sub_folders.size))
62
+ return
63
+ end
64
+
65
+ # Ensure support directory's name match the root Ruby file.
66
+ support_directory = sub_folders.first
67
+ unless support_directory.basename.to_s == extension_basename
68
+ msg = 'Extensions must have a support directory matching the ' \
69
+ 'name of the root Ruby file. Expected %s, found %s'
70
+ msg = format(msg, extension_basename, support_directory.basename)
71
+ add_offense(nil,
72
+ location: range,
73
+ message: msg)
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ @@already_run = false
80
+
81
+ def already_run?
82
+ return true if @@already_run
83
+
84
+ @@already_run = true
85
+ false
86
+ end
87
+
88
+ # rubocop:disable Lint/IneffectiveAccessModifier
89
+ def self.reset
90
+ @@already_run = false
91
+ end
92
+ # rubocop:enable Lint/IneffectiveAccessModifier
93
+
94
+ end
95
+ end
96
+ end
97
+ end