Module: RuboCop::Cop::ViewComponent::Base

Includes:
ProjectIndexHelp
Included in:
ComponentSuffix, MissingPreview, NoGlobalState, PreferComposition, PreferPrivateMethods, PreferSlots
Defined in:
lib/rubocop/cop/view_component/base.rb

Overview

Shared helper methods for ViewComponent cops. Requires rubydex with UseProjectIndex: true for ancestry resolution.

Constant Summary collapse

VC_BASE =
"ViewComponent::Base"

Instance Method Summary collapse

Instance Method Details

#component_namespacesObject



64
65
66
# File 'lib/rubocop/cop/view_component/base.rb', line 64

def component_namespaces
  cop_config["ComponentNamespaces"] || []
end

#enclosing_class(node) ⇒ Object

Find the enclosing class node



69
70
71
# File 'lib/rubocop/cop/view_component/base.rb', line 69

def enclosing_class(node)
  node.each_ancestor(:class).first
end

#fully_qualified_name(node) ⇒ Object



56
57
58
59
60
61
62
# File 'lib/rubocop/cop/view_component/base.rb', line 56

def fully_qualified_name(node)
  namespace = node.parent_module_name
  short_name = node.identifier.source
  return short_name if namespace.nil? || namespace == "Object"

  "#{namespace}::#{short_name}"
end

#inside_view_component?(node) ⇒ Boolean

Check if node is within a ViewComponent class

Returns:

  • (Boolean)


74
75
76
77
78
79
80
81
# File 'lib/rubocop/cop/view_component/base.rb', line 74

def inside_view_component?(node)
  require_project_index!

  klass = enclosing_class(node)
  return false unless klass

  view_component_class?(klass)
end

#require_project_index!Object



83
84
85
86
87
88
89
90
# File 'lib/rubocop/cop/view_component/base.rb', line 83

def require_project_index!
  return if project_index

  raise <<~MSG
    ViewComponent cops require `AllCops/UseProjectIndex: true` in your .rubocop.yml
    for ancestry-based class detection.
  MSG
end

#view_component_class?(node) ⇒ Boolean

Check if a class node inherits from ViewComponent::Base

Returns:

  • (Boolean)


16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/rubocop/cop/view_component/base.rb', line 16

def view_component_class?(node)
  return false unless node&.class_type?

  class_source = fully_qualified_name(node)
  return true if component_namespaces.any? { |ns| class_source.start_with?(ns) }

  require_project_index!

  parent_class = node.parent_class
  return false unless parent_class

  view_component_parent?(parent_class)
end

#view_component_parent?(node) ⇒ Boolean

Check if a constant node resolves to a class with ViewComponent::Base as ancestor

Returns:

  • (Boolean)


31
32
33
34
35
36
37
38
39
40
# File 'lib/rubocop/cop/view_component/base.rb', line 31

def view_component_parent?(node)
  return false unless node.const_type?

  require_project_index!

  declaration = resolve_constant_in_index(node)
  return false unless declaration.respond_to?(:has_ancestor?)

  declaration.name == VC_BASE || declaration.has_ancestor?(VC_BASE)
end

#view_component_parent_class?(node) ⇒ Boolean

Check if a class node is itself an abstract parent class (has ViewComponent::Base as ancestor and has descendants in the project)

Returns:

  • (Boolean)


44
45
46
47
48
49
50
51
52
53
54
# File 'lib/rubocop/cop/view_component/base.rb', line 44

def view_component_parent_class?(node)
  return false unless node&.class_type?

  require_project_index!

  declaration = resolve_constant_in_index(node.identifier)
  return false unless declaration.respond_to?(:has_ancestor?)

  declaration.has_ancestor?(VC_BASE) &&
    declaration.descendants.any? { |d| d.name != declaration.name }
end