Class: RuboCop::Cop::SketchupRequirements::ExtensionNamespace

Inherits:
SketchUp::Cop
  • Object
show all
Includes:
SketchUp, SketchUp::NoCommentDisable
Defined in:
lib/rubocop/sketchup/cop/requirements/extension_namespace.rb

Overview

Extensions in SketchUp all share the same Ruby environment on the user’s machine. Because of this it’s important that each extension isolate itself to avoid clashing with other extensions.

Extensions submitted to Extension Warehouse is expected to use only one root module.

Examples:

Good - this contains everything in the extension.

module MyExtension
  class Foo
  end
  class Bar
  end
end

Better - this further reduce chance of clashing.

module MyCompany
  module MyExtension
    class Foo
    end
    class Bar
    end
  end
end

Constant Summary collapse

@@namespace =

Class variables are normally frowned upon since they leak through all instances. However, in this case this is exactly what we want. The Cop picks up the first top level namespace it encounters and then keep track of whether it detects more top level namespaces.

nil

Constants included from SketchUp

SketchUp::CONFIG, SketchUp::VERSION

Constants inherited from SketchUp::Cop

SketchUp::Cop::SKETCHUP_DEPARTMENT_SEVERITY

Constants included from SketchUp::Config

SketchUp::Config::DEFAULT_CONFIGURATION

Instance Method Summary collapse

Methods inherited from SketchUp::Cop

inherited, #relevant_file?

Instance Method Details

#check_class_or_module(node) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/rubocop/sketchup/cop/requirements/extension_namespace.rb', line 47

def check_class_or_module(node)
  name = node.defined_module_name

  if node.parent_module_name
    parent = Namespace.new(node.parent_module_name)
  else
    # This is somewhat of an educated guess. We might end up here with
    # code like this:
    #
    #   Example.generate do
    #     module HelloWorld
    #     end
    #   end
    #
    # It might be that the module is evaluated in a different context.
    # But we'll accept the possible false positive and let the user
    # config exceptions if needed.
    parent = Namespace.new('Object')
  end
  namespace = parent.join(name)

  # Don't want to process anything that aren't top level namespaces.
  return unless parent.top_level?
  # Don't check excluded namespaces.
  return if exempted?(namespace)

  check_namespace(node, namespace)
end

#check_namespace(node, namespace) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/rubocop/sketchup/cop/requirements/extension_namespace.rb', line 81

def check_namespace(node, namespace)
  # Make sure the namespace isn't part of reserved namespaces that other
  # cops are checking.
  return if reserved?(namespace)

  # Remember the first namespace encountered and log an offence if
  # more top level namespaces are registered.
  top = namespace.first
  @@namespace ||= top
  return if @@namespace == top

  add_offense(node, location: :name)
end

#exempted?(namespace) ⇒ Boolean

Returns:

  • (Boolean)


111
112
113
# File 'lib/rubocop/sketchup/cop/requirements/extension_namespace.rb', line 111

def exempted?(namespace)
  namespace_exceptions.include?(namespace.first)
end

#message(node) ⇒ Object



104
105
106
107
108
109
# File 'lib/rubocop/sketchup/cop/requirements/extension_namespace.rb', line 104

def message(node)
  namespace = Namespace.new(node.defined_module_name).from_root
  format('Use a single root namespace. '\
         '(Found `%<found>s`; Previously found `%<expected>s`)',
         found: namespace, expected: @@namespace)
end

#namespace_exceptionsObject



115
116
117
118
119
120
# File 'lib/rubocop/sketchup/cop/requirements/extension_namespace.rb', line 115

def namespace_exceptions
  exceptions = cop_config['Exceptions'] || []
  return exceptions if exceptions.is_a?(Array)

  raise 'exceptions needs to be an array of strings!'
end

#on_class(node) ⇒ Object



39
40
41
# File 'lib/rubocop/sketchup/cop/requirements/extension_namespace.rb', line 39

def on_class(node)
  check_class_or_module(node)
end

#on_module(node) ⇒ Object



43
44
45
# File 'lib/rubocop/sketchup/cop/requirements/extension_namespace.rb', line 43

def on_module(node)
  check_class_or_module(node)
end

#reserved?(namespace) ⇒ Boolean

Returns:

  • (Boolean)


95
96
97
98
99
100
101
102
# File 'lib/rubocop/sketchup/cop/requirements/extension_namespace.rb', line 95

def reserved?(namespace)
  top = namespace.first
  return true if RubyCoreNamespace::NAMESPACES.include?(top)
  return true if RubyStdLibNamespace::NAMESPACES.include?(top)
  return true if ApiNamespace::NAMESPACES.include?(top)

  false
end