rubocop-sketchup 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +19 -19
  3. data/assets/output.html.erb +301 -301
  4. data/config/default.yml +355 -355
  5. data/lib/rubocop/sketchup/config.rb +63 -63
  6. data/lib/rubocop/sketchup/cop/deprecations/add_separator_to_menu.rb +25 -25
  7. data/lib/rubocop/sketchup/cop/deprecations/operation_next_transparent.rb +30 -30
  8. data/lib/rubocop/sketchup/cop/deprecations/require_all.rb +27 -27
  9. data/lib/rubocop/sketchup/cop/deprecations/set_texture_projection.rb +26 -26
  10. data/lib/rubocop/sketchup/cop/deprecations/show_ruby_panel.rb +25 -25
  11. data/lib/rubocop/sketchup/cop/deprecations/sketchup_set.rb +30 -30
  12. data/lib/rubocop/sketchup/cop/performance/openssl.rb +41 -41
  13. data/lib/rubocop/sketchup/cop/performance/operation_disable_ui.rb +33 -33
  14. data/lib/rubocop/sketchup/cop/performance/selection_bulk.rb +79 -79
  15. data/lib/rubocop/sketchup/cop/performance/type_check.rb +63 -63
  16. data/lib/rubocop/sketchup/cop/performance/typename.rb +24 -24
  17. data/lib/rubocop/sketchup/cop/requirements/api_namespace.rb +30 -30
  18. data/lib/rubocop/sketchup/cop/requirements/exit.rb +32 -32
  19. data/lib/rubocop/sketchup/cop/requirements/extension_namespace.rb +108 -108
  20. data/lib/rubocop/sketchup/cop/requirements/file_structure.rb +97 -97
  21. data/lib/rubocop/sketchup/cop/requirements/gem_install.rb +45 -45
  22. data/lib/rubocop/sketchup/cop/requirements/get_extension_license.rb +95 -95
  23. data/lib/rubocop/sketchup/cop/requirements/global_constants.rb +38 -38
  24. data/lib/rubocop/sketchup/cop/requirements/global_include.rb +42 -42
  25. data/lib/rubocop/sketchup/cop/requirements/global_methods.rb +65 -65
  26. data/lib/rubocop/sketchup/cop/requirements/global_variables.rb +95 -95
  27. data/lib/rubocop/sketchup/cop/requirements/language_handler_globals.rb +46 -46
  28. data/lib/rubocop/sketchup/cop/requirements/load_path.rb +83 -83
  29. data/lib/rubocop/sketchup/cop/requirements/minimal_registration.rb +73 -73
  30. data/lib/rubocop/sketchup/cop/requirements/observers_start_operation.rb +161 -161
  31. data/lib/rubocop/sketchup/cop/requirements/register_extension.rb +45 -45
  32. data/lib/rubocop/sketchup/cop/requirements/ruby_core_namespace.rb +291 -291
  33. data/lib/rubocop/sketchup/cop/requirements/ruby_stdlib_namespace.rb +634 -634
  34. data/lib/rubocop/sketchup/cop/requirements/shipped_extensions_namespace.rb +61 -61
  35. data/lib/rubocop/sketchup/cop/requirements/sketchup_extension.rb +119 -119
  36. data/lib/rubocop/sketchup/cop/requirements/sketchup_require.rb +163 -163
  37. data/lib/rubocop/sketchup/cop/suggestions/add_group.rb +49 -49
  38. data/lib/rubocop/sketchup/cop/suggestions/compatibility.rb +117 -117
  39. data/lib/rubocop/sketchup/cop/suggestions/dc_internals.rb +34 -34
  40. data/lib/rubocop/sketchup/cop/suggestions/file_encoding.rb +78 -78
  41. data/lib/rubocop/sketchup/cop/suggestions/model_entities.rb +58 -58
  42. data/lib/rubocop/sketchup/cop/suggestions/monkey_patched_api.rb +45 -45
  43. data/lib/rubocop/sketchup/cop/suggestions/operation_name.rb +103 -103
  44. data/lib/rubocop/sketchup/cop/suggestions/sketchup_find_support_file.rb +39 -39
  45. data/lib/rubocop/sketchup/cop/suggestions/tool_drawing_bounds.rb +44 -44
  46. data/lib/rubocop/sketchup/cop/suggestions/tool_invalidate.rb +66 -66
  47. data/lib/rubocop/sketchup/cop/suggestions/tool_user_input.rb +41 -41
  48. data/lib/rubocop/sketchup/cop/suggestions/toolbar_timer.rb +65 -65
  49. data/lib/rubocop/sketchup/cop.rb +111 -111
  50. data/lib/rubocop/sketchup/dc_globals.rb +24 -24
  51. data/lib/rubocop/sketchup/dc_methods.rb +130 -130
  52. data/lib/rubocop/sketchup/extension_project.rb +65 -65
  53. data/lib/rubocop/sketchup/features.rb +738 -738
  54. data/lib/rubocop/sketchup/formatter/extension_review.rb +259 -259
  55. data/lib/rubocop/sketchup/inject.rb +19 -19
  56. data/lib/rubocop/sketchup/namespace.rb +47 -47
  57. data/lib/rubocop/sketchup/namespace_checker.rb +46 -46
  58. data/lib/rubocop/sketchup/no_comment_disable.rb +17 -17
  59. data/lib/rubocop/sketchup/range_help.rb +52 -52
  60. data/lib/rubocop/sketchup/sketchup_version.rb +87 -87
  61. data/lib/rubocop/sketchup/tool_checker.rb +43 -43
  62. data/lib/rubocop/sketchup/version.rb +5 -5
  63. data/lib/rubocop/sketchup.rb +12 -12
  64. data/lib/rubocop-sketchup.rb +48 -48
  65. data/rubocop-sketchup.gemspec +27 -27
  66. metadata +4 -4
@@ -1,34 +1,34 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module SketchupSuggestions
6
- # Tapping into the internals of Dynamic Components is risky. It could
7
- # change at any time. If you create an extension that depend on the
8
- # internal logic of another extension you are at the mercy of change and
9
- # luck!
10
- class DynamicComponentInternals < SketchUp::Cop
11
-
12
- include SketchUp::DynamicComponentGlobals
13
-
14
- MSG = 'Avoid relying on internal logic of Dynamic Components.'.freeze
15
-
16
- def on_gvar(node)
17
- check_global(node)
18
- end
19
-
20
- def on_gvasgn(node)
21
- check_global(node)
22
- end
23
-
24
- def check_global(node)
25
- global_var, = *node
26
- return unless dc_global_var?(global_var)
27
-
28
- add_offense(node, location: :name, severity: :warning)
29
- end
30
-
31
- end
32
- end
33
- end
34
- end
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupSuggestions
6
+ # Tapping into the internals of Dynamic Components is risky. It could
7
+ # change at any time. If you create an extension that depend on the
8
+ # internal logic of another extension you are at the mercy of change and
9
+ # luck!
10
+ class DynamicComponentInternals < SketchUp::Cop
11
+
12
+ include SketchUp::DynamicComponentGlobals
13
+
14
+ MSG = 'Avoid relying on internal logic of Dynamic Components.'.freeze
15
+
16
+ def on_gvar(node)
17
+ check_global(node)
18
+ end
19
+
20
+ def on_gvasgn(node)
21
+ check_global(node)
22
+ end
23
+
24
+ def check_global(node)
25
+ global_var, = *node
26
+ return unless dc_global_var?(global_var)
27
+
28
+ add_offense(node, location: :name, severity: :warning)
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,78 +1,78 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module SketchupSuggestions
6
- # When using __FILE__ and __dir__, beware that Ruby doesn't apply the
7
- # correct encoding to the strings under Windows. When they contain
8
- # non-english characters it will lead to exceptions being raised when the
9
- # strings are used. Force encoding to work around this.
10
- #
11
- # @example Might fail
12
- # basename = File.basename(__FILE__, '.*')
13
- #
14
- # @example Workaround
15
- # file = __FILE__.dup
16
- # file.force_encoding('UTF-8') if file.respond_to?(:force_encoding)
17
- # basename = File.basename(file, '.*')
18
- class FileEncoding < SketchUp::Cop
19
-
20
- MSG = 'Beware encoding bug with `__FILE__` and `__dir__`.'.freeze
21
-
22
- def_node_matcher :file_loaded?, <<-PATTERN
23
- (send nil? {:file_loaded? :file_loaded} ...)
24
- PATTERN
25
-
26
- def_node_matcher :magic_dir?, <<-PATTERN
27
- (send nil? :__dir__)
28
- PATTERN
29
-
30
- def magic_file?(node)
31
- node.respond_to?(:str_type?) &&
32
- node.str_type? &&
33
- node.source_range.is?('__FILE__')
34
- end
35
-
36
- def magic_file_or_dir?(node)
37
- magic_file?(node) || magic_dir?(node)
38
- end
39
-
40
- def on_send(node)
41
- return if file_loaded?(node)
42
- return if node.arguments.none?(&method(:magic_file_or_dir?))
43
-
44
- add_offense(node, location: :expression)
45
- end
46
-
47
-
48
- def_node_search :force_encoding, <<-PATTERN
49
- (send ({lvar ivar cvar} $_) :force_encoding ...)
50
- PATTERN
51
-
52
- def on_assign(node)
53
- lhs, value = *node
54
- return unless magic_file_or_dir?(value)
55
- # After assigning __FILE__ or __dir_ to a variable, check the parent
56
- # scope to whether .force_encoding is called on the variable.
57
- return if node.parent.nil?
58
-
59
- encoded = force_encoding(node.parent).to_a
60
- return if encoded.include?(lhs)
61
-
62
- add_offense(node)
63
- end
64
-
65
- alias on_lvasgn on_assign
66
- alias on_masgn on_assign
67
- alias on_casgn on_assign
68
- alias on_ivasgn on_assign
69
- alias on_cvasgn on_assign
70
- alias on_gvasgn on_assign
71
- alias on_or_asgn on_assign
72
- alias on_and_asgn on_assign
73
- alias on_op_asgn on_assign
74
-
75
- end
76
- end
77
- end
78
- end
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupSuggestions
6
+ # When using __FILE__ and __dir__, beware that Ruby doesn't apply the
7
+ # correct encoding to the strings under Windows. When they contain
8
+ # non-english characters it will lead to exceptions being raised when the
9
+ # strings are used. Force encoding to work around this.
10
+ #
11
+ # @example Might fail
12
+ # basename = File.basename(__FILE__, '.*')
13
+ #
14
+ # @example Workaround
15
+ # file = __FILE__.dup
16
+ # file.force_encoding('UTF-8') if file.respond_to?(:force_encoding)
17
+ # basename = File.basename(file, '.*')
18
+ class FileEncoding < SketchUp::Cop
19
+
20
+ MSG = 'Beware encoding bug with `__FILE__` and `__dir__`.'.freeze
21
+
22
+ def_node_matcher :file_loaded?, <<-PATTERN
23
+ (send nil? {:file_loaded? :file_loaded} ...)
24
+ PATTERN
25
+
26
+ def_node_matcher :magic_dir?, <<-PATTERN
27
+ (send nil? :__dir__)
28
+ PATTERN
29
+
30
+ def magic_file?(node)
31
+ node.respond_to?(:str_type?) &&
32
+ node.str_type? &&
33
+ node.source_range.is?('__FILE__')
34
+ end
35
+
36
+ def magic_file_or_dir?(node)
37
+ magic_file?(node) || magic_dir?(node)
38
+ end
39
+
40
+ def on_send(node)
41
+ return if file_loaded?(node)
42
+ return if node.arguments.none?(&method(:magic_file_or_dir?))
43
+
44
+ add_offense(node, location: :expression)
45
+ end
46
+
47
+
48
+ def_node_search :force_encoding, <<-PATTERN
49
+ (send ({lvar ivar cvar} $_) :force_encoding ...)
50
+ PATTERN
51
+
52
+ def on_assign(node)
53
+ lhs, value = *node
54
+ return unless magic_file_or_dir?(value)
55
+ # After assigning __FILE__ or __dir_ to a variable, check the parent
56
+ # scope to whether .force_encoding is called on the variable.
57
+ return if node.parent.nil?
58
+
59
+ encoded = force_encoding(node.parent).to_a
60
+ return if encoded.include?(lhs)
61
+
62
+ add_offense(node)
63
+ end
64
+
65
+ alias on_lvasgn on_assign
66
+ alias on_masgn on_assign
67
+ alias on_casgn on_assign
68
+ alias on_ivasgn on_assign
69
+ alias on_cvasgn on_assign
70
+ alias on_gvasgn on_assign
71
+ alias on_or_asgn on_assign
72
+ alias on_and_asgn on_assign
73
+ alias on_op_asgn on_assign
74
+
75
+ end
76
+ end
77
+ end
78
+ end
@@ -1,58 +1,58 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module SketchupSuggestions
6
- # Prefer `model.active_entities` over `model.entities`.
7
- #
8
- # Most tools/actions act upon the active entities context. This could be
9
- # an opened group or component instance. Because of this, prefer
10
- # `model.active_entities` by default over `model.entities` unless you
11
- # have an explicit reason to work in the root model context.
12
- class ModelEntities < SketchUp::Cop
13
-
14
- MSG = 'Prefer `model.active_entities` over `model.entities`.'.freeze
15
-
16
- # Reference: http://www.rubydoc.info/gems/rubocop/RuboCop/NodePattern
17
- def_node_matcher :active_model_entities?, <<-PATTERN
18
- (send
19
- (send (const nil? :Sketchup) :active_model) :entities)
20
- PATTERN
21
-
22
- def_node_matcher :entities_receiver, <<-PATTERN
23
- (send
24
- ({lvar ivar cvar} $_) :entities)
25
- PATTERN
26
-
27
- MODEL_VARIABLE_NAMES = %w[model mod].freeze
28
-
29
- def model_entities?(node)
30
- return true if active_model_entities?(node)
31
-
32
- name = entities_receiver(node)
33
- name && model_variable?(name)
34
- end
35
-
36
- def on_send(node)
37
- add_offense(node, location: :expression) if model_entities?(node)
38
- end
39
-
40
- private
41
-
42
- def model_variable?(name)
43
- basename = variable_basename(name)
44
- MODEL_VARIABLE_NAMES.include?(basename)
45
- end
46
-
47
- def variable_basename(name)
48
- # Extract the basename from variables:
49
- # model => model
50
- # @model => model
51
- # @@model => model
52
- name.to_s.tr('@', '')
53
- end
54
-
55
- end
56
- end
57
- end
58
- end
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupSuggestions
6
+ # Prefer `model.active_entities` over `model.entities`.
7
+ #
8
+ # Most tools/actions act upon the active entities context. This could be
9
+ # an opened group or component instance. Because of this, prefer
10
+ # `model.active_entities` by default over `model.entities` unless you
11
+ # have an explicit reason to work in the root model context.
12
+ class ModelEntities < SketchUp::Cop
13
+
14
+ MSG = 'Prefer `model.active_entities` over `model.entities`.'.freeze
15
+
16
+ # Reference: http://www.rubydoc.info/gems/rubocop/RuboCop/NodePattern
17
+ def_node_matcher :active_model_entities?, <<-PATTERN
18
+ (send
19
+ (send (const nil? :Sketchup) :active_model) :entities)
20
+ PATTERN
21
+
22
+ def_node_matcher :entities_receiver, <<-PATTERN
23
+ (send
24
+ ({lvar ivar cvar} $_) :entities)
25
+ PATTERN
26
+
27
+ MODEL_VARIABLE_NAMES = %w[model mod].freeze
28
+
29
+ def model_entities?(node)
30
+ return true if active_model_entities?(node)
31
+
32
+ name = entities_receiver(node)
33
+ name && model_variable?(name)
34
+ end
35
+
36
+ def on_send(node)
37
+ add_offense(node, location: :expression) if model_entities?(node)
38
+ end
39
+
40
+ private
41
+
42
+ def model_variable?(name)
43
+ basename = variable_basename(name)
44
+ MODEL_VARIABLE_NAMES.include?(basename)
45
+ end
46
+
47
+ def variable_basename(name)
48
+ # Extract the basename from variables:
49
+ # model => model
50
+ # @model => model
51
+ # @@model => model
52
+ name.to_s.tr('@', '')
53
+ end
54
+
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,45 +1,45 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module SketchupSuggestions
6
- # Some of the shipped extensions in SketchUp monkey-patch the API
7
- # namespace. This is an unfortunate no-no that was done a long time ago
8
- # before the extension best-practices were established. These functions
9
- # might change or be removed at any time. They will also not work when
10
- # the extensions are disabled. Avoid using these methods.
11
- class MonkeyPatchedApi < SketchUp::Cop
12
-
13
- include SketchUp::DynamicComponentMethods
14
-
15
- def on_send(node)
16
- # Only check instance methods.
17
- return if node.receiver && node.receiver.const_type?
18
-
19
- name = node.method_name
20
-
21
- dc_method = DC_METHODS.find { |m| m[:name] == name }
22
- return unless dc_method
23
-
24
- if dc_method.key?(:variables)
25
- return unless node.receiver && node.receiver.variable?
26
-
27
- receiver_name = node.receiver.children.first
28
- # Account for instance and class variables.
29
- receiver_name = receiver_name.to_s.tr('@', '').to_sym
30
- return unless dc_method[:variables].include?(receiver_name)
31
- end
32
-
33
- path = dc_method[:path]
34
- message = "#{path}##{name} is not part of the official API. "\
35
- "It's a monkey-patched addition by Dynamic Components."
36
- add_offense(node,
37
- location: :selector,
38
- severity: :warning,
39
- message: message)
40
- end
41
-
42
- end
43
- end
44
- end
45
- end
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupSuggestions
6
+ # Some of the shipped extensions in SketchUp monkey-patch the API
7
+ # namespace. This is an unfortunate no-no that was done a long time ago
8
+ # before the extension best-practices were established. These functions
9
+ # might change or be removed at any time. They will also not work when
10
+ # the extensions are disabled. Avoid using these methods.
11
+ class MonkeyPatchedApi < SketchUp::Cop
12
+
13
+ include SketchUp::DynamicComponentMethods
14
+
15
+ def on_send(node)
16
+ # Only check instance methods.
17
+ return if node.receiver && node.receiver.const_type?
18
+
19
+ name = node.method_name
20
+
21
+ dc_method = DC_METHODS.find { |m| m[:name] == name }
22
+ return unless dc_method
23
+
24
+ if dc_method.key?(:variables)
25
+ return unless node.receiver && node.receiver.variable?
26
+
27
+ receiver_name = node.receiver.children.first
28
+ # Account for instance and class variables.
29
+ receiver_name = receiver_name.to_s.tr('@', '').to_sym
30
+ return unless dc_method[:variables].include?(receiver_name)
31
+ end
32
+
33
+ path = dc_method[:path]
34
+ message = "#{path}##{name} is not part of the official API. "\
35
+ "It's a monkey-patched addition by Dynamic Components."
36
+ add_offense(node,
37
+ location: :selector,
38
+ severity: :warning,
39
+ message: message)
40
+ end
41
+
42
+ end
43
+ end
44
+ end
45
+ end
@@ -1,103 +1,103 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module SketchupSuggestions
6
- # Operation name should be a short capitalized description. It will be
7
- # visible to the user in the Edit > Undo menu. Make sure to give it a
8
- # short human readable name, similar to SketchUp's own operation names.
9
- #
10
- # This cop make some very naive assumptions and will have more false
11
- # positives than most of the other cops. It's purpose is mainly to enable
12
- # awareness.
13
- class OperationName < SketchUp::Cop
14
-
15
- include RangeHelp
16
-
17
- MSG = 'Operation name should be a short capitalized description.'.freeze
18
- MSG_MAX = 'Operation names should not be short and concise. [%d/%d]'.freeze
19
-
20
- def on_send(node)
21
- _, method_name, *args = *node
22
- return unless method_name == :start_operation
23
- return if args.empty? || !args.first.str_type?
24
-
25
- # Ignore transparent operations.
26
- return if args.size == 4 && args.last.true_type?
27
-
28
- operation_name = args.first.str_content
29
- # We can only inspect string literals.
30
- return unless operation_name.is_a?(String)
31
-
32
- # Check the format of the operation name.
33
- unless acceptable_operation_name?(operation_name)
34
- msg = %(#{MSG} Expected: `"#{titleize(operation_name)}"`)
35
- add_offense(args.first, location: :expression, message: msg)
36
- end
37
- # Check the length of the operation name.
38
- unless operation_name.size <= max_operation_name_length
39
- message = format(MSG_MAX, operation_name.size, max_operation_name_length)
40
- add_offense(args.first,
41
- location: excess_range(args.first, operation_name),
42
- message: message)
43
- end
44
- # Ensure operation name is not empty.
45
- if operation_name.empty?
46
- msg = 'Operation names should not be empty.'
47
- add_offense(args.first, location: :expression, message: msg)
48
- end
49
- end
50
-
51
- private
52
-
53
- def excess_range(node, operation_name)
54
- string_start = node.source.index(operation_name)
55
- range = node.loc.expression
56
- if string_start
57
- excess_start = range.begin_pos + string_start + max_operation_name_length
58
- excess_end = range.begin_pos + string_start + operation_name.size
59
- range_between(excess_start, excess_end)
60
- else
61
- range_between(range.begin_pos, range.end_pos)
62
- end
63
- end
64
-
65
- def acceptable_operation_name?(name)
66
- # Capitalization, no programmer name, no punctuation.
67
- return false if name.end_with?('.')
68
- return false if titleize(name) != name
69
-
70
- true
71
- end
72
-
73
- def max_operation_name_length
74
- length = cop_config['Max'] || 25
75
- return length if length.is_a?(Integer) && length > 0
76
-
77
- raise 'Max needs to be a positive integer!'
78
- end
79
-
80
- TITLEIZE_EXCLUDE = %w[
81
- by for from in of to
82
- and or if
83
- ].freeze
84
-
85
- def titleize(string)
86
- string = string.gsub(/[_.]/, ' ')
87
- words = string.split.map { |word|
88
- unless TITLEIZE_EXCLUDE.include?(word)
89
- # word.capitalize won't work here, as we want to allow words like:
90
- # "HTML", "SketchUp". So instead only the first character in each
91
- # word is modified.
92
- char = word[0].upcase
93
- word[0, 1] = char
94
- end
95
- word
96
- }
97
- words.join(' ')
98
- end
99
-
100
- end
101
- end
102
- end
103
- end
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupSuggestions
6
+ # Operation name should be a short capitalized description. It will be
7
+ # visible to the user in the Edit > Undo menu. Make sure to give it a
8
+ # short human readable name, similar to SketchUp's own operation names.
9
+ #
10
+ # This cop make some very naive assumptions and will have more false
11
+ # positives than most of the other cops. It's purpose is mainly to enable
12
+ # awareness.
13
+ class OperationName < SketchUp::Cop
14
+
15
+ include RangeHelp
16
+
17
+ MSG = 'Operation name should be a short capitalized description.'.freeze
18
+ MSG_MAX = 'Operation names should not be short and concise. [%d/%d]'.freeze
19
+
20
+ def on_send(node)
21
+ _, method_name, *args = *node
22
+ return unless method_name == :start_operation
23
+ return if args.empty? || !args.first.str_type?
24
+
25
+ # Ignore transparent operations.
26
+ return if args.size == 4 && args.last.true_type?
27
+
28
+ operation_name = args.first.str_content
29
+ # We can only inspect string literals.
30
+ return unless operation_name.is_a?(String)
31
+
32
+ # Check the format of the operation name.
33
+ unless acceptable_operation_name?(operation_name)
34
+ msg = %(#{MSG} Expected: `"#{titleize(operation_name)}"`)
35
+ add_offense(args.first, location: :expression, message: msg)
36
+ end
37
+ # Check the length of the operation name.
38
+ unless operation_name.size <= max_operation_name_length
39
+ message = format(MSG_MAX, operation_name.size, max_operation_name_length)
40
+ add_offense(args.first,
41
+ location: excess_range(args.first, operation_name),
42
+ message: message)
43
+ end
44
+ # Ensure operation name is not empty.
45
+ if operation_name.empty?
46
+ msg = 'Operation names should not be empty.'
47
+ add_offense(args.first, location: :expression, message: msg)
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def excess_range(node, operation_name)
54
+ string_start = node.source.index(operation_name)
55
+ range = node.loc.expression
56
+ if string_start
57
+ excess_start = range.begin_pos + string_start + max_operation_name_length
58
+ excess_end = range.begin_pos + string_start + operation_name.size
59
+ range_between(excess_start, excess_end)
60
+ else
61
+ range_between(range.begin_pos, range.end_pos)
62
+ end
63
+ end
64
+
65
+ def acceptable_operation_name?(name)
66
+ # Capitalization, no programmer name, no punctuation.
67
+ return false if name.end_with?('.')
68
+ return false if titleize(name) != name
69
+
70
+ true
71
+ end
72
+
73
+ def max_operation_name_length
74
+ length = cop_config['Max'] || 25
75
+ return length if length.is_a?(Integer) && length > 0
76
+
77
+ raise 'Max needs to be a positive integer!'
78
+ end
79
+
80
+ TITLEIZE_EXCLUDE = %w[
81
+ by for from in of to
82
+ and or if
83
+ ].freeze
84
+
85
+ def titleize(string)
86
+ string = string.gsub(/[_.]/, ' ')
87
+ words = string.split.map { |word|
88
+ unless TITLEIZE_EXCLUDE.include?(word)
89
+ # word.capitalize won't work here, as we want to allow words like:
90
+ # "HTML", "SketchUp". So instead only the first character in each
91
+ # word is modified.
92
+ char = word[0].upcase
93
+ word[0, 1] = char
94
+ end
95
+ word
96
+ }
97
+ words.join(' ')
98
+ end
99
+
100
+ end
101
+ end
102
+ end
103
+ end