rubocop-sketchup 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -1
  3. data/assets/logo.png +0 -0
  4. data/assets/output.html.erb +301 -0
  5. data/config/default.yml +141 -36
  6. data/lib/rubocop/sketchup/config.rb +28 -8
  7. data/lib/rubocop/sketchup/cop/deprecations/add_separator_to_menu.rb +4 -1
  8. data/lib/rubocop/sketchup/cop/deprecations/operation_next_transparent.rb +11 -2
  9. data/lib/rubocop/sketchup/cop/deprecations/require_all.rb +7 -2
  10. data/lib/rubocop/sketchup/cop/deprecations/set_texture_projection.rb +6 -2
  11. data/lib/rubocop/sketchup/cop/deprecations/show_ruby_panel.rb +4 -1
  12. data/lib/rubocop/sketchup/cop/deprecations/sketchup_set.rb +8 -0
  13. data/lib/rubocop/sketchup/cop/performance/openssl.rb +5 -3
  14. data/lib/rubocop/sketchup/cop/performance/operation_disable_ui.rb +10 -2
  15. data/lib/rubocop/sketchup/cop/performance/selection_bulk.rb +11 -4
  16. data/lib/rubocop/sketchup/cop/performance/type_check.rb +63 -0
  17. data/lib/rubocop/sketchup/cop/performance/typename.rb +6 -1
  18. data/lib/rubocop/sketchup/cop/requirements/api_namespace.rb +8 -2
  19. data/lib/rubocop/sketchup/cop/requirements/exit.rb +7 -3
  20. data/lib/rubocop/sketchup/cop/requirements/extension_namespace.rb +32 -2
  21. data/lib/rubocop/sketchup/cop/requirements/file_structure.rb +22 -10
  22. data/lib/rubocop/sketchup/cop/requirements/gem_install.rb +45 -0
  23. data/lib/rubocop/sketchup/cop/requirements/get_extension_license.rb +95 -0
  24. data/lib/rubocop/sketchup/cop/requirements/global_constants.rb +10 -1
  25. data/lib/rubocop/sketchup/cop/requirements/global_include.rb +9 -2
  26. data/lib/rubocop/sketchup/cop/requirements/global_methods.rb +10 -1
  27. data/lib/rubocop/sketchup/cop/requirements/global_variables.rb +13 -4
  28. data/lib/rubocop/sketchup/cop/requirements/language_handler_globals.rb +6 -4
  29. data/lib/rubocop/sketchup/cop/requirements/load_path.rb +9 -6
  30. data/lib/rubocop/sketchup/cop/requirements/minimal_registration.rb +26 -2
  31. data/lib/rubocop/sketchup/cop/requirements/observers_start_operation.rb +28 -2
  32. data/lib/rubocop/sketchup/cop/requirements/register_extension.rb +12 -2
  33. data/lib/rubocop/sketchup/cop/requirements/ruby_core_namespace.rb +14 -8
  34. data/lib/rubocop/sketchup/cop/requirements/ruby_stdlib_namespace.rb +594 -588
  35. data/lib/rubocop/sketchup/cop/requirements/shipped_extensions_namespace.rb +6 -6
  36. data/lib/rubocop/sketchup/cop/requirements/sketchup_extension.rb +28 -9
  37. data/lib/rubocop/sketchup/cop/requirements/sketchup_require.rb +163 -0
  38. data/lib/rubocop/sketchup/cop/suggestions/add_group.rb +49 -0
  39. data/lib/rubocop/sketchup/cop/suggestions/compatibility.rb +35 -6
  40. data/lib/rubocop/sketchup/cop/suggestions/dc_internals.rb +6 -3
  41. data/lib/rubocop/sketchup/cop/suggestions/file_encoding.rb +3 -0
  42. data/lib/rubocop/sketchup/cop/suggestions/model_entities.rb +9 -2
  43. data/lib/rubocop/sketchup/cop/suggestions/monkey_patched_api.rb +5 -1
  44. data/lib/rubocop/sketchup/cop/suggestions/operation_name.rb +20 -9
  45. data/lib/rubocop/sketchup/cop/suggestions/sketchup_find_support_file.rb +13 -2
  46. data/lib/rubocop/sketchup/cop/suggestions/tool_drawing_bounds.rb +44 -0
  47. data/lib/rubocop/sketchup/cop/suggestions/tool_invalidate.rb +66 -0
  48. data/lib/rubocop/sketchup/cop/suggestions/tool_user_input.rb +41 -0
  49. data/lib/rubocop/sketchup/cop/suggestions/toolbar_timer.rb +65 -0
  50. data/lib/rubocop/sketchup/cop.rb +38 -18
  51. data/lib/rubocop/sketchup/dc_globals.rb +1 -1
  52. data/lib/rubocop/sketchup/dc_methods.rb +27 -27
  53. data/lib/rubocop/sketchup/extension_project.rb +19 -2
  54. data/lib/rubocop/sketchup/formatter/extension_review.rb +35 -15
  55. data/lib/rubocop/sketchup/inject.rb +1 -1
  56. data/lib/rubocop/sketchup/namespace.rb +1 -0
  57. data/lib/rubocop/sketchup/namespace_checker.rb +4 -1
  58. data/lib/rubocop/sketchup/no_comment_disable.rb +1 -1
  59. data/lib/rubocop/sketchup/range_help.rb +52 -0
  60. data/lib/rubocop/sketchup/sketchup_version.rb +4 -2
  61. data/lib/rubocop/sketchup/tool_checker.rb +43 -0
  62. data/lib/rubocop/sketchup/version.rb +1 -1
  63. data/lib/rubocop/sketchup.rb +1 -1
  64. data/lib/rubocop-sketchup.rb +9 -0
  65. data/rubocop-sketchup.gemspec +8 -11
  66. metadata +18 -6
  67. data/lib/rubocop/sketchup/cop/suggestions/sketchup_require.rb +0 -67
@@ -14,7 +14,7 @@ module RuboCop
14
14
  # We check only against the top level namespaces. The core define more
15
15
  # objects, but they are under one of the top level namespaces listed.
16
16
 
17
- NAMESPACES_ADVANCED_CAMERA_TOOLS = %w(
17
+ NAMESPACES_ADVANCED_CAMERA_TOOLS = %w[
18
18
  ACTUtil
19
19
  CameraAppObserver
20
20
  CameraEntityObserver
@@ -28,9 +28,9 @@ module RuboCop
28
28
  FSGeomUtils
29
29
  FSValidate
30
30
  PageNameChangeObserver
31
- ).freeze
31
+ ].freeze
32
32
 
33
- NAMESPACES_DYNAMIC_COMPONENTS = %w(
33
+ NAMESPACES_DYNAMIC_COMPONENTS = %w[
34
34
  DCConverter
35
35
  DCDownloader
36
36
  DCFunctionsV1
@@ -39,11 +39,11 @@ module RuboCop
39
39
  DCProgressBar
40
40
  DynamicComponents
41
41
  DynamicComponentsV1
42
- ).freeze
42
+ ].freeze
43
43
 
44
- NAMESPACES_TRIMBLE_CONNECT = %w(
44
+ NAMESPACES_TRIMBLE_CONNECT = %w[
45
45
  Trimble
46
- ).freeze
46
+ ].freeze
47
47
 
48
48
  NAMESPACES = (
49
49
  NAMESPACES_ADVANCED_CAMERA_TOOLS |
@@ -5,22 +5,33 @@ module RuboCop
5
5
  module SketchupRequirements
6
6
  # Register a single instance of SketchupExtension per extension.
7
7
  # This should be done by the root .rb file in the extension package.
8
+ #
9
+ # @example Good - a single SketchupExtension is registered.
10
+ # module Example
11
+ # unless file_loaded?(__FILE__)
12
+ # extension = SketchupExtension.new('Hello World', 'example/main')
13
+ # Sketchup.register_extension(extension, true)
14
+ # file_loaded(__FILE__)
15
+ # end
16
+ # end
8
17
  class SketchupExtension < SketchUp::Cop
9
18
 
10
19
  include SketchUp::NoCommentDisable
11
20
  include SketchUp::ExtensionProject
12
21
  include RangeHelp
13
22
 
23
+ # rubocop:disable Metrics/LineLength
14
24
  MSG = 'Create and register one SketchupExtension instance per extension.'.freeze
15
25
  MSG_CREATE_ONE = 'Create only SketchupExtension instance per extension.'.freeze
16
26
  MSG_CREATE_MISSING = 'SketchupExtension.new not found.'.freeze
17
27
  MSG_REGISTER_ONE = 'Only register one SketchupExtension instance per extension.'.freeze
18
28
  MSG_REGISTER_MISSING = 'Registration of SketchupExtension not found. Expected %s'.freeze
29
+ # rubocop:enable Metrics/LineLength
19
30
 
20
31
  # Reference: http://rubocop.readthedocs.io/en/latest/node_pattern/
21
32
  def_node_search :sketchup_extension_new, <<-PATTERN
22
33
  (send
23
- (const nil? :SketchupExtension) :new _ _)
34
+ (const nil? :SketchupExtension) :new ...)
24
35
  PATTERN
25
36
 
26
37
  def_node_search :sketchup_register_extension, <<-PATTERN
@@ -49,8 +60,7 @@ module RuboCop
49
60
  if extension_nodes.size > 1
50
61
  add_offense(nil,
51
62
  location: range,
52
- message: MSG_CREATE_ONE,
53
- severity: :error)
63
+ message: MSG_CREATE_ONE)
54
64
  return
55
65
  end
56
66
 
@@ -59,8 +69,19 @@ module RuboCop
59
69
  if extension_node.nil?
60
70
  add_offense(nil,
61
71
  location: range,
62
- message: MSG_CREATE_MISSING,
63
- severity: :error)
72
+ message: MSG_CREATE_MISSING)
73
+ return
74
+ end
75
+
76
+ # Ensure it have two arguments.
77
+ if extension_node.arguments.size < 2
78
+ message = if extension_node.arguments.size == 1
79
+ 'Missing second argument for the path'
80
+ else
81
+ 'Missing required name arguments'
82
+ end
83
+ add_offense(extension_node,
84
+ message: message)
64
85
  return
65
86
  end
66
87
 
@@ -79,8 +100,7 @@ module RuboCop
79
100
  # Make sure there is only one call to `register_extension`.
80
101
  if registered_vars.size > 1
81
102
  add_offense(registered_vars[1],
82
- message: MSG_REGISTER_ONE,
83
- severity: :error)
103
+ message: MSG_REGISTER_ONE)
84
104
  return
85
105
  end
86
106
 
@@ -89,8 +109,7 @@ module RuboCop
89
109
  msg = MSG_REGISTER_MISSING % extension_var.to_s
90
110
  add_offense(nil,
91
111
  location: range,
92
- message: msg,
93
- severity: :error)
112
+ message: msg)
94
113
  end
95
114
  end
96
115
 
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupRequirements
6
+ # Omit file extensions when using `Sketchup.require` to allow encrypted
7
+ # files to be loaded.
8
+ #
9
+ # Ruby C extensions, `.so`/`.bundle` libraries must always be loaded via
10
+ # the normal `require`. Omit the file extension to the libraries as Ruby
11
+ # will resolve the`.so`/`.bundle` automatically.
12
+ #
13
+ # @example Bad - This will fail if extension is encrypted
14
+ # Sketchup.require 'hello/world.rb'
15
+ #
16
+ # @example Good - This will work for `.rbe`, `.rbs` and `rb` files.
17
+ # Sketchup.require 'hello/world'
18
+ #
19
+ # @example Bad - This will fail if extension is encrypted
20
+ # extension = SketchupExtension.new("Example", "Example/main.rb")
21
+ #
22
+ # @example Good - This will work for `.rbe`, `.rbs` and `rb` files.
23
+ # extension = SketchupExtension.new("Example", "Example/main")
24
+ class SketchupRequire < SketchUp::Cop
25
+
26
+ include SketchUp::ExtensionProject
27
+ include SketchUp::NoCommentDisable
28
+ include RangeHelp
29
+
30
+ MSG_SKETCHUP_REQUIRE_EXT_NAME = 'Do not hard code file extensions '\
31
+ 'with `Sketchup.require`.'.freeze
32
+
33
+ MSG_EXTENSION_NEW_EXT_NAME = 'Do not hard code file extensions '\
34
+ 'with `SketchupExtension.new`.'.freeze
35
+
36
+ MSG_REQUIRE_FOR_BINARY = 'Use `require` instead of `Sketchup.require` '\
37
+ 'to load binary Ruby libraries.'.freeze
38
+
39
+ MSG_OMIT_BINARY_EXT = 'Do not hard code .so/.bundle file '\
40
+ 'extensions'.freeze
41
+
42
+ MSG_REQUIRE_ENCRYPTED = 'Use `Sketchup.require` when loading Ruby '\
43
+ 'files for encrypted extensions.'.freeze
44
+
45
+ def_node_matcher :ruby_require, <<-PATTERN
46
+ (send nil? :require (str $_))
47
+ PATTERN
48
+
49
+ def_node_matcher :ruby_require?, <<-PATTERN
50
+ (send nil? :require (str _))
51
+ PATTERN
52
+
53
+
54
+ def_node_matcher :sketchup_require, <<-PATTERN
55
+ (send (const nil? :Sketchup) :require (str $_))
56
+ PATTERN
57
+
58
+ def_node_matcher :sketchup_require?, <<-PATTERN
59
+ (send (const nil? :Sketchup) :require (str _))
60
+ PATTERN
61
+
62
+
63
+ def_node_matcher :sketchup_extension_new, <<-PATTERN
64
+ (send (const nil? :SketchupExtension) :new _ (str $_))
65
+ PATTERN
66
+
67
+ def_node_matcher :sketchup_extension_new?, <<-PATTERN
68
+ (send (const nil? :SketchupExtension) :new _ (str _))
69
+ PATTERN
70
+
71
+
72
+ TOOLS_RUBY_FILES = %w[extensions.rb langhandler.rb sketchup.rb].freeze
73
+
74
+
75
+ def on_send(node)
76
+ if sketchup_require?(node)
77
+ filename = sketchup_require(node)
78
+ return if check_binary_sketchup_require(node, filename)
79
+ return if check_sketchup_require_filename(node, filename)
80
+
81
+ elsif ruby_require?(node)
82
+ filename = ruby_require(node)
83
+ return if check_binary_ruby_require(node, filename)
84
+ return if check_encrypted_require(node, filename)
85
+
86
+ elsif sketchup_extension_new?(node)
87
+ filename = sketchup_extension_new(node)
88
+ return if check_sketchup_extension_new_filename(node, filename)
89
+
90
+ end
91
+ end
92
+
93
+ private
94
+
95
+ def binary_require?(filename)
96
+ return unless extension_binaries?
97
+ return if extension_binaries.empty?
98
+
99
+ extension_binaries.include?(filename)
100
+ end
101
+
102
+ def check_binary_sketchup_require(node, filename)
103
+ return unless binary_require?(filename)
104
+
105
+ end_pos = node.loc.dot.end_pos
106
+ range = node.receiver.loc.expression.with(end_pos: end_pos)
107
+ add_offense(node, location: range, message: MSG_REQUIRE_FOR_BINARY)
108
+ true
109
+ end
110
+
111
+ def check_binary_ruby_require(node, filename)
112
+ ext_name = File.extname(filename)
113
+ return unless %w[.so .bundle].include?(ext_name)
114
+
115
+ add_offense(node, location: file_ext_range(node.arguments.first),
116
+ message: MSG_OMIT_BINARY_EXT)
117
+ true
118
+ end
119
+
120
+ def check_sketchup_require_filename(node, filename)
121
+ return if valid_filename?(filename)
122
+
123
+ add_offense(node, location: file_ext_range(node.arguments.first),
124
+ message: MSG_SKETCHUP_REQUIRE_EXT_NAME)
125
+ true
126
+ end
127
+
128
+ def check_sketchup_extension_new_filename(node, filename)
129
+ return if valid_filename?(filename)
130
+
131
+ add_offense(node, location: file_ext_range(node.arguments.last),
132
+ message: MSG_EXTENSION_NEW_EXT_NAME)
133
+ true
134
+ end
135
+
136
+ def check_encrypted_require(node, filename)
137
+ return unless encrypted_extension?
138
+ return unless extension_file?(filename)
139
+
140
+ add_offense(node, location: node.loc.selector,
141
+ message: MSG_REQUIRE_ENCRYPTED)
142
+ true
143
+ end
144
+
145
+ def first_directory(path)
146
+ path.to_s.split(File::SEPARATOR).first.downcase
147
+ end
148
+
149
+ def extension_file?(filename)
150
+ pathname = Pathname.new(filename).cleanpath
151
+ return false unless pathname.relative?
152
+
153
+ first_directory(extension_directory) == first_directory(pathname)
154
+ end
155
+
156
+ def valid_filename?(filename)
157
+ File.extname(filename).empty? || TOOLS_RUBY_FILES.include?(filename)
158
+ end
159
+
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupSuggestions
6
+ # The idiomatic way to create groups via the Ruby API differ from the
7
+ # way you'd do it from the UI.
8
+ #
9
+ # Using the API you should prefer to create the group first, then add
10
+ # your geometry into the group. This is more performant and predictable.
11
+ #
12
+ # Grouping existing geometry via the API have historically been affected
13
+ # by bugs and issues.
14
+ #
15
+ # If you do have to group existing geometry via the API, make sure you
16
+ # group geometry from the active context; `model.active_entities`.
17
+ # Otherwise you might run into unexpected issues, even crashes.
18
+ #
19
+ # @example Adding new geometry
20
+ # # bad
21
+ # face1 = model.active_entities.add_face(points1)
22
+ # face2 = model.active_entities.add_face(points2)
23
+ # group = model.active_entities.add_group([face1, face2])
24
+ #
25
+ # # good
26
+ # group = model.active_entities.add_group
27
+ # face1 = group.entities.add_face(points1)
28
+ # face2 = group.entities.add_face(points2)
29
+ class AddGroup < Cop
30
+
31
+ include RangeHelp
32
+
33
+ MSG = 'Avoid creating groups out of existing entities.'.freeze
34
+
35
+ def_node_matcher :add_group?, <<-PATTERN
36
+ (send _ :add_group ...)
37
+ PATTERN
38
+
39
+ def on_send(node)
40
+ return unless add_group?(node)
41
+ return if node.arguments.empty?
42
+
43
+ add_offense(node, location: arguments_range(node))
44
+ end
45
+
46
+ end
47
+ end
48
+ end
49
+ end
@@ -3,22 +3,31 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module SketchupSuggestions
6
+ # It's easy to lose track of what API feature was added in what version or
7
+ # SketchUp. You can configure your target SketchUp version and be notified
8
+ # if you use features introduced in newer versions.
9
+ #
10
+ # @example Add this to your .rubocop.yml
11
+ # AllCops:
12
+ # SketchUp:
13
+ # TargetSketchUpVersion: 2016 M1
6
14
  class Compatibility < SketchUp::Cop
7
15
 
8
16
  include SketchUp::Features
9
17
 
10
- MSG = "Incompatible feature with target SketchUp version".freeze
18
+ MSG = 'Incompatible feature with target SketchUp version'.freeze
11
19
 
12
20
  def on_def(node)
13
21
  return unless observer_method?(node)
22
+
14
23
  feature_name = "##{node.method_name}"
15
24
  check_feature(node, :method, feature_name)
16
25
  end
17
26
 
18
27
  def on_send(node)
28
+ feature_name = ''
19
29
  if module_method?(node)
20
30
  feature_name = "#{node.receiver.const_name}.#{node.method_name}"
21
- check_feature(node, :method, feature_name)
22
31
  else
23
32
  # Instance methods are harder. It's difficult to infer the type of
24
33
  # the receiver. If we only check the method name in isolation we
@@ -26,9 +35,10 @@ module RuboCop
26
35
  # methods in Ruby itself and other older features.
27
36
  # We try to match names that are unlikely to cause much noise.
28
37
  return unless checkable_instance_method?(node)
38
+
29
39
  feature_name = "##{node.method_name}"
30
- check_feature(node, :method, feature_name)
31
40
  end
41
+ check_feature(node, :method, feature_name)
32
42
  end
33
43
 
34
44
  def on_const(node)
@@ -42,15 +52,20 @@ module RuboCop
42
52
 
43
53
  def check_feature(node, type, feature_name)
44
54
  return unless sketchup_target_version?
55
+
45
56
  full_feature_name = feature_name
46
57
  FEATURES.each { |feature_set|
47
- feature_version = SketchUp::SketchUpVersion.new(feature_set[:version])
58
+ version = feature_set[:version]
59
+ feature_version = SketchUp::SketchUpVersion.new(version)
48
60
  next unless feature_version > sketchup_target_version
61
+
49
62
  objects = feature_set[:types][type] || []
50
63
  if type == :method && instance_method?(feature_name)
51
64
  # Instance methods are simply matching the method name since it's
52
65
  # very difficult to determine the type of the receiver.
53
- full_feature_name = objects.find { |object| object.end_with?(feature_name) }
66
+ full_feature_name = objects.find { |object|
67
+ object.end_with?(feature_name)
68
+ }
54
69
  next unless full_feature_name
55
70
  else
56
71
  next unless objects.include?(feature_name)
@@ -63,7 +78,21 @@ module RuboCop
63
78
  message = "The #{feature_type} `#{feature_name}` was added in "\
64
79
  "#{feature_version} which is incompatible with target "\
65
80
  "#{sketchup_target_version}."
66
- add_offense(node, message: message)
81
+ location = find_node_location(node)
82
+ add_offense(node, location: location, message: message)
83
+ end
84
+
85
+ def find_node_location(node)
86
+ # Highlight the most pertinent piece of the expression.
87
+ if node.const_type?
88
+ :expression
89
+ elsif node.send_type?
90
+ :selector
91
+ elsif node.def_type?
92
+ :name
93
+ else
94
+ :expression
95
+ end
67
96
  end
68
97
 
69
98
  def module_method?(node)
@@ -4,12 +4,14 @@ module RuboCop
4
4
  module Cop
5
5
  module SketchupSuggestions
6
6
  # Tapping into the internals of Dynamic Components is risky. It could
7
- # change at any time.
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!
8
10
  class DynamicComponentInternals < SketchUp::Cop
9
11
 
10
12
  include SketchUp::DynamicComponentGlobals
11
13
 
12
- MSG = "Avoid relying on internal logic of Dynamic Components.".freeze
14
+ MSG = 'Avoid relying on internal logic of Dynamic Components.'.freeze
13
15
 
14
16
  def on_gvar(node)
15
17
  check_global(node)
@@ -22,7 +24,8 @@ module RuboCop
22
24
  def check_global(node)
23
25
  global_var, = *node
24
26
  return unless dc_global_var?(global_var)
25
- add_offense(node, location: :name, severity: :error)
27
+
28
+ add_offense(node, location: :name, severity: :warning)
26
29
  end
27
30
 
28
31
  end
@@ -40,6 +40,7 @@ module RuboCop
40
40
  def on_send(node)
41
41
  return if file_loaded?(node)
42
42
  return if node.arguments.none?(&method(:magic_file_or_dir?))
43
+
43
44
  add_offense(node, location: :expression)
44
45
  end
45
46
 
@@ -54,8 +55,10 @@ module RuboCop
54
55
  # After assigning __FILE__ or __dir_ to a variable, check the parent
55
56
  # scope to whether .force_encoding is called on the variable.
56
57
  return if node.parent.nil?
58
+
57
59
  encoded = force_encoding(node.parent).to_a
58
60
  return if encoded.include?(lhs)
61
+
59
62
  add_offense(node)
60
63
  end
61
64
 
@@ -3,9 +3,15 @@
3
3
  module RuboCop
4
4
  module Cop
5
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.
6
12
  class ModelEntities < SketchUp::Cop
7
13
 
8
- MSG = 'Typically one should use model.active_entities instead of model.entities.'.freeze
14
+ MSG = 'Prefer `model.active_entities` over `model.entities`.'.freeze
9
15
 
10
16
  # Reference: http://www.rubydoc.info/gems/rubocop/RuboCop/NodePattern
11
17
  def_node_matcher :active_model_entities?, <<-PATTERN
@@ -18,10 +24,11 @@ module RuboCop
18
24
  ({lvar ivar cvar} $_) :entities)
19
25
  PATTERN
20
26
 
21
- MODEL_VARIABLE_NAMES = %w[model mod]
27
+ MODEL_VARIABLE_NAMES = %w[model mod].freeze
22
28
 
23
29
  def model_entities?(node)
24
30
  return true if active_model_entities?(node)
31
+
25
32
  name = entities_receiver(node)
26
33
  name && model_variable?(name)
27
34
  end
@@ -23,6 +23,7 @@ module RuboCop
23
23
 
24
24
  if dc_method.key?(:variables)
25
25
  return unless node.receiver && node.receiver.variable?
26
+
26
27
  receiver_name = node.receiver.children.first
27
28
  # Account for instance and class variables.
28
29
  receiver_name = receiver_name.to_s.tr('@', '').to_sym
@@ -32,7 +33,10 @@ module RuboCop
32
33
  path = dc_method[:path]
33
34
  message = "#{path}##{name} is not part of the official API. "\
34
35
  "It's a monkey-patched addition by Dynamic Components."
35
- add_offense(node, severity: :error, message: message)
36
+ add_offense(node,
37
+ location: :selector,
38
+ severity: :warning,
39
+ message: message)
36
40
  end
37
41
 
38
42
  end
@@ -3,31 +3,43 @@
3
3
  module RuboCop
4
4
  module Cop
5
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.
6
13
  class OperationName < SketchUp::Cop
7
14
 
8
15
  include RangeHelp
9
16
 
10
17
  MSG = 'Operation name should be a short capitalized description.'.freeze
11
- MSG_MAX = "Operation names should not be short and concise. [%d/%d]".freeze
18
+ MSG_MAX = 'Operation names should not be short and concise. [%d/%d]'.freeze
12
19
 
13
20
  def on_send(node)
14
21
  _, method_name, *args = *node
15
22
  return unless method_name == :start_operation
16
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
+
17
28
  operation_name = args.first.str_content
18
29
  # We can only inspect string literals.
19
30
  return unless operation_name.is_a?(String)
31
+
20
32
  # Check the format of the operation name.
21
33
  unless acceptable_operation_name?(operation_name)
22
- msg = %[#{MSG} Expected: `"#{titleize(operation_name)}"`]
34
+ msg = %(#{MSG} Expected: `"#{titleize(operation_name)}"`)
23
35
  add_offense(args.first, location: :expression, message: msg)
24
36
  end
25
37
  # Check the length of the operation name.
26
38
  unless operation_name.size <= max_operation_name_length
27
39
  message = format(MSG_MAX, operation_name.size, max_operation_name_length)
28
40
  add_offense(args.first,
29
- location: excess_range(args.first, operation_name),
30
- message: message)
41
+ location: excess_range(args.first, operation_name),
42
+ message: message)
31
43
  end
32
44
  # Ensure operation name is not empty.
33
45
  if operation_name.empty?
@@ -54,6 +66,7 @@ module RuboCop
54
66
  # Capitalization, no programmer name, no punctuation.
55
67
  return false if name.end_with?('.')
56
68
  return false if titleize(name) != name
69
+
57
70
  true
58
71
  end
59
72
 
@@ -67,21 +80,19 @@ module RuboCop
67
80
  TITLEIZE_EXCLUDE = %w[
68
81
  by for from in of to
69
82
  and or if
70
- ]
83
+ ].freeze
71
84
 
72
85
  def titleize(string)
73
86
  string = string.gsub(/[_.]/, ' ')
74
87
  words = string.split.map { |word|
75
- if TITLEIZE_EXCLUDE.include?(word)
76
- word
77
- else
88
+ unless TITLEIZE_EXCLUDE.include?(word)
78
89
  # word.capitalize won't work here, as we want to allow words like:
79
90
  # "HTML", "SketchUp". So instead only the first character in each
80
91
  # word is modified.
81
92
  char = word[0].upcase
82
93
  word[0, 1] = char
83
- word
84
94
  end
95
+ word
85
96
  }
86
97
  words.join(' ')
87
98
  end
@@ -3,10 +3,20 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module SketchupSuggestions
6
- # Avoid Sketchup.find_support_file to find your extension's files.
6
+ # Avoid `Sketchup.find_support_file` to find your extension's files.
7
+ #
8
+ # Users might install your extension to locations other than the default
9
+ # Plugins directory. If you use `Sketchup.find_support_file` to build
10
+ # a path for files in your extension it will fail in these scenarios.
11
+ #
12
+ # Instead prefer to use `__FILE__` or `__dir__` to build paths relative
13
+ # to your source files. This have the added benefit of allowing you to
14
+ # load your extensions directly from external directories under version
15
+ # control.
7
16
  class SketchupFindSupportFile < SketchUp::Cop
8
17
 
9
- MSG = "Avoid Sketchup.find_support_file to find your extension's files.".freeze
18
+ MSG = 'Avoid `Sketchup.find_support_file` to find your '\
19
+ "extension's files.".freeze
10
20
 
11
21
  # http://www.rubydoc.info/gems/rubocop/RuboCop/NodePattern
12
22
  # https://rubocop.readthedocs.io/en/latest/node_pattern/
@@ -19,6 +29,7 @@ module RuboCop
19
29
 
20
30
  def on_send(node)
21
31
  return unless sketchup_find_support_file?(node)
32
+
22
33
  add_offense(node, location: :expression)
23
34
  end
24
35
 
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupSuggestions
6
+ # When drawing 3D geometry to the viewport from a tool, make sure to
7
+ # implement `getExtents` that return a `Geom::BoundingBox` object large
8
+ # enough to encompass what you draw.
9
+ #
10
+ # With out doing that the drawn content might end up being clipped.
11
+ #
12
+ # @example
13
+ # # good
14
+ # class ExampleTool
15
+ #
16
+ # def getExtents
17
+ # bounds = Geom::BoundingBox.new
18
+ # bounds.add(@points)
19
+ # bounds
20
+ # end
21
+ #
22
+ # def draw(view)
23
+ # view.draw(GL_LINES, @points)
24
+ # end
25
+ #
26
+ # end
27
+ class ToolDrawingBounds < Cop
28
+
29
+ include SketchUp::ToolChecker
30
+
31
+ MSG_MISSING_GET_EXTENTS = 'When drawing to the viewport implement '\
32
+ '`getExtents` so drawn geometry is not clipped.'.freeze
33
+
34
+ def on_tool_class(class_node, body_methods)
35
+ return unless find_method(body_methods, :draw)
36
+ return if find_method(body_methods, :getExtents)
37
+
38
+ add_offense(class_node, message: MSG_MISSING_GET_EXTENTS)
39
+ end
40
+
41
+ end
42
+ end
43
+ end
44
+ end